blob: 5f80726f8fa5ca1c0f10d4156974d5b443bb4673 [file] [log] [blame]
/*jslint vars: true , plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global $, bm_eventDispatcher, esprima, escodegen*/
var bm_expressionHelper = (function () {
'use strict';
var ob = {};
var options = {
tokens: true,
range: true
};
var expressionStr;
var pendingBodies = [], doneBodies = [];
function spliceSlice(str, index, count, add) {
return str.slice(0, index) + (add || "") + str.slice(index + count);
}
function addReturnStatement(expression) {
var parsed = esprima.parse(expression, options);
var body = parsed.body;
var lastRange = body[body.length - 1].range;
return spliceSlice(expression, lastRange[0], 0, 'var $bm_rt = ');
}
function includeUndeclaredVariables() {
doneBodies.sort(function (a, b) {
return parseInt(b.p, 10) - parseInt(a.p, 10);
});
var i, len = doneBodies.length;
var declarationStr = '';
for (i = 0; i < len; i += 1) {
if (doneBodies[i].u.length) {
declarationStr = 'var ' + doneBodies[i].u.join(',') + ';';
expressionStr = spliceSlice(expressionStr, doneBodies[i].p, 0, declarationStr);
}
}
}
function exportNextBody() {
if (pendingBodies.length === 0) {
includeUndeclaredVariables();
} else {
var next = pendingBodies.shift();
var preDeclared = [];
preDeclared = preDeclared.concat(next.pre);
preDeclared = preDeclared.concat(next.d);
preDeclared = preDeclared.concat(next.u);
findUndeclaredVariables(next.body, next.pos, preDeclared);
}
}
function findUndeclaredVariables(body, pos, predeclared, declared, undeclared, isContinuation) {
function addAssignment(expression) {
var variableName;
if (expression.left && expression.left.name) {
variableName = expression.left.name;
var i = 0, len = declared.length;
while (i < len) {
if (declared[i] === variableName) {
return;
}
i += 1;
}
i = 0;
len = declared.length;
while (i < len) {
if (undeclared[i] === variableName) {
return;
}
i += 1;
}
undeclared.push(variableName);
}
}
function addSequenceExpressions(expressions) {
var i, len = expressions.length;
for (i = 0; i < len; i += 1) {
if (expressions[i].type === 'AssignmentExpression') {
addAssignment(expressions[i]);
}
}
}
function addDeclaredVariable(variableName) {
var i = 0, len = declared.length;
while (i < len) {
if (declared[i] === variableName) {
return;
}
i += 1;
}
declared.push(variableName);
}
if (!declared) {
declared = [];
}
if (!undeclared) {
undeclared = [];
}
var i, len;
if (predeclared) {
len = predeclared.length;
for (i = 0; i < len; i += 1) {
declared.push(predeclared[i]);
}
}
len = body.length;
var j, jLen, expression, declarations;
for (i = 0; i < len; i += 1) {
if (body[i].type === 'VariableDeclaration') {
declarations = body[i].declarations;
jLen = declarations.length;
for (j = 0; j < jLen; j += 1) {
if (declarations[j].type === 'VariableDeclarator') {
if (declarations[j].id && declarations[j].id.name) {
addDeclaredVariable(declarations[j].id.name);
}
}
}
} else if (body[i].type === 'ExpressionStatement') {
expression = body[i].expression;
if (expression.type === 'AssignmentExpression') {
addAssignment(expression);
} else if (expression.type === 'SequenceExpression') {
addSequenceExpressions(expression.expressions);
}
//
} else if (body[i].type === 'ForStatement') {
if (body[i].init) {
if (body[i].init.type === 'SequenceExpression') {
addSequenceExpressions(body[i].init.expressions);
} else if (body[i].init.type === 'AssignmentExpression') {
addAssignment(body[i].init);
}
}
if (body[i].body) {
if (body[i].body.type === 'BlockStatement') {
findUndeclaredVariables(body[i].body.body, 0, null, declared, undeclared, true);
} else if (body[i].body.type === 'ExpressionStatement') {
expression = body[i].body.expression;
if (expression.type === 'AssignmentExpression') {
addAssignment(expression);
} else if (expression.type === 'SequenceExpression') {
addSequenceExpressions(expression.expressions);
}
//addAssignment(body[i].body);
}
}
} else if (body[i].type === 'FunctionDeclaration') {
if (body[i].body && body[i].body.type === 'BlockStatement') {
var p = [];
if (body[i].params) {
jLen = body[i].params.length;
for (j = 0; j < jLen; j += 1) {
p.push(body[i].params[j].name);
}
}
pendingBodies.push({body: body[i].body.body, d: declared, u: undeclared, pre: p, pos: body[i].body.range[0] + 1});
}
}
}
if (!isContinuation) {
doneBodies.push({u: undeclared, p: pos});
exportNextBody();
}
}
function searchUndeclaredVariables() {
var parsed = esprima.parse(expressionStr, options);
var body = parsed.body;
pendingBodies.push({body: body, d: [], u: [], pre: [], pos: 0});
exportNextBody();
}
function searchOperations(body) {
var i, len = body.length;
for (i = 0; i < len; i += 1) {
if (body[i].type === 'ExpressionStatement') {
handleExpressionStatement(body[i]);
} else if (body[i].type === 'IfStatement') {
handleIfStatement(body[i]);
} else if (body[i].type === 'FunctionDeclaration') {
handleFunctionDeclaration(body[i]);
} else if (body[i].type === 'WhileStatement') {
handleWhileStatement(body[i]);
} else if (body[i].type === 'ForStatement') {
handleForStatement(body[i]);
} else if (body[i].type === 'VariableDeclaration') {
handleVariableDeclaration(body[i]);
} else if (body[i].type === 'ReturnStatement') {
handleReturnStatement(body[i]);
} else {
//bm_eventDispatcher.log(body[i].type);
//bm_eventDispatcher.log(body[i]);
}
}
}
function getBinaryElement(element) {
switch (element.type) {
case "Literal":
case "Identifier":
return element;
case "CallExpression":
handleCallExpression(element);
return element;
case "BinaryExpression":
return convertBinaryExpression(element);
default:
//bm_eventDispatcher.log('es: ', element);
return element;
}
}
function getOperatorName(operator) {
switch (operator) {
case '+':
return 'sum';
case '-':
return 'sub';
case '*':
return 'mul';
case '/':
return 'div';
}
}
function convertBinaryExpression(expression) {
if (expression.left.type === 'Literal' && expression.right.type === 'Literal') {
return expression;
}
var callStatementOb = {
'arguments': [
getBinaryElement(expression.left),
getBinaryElement(expression.right)
],
type: "CallExpression",
callee: {
name: getOperatorName(expression.operator),
type: 'Identifier'
}
};
return callStatementOb;
}
function handleCallExpression(expression) {
var args = expression['arguments'];
var i, len = args.length;
for (i = 0; i < len; i += 1) {
if (args[i].type === 'BinaryExpression') {
args[i] = convertBinaryExpression(args[i]);
}
}
}
function handleIfStatement(ifStatement) {
if (ifStatement.consequent) {
if (ifStatement.consequent.type === 'BlockStatement') {
searchOperations(ifStatement.consequent.body);
} else if (ifStatement.consequent.type === 'ExpressionStatement') {
handleExpressionStatement(ifStatement.consequent);
}
}
if (ifStatement.alternate) {
if (ifStatement.alternate.type === 'IfStatement') {
handleIfStatement(ifStatement.alternate);
} else if (ifStatement.alternate.type === 'BlockStatement') {
searchOperations(ifStatement.alternate.body);
} else if (ifStatement.alternate.type === 'ExpressionStatement') {
handleExpressionStatement(ifStatement.alternate);
}
}
}
function handleWhileStatement(whileStatement) {
if (whileStatement.body) {
if (whileStatement.body.type === 'BlockStatement') {
searchOperations(whileStatement.body.body);
} else if (whileStatement.body.type === 'ExpressionStatement') {
handleExpressionStatement(whileStatement.body);
}
}
}
function handleForStatement(forStatement) {
if (forStatement.body) {
if (forStatement.body.type === 'BlockStatement') {
searchOperations(forStatement.body.body);
} else if (forStatement.body.type === 'ExpressionStatement') {
handleExpressionStatement(forStatement.body);
}
}
}
function handleReturnStatement(returnStatement) {
if (returnStatement.argument) {
returnStatement.argument = getBinaryElement(returnStatement.argument);
}
}
function handleVariableDeclaration(variableDeclaration) {
var declarations = variableDeclaration.declarations;
var i, len = declarations.length;
for (i = 0; i < len; i += 1) {
if (declarations[i].init) {
if (declarations[i].init.type === 'BinaryExpression') {
declarations[i].init = convertBinaryExpression(declarations[i].init);
}
}
}
}
function handleExpressionStatement(expressionStatement) {
if (expressionStatement.expression.type === 'CallExpression') {
handleCallExpression(expressionStatement.expression);
}
}
function handleFunctionDeclaration(functionDeclaration) {
if (functionDeclaration.body && functionDeclaration.body.type === 'BlockStatement') {
searchOperations(functionDeclaration.body.body);
}
}
function replaceOperations() {
var parsed = esprima.parse(expressionStr, options);
var body = parsed.body;
searchOperations(body);
var escodegen = ob.escodegen;
expressionStr = escodegen.generate(parsed);
}
function checkExpression(prop, returnOb) {
if (prop.expressionEnabled && !prop.expressionError) {
pendingBodies.length = 0;
doneBodies.length = 0;
expressionStr = prop.expression;
searchUndeclaredVariables();
replaceOperations();
expressionStr = addReturnStatement(expressionStr);
returnOb.x = expressionStr;
}
}
ob.checkExpression = checkExpression;
return ob;
}());