'use strict'; const FunctionNodeBase = require('../function-node-base'); const utils = require('../../core/utils'); // Closure capture for the ast function, prevent collision with existing AST functions // The prefixes to use const jsMathPrefix = 'Math.'; const localPrefix = 'this.'; const constantsPrefix = 'this.constants.'; const DECODE32_ENCODE32 = /decode32\(\s+encode32\(/g; const ENCODE32_DECODE32 = /encode32\(\s+decode32\(/g; /** * @class WebGLFunctionNode * * @desc [INTERNAL] Takes in a function node, and does all the AST voodoo required to generate its respective webGL code. * * @extends FunctionNodeBase * * @param {functionNode} inNode - The function node object * * @returns the converted webGL function string * */ module.exports = class WebGLFunctionNode extends FunctionNodeBase { generate() { if (this.debug) { console.log(this); } if (this.prototypeOnly) { return WebGLFunctionNode.astFunctionPrototype(this.getJsAST(), [], this).join('').trim(); } else { this.functionStringArray = this.astGeneric(this.getJsAST(), [], this); } this.functionString = webGlRegexOptimize( this.functionStringArray.join('').trim() ); return this.functionString; } /** * @memberOf WebGLFunctionNode# * @function * @name astGeneric * * @desc Parses the abstract syntax tree for generically to its respective function * * @param {Object} ast - the AST object to parse * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the parsed webgl string array */ astGeneric(ast, retArr, funcParam) { if (ast === null) { throw this.astErrorOutput('NULL ast', ast, funcParam); } else { if (Array.isArray(ast)) { for (let i = 0; i < ast.length; i++) { this.astGeneric(ast[i], retArr, funcParam); } return retArr; } switch (ast.type) { case 'FunctionDeclaration': return this.astFunctionDeclaration(ast, retArr, funcParam); case 'FunctionExpression': return this.astFunctionExpression(ast, retArr, funcParam); case 'ReturnStatement': return this.astReturnStatement(ast, retArr, funcParam); case 'Literal': return this.astLiteral(ast, retArr, funcParam); case 'BinaryExpression': return this.astBinaryExpression(ast, retArr, funcParam); case 'Identifier': return this.astIdentifierExpression(ast, retArr, funcParam); case 'AssignmentExpression': return this.astAssignmentExpression(ast, retArr, funcParam); case 'ExpressionStatement': return this.astExpressionStatement(ast, retArr, funcParam); case 'EmptyStatement': return this.astEmptyStatement(ast, retArr, funcParam); case 'BlockStatement': return this.astBlockStatement(ast, retArr, funcParam); case 'IfStatement': return this.astIfStatement(ast, retArr, funcParam); case 'BreakStatement': return this.astBreakStatement(ast, retArr, funcParam); case 'ContinueStatement': return this.astContinueStatement(ast, retArr, funcParam); case 'ForStatement': return this.astForStatement(ast, retArr, funcParam); case 'WhileStatement': return this.astWhileStatement(ast, retArr, funcParam); case 'VariableDeclaration': return this.astVariableDeclaration(ast, retArr, funcParam); case 'VariableDeclarator': return this.astVariableDeclarator(ast, retArr, funcParam); case 'ThisExpression': return this.astThisExpression(ast, retArr, funcParam); case 'SequenceExpression': return this.astSequenceExpression(ast, retArr, funcParam); case 'UnaryExpression': return this.astUnaryExpression(ast, retArr, funcParam); case 'UpdateExpression': return this.astUpdateExpression(ast, retArr, funcParam); case 'LogicalExpression': return this.astLogicalExpression(ast, retArr, funcParam); case 'MemberExpression': return this.astMemberExpression(ast, retArr, funcParam); case 'CallExpression': return this.astCallExpression(ast, retArr, funcParam); case 'ArrayExpression': return this.astArrayExpression(ast, retArr, funcParam); case 'DebuggerStatement': return this.astDebuggerStatement(ast, retArr, funcParam); } throw this.astErrorOutput('Unknown ast type : ' + ast.type, ast, funcParam); } } /** * @memberOf WebGLFunctionNode# * @function * @name astFunctionDeclaration * * @desc Parses the abstract syntax tree for to its *named function declaration* * * @param {Object} ast - the AST object to parse * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astFunctionDeclaration(ast, retArr, funcParam) { if (this.addFunction) { this.addFunction(null, utils.getAstString(this.jsFunctionString, ast)); } return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astFunctionPrototype * @static * * @desc Parses the abstract syntax tree for to its *named function prototype* * * @param {Object} ast - the AST object to parse * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ static astFunctionPrototype(ast, retArr, funcParam) { // Setup function return type and name if (funcParam.isRootKernel || funcParam.isSubKernel) { return retArr; } retArr.push(funcParam.returnType); retArr.push(' '); retArr.push(funcParam.functionName); retArr.push('('); // Arguments handling for (let i = 0; i < funcParam.paramNames.length; ++i) { if (i > 0) { retArr.push(', '); } retArr.push(funcParam.paramTypes[i]); retArr.push(' '); retArr.push('user_'); retArr.push(funcParam.paramNames[i]); } retArr.push(');\n'); return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astFunctionExpression * * @desc Parses the abstract syntax tree for to its *named function* * * @param {Object} ast - the AST object to parse * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astFunctionExpression(ast, retArr, funcParam) { // Setup function return type and name if (funcParam.isRootKernel) { retArr.push('void'); funcParam.kernalAst = ast; } else { retArr.push(funcParam.returnType); } retArr.push(' '); retArr.push(funcParam.functionName); retArr.push('('); if (!funcParam.isRootKernel) { // Arguments handling for (let i = 0; i < funcParam.paramNames.length; ++i) { const paramName = funcParam.paramNames[i]; if (i > 0) { retArr.push(', '); } const type = funcParam.getParamType(paramName); switch (type) { case 'Texture': case 'Input': case 'Array': retArr.push('sampler2D'); break; default: retArr.push('float'); } retArr.push(' '); retArr.push('user_'); retArr.push(paramName); } } // Function opening retArr.push(') {\n'); // Body statement iteration for (let i = 0; i < ast.body.body.length; ++i) { this.astGeneric(ast.body.body[i], retArr, funcParam); retArr.push('\n'); } // Function closing retArr.push('}\n'); return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astReturnStatement * * @desc Parses the abstract syntax tree for to *return* statement * * @param {Object} ast - the AST object to parse * @param {Array} retArr - return array string * @param {Object} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astReturnStatement(ast, retArr, funcParam) { if (funcParam.isRootKernel) { retArr.push('kernelResult = '); this.astGeneric(ast.argument, retArr, funcParam); retArr.push(';'); retArr.push('return;'); } else if (funcParam.isSubKernel) { retArr.push(`${ funcParam.functionName }Result = `); this.astGeneric(ast.argument, retArr, funcParam); retArr.push(';'); retArr.push(`return ${ funcParam.functionName }Result;`); } else { retArr.push('return '); this.astGeneric(ast.argument, retArr, funcParam); retArr.push(';'); } //throw this.astErrorOutput( // 'Non main function return, is not supported : '+funcParam.currentFunctionNamespace, // ast, funcParam //); return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astLiteral * * @desc Parses the abstract syntax tree for *literal value* * * @param {Object} ast - the AST object to parse * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astLiteral(ast, retArr, funcParam) { // Reject non numeric literals if (isNaN(ast.value)) { throw this.astErrorOutput( 'Non-numeric literal not supported : ' + ast.value, ast, funcParam ); } // Push the literal value as a float/int retArr.push(ast.value); // If it was an int, node made a float if (Number.isInteger(ast.value)) { retArr.push('.0'); } return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astBinaryExpression * * @desc Parses the abstract syntax tree for *binary* expression * * @param {Object} ast - the AST object to parse * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astBinaryExpression(ast, retArr, funcParam) { retArr.push('('); if (ast.operator === '%') { retArr.push('mod('); this.astGeneric(ast.left, retArr, funcParam); retArr.push(','); this.astGeneric(ast.right, retArr, funcParam); retArr.push(')'); } else if (ast.operator === '===') { this.astGeneric(ast.left, retArr, funcParam); retArr.push('=='); this.astGeneric(ast.right, retArr, funcParam); } else if (ast.operator === '!==') { this.astGeneric(ast.left, retArr, funcParam); retArr.push('!='); this.astGeneric(ast.right, retArr, funcParam); } else { this.astGeneric(ast.left, retArr, funcParam); retArr.push(ast.operator); this.astGeneric(ast.right, retArr, funcParam); } retArr.push(')'); return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astIdentifierExpression * * @desc Parses the abstract syntax tree for *identifier* expression * * @param {Object} idtNode - An ast Node * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astIdentifierExpression(idtNode, retArr, funcParam) { if (idtNode.type !== 'Identifier') { throw this.astErrorOutput( 'IdentifierExpression - not an Identifier', idtNode, funcParam ); } switch (idtNode.name) { case 'gpu_threadX': retArr.push('threadId.x'); break; case 'gpu_threadY': retArr.push('threadId.y'); break; case 'gpu_threadZ': retArr.push('threadId.z'); break; case 'gpu_outputX': retArr.push('uOutputDim.x'); break; case 'gpu_outputY': retArr.push('uOutputDim.y'); break; case 'gpu_outputZ': retArr.push('uOutputDim.z'); break; default: if (this.constants && this.constants.hasOwnProperty(idtNode.name)) { retArr.push('constants_' + idtNode.name); } else { const userParamName = funcParam.getUserParamName(idtNode.name); if (userParamName !== null) { retArr.push('user_' + userParamName); } else { retArr.push('user_' + idtNode.name); } } } return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astForStatement * * @desc Parses the abstract syntax tree forfor *for-loop* expression * * @param {Object} forNode - An ast Node * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the parsed webgl string */ astForStatement(forNode, retArr, funcParam) { if (forNode.type !== 'ForStatement') { throw this.astErrorOutput( 'Invalid for statment', forNode, funcParam ); } if (forNode.test && forNode.test.type === 'BinaryExpression') { if (forNode.test.right.type === 'Identifier' && forNode.test.operator === '<' && this.isIdentifierConstant(forNode.test.right.name) === false) { if (!this.loopMaxIterations) { console.warn('Warning: loopMaxIterations is not set! Using default of 1000 which may result in unintended behavior.'); console.warn('Set loopMaxIterations or use a for loop of fixed length to silence this message.'); } retArr.push('for ('); this.astGeneric(forNode.init, retArr, funcParam); this.astGeneric(forNode.test.left, retArr, funcParam); retArr.push(forNode.test.operator); retArr.push('LOOP_MAX'); retArr.push(';'); this.astGeneric(forNode.update, retArr, funcParam); retArr.push(')'); retArr.push('{\n'); retArr.push('if ('); this.astGeneric(forNode.test.left, retArr, funcParam); retArr.push(forNode.test.operator); this.astGeneric(forNode.test.right, retArr, funcParam); retArr.push(') {\n'); if (forNode.body.type === 'BlockStatement') { for (let i = 0; i < forNode.body.body.length; i++) { this.astGeneric(forNode.body.body[i], retArr, funcParam); } } else { this.astGeneric(forNode.body, retArr, funcParam); } retArr.push('} else {\n'); retArr.push('break;\n'); retArr.push('}\n'); retArr.push('}\n'); return retArr; } else { const declarations = JSON.parse(JSON.stringify(forNode.init.declarations)); const updateArgument = forNode.update.argument; if (!Array.isArray(declarations) || declarations.length < 1) { console.log(this.jsFunctionString); throw new Error('Error: Incompatible for loop declaration'); } if (declarations.length > 1) { let initArgument = null; for (let i = 0; i < declarations.length; i++) { const declaration = declarations[i]; if (declaration.id.name === updateArgument.name) { initArgument = declaration; declarations.splice(i, 1); } else { retArr.push('float '); this.astGeneric(declaration, retArr, funcParam); retArr.push(';'); } } retArr.push('for (float '); this.astGeneric(initArgument, retArr, funcParam); retArr.push(';'); } else { retArr.push('for ('); this.astGeneric(forNode.init, retArr, funcParam); } this.astGeneric(forNode.test, retArr, funcParam); retArr.push(';'); this.astGeneric(forNode.update, retArr, funcParam); retArr.push(')'); this.astGeneric(forNode.body, retArr, funcParam); return retArr; } } throw this.astErrorOutput( 'Invalid for statement', forNode, funcParam ); } /** * @memberOf WebGLFunctionNode# * @function * @name astWhileStatement * * @desc Parses the abstract syntax tree for *while* loop * * * @param {Object} whileNode - An ast Node * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the parsed webgl string */ astWhileStatement(whileNode, retArr, funcParam) { if (whileNode.type !== 'WhileStatement') { throw this.astErrorOutput( 'Invalid while statment', whileNode, funcParam ); } retArr.push('for (float i = 0.0; i < LOOP_MAX; i++) {'); retArr.push('if ('); this.astGeneric(whileNode.test, retArr, funcParam); retArr.push(') {\n'); this.astGeneric(whileNode.body, retArr, funcParam); retArr.push('} else {\n'); retArr.push('break;\n'); retArr.push('}\n'); retArr.push('}\n'); return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astAssignmentExpression * * @desc Parses the abstract syntax tree for *Assignment* Expression * * @param {Object} assNode - An ast Node * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astAssignmentExpression(assNode, retArr, funcParam) { if (assNode.operator === '%=') { this.astGeneric(assNode.left, retArr, funcParam); retArr.push('='); retArr.push('mod('); this.astGeneric(assNode.left, retArr, funcParam); retArr.push(','); this.astGeneric(assNode.right, retArr, funcParam); retArr.push(')'); } else { this.astGeneric(assNode.left, retArr, funcParam); retArr.push(assNode.operator); this.astGeneric(assNode.right, retArr, funcParam); return retArr; } } /** * @memberOf WebGLFunctionNode# * @function * @name astEmptyStatement * * @desc Parses the abstract syntax tree for an *Empty* Statement * * @param {Object} eNode - An ast Node * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astEmptyStatement(eNode, retArr, funcParam) { //retArr.push(';\n'); return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astBlockStatement * * @desc Parses the abstract syntax tree for *Block* statement * * @param {Object} bnode - the AST object to parse * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astBlockStatement(bNode, retArr, funcParam) { retArr.push('{\n'); for (let i = 0; i < bNode.body.length; i++) { this.astGeneric(bNode.body[i], retArr, funcParam); } retArr.push('}\n'); return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astExpressionStatement * * @desc Parses the abstract syntax tree for *generic expression* statement * * @param {Object} esNode - An ast Node * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astExpressionStatement(esNode, retArr, funcParam) { this.astGeneric(esNode.expression, retArr, funcParam); retArr.push(';\n'); return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astVariableDeclaration * * @desc Parses the abstract syntax tree for *Variable Declaration* * * @param {Object} vardecNode - An ast Node * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astVariableDeclaration(vardecNode, retArr, funcParam) { retArr.push('float '); for (let i = 0; i < vardecNode.declarations.length; i++) { if (i > 0) { retArr.push(','); } this.astGeneric(vardecNode.declarations[i], retArr, funcParam); } retArr.push(';'); return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astVariableDeclarator * * @desc Parses the abstract syntax tree for *Variable Declarator* * * @param {Object} ivardecNode - An ast Node * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astVariableDeclarator(ivardecNode, retArr, funcParam) { this.astGeneric(ivardecNode.id, retArr, funcParam); if (ivardecNode.init !== null) { retArr.push('='); this.astGeneric(ivardecNode.init, retArr, funcParam); } return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astIfStatement * * @desc Parses the abstract syntax tree for *If* Statement * * @param {Object} ifNode - An ast Node * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astIfStatement(ifNode, retArr, funcParam) { retArr.push('if ('); this.astGeneric(ifNode.test, retArr, funcParam); retArr.push(')'); if (ifNode.consequent.type === 'BlockStatement') { this.astGeneric(ifNode.consequent, retArr, funcParam); } else { retArr.push(' {\n'); this.astGeneric(ifNode.consequent, retArr, funcParam); retArr.push('\n}\n'); } if (ifNode.alternate) { retArr.push('else '); if (ifNode.alternate.type === 'BlockStatement') { this.astGeneric(ifNode.alternate, retArr, funcParam); } else { retArr.push(' {\n'); this.astGeneric(ifNode.alternate, retArr, funcParam); retArr.push('\n}\n'); } } return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astBreakStatement * * @desc Parses the abstract syntax tree for *Break* Statement * * @param {Object} brNode - An ast Node * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astBreakStatement(brNode, retArr, funcParam) { retArr.push('break;\n'); return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astContinueStatement * * @desc Parses the abstract syntax tree for *Continue* Statement * * @param {Object} crNode - An ast Node * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astContinueStatement(crNode, retArr, funcParam) { retArr.push('continue;\n'); return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astLogicalExpression * * @desc Parses the abstract syntax tree for *Logical* Expression * * @param {Object} logNode - An ast Node * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astLogicalExpression(logNode, retArr, funcParam) { retArr.push('('); this.astGeneric(logNode.left, retArr, funcParam); retArr.push(logNode.operator); this.astGeneric(logNode.right, retArr, funcParam); retArr.push(')'); return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astUpdateExpression * * @desc Parses the abstract syntax tree for *Update* Expression * * @param {Object} uNode - An ast Node * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astUpdateExpression(uNode, retArr, funcParam) { if (uNode.prefix) { retArr.push(uNode.operator); this.astGeneric(uNode.argument, retArr, funcParam); } else { this.astGeneric(uNode.argument, retArr, funcParam); retArr.push(uNode.operator); } return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astUnaryExpression * * @desc Parses the abstract syntax tree for *Unary* Expression * * @param {Object} uNode - An ast Node * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astUnaryExpression(uNode, retArr, funcParam) { if (uNode.prefix) { retArr.push(uNode.operator); this.astGeneric(uNode.argument, retArr, funcParam); } else { this.astGeneric(uNode.argument, retArr, funcParam); retArr.push(uNode.operator); } return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astThisExpression * * @desc Parses the abstract syntax tree for *This* expression * * @param {Object} tNode - An ast Node * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astThisExpression(tNode, retArr, funcParam) { retArr.push('this'); return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astMemberExpression * * @desc Parses the abstract syntax tree for *Member* Expression * * @param {Object} mNode - An ast Node * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astMemberExpression(mNode, retArr, funcParam) { if (mNode.computed) { if (mNode.object.type === 'Identifier') { // Working logger const reqName = mNode.object.name; const funcName = funcParam.functionName || 'kernel'; let assumeNotTexture = false; // Possibly an array request - handle it as such if (funcParam.paramNames) { const idx = funcParam.paramNames.indexOf(reqName); if (idx >= 0 && funcParam.paramTypes[idx] === 'float') { assumeNotTexture = true; } } if (assumeNotTexture) { // Get from array this.astGeneric(mNode.object, retArr, funcParam); retArr.push('[int('); this.astGeneric(mNode.property, retArr, funcParam); retArr.push(')]'); } else { // Get from texture // This normally refers to the global read only input vars retArr.push('get('); this.astGeneric(mNode.object, retArr, funcParam); retArr.push(', vec2('); this.astGeneric(mNode.object, retArr, funcParam); retArr.push('Size[0],'); this.astGeneric(mNode.object, retArr, funcParam); retArr.push('Size[1]), vec3('); this.astGeneric(mNode.object, retArr, funcParam); retArr.push('Dim[0],'); this.astGeneric(mNode.object, retArr, funcParam); retArr.push('Dim[1],'); this.astGeneric(mNode.object, retArr, funcParam); retArr.push('Dim[2]'); retArr.push('), '); this.astGeneric(mNode.property, retArr, funcParam); retArr.push(')'); } } else { this.astGeneric(mNode.object, retArr, funcParam); const last = retArr.pop(); retArr.push(','); this.astGeneric(mNode.property, retArr, funcParam); retArr.push(last); } } else { // Unroll the member expression let unrolled = this.astMemberExpressionUnroll(mNode); let unrolled_lc = unrolled.toLowerCase(); // Its a constant, remove this.constants. if (unrolled.indexOf(constantsPrefix) === 0) { unrolled = 'constants_' + unrolled.slice(constantsPrefix.length); } switch (unrolled_lc) { case 'this.thread.x': retArr.push('threadId.x'); break; case 'this.thread.y': retArr.push('threadId.y'); break; case 'this.thread.z': retArr.push('threadId.z'); break; case 'this.output.x': retArr.push(this.output[0] + '.0'); break; case 'this.output.y': retArr.push(this.output[1] + '.0'); break; case 'this.output.z': retArr.push(this.output[2] + '.0'); break; default: retArr.push(unrolled); } } return retArr; } astSequenceExpression(sNode, retArr, funcParam) { for (let i = 0; i < sNode.expressions.length; i++) { if (i > 0) { retArr.push(','); } this.astGeneric(sNode.expressions, retArr, funcParam); } return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astCallExpression * * @desc Parses the abstract syntax tree for *call* expression * * @param {Object} ast - the AST object to parse * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astCallExpression(ast, retArr, funcParam) { if (ast.callee) { // Get the full function call, unrolled let funcName = this.astMemberExpressionUnroll(ast.callee); // Its a math operator, remove the prefix if (funcName.indexOf(jsMathPrefix) === 0) { funcName = funcName.slice(jsMathPrefix.length); } // Its a local function, remove this if (funcName.indexOf(localPrefix) === 0) { funcName = funcName.slice(localPrefix.length); } // if this if grows to more than one, lets use a switch if (funcName === 'atan2') { funcName = 'atan'; } // Register the function into the called registry if (funcParam.calledFunctions.indexOf(funcName) < 0) { funcParam.calledFunctions.push(funcName); } if (!funcParam.hasOwnProperty('funcName')) { funcParam.calledFunctionsArguments[funcName] = []; } const functionArguments = []; funcParam.calledFunctionsArguments[funcName].push(functionArguments); // Call the function retArr.push(funcName); // Open arguments space retArr.push('('); // Add the vars for (let i = 0; i < ast.arguments.length; ++i) { const argument = ast.arguments[i]; if (i > 0) { retArr.push(', '); } this.astGeneric(argument, retArr, funcParam); if (argument.type === 'Identifier') { const paramIndex = funcParam.paramNames.indexOf(argument.name); if (paramIndex === -1) { functionArguments.push(null); } else { functionArguments.push({ name: argument.name, type: funcParam.paramTypes[paramIndex] }); } } else { functionArguments.push(null); } } // Close arguments space retArr.push(')'); return retArr; } // Failure, unknown expression throw this.astErrorOutput( 'Unknown CallExpression', ast, funcParam ); return retArr; } /** * @memberOf WebGLFunctionNode# * @function * @name astArrayExpression * * @desc Parses the abstract syntax tree for *Array* Expression * * @param {Object} ast - the AST object to parse * @param {Array} retArr - return array string * @param {Function} funcParam - FunctionNode, that tracks compilation state * * @returns {Array} the append retArr */ astArrayExpression(arrNode, retArr, funcParam) { const arrLen = arrNode.elements.length; retArr.push('float[' + arrLen + ']('); for (let i = 0; i < arrLen; ++i) { if (i > 0) { retArr.push(', '); } const subNode = arrNode.elements[i]; this.astGeneric(subNode, retArr, funcParam) } retArr.push(')'); return retArr; // // Failure, unknown expression // throw this.astErrorOutput( // 'Unknown ArrayExpression', // arrNode, funcParam //); } /** * @memberOf WebGLFunctionNode# * @function * @name getFunctionPrototypeString * * @desc Returns the converted webgl shader function equivalent of the JS function * * @returns {String} webgl function string, result is cached under this.getFunctionPrototypeString * */ getFunctionPrototypeString() { if (this.webGlFunctionPrototypeString) { return this.webGlFunctionPrototypeString; } return this.webGlFunctionPrototypeString = this.generate(); } build() { return this.getFunctionPrototypeString().length > 0; } }; function isIdentifierKernelParam(paramName, ast, funcParam) { return funcParam.paramNames.indexOf(paramName) !== -1; } function ensureIndentifierType(paramName, expectedType, ast, funcParam) { const start = ast.loc.start; if (!isIdentifierKernelParam(paramName, funcParam) && expectedType !== 'float') { throw new Error('Error unexpected identifier ' + paramName + ' on line ' + start.line); } else { const actualType = funcParam.paramTypes[funcParam.paramNames.indexOf(paramName)]; if (actualType !== expectedType) { throw new Error('Error unexpected identifier ' + paramName + ' on line ' + start.line); } } } /** * @ignore * @function * @name webgl_regex_optimize * * @desc [INTERNAL] Takes the near final webgl function string, and do regex search and replacments. * For voodoo optimize out the following: * * - decode32(encode32(
* - encode32(decode32(
* * @param {String} inStr - The webGl function String * */ function webGlRegexOptimize(inStr) { return inStr .replace(DECODE32_ENCODE32, '((') .replace(ENCODE32_DECODE32, '(('); }