'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var FunctionNodeBase = require('../function-node-base');
var utils = require('../../core/utils');
// Closure capture for the ast function, prevent collision with existing AST functions
// The prefixes to use
var jsMathPrefix = 'Math.';
var localPrefix = 'this.';
var constantsPrefix = 'this.constants.';
var DECODE32_ENCODE32 = /decode32\(\s+encode32\(/g;
var ENCODE32_DECODE32 = /encode32\(\s+decode32\(/g;
// these debugs were hugely usefull...
// TODO: optimise out - webpack/babel options? maybe some generic logging support in core/utils?
// const debugLog = console.log
var debugLog = function debugLog() {};
/**
* @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 = function (_FunctionNodeBase) {
_inherits(WebGLFunctionNode, _FunctionNodeBase);
function WebGLFunctionNode() {
_classCallCheck(this, WebGLFunctionNode);
return _possibleConstructorReturn(this, (WebGLFunctionNode.__proto__ || Object.getPrototypeOf(WebGLFunctionNode)).apply(this, arguments));
}
_createClass(WebGLFunctionNode, [{
key: 'generate',
value: function generate() {
if (this.debug) {
debugLog(this);
}
if (this.prototypeOnly) {
return this.astFunctionPrototype(this.getJsAST(), []).join('').trim();
} else {
this.functionStringArray = this.astGeneric(this.getJsAST(), []);
}
this.functionString = webGlRegexOptimize(this.functionStringArray.join('').trim());
return this.functionString;
}
/**
* @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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astFunctionDeclaration',
value: function astFunctionDeclaration(ast, retArr) {
this.builder.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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astFunctionPrototype',
value: function astFunctionPrototype(ast, retArr) {
// Setup function return type and name
if (this.isRootKernel || this.isSubKernel) {
return retArr;
}
var returnType = this.returnType;
var type = typeMap[returnType];
if (!type) {
throw new Error('unknown type ' + returnType);
}
retArr.push(type);
retArr.push(' ');
retArr.push(this.functionName);
retArr.push('(');
// Arguments handling
for (var i = 0; i < this.paramNames.length; ++i) {
if (i > 0) {
retArr.push(', ');
}
retArr.push(this.paramTypes[i]);
retArr.push(' ');
retArr.push('user_');
retArr.push(this.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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astFunctionExpression',
value: function astFunctionExpression(ast, retArr) {
// Setup function return type and name
if (this.isRootKernel) {
retArr.push('void');
this.kernalAst = ast;
} else {
var returnType = this.returnType;
var type = typeMap[returnType];
if (!type) {
throw new Error('unknown type ' + returnType);
}
retArr.push(type);
}
retArr.push(' ');
retArr.push(this.functionName);
retArr.push('(');
if (!this.isRootKernel) {
// Arguments handling
for (var i = 0; i < this.paramNames.length; ++i) {
var paramName = this.paramNames[i];
if (i > 0) {
retArr.push(', ');
}
var paramType = this.getParamType(paramName);
var _type = typeMap[paramType];
if (!_type) {
throw new Error('unknown type ' + paramType);
}
retArr.push(_type);
retArr.push(' ');
retArr.push('user_');
retArr.push(paramName);
}
}
// Function opening
retArr.push(') {\n');
// Body statement iteration
for (var _i = 0; _i < ast.body.body.length; ++_i) {
this.astGeneric(ast.body.body[_i], retArr);
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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astReturnStatement',
value: function astReturnStatement(ast, retArr) {
if (this.isRootKernel) {
retArr.push('kernelResult = ');
this.astGeneric(ast.argument, retArr);
retArr.push(';');
retArr.push('return;');
} else if (this.isSubKernel) {
retArr.push(this.functionName + 'Result = ');
this.astGeneric(ast.argument, retArr);
retArr.push(';');
retArr.push('return ' + this.functionName + 'Result;');
} else {
retArr.push('return ');
this.astGeneric(ast.argument, retArr);
retArr.push(';');
}
//throw this.astErrorOutput(
// 'Non main function return, is not supported : '+this.currentFunctionNamespace,
// ast
//);
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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astLiteral',
value: function astLiteral(ast, retArr) {
// Reject non numeric literals
if (isNaN(ast.value)) {
throw this.astErrorOutput('Non-numeric literal not supported : ' + ast.value, ast);
}
// Push the literal value as a float/int
retArr.push(ast.value);
var inGetParams = this.isState('in-get-call-parameters');
// If it was an int, node made a float if necessary
if (Number.isInteger(ast.value)) {
if (!inGetParams) {
retArr.push('.0');
}
} else if (inGetParams) {
// or cast to an int as we are addressing an input array
retArr.pop();
retArr.push('int(');
retArr.push(ast.value);
retArr.push(')');
}
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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astBinaryExpression',
value: function astBinaryExpression(ast, retArr) {
var inGetParams = this.isState('in-get-call-parameters');
if (inGetParams) {
this.pushState('not-in-get-call-parameters');
retArr.push('int');
}
retArr.push('(');
if (ast.operator === '%') {
retArr.push('mod(');
this.astGeneric(ast.left, retArr);
retArr.push(',');
this.astGeneric(ast.right, retArr);
retArr.push(')');
} else if (ast.operator === '===') {
this.astGeneric(ast.left, retArr);
retArr.push('==');
this.astGeneric(ast.right, retArr);
} else if (ast.operator === '!==') {
this.astGeneric(ast.left, retArr);
retArr.push('!=');
this.astGeneric(ast.right, retArr);
} else if (this.fixIntegerDivisionAccuracy && ast.operator === '/') {
retArr.push('div_with_int_check(');
this.astGeneric(ast.left, retArr);
retArr.push(', ');
this.astGeneric(ast.right, retArr);
retArr.push(')');
} else {
this.astGeneric(ast.left, retArr);
retArr.push(ast.operator);
this.astGeneric(ast.right, retArr);
}
retArr.push(')');
if (inGetParams) {
this.popState('not-in-get-call-parameters');
}
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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astIdentifierExpression',
value: function astIdentifierExpression(idtNode, retArr) {
if (idtNode.type !== 'Identifier') {
throw this.astErrorOutput('IdentifierExpression - not an Identifier', idtNode);
}
// do we need to cast addressing vales to float?
var castFloat = !this.isState('in-get-call-parameters');
switch (idtNode.name) {
case 'gpu_threadX':
castFloat && retArr.push('float(');
retArr.push('threadId.x');
castFloat && retArr.push(')');
break;
case 'gpu_threadY':
castFloat && retArr.push('float(');
retArr.push('threadId.y');
castFloat && retArr.push(')');
break;
case 'gpu_threadZ':
castFloat && retArr.push('float(');
retArr.push('threadId.z');
castFloat && retArr.push(')');
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;
case 'Infinity':
// https://stackoverflow.com/a/47543127/1324039
retArr.push('3.402823466e+38');
break;
default:
var userParamName = this.getUserParamName(idtNode.name);
if (userParamName !== null) {
this.pushParameter(retArr, 'user_' + userParamName);
} else {
this.pushParameter(retArr, '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
*
* @returns {Array} the parsed webgl string
*/
}, {
key: 'astForStatement',
value: function astForStatement(forNode, retArr) {
if (forNode.type !== 'ForStatement') {
throw this.astErrorOutput('Invalid for statment', forNode);
}
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);
this.astGeneric(forNode.test.left, retArr);
retArr.push(forNode.test.operator);
retArr.push('LOOP_MAX');
retArr.push(';');
this.astGeneric(forNode.update, retArr);
retArr.push(')');
retArr.push('{\n');
retArr.push('if (');
this.astGeneric(forNode.test.left, retArr);
retArr.push(forNode.test.operator);
this.astGeneric(forNode.test.right, retArr);
retArr.push(') {\n');
if (forNode.body.type === 'BlockStatement') {
for (var i = 0; i < forNode.body.body.length; i++) {
this.astGeneric(forNode.body.body[i], retArr);
}
} else {
this.astGeneric(forNode.body, retArr);
}
retArr.push('\n} else {\n');
retArr.push('break;\n');
retArr.push('}\n');
retArr.push('}\n');
return retArr;
} else {
var declarations = JSON.parse(JSON.stringify(forNode.init.declarations));
var updateArgument = forNode.update.argument;
if (!Array.isArray(declarations) || declarations.length < 1) {
debugLog(this.jsFunctionString);
throw new Error('Error: Incompatible for loop declaration');
}
if (declarations.length > 1) {
var initArgument = null;
for (var _i2 = 0; _i2 < declarations.length; _i2++) {
var declaration = declarations[_i2];
if (declaration.id.name === updateArgument.name) {
initArgument = declaration;
declarations.splice(_i2, 1);
} else {
retArr.push('float ');
this.astGeneric(declaration, retArr);
retArr.push(';');
}
}
retArr.push('for (float ');
this.astGeneric(initArgument, retArr);
retArr.push(';');
} else {
retArr.push('for (');
this.astGeneric(forNode.init, retArr);
}
this.astGeneric(forNode.test, retArr);
retArr.push(';');
this.astGeneric(forNode.update, retArr);
retArr.push(')');
this.astGeneric(forNode.body, retArr);
return retArr;
}
}
throw this.astErrorOutput('Invalid for statement', forNode);
}
/**
* @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
*
* @returns {Array} the parsed webgl string
*/
}, {
key: 'astWhileStatement',
value: function astWhileStatement(whileNode, retArr) {
if (whileNode.type !== 'WhileStatement') {
throw this.astErrorOutput('Invalid while statment', whileNode);
}
retArr.push('for (float i = 0.0; i < LOOP_MAX; i++) {');
retArr.push('if (');
this.astGeneric(whileNode.test, retArr);
retArr.push(') {\n');
this.astGeneric(whileNode.body, retArr);
retArr.push('} else {\n');
retArr.push('break;\n');
retArr.push('}\n');
retArr.push('}\n');
return retArr;
}
/**
* @memberOf WebGLFunctionNode#
* @function
* @name astWhileStatement
*
* @desc Parses the abstract syntax tree for *do while* loop
*
*
* @param {Object} doWhileNode - An ast Node
* @param {Array} retArr - return array string
*
* @returns {Array} the parsed webgl string
*/
}, {
key: 'astDoWhileStatement',
value: function astDoWhileStatement(doWhileNode, retArr) {
if (doWhileNode.type !== 'DoWhileStatement') {
throw this.astErrorOutput('Invalid while statment', doWhileNode);
}
retArr.push('for (float i = 0.0; i < LOOP_MAX; i++) {');
this.astGeneric(doWhileNode.body, retArr);
retArr.push('if (!');
this.astGeneric(doWhileNode.test, retArr);
retArr.push(') {\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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astAssignmentExpression',
value: function astAssignmentExpression(assNode, retArr) {
if (assNode.operator === '%=') {
this.astGeneric(assNode.left, retArr);
retArr.push('=');
retArr.push('mod(');
this.astGeneric(assNode.left, retArr);
retArr.push(',');
this.astGeneric(assNode.right, retArr);
retArr.push(')');
} else {
this.astGeneric(assNode.left, retArr);
retArr.push(assNode.operator);
this.astGeneric(assNode.right, retArr);
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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astEmptyStatement',
value: function astEmptyStatement(eNode, retArr) {
//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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astBlockStatement',
value: function astBlockStatement(bNode, retArr) {
retArr.push('{\n');
for (var i = 0; i < bNode.body.length; i++) {
this.astGeneric(bNode.body[i], retArr);
}
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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astExpressionStatement',
value: function astExpressionStatement(esNode, retArr) {
this.astGeneric(esNode.expression, retArr);
retArr.push(';');
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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astVariableDeclaration',
value: function astVariableDeclaration(vardecNode, retArr) {
for (var i = 0; i < vardecNode.declarations.length; i++) {
var declaration = vardecNode.declarations[i];
if (i > 0) {
retArr.push(',');
}
var retDeclaration = [];
this.astGeneric(declaration, retDeclaration);
var declarationType = 'Number';
if (i === 0) {
var init = declaration.init;
if (init) {
if (init.object) {
if (init.object.type === 'MemberExpression' && init.object.object) {
// param[]
if (init.object.object.type === 'Identifier') {
var _type2 = this.getParamType(init.object.object.name);
declarationType = typeLookupMap[_type2];
}
// param[][]
else if (init.object.object.object && init.object.object.object.type === 'Identifier') {
var _type3 = this.getParamType(init.object.object.object.name);
declarationType = typeLookupMap[_type3];
}
// this.constants.param[]
else if (init.object.object.object.object && init.object.object.object.object.type === 'ThisExpression' && init.object.object.object.property.name === 'constants') {
var _type4 = this.getConstantType(init.object.object.property.name);
declarationType = typeLookupMap[_type4];
}
// this.constants.param[][]
else if (init.object.object.object.object.object && init.object.object.object.object.object.type === 'ThisExpression' && init.object.object.object.object.property.name === 'constants') {
var _type5 = this.getConstantType(init.object.object.object.property.name);
declarationType = typeLookupMap[_type5];
}
}
if (!declarationType) {
throw new Error('unknown lookup type ' + typeLookupMap);
}
} else {
if (init.name && this.declarations[init.name]) {
declarationType = this.declarations[init.name];
} else if (init.type === 'ArrayExpression') {
declarationType = 'Array(' + init.elements.length + ')';
} else if (init.type === 'CallExpression') {
var node = this.builder.nodeMap[init.callee.name];
if (node && node.returnType) {
declarationType = node.returnType;
}
}
}
}
var type = typeMap[declarationType];
if (!type) {
throw new Error('type ' + declarationType + ' not handled');
}
retArr.push(type + ' ');
}
this.declarations[declaration.id.name] = declarationType;
retArr.push.apply(retArr, retDeclaration);
}
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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astVariableDeclarator',
value: function astVariableDeclarator(ivardecNode, retArr) {
this.astGeneric(ivardecNode.id, retArr);
if (ivardecNode.init !== null) {
retArr.push('=');
this.astGeneric(ivardecNode.init, retArr);
}
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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astIfStatement',
value: function astIfStatement(ifNode, retArr) {
retArr.push('if (');
this.astGeneric(ifNode.test, retArr);
retArr.push(')');
if (ifNode.consequent.type === 'BlockStatement') {
this.astGeneric(ifNode.consequent, retArr);
} else {
retArr.push(' {\n');
this.astGeneric(ifNode.consequent, retArr);
retArr.push('\n}\n');
}
if (ifNode.alternate) {
retArr.push('else ');
if (ifNode.alternate.type === 'BlockStatement') {
this.astGeneric(ifNode.alternate, retArr);
} else {
retArr.push(' {\n');
this.astGeneric(ifNode.alternate, retArr);
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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astBreakStatement',
value: function astBreakStatement(brNode, retArr) {
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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astContinueStatement',
value: function astContinueStatement(crNode, retArr) {
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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astLogicalExpression',
value: function astLogicalExpression(logNode, retArr) {
retArr.push('(');
this.astGeneric(logNode.left, retArr);
retArr.push(logNode.operator);
this.astGeneric(logNode.right, retArr);
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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astUpdateExpression',
value: function astUpdateExpression(uNode, retArr) {
if (uNode.prefix) {
retArr.push(uNode.operator);
this.astGeneric(uNode.argument, retArr);
} else {
this.astGeneric(uNode.argument, retArr);
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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astUnaryExpression',
value: function astUnaryExpression(uNode, retArr) {
if (uNode.prefix) {
retArr.push(uNode.operator);
this.astGeneric(uNode.argument, retArr);
} else {
this.astGeneric(uNode.argument, retArr);
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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astThisExpression',
value: function astThisExpression(tNode, retArr) {
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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astMemberExpression',
value: function astMemberExpression(mNode, retArr) {
debugLog("[in] astMemberExpression " + mNode.object.type);
if (mNode.computed) {
if (mNode.object.type === 'Identifier' || mNode.object.type === 'MemberExpression' &&
// mNode.object.object &&
mNode.object.object.object && mNode.object.object.object.type === 'ThisExpression' && mNode.object.object.property.name === 'constants') {
// Working logger
var reqName = mNode.object.name;
var funcName = this.functionName || 'kernel';
var assumeNotTexture = false;
// Possibly an array request - handle it as such
if (this.paramNames) {
var idx = this.paramNames.indexOf(reqName);
if (idx >= 0 && this.paramTypes[idx] === 'Number') {
assumeNotTexture = true;
}
}
debugLog("- astMemberExpression " + reqName + " " + funcName);
if (assumeNotTexture) {
// Get from array
this.astGeneric(mNode.object, retArr);
retArr.push('[int(');
this.astGeneric(mNode.property, retArr);
retArr.push(')]');
} else {
var isInGetParams = this.isState('in-get-call-parameters');
var multiMemberExpression = this.isState('multi-member-expression');
if (multiMemberExpression) {
this.popState('multi-member-expression');
}
this.pushState('not-in-get-call-parameters');
// This normally refers to the global read only input vars
var variableType = null;
if (mNode.object.name) {
if (this.declarations[mNode.object.name]) {
variableType = this.declarations[mNode.object.name];
} else {
variableType = this.getParamType(mNode.object.name);
}
} else if (mNode.object && mNode.object.object && mNode.object.object.object && mNode.object.object.object.type === 'ThisExpression') {
variableType = this.getConstantType(mNode.object.property.name);
}
switch (variableType) {
case 'Array(2)':
case 'Array(3)':
case 'Array(4)':
// Get from local vec4
this.astGeneric(mNode.object, retArr);
retArr.push('[');
retArr.push(mNode.property.raw);
retArr.push(']');
if (multiMemberExpression) {
this.popState('not-in-get-call-parameters');
}
break;
case 'HTMLImageArray':
// Get from image
retArr.push('getImage3D(');
this.astGeneric(mNode.object, retArr);
retArr.push(', ivec2(');
this.astGeneric(mNode.object, retArr);
retArr.push('Size[0],');
this.astGeneric(mNode.object, retArr);
retArr.push('Size[1]), ivec3(');
this.astGeneric(mNode.object, retArr);
retArr.push('Dim[0],');
this.astGeneric(mNode.object, retArr);
retArr.push('Dim[1],');
this.astGeneric(mNode.object, retArr);
retArr.push('Dim[2]');
retArr.push('), ');
this.popState('not-in-get-call-parameters');
this.pushState('in-get-call-parameters');
this.astGeneric(mNode.property, retArr);
if (!multiMemberExpression) {
this.popState('in-get-call-parameters');
}
retArr.push(')');
break;
case 'ArrayTexture(4)':
case 'HTMLImage':
// Get from image
retArr.push('getImage2D(');
this.astGeneric(mNode.object, retArr);
retArr.push(', ivec2(');
this.astGeneric(mNode.object, retArr);
retArr.push('Size[0],');
this.astGeneric(mNode.object, retArr);
retArr.push('Size[1]), ivec3(');
this.astGeneric(mNode.object, retArr);
retArr.push('Dim[0],');
this.astGeneric(mNode.object, retArr);
retArr.push('Dim[1],');
this.astGeneric(mNode.object, retArr);
retArr.push('Dim[2]');
retArr.push('), ');
this.popState('not-in-get-call-parameters');
this.pushState('in-get-call-parameters');
this.astGeneric(mNode.property, retArr);
if (!multiMemberExpression) {
this.popState('in-get-call-parameters');
}
retArr.push(')');
break;
default:
// Get from texture
if (isInGetParams) {
retArr.push('int(');
}
retArr.push('get(');
this.astGeneric(mNode.object, retArr);
retArr.push(', ivec2(');
this.astGeneric(mNode.object, retArr);
retArr.push('Size[0],');
this.astGeneric(mNode.object, retArr);
retArr.push('Size[1]), ivec3(');
this.astGeneric(mNode.object, retArr);
retArr.push('Dim[0],');
this.astGeneric(mNode.object, retArr);
retArr.push('Dim[1],');
this.astGeneric(mNode.object, retArr);
retArr.push('Dim[2]');
retArr.push('), ');
this.astGeneric(mNode.object, retArr);
retArr.push('BitRatio');
retArr.push(', ');
this.popState('not-in-get-call-parameters');
this.pushState('in-get-call-parameters');
this.astGeneric(mNode.property, retArr);
if (!multiMemberExpression) {
this.popState('in-get-call-parameters');
}
retArr.push(')');
if (isInGetParams) {
retArr.push(')');
}
break;
}
}
} else {
debugLog("- astMemberExpression obj:", mNode.object);
var stateStackDepth = this.states.length;
var startedInGetParamsState = this.isState('in-get-call-parameters');
if (!startedInGetParamsState) {
this.pushState('multi-member-expression');
}
this.astGeneric(mNode.object, retArr);
if (this.isState('multi-member-expression')) {
this.popState('multi-member-expression');
}
var changedGetParamsState = !startedInGetParamsState && this.isState('in-get-call-parameters');
var last = retArr.pop();
retArr.push(',');
debugLog("- astMemberExpression prop:", mNode.property);
var shouldPopParamState = this.isState('should-pop-in-get-call-parameters');
if (shouldPopParamState) {
// go back to in-get-call-parameters state
this.popState('should-pop-in-get-call-parameters');
}
this.astGeneric(mNode.property, retArr);
retArr.push(last);
if (changedGetParamsState) {
// calling memberExpression should pop...
this.pushState('should-pop-in-get-call-parameters');
} else if (shouldPopParamState) {
// do the popping!
this.popState('in-get-call-parameters');
}
}
} else {
// Unroll the member expression
var unrolled = this.astMemberExpressionUnroll(mNode);
var unrolled_lc = unrolled.toLowerCase();
debugLog("- astMemberExpression unrolled:", unrolled);
// Its a constant, remove this.constants.
if (unrolled.indexOf(constantsPrefix) === 0) {
unrolled = 'constants_' + unrolled.slice(constantsPrefix.length);
}
// do we need to cast addressing vales to float?
var castFloat = !this.isState('in-get-call-parameters');
switch (unrolled_lc) {
case 'this.thread.x':
castFloat && retArr.push('float(');
retArr.push('threadId.x');
castFloat && retArr.push(')');
break;
case 'this.thread.y':
castFloat && retArr.push('float(');
retArr.push('threadId.y');
castFloat && retArr.push(')');
break;
case 'this.thread.z':
castFloat && retArr.push('float(');
retArr.push('threadId.z');
castFloat && retArr.push(')');
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:
if (mNode.object && mNode.object.name && this.declarations[mNode.object.name]) {
retArr.push('user_');
}
retArr.push(unrolled);
}
}
debugLog("[out] astMemberExpression " + mNode.object.type);
return retArr;
}
}, {
key: 'astSequenceExpression',
value: function astSequenceExpression(sNode, retArr) {
for (var i = 0; i < sNode.expressions.length; i++) {
if (i > 0) {
retArr.push(',');
}
this.astGeneric(sNode.expressions, retArr);
}
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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astCallExpression',
value: function astCallExpression(ast, retArr) {
if (ast.callee) {
// Get the full function call, unrolled
var 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 (this.calledFunctions.indexOf(funcName) < 0) {
this.calledFunctions.push(funcName);
}
if (!this.hasOwnProperty('funcName')) {
this.calledFunctionsArguments[funcName] = [];
}
var functionArguments = [];
this.calledFunctionsArguments[funcName].push(functionArguments);
// Call the function
retArr.push(funcName);
// Open arguments space
retArr.push('(');
// Add the vars
for (var i = 0; i < ast.arguments.length; ++i) {
var argument = ast.arguments[i];
if (i > 0) {
retArr.push(', ');
}
this.astGeneric(argument, retArr);
if (argument.type === 'Identifier') {
var paramIndex = this.paramNames.indexOf(argument.name);
if (paramIndex === -1) {
functionArguments.push(null);
} else {
functionArguments.push({
name: argument.name,
type: this.paramTypes[paramIndex] || 'Number'
});
}
} else {
functionArguments.push(null);
}
}
// Close arguments space
retArr.push(')');
return retArr;
}
// Failure, unknown expression
throw this.astErrorOutput('Unknown CallExpression', ast);
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
*
* @returns {Array} the append retArr
*/
}, {
key: 'astArrayExpression',
value: function astArrayExpression(arrNode, retArr) {
var arrLen = arrNode.elements.length;
retArr.push('vec' + arrLen + '(');
for (var i = 0; i < arrLen; ++i) {
if (i > 0) {
retArr.push(', ');
}
var subNode = arrNode.elements[i];
this.astGeneric(subNode, retArr);
}
retArr.push(')');
return retArr;
// // Failure, unknown expression
// throw this.astErrorOutput(
// 'Unknown ArrayExpression',
// arrNode
//);
}
/**
* @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
*
*/
}, {
key: 'getFunctionPrototypeString',
value: function getFunctionPrototypeString() {
if (this.webGlFunctionPrototypeString) {
return this.webGlFunctionPrototypeString;
}
return this.webGlFunctionPrototypeString = this.generate();
}
}, {
key: 'build',
value: function build() {
return this.getFunctionPrototypeString().length > 0;
}
}]);
return WebGLFunctionNode;
}(FunctionNodeBase);
var typeMap = {
'Array': 'sampler2D',
'Array(2)': 'vec2',
'Array(3)': 'vec3',
'Array(4)': 'vec4',
'Array2D': 'sampler2D',
'Array3D': 'sampler2D',
'Float': 'float',
'Input': 'sampler2D',
'Integer': 'float',
'Number': 'float',
'NumberTexture': 'sampler2D',
'ArrayTexture(4)': 'sampler2D'
};
var typeLookupMap = {
'Array': 'Number',
'Array2D': 'Number',
'Array3D': 'Number',
'HTMLImage': 'Array(4)',
'HTMLImageArray': 'Array(4)',
'NumberTexture': 'Number',
'ArrayTexture(4)': 'Array(4)'
};
/**
* @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, '((');
}