(function () {
var codeGenerator = (typeof eval("(function () {})") == "function") ?
function (code) { return code; } :
function (code) { return "false || " + code; };
// support string type only.
var stringify = (typeof JSON !== "undefined" && JSON.stringify) ?
function (s) { return JSON.stringify(s); } :
(function () {
// Implementation comes from JSON2 (http://www.json.org/js.html)
var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
var meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
}
return function (s) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
escapable.lastIndex = 0;
return escapable.test(s) ? '"' + s.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 's' ? c :
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' : '"' + s + '"';
};
})();
// seed defined in global
if (typeof __jscex__tempVarSeed === "undefined") {
__jscex__tempVarSeed = 0;
}
var init = function (root) {
if (root.modules["jit"]) {
return;
}
function JscexTreeGenerator(binder) {
this._binder = binder;
this._root = null;
}
JscexTreeGenerator.prototype = {
generate: function (ast) {
var params = ast[2], statements = ast[3];
this._root = { type: "delay", stmts: [] };
this._visitStatements(statements, this._root.stmts);
return this._root;
},
_getBindInfo: function (stmt) {
var type = stmt[0];
if (type == "stat") {
var expr = stmt[1];
if (expr[0] == "call") {
var callee = expr[1];
if (callee[0] == "name" && callee[1] == this._binder && expr[2].length == 1) {
return {
expression: expr[2][0],
argName: "",
assignee: null
};
}
} else if (expr[0] == "assign") {
var assignee = expr[2];
expr = expr[3];
if (expr[0] == "call") {
var callee = expr[1];
if (callee[0] == "name" && callee[1] == this._binder && expr[2].length == 1) {
return {
expression: expr[2][0],
argName: "$$_result_$$",
assignee: assignee
};
}
}
}
} else if (type == "var") {
var defs = stmt[1];
if (defs.length == 1) {
var item = defs[0];
var name = item[0];
var expr = item[1];
if (expr && expr[0] == "call") {
var callee = expr[1];
if (callee[0] == "name" && callee[1] == this._binder && expr[2].length == 1) {
return {
expression: expr[2][0],
argName: name,
assignee: null
};
}
}
}
} else if (type == "return") {
var expr = stmt[1];
if (expr && expr[0] == "call") {
var callee = expr[1];
if (callee[0] == "name" && callee[1] == this._binder && expr[2].length == 1) {
return {
expression: expr[2][0],
argName: "$$_result_$$",
assignee: "return"
};
}
}
}
return null;
},
_visitStatements: function (statements, stmts, index) {
if (arguments.length <= 2) index = 0;
if (index >= statements.length) {
stmts.push({ type: "normal" });
return this;
}
var currStmt = statements[index];
var bindInfo = this._getBindInfo(currStmt);
if (bindInfo) {
var bindStmt = { type: "bind", info: bindInfo };
stmts.push(bindStmt);
if (bindInfo.assignee != "return") {
bindStmt.stmts = [];
this._visitStatements(statements, bindStmt.stmts, index + 1);
}
} else {
var type = currStmt[0];
if (type == "return" || type == "break" || type == "continue" || type == "throw") {
stmts.push({ type: type, stmt: currStmt });
} else if (type == "if" || type == "try" || type == "for" || type == "do"
|| type == "while" || type == "switch" || type == "for-in") {
var newStmt = this._visit(currStmt);
if (newStmt.type == "raw") {
stmts.push(newStmt);
this._visitStatements(statements, stmts, index + 1);
} else {
var isLast = (index == statements.length - 1);
if (isLast) {
stmts.push(newStmt);
} else {
var combineStmt = {
type: "combine",
first: { type: "delay", stmts: [newStmt] },
second: { type: "delay", stmts: [] }
};
stmts.push(combineStmt);
this._visitStatements(statements, combineStmt.second.stmts, index + 1);
}
}
} else {
stmts.push({ type: "raw", stmt: currStmt });
this._visitStatements(statements, stmts, index + 1);
}
}
return this;
},
_visit: function (ast) {
var type = ast[0];
function throwUnsupportedError() {
throw new Error('"' + type + '" is not currently supported.');
}
var visitor = this._visitors[type];
if (visitor) {
return visitor.call(this, ast);
} else {
throwUnsupportedError();
}
},
_visitBody: function (ast, stmts) {
if (ast[0] == "block") {
this._visitStatements(ast[1], stmts);
} else {
this._visitStatements([ast], stmts);
}
},
_noBinding: function (stmts) {
switch (stmts[stmts.length - 1].type) {
case "normal":
case "return":
case "break":
case "throw":
case "continue":
return true;
}
return false;
},
_collectCaseStatements: function (cases, index) {
var res = [];
for (var i = index; i < cases.length; i++) {
var rawStmts = cases[i][1];
for (var j = 0; j < rawStmts.length; j++) {
if (rawStmts[j][0] == "break") {
return res
}
res.push(rawStmts[j]);
}
}
return res;
},
_visitors: {
"for": function (ast) {
var bodyStmts = [];
var body = ast[4];
this._visitBody(body, bodyStmts);
if (this._noBinding(bodyStmts)) {
return { type: "raw", stmt: ast };
}
var delayStmt = { type: "delay", stmts: [] };
var setup = ast[1];
if (setup) {
delayStmt.stmts.push({ type: "raw", stmt: setup });
}
var loopStmt = { type: "loop", bodyFirst: false, bodyStmt: { type: "delay", stmts: bodyStmts } };
delayStmt.stmts.push(loopStmt);
var condition = ast[2];
if (condition) {
loopStmt.condition = condition;
}
var update = ast[3];
if (update) {
loopStmt.update = update;
}
return delayStmt;
},
"for-in": function (ast) {
var body = ast[4];
var bodyStmts = [];
this._visitBody(body, bodyStmts);
if (this._noBinding(bodyStmts)) {
return { type: "raw", stmt: ast };
}
var id = (__jscex__tempVarSeed++);
var keysVar = "$$_keys_$$_" + id;
var indexVar = "$$_index_$$_" + id;
// var memVar = "$$_mem_$$_" + id;
var delayStmt = { type: "delay", stmts: [] };
// var members = Jscex._forInKeys(obj);
var keysAst = root.parse("var " + keysVar + " = Jscex._forInKeys(obj);")[1][0];
keysAst[1][0][1][2][0] = ast[3]; // replace obj with real AST;
delayStmt.stmts.push({ type: "raw", stmt: keysAst });
/*
// var members = [];
delayStmt.stmts.push({
type: "raw",
stmt: uglifyJS.parse("var " + membersVar + " = [];")[1][0]
});
// for (var mem in obj) members.push(mem);
var keysAst = uglifyJS.parse("for (var " + memVar +" in obj) " + membersVar + ".push(" + memVar + ");")[1][0];
keysAst[3] = ast[3]; // replace the "obj" with real AST.
delayStmt.stmts.push({ type : "raw", stmt: keysAst});
*/
// var index = 0;
delayStmt.stmts.push({
type: "raw",
stmt: root.parse("var " + indexVar + " = 0;")[1][0]
});
// index < members.length
var condition = root.parse(indexVar + " < " + keysVar + ".length")[1][0][1];
// index++
var update = root.parse(indexVar + "++")[1][0][1];
var loopStmt = {
type: "loop",
bodyFirst: false,
update: update,
condition: condition,
bodyStmt: { type: "delay", stmts: [] }
};
delayStmt.stmts.push(loopStmt);
var varName = ast[2][1]; // ast[2] == ["name", m]
if (ast[1][0] == "var") {
loopStmt.bodyStmt.stmts.push({
type: "raw",
stmt: root.parse("var " + varName + " = " + keysVar + "[" + indexVar + "];")[1][0]
});
} else {
loopStmt.bodyStmt.stmts.push({
type: "raw",
stmt: root.parse(varName + " = " + keysVar + "[" + indexVar + "];")[1][0]
});
}
this._visitBody(body, loopStmt.bodyStmt.stmts);
return delayStmt;
},
"while": function (ast) {
var bodyStmts = [];
var body = ast[2];
this._visitBody(body, bodyStmts);
if (this._noBinding(bodyStmts)) {
return { type: "raw", stmt: ast }
}
var loopStmt = { type: "loop", bodyFirst: false, bodyStmt: { type: "delay", stmts: bodyStmts } };
var condition = ast[1];
loopStmt.condition = condition;
return loopStmt;
},
"do": function (ast) {
var bodyStmts = [];
var body = ast[2];
this._visitBody(body, bodyStmts);
if (this._noBinding(bodyStmts)) {
return { type: "raw", stmt: ast };
}
var loopStmt = { type: "loop", bodyFirst: true, bodyStmt: { type: "delay", stmts: bodyStmts } };
var condition = ast[1];
loopStmt.condition = condition;
return loopStmt;
},
"switch": function (ast) {
var noBinding = true;
var switchStmt = { type: "switch", item: ast[1], caseStmts: [] };
var cases = ast[2];
for (var i = 0; i < cases.length; i++) {
var caseStmt = { item: cases[i][0], stmts: [] };
switchStmt.caseStmts.push(caseStmt);
var statements = this._collectCaseStatements(cases, i);
this._visitStatements(statements, caseStmt.stmts);
noBinding = noBinding && this._noBinding(caseStmt.stmts);
}
if (noBinding) {
return { type: "raw", stmt: ast };
} else {
return switchStmt;
}
},
"if": function (ast) {
var noBinding = true;
var ifStmt = { type: "if", conditionStmts: [] };
var currAst = ast;
while (true) {
var condition = currAst[1];
var condStmt = { cond: condition, stmts: [] };
ifStmt.conditionStmts.push(condStmt);
var thenPart = currAst[2];
this._visitBody(thenPart, condStmt.stmts);
noBinding = noBinding && this._noBinding(condStmt.stmts);
var elsePart = currAst[3];
if (elsePart && elsePart[0] == "if") {
currAst = elsePart;
} else {
break;
}
}
var elsePart = currAst[3];
if (elsePart) {
ifStmt.elseStmts = [];
this._visitBody(elsePart, ifStmt.elseStmts);
noBinding = noBinding && this._noBinding(ifStmt.elseStmts);
}
if (noBinding) {
return { type: "raw", stmt: ast };
} else {
return ifStmt;
}
},
"try": function (ast, stmts) {
var bodyStmts = [];
var bodyStatements = ast[1];
this._visitStatements(bodyStatements, bodyStmts);
var noBinding = this._noBinding(bodyStmts)
var tryStmt = { type: "try", bodyStmt: { type: "delay", stmts: bodyStmts } };
var catchClause = ast[2];
if (catchClause) {
var exVar = catchClause[0];
tryStmt.exVar = exVar;
tryStmt.catchStmts = [];
this._visitStatements(catchClause[1], tryStmt.catchStmts);
noBinding = noBinding && this._noBinding(tryStmt.catchStmts);
}
var finallyStatements = ast[3];
if (finallyStatements) {
tryStmt.finallyStmt = { type: "delay", stmts: [] };
this._visitStatements(finallyStatements, tryStmt.finallyStmt.stmts);
noBinding = noBinding && this._noBinding(tryStmt.finallyStmt.stmts);
}
if (noBinding) {
return { type: "raw", stmt: ast };
} else {
return tryStmt;
}
}
}
}
function CodeGenerator(builderName, binder, indent) {
this._builderName = builderName;
this._binder = binder;
this._normalMode = false;
this._indent = indent;
this._indentLevel = 0;
this._builderVar = "$$_builder_$$_" + (__jscex__tempVarSeed++);
}
CodeGenerator.prototype = {
_write: function (s) {
this._buffer.push(s);
return this;
},
_writeLine: function (s) {
this._write(s)._write("\n");
return this;
},
_writeIndents: function () {
for (var i = 0; i < this._indent; i++) {
this._write(" ");
}
for (var i = 0; i < this._indentLevel; i++) {
this._write(" ");
}
return this;
},
generate: function (params, jscexAst) {
this._buffer = [];
this._writeLine("(function (" + params.join(", ") + ") {");
this._indentLevel++;
this._writeIndents()
._writeLine("var " + this._builderVar + " = Jscex.builders[" + stringify(this._builderName) + "];");
this._writeIndents()
._writeLine("return " + this._builderVar + ".Start(this,");
this._indentLevel++;
this._pos = { };
this._writeIndents()
._visitJscex(jscexAst)
._writeLine();
this._indentLevel--;
this._writeIndents()
._writeLine(");");
this._indentLevel--;
this._writeIndents()
._write("})");
return this._buffer.join("");
},
_visitJscex: function (ast) {
this._jscexVisitors[ast.type].call(this, ast);
return this;
},
_visitRaw: function (ast) {
var type = ast[0];
function throwUnsupportedError() {
throw new Error('"' + type + '" is not currently supported.');
}
var visitor = this._rawVisitors[type];
if (visitor) {
visitor.call(this, ast);
} else {
throwUnsupportedError();
}
return this;
},
_visitJscexStatements: function (statements) {
for (var i = 0; i < statements.length; i++) {
var stmt = statements[i];
if (stmt.type == "raw" || stmt.type == "if" || stmt.type == "switch") {
this._writeIndents()
._visitJscex(stmt)._writeLine();
} else if (stmt.type == "delay") {
this._visitJscexStatements(stmt.stmts);
} else {
this._writeIndents()
._write("return ")._visitJscex(stmt)._writeLine(";");
}
}
},
_visitRawStatements: function (statements) {
for (var i = 0; i < statements.length; i++) {
var s = statements[i];
this._writeIndents()
._visitRaw(s)._writeLine();
switch (s[0]) {
case "break":
case "return":
case "continue":
case "throw":
return;
}
}
},
_visitRawBody: function (body) {
if (body[0] == "block") {
this._visitRaw(body);
} else {
this._writeLine();
this._indentLevel++;
this._writeIndents()
._visitRaw(body);
this._indentLevel--;
}
return this;
},
_visitRawFunction: function (ast) {
var funcName = ast[1] || "";
var args = ast[2];
var statements = ast[3];
this._writeLine("function " + funcName + "(" + args.join(", ") + ") {")
this._indentLevel++;
var currInFunction = this._pos.inFunction;
this._pos.inFunction = true;
this._visitRawStatements(statements);
this._indentLevel--;
this._pos.inFunction = currInFunction;
this._writeIndents()
._write("}");
},
_jscexVisitors: {
"delay": function (ast) {
if (ast.stmts.length == 1) {
var subStmt = ast.stmts[0];
switch (subStmt.type) {
case "delay":
case "combine":
case "normal":
case "break":
case "continue":
case "loop":
case "try":
this._visitJscex(subStmt);
return;
case "return":
if (!subStmt.stmt[1]) {
this._visitJscex(subStmt);
return;
}
}
}
this._writeLine(this._builderVar + ".Delay(function () {");
this._indentLevel++;
this._visitJscexStatements(ast.stmts);
this._indentLevel--;
this._writeIndents()
._write("})");
},
"combine": function (ast) {
this._writeLine(this._builderVar + ".Combine(");
this._indentLevel++;
this._writeIndents()
._visitJscex(ast.first)._writeLine(",");
this._writeIndents()
._visitJscex(ast.second)._writeLine();
this._indentLevel--;
this._writeIndents()
._write(")");
},
"loop": function (ast) {
this._writeLine(this._builderVar + ".Loop(");
this._indentLevel++;
if (ast.condition) {
this._writeIndents()
._writeLine("function () {");
this._indentLevel++;
this._writeIndents()
._write("return ")._visitRaw(ast.condition)._writeLine(";");
this._indentLevel--;
this._writeIndents()
._writeLine("},");
} else {
this._writeIndents()._writeLine("null,");
}
if (ast.update) {
this._writeIndents()
._writeLine("function () {");
this._indentLevel++;
this._writeIndents()
._visitRaw(ast.update)._writeLine(";");
this._indentLevel--;
this._writeIndents()
._writeLine("},");
} else {
this._writeIndents()._writeLine("null,");
}
this._writeIndents()
._visitJscex(ast.bodyStmt)._writeLine(",");
this._writeIndents()
._writeLine(ast.bodyFirst);
this._indentLevel--;
this._writeIndents()
._write(")");
},
"raw": function (ast) {
this._visitRaw(ast.stmt);
},
"bind": function (ast) {
var info = ast.info;
this._write(this._builderVar + ".Bind(")._visitRaw(info.expression)._writeLine(", function (" + info.argName + ") {");
this._indentLevel++;
if (info.assignee == "return") {
this._writeIndents()
._writeLine("return " + this._builderVar + ".Return(" + info.argName + ");");
} else {
if (info.assignee) {
this._writeIndents()
._visitRaw(info.assignee)._writeLine(" = " + info.argName + ";");
}
this._visitJscexStatements(ast.stmts);
}
this._indentLevel--;
this._writeIndents()
._write("})");
},
"if": function (ast) {
for (var i = 0; i < ast.conditionStmts.length; i++) {
var stmt = ast.conditionStmts[i];
this._write("if (")._visitRaw(stmt.cond)._writeLine(") {");
this._indentLevel++;
this._visitJscexStatements(stmt.stmts);
this._indentLevel--;
this._writeIndents()
._write("} else ");
}
this._writeLine("{");
this._indentLevel++;
if (ast.elseStmts) {
this._visitJscexStatements(ast.elseStmts);
} else {
this._writeIndents()
._writeLine("return " + this._builderVar + ".Normal();");
}
this._indentLevel--;
this._writeIndents()
._write("}");
},
"switch": function (ast) {
this._write("switch (")._visitRaw(ast.item)._writeLine(") {");
this._indentLevel++;
for (var i = 0; i < ast.caseStmts.length; i++) {
var caseStmt = ast.caseStmts[i];
if (caseStmt.item) {
this._writeIndents()
._write("case ")._visitRaw(caseStmt.item)._writeLine(":");
} else {
this._writeIndents()._writeLine("default:");
}
this._indentLevel++;
this._visitJscexStatements(caseStmt.stmts);
this._indentLevel--;
}
this._writeIndents()
._write("}");
},
"try": function (ast) {
this._writeLine(this._builderVar + ".Try(");
this._indentLevel++;
this._writeIndents()
._visitJscex(ast.bodyStmt)._writeLine(",");
if (ast.catchStmts) {
this._writeIndents()
._writeLine("function (" + ast.exVar + ") {");
this._indentLevel++;
this._visitJscexStatements(ast.catchStmts);
this._indentLevel--;
this._writeIndents()
._writeLine("},");
} else {
this._writeIndents()
._writeLine("null,");
}
if (ast.finallyStmt) {
this._writeIndents()
._visitJscex(ast.finallyStmt)._writeLine();
} else {
this._writeIndents()
._writeLine("null");
}
this._indentLevel--;
this._writeIndents()
._write(")");
},
"normal": function (ast) {
this._write(this._builderVar + ".Normal()");
},
"throw": function (ast) {
this._write(this._builderVar + ".Throw(")._visitRaw(ast.stmt[1])._write(")");
},
"break": function (ast) {
this._write(this._builderVar + ".Break()");
},
"continue": function (ast) {
this._write(this._builderVar + ".Continue()");
},
"return": function (ast) {
this._write(this._builderVar + ".Return(");
if (ast.stmt[1]) this._visitRaw(ast.stmt[1]);
this._write(")");
}
},
_rawVisitors: {
"var": function (ast) {
this._write("var ");
var items = ast[1];
for (var i = 0; i < items.length; i++) {
this._write(items[i][0]);
if (items[i].length > 1) {
this._write(" = ")._visitRaw(items[i][1]);
}
if (i < items.length - 1) this._write(", ");
}
this._write(";");
},
"seq": function (ast) {
for (var i = 1; i < ast.length; i++) {
this._visitRaw(ast[i]);
if (i < ast.length - 1) this._write(", ");
}
},
"binary": function (ast) {
var op = ast[1], left = ast[2], right = ast[3];
function needBracket(item) {
var type = item[0];
return !(type == "num" || type == "name" || type == "dot");
}
if (needBracket(left)) {
this._write("(")._visitRaw(left)._write(") ");
} else {
this._visitRaw(left)._write(" ");
}
this._write(op);
if (needBracket(right)) {
this._write(" (")._visitRaw(right)._write(")");
} else {
this._write(" ")._visitRaw(right);
}
},
"sub": function (ast) {
var prop = ast[1], index = ast[2];
function needBracket() {
return !(prop[0] == "name")
}
if (needBracket()) {
this._write("(")._visitRaw(prop)._write(")[")._visitRaw(index)._write("]");
} else {
this._visitRaw(prop)._write("[")._visitRaw(index)._write("]");
}
},
"unary-postfix": function (ast) {
var op = ast[1];
var item = ast[2];
this._visitRaw(item)._write(op);
},
"unary-prefix": function (ast) {
var op = ast[1];
var item = ast[2];
this._write(op);
if (op == "typeof") {
this._write("(")._visitRaw(item)._write(")");
} else {
this._visitRaw(item);
}
},
"assign": function (ast) {
var op = ast[1];
var name = ast[2];
var value = ast[3];
this._visitRaw(name);
if ((typeof op) == "string") {
this._write(" " + op + "= ");
} else {
this._write(" = ");
}
this._visitRaw(value);
},
"stat": function (ast) {
this._visitRaw(ast[1])._write(";");
},
"dot": function (ast) {
function needBracket() {
var leftOp = ast[1][0];
return !(leftOp == "dot" || leftOp == "name");
}
if (needBracket()) {
this._write("(")._visitRaw(ast[1])._write(").")._write(ast[2]);
} else {
this._visitRaw(ast[1])._write(".")._write(ast[2]);
}
},
"new": function (ast) {
var ctor = ast[1];
this._write("new ")._visitRaw(ctor)._write("(");
var args = ast[2];
for (var i = 0, len = args.length; i < len; i++) {
this._visitRaw(args[i]);
if (i < len - 1) this._write(", ");
}
this._write(")");
},
"call": function (ast) {
if (_isJscexPattern(ast)) {
var indent = this._indent + this._indentLevel * 4;
var newCode = _compileJscexPattern(ast, indent);
this._write(newCode);
} else {
var invalidBind = (ast[1][0] == "name") && (ast[1][1] == this._binder);
if (invalidBind) {
this._pos = { inFunction: true };
this._buffer = [];
}
this._visitRaw(ast[1])._write("(");
var args = ast[2];
for (var i = 0; i < args.length; i++) {
this._visitRaw(args[i]);
if (i < args.length - 1) this._write(", ");
}
this._write(")");
if (invalidBind) {
throw ("Invalid bind operation: " + this._buffer.join(""));
}
}
},
"name": function (ast) {
this._write(ast[1]);
},
"object": function (ast) {
var items = ast[1];
if (items.length <= 0) {
this._write("{ }");
} else {
this._writeLine("{");
this._indentLevel++;
for (var i = 0; i < items.length; i++) {
this._writeIndents()
._write(stringify(items[i][0]) + ": ")
._visitRaw(items[i][1]);
if (i < items.length - 1) {
this._writeLine(",");
} else {
this._writeLine("");
}
}
this._indentLevel--;
this._writeIndents()._write("}");
}
},
"array": function (ast) {
this._write("[");
var items = ast[1];
for (var i = 0; i < items.length; i++) {
this._visitRaw(items[i]);
if (i < items.length - 1) this._write(", ");
}
this._write("]");
},
"num": function (ast) {
this._write(ast[1]);
},
"regexp": function (ast) {
this._write("/" + ast[1] + "/" + ast[2]);
},
"string": function (ast) {
this._write(stringify(ast[1]));
},
"function": function (ast) {
this._visitRawFunction(ast);
},
"defun": function (ast) {
this._visitRawFunction(ast);
},
"return": function (ast) {
if (this._pos.inFunction) {
this._write("return");
var value = ast[1];
if (value) this._write(" ")._visitRaw(value);
this._write(";");
} else {
this._write("return ")._visitJscex({ type: "return", stmt: ast })._write(";");
}
},
"for": function (ast) {
this._write("for (");
var setup = ast[1];
if (setup) {
this._visitRaw(setup);
if (setup[0] != "var") {
this._write("; ");
} else {
this._write(" ");
}
} else {
this._write("; ");
}
var condition = ast[2];
if (condition) this._visitRaw(condition);
this._write("; ");
var update = ast[3];
if (update) this._visitRaw(update);
this._write(") ");
var currInLoop = this._pos.inLoop;
this._pos.inLoop = true;
var body = ast[4];
this._visitRawBody(body);
this._pos.inLoop = currInLoop;
},
"for-in": function (ast) {
this._write("for (");
var declare = ast[1];
if (declare[0] == "var") { // declare == ["var", [["m"]]]
this._write("var " + declare[1][0][0]);
} else {
this._visitRaw(declare);
}
this._write(" in ")._visitRaw(ast[3])._write(") ");
var body = ast[4];
this._visitRawBody(body);
},
"block": function (ast) {
this._writeLine("{")
this._indentLevel++;
this._visitRawStatements(ast[1]);
this._indentLevel--;
this._writeIndents()
._write("}");
},
"while": function (ast) {
var condition = ast[1];
var body = ast[2];
var currInLoop = this._pos.inLoop
this._pos.inLoop = true;
this._write("while (")._visitRaw(condition)._write(") ")._visitRawBody(body);
this._pos.inLoop = currInLoop;
},
"do": function (ast) {
var condition = ast[1];
var body = ast[2];
var currInLoop = this._pos.inLoop;
this._pos.inLoop = true;
this._write("do ")._visitRawBody(body);
this._pos.inLoop = currInLoop;
if (body[0] == "block") {
this._write(" ");
} else {
this._writeLine()._writeIndents();
}
this._write("while (")._visitRaw(condition)._write(");");
},
"if": function (ast) {
var condition = ast[1];
var thenPart = ast[2];
this._write("if (")._visitRaw(condition)._write(") ")._visitRawBody(thenPart);
var elsePart = ast[3];
if (elsePart) {
if (thenPart[0] == "block") {
this._write(" ");
} else {
this._writeLine("")
._writeIndents();
}
if (elsePart[0] == "if") {
this._write("else ")._visitRaw(elsePart);
} else {
this._write("else ")._visitRawBody(elsePart);
}
}
},
"break": function (ast) {
if (this._pos.inLoop || this._pos.inSwitch) {
this._write("break;");
} else {
this._write("return ")._visitJscex({ type: "break", stmt: ast })._write(";");
}
},
"continue": function (ast) {
if (this._pos.inLoop) {
this._write("continue;");
} else {
this._write("return ")._visitJscex({ type: "continue", stmt: ast })._write(";");
}
},
"throw": function (ast) {
var pos = this._pos;
if (pos.inTry || pos.inFunction) {
this._write("throw ")._visitRaw(ast[1])._write(";");
} else {
this._write("return ")._visitJscex({ type: "throw", stmt: ast })._write(";");
}
},
"conditional": function (ast) {
this._write("(")._visitRaw(ast[1])._write(") ? (")._visitRaw(ast[2])._write(") : (")._visitRaw(ast[3])._write(")");
},
"try": function (ast) {
this._writeLine("try {");
this._indentLevel++;
var currInTry = this._pos.inTry;
this._pos.inTry = true;
this._visitRawStatements(ast[1]);
this._indentLevel--;
this._pos.inTry = currInTry;
var catchClause = ast[2];
var finallyStatements = ast[3];
if (catchClause) {
this._writeIndents()
._writeLine("} catch (" + catchClause[0] + ") {")
this._indentLevel++;
this._visitRawStatements(catchClause[1]);
this._indentLevel--;
}
if (finallyStatements) {
this._writeIndents()
._writeLine("} finally {");
this._indentLevel++;
this._visitRawStatements(finallyStatements);
this._indentLevel--;
}
this._writeIndents()
._write("}");
},
"switch": function (ast) {
this._write("switch (")._visitRaw(ast[1])._writeLine(") {");
this._indentLevel++;
var currInSwitch = this._pos.inSwitch;
this._pos.inSwitch = true;
var cases = ast[2];
for (var i = 0; i < cases.length; i++) {
var c = cases[i];
this._writeIndents();
if (c[0]) {
this._write("case ")._visitRaw(c[0])._writeLine(":");
} else {
this._writeLine("default:");
}
this._indentLevel++;
this._visitRawStatements(c[1]);
this._indentLevel--;
}
this._indentLevel--;
this._pos.inSwitch = currInSwitch;
this._writeIndents()
._write("}");
}
}
}
function _isJscexPattern(ast) {
if (ast[0] != "call") return false;
var evalName = ast[1];
if (evalName[0] != "name" || evalName[1] != "eval") return false;
var compileCall = ast[2][0];
if (!compileCall || compileCall[0] != "call") return false;
var compileMethod = compileCall[1];
if (!compileMethod || compileMethod[0] != "dot" || compileMethod[2] != "compile") return false;
var jscexName = compileMethod[1];
if (!jscexName || jscexName[0] != "name" || jscexName[1] != "Jscex") return false;
var builder = compileCall[2][0];
if (!builder || builder[0] != "string") return false;
var func = compileCall[2][1];
if (!func || func[0] != "function") return false;
return true;
}
function _compileJscexPattern(ast, indent) {
var builderName = ast[2][0][2][0][1];
var funcAst = ast[2][0][2][1];
var binder = root.binders[builderName];
var jscexTreeGenerator = new JscexTreeGenerator(binder);
var jscexAst = jscexTreeGenerator.generate(funcAst);
var codeGenerator = new CodeGenerator(builderName, binder, indent);
var newCode = codeGenerator.generate(funcAst[2], jscexAst);
return newCode;
}
function compile(builderName, func) {
var funcCode = func.toString();
var evalCode = "eval(Jscex.compile(" + stringify(builderName) + ", " + funcCode + "))"
var evalCodeAst = root.parse(evalCode);
// [ "toplevel", [ [ "stat", [ "call", ... ] ] ] ]
var evalAst = evalCodeAst[1][0][1];
var newCode = _compileJscexPattern(evalAst, 0);
root.logger.debug(funcCode + "\n\n>>>\n\n" + newCode);
return codeGenerator(newCode);
};
root.compile = compile;
root.modules["jit"] = true;
}
var isCommonJS = (typeof require !== "undefined" && typeof module !== "undefined" && module.exports);
var isAmd = (typeof define !== "undefined" && define.amd);
if (isCommonJS) {
module.exports.init = function (root) {
if (!root.modules["parser"]) {
require("./jscex-parser").init(root);
};
init(root);
}
} else if (isAmd) {
define("jscex-jit", ["jscex-parser"], function (parser) {
return {
init: function (root) {
if (!root.modules["parser"]) {
parser.init(root);
}
init(root);
}
};
});
} else {
if (typeof Jscex === "undefined") {
throw new Error('Missing root object, please load "jscex" module first.');
}
if (!Jscex.modules["parser"]) {
throw new Error('Missing essential components, please initialize "parser" module first.');
}
init(Jscex);
}
})();