Basic return statement, string conversion

Merge commit '40d7f264447ddc51b258ee82520963dc0ae34326' into eugene-prototype

Conflicts:
	src/js-to-webclgl.js
This commit is contained in:
Eugene Cheah 2016-01-23 22:46:16 +08:00
commit e5b491dcad
5 changed files with 365 additions and 21 deletions

139
src/js-regex.js Normal file
View File

@ -0,0 +1,139 @@
var GPU_regex = (function() {
String.prototype.replaceAll = function (find, replace) {
var str = this;
return str.replace(new RegExp(find, 'g'), replace);
};
webCLGL = new WebCLGL();
function clone(obj) {
if(obj === null || typeof(obj) !== 'object' || 'isActiveClone' in obj)
return obj;
var temp = obj.constructor(); // changed
for(var key in obj) {
if(Object.prototype.hasOwnProperty.call(obj, key)) {
obj['isActiveClone'] = null;
temp[key] = clone(obj[key]);
delete obj['isActiveClone'];
}
}
return temp;
}
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var ARGUMENT_NAMES = /([^\s,]+)/g;
function getParamNames(func) {
var fnStr = func.toString().replace(STRIP_COMMENTS, '');
var result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
if(result === null)
result = [];
return result;
}
/// Does simple validation of the provided function string if it is a function
/// this is a basic sanity testing before Jison conversion
///
/// @param funcStr the input function string
///
/// @returns boolean
function validateStringIsFunction( funcStr ) {
if( funcStr != null ) {
return (funcStr.slice(0, "function".length).toLowerCase() == "function")
}
return false;;
}
/// Does the core conversion of a basic Javascript function into a webclgl
/// and returns a callable function if valid
///
/// @param inputFunction The calling to perform the conversion
/// @param _threadDim The thread dim array configuration
/// @param _blockDim The block dim array configuration
/// @param paramObj The parameter object
///
/// @returns callable function if converted, else returns null
function regex( inputFunction, _threadDim, _blockDim ) {
var threadDim = clone(_threadDim);
var blockDim = clone(_blockDim);
while (threadDim.length < 3) {
threadDim.push(1);
}
while (blockDim.length < 3) {
blockDim.push(1);
}
var globalDim = [
threadDim[0] * blockDim[0],
threadDim[1] * blockDim[1],
threadDim[2] * blockDim[2]
];
var totalSize = globalDim[0] * globalDim[1] * globalDim[2];
var funcStr = inputFunction.toString();
var funcBody = funcStr.toString().match(/function[^{]+\{([\s\S]*)\}$/)[1];
funcBody = funcBody.replaceAll('var ', 'float ');
funcBody = funcBody.replaceAll('this.thread.x', '_x_');
funcBody = funcBody.replaceAll('return ', 'out_float = ');
var argNames = getParamNames(inputFunction);
var shaderCode = 'void main(';
for (var i=0; i<argNames.length; i++) {
shaderCode += 'float* ' + argNames[i];
if (i!=argNames.length-1) {
shaderCode += ', \n';
}
}
shaderCode += ') {\n';
shaderCode += ' vec2 _x_ = get_global_id();';
shaderCode += funcBody;
shaderCode += '}';
console.log(shaderCode);
//var buffer_A = webCLGL.createBuffer(_length, "FLOAT", offset);
return function() {
var argBuffers = [];
var offset = 0;
var resultBuffer = webCLGL.createBuffer(totalSize, "FLOAT", offset);
var argNorm = [];
for (var i=0; i<argNames.length; i++) {
argNorm[i] = arguments[i].map(function(x) {
return x / 65535.0;
});
}
for (var i=0; i<argNames.length; i++) {
argBuffers[i] = webCLGL.createBuffer(argNorm[i].length, "FLOAT", offset);
}
for (var i=0; i<argNames.length; i++) {
webCLGL.enqueueWriteBuffer(argBuffers[i], argNorm[i]);
}
var kernel = webCLGL.createKernel(shaderCode);
for (var i=0; i<argNames.length; i++) {
kernel.setKernelArg(i, argBuffers[i]);
}
webCLGL.enqueueNDRangeKernel(kernel, resultBuffer);
var result = webCLGL.enqueueReadBuffer_Float(resultBuffer);
result = Array.prototype.slice.call(result[0]);
result = result.map(function(x) {
return Math.round(x * 65535.0 * 100000.0) / 100000.0;
});
return result;
};
}
return regex;
})();

View File

@ -1,62 +1,223 @@
var GPU_jsStrToWebclglStr = (function() {
/// @returns the default state paramater set, used across the parser
function jison_defaultStateParam(funcStr, inParamObj) {
return {
// The source code rows array
src : funcStr,
srcArr : funcStr.split("\n"),
// The compiler parameter options
paramObj : inParamObj,
// Original main function naming
customMainFunctionName : null,
// Reserved namespace used for GPU.js code
reservedNamespace : "gpu_",
// Current function state, being processed
currentFunctionNamespace : null,
// Annoymous function number tracking
annoymousFunctionNumber : 0
};
}
/// Parses the source code string
///
/// @param funcStr the input function string
/// @param funcStr the input function string
/// @param stateParam the compiled state tracking
///
/// @returns the parsed json obj
function jison_parseFuncStr( funcStr ) {
function jison_parseFuncStr( funcStr, stateParam ) {
var mainObj = parser.parse( "var main = "+funcStr+";" );
if( mainObj == null ) {
throw "Failed to parse JS code via JISON";
}
// take out the function object, outside the main var declarations
return mainObj.body[0].declarations[0].init;
var mainAst = mainObj.body[0].declarations[0].init;
// Capture the original statment code
stateParam.customMainFunctionName = mainAst.id;
mainAst.id = "main";
return mainAst;
}
/// the AST error, with its location. To throw (@TODO add location support)
///
/// @param error the error message output
/// @param ast the AST object where the error is
/// @param srcArr the source code array (for better error tracing)
function ast_errorOutput(error, ast, srcArr) {
/// @param error the error message output
/// @param ast the AST object where the error is
/// @param stateParam the compiled state tracking
function ast_errorOutput(error, ast, stateParam) {
return error;
}
/// Prases the abstract syntax tree, genericially to its respective function
///
/// @param ast the AST object to parse
/// @param ast the AST object to parse
/// @param retArr return array string
/// @param stateParam the compiled state tracking
///
/// @returns the prased openclgl string array
function ast_generic(ast, retArr, paramObj) {
function ast_generic(ast, retArr, stateParam) {
switch(ast.type) {
case "FunctionExpression":
return ast_FunctionExpression(ast, retArr, paramObj);
return ast_FunctionExpression(ast, retArr, stateParam);
case "ReturnStatement":
return ast_ReturnStatement(ast, retArr, stateParam);
case "Literal":
return ast_Literal(ast, retArr, stateParam);
}
throw ast_errorOutput("Unknown ast type : "+ast.type);
throw ast_errorOutput("Unknown ast type : "+ast.type, ast, stateParam);
}
/// Prases the abstract syntax tree, to its named function
///
/// @param ast the AST object to parse
/// @param retArr return array string
/// @param stateParam the compiled state tracking
///
/// @returns the appened retArr
function ast_FunctionExpression(ast, retArr, stateParam) {
// Setup the main function
if( ast.id == "main" ) {
retArr.push("void main(");
} else {
// @TODO : Suuport return type values
retArr.push("void ");
// Its an Annoymous function, handle it
if(ast.id == null) {
ast.id = stateParam.reservedNamespace+"a"+stateParam.annoymousFunctionNumber;
stateParam.annoymousFunctionNumber++;
}
//Function name
retArr.push(ast.id);
retArr.push("(");
}
stateParam.currentFunctionNamespace = ast.id;
// @TODO : Handle parameters tokens
// retArr.push(float* a, float* b)
// Function opening bracket
retArr.push(") { ");
// Body statement iteration
for(var i=0; i<ast.body.length; ++i) {
ast_generic(ast.body[i], retArr, stateParam);
}
// Function closing
retArr.push("}");
return retArr;
}
/// Prases the abstract syntax tree, to return function
///
/// @param ast the AST object to parse
/// @param retArr return array string
/// @param stateParam the compiled state tracking
///
/// @returns the appened retArr
function ast_ReturnStatement(ast, retArr, stateParam) {
if( stateParam.currentFunctionNamespace == "main" ) {
retArr.push("out_float = ");
ast_generic(ast.argument, retArr, stateParam);
retArr.push("; return; ");
} else {
throw ast_errorOutput(
"Non main function return, is not supported : "+stateParam.currentFunctionNamespace,
ast, stateParam
);
}
return retArr;
}
/// Prases the abstract syntax tree, literal value
///
/// @param ast the AST object to parse
/// @param retArr return array string
/// @param stateParam the compiled state tracking
///
/// @returns the appened retArr
function ast_Literal(ast, retArr, stateParam) {
// Reject non numeric literals
if( isNaN(ast.value) ) {
throw ast_errorOutput(
"Non-numeric literal not supported : "+ast.value,
ast, stateParam
);
}
// 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;
}
/*
/// Prases the abstract syntax tree, genericially to its respective function
///
/// @param ast the AST object to parse
///
/// @returns the prased openclgl string
function ast_FunctionExpression(ast, retArr, paramObj) {
console.log(arguments);
function ast_ForStatement(forNode, retArr) {
if (forNode.type != "ForStatement") {
throw "error";
}
retArr.push("for (");
ast_generic(forNode.init, retArr);
ast_generic(forNode.test, retArr);
ast_generic(forNode.update, retArr);
retArr.push(")");
ast_generic(forNode.body, retArr);
return retArr;
}
function ast_ExpressionStatement(expNode, retArr) {
if (expNode.type != "ExpressionStatement") {
throw "error";
}
ast_generic(expNode.expression, retArr);
retArr.push(";");
return retArr;
}
function ast_AssignmentExpression(assNode, retArr) {
if (assNode.type != "AssignmentExpression") {
throw "error";
}
retArr.push("float");
ast_generic(assNode.left, retArr);
retArr.push(assNode.operator);
ast_generic(assNode.right, retArr);
}
*/
/// The function string to
///
///
/// The function string to openslgl code generator
function jsStrToWebclglStr( funcStr, _threadDim, _blockDim, paramObj ) {
var funcStrLines = funcStr.split("\n");
var astOutputObj = jison_parseFuncStr(funcStr);
var retArr = ast_generic(astOutputObj, [], paramObj);
return retArr.join(" ");
var stateObj = jison_defaultStateParam(funcStr, paramObj);
var astOutputObj = jison_parseFuncStr(funcStr, stateObj);
var retArr = ast_generic( astOutputObj, [], stateObj );
return retArr.join("");
}
return jsStrToWebclglStr;

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>GPU.JS : Basic Sum GPU AB</title>
<link rel="stylesheet" href="../lib/qunit-1.20.0.css">
<!-- jison -->
<script src="../../lib/jison.js"></script>
<!-- webclgl -->
<script src="../../lib/WebCLGLUtils.class.js"></script>
<script src="../../lib/WebCLGLBuffer.class.js"></script>
<script src="../../lib/WebCLGLBufferItem.class.js"></script>
<script src="../../lib/WebCLGLKernel.class.js"></script>
<script src="../../lib/WebCLGLKernelProgram.class.js"></script>
<script src="../../lib/WebCLGL.class.js"></script>
<!-- gpu.js scripts -->
<script src="../../src/js-fallback.js"></script>
<script src="../../src/js-to-webclgl.js"></script>
<script src="../../src/js-regex.js"></script>
<script src="../../src/gpu.js"></script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script src="../lib/qunit-1.20.0.js"></script>
<script src="../src/basic_sum_AB.js"></script>
</body>
</html>

View File

@ -12,7 +12,11 @@ function basic_return( assert, mode ) {
}
QUnit.test( "basic_return (string)", function( assert ) {
assert.equal( GPU._jsStrToWebclglStr( "function() { return 42.0; }" ), "", "Basic return string conversion" );
assert.equal(
GPU._jsStrToWebclglStr( "function() { return 42.0; }" ),
"void main() { out_float = 42.0; return; }",
"Basic return string conversion"
);
});
QUnit.test( "basic_return (auto)", function( assert ) {

View File

@ -23,3 +23,12 @@ QUnit.test( "basic_sum_AB (GPU)", function( assert ) {
QUnit.test( "basic_sum_AB (CPU)", function( assert ) {
basic_sum_AB_test(assert, "cpu");
});
/*
// EXAMPLE GLSL
void main(float* a, float* b) {
vec2 _x_ = get_global_id();
float ret = a[_x_] + b[_x_];
out_float = ret;
}
*/