// Closure capture for the ast function, prevent collision with existing AST functions var functionNode_webgl = (function() { var gpu, opt, jsFunctionString; function isIdentifierKernelParam(paramName, ast, funcParam) { return funcParam.paramNames.indexOf(paramName) != -1; } function ensureIndentifierType(paramName, expectedType, ast, funcParam) { var start = ast.loc.start; if (!isIdentifierKernelParam(paramName, funcParam) && expectedType != 'float') { throw "Error unxpected identifier " + paramName + " on line " + start.line; } else { var actualType = funcParam.paramType[funcParam.paramNames.indexOf(paramName)]; if (actualType != expectedType) { throw "Error unxpected identifier " + paramName + " on line " + start.line; } } } /// /// Function: functionNode_webgl /// /// [INTERNAL] Takes in a function node, and does all the AST voodoo required to generate its respective webGL code. /// /// Parameter: /// inNode - {functionNode} The function node object /// /// Returns: /// the converted webGL function string /// function functionNode_webgl( inNode, _opt ) { gpu = inNode.gpu; opt = _opt; jsFunctionString = inNode.jsFunctionString; inNode.webglFunctionString_array = ast_generic( inNode.getJS_AST(), [], inNode ); inNode.webglFunctionString = webgl_regex_optimize( inNode.webglFunctionString_array.join("").trim() ); return inNode.webglFunctionString; } var DECODE32_ENCODE32 = /decode32\(\s+encode32\(/g; var ENCODE32_DECODE32 = /encode32\(\s+decode32\(/g; /// /// Function: webgl_regex_optimize /// /// [INTERNAL] Takes the near final webgl function string, and do regex search and replacments. /// For voodoo optimize out the following /// /// - decode32(encode32( /// - encode32(decode32( /// function webgl_regex_optimize( inStr ) { return inStr .replace(DECODE32_ENCODE32, "((") .replace(ENCODE32_DECODE32, "((") ; } /// the AST error, with its location. To throw /// /// @TODO: add location support fpr the AST error /// /// @param error the error message output /// @param ast the AST object where the error is /// @param funcParam FunctionNode, that tracks compilation state function ast_errorOutput(error, ast, funcParam) { console.error(error, ast, funcParam); return error; } /// Prases the abstract syntax tree, genericially to its respective function /// /// @param ast the AST object to parse /// @param retArr return array string /// @param funcParam FunctionNode, that tracks compilation state /// /// @returns the prased openclgl string array function ast_generic(ast, retArr, funcParam) { if(ast === null) { throw ast_errorOutput("NULL ast", ast, funcParam); } else { if (Array.isArray(ast)) { for (var i=0; i 0 ) { retArr.push(", "); } retArr.push( funcParam.paramType[i] ); retArr.push(" "); retArr.push("user_"); retArr.push( funcParam.paramNames[i] ); } } // Function opening retArr.push(") {\n"); // Body statement iteration for(var i=0; i 0) { retArr.push(","); } ast_generic(vardecNode.declarations[i], retArr, funcParam); } retArr.push(";"); return retArr; } function ast_VariableDeclarator(ivardecNode, retArr, funcParam) { ast_generic(ivardecNode.id, retArr, funcParam); if (ivardecNode.init !== null) { retArr.push("="); ast_generic(ivardecNode.init, retArr, funcParam); } return retArr; } function ast_IfStatement(ifNode, retArr, funcParam) { retArr.push("if("); ast_generic(ifNode.test, retArr, funcParam); retArr.push(")"); ast_generic(ifNode.consequent, retArr, funcParam); if (ifNode.alternate) { retArr.push("else "); ast_generic(ifNode.alternate, retArr, funcParam); } return retArr; } function ast_Break(brNode, retArr, funcParam) { retArr.push("break;\n"); return retArr; } function ast_Continue(crNode, retArr, funcParam) { retArr.push("continue;\n"); return retArr; } function ast_LogicalExpression(logNode, retArr, funcParam) { ast_generic(logNode.left, retArr, funcParam); ast_generic(logNode.operator, retArr, funcParam); ast_generic(logNode.right, retArr, funcParam); return retArr; } function ast_UpdateExpression(uNode, retArr, funcParam) { if(uNode.prefix) { retArr.push(uNode.operator); ast_generic(uNode.argument, retArr, funcParam); } else { ast_generic(uNode.argument, retArr, funcParam); retArr.push(uNode.operator); } return retArr; } function ast_UnaryExpression(uNode, retArr, funcParam) { if(uNode.prefix) { retArr.push(uNode.operator); ast_generic(uNode.argument, retArr, funcParam); } else { ast_generic(uNode.argument, retArr, funcParam); retArr.push(uNode.operator); } return retArr; } function ast_ThisExpression(tNode, retArr, funcParam) { retArr.push("this"); return retArr; } function ast_MemberExpression(mNode, retArr, funcParam) { if(mNode.computed) { if (mNode.object.type == "Identifier") { retArr.push("get("); ast_generic(mNode.object, retArr, funcParam); retArr.push(", vec2("); ast_generic(mNode.object, retArr, funcParam); retArr.push("Size[0],"); ast_generic(mNode.object, retArr, funcParam); retArr.push("Size[1]), vec3("); ast_generic(mNode.object, retArr, funcParam); retArr.push("Dim[0],"); ast_generic(mNode.object, retArr, funcParam); retArr.push("Dim[1],"); ast_generic(mNode.object, retArr, funcParam); retArr.push("Dim[2]"); retArr.push("), "); } else { ast_generic(mNode.object, retArr, funcParam); var last = retArr.pop(); retArr.push(","); } ast_generic(mNode.property, retArr, funcParam); retArr.push(")"); } else { // Unroll the member expression var unrolled = ast_MemberExpression_unroll(mNode); var unrolled_lc = unrolled.toLowerCase() if (unrolled_lc == "this.thread.x") { retArr.push('threadId.x'); } else if (unrolled_lc == "this.thread.y") { retArr.push('threadId.y'); } else if (unrolled_lc == "this.thread.z") { retArr.push('threadId.z'); } else if (unrolled_lc == "this.dimensions.x") { retArr.push('uOutputDim.x'); } else if (unrolled_lc == "this.dimensions.y") { retArr.push('uOutputDim.y'); } else if (unrolled_lc == "this.dimensions.z") { retArr.push('uOutputDim.z'); } else { retArr.push(unrolled); } } return retArr; } function ast_SequenceExpression(sNode, retArr, funcParam) { for (var i = 0; i < sNode.expressions.length; i++) { if (i > 0) { retArr.push(","); } ast_generic(sNode.expressions, retArr, funcParam); } return retArr; } /// Utility function for ast_CallExpression. /// /// Prases the abstract syntax tree, binary expression. /// /// @param ast the AST object to parse /// /// @returns {String} the function namespace call, unrolled function ast_MemberExpression_unroll(ast, funcParam) { if( ast.type == "Identifier" ) { return ast.name; } else if( ast.type == "ThisExpression" ) { return "this"; } if( ast.type == "MemberExpression" ) { if( ast.object && ast.property ) { return ( ast_MemberExpression_unroll( ast.object, funcParam ) + "." + ast_MemberExpression_unroll( ast.property, funcParam ) ); } } // Failure, unknown expression throw ast_errorOutput( "Unknown CallExpression_unroll", ast, funcParam ); } // The prefixes to use var jsMathPrefix = "Math."; var localPrefix = "this."; /// Prases the abstract syntax tree, binary expression /// /// @param ast the AST object to parse /// @param retArr return array string /// @param funcParam FunctionNode, that tracks compilation state /// /// @returns the appened retArr function ast_CallExpression(ast, retArr, funcParam) { if( ast.callee ) { // Get the full function call, unrolled var funcName = ast_MemberExpression_unroll(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 ); } // Register the function into the called registry if( funcParam.calledFunctions.indexOf(funcName) < 0 ) { funcParam.calledFunctions.push(funcName); } // Call the function retArr.push( funcName ); // Open arguments space retArr.push( "(" ); // Add the vars for(var i=0; i 0) { retArr.push(", "); } ast_generic(ast.arguments[i],retArr,funcParam); } // Close arguments space retArr.push( ")" ); return retArr; } // Failure, unknown expression throw ast_errorOutput( "Unknown CallExpression", ast, funcParam ); return retArr; } return functionNode_webgl; })();