Arquivos
deepforge/src/common/lua.js
T
Brian Broll 3e3326688c Added meaningful error if retrieving nil val from layer. Fixes #386 (#442)
WIP #386 Added googlenet-setters for testing error messages

WIP #386 Commented out tacking in extra values

WIP #386 Ignore "nil gets" if in "set" operation
2016-07-01 13:14:06 -05:00

3246 linhas
86 KiB
JavaScript

/**
* lua.js
*/
(function(root, factory){
if(typeof define === "function" && define.amd) {
define([], function(){
return (root.luajs = factory());
});
} else if(typeof module === "object" && module.exports) {
module.exports = (root.luajs = factory());
} else {
root.luajs = factory();
}
}(this, function() {
var modules = {
lex: {},
parser: {},
codegen: {},
types:{},
index: {}
};
function require(x){
return modules[/\.\/(\w*)\.js/.exec(x)[1]];
}
(function(exports){
/**
* Created by Yun on 2014/9/23.
*/
var tokens = [
"<eof>",
"and", "break", "do", "else", "elseif",
"end", "false", "for", "function", "goto", "if",
"in", "local", "nil", "not", "or", "repeat",
"return", "then", "true", "until", "while",
"..", "...", "==", ">=", "<=", "~=", "::", "<--orgion-eof>",
"<number>", "<name>", "<string>",
"__javascript", "__jsreturn"
];
exports.tokens = tokens;
var reserved = {
"and":1,
"break":2, "do":3, "else":4, "elseif":5,
"end":6, "false":7, "for":8, "function":9, "goto":10, "if":11,
"in":12, "local":13, "nil":14, "not":15, "or":16, "repeat":17,
"return":18, "then":19, "true":20, "until":21, "while":22,
"__javascript":34,
"__js": 34,
"__jsreturn": 35
}
var TK = {
TK_EOS: 0,
/* terminal symbols denoted by reserved words */
TK_AND : 1,
TK_BREAK : 2,
TK_DO : 3,
TK_ELSE : 4,
TK_ELSEIF : 5,
TK_END : 6,
TK_FALSE : 7,
TK_FOR : 8,
TK_FUNCTION : 9,
TK_GOTO : 10,
TK_IF : 11,
TK_IN : 12,
TK_LOCAL : 13,
TK_NIL : 14,
TK_NOT : 15,
TK_OR : 16,
TK_REPEAT : 17,
TK_RETURN : 18,
TK_THEN : 19,
TK_TRUE : 20,
TK_UNTIL : 21,
TK_WHILE : 22,
/* other terminal symbols */
TK_CONCAT : 23,
TK_DOTS: 24,
TK_EQ: 25,
TK_GE: 26,
TK_LE: 27,
TK_NE: 28,
TK_DBCOLON:29,
TK_NUMBER:31,
TK_NAME: 32,
TK_STRING: 33,
TK_JAVASCRIPT: 34,
TK_JSRETURN: 35
};
exports.TK = TK;
var lalphaExp = /^[A-Za-z\_]$/;
function lislalpha(ch){
return lalphaExp.test(ch);
}
var digitExp = /^[0-9]$/;
function lisdigit(ch){
return digitExp.test(ch);
}
var lalnumExp = /^[0-9A-Za-z\_]$/;
function lislalnum(ch){
return lalnumExp.test(ch);
}
function isNewLine(ch){
return ch=='\n' || ch == '\r';
}
/*
** skip a sequence '[=*[' or ']=*]' and return its number of '='s or
** -1 if sequence is malformed
*/
function skip_sep(curr, next, save){
var count = 0;
var ch = curr();
if (ch != '[' && ch != ']'){
throw "Lexical internal error!";
}
if (save){
save(ch);
}
next()
while (curr() == '='){
next();
if (save){
save(curr());
}
++count;
}
return curr() == ch ? count : (-count - 1);
}
function inclinenumber(curr, next){
var old = curr();
next();
if (isNewLine(curr()) && curr() != old){
next();
}
}
//TODO: avoid wasting space when read comments.
function read_long_string(curr, next, sep){
var buff = [];
function save(ch){
buff.push(ch);
}
next();/* skip 2nd `[' */
/* string starts with a newline? */
if (isNewLine(curr())){
inclinenumber(curr, next);
}
for (;;){
var ch = curr();
switch(ch){
case null:
throw "unfinished long string/comment";
case ']':{
if (skip_sep(curr, next, save) == sep){
/* skip 2nd `]' */
next();
buff.splice(-sep-1, sep+1);
return buff.join("");
}
break;
}
case '\n':case '\r':{
inclinenumber(curr, next);
save('\n'); /* avoid wasting space */
break;
}
default:{
save(ch);
next();
}
}
}
}
//TODO: support utf8 `\` escape.
function read_string(curr, next){
var buff = [];
var beginCh = curr();
next();
var currCh;
while ((currCh = curr()) != beginCh){
switch (currCh){
case null:
throw "unfinished string";
case '\n':
case '\r':
throw "unfinished string";
case '\\':{
next();
currCh = curr();
switch (currCh){
// case 'a': buff.push('\a'); next(); break;
case 'b': buff.push('\b'); next(); break;
case 'f': buff.push('\f'); next(); break;
case 'n': buff.push('\n'); next(); break;
case 'r': buff.push('\r'); next(); break;
case 't': buff.push('\t'); next(); break;
case 'v': buff.push('\011'); next(); break;
case '\n': case '\r': {
buff.push('\n');
inclinenumber();
break;
}
case '\\': case '\"': case '\'':{
buff.push(currCh);
next();
break;
}
case null:
throw "unfinished string";
case 'z':{
next();
while (lisspace(curr())){
next();
}
break;
}
case 'x': throw "esacpe for char code not supported in lua.js yet.";
default:{
if (!lisdigit(currCh)){
throw "invalid escape sequence" + currCh;
}
throw "esacpe for char code not supported in lua.js yet.";
}
}
break;
}
default: {
buff.push(currCh);
next();
}
}
}
next();
return buff.join("");
}
function read_numeral(curr, next){
var first = curr();
next();
var hex = true;
if (first == '0' && (curr() == 'x' || curr() == 'X')){ /* hexadecimal? */
next();
throw new Error("Hexadecimal numeric not supported in lua.js yet.");
} else {
var buff = [first];
var ch = curr();
var prev = first;
while (lisdigit(ch) || ch == '.' || ch == 'e' || (prev == 'e' && ch == '-')){
buff.push(ch);
next();
prev = ch;
ch = curr();
}
//not TODO: use locale decimal point.
return parseFloat(buff.join(""));
}
}
function read_identifier_name(curr, next){
var buff = [];
while (lislalnum(curr())){
buff.push(curr());
next();
}
return buff.join("");
}
function is_reserved(name){
return typeof(reserved[name]) == 'number' && reserved[name];
}
function llex(curr, next){
for (;;){
switch(curr()){
case '\n': case '\r':
case ' ': case '\f': case '\t': case '\011':next();break;
case '-':{
/* '-' or '--' (comment) */
next();
if (curr() != '-') {
return '-';
}
/* else is a comment */
next();
var ch = curr();
if (ch == '[') { /* long comment? */
var sep = skip_sep(curr, next);
if (sep >= 0){
read_long_string(curr, next, sep); /* skip long comment */
break;
}
}
/* else short comment */
while (ch && ch != "\n" && ch != "\r"){
/* skip until end of line (or end of file) */
next();
ch = curr();
}
break;
}
case '[':{
/* long string or simply '[' */
var sep = skip_sep(curr, next);
if (sep >= 0){
return {
id: TK.TK_STRING,
val: read_long_string(curr, next, sep)
};
} else if (sep == -1){
return '[';
} else {
throw "invalid long string delimiter";
}
}
case '=':{
next();
if (curr()!= '=') {
return '=';
} else {
next();
return TK.TK_EQ;
}
}
case '<':{
next();
if (curr()!= '=') {
return '<';
} else {
next();
return TK.TK_LE;
}
}
case '>':{
next();
if (curr()!= '=') {
return '>';
} else {
next();
return TK.TK_GE;
}
}
case '~':{
next();
if (curr()!= '=') {
return '~';
} else {
next();
return TK.TK_NE;
}
}
case ':': {
next();
if (curr() != ':'){
return ':';
} else {
next(ls);
return TK.TK_DBCOLON;
}
}
case '"': case '\'': { /* short literal strings */
return {
id: TK.TK_STRING,
val: read_string(curr, next)
};
}
case '.': { /* '.', '..', '...', or number */
next();
if (curr() == ".") {
next();
if (curr() == ".")
{
next();
return TK.TK_DOTS;/* '...' */
}
else return TK.TK_CONCAT; /* '..' */
}
else if (!lisdigit(curr())) return '.';
/* else go through */
}
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
return {
id: TK.TK_NUMBER,
val: read_numeral(curr, next)
};
}
case null:{
return TK.TK_EOS;
}
default:{
if (lislalpha(curr())){
var str = read_identifier_name(curr, next);
var reserved = is_reserved(str);
if (reserved){
return reserved;
}
return {
id: TK.TK_NAME,
val: str
};
} else {
/* single-char tokens (+ - / ...) */
var ret = curr();
next();
return ret;
}
}
}
}
}
function generator(s){
var cur = 0;
function curr(){
if (cur < s.length){
return s.charAt(cur);
}
return null;
}
function next(){
if (cur < s.length){
++cur;
}
}
return function(){
return llex(curr, next);
}
}
exports.generator = generator;
function lex(s){
var gen = generator(s);
var curr = gen();
var next = gen();
return {
curr: function(){
return curr;
},
lookAhead: function(){
return next;
},
next: function(){
curr = next;
next = gen();
}
}
}
exports.lex = lex;
})(modules.lex);
(function(exports){
/**
* Created by Yun on 2014/9/23.
*/
var liblex = require("./lex.js");
var lex = liblex.lex;
var TK = liblex.TK;
var tokenNames = liblex.tokens;
function tokentype(sym){
switch (typeof(sym)) {
case 'string':
case 'number':
return sym;
case 'object':
return sym.id;
}
}
function tokenName(sym){
if (typeof(sym) == 'number'){
return tokenNames[sym];
} else if (typeof(sym) == 'object') {
return tokenNames[sym.id];
} else {
return sym;
}
}
function singlevar(s){
check(s, TK.TK_NAME);
var ret = s.curr().val;
s.next();
return {
type: "variable",
val: ret
};
}
function primaryexp(s){
var t = tokentype(s.curr())
switch(t){
case '(':{
s.next();
var exp = expr(s);
checknext(s, ')');
return {
type: "expr.brackets",
"expr": exp
};
}
case TK.TK_NAME:{
return singlevar(s);
}
case TK.TK_JAVASCRIPT:{
s.next();
return {
type: "expr.javascript",
"args": funcargs(s)
}
}
default:{
throw new Error("unexpected symbol " + (tokenNames[t] || t));
}
}
}
function testnext(s, expected){
if (tokentype(s.curr()) == expected){
s.next();
return true;
}
//return false;
}
function check(s, expected){
if (tokentype(s.curr()) != expected){
throw new Error("symbol "+tokenName(expected)+" expected!");
}
}
function checknext(s, val){
check(s, val);
s.next();
}
function str_checkname(s){
check(s, TK.TK_NAME);
var ret = s.curr().val;
s.next();
return ret;
}
function checkname(s){
return {
type: "const.string",
val: str_checkname(s)
};
}
function codestring(s){
check(s, TK.TK_STRING);
var ret = s.curr().val;
s.next();
return {
type: "const.string",
val: ret
};
}
function yindex(s){
s.next(); /* skip the '[' */
var exp = expr(s);
checknext(s, ']');
return exp;
}
function funcargs(s){
switch (tokentype(s.curr())){
case '(':{
s.next();
if (s.curr() == ')') { /* arg list is empty? */
s.next();
return [];
} else {
var args = explist(s);
checknext(s, ')');
return args;
}
}
case '{':{
return [constructor(s)];
}
case TK.TK_STRING:{
return [codestring(s)];
}
default:{
throw new Error("function arguments expected, got "+ tokenName(s.curr()));
}
}
}
function suffixedexp(s){
var primary = primaryexp(s);
for (;;) {
switch(tokentype(s.curr())){
case '.':{
s.next();
var key = checkname(s);
primary = {
"type": "expr.index",
"self": primary,
"key": key
};
break;
}
case '[':{
var key = yindex(s);
primary = {
"type": "expr.index",
"self": primary,
"key": key
};
break;
}
case ':':{
s.next();
var key = checkname(s);
var args = funcargs(s);
primary = {
"type": "expr.callMethod",
"self": primary,
"key": key,
"args": args
}
break;
}
case '(': case '{':case TK.TK_STRING:{
var args = funcargs(s);
primary = {
"type": "expr.call",
"func": primary,
"args": args
}
break;
}
default:
return primary;
}
}
}
function getunopr(s){
switch (s.curr()){
case TK.TK_NOT: return "uop.not";
case '-': return "uop.minus";
case '#': return "uop.len";
// default: return null;
}
}
function getbinopr(s){
switch (s.curr()){
case '+': return 1;
case '-': return 2;
case '*': return 3;
case '/': return 4;
case '%': return 5;
case '^': return 6;
case TK.TK_CONCAT: return 7;
case TK.TK_EQ: return 8;
case '<': return 9;
case TK.TK_LE: return 10;
case TK.TK_NE: return 11;
case '>': return 12;
case TK.TK_GE: return 13;
case TK.TK_AND: return 14;
case TK.TK_OR: return 15;
// default: return null;
}
}
function simpleexp(s){
switch (tokentype(s.curr())){
case TK.TK_NUMBER:{
var val = s.curr().val;
s.next();
return {
type: "const.number",
val: val
}
}
case TK.TK_STRING:{
return codestring(s);
}
case TK.TK_NIL: {
s.next();
return {
type: "const.nil"
}
}
case TK.TK_TRUE:{
s.next();
return {
type: "const.boolean",
val: true
}
}
case TK.TK_FALSE:{
s.next();
return {
type: "const.boolean",
val: false
}
}
case TK.TK_DOTS: { /* vararg */
s.next();
return {
type: "vararg"
}
}
case '{': {
return constructor(s);
}
case TK.TK_FUNCTION:{
s.next();
return body(s);
}
default: {
return suffixedexp(s);
}
}
}
var priority = [
null,
[6, 6], [6, 6], [7, 7], [7, 7], [7, 7], /* `+' `-' `*' `/' `%' */
[10, 9], [5, 4], /* ^, .. (right associative) */
[3, 3], [3, 3], [3, 3], /* ==, <, <= */
[3, 3], [3, 3], [3, 3], /* ~=, >, >= */
[2, 2], [1, 1] /* and, or */
];
var opname = [
null,
"op.add", "op.minus", "op.mul", "op.div", "op.mod",
"op.pow", "op.concat",
"op.equal", "op.less", "op.lessequal",
"op.notequal", "op.great", "op.greatequal",
"op.and", "op.or"
]
exports.opname = opname;
var UNARY_PRIORITY = 8;
function subexpr(s, limit){
var ret;
var uop = getunopr(s);
if (uop) {
s.next();
ret = subexpr(s, UNARY_PRIORITY);
ret = {
type: 'expr.uop',
op: uop,
operand: ret
}
} else {
ret = simpleexp(s);
}
var op = getbinopr(s);
while (op && priority[op][0] > limit){
s.next();
var e2 = subexpr(s, priority[op][1]);
ret = {
type: 'expr.op',
op: opname[op],
left: ret,
right: e2
}
op = getbinopr(s);
}
return ret;
}
function expr(s){
return subexpr(s, 0);
}
function explist(s){
var exps = [];
exps.push(expr(s));
while (testnext(s, ',')){
exps.push(expr(s));
}
return exps;
}
function assignment(s, lefts){
while (testnext(s, ',')){
lefts.push(suffixedexp(s));
}
checknext(s, '=');
return {
type: "stat.assignment",
lefts: lefts,
right: explist(s)
}
}
function exprstat(s){
var exp1 = suffixedexp(s);
if (s.curr() == '=' || s.curr() == ',') {
return assignment(s, [exp1]);
} else {
if (exp1.type != "expr.call" && exp1.type != "expr.callMethod" && exp1.type != "expr.javascript"){
throw new Error("syntax error, unexpected expr type "+exp1.type);
}
return {
"type": "stat.expr",
"expr": exp1
};
}
}
function listfield(s){
return {
"type": "field.list",
"val": expr(s)
}
}
function recfield(s){
var key;
if (tokentype(s.curr()) == TK.TK_NAME){
key = checkname(s);
} else {
key = yindex(s);
}
checknext(s, '=');
return {
"type": "field.rec",
"key": key,
"val": expr(s)
}
}
function field(s){
var curr = s.curr();
switch (tokentype(curr)){
case TK.TK_NAME:{
if (s.lookAhead() != '=') {
return listfield(s);
} else {
return recfield(s);
}
}
case '[': {
return recfield(s);
}
default: {
return listfield(s);
}
}
}
function constructor(s){
checknext(s, '{');
var fields = [];
do {
if (s.curr() == '}') break;
var fi = field(s);
if (fi){
fields.push(fi);
}
} while (testnext(s, ',') || testnext(s, ';'));
checknext(s, '}');
return {
"type": "expr.constructor",
"fields": fields
}
}
function test_then_block(s, target){
target.cond = expr(s);
checknext(s, TK.TK_THEN);
target.tblock = block(s);
}
function ifstat(s){
var root = {
type: "stat.if"
}
var current = root;
s.next(); //skip if
test_then_block(s, current);
while (testnext(s, TK.TK_ELSEIF)) { /*elseif */
current.fblock = {
type: "block",
stats: [
{
type: "stat.if"
}
]
}
current = current.fblock.stats[0];
test_then_block(s, current);
}
if (testnext(s, TK.TK_ELSE)){
current.fblock = block(s);
}
checknext(s, TK.TK_END);
return root;
}
function whilestat(s){
s.next(); // skip while
var cond = expr(s);
checknext(s, TK.TK_DO);
var blk = block(s);
checknext(s, TK.TK_END);
return {
type: "stat.while",
cond: cond,
block: blk
}
}
function fornum(s, varname){
s.next(); //skip '='
var from = expr(s);
checknext(s, ',');
var to = expr(s);
var step;
if (testnext(s, ',')) {
step = expr(s);
}
return {
type: "stat.fornum",
varname: varname,
from: from,
to: to,
step: step
}
}
function forlist(s, varnames){
while (testnext(s, ',')){
varnames.push(str_checkname(s));
}
checknext(s, TK.TK_IN);
return {
type: "stat.forlist",
varnames: varnames,
explist: explist(s)
}
}
function forstat(s){
s.next(); // skip for;
var varname = str_checkname(s);
var ret;
switch (s.curr()){
case '=':
ret = fornum(s, varname);
break;
case ',':
case TK.TK_IN:
ret = forlist(s, [varname]);
break;
default:
throw "`=` or `in` expected"
}
checknext(s, TK.TK_DO); // skip do
ret.block = block(s);
checknext(s, TK.TK_END);
return ret;
}
function repeatstat(s){
s.next();
var b = block(s);
checknext(s, TK.TK_UNTIL);
return {
type: "stat.repeat",
until: expr(s),
block: b
}
}
function funcname(s){
var ret = singlevar(s);
while (testnext(s, '.')){
var key = checkname(s);
ret = {
type: "expr.index",
self: ret,
key: key
}
}
if (testnext(s, ':')) {
var key = checkname(s);
return {
type: "stat.method",
self: ret,
key: key
}
} else {
return {
type: "stat.function",
left: ret
}
}
}
function funcstat(s){
s.next();
var f = funcname(s);
f.func = body(s);
return f;
}
function localfunc(s){
var name = str_checkname(s);
return {
type: "stat.localfunction",
name: name,
func: body(s)
}
}
function localstat(s){
var names = [];
do{
names.push(str_checkname(s));
} while (testnext(s, ','));
var right;
if (testnext(s, '=')){
right = explist(s);
}
return {
type: "stat.local",
names: names,
right: right
}
}
function retstat(s, isJSReturn){
var ret = {
type: isJSReturn ? 'stat.jsreturn' : "stat.return",
nret: []
};
s.next();
if (block_follow(s) || s.curr() == ';'){
} else {
ret.nret = explist(s);
}
//skip all ';' after return
while (testnext(s, ';')){
}
return ret;
}
function statement(s){
var curr = s.curr();
switch(curr){
case ';': { /* stat -> ';' (empty statement) */
s.next(); /* skip ';' */
break;
}
//TODO:
case TK.TK_IF:
return ifstat(s);
case TK.TK_WHILE:
return whilestat(s);
case TK.TK_DO:{
s.next();
var ret = block(s);
checknext(s, TK.TK_END);
return ret;
}
case TK.TK_FOR:
return forstat(s);
case TK.TK_REPEAT:
return repeatstat(s);
case TK.TK_FUNCTION:
return funcstat(s);
case TK.TK_LOCAL:{
s.next();
if (testnext(s, TK.TK_FUNCTION)){
return localfunc(s);
} else {
return localstat(s);
}
}
case TK.TK_RETURN: {
return retstat(s);
}
case TK.TK_JSRETURN:{
return retstat(s, true);
}
case TK.TK_BREAK: {
s.next()
return {
type:"stat.break"
}
}
// case TK.TK_DBCOLON:
// case TK.TK_GOTO:
default:{
return exprstat(s);
}
}
}
function block_follow(s){
switch (s.curr()){
case TK.TK_ELSE:
case TK.TK_ELSEIF:
case TK.TK_END:
case TK.TK_EOS:
case TK.TK_UNTIL:
return true;
default:
return 0;
}
}
function statlist(s){
var ret = [];
while (!block_follow(s)){
if (s.curr() == TK.TK_RETURN || s.curr() == TK.TK_JSRETURN){
ret.push(statement(s));
break;
}
var stat = statement(s);
if (stat) {
ret.push(stat);
}
}
return ret;
}
function block(s){
return {
type: 'block',
stats: statlist(s)
}
}
function parlist(s){
var ret = [];
if (s.curr() != ')'){
do {
switch (tokentype(s.curr())){
case TK.TK_NAME:{
ret.push(str_checkname(s));
break;
}
case TK.TK_DOTS:{
ret.push(TK.TK_DOTS);
s.next();
break;
}
default:
throw "<name> or `...` expected";
}
} while(testnext(s ,','));
}
return ret;
}
function body(s){
checknext(s, '(');
var args = parlist(s);
var varargs = false;
if (args.length > 0 && args[args.length - 1] == TK.TK_DOTS) {
args.pop();
varargs = true;
}
checknext(s, ')');
var body = block(s);
checknext(s, TK.TK_END);
return {
type: 'function',
args: args,
varargs: varargs,
block: body
}
}
function main(s){
var ret = {
type: "function",
args: [],
varargs: true,
block: block(s)
};
check(s, TK.TK_EOS);
return ret;
}
exports.main = main;
function parse(s){
return exports.main(lex(s));
}
exports.parse = parse;
})(modules.parser);
(function(exports){
/**
* Created by Yun on 2014/9/24.
*/
var PRETTY = true;
var gens = {};
var indent, pushIndent, popIndent;
if (PRETTY) {
indent = "\n";
pushIndent = function () {
indent = indent + " ";
}
popIndent= function (){
indent = indent.substr(0, indent.length - 4);
}
} else {
indent = "";
pushIndent = function (){}
popIndent = function (){}
}
gens["function"] = function(ast){
return "l._f(function(" + ast.args.join(',')+")" + codegen(ast.block)+")";
}
gens["block"] = function(ast){
var codes = [];
pushIndent();
for (var i = 0; i < ast.stats.length; i++){
codes.push(codegen(ast.stats[i]));
}
popIndent();
return indent + "{" + codes.join("") + indent + "}";
}
gens["stat.getvaargs"] = function(ast){
return indent + "var __VA_ARG = Array.prototype.slice.call(arguments, " + ast.argCount +");";
}
gens["stat.expr"] = function(ast){
return indent + codegen(ast.expr) + ";";
}
gens["stat.local"] = function(ast){
if (!ast.right){
return indent + "var " + ast.names.join(',') + ";"
} else if (ast.names.length == 1){
// a = exp
return indent + "var " + ast.names[0] + " = " + codegenSimpleVar(ast.right[0]) + ";";
} else {
// var list = explist
var names = [];
for (var i = 0; i < ast.names.length; i++){
names.push(ast.names[i] + " = t[" + i + "]");
}
//TODO: use a generated template varaiable name.
return indent + "var t =" + codegenVarList(ast.right)+ "," +
indent + names.join(",") + ";";
}
}
gens["stat.if"] = function(ast){
var base = indent + "if (" + codegenSimpleVar(ast.cond) + ")" + codegen(ast.tblock);
if (ast.fblock && ast.fblock.stats.length > 0){
if (ast.fblock.stats.length == 1 && ast.fblock.stats[0].type == "stat.if") {
//try generate better code for elseif.
return base + "else" + codegen(ast.fblock.stats[0]);
} else {
return base + "else" + codegen(ast.fblock);
}
}
return base;
}
function codegenChecknumber(ast){
if (ast.type == "const.number"){
return codegen(ast);
}
return "l.__checknumber("+codegenSimpleVar(ast)+")";
}
gens["stat.fornum"] = function(ast){
if (!ast.step){
return indent + "for (var "+ast.varname+", " +
ast.$var + " = " + codegenChecknumber(ast.from) + ", " +
ast.$limit + " = " + codegenChecknumber(ast.to) + ";" +
"("+ast.varname+"="+ast.$var+")<="+ast.$limit+";++" + ast.$var +")" +
codegen(ast.block);
} else {
pushIndent();
var block =
indent + ast.varname + " = " + ast.$var + ";" +
codegen(ast.block) +
indent + ast.$var + " += " + ast.$step;
popIndent();
return indent + "var " +
ast.$var + " = " + codegenChecknumber(ast.from) + ", " +
ast.$limit + " = " + codegenChecknumber(ast.to) + ", " +
ast.$step + " = " + codegenChecknumber(ast.step) + "," +
ast.varname + ";" +
indent + "while ((" + ast.$step + ">0 && " + ast.$var + "<=" + ast.$limit + ") || " +
"(" + ast.$step + "<0 && " + ast.$var + ">=" + ast.$limit + ")){" +
block +
indent + "}";
}
}
gens["stat.forlist"] = function(ast){
pushIndent();
var st = ast.$st;
var ret = [];
ret.push(indent + "var t = " + st+"[0]("+st+"[1],"+st+"[2]);");
for (var i = 0; i < ast.varnames.length; ++i){
ret.push(indent + "var " +ast.varnames[i] + " = t[" + i + "];");
}
ret.push(indent + "if ("+ ast.varnames[0]+" == null) break;");
ret.push(indent + st+ "[2] = " + ast.varnames[0] + ";");
ret.push(codegen(ast.block));
popIndent();
return indent + "var " + st + " = " + codegenVarList(ast.explist)+";" +
indent + "for (;;)" +
indent + "{" +
ret.join("") +
indent + "}"
}
gens["stat.while"] = function(ast){
return indent + "while ("+ codegenSimpleVar(ast.cond)+")" + codegen(ast.block);
}
gens["stat.repeat"] = function(ast){
return indent + "do " + codegen(ast.block) + "while (!(" + codegenSimpleVar(ast.until)+"));";
}
gens["stat.break"] = function(ast){
return indent + "break;"
}
function isVarExpr(exp){
if (exp.type == "vararg" || exp.type == "expr.call" || exp.type == "expr.callMethod") {
return true;
}
}
function isVarlist(explist){
if (!explist.length){
return false;
}
return isVarExpr(explist[explist.length - 1]);
}
function codegenSimpleVar(exp){
if (isVarExpr(exp)) {
return ('('+codegen(exp)+')' + "[0]");
} else {
return (codegen(exp));
}
}
function codegenVarList(explist){
if (isVarlist(explist)){
if (explist.length == 1){
return codegen(explist[0]);
}
var pres = [];
for (var i = 0; i < explist.length-1; i++){
pres.push(codegenSimpleVar(explist[i]));
}
return "[" + pres.join(',')+"].concat(" + codegen(explist[explist.length - 1]) + ")";
} else {
var pres = [];
for (var i = 0; i < explist.length; i++){
pres.push(codegenSimpleVar(explist[i]));
}
return "[" + pres.join(',')+"]";
}
}
gens["vararg"] = function(ast){
//TODO: throw a error when use __VA_ARG not in a va_arg function.
return "__VA_ARG";
}
gens["stat.return"] = function(ast){
if (isVarlist(ast.nret)){
return indent + "return " + codegenVarList(ast.nret);
} else {
var nrets = [];
for (var i = 0; i < ast.nret.length; i++){
nrets.push(codegenSimpleVar(ast.nret[i]));
}
return indent + "return [" + nrets + "];";
}
}
gens["stat.jsreturn"] = function(ast){
return indent + "return " + codegenSimpleVar(ast.nret);
}
gens["stat.assignment"] = function(ast){
if (ast.lefts.length == 1){
//variable = explist
var right = ast.right.length == 1 ? codegenSimpleVar(ast.right[0]) : (codegenVarList(ast.right) + "[0]");
// single assignment
if (ast.lefts[0].type == "expr.index") {
//a[key] = value
var tar = ast.lefts[0];
return indent + "l.__set(" + codegenSimpleVar(tar.self)+", " +codegen(tar.key) + ", " + right + ");";
}
return indent + codegen(ast.lefts[0]) + " = " + right + ";";
} else {
// list = explist
var ret = [];
ret.push(indent + "var t = " + codegenVarList(ast.right) +";");
for (var i = 0; i < ast.lefts.length; ++i){
if (ast.lefts[i].type == "expr.index") {
var tar = ast.lefts[i];
ret.push(indent + "l.__set(" + codegenSimpleVar(tar.self) + "," + codegen(tar.key) + ",t[" +i+"]);");
} else {
ret.push(indent + codegen(ast.lefts[i]) + " = t[" + i + "];");
}
}
return ret.join("");
}
}
gens["expr.index"] = function(ast){
return "l.__get(" + codegenSimpleVar(ast.self) + "," + codegen(ast.key)+")";
}
gens["expr.op"] = function(ast){
var func;
var op;
switch(ast.op){
case "op.add":
func = "l.__add";
break;
case "op.minus":
func = "l.__sub";
break;
case "op.mul":
func = "l.__mul";
break;
case "op.div":
func = "l.__div";
break;
case "op.mod":
func = "l.__mod";
break;
case "op.pow":
func = "l.__pow";
break;
case "op.concat":
func = "l.__concat";
break;
case "op.equal":
func = "l.__eq";
break;
case "op.less":
func = "l.__lt";
break;
case "op.lessequal":
func = "l.__le";
break;
case "op.notequal":
func = "l.__neq";
break;
case "op.great":
func = "l.__gt";
break;
case "op.greatequal":
func = "l.__ge";
break;
case "op.and":
op = "&&";
break;
case "op.or":
op = "||";
break;
default:
throw new Error(ast.op + " is not implemented yet.");
}
if (op){
return codegenSimpleVar(ast.left) + op + codegenSimpleVar(ast.right);
} else if (func){
return func +"(" + codegenSimpleVar(ast.left) + "," + codegenSimpleVar(ast.right) + ")";
}
}
gens["expr.uop"] = function(ast){
switch (ast.op){
case "uop.minus":
return "l.__unm(" + codegenSimpleVar(ast.operand)+")";
case "uop.not":
return "!("+codegenSimpleVar(ast.operand)+")";
case "uop.len":
return "l.__len("+codegenSimpleVar(ast.operand) +")";
default:
throw new Error(ast.op + " is not implemented yet.");
}
}
gens["expr.call"] = function(ast){
var func = codegenSimpleVar(ast.func);
return "l.__call("+func+","+ codegenVarList(ast.args) +")";
}
gens["expr.callMethod"] = function(ast){
return "l.__callMethod(" + codegenSimpleVar(ast.self) + "," + codegenSimpleVar(ast.key) + "," + codegenVarList(ast.args) +")";
}
gens["expr.brackets"] = function(ast){
if (isVarExpr(ast.expr)) {
return codegenSimpleVar(ast.expr);
} else {
return '(' + codegen(ast.expr)+ ")";
}
}
gens["expr.constructor"] = function(ast){
if (!ast.fields.length){
return "l.__newTable()";
}
var fields = [];
for (var i = 0; i < ast.fields.length; i++){
var f = ast.fields[i];
if (f.type == "field.list"){
if (isVarExpr(f.val) && i == ast.fields.length - 1){
fields.push("[2, " + codegen(f.val) + "]");
} else {
fields.push("[0, " + codegenSimpleVar(f.val) + "]");
}
} else if (f.type == "field.rec"){
fields.push("[1, " + codegen(f.key) + "," + codegenSimpleVar(f.val) + "]");
} else {
throw new Error("Invalid field type "+ f.type);
}
}
return "l.__newTable([" + fields.join(",")+ "])";
}
gens["variable"] = function(ast){
return ast.val;
}
gens["const.string"] = function(ast){
return ast.val ? JSON.stringify(ast.val) : "l.ds";
}
gens["const.number"] = function(ast){
return ast.val ? JSON.stringify(ast.val) : "l.d0";
}
gens["const.boolean"] = function(ast){
return JSON.stringify(ast.val);
}
gens["const.nil"] = function(ast){
return "null";
}
gens["expr.javascript"] = function(ast){
if (ast.args.length != 1){
throw new Error("__javascript should have exactly one arguments.");
}
if (ast.args[0].type == "const.string") {
return '(' + ast.args[0].val + ")";
} else {
return 'eval(' + codegenSimpleVar(ast.args[0]) +")";
}
}
function codegen(ast){
if (gens[ast.type]){
return gens[ast.type](ast);
}
throw new Error("Unsupported ast type " + ast.type);
}
var childfields = {
"expr.uop": ["operand"],
"expr.op": ["left", "right"],
"stat.assignment":["lefts", "right"],
"stat.expr": ["expr"],
"field.list": ["val"],
"field.rec": ["key", "val"],
"expr.constructor": ["fields", "recs", "list"],
"stat.if": ["cond", "tblock", "fblock"],
"stat.fornum": ["from", "to", "step", "block"],
"stat.forlist": ["explist", "block"],
"stat.while": ["cond", "block"],
"stat.repeat": ["until", "block"],
"expr.index": ["self", "key"],
"expr.callMethod": ["self", "key", "args"],
"expr.call": ["func", "args"],
"expr.brackets": ["expr"],
"stat.method": ["self", "key", "func"],
"stat.function": ["left", "func"],
"stat.localfunction": ["func"],
"stat.local": ["right"],
"stat.return": ["nret"],
"function": ["block"],
"block": ["stats"]
}
function traverse(func, out){
return function (ast) {
function work(curr, parent) {
if (curr && curr.constructor == Array) {
for (var i = 0; i < curr.length; i++) {
curr[i] = work(curr[i], parent);
}
return curr;
} else if (curr && curr.type) {
var ret = func(curr, parent) || curr;
var fields = childfields[ret.type];
if (fields) {
for (var i = 0; i < fields.length; i++) {
ret[fields[i]] = work(ret[fields[i]], ret);
}
}
if (out){
ret = out(ret, parent) || ret;
}
return ret;
}
}
return work(ast);
}
}
exports.traverse = traverse;
exports.phases = [];
exports.postphases = [];
exports.run = function(ast){
// console.log("\n");
for (var i= 0; i < exports.phases.length; i++){
// console.log(require("jsonf")(JSON.stringify(ast)));
ast = exports.phases[i](ast);
// console.log("\n");
}
//console.log(require("jsonf")(JSON.stringify(ast)));
var ret = codegen(ast);
// var ret = "";
for (var i= 0; i < exports.postphases.length; i++){
// console.log(ret);
ret = exports.postphases[i](ret);
}
// console.log(ret);
return ret;
};
// process stat.localfunc & stat.local
(function(){
exports.phases.push(traverse(function(ast){
switch (ast.type ) {
case 'function':{
if (ast.varargs){
ast.block.stats.unshift({
type: "stat.getvaargs",
argCount: ast.args.length
});
}
// add return for function that does not return a value.
var stats = ast.block.stats;
if (stats.length == 0 || stats[stats.length-1].type != "stat.return"){
stats.push({
type: 'stat.return',
nret: []
});
}
break;
}
case "block":{
var out = [];
for (var i= 0; i < ast.stats.length; i++){
var curr = ast.stats[i];
switch (curr.type){
case 'stat.localfunction':{
// local function a -> local a = function()
out.push({
type: 'stat.local',
names: [curr.name]
});
out.push({
type: 'stat.assignment',
lefts: [{
type: "variable",
val: curr.name
}],
right: [curr.func]
})
break;
}
case 'stat.function':{
out.push({
type: 'stat.assignment',
lefts: [curr.left],
right: [curr.func]
});
break;
}
case 'stat.method':{
curr.func.args.unshift("self");
out.push({
type: 'stat.assignment',
lefts: [{
type: "expr.index",
self: curr.self,
key: curr.key
}],
right: [curr.func]
});
break;
}
default:
out.push(ast.stats[i]);
}
}
ast.stats = out;
}
}
}));
})();
//phase: check global variable;
//TODO: mark local variables with extra tag.
(function(){
exports.phases.push(function(ast){
var blocklevel = [];
var top;
var varNames = {};
var varId = 0;
ast = traverse(function(curr){
var names;
switch(curr.type){
case 'block':{
names = [];
break;
}
case 'function':{
names = curr.args;
break;
}
case 'stat.fornum':{
names = [curr.varname, "$var", "$limit", "$step"];
break;
}
case 'stat.forlist':{
names = curr.varnames;
names.push("$st");
break;
}
case 'variable':{
if (varNames[curr.val]) {
var ref;
var stack = varNames[curr.val];
if (top.curr.type == "fornum" || top.curr.type == "forlist"){
// defining for head.
// body will in another block;
//check whether there's another define.
if (stack[stack.length-1] != top.curr){
ref = stack[stack.length - 1];
} else if (stack.length > 1){
ref = stack[stack.length - 2];
}
} else {
ref = stack[stack.length - 1];
}
if (ref){
curr.val = curr.val + "$" + ref.id;
return ;
}
}
if (curr.val == '_ENV'){
return ;
}
// global variable. use _ENV[name]
return {
"type": "expr.index",
"self": {
"type": "variable",
"val": "_ENV"
},
"key": {
"type": "const.string",
"val": curr.val
}
}
}
}
if (names){
if (top) {
blocklevel.push(top);
}
top = {
curr: curr,
names: names.slice(0)
};
for (var i= 0; i < names.length; i++){
var name = names[i];
varNames[name] = varNames[name] || [];
var id = ++varId;
names[i] = name + "$" + id;
varNames[name].push({
id: id,
curr: curr
});
}
if (curr.type == 'stat.fornum'){
curr.varname = names[0];
curr.$var = names[1];
curr.$limit = names[2];
curr.$step = names[3];
} else if (curr.type == "stat.forlist") {
curr.$st = names.pop();
}
}
}, function(curr){
if (top && top.curr == curr) {
for (var i = 0; i < top.names.length; i++){
var name = top.names[i];
varNames[name].pop();//assert == blocklevel
if (varNames[name].length == 0){
delete varNames[name];
}
}
top = blocklevel.pop();
} else if (curr.type == 'stat.local') {
//Define local variables in current block.
// define when out of this statement to avoid effecting initialize list.
//add to current block;
for (var i= 0; i < curr.names.length; i++){
var name = curr.names[i];
varNames[name] = varNames[name] || [];
var stack = varNames[name];
if (stack.length > 0 && stack[stack.length-1] === top){
// variable redefined.
var id = stack[stack.length - 1].id = ++varId;
curr.names[i] = name + "$" + id;
continue;
}
var id = ++varId;
curr.names[i] = name + "$" + id;
stack.push({
id: id,
curr: top.curr
});
top.names.push(name);
}
}
})(ast);
return ast;
});
})();
// Fix issue #2
(function(){
exports.phases.push(function(ast) {
var stack = [];
ast = traverse(function(curr){
switch(curr.type){
case 'function':
{
if (stack.length > 0){
var top = stack[stack.length-1];
for (var i= 0; i < top.length; i++){
top[i].has_closure = true;
}
}
stack.push([]);
break;
}
case 'stat.fornum':
case 'stat.forlist':
case 'stat.repeat':
case 'stat.while':
{
stack[stack.length-1].push(curr);
}
}
}, function(curr){
switch(curr.type){
case 'function':
{
stack.pop();
break;
}
case 'stat.fornum':
case 'stat.forlist':
{
if (curr.has_closure){
var block = curr.block;
/*
for (var i= 0; i < 100; i++){
function(i){
}(i)
}
*/
var newBlock = {
"type": "block",
"stats": [
{
"type": "stat.expr",
"expr": {
"type": "expr.call",
"func": {
"type": "function",
"args": [
curr.varname
],
"varargs": false,
block: block
},
"args": [
{
"type" : "variable",
"val": curr.varname
}
]
}
}
]
}
curr.block = newBlock;
}
break;
}
case 'stat.repeat':
case 'stat.while':
{
var block = curr.block;
/*
for (var i= 0; i < 100; i++){
function(i){
}(i)
}
*/
var newBlock = {
"type": "block",
"stats": [
{
"type": "stat.expr",
"expr": {
"type": "expr.call",
"func": {
"type": "function",
"args": [
],
"varargs": false,
block: block
},
"args": [
]
}
}
]
}
curr.block = newBlock;
break;
}
}
})(ast);
return ast;
});
})();
var sign = "/*lua.js generated code*/";
exports.sign = sign;
//Post-Phase: add _ENV upvalue for whole scope.
(function() {
exports.postphases.push(function(code){
code = sign + 'return '+code+';';
return code;
});
})();
})(modules.codegen);
(function(exports){
/**
* Created by Yun on 2014/9/23.
*/
var dummy0 = {
_hashKey: -1,
valueOf:function(){return 0;},
toString:function(){return "0";}
};
function dstype(){
this._hashKey = -2;
this.toString = function(){return "";}
}
dstype.prototype = String.prototype;
var dummyStr = new dstype();
exports.dummy0 = dummy0;
exports.dummyStr = dummyStr;
var _hashKeyId = 0;
function LuaTable(fields){
this.stringMap = {};
this.array = [];
this.hashMap = {};
this.metatable = null;
this._hashKey = ++ _hashKeyId;
if (fields){
for (var i = 0 ;i < fields.length; i++){
var v = fields[i];
if (v[0] == 0){
this.array.push(v[1]);
} else if (v[0] == 1) {
this.set(v[1],v[2]);
} else {
for (var j = 0; j < v[1].length; j++){
this.array.push(v[1][j]);
}
}
}
}
}
exports.LuaTable = LuaTable;
exports.newHashKey = function(){
return ++ _hashKeyId;
}
LuaTable.prototype = {};
LuaTable.prototype.constructor = LuaTable;
LuaTable.prototype.get = function(k){
switch(typeof(k)){
case 'number':
return this.array[k-1];
case 'string':
return this.stringMap[k];
case 'object':
if (k === dummy0){
return this.array[-1];
}
if (k === dummyStr){
return this.stringMap[k];
}
if (k === null){
throw new Error("table index is nil");
}
case 'function':{
if (!k._hashKey) {
//throw new Error("get with a invalid object" + k);
k._hashKey = ++ _hashKeyId;
}
var rec = this.hashMap[k._hashKey];
return rec && rec[1];
}
default:{
throw new Error("get with a invalid argument" + k);
}
}
}
LuaTable.prototype.set = function(k, v){
if (k == null){
throw new Error("table index is nil");
}
if (v == null){
switch(typeof(k)){
case 'number':
if (k == this.array.length){
this.array.pop();
} else {
delete this.array[k-1];
}
break;
case 'string':
delete this.stringMap[k];
break;
case 'object':case 'function':{
if (!k._hashKey) {
// ignore object that is not a key.
return;
}
delete this.hashMap[k._hashKey];
break;
}
default:{
throw new Error("set with a invalid argument" + k);
}
}
return;
}
switch(typeof(k)){
case 'number':
this.array[k-1] = v;
break;
case 'string':
this.stringMap[k] = v;
break;
case 'object':case 'function':{
if (!k._hashKey) {
k._hashKey = ++ _hashKeyId;
//throw new Error("set with a invalid object" + k);
}
this.hashMap[k._hashKey] = [k, v];
break;
}
default:{
throw new Error("set with a invalid argument" + k);
}
}
}
LuaTable.prototype.length = function(){
var len = this.array.length;
for (; len > 0 && this.array[len-1] == null; --len){}
if (len != this.array.length){
this.array.length = len;
}
return this.array.length;
}
LuaTable.prototype.toString = function(){
return ("table " + this._hashKey);
}
})(modules.types);
(function(exports){
/**
* Created by Yun on 2014/9/23.
*/
// Compile
var parser = require("./parser.js");
exports.parser = parser;
var codegen = require("./codegen.js");
exports.codegen = codegen;
var sign = codegen.sign;
function compile(s){
if (s.substr(0, sign.length) != sign) {
return codegen.run(parser.parse(s));
} else {
return s;
}
}
exports.compile = compile;
var types = require("./types.js");
exports.types = types;
var dummy0 = types.dummy0;
var dummyStr = types.dummyStr;
function LuaContext(){
if (!(this instanceof LuaContext)){
return new LuaContext();
}
// Globals for lua usage.
var _G = new types.LuaTable();
this._G = _G;
_G.set("_G", _G);
_G.set("_VERSION", "Lua 5.2");
_G.set("_LUAJS", "Lua.js 0.1");
var helpers = {};
helpers.d0 = dummy0;
helpers.ds = dummyStr;
helpers.__getmetatable = function(t){
if (!t){
return null;
}
switch(typeof(t)){
case 'object':
return t.metatable || ((t instanceof types.LuaTable) ? null : helpers.jsObjMT);
case 'string':
return helpers.stringMT;
case 'function':
if (!f.__lua_function){
return helpers.jsFuncMT;
}
default:
return null;
}
}
helpers._f = function(f){
f.__lua_function = true;
return f;
}
function getMTMethod(t, e){
var mt = helpers.__getmetatable(t);
return mt && mt.stringMap && mt.stringMap[e];
}
//getter & setter
helpers.__get = function(s, k){
var h;
if (s instanceof types.LuaTable){
var v = s.get(k);
if (v){
return v;
}
h = getMTMethod(s, "__index")
if (!h){
return null;
}
} else if (typeof(s) == 'object' || (typeof(s) == 'function' && !s.__lua_function)) {
if (typeof(s) == 'function' && s.__beforeBind){
s = s.__beforeBind;
}
var ret = typeof(k)=='number'?s[k-1]:s[k];
if (typeof(ret) == 'function'){
var ret1 = ret.bind(s);
ret1.__beforeBind = ret;
return ret1;
} else if (!ret && k == "new") {
var dummy = function(){}
dummy.prototype = s.prototype;
return function(){
var ret = new dummy();
t.apply(ret, arguments);
return ret;
}
}
return ret;
} else {
h = getMTMethod(s, "__index")
if (!h){
throw new Error("attempt to index a "+helpers.__type(s)+" value.");
}
}
if (typeof(h) == "function"){
return helpers.__call(h, [s, k])[0];
}
return helpers.__get(h, k);
}
helpers.__set = function(s, k, v){
var h;
if (s instanceof types.LuaTable){
var oldv = s.get(k);
if (oldv){
s.set(k, v);
return;
}
h = getMTMethod(s, "__newindex");
if (!h){
s.set(k, v);
return
}
} else if (typeof(s) == 'object' || (typeof(s) == 'function' && !s.__lua_function)) {
s[k] = v;
return;
} else {
h = getMTMethod(s, "__newindex")
if (!h){
throw new Error("attempt to index a "+helpers.__type(s)+" value.");
}
}
if (typeof(h) == "function"){
helpers.__call(h, [s,k,v]);
} else {
helpers.__set(h, k, v);
}
};
// operators:
// function defineNumberOper(name, opf){
// helpers[name] = function(a, b){
// if (typeof(a) == 'number' && typeof(b) == 'number'){
// return opf(a, b)||dummy0;
// }
// var o1 = helpers.__tonumber(a), o2 = helpers.__tonumber(b);
// if (o1 && o2) {
// return opf(o1, o2)||dummy0;
// }
// var h = getMTMethod(a, name) || getMTMethod(b, name)
// if (h){
// return helpers.__call(h, [a, b])[0];
// }
// throw new Error("attempt to perform arithmetic on a " + helpers.__type(a)+" value");
// }
// }
helpers.__add = function(a, b){
if (typeof(a) == 'number' && typeof(b) == 'number'){
return (a+b)||dummy0;
}
var o1 = helpers.__tonumber(a), o2 = helpers.__tonumber(b);
if (o1 && o2) {
return (a+b)||dummy0;
}
var h = getMTMethod(a, "__add") || getMTMethod(b, "__add")
if (h){
return helpers.__call(h, [a, b])[0];
}
throw new Error("attempt to perform arithmetic on a " + helpers.__type(a)+" value");
}
helpers.__sub = function(a, b){
if (typeof(a) == 'number' && typeof(b) == 'number'){
return (a-b)||dummy0;
}
var o1 = helpers.__tonumber(a), o2 = helpers.__tonumber(b);
if (o1 && o2) {
return (a-b)||dummy0;
}
var h = getMTMethod(a, "__sub") || getMTMethod(b, "__sub")
if (h){
return helpers.__call(h, [a, b])[0];
}
throw new Error("attempt to perform arithmetic on a " + helpers.__type(a)+" value");
}
helpers.__mul = function(a, b){
if (typeof(a) == 'number' && typeof(b) == 'number'){
return (a*b)||dummy0;
}
var o1 = helpers.__tonumber(a), o2 = helpers.__tonumber(b);
if (o1 && o2) {
return (a*b)||dummy0;
}
var h = getMTMethod(a, "__mul") || getMTMethod(b, "__mul")
if (h){
return helpers.__call(h, [a, b])[0];
}
throw new Error("attempt to perform arithmetic on a " + helpers.__type(a)+" value");
}
helpers.__div = function(a, b){
if (typeof(a) == 'number' && typeof(b) == 'number'){
return (a/b)||dummy0;
}
var o1 = helpers.__tonumber(a), o2 = helpers.__tonumber(b);
if (o1 && o2) {
return (a/b)||dummy0;
}
var h = getMTMethod(a, "__div") || getMTMethod(b, "__div")
if (h){
return helpers.__call(h, [a, b])[0];
}
throw new Error("attempt to perform arithmetic on a " + helpers.__type(a)+" value");
}
helpers.__mod = function(a, b){
if (typeof(a) == 'number' && typeof(b) == 'number'){
return (a%b)||dummy0;
}
var o1 = helpers.__tonumber(a), o2 = helpers.__tonumber(b);
if (o1 && o2) {
return (a%b)||dummy0;
}
var h = getMTMethod(a, "__mod") || getMTMethod(b, "__mod")
if (h){
return helpers.__call(h, [a, b])[0];
}
throw new Error("attempt to perform arithmetic on a " + helpers.__type(a)+" value");
}
helpers.__pow = function(a, b){
if (typeof(a) == 'number' && typeof(b) == 'number'){
return (Math.pow(a,b))||dummy0;
}
var o1 = helpers.__tonumber(a), o2 = helpers.__tonumber(b);
if (o1 && o2) {
return (Math.pow(a,b))||dummy0;
}
var h = getMTMethod(a, "__pow") || getMTMethod(b, "__pow")
if (h){
return helpers.__call(h, [a, b])[0];
}
throw new Error("attempt to perform arithmetic on a " + helpers.__type(a)+" value");
}
//
// defineNumberOper("__add", function(a,b){return a+b;});
// defineNumberOper("__mul", function(a,b){return a*b;});
// defineNumberOper("__sub", function(a,b){return a-b;});
// defineNumberOper("__div", function(a,b){return a/b;});
// defineNumberOper("__mod", function(a,b){return a%b;});
// defineNumberOper("__pow", function(a,b){return Math.pow(a,b);});
helpers.__unm = function(a){
var o = helpers.__tonumber(a);
if (o) {
return -o;
}
var h = getMTMethod(a).__unm
if (h) {
return helpers.__call(h, [a])[0];
}
throw new Error("attempt to perform arithmetic on a " + helpers.__type(a)+" value");
}
helpers.__eq = function(a, b){
return !helpers.__neq(a, b);
}
helpers.__neq = function(a,b){
if (a===b){
return false
}
var ta = helpers.__type(a);
var tb = helpers.__type(b);
if (ta != tb || (ta != 'table' && ta !="userdata")){
return true;
}
var h1 = getMTMethod(a, "__eq");
var h2 = getMTMethod(b, "__eq");
if (!h1 || h1 != h2){
return true;
}
return !(helpers.__call(h1, [a, b])[0]);
}
helpers.__gt = function(a, b){
return helpers.__lt(b, a);
}
helpers.__ge = function(a, b){
return helpers.__le(b, a);
}
helpers.__lt = function(a, b){
var ta = helpers.__type(a);
var tb = helpers.__type(b);
if ((ta=="number" && tb == "number") ||
(ta == "string" && tb == "string")) {
return a < b;
} else {
var h = getMTMethod(a, "__lt") || getMTMethod(a, "__lt");
if (h){
return !!(helpers.__call(h1, [a, b])[0]);
}
throw new Error("attempt to compare " + helpers.__type(a)+" with " + helpers.__type(b));
}
}
helpers.__le = function(a, b){
var ta = helpers.__type(a);
var tb = helpers.__type(b);
if ((ta=="number" && tb == "number") ||
(ta == "string" && tb == "string")) {
return a <= b;
} else {
var h = getMTMethod(a, "__le") || getMTMethod(a, "__le");
if (h){
return !!(helpers.__call(h1, [a, b])[0]);
}
h = getMTMethod(a, "__lt") || getMTMethod(a, "__lt");
if (h){
return !(helpers.__call(h1, [a, b])[0]);
}
throw new Error("attempt to compare " + helpers.__type(a)+" with " + helpers.__type(b));
}
}
helpers.__concat = function(a, b){
var ta = helpers.__type(a);
var tb = helpers.__type(b);
if ((ta == 'number' || ta == 'string') &&
(tb == 'number' || tb == 'string')){
return (""+a+b) || dummyStr;
}
var h = getMTMethod(a, "__concat") || getMTMethod(b, "__concat")
if (h){
return helpers.__call(h1, [a, b])[0];
}
throw new Error("attempt to concatenate a " + helpers.__type(a)+" value");
}
// other functions
helpers.__newTable = function(fields){
var ret = new types.LuaTable(fields);
return ret;
}
helpers.__len = function(c){
switch(typeof(c)){
case 'string':
return c.length;
}
var h = getMTMethod(c, "__len");
if (h){
return helpers.__call(h, [a])[0];
}
if (c instanceof types.LuaTable) {
return c.length();
}
if (typeof(c) == 'object' && c.length){
return c.length;
}
throw new Error("attempt to get length of a " + helpers.__type(c)+" value");
}
helpers.__tonumber = function(c){
switch(typeof(c)){
case 'number':
return c;
case 'string':
return parseInt(s) || dummy0;
default:
if (c == dummy0){
return c;
}
}
return null;
}
helpers.__checknumber = function(c){
switch(typeof(c)){
case 'number':
return c;
case 'string':
return parseInt(s);
}
if (c == dummy0){
return c;
}
throw new Error("Not a number.");
}
helpers.__tostring = function(c){
if (c == null){
return "nil";
}
switch(typeof(c)){
case 'number':case 'boolean':
return c.toString();
case 'string':
return c;
case 'function':
c._hashKey = c._hashKey || types.newHashKey();
return 'function('+ c._hashKey+")";
case 'object':
if (c == dummy0){
return "0";
}
if (c == dummyStr){
return dummyStr;
}
var h = getMTMethod(c, "__tostring");
if (h){
return helpers.__call(h, [c])[0];
}
if (c.toString) {
return c.toString();
}
default:
return "userdata("+ c + ")";
}
}
helpers.__type = function(c){
if (c == null){
return "nil";
}
var t = typeof(c);
switch(t){
case 'number':case 'boolean':case 'string':case 'function':
return t;
case 'object':
if (c == dummy0){
return "number";
}
if (c == dummyStr){
return dummyStr;
}
if (c instanceof types.LuaTable) {
return "table";
}
default:
return "userdata";
}
}
helpers.__call = function(f, args){
if (typeof(f) == 'function'){
if (f.__lua_function) {
return f.apply(null, args);
} else {
return [f.apply(null, args)];
}
}
var h = getMTMethod(f, "__call");
if (h){
args.unshift(h);
return helpers.__call(h, args);
}
throw new Error("attempt to call a " + helpers.__type(f)+" value");
}
helpers.__callMethod = function(s, k, args){
args.unshift(s);
//helpers.__get(s, k).apply(null, args);
return helpers.__call(helpers.__get(s, k), args);
}
helpers.__dump = function(f){
if (typeof(f) != "function"){
throw new Error("bad argument #1 to `dump` (function expected, got " + helpers.__type(f) + ")");
}
return codegen.sign + "return "+ f.toString()+";"
}
this.loadString = helpers.__loadString = function(s, env){
s = compile(s);
//TODO; add extra info for s
return new Function('_ENV', 'l', s)(env || _G, helpers);
}
this.loadStdLib = function(){
if (!exports.stdlib){
var fs = require("fs");
var path = require("path");
var code = fs.readFileSync(path.join(path.dirname(module.filename), "./stdlib.lua"), {encoding:'utf-8'});
exports.stdlib = new Function('_ENV', 'l', exports.compile(code));
}
exports.stdlib(_G, helpers)();
}
this.__helpers = helpers;
}
LuaContext.prototype = {}
exports.LuaContext = exports.newContext = LuaContext;
})(modules.index);
modules.index.stdlib = (function(_ENV, l){
/*lua.js generated code*/return l._f(function()
{
var __VA_ARG = Array.prototype.slice.call(arguments, 0);
l.__set(_ENV, "assert", l._f(function(v$1,msg$2)
{
if ((!(v$1)))
{
l.__call(l.__get(_ENV,"error"),[msg$2||"Assertion failed!"]);
}
return [];
}));
l.__set(_ENV, "collectgarbage", l._f(function()
{
return [];
}));
var notImplemented$3;
notImplemented$3 = l._f(function(fn$4)
{
l.__call(l.__get(_ENV,"error"),["Not implemented"]);
return [];
});
l.__set(_ENV, "dofile", notImplemented$3);
l.__set(_ENV, "error", (
function(val){
if (typeof(val) =='string')
throw new Error(val);
throw val;
}
));
l.__set(_ENV, "getmetatable", (
function(val){
return val && val.metatable;
}
));
l.__set(_ENV, "ipairs", l._f(function(t$5)
{
return [l._f(function(t$6,i$7)
{
if ((l.__ge(i$7,l.__len(t$6))))
{
return [];
}
return [l.__add(i$7,1),l.__get(t$6,l.__add(i$7,1))];
}),t$5,l.d0];
}));
l.__set(_ENV, "loadString", (function(str, env){
return l.__loadString(str, env);
}
));
l.__set(_ENV, "load", l._f(function(ld$8,source$9,mod$10,env$11)
{
if ((l.__eq((l.__call(l.__get(_ENV,"type"),[ld$8]))[0],"function")))
{
var chunks$12 = l.__newTable();
var $st$14 = [ld$8];
for (;;)
{
var t = $st$14[0]($st$14[1],$st$14[2]);
var v$13 = t[0];
if (v$13 == null) break;
$st$14[2] = v$13;
{
l.__call(l.__get(l.__get(_ENV,"table"),"insert"),[l.__get(_ENV,"chunk"),v$13]);
}
}
ld$8 = (l.__call(l.__get(l.__get(_ENV,"table"),"concat"),[chunks$12]))[0];
}
return l.__call(l.__get(_ENV,"loadString"),[ld$8,env$11])
}));
l.__set(_ENV, "loadfile", notImplemented$3);
l.__set(_ENV, "next", notImplemented$3);
l.__set(_ENV, "pairs", notImplemented$3);
l.__set(_ENV, "pcall", (l._f(function (f){
var args = Array.prototype.slice.call(arguments, 1);
try{
var ret = l.__call(f, args);
ret.unshift(true);
return ret;
} catch(e){
return [false, e];
}
})
));
l.__set(_ENV, "print", l._f(function()
{
var __VA_ARG = Array.prototype.slice.call(arguments, 0);
var args$15 = l.__newTable([[2, __VA_ARG]]);
for (var i$16, $var$17 = 1, $limit$18 = l.__checknumber(l.__len(args$15));(i$16=$var$17)<=$limit$18;++$var$17)
{
l.__set(args$15, i$16, (l.__call(l.__get(_ENV,"tostring"),[l.__get(args$15,i$16)]))[0]);
}
l.__call(l.__get(l.__get(_ENV,"io"),"write"),l.__call(l.__get(l.__get(_ENV,"table"),"concat"),[args$15," "]));
l.__call(l.__get(l.__get(_ENV,"io"),"write"),["\n"]);
return [];
}));
l.__set(_ENV, "rawequal", (function (a, b){
return a === b;
}
));
l.__set(_ENV, "rawget", (function (t, k){
var tp = l.__type(t);
if (tp != 'table') {
throw Error("rawget called with type " + tp);
}
return t.get(k);
}
));
l.__set(_ENV, "rawlen", (function (t){
var tp = l.__type(t);
if (tp != 'table') {
throw Error("rawlen called with type " + tp);
}
return t.length();
}
));
l.__set(_ENV, "rawset", (function (t, k, v){
var tp = l.__type(t);
if (tp != 'table') {
throw Error("rawget called with type " + tp);
}
t.set(k, v);
return t;
}
));
l.__set(_ENV, "select", (function (index){
if (index == '#') {
return arguments.length;
}
return arguments[index];
}
));
l.__set(_ENV, "setmetatable", (
function(val, mt){
if (typeof(val) != "object") {
throw new Error("Cannot set metatable to non-object values.");
}
val.metatable = mt;
return val;
}
));
l.__set(_ENV, "tonumber", (l.__tonumber));
l.__set(_ENV, "tostring", (l.__tostring));
l.__set(_ENV, "type", (l.__type));
l.__set(_ENV, "xpcall", (function (f, msgh){
var args = Array.prototype.slice.call(arguments, 2);
try{
return l.__call(f, args);
} catch(e){
return l.__call(msgh, [e]);
}
}
));
l.__set(_ENV, "package", l.__newTable());
l.__set(l.__get(_ENV,"package"), "loaded", l.__newTable());
l.__set(l.__get(_ENV,"package"), "preload", l.__newTable());
l.__set(_ENV, "require", l._f(function(modname$20)
{
var mod$21 = l.__get(l.__get(l.__get(_ENV,"package"),"loaded"),modname$20);
if ((mod$21))
{
return [mod$21];
}
var func$22,extra$23;
var $st$26 = l.__call(l.__get(_ENV,"ipairs"),[l.__get(l.__get(_ENV,"package"),"searchers")]);
for (;;)
{
var t = $st$26[0]($st$26[1],$st$26[2]);
var i$24 = t[0];
var v$25 = t[1];
if (i$24 == null) break;
$st$26[2] = i$24;
{
var t = l.__call(v$25,[modname$20]);
func$22 = t[0];
extra$23 = t[1];
if ((func$22))
{
break;
}
}
}
var ret$27 = (l.__call(func$22,[modname$20,extra$23]))[0];
l.__set(l.__get(l.__get(_ENV,"package"),"loaded"), modname$20, l.__get(l.__get(l.__get(_ENV,"package"),"loaded"),modname$20)||ret$27);
return [l.__get(l.__get(l.__get(_ENV,"package"),"loaded"),modname$20)];
}));
l.__set(_ENV, "string", l.__newTable());
(l.stringMT) = l.__get(_ENV,"string");
l.__set(l.__get(_ENV,"string"), "byte", (l._f(function (s, i, j){
if (typeof(s) != 'string'){
s = l.__tostring(s);
if (s == l.ds){
// Empty string.
return [];
}
}
i = i || 1;
j = j || i;
if (i < 0){
i = s.length + i;
} else {
i--;
}
if (j < 0){
j = s.length + j + 1;
}
var ret = [];
for (; i<j; ++i){
var c = s.charCodeAt(i);
if (c){
ret.push(c);
}
}
return ret;
})
));
l.__set(l.__get(_ENV,"string"), "dump", (l.__dump));
l.__set(l.__get(_ENV,"string"), "find", (function (s, pattern, init, plain){
if (plain){
return s.indexOf(pattern, init && (init-1))+1
}
throw new Error("Not implemented.")
}
));
l.__set(l.__get(_ENV,"string"), "len", (function (s){
if (typeof(s) != 'string' && s != l.ds){
s = l.__tostring(s);
}
if (s == l.ds){
// Empty string.
return 0;
}
return s.length;
}
));
l.__set(l.__get(_ENV,"string"), "lower", (function (s){
if (typeof(s) != 'string' && s != l.ds){
s = l.__tostring(s);
}
if (s == l.ds){
// Empty string.
return s;
}
return s.toLowerCase();
}
));
l.__set(l.__get(_ENV,"string"), "upper", (function (s){
if (typeof(s) != 'string' && s != l.ds){
s = l.__tostring(s);
}
if (s == l.ds){
// Empty string.
return s;
}
return s.toUpperCase();
}
));
l.__set(l.__get(_ENV,"string"), "rep", (function (s, n, sep){
if (sep){
return new Array(n).join(s+sep) + s;
} else {
return new Array(n+1).join(s);
}
}
));
l.__set(l.__get(_ENV,"string"), "reverse", (function (s){
if (typeof(s) != 'string' && s != l.ds){
s = l.__tostring(s);
}
if (s == l.ds){
// Empty string.
return s;
}
return s.split("").reverse().join("")
}
));
l.__set(l.__get(_ENV,"string"), "sub", (function (s, i, j){
if (typeof(s) != 'string' && s != l.ds){
s = l.__tostring(s);
}
if (s == l.ds){
// Empty string.
return [s];
}
j = j || s.length;
if (i < 0){
i = s.length + i;
} else {
i--;
}
if (j < 0){
j = s.length + j + 1;
}
return s.substring(s, i, j);
}
));
l.__set(_ENV, "table", l.__newTable());
l.__set(l.__get(_ENV,"table"), "concat", (function (list, sep, i, j){
list = list.array;
if (i){
if (j){
list = list.slice(i-1, j-1);
} else {
list = list.slice(i-1);
}
}
return list.join(sep || "")
}
));
l.__set(l.__get(_ENV,"table"), "insert", (function (t, pos, value){
if (pos){
t.array.splice(pos-1, 0, value);
} else {
t.array.push(value);
}
}
));
l.__set(l.__get(_ENV,"table"), "pack", l._f(function()
{
var __VA_ARG = Array.prototype.slice.call(arguments, 0);
return [l.__newTable([[2, __VA_ARG]])];
}));
l.__set(_ENV, "pack", l.__get(l.__get(_ENV,"table"),"pack"));
l.__set(l.__get(_ENV,"table"), "remove", (l._f(function (t, pos){
if (pos){
return t.array.splice(pos-1, 1);
} else {
return [t.array.pop()];
}
})
));
l.__set(l.__get(_ENV,"table"), "sort", (function (t, comp){
if (comp){
t.array.sort(function(){
if (comp(a, b)[0]){
return -1;
} else if (comp(b, a)[0]){
return 1;
}
return 0;
})
} else {
t.array.sort(function(a, b){
if (l.__lt(a, b)){
return -1;
} else if (l.__lt(b, a)){
return 1;
}
return 0;
});
}
}
));
l.__set(l.__get(_ENV,"table"), "unpack", (l._f(function (t){
return t.array;
})
));
l.__set(_ENV, "math", l.__newTable());
l.__set(l.__get(_ENV,"math"), "abs", (Math.abs));
l.__set(l.__get(_ENV,"math"), "acos", (Math.acos));
l.__set(l.__get(_ENV,"math"), "asin", (Math.asin));
l.__set(l.__get(_ENV,"math"), "atan", (Math.atan));
l.__set(l.__get(_ENV,"math"), "atan2", (Math.atan2));
l.__set(l.__get(_ENV,"math"), "ceil", (Math.ceil));
l.__set(l.__get(_ENV,"math"), "cos", (Math.cos));
l.__set(l.__get(_ENV,"math"), "cosh", (Math.cosh));
l.__set(l.__get(_ENV,"math"), "deg", (Math.deg));
l.__set(l.__get(_ENV,"math"), "exp", (Math.exp));
l.__set(l.__get(_ENV,"math"), "floor", (Math.floor));
l.__set(l.__get(_ENV,"math"), "pow", (Math.pow));
l.__set(l.__get(_ENV,"math"), "sin", (Math.sin));
l.__set(l.__get(_ENV,"math"), "sinh", (Math.sinh));
l.__set(l.__get(_ENV,"math"), "sqrt", (Math.sqrt));
l.__set(l.__get(_ENV,"math"), "tan", (Math.tan));
l.__set(l.__get(_ENV,"math"), "tanh", (Math.tanh));
l.__set(l.__get(_ENV,"math"), "pi", (Math.PI));
l.__set(l.__get(_ENV,"math"), "log", (function (v, base){
return base ? Math.log(v)/Math.log(base) : Math.log(v);
}
));
l.__set(l.__get(_ENV,"math"), "max", (function (){
return Math.max.apply(null, arguments);
}
));
l.__set(l.__get(_ENV,"math"), "min", (function (v){
return Math.min.apply(null, arguments);
}
));
l.__set(l.__get(_ENV,"math"), "rad", ( function (v){
return [v*Math.PI/180];
}
));
l.__set(l.__get(_ENV,"math"), "random", ( function (m, n){
if (m){
if (!n){
return [Math.floor(Math.random()*m)+1]
}
return [Math.floor(Math.random()*(n-m+1))+m]
}
return [Math.random()];
}
));
l.__set(_ENV, "bit32", l.__newTable());
l.__set(l.__get(_ENV,"bit32"), "arshift", ( function (x, disp){
return x<<disp;
}
));
l.__set(l.__get(_ENV,"bit32"), "band", ( function (x){
for (var i= 1; i < arguments.length; i++){
x &= arguments[i];
}
return x;
}
));
l.__set(l.__get(_ENV,"bit32"), "bnot", ( function (x){
return ~x;
}
));
l.__set(l.__get(_ENV,"bit32"), "bor", ( function (x){
for (var i= 1; i < arguments.length; i++){
x |= arguments[i];
}
return x;
}
));
l.__set(l.__get(_ENV,"bit32"), "btest", ( function (x){
for (var i= 1; i < arguments.length; i++){
x &= arguments[i];
}
return x != 0;
}
));
l.__set(l.__get(_ENV,"bit32"), "bxor", ( function (x){
for (var i= 1; i < arguments.length; i++){
x ^= arguments[i];
}
return x != 0;
}
));
l.__set(_ENV, "io", l.__newTable());
l.__set(l.__get(_ENV,"io"), "write", (function (v){
if (v != "\n"){
console.log(v)
}
}
));
l.__set(l.__get(_ENV,"io"), "flush", l._f(function()
{
return [];
}));
l.__set(_ENV, "os", l.__newTable());
l.__set(l.__get(_ENV,"os"), "clock", ( (function(){
var start = Date.now();
return function(){
return (Date.now() - start)/1000;
}
})()
));
l.__set(l.__get(_ENV,"os"), "difftime", l._f(function(t2$28,t1$29)
{
return [l.__sub(t2$28,t1$29)];
}));
l.__set(l.__get(_ENV,"os"), "time", (function(){
return Date.now()/1000;
}
));
return [];
});
});
if (typeof window !== 'undefined') {
(function(){
var luajs = modules.index;
/**
* Created by Yun on 2014/11/7.
*/
var L = luajs.newContext();
L.loadStdLib();
L._G.metatable = new luajs.types.LuaTable([[1, "__index", window]]);
function httpGet(url, cb){
var request = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); ;
request.addEventListener("readystatechange", function(){
if (request.readyState == 4){
if (request.status != 200 && request.status != 0){
throw new Error("HTTP " + request.status + ": " + url);
}
console.info("Loaded: ", url);
cb(request.responseText);
}
});
request.open("GET", url, true);// 异步处理返回
request.send();
}
function execute(luas, i){
i = i || 0;
if (i>=luas.length){
return;
}
var cur = luas[i];
if (cur.code){
L.loadString(cur.code)("embedded");
execute(luas, i+1);
} else {
httpGet(cur.src, function(content){
L.loadString(content)(cur.src);
execute(luas, i+1);
})
}
}
function runScripts(){
var scripts = window.document.getElementsByTagName("script");
var luas = [];
var works = 0;
for (var i=0;i<scripts.length; i++){
var sc = scripts[i];
if (sc.type == "text/lua" || sc.type == "application/x-lua"){
if (sc.src){
luas.push({
src: sc.src
});
} else {
luas.push({
code: sc.innerHTML
})
}
}
}
execute(luas);
}
if (window.addEventListener){
window.addEventListener("DOMContentLoaded", runScripts, false);
} else {
window.attachEvent("onload", runScripts);
}
})();
}
return modules.index;
}));