blob: c83bad5cd7fa0a892a7307b604291f3c471e40a1 [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 if (body[i].type === 'TryStatement') {
handleTryStatement(body[i]);
} else if (body[i].type === 'SwitchStatement') {
handleSwitchStatement(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);
case "MemberExpression":
handleMemberExpression(element);
return element;
case "UpdateExpression":
case "UnaryExpression":
return 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 isOperatorTransformable(operator){
switch(operator){
case '+':
case '-':
case '*':
case '/':
return true;
}
return false;
}
function convertBinaryExpression(expression) {
if (expression.left.type === 'Literal' && expression.right.type === 'Literal') {
return expression;
}
if(!isOperatorTransformable(expression.operator)){
return expression;
}
var callStatementOb = {
'arguments': [
getBinaryElement(expression.left),
getBinaryElement(expression.right)
],
type: "CallExpression",
callee: {
name: getOperatorName(expression.operator),
type: 'Identifier'
}
};
return callStatementOb;
}
function handleMemberExpression(expression) {
if (expression.property.type === 'BinaryExpression') {
expression.property = convertBinaryExpression(expression.property);
} else if (expression.property.type === 'CallExpression') {
handleCallExpression(expression.property);
}
}
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);
} else if (ifStatement.consequent.type === 'ReturnStatement') {
handleReturnStatement(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 handleTryStatement(tryStatement) {
if (tryStatement.block) {
if (tryStatement.block.type === 'BlockStatement') {
searchOperations(tryStatement.block.body);
}
}
if (tryStatement.handler) {
if (tryStatement.handler.body.type === 'BlockStatement') {
searchOperations(tryStatement.handler.body.body);
}
}
}
function handleSwitchStatement(switchStatement) {
var cases = switchStatement.cases;
var i, len = cases.length;
for(i = 0; i < len; i += 1) {
searchOperations(cases[i].consequent);
}
}
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);
} else if (declarations[i].init.type === 'CallExpression') {
handleCallExpression(declarations[i].init);
}
}
}
}
function handleAssignmentExpression(assignmentExpression) {
if(assignmentExpression.right){
if(assignmentExpression.right.type === 'BinaryExpression') {
assignmentExpression.right = convertBinaryExpression(assignmentExpression.right);
} else if (assignmentExpression.right.type === 'CallExpression') {
handleCallExpression(assignmentExpression.right);
}
}
}
function handleExpressionStatement(expressionStatement) {
if (expressionStatement.expression.type === 'CallExpression') {
handleCallExpression(expressionStatement.expression);
} else if (expressionStatement.expression.type === 'BinaryExpression') {
expressionStatement.expression = convertBinaryExpression(expressionStatement.expression);
} else if (expressionStatement.expression.type === 'AssignmentExpression') {
handleAssignmentExpression(expressionStatement.expression);
}
}
function handleFunctionDeclaration(functionDeclaration) {
if (functionDeclaration.body && functionDeclaration.body.type === 'BlockStatement') {
searchOperations(functionDeclaration.body.body);
}
}
function replaceOperations(body) {
searchOperations(body);
}
function createAssignmentObject(){
return {
type: 'ExpressionStatement',
expression: {
left: {
name: '$bm_rt',
type: 'Identifier'
},
type: "AssignmentExpression",
operator: '='
}
}
}
function convertExpressionStatementToVariableDeclaration(expressionStatement) {
var assignmentObject;
if(expressionStatement.expression.type === 'Literal'){
assignmentObject = createAssignmentObject();
assignmentObject.expression.right = expressionStatement.expression;
return assignmentObject;
} else if(expressionStatement.expression.type === 'Identifier'){
assignmentObject = createAssignmentObject();
assignmentObject.expression.right = expressionStatement.expression;
return assignmentObject;
} else if(expressionStatement.expression.type === 'CallExpression'){
assignmentObject = createAssignmentObject();
assignmentObject.expression.right = expressionStatement.expression;
return assignmentObject;
} else if(expressionStatement.expression.type === 'ArrayExpression'){
assignmentObject = createAssignmentObject();
assignmentObject.expression.right = expressionStatement.expression;
return assignmentObject;
} else if(expressionStatement.expression.type === 'BinaryExpression'){
assignmentObject = createAssignmentObject();
assignmentObject.expression.right = expressionStatement.expression;
return assignmentObject;
} else if(expressionStatement.expression.type === 'MemberExpression'){
assignmentObject = createAssignmentObject();
assignmentObject.expression.right = expressionStatement.expression;
return assignmentObject;
} else if(expressionStatement.expression.type === 'LogicalExpression'){
assignmentObject = createAssignmentObject();
assignmentObject.expression.right = expressionStatement.expression;
return assignmentObject;
} else if(expressionStatement.expression.type === 'UnaryExpression'){
assignmentObject = createAssignmentObject();
assignmentObject.expression.right = expressionStatement.expression;
return assignmentObject;
}
return expressionStatement;
}
function assignVariableToIfStatement(ifStatement){
if (ifStatement.consequent) {
if (ifStatement.consequent.type === 'BlockStatement') {
assignVariable(ifStatement.consequent.body);
} else if (ifStatement.consequent.type === 'ExpressionStatement') {
ifStatement.consequent = convertExpressionStatementToVariableDeclaration(ifStatement.consequent);
}
}
if (ifStatement.alternate) {
if (ifStatement.alternate.type === 'IfStatement') {
assignVariableToIfStatement(ifStatement.alternate);
} else if (ifStatement.alternate.type === 'BlockStatement') {
assignVariable(ifStatement.alternate.body);
} else if (ifStatement.alternate.type === 'ExpressionStatement') {
ifStatement.alternate = convertExpressionStatementToVariableDeclaration(ifStatement.alternate);
}
}
}
function assignVariable(body){
var len = body.length - 1;
var flag = true;
var lastElem;
while (flag) {
lastElem = body[len];
if ((lastElem.type !== 'EmptyStatement' && lastElem.type !== 'FunctionDeclaration') || len === 0) {
flag = false;
} else {
len -= 1;
}
}
if (lastElem.type === 'ExpressionStatement') {
lastElem = convertExpressionStatementToVariableDeclaration(lastElem);
} else if (lastElem.type === 'TryStatement') {
if (lastElem.block) {
if (lastElem.block.type === 'BlockStatement') {
assignVariable(lastElem.block.body);
}
}
if (lastElem.handler) {
if (lastElem.handler.body.type === 'BlockStatement') {
assignVariable(lastElem.handler.body.body);
}
}
} else if (lastElem.type === 'IfStatement') {
assignVariableToIfStatement(lastElem);
}
body[len] = lastElem;
}
function checkExpression(prop, returnOb) {
if (prop.expressionEnabled && !prop.expressionError) {
pendingBodies.length = 0;
doneBodies.length = 0;
expressionStr = prop.expression;
expressionStr = correctEaseAndWizz(expressionStr);
expressionStr = correctElseToken(expressionStr);
searchUndeclaredVariables();
var parsed = esprima.parse(expressionStr, options);
var body = parsed.body;
replaceOperations(body);
assignVariable(body);
var escodegen = ob.escodegen;
expressionStr = escodegen.generate(parsed);
expressionStr = 'var $bm_rt;\n' + expressionStr;
returnOb.x = expressionStr;
}
}
function correctElseToken(str){
var regElse = / else /g;
return str.replace(regElse,'\n else ');
}
function correctEaseAndWizz(str){
var easeRegex = /Ease and Wizz\s[0-9. ]+:/;
if (easeRegex.test(str)) {
str = str.replace('key(1)[1];', 'key(1)[1].length;');
str = str.replace('key(1)[2];', 'key(1)[2].length;');
}
return str;
}
ob.checkExpression = checkExpression;
return ob;
}());