| addToLibrary({ |
| |
| |
| $remove_cpp_comments_in_shaders: (code) => { |
| var i = 0, out = '', ch, next, len = code.length; |
| for (; i < len; ++i) { |
| ch = code[i]; |
| if (ch == '/') { |
| next = code[i+1]; |
| if (next == '/') { |
| while (i < len && code[i+1] != '\n') ++i; |
| } else if (next == '*') { |
| while (i < len && (code[i-1] != '*' || code[i] != '/')) ++i; |
| } else { |
| out += ch; |
| } |
| } else { |
| out += ch; |
| } |
| } |
| return out; |
| }, |
|
|
| |
| |
| $find_closing_parens_index: (arr, i, opening='(', closing=')') => { |
| for (var nesting = 0; i < arr.length; ++i) { |
| if (arr[i] == opening) ++nesting; |
| if (arr[i] == closing && --nesting == 0) { |
| return i; |
| } |
| } |
| }, |
|
|
| |
| |
| |
| $preprocess_c_code__deps: ['$find_closing_parens_index'], |
| $preprocess_c_code: function(code, defs = {}) { |
| var i = 0, |
| len = code.length, |
| out = '', |
| stack = [1]; |
| |
| defs['defined'] = (args) => { |
| #if ASSERTIONS |
| assert(args.length == 1); |
| assert(/^[A-Za-z0-9_$]+$/.test(args[0].trim())); |
| #endif |
| return defs[args[0].trim()] ? 1 : 0; |
| }; |
|
|
| |
| function isWhitespace(str, i) { |
| return !(str.charCodeAt(i) > 32); |
| } |
|
|
| |
| function nextWhitespace(str, i) { |
| while (!isWhitespace(str, i)) ++i; |
| return i; |
| } |
|
|
| |
| function classifyChar(str, idx) { |
| var cc = str.charCodeAt(idx); |
| #if ASSERTIONS |
| assert(!(cc > 127), "only 7-bit ASCII can be used in preprocessor #if/#ifdef/#define statements"); |
| #endif |
| if (cc > 32) { |
| if (cc < 48) return 1; |
| if (cc < 58) return 2; |
| if (cc < 65) return 1; |
| if (cc < 91 || cc == 95) return 3; |
| if (cc < 97) return 1; |
| if (cc < 123) return 3; |
| return 1; |
| } |
| return cc < 33 ? 0 : 4; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| function tokenize(exprString, keepWhitespace) { |
| var out = [], len = exprString.length; |
| for (var i = 0; i <= len; ++i) { |
| var kind = classifyChar(exprString, i); |
| if (kind == 2 || kind == 3) { |
| for (var j = i+1; j <= len; ++j) { |
| var kind2 = classifyChar(exprString, j); |
| if (kind2 != kind && (kind2 != 2 || kind != 3)) { |
| out.push(exprString.substring(i, j)); |
| i = j-1; |
| break; |
| } |
| } |
| } else if (kind == 1) { |
| |
| var op2 = exprString.slice(i, i + 2); |
| if (['<=', '>=', '==', '!=', '&&', '||'].includes(op2)) { |
| out.push(op2); |
| ++i; |
| } else { |
| out.push(exprString[i]); |
| } |
| } |
| } |
| return out; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| function expandMacros(str, lineStart, lineEnd=str.length) { |
| var len = str.length; |
| var out = ''; |
| for (var i = lineStart; i < lineEnd; ++i) { |
| var kind = classifyChar(str, i); |
| if (kind == 3) { |
| for (var j = i + 1; j <= lineEnd; ++j) { |
| var kind2 = classifyChar(str, j); |
| if (kind2 != 2 && kind2 != 3) { |
| var symbol = str.substring(i, j); |
| #if MIN_FIREFOX_VERSION < 92 |
| |
| if (defs.hasOwnProperty(symbol)) { |
| #else |
| if (Object.hasOwn(defs, symbol)) { |
| #endif |
| var pp = defs[symbol], expanded; |
| if (typeof pp == 'function') { |
| if (pp.length) { |
| while (str[j] && isWhitespace(str, j)) ++j; |
| if (str[j] == '(') { |
| var closeParens = find_closing_parens_index(str, j); |
| |
| |
| expanded = pp(str.substring(j+1, closeParens).split(',')); |
| if (expanded === !!expanded) expanded = expanded|0; |
| j = closeParens+1; |
| } else { |
| var start = j; |
| j = nextWhitespace(str, j); |
| expanded = pp([str.substring(start, j)]); |
| } |
| } else { |
| expanded = pp(); |
| } |
| } else { |
| |
| |
| |
| expanded = (pp === !!pp ? pp|0 : pp); |
| } |
| return expandMacros(str.substring(lineStart, i) + expanded + str.substring(j, lineEnd), 0); |
| } |
| out += symbol; |
| i = j-1; |
| break; |
| } |
| } |
| } else { |
| out += str[i]; |
| } |
| } |
| return out; |
| } |
|
|
| |
| function buildExprTree(tokens) { |
| |
| |
| while (tokens.length > 1 || typeof tokens[0] != 'function') { |
| tokens = ((tokens) => { |
| |
| var i, j, p, operatorAndPriority = -2; |
| for (j = 0; j < tokens.length; ++j) { |
| if ((p = ['*', '/', '+', '-', '!', '<', '<=', '>', '>=', '==', '!=', '&&', '||', '('].indexOf(tokens[j])) > operatorAndPriority) { |
| i = j; |
| operatorAndPriority = p; |
| } |
| } |
|
|
| if (operatorAndPriority == 13 ) { |
| |
| j = find_closing_parens_index(tokens, i); |
| if (j) { |
| tokens.splice(i, j+1-i, buildExprTree(tokens.slice(i+1, j))); |
| return tokens; |
| } |
| } |
|
|
| if (operatorAndPriority == 4 ) { |
| |
| i = tokens.lastIndexOf('!'); |
| var innerExpr = buildExprTree(tokens.slice(i+1, i+2)); |
| tokens.splice(i, 2, function() { return !innerExpr(); }) |
| return tokens; |
| } |
|
|
| |
| if (operatorAndPriority >= 0) { |
| var left = buildExprTree(tokens.slice(0, i)); |
| var right = buildExprTree(tokens.slice(i+1)); |
| switch(tokens[i]) { |
| case '&&': return [function() { return left() && right(); }]; |
| case '||': return [function() { return left() || right(); }]; |
| case '==': return [function() { return left() == right(); }]; |
| case '!=': return [function() { return left() != right(); }]; |
| case '<' : return [function() { return left() < right(); }]; |
| case '<=': return [function() { return left() <= right(); }]; |
| case '>' : return [function() { return left() > right(); }]; |
| case '>=': return [function() { return left() >= right(); }]; |
| case '+': return [function() { return left() + right(); }]; |
| case '-': return [function() { return left() - right(); }]; |
| case '*': return [function() { return left() * right(); }]; |
| case '/': return [function() { return Math.floor(left() / right()); }]; |
| } |
| } |
| |
| #if ASSERTIONS |
| assert(tokens[i] !== ')', 'parse failure, mismatched parentheses in parsing' + tokens.toString()); |
| assert(operatorAndPriority == -1); |
| #endif |
| var num = Number(tokens[i]); |
| return [function() { return num; }] |
| })(tokens); |
| } |
| return tokens[0]; |
| } |
|
|
| |
| for (; i < len; ++i) { |
| |
| var lineStart = i; |
|
|
| |
| i = code.indexOf('\n', i); |
| if (i < 0) i = len; |
|
|
| |
| for (var j = lineStart; j < i && isWhitespace(code, j);) ++j; |
|
|
| |
| var thisLineIsInActivePreprocessingBlock = stack[stack.length-1]; |
| if (code[j] != '#') { |
| if (thisLineIsInActivePreprocessingBlock) { |
| out += expandMacros(code, lineStart, i) + '\n'; |
| } |
| continue; |
| } |
| |
|
|
| |
| var space = nextWhitespace(code, j); |
| var directive = code.substring(j+1, space); |
| var expression = code.substring(space, i).trim(); |
| switch(directive) { |
| case 'if': |
| var tokens = tokenize(expandMacros(expression, 0)); |
| var exprTree = buildExprTree(tokens); |
| var evaluated = exprTree(); |
| stack.push(!!evaluated * stack[stack.length-1]); |
| break; |
| case 'elif': |
| var tokens = tokenize(expandMacros(expression, 0)); |
| var exprTree = buildExprTree(tokens); |
| var evaluated = exprTree(); |
| |
| |
| stack[stack.length-1] = !!evaluated * (stack[stack.length-1] ? NaN : 1-stack[stack.length-1]); |
| break; |
| case 'ifdef': stack.push(!!defs[expression] * stack[stack.length-1]); break; |
| case 'ifndef': stack.push(!defs[expression] * stack[stack.length-1]); break; |
| case 'else': stack[stack.length-1] = (1-stack[stack.length-1]) * stack[stack.length-2]; break; |
| case 'endif': stack.pop(); break; |
| case 'define': |
| if (thisLineIsInActivePreprocessingBlock) { |
| |
| |
| var macroStart = expression.indexOf('('); |
| var firstWs = nextWhitespace(expression, 0); |
| if (firstWs < macroStart) macroStart = 0; |
| if (macroStart > 0) { |
| var macroEnd = expression.indexOf(')', macroStart); |
| let params = expression.substring(macroStart+1, macroEnd).split(',').map(x => x.trim()); |
| let value = tokenize(expression.substring(macroEnd+1).trim()) |
| defs[expression.substring(0, macroStart)] = (args) => { |
| var ret = ''; |
| value.forEach((x) => { |
| var argIndex = params.indexOf(x); |
| ret += (argIndex >= 0) ? args[argIndex] : x; |
| }); |
| return ret; |
| }; |
| } else { |
| let value = expandMacros(expression.substring(firstWs+1).trim(), 0); |
| defs[expression.substring(0, firstWs)] = () => value; |
| } |
| } |
| break; |
| case 'undef': if (thisLineIsInActivePreprocessingBlock) delete defs[expression]; break; |
| default: |
| if (directive != 'version' && directive != 'pragma' && directive != 'extension' && directive != 'line') { |
| #if ASSERTIONS |
| err('Unrecognized preprocessor directive #' + directive + '!'); |
| #endif |
| } |
|
|
| |
| out += expandMacros(code, lineStart, i) + '\n'; |
| } |
| } |
| return out; |
| } |
| }); |
|
|