├── Makefile ├── README.md ├── src ├── ljs.baselib.js ├── ljs.bytecode.js ├── ljs.lvm.js ├── ljs.module51.js └── ljs.sys.js └── tests ├── fail ├── OP_CONCAT.lua ├── assert.lua ├── badmodule.lua ├── badmodule2.lua ├── badmodule3.lua ├── basic_logic_fail.lua ├── demo_bisect.lua ├── demo_globals.lua ├── demo_sieve.lua ├── frexp.lua ├── requirefail.lua ├── string_byte.lua ├── string_find_fails.lua ├── tablekeys.lua └── upvalue.lua ├── pass ├── add.lua ├── arithmetic.lua ├── assert.lua ├── basic_logic.lua ├── bool.lua ├── callmeta.lua ├── concat.lua ├── demo_account.lua ├── demo_hello.lua ├── demoscripts.lua ├── forloop.lua ├── gmatch.lua ├── hello.lua ├── indexmeta.lua ├── jsffi.lua ├── ldexp.lua ├── length.lua ├── loop.lua ├── meta__eq.lua ├── nilglobal.lua ├── op_close.lua ├── pairs.lua ├── require.lua ├── string_byte.lua ├── string_find.lua ├── string_lower.lua ├── string_sub.lua ├── t.lua ├── tableconcat.lua ├── tablekeys.lua ├── test_test.lua ├── tostring.lua ├── upvalue.lua └── vararg.lua ├── run.sh ├── runLJS-min.js └── runljs.js /Makefile: -------------------------------------------------------------------------------- 1 | all: release 2 | 3 | release: LJS.js 4 | 5 | LJS.js: src/ljs.sys.js src/ljs.lvm.js src/ljs.baselib.js src/ljs.bytecode.js src/ljs.module51.js 6 | cat $^ > $@ 7 | 8 | LJS-min.js: LJS.js 9 | java -jar compiler.jar --js $< > $@ 10 | 11 | clean: 12 | rm -vf LJS.js LJS-min.js luac.out 13 | 14 | # sanity check to make sure these run in stock lua 15 | pretest: 16 | @echo "--- running tests/pass/*.lua through lua ---" 17 | @sh -c 'for x in tests/pass/*.lua ; \ 18 | do lua $$x >/dev/null || exit; done \ 19 | && echo lua tests/pass/\*.lua ran OK' 20 | 21 | # run node-based test framework 22 | test: pretest 23 | bash tests/run.sh 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ljs - Lua VM implemented in Javascript 2 | 3 | * This is the Lua VM / core only. See [TBD] for example integrations. 4 | 5 | * [OLD DEMO](http://humbletim.github.com/ljs/demo/codemirror.html) 6 | (ljs + precompiled [yeuliang](http://yueliang.luaforge.net/) + [codemirror](http://codemirror.net) editor) 7 | 8 | note: ljs itself is bytecode loader / interpreter only 9 | (.lua scripts must be compiled to .luac in advance) 10 | 11 | ### to run the node-based tests 12 | 13 | make test 14 | # -> M/N TESTS PASSED 15 | 16 | #### dependencies 17 | 18 | node (for running tests) 19 | 20 | #### history 21 | 22 | originally *ljs-16b833862ae2* from mecurial 23 | http://code.matthewwild.co.uk/ljs/ :: 24 | [16b833862ae2](http://code.matthewwild.co.uk/ljs/rev/16b833862ae2) 25 | 26 | ------ 27 | #### License (MIT/X11) 28 | -------------------------------------------------------------------------------- /src/ljs.baselib.js: -------------------------------------------------------------------------------- 1 | /** @license 2 | ljs - Lua VM in JavaScript 3 | ljs.baselib.js - lua base library 4 | Copyright (c) 2011 Tim Dedischew 5 | Copyright (c) 2010 Matthew Wild 6 | MIT license 7 | */ 8 | 9 | (function(exports) { 10 | 11 | // Standard library 12 | 13 | var sys = require('sys'); 14 | var baselib = { 15 | error: function (message) 16 | { 17 | throw message.toString(); 18 | }, 19 | print: function () 20 | { 21 | if (arguments.length > 0) { 22 | var args = Array.prototype.slice.call(arguments); 23 | sys.print(args[0].toString()); 24 | for(var i = 1; i= "0" && c <= "9") 168 | { 169 | regexp += ("\\"+c); 170 | continue; 171 | } 172 | else 173 | { 174 | var cls = _patternClasses[c]; 175 | if(cls) 176 | { 177 | regexp += cls; 178 | continue; 179 | } 180 | } 181 | } 182 | else if(c == "\\" || c == "/") 183 | regexp += "\\"; // Escape escapes 184 | regexp += c; 185 | } 186 | var old = regexp; 187 | if (patt[0] == '[' && original.indexOf('%') > 0 ) {// FIXME: only handles "[_%a]" but not ".+[_%a]" !!!! 188 | regexp = "["+regexp.substring(1,regexp.length-1).replace(/[\[\]]/g,'')+"]"; 189 | //sys.debug&&sys.debug(patt+" :: "+regexp+" (was '"+old+"')") 190 | } 191 | 192 | return new RegExp(regexp, "g"); 193 | }; 194 | 195 | var string = { 196 | len: function(s) { return [this.LValue(s.len())]; }, 197 | "byte": function(s) { 198 | if (s.type != 'string' && s.type != 'number') 199 | throw "bad argument #1 to 'byte' (string expected, got "+s.type+")"; 200 | 201 | var nArgs = arguments.length; 202 | if(nArgs != 1) 203 | throw "ljs.string.byte(): only implemented single-char version...."+nArgs; 204 | return [this.LValue(s.toString().charCodeAt(0))]; 205 | }, 206 | "char": function () 207 | { 208 | var nArgs = arguments.length; 209 | if(nArgs < 1) 210 | throw "string.char(): Expects at least 1 parameter"; 211 | var results = ""; 212 | for(var i=0; i 2) 233 | // throw "string.find(): No more than the first 2 arguments supported"+arguments.length; 234 | 235 | var offset = (init && init.type == 'number' ? init.value : 1) -1; 236 | if (offset > 1) { 237 | tstr = tstr.substring(offset); 238 | // sys.puts(sys.inspect(["offseted string.find", tstr])); 239 | } 240 | 241 | var re; 242 | if (isplain) { 243 | var start = tstr.indexOf(patt.value); 244 | if (start < 0) 245 | return [this.LValue(null)]; 246 | 247 | return [this.LValue(start+1+offset), 248 | this.LValue(start+patt.value.length+offset)]; 249 | // re = new RegExp(patt.value.replace(/([\S\s])/g,'[$1]'), "g"); 250 | // sys.puts("(verify) string.find... plain mode!"+re+"/"+tstr); 251 | // sys.debug(sys.inspect(re.exec(tstr))); 252 | } else { 253 | re = _patternToRegExp(patt.value); 254 | } 255 | var result = re.exec(tstr); 256 | if(!result) { 257 | // sys.puts(sys.inspect(["string.find", str.value, patt.value, ""+re, start, end])) 258 | return [this.LValue(null)]; 259 | } 260 | var start = result.index+1 + offset; 261 | var end = start + result[0].length - 1; 262 | var ret = [this.LValue(start), this.LValue(end)]; 263 | //sys.puts(sys.inspect(["string.find", patt.value, ""+re, start, end])) 264 | for(var i=1; i0) 281 | throw "string.format(): Number format modifers not yet implemented"; 282 | case "s": 283 | result+=arguments[currparam++].value.toString(); 284 | case "%": 285 | break; 286 | default: 287 | throw "string.format(): Format %"+match[2]+" not implemented"; 288 | } 289 | } 290 | result += format.substring(currpos); 291 | return [this.LValue(result)]; 292 | }, 293 | gmatch: function (str, patt) 294 | { 295 | var re = _patternToRegExp(patt.value); 296 | var matches = str.value.match(re)||[]; 297 | var curr = 0; 298 | var iter = function () 299 | { 300 | return [this.LValue(matches[curr++])]; 301 | }; 302 | return [this.LValue(iter)]; 303 | }, 304 | sub: function (str, from, to) 305 | { 306 | var result; 307 | switch(arguments.length) 308 | { 309 | case 0: 310 | case 1: 311 | throw "string.sub(): Expected 2 or more arguments"; 312 | case 2: 313 | result = str.value.substring( 314 | from.value < 0 ? str.value.length + from.value : from.value-1); 315 | //sys.puts(sys.inspect(["string.sub",str.value,from.value,result])) 316 | break 317 | case 3: 318 | result = str.value.substring( 319 | from.value < 0 ? str.value.length + from.value : from.value-1, 320 | to.value < 0 ? str.value.length + to.value + 1 : to.value); 321 | //sys.puts(sys.inspect(["string.sub",str.value,from.value,to.value,result,result.length])); 322 | break 323 | } 324 | // sys.debug(sys.inspect(["string.sub", str, from, to, result])) 325 | return [this.LValue(result)]; 326 | }, 327 | rep: function(str, n) { 328 | var ret = ""; 329 | for (var i=0; i < n.value ;i++) 330 | ret += str; 331 | return [this.LValue(ret)]; 332 | }, 333 | lower: function(s) { 334 | s = s || { type:'no value' }; 335 | if (s.type != 'string' && s.type != 'number') 336 | throw "bad argument #1 to 'lower' (string expected, got "+s.type+")"; 337 | return [this.LValue(s.toString().toLowerCase())] 338 | } 339 | }; 340 | 341 | // TODO: keep refactoring... 342 | function openlibs(testvm, _G) { 343 | _G = _G || testvm.LValue([]); 344 | 345 | testvm.registerLib(_G, null, baselib); 346 | testvm.registerLib(_G, "math", math); 347 | testvm.registerLib(_G, "io", { 348 | open: function() { throw "io.open -- not available"; }, 349 | write: function() { 350 | for (var i=0; i < arguments.length; i++) 351 | sys.print(arguments[i].toString()); 352 | return []; 353 | } 354 | }); 355 | 356 | testvm.registerLib(_G, "table", { 357 | concat: function(t,s) 358 | { 359 | if (t.type != 'table') 360 | throw "bad argument #1 to 'concat' (table expected, got "+t.type+")"; 361 | if (!s || s.type == 'nil') 362 | s=this.LValue(""); 363 | if (s.type != 'string' && s.type != 'number') 364 | throw "bad argument #2 to 'concat' (string expected, got "+s.type+")"; 365 | var ret = []; 366 | for (var p=1; p <= t.len(); p++) { 367 | ret.push(t.value[p].value); 368 | } 369 | return [this.LValue(ret.join(s.toString()))]; 370 | } 371 | }); 372 | testvm.registerLib(_G, "string", string); 373 | return _G; 374 | } 375 | 376 | function make_G(testvm) { 377 | var _G = openlibs(testvm); 378 | _G.setIndex(testvm.LValue("_G"), _G); 379 | _G.setIndex(testvm.LValue("_VERSION"), testvm.LValue(testvm._VERSION)); 380 | 381 | // Metatable on environment to print out nil global accesses 382 | var mt = testvm.LValue([]); 383 | mt.setIndex( 384 | testvm.LValue("__index"), 385 | testvm.LValue(function (t, k) { (sys.debug||sys.puts)("(debug) Access of nil global: "+k); }) 386 | ); 387 | _G.setMetatable(mt); 388 | return _G; 389 | }; 390 | // exports.openlibs = openlibs; 391 | exports.make_G = make_G; 392 | return exports; 393 | })(typeof exports == 'object' ? exports : typeof window == 'object' ? window : {} ); 394 | 395 | -------------------------------------------------------------------------------- /src/ljs.bytecode.js: -------------------------------------------------------------------------------- 1 | /* -*- indent-tabs-mode: t; -*- */ 2 | /** @license 3 | ljs - Lua VM in JavaScript 4 | ljs.bytecode.js - binary chunk (bytecode/.luac) support 5 | Copyright (c) 2011-2012 Tim Dedischew 6 | Copyright (c) 2010 Matthew Wild 7 | extracted as sub-module 2011 Tim Dedischew 8 | MIT license 9 | */ 10 | 11 | (function(exports) { 12 | 13 | function LBinaryChunk(vm, chunk, start, sourceName, _sizeFlag) 14 | { 15 | this.chunk = chunk; 16 | this.pos = start||0; 17 | 18 | /* initial "64-bit" (empirical) support */ 19 | if (start === undefined) { 20 | this.pos = 8; 21 | this.sizeFlag = this.readByte(); 22 | this.pos = 12; 23 | } else { 24 | this.sizeFlag = _sizeFlag; 25 | } 26 | 27 | this.sourceName = this.readString(); 28 | if(sourceName) 29 | this.sourceName = sourceName; 30 | this.lineDefined = this.readInt(); 31 | this.lastLineDefined = this.readInt(); 32 | this.numUpvalues = this.readByte(); 33 | this.numParameters = this.readByte(); 34 | this.isVararg = this.readByte(); 35 | this.maxStackSize = this.readByte(); 36 | 37 | this.instructions = []; 38 | 39 | this.numInstructions = this.readInt(); 40 | for(var i=0;i>6)&0xFF, // Field A 46 | (ins>>23)&0x1FF, // Field B 47 | (ins>>14)&0x1FF // Field C 48 | ]); 49 | if(exports.debugMode) 50 | { 51 | var pi = this.instructions[this.instructions.length-1]; 52 | //sys.puts("Pos: "+(this.pos-4)+" Ins: "+ins+" OP: "+INS_OPCODE(pi)+" A: "+INS_A(pi)+" B: "+INS_B(pi)+" C: "+INS_C(pi)+" Bx: "+INS_Bx(pi)+" sBx: "+(INS_Bx(pi)-0x1FFFE)); 53 | } 54 | } 55 | 56 | this.constants = []; 57 | 58 | this.numConstants = this.readInt(); 59 | var LValue = vm._LValue; 60 | for(var i=0;i>7)&0x1; 167 | var exp = (bytes[0]&0x7F)<<4 | (bytes[1]&0xf0)>>4; 168 | 169 | var frac = ((bytes[1] & 0x0f) * Math.pow(2,48)) 170 | + (bytes[2] * Math.pow(2,40)) 171 | + (bytes[3] * Math.pow(2,32)) 172 | + (bytes[4] * Math.pow(2,24)) 173 | + (bytes[5] * Math.pow(2,16)) 174 | + (bytes[6] * Math.pow(2,8)) 175 | + bytes[7]; 176 | 177 | if(exp != 0x000 && exp != 0x7FF) 178 | { 179 | var n = (sign==1?-1:1)*Math.pow(2,exp-1023)*(1+(frac/0x10000000000000)); 180 | return n; 181 | } 182 | else if(exp == 0x000) 183 | { 184 | return sign*0; 185 | } 186 | else 187 | return frac==0?sign*Infinity:NaN; 188 | } 189 | }; 190 | 191 | exports.LBinaryChunk = LBinaryChunk; 192 | exports.loadbytecode = function (vm, bytes, env) { 193 | var c = new LBinaryChunk(vm, bytes); 194 | return vm.loadchunk(c, env); 195 | // var f = new LFunction(this, c, env); 196 | // return new LValue(this, "function", f); 197 | }; 198 | exports.luaopen_bytecode51 = function(VM, _G) { 199 | VM.loadbytecode = function(b, g) { 200 | return exports.loadbytecode(this, b, g); 201 | }; 202 | return true; 203 | }; 204 | return exports; 205 | })(typeof exports == 'object' ? exports : typeof window == 'object' ? window : {} ); 206 | -------------------------------------------------------------------------------- /src/ljs.lvm.js: -------------------------------------------------------------------------------- 1 | /** @license 2 | ljs - Lua VM in JavaScript 3 | ljs.lvm.js - Lua VM and bytecode interpreter core 4 | Copyright (c) 2011 Tim Dedischew 5 | Copyright (c) 2010 Matthew Wild 6 | MIT license 7 | */ 8 | 9 | (function(exports) { 10 | 11 | var _VERSION = "Lua 5.1 {ljs=0.00200}" 12 | 13 | var sys=require("sys"); 14 | var debugMode = false; 15 | var logOP_VARG = sys.debug || function() {}; 16 | 17 | function LValue(vm, type, value) 18 | { 19 | this.vm = vm; 20 | this.type = type||"nil"; 21 | this.value = value; 22 | } 23 | 24 | var _hasprop = function(thing, key) { 25 | return thing.hasOwnProperty(key); 26 | }; 27 | 28 | if (typeof navigator == 'object' && /opera/i.test(navigator.userAgent)) { 29 | _hasprop = function(thing, key) { 30 | return key in thing; 31 | }; 32 | } 33 | 34 | LValue.prototype = { 35 | call: function (args) 36 | { 37 | var ret = this.vm.call(this, args); 38 | if(typeof(ret) == "undefined") 39 | ret = []; 40 | return ret; 41 | }, 42 | precall: function () 43 | { 44 | if(this.type == "function") 45 | return this.value; 46 | var func = this; 47 | if(func.type == "table" && 48 | func.metatable && func.metatable.type != "nil") { 49 | var tfunc = this.metatable.index(this.vm.LValue("__call")); 50 | if (tfunc.type == 'function') { 51 | return function() { 52 | var cargs = [func].concat(Array.prototype.slice.call(arguments)); 53 | return tfunc.call(cargs); 54 | }; 55 | } 56 | } 57 | 58 | throw "Attempt to call a " + this.type + " value"; 59 | }, 60 | index: function (key, raw) 61 | { 62 | //TODO: string metas like "val:find(" -> "string.find(val," -t 63 | // if (this.type == 'string' && raw != true) { 64 | // if (key.value == "find") 65 | // sys.print( this.vm) 66 | // return 67 | // } 68 | 69 | if(this.type == "table") { 70 | 71 | if (0 > "string number nil boolean".indexOf(key.type)) 72 | throw "(TODO) ljs.table.index - sorry"+ 73 | ", table key can't be this type yet:"+key.type; 74 | 75 | // lua allows table[nil] for lookup 76 | if (key.type == 'nil') return this.vm.LValue(null); 77 | 78 | // RE: lua tables vs. js Arrays 79 | // in all js it seems non-int gets coerced to strings 80 | /* 81 | sys=require('sys'); 82 | t=[]; t[1] = true; t[5.5] = 'fiver'; t[t] = true; 83 | sys.debug(sys.inspect(t)) 84 | sys.debug("t.length:" + t.length); 85 | 86 | -> DEBUG: [ true, '5.5': 'fiver', ',true': true ] 87 | -> DEBUG: t.length:2 88 | */ 89 | 90 | // number, nil, boolean -> string rep 91 | // see: updated len() implementation 92 | var kv = key.value.toString(); 93 | 94 | // .hasOwnProperty so "concat" doesn't pick up javascript:[].concat 95 | if (_hasprop(this.value, kv)) { 96 | if (typeof(this.value[kv]) == 'function') 97 | throw "javascript key issue..."+kv+"="+typeof(this.value[kv]); 98 | return this.value[kv]; 99 | } 100 | if(raw != true && this.metatable && this.metatable.type != "nil") { 101 | var __index = this._getMeta("__index"); 102 | if(__index.type == "function") { 103 | // VERIFY: looks like [0] already LValue or undefined... 104 | // trying it without wrapping in another LValue -t 105 | var ret = /*this.vm.LValue(*/__index.call([this, key])[0] /*)*/; 106 | if (ret) return ret; 107 | } else if(__index.type != "nil") 108 | return __index.index(key); 109 | } 110 | return this.vm.LValue(null); 111 | } else 112 | throw "Attempt to index a " + this.type + " value"; 113 | }, 114 | setIndex: function (key, value, raw) 115 | { 116 | if(this.type == "table") { 117 | var kv = key.value; 118 | if (key.type == 'number') kv = kv.toString(); 119 | if ( !raw && this.metatable && !_hasprop(this.value, kv)) { 120 | // key.type == 'boolean', 'nil', 'table', 'function' 121 | // all possible if forwarding to metamethod... 122 | if (0 > "string number".indexOf(key.type )) 123 | sys.debug("(info) __newindex["+key.type+"]"); 124 | var metamethod = this._getMeta("__newindex"); 125 | if (metamethod) 126 | return metamethod.call([this,key,value]); 127 | } 128 | // treat numbers as a string index (like js does) 129 | if (key.type == "number") { 130 | this.value[kv] = value; 131 | } 132 | else if (key.type != 'string') 133 | throw "(TODO) ljs.table.setIndex: for now indexes must be string or number, not: "+key.type; 134 | else this.value[key.value] = value; 135 | } else 136 | throw "Attempt to index a " + this.type + " value"; 137 | }, 138 | setMetatable: function (metatable) 139 | { 140 | if(metatable.type == "table") 141 | this.metatable = metatable; 142 | else if(metatable.type == "nil") 143 | this.metatable = null; 144 | else 145 | throw "Attempt to set a "+metatable.type+" value as a metatable"; 146 | }, 147 | getMetatable: function () 148 | { 149 | return this.metatable; 150 | }, 151 | _getMeta: function (jstr) 152 | { 153 | //this.type == 'table' ? 154 | if(this.metatable) { 155 | var __mm = this.vm.LValue(jstr); 156 | var meta = this.metatable.index(__mm); 157 | if (meta && meta.type != "nil") 158 | return meta; 159 | } 160 | return false; 161 | }, 162 | toString: function () 163 | { 164 | if (this.type == "nil") return "nil"; 165 | if (this.type == "function") return "function: 0xLVM"; 166 | if (this.type == "boolean") return this.truth().toString(); 167 | 168 | var metamethod = this._getMeta("__tostring"); 169 | if (metamethod) 170 | return metamethod.call([this]).toString(); 171 | 172 | if (this.type == "table") return "table: 0xLVM"; 173 | 174 | return this.value.toString(); 175 | }, 176 | truth: function () 177 | { 178 | if(this.type == "nil" || (this.type == "boolean" && this.value == false)) 179 | return false; 180 | return true; 181 | }, 182 | add: function (op2) 183 | { 184 | var metamethod; 185 | var __add = this.vm.LValue("__add"); 186 | if(this.metatable) 187 | metamethod = this.metatable.index(__add); 188 | if((!metamethod || metamethod.type == "nil") && op2.metatable) 189 | metamethod = op2.metatable.index(__add); 190 | if(metamethod && metamethod.type != "nil") 191 | { 192 | return metamethod.call([this, op2]); 193 | } 194 | else if((this.type == "number" || this.type == "string") 195 | && (op2.type == "number" || op2.type == "string")) 196 | { 197 | // Plain addition 198 | return this.vm.LValue(parseFloat(this.value, 10) + parseFloat(op2.value, 10)); 199 | } 200 | else 201 | throw "Attempt to perform arithmetic on a "+this.type+" and "+op2.type; 202 | }, 203 | equals: function (op2) 204 | { 205 | if(this.type != op2.type) 206 | return false; 207 | if(this.value == op2.value) 208 | return true; 209 | var __eq = this.vm.LValue("__eq"); 210 | if(this.metatable && op2.metatable) 211 | { 212 | var metamethod1 = this.metatable.index(__eq); 213 | var metamethod2 = op2.metatable.index(__eq); 214 | if(metamethod1.equals(metamethod2)) 215 | { 216 | var result = metamethod1.call([this, op2]); 217 | // ... return result[0].truth()? 218 | return (result[0].type != "nil" 219 | && (result[0].type != "boolean" || result[0].value == true) 220 | ); 221 | } 222 | } 223 | return false; 224 | }, 225 | len: function () 226 | { 227 | switch(this.type) 228 | { 229 | case "string": 230 | return this.value.length; 231 | case "table": // FIXME: naive brute-force implementation... 232 | var i = 1; 233 | while (1) { 234 | if (!this.value[i.toString()]) 235 | return i-1; 236 | i++; 237 | } 238 | default: 239 | throw "attempt to get length of a "+this.type+" value"; 240 | } 241 | } 242 | }; 243 | 244 | function INS_OPCODE(ins) 245 | { 246 | return ins[0]; 247 | } 248 | 249 | function INS_A(ins) 250 | { 251 | return ins[1]; 252 | } 253 | 254 | function INS_B(ins) 255 | { 256 | return ins[2]; 257 | } 258 | 259 | function INS_C(ins) 260 | { 261 | return ins[3]; 262 | } 263 | 264 | function INS_Bx(ins) 265 | { 266 | return ((INS_C(ins))|(INS_B(ins)<<9)); 267 | } 268 | 269 | function INS_sBx(ins) 270 | { 271 | return (INS_Bx(ins)-0x1FFFF); 272 | } 273 | 274 | function RK(frame, R) 275 | { 276 | var keysource = (R&0x100)?frame.f.constants:frame.reg; 277 | return keysource[R&0xff]; 278 | } 279 | 280 | function LFunction(vm, chunk, env) 281 | { 282 | var F = function () {}; 283 | F.prototype = chunk; 284 | var o = new F(); 285 | o.vm = vm; 286 | o.environment = env; 287 | o.chunk = chunk; 288 | o.upvalues = []; 289 | return o; 290 | } 291 | 292 | function LVM() 293 | { 294 | this.callstack = []; 295 | this.stack = []; 296 | this.OPS = 0; 297 | this._VERSION = _VERSION; 298 | this._LValue = LValue; 299 | return this; 300 | } 301 | 302 | var OP_MOVE = 0; 303 | var OP_LOADK = 1; 304 | var OP_LOADBOOL = 2; 305 | var OP_LOADNIL = 3; 306 | var OP_GETUPVAL = 4; 307 | var OP_GETGLOBAL = 5; 308 | var OP_GETTABLE = 6; 309 | var OP_SETGLOBAL = 7; 310 | var OP_SETUPVAL = 8; 311 | var OP_SETTABLE = 9; 312 | var OP_NEWTABLE = 10; 313 | var OP_SELF = 11; 314 | var OP_ADD = 12; 315 | var OP_SUB = 13; 316 | var OP_MUL = 14; 317 | var OP_DIV = 15; 318 | var OP_MOD = 16; 319 | var OP_POW = 17; 320 | var OP_UNM = 18; 321 | var OP_NOT = 19; 322 | var OP_LEN = 20; 323 | var OP_CONCAT = 21; 324 | var OP_JMP = 22; 325 | var OP_EQ = 23; 326 | var OP_LT = 24; 327 | var OP_LE = 25; 328 | var OP_TEST = 26; 329 | var OP_TESTSET = 27; 330 | var OP_CALL = 28; 331 | var OP_TAILCALL = 29; 332 | var OP_RETURN = 30; 333 | var OP_FORLOOP = 31; 334 | var OP_FORPREP = 32; 335 | var OP_TFORLOOP = 33; 336 | var OP_SETLIST = 34; 337 | var OP_CLOSE = 35; 338 | var OP_CLOSURE = 36; 339 | var OP_VARARG = 37; 340 | 341 | LVM.prototype = { 342 | LValue: function (value) 343 | { 344 | switch(typeof(value)) 345 | { 346 | case "number": 347 | return new LValue(this, "number", value); 348 | case "boolean": 349 | return new LValue(this, "boolean", value != 0); 350 | case "string": 351 | return new LValue(this, "string", value); 352 | case "function": 353 | return new LValue(this, "function", value); 354 | case "object": 355 | if(value == null) 356 | return new LValue(this, "nil", value); 357 | else { 358 | if (value.length > 0) { 359 | sys.debug&&sys.debug("(verify) adapting js array (0 -> 1)"); 360 | var ml = value.length; 361 | for (var i=ml;i>0;i--) { 362 | value[i.toString()] = value[i-1]; 363 | } 364 | delete value[0]; 365 | } 366 | return new LValue(this, "table", value); 367 | } 368 | case "undefined": 369 | return new LValue(this, "nil", null); 370 | default: 371 | throw "Not able to convert type " + 372 | typeof(value)+" from Javascript to Lua"; 373 | } 374 | }, 375 | call: function (func, args) 376 | { 377 | var f = func.precall(); 378 | if(typeof(f) == "function") 379 | { 380 | return f.apply(this, args); 381 | } 382 | else if(f.instructions) 383 | { 384 | var frame = {f:f,pc:0,entry:true}; 385 | if(args) 386 | frame.reg = args.slice(0); 387 | else 388 | frame.reg = []; 389 | this.callstack.push(frame); 390 | 391 | if (this.callstack.length == 1 && frame.reg.length > 0) { 392 | logOP_VARG("(verify) OP_VARG top-level workaround..."+ 393 | frame.reg.length); 394 | this._arg = args; 395 | // logOP_VARG("f.environment.value.arg..."+ 396 | // (f.environment.value.arg||null)+"/"+this._arg.length); 397 | 398 | // note: make a copy, since this will re-index from 0-based to 1-based... 399 | var arg = this.LValue(args.slice(0)); 400 | var old = frame.f.environment; 401 | old.setIndex( 402 | this.LValue("arg"), 403 | arg 404 | ); 405 | 406 | // for(var i=frame.reg.length;i0) 436 | { 437 | instruction = frame.f.instructions[frame.pc++]; 438 | if(debugMode) 439 | { 440 | sys.puts("PC: "+(frame.pc-1)+" OP: "+instruction[0]); 441 | for(var i = 0; i < frame.reg.length; i++) 442 | { 443 | var entry = frame.reg[i]; 444 | if(entry && entry.type) 445 | sys.puts("\t"+i+":\t("+entry.type+") "+entry.toString()); 446 | else 447 | sys.puts("\t"+i+": "+entry); 448 | } 449 | } 450 | this.OPS++; 451 | switch(INS_OPCODE(instruction)) 452 | { 453 | case OP_MOVE: 454 | frame.reg[INS_A(instruction)] = frame.reg[INS_B(instruction)]; 455 | break; 456 | case OP_LOADNIL: 457 | for(var i = INS_A(instruction);i<=INS_B(instruction);i++) 458 | frame.reg[i] = new LValue(this, "nil", null); 459 | break; 460 | case OP_LOADBOOL: 461 | frame.reg[INS_A(instruction)] = new LValue(this, "boolean", INS_B(instruction)!=0); 462 | if(INS_C(instruction)!=0) 463 | frame.pc++; 464 | break; 465 | case OP_GETUPVAL: 466 | var upvalue = frame.f.upvalues[INS_B(instruction)]; 467 | frame.reg[INS_A(instruction)] = new LValue(this, upvalue.type, upvalue.value); 468 | if (upvalue.metatable) 469 | frame.reg[INS_A(instruction)].metatable = upvalue.metatable; 470 | break; 471 | case OP_GETGLOBAL: 472 | var name = frame.f.constants[INS_Bx(instruction)]; 473 | frame.reg[INS_A(instruction)] = frame.f.environment.index(name); 474 | break; 475 | case OP_SETUPVAL: 476 | var reg = frame.reg[INS_A(instruction)]; 477 | var upvalue = frame.f.upvalues[INS_B(instruction)]; 478 | upvalue.type = reg.type; 479 | upvalue.value = reg.value; 480 | upvalue.metatable = reg.metatable; 481 | break; 482 | case OP_SETGLOBAL: 483 | var name = frame.f.constants[INS_Bx(instruction)]; 484 | frame.f.environment.setIndex(name, frame.reg[instruction[1]]); 485 | break; 486 | case OP_LOADK: 487 | var constant = frame.f.constants[INS_Bx(instruction)]; 488 | if (constant.metatable) throw "OP_LOADK: FIXME: metatable support?"; 489 | frame.reg[INS_A(instruction)] = new LValue(this, constant.type, constant.value); 490 | break; 491 | case OP_NEWTABLE: 492 | frame.reg[INS_A(instruction)] = new LValue(this, "table", []); 493 | break; 494 | case OP_GETTABLE: 495 | var C = INS_C(instruction); 496 | var value = frame.reg[INS_B(instruction)].index(RK(frame, C)); 497 | frame.reg[INS_A(instruction)] = new LValue(this, value.type, value.value); 498 | if (value.metatable) 499 | frame.reg[INS_A(instruction)].metatable = value.metatable; 500 | break; 501 | case OP_SETTABLE: 502 | var C = INS_C(instruction); 503 | var B = INS_B(instruction); 504 | frame.reg[INS_A(instruction)].setIndex(RK(frame, B), RK(frame, C)); 505 | break; 506 | case OP_VARARG: 507 | var A = INS_A(instruction); 508 | var wanted = INS_B(instruction)-1; 509 | var prevframe = this.callstack[this.callstack.length-2]; 510 | var base = frame.retAt+frame.f.numParameters; 511 | 512 | // FIXME: workaround for top-level varargs (main program) 513 | if (!prevframe && 514 | frame.entry == true 515 | // && this.callstack.length == 1 516 | // && this._arg 517 | ) { 518 | logOP_VARG("(verify) OP_VARARG "+ 519 | "patching top-level main chunk"+ 520 | this._arg +"#"+frame.reg.length); 521 | prevframe = { reg: this._arg || [] }; 522 | base = -1; 523 | frame.reg.length = A; 524 | } 525 | 526 | if (isNaN(base)) { 527 | //logOP_VARG("(xxx)"+ sys.inspect(this.callstack[0].reg)); 528 | base = prevframe.reg.length - 2; 529 | logOP_VARG("(verify) wasNaN(base)... now:"+base); 530 | } 531 | 532 | var available = (prevframe.reg.length - base) - 1; 533 | 534 | if (prevframe.reg == this._arg) 535 | logOP_VARG("(verify) OP_VARARGs patched..."+sys.inspect( 536 | {A:A,wanted: wanted, available: available})) 537 | if(wanted < 0) 538 | wanted = available; 539 | for(var i = 0; i f.numParameters) 558 | args.length = f.numParameters; 559 | for(var i=args.length;i f.numParameters) 603 | args.length = f.numParameters; 604 | for(var i=args.length;i 0 683 | ? RA.value <= frame.reg[A+1].value 684 | : RA.value >= frame.reg[A+1].value; 685 | 686 | // sys.puts(sys.inspect(["OP_FORLOOP", RA.value, frame.reg[A+2].value, contd])); 687 | if(contd) 688 | { 689 | frame.pc += INS_sBx(instruction); 690 | frame.reg[A+3] = new LValue(this, "number", RA.value); 691 | } 692 | break; 693 | case OP_TFORLOOP: 694 | var A = INS_A(instruction); 695 | var C = INS_C(instruction); 696 | var RA = frame.reg[A]; // Iterator function 697 | var rets = this.call(RA, [frame.reg[A+1], frame.reg[A+2]]); 698 | frame.reg.length = A+3; 699 | for(var i = 0; i (a "+ 734 | frame.reg[i].type+" value)"; 735 | } 736 | frame.reg[A] = new LValue(this, "string", values.join('')); 737 | break; 738 | case OP_ADD: 739 | var RB = RK(frame, INS_B(instruction)); 740 | var RC = RK(frame, INS_C(instruction)); 741 | frame.reg[INS_A(instruction)] = RB.add(RC); 742 | break; 743 | case OP_SUB: 744 | var RB = RK(frame, INS_B(instruction)); 745 | var RC = RK(frame, INS_C(instruction)); 746 | frame.reg[INS_A(instruction)] = new LValue(this, "number", RB.value - RC.value); 747 | break; 748 | case OP_MUL: 749 | var RB = RK(frame, INS_B(instruction)); 750 | var RC = RK(frame, INS_C(instruction)); 751 | frame.reg[INS_A(instruction)] = new LValue(this, "number", RB.value * RC.value); 752 | break; 753 | case OP_DIV: 754 | var RB = RK(frame, INS_B(instruction)); 755 | var RC = RK(frame, INS_C(instruction)); 756 | frame.reg[INS_A(instruction)] = new LValue(this, "number", RB.value / RC.value); 757 | break; 758 | case OP_MOD: 759 | var RB = RK(frame, INS_B(instruction)); 760 | var RC = RK(frame, INS_C(instruction)); 761 | frame.reg[INS_A(instruction)] = new LValue(this, "number", RB.value % RC.value); 762 | break; 763 | case OP_POW: 764 | var RB = RK(frame, INS_B(instruction)); 765 | var RC = RK(frame, INS_C(instruction)); 766 | frame.reg[INS_A(instruction)] = 767 | new LValue(this, "number", Math.pow(RB.value, RC.value)); 768 | break; 769 | case OP_UNM: 770 | var RB = frame.reg[INS_B(instruction)]; 771 | if (RB.type != 'number') 772 | throw "attempt to perform arithmetic on ... "+ 773 | "(a "+RB.type+" value)"; 774 | frame.reg[INS_A(instruction)] = new LValue(this, "number", -RB.value); 775 | break; 776 | case OP_NOT: 777 | var RB = frame.reg[INS_B(instruction)]; 778 | frame.reg[INS_A(instruction)] = new LValue(this, "boolean", !RB.truth()); 779 | break; 780 | case OP_LEN: 781 | var RB = frame.reg[INS_B(instruction)]; 782 | frame.reg[INS_A(instruction)] = new LValue(this, "number", RB.len()); 783 | break; 784 | case OP_EQ: 785 | var A = INS_A(instruction); 786 | var RB = RK(frame, INS_B(instruction)); 787 | var RC = RK(frame, INS_C(instruction)); 788 | if(RB.equals(RC) != (A!=0)) 789 | frame.pc++; 790 | break; 791 | case OP_LT: 792 | var A = INS_A(instruction); 793 | var RB = RK(frame, INS_B(instruction)); 794 | var RC = RK(frame, INS_C(instruction)); 795 | if (RB.type == 'table' || RC.type == 'table') 796 | throw "attempt to compare number with table"; 797 | if(RB.value < RC.value != (A!=0)) 798 | frame.pc++; 799 | break; 800 | case OP_LE: 801 | var A = INS_A(instruction); 802 | var RB = RK(frame, INS_B(instruction)); 803 | var RC = RK(frame, INS_C(instruction)); 804 | if (RB.type == 'table' || RC.type == 'table') 805 | throw "attempt to compare number with table"; 806 | if(RB.value <= RC.value != (A!=0)) 807 | frame.pc++; 808 | break; 809 | case OP_SETLIST: 810 | var A = INS_A(instruction); 811 | var RA = frame.reg[A]; 812 | var B = INS_B(instruction); 813 | var C = INS_C(instruction); 814 | if(C == 0) 815 | throw "Dynamic table construction not yet implemented"; 816 | // #define LFIELDS_PER_FLUSH 50 // Lua 5.1 817 | var baseindex = (C-1)*50; 818 | var index = new LValue(this, "number", 1); 819 | var lim = B>0?B:((frame.reg.length-baseindex)-2); 820 | for(var i = 1; i<=lim; index.value=(baseindex+(++i))) 821 | RA.setIndex(index, frame.reg[A+i]); 822 | break; 823 | default: 824 | throw "Unhandled opcode: "+INS_OPCODE(instruction); 825 | } 826 | } 827 | }, 828 | registerLib: function (env, name, lib) 829 | { 830 | var t; 831 | if(name) 832 | { 833 | t = this.LValue([]); // Create env[name] and put fns in there 834 | env.setIndex(this.LValue(name), t); 835 | } 836 | else 837 | t = env; // Import directly into env 838 | 839 | for(var k in lib) 840 | t.setIndex(this.LValue(k), this.LValue(lib[k])); 841 | return t; 842 | }, 843 | loadchunk: function (c, env) 844 | { 845 | var f = new LFunction(this, c, env); 846 | return new LValue(this, "function", f); 847 | }, 848 | traceback: function () 849 | { 850 | var trace = []; 851 | for(var i=this.callstack.length-1; i>=0; i--) 852 | { 853 | var currframe = this.callstack[i]; 854 | var currfunc = currframe.f; 855 | var sourceName = (currfunc.sourceName||"=?").substr(1); 856 | var line = "?"; 857 | if(currfunc.sourceLines && currfunc.sourceLines[currframe.pc-1]) 858 | line = currfunc.sourceLines[currframe.pc-1]; 859 | trace.push({ sourceName: sourceName, line: line }); 860 | } 861 | return trace; 862 | } 863 | }; 864 | 865 | exports.LVM = LVM; 866 | exports.LValue = LValue; 867 | exports.LFunction = LFunction; 868 | return exports; 869 | 870 | })(typeof exports == 'object' ? exports : 871 | typeof window == 'object' ? window : 872 | {} ); 873 | -------------------------------------------------------------------------------- /src/ljs.module51.js: -------------------------------------------------------------------------------- 1 | /** @license 2 | ljs - Lua VM in JavaScript 3 | ljs.module51.js - package/require/module - Lua 5.1 style 4 | Copyright (c) 2011 Tim Dedischew 5 | portions Copyright (c) 2006-2008 Lua.org, PUC-Rio 6 | MIT license 7 | */ 8 | 9 | (function(exports) { 10 | var sys = require('sys'); 11 | 12 | function luaopen_module51(testvm, _G) { 13 | _G = _G || testvm.LValue([]); 14 | _G.setIndex(testvm.LValue("_MODULE_VERSION"), testvm.LValue("5.1 experimental")); 15 | // ----------------------------------------- 16 | /* experimental module/require support */ 17 | 18 | var modlog = sys.debug || function(){};//sys.puts 19 | var modlogV = sys.debug || function(){};//sys.puts 20 | var _G_package = testvm.registerLib(_G, "package", { 21 | seeall: "seeall", 22 | loaded: [], 23 | preload: [] 24 | }); 25 | 26 | 27 | function _getPackageObs() { 28 | return _G_package.value; /*{ 29 | loaded: _G_package.index(testvm.LValue("loaded")), 30 | preload: _G_package.index(testvm.LValue("preload")), 31 | loaders: _G_package.index(testvm.LValue("loaders")), 32 | };*/ 33 | } 34 | 35 | /*= http://www.lua.org/manual/5.1/manual.html#pdf-package.loaders 36 | Copyright (c) 2006-2008 Lua.org, PUC-Rio [Lua license] 37 | 38 | package.loaders 39 | 40 | A table used by require to control how to load modules. 41 | 42 | Each entry in this table is a searcher function. When looking for a module, require calls each of these searchers in ascending order, with the module name (the argument given to require) as its sole parameter. The function can return another function (the module loader) or a string explaining why it did not find that module (or nil if it has nothing to say). */ 43 | var loaders = { 44 | 1: function(name) { 45 | var loader = _G_package.index(testvm.LValue("preload")).index(name); 46 | if (loader.type == 'function') 47 | return [loader]; 48 | return [testvm.LValue("no field package.preload['"+name+"']")]; 49 | } 50 | }; 51 | 52 | testvm.registerLib(_G_package, "loaders", loaders); 53 | //= module() -- faithfully implemented against lua.org manual -t 54 | function lua_module(name, opt) { 55 | if (!name || name.type != 'string') 56 | throw "bad argument #1 to 'module' (string expected, got "+ 57 | (name?name.type:"no")+" value)"; 58 | 59 | modlog("module('"+name+"', "+opt+")"); 60 | var _package = _getPackageObs();//{ loaded: _G._package_loaded }; 61 | var G = this.callstack[this.callstack.length-1].f.environment; 62 | if (G != _G) 63 | throw "ljs.module only supports top-level modules (_G == VM._G!)" 64 | 65 | /*= http://www.lua.org/manual/5.1/manual.html#5.3 66 | Copyright (c) 2006-2008 Lua.org, PUC-Rio [Lua license] 67 | module (name [, ...]) 68 | Creates a module. */ 69 | 70 | /*= If there is a table in package.loaded[name], 71 | this table is the module. */ 72 | var t = _package.loaded.index(name); 73 | 74 | if (t.type == 'nil') { 75 | /*= Otherwise, if there is a global table t with the given name, 76 | this table is the module. */ 77 | t = _G.index(name); 78 | } 79 | 80 | if (t.type == 'nil') { 81 | /*= Otherwise creates a new table t 82 | and sets it as the value of the global name 83 | and the value of package.loaded[name]. */ 84 | t = this.LValue([]); 85 | _G.setIndex(name, t); 86 | _package.loaded.setIndex(name, t); 87 | } 88 | 89 | /*= This function also initializes t._NAME with the given name, 90 | t._M with the module (t itself), 91 | and t._PACKAGE with the package name 92 | (the full module name minus last component; see below). */ 93 | 94 | t.setIndex(this.LValue("_NAME"), name); 95 | t.setIndex(this.LValue("_M"), t); 96 | //= TODO: t.setIndex(this.LValue("_PACKAGE", ... 97 | 98 | /*= Finally, module sets t as the new environment of the current function 99 | and the new value of package.loaded[name], so that require returns t.*/ 100 | 101 | this.callstack[this.callstack.length-1].f.environment = t; 102 | 103 | _package.loaded.setIndex(name, t); 104 | 105 | /*= TODO: If name is a compound name (that is, one with components separated by dots), module creates (or reuses, if they already exist) tables for each component. For instance, if name is a.b.c, then module stores the module table in field c of field b of global a. */ 106 | 107 | /*= TODO: This function can receive optional options after the module 108 | name, where each option is a function to be applied over the module. */ 109 | 110 | if (opt && opt.type != 'nil' && opt.value != 'seeall') 111 | throw "ljs.module: package.seeall only supported"+ 112 | " (optional) second parameter to module() ["+opt+"]"; 113 | 114 | //= /lua.org 115 | 116 | // VERIFY: does lua copy values over as well? 117 | if (opt && opt.value == "seeall") { 118 | for (var p in _G.value) { 119 | //sys.puts("_G."+p+" -> _M."+p); 120 | p = this.LValue(p); 121 | t.setIndex(p, _G.index(p)); 122 | } 123 | } 124 | 125 | return []; 126 | } 127 | 128 | // require() -- faithfully implemented against lua.org manual -t 129 | function lua_require(modname) { 130 | var _package = _getPackageObs(); 131 | 132 | /*= http://www.lua.org/manual/5.1/manual.html#pdf-require 133 | Copyright (c) 2006-2008 Lua.org, PUC-Rio [Lua license] 134 | require (modname) 135 | 136 | Loads the given module. */ 137 | 138 | /*= The function starts by looking into the package.loaded table to 139 | determine whether modname is already loaded. */ 140 | 141 | var value = _package.loaded.index(modname); 142 | 143 | /*= If it is, then require returns the value stored at 144 | package.loaded[modname]. */ 145 | 146 | if (value.type != 'nil') 147 | return [value]; 148 | 149 | /*= Otherwise, it tries to find a loader for the module. 150 | To find a loader, require is guided by the package.loaders array. 151 | By changing this array, we can change how require looks for a module. */ 152 | var errors = []; 153 | var loader = testvm.LValue(null); 154 | for(var i=1; i <= _package.loaders.len(); i++) { 155 | var _search = _package.loaders.index(testvm.LValue(i)); 156 | // sys.puts("trying package.loaders["+i+"]: " + _search); 157 | if (!_search || _search.type != 'function') 158 | throw "package.loaders["+i+"] invalid type: "+(_search?_search.type:'null'); 159 | 160 | var _ret = (_search.call([modname])||[])[0]; 161 | // sys.puts("package.loaders["+i+"] returned " + _ret); 162 | if (!_ret || _ret.type == 'nil') 163 | continue; 164 | if (_ret.type == 'string') { 165 | errors.push("\t"+_ret.toString()); 166 | continue; 167 | } 168 | if (_ret.type == 'function') { 169 | loader = _ret; 170 | break; 171 | } 172 | throw "invalid searcher (package.loaders["+i+"]) retval:"+typeof _ret+"/"+_ret.type; 173 | } 174 | 175 | 176 | 177 | if (loader.type == 'nil') { 178 | 179 | throw "module '"+modname+"' not found:\n"+ 180 | errors.join("\n") 181 | } 182 | 183 | /*= Once a loader is found, 184 | require calls the loader with a single argument, modname. */ 185 | 186 | var retval = loader.call([modname])[0]; 187 | 188 | /*= If the loader returns any value, 189 | require assigns the returned value to package.loaded[modname]. */ 190 | if (retval) 191 | _package.loaded.setIndex(modname, retval); 192 | else { 193 | /*= If the loader returns no value 194 | and has not assigned any value to package.loaded[modname], 195 | then require assigns true to this entry. */ 196 | if (_package.loaded.index(modname).type == 'nil') { 197 | _package.loaded.setIndex(modname, this.LValue(true)); 198 | } 199 | } 200 | 201 | /*= In any case, 202 | require returns the final value of package.loaded[modname]. */ 203 | return [_package.loaded.index(modname)]; 204 | 205 | /*= If there is any error loading or running the module, 206 | or if it cannot find any loader for the module, 207 | then require signals an error. */ 208 | //= /lua.org 209 | } 210 | _G.setIndex(testvm.LValue("module"), testvm.LValue(lua_module)); 211 | _G.setIndex(testvm.LValue("require"), testvm.LValue(lua_require)); 212 | return _G; 213 | } 214 | exports.luaopen_module51 = luaopen_module51; 215 | return exports; 216 | })(typeof exports == 'object' ? exports : typeof window == 'object' ? window : {} ); 217 | 218 | -------------------------------------------------------------------------------- /src/ljs.sys.js: -------------------------------------------------------------------------------- 1 | /** @license 2 | ljs - Lua VM in JavaScript 3 | ljs.sys.js - require('sys') stubs 4 | Copyright (c) 2011 Tim Dedischew 5 | MIT license 6 | */ 7 | 8 | // TODO: more cleanup; prefer console.warn over sys.puts 9 | (function(exports) { 10 | if (typeof require == 'undefined' || !require('sys')) { 11 | if (typeof WScript == 'object') { 12 | console={log:function(a,b,c,d) { WScript.Echo(a,b,c,d);}}; 13 | console.warn=console.log; 14 | } 15 | 16 | var clog = typeof console == 'object' && console.log; 17 | 18 | var stubs = { 19 | sys: { 20 | _printbuf: "", 21 | _outputline: function(s) { 22 | if (!clog) return; 23 | if (console.log == stubs.sys.puts) throw "console.log == stubs.sys.puts"; 24 | console.log("sys._outputline: "+s); 25 | }, 26 | puts: function() { stubs.sys.print(Array.prototype.slice.call(arguments)+"\n"); }, 27 | //debug: function(s) { stubs.sys.puts("DEBUG: "+s); }, 28 | print: function(s) { 29 | // emulate node(?) sys.print behaviour for lvm.js 30 | if (/\n/.test(s)) { 31 | stubs.sys._outputline(stubs.sys._printbuf+s); 32 | stubs.sys._printbuf = ""; 33 | } else 34 | stubs.sys._printbuf += s; 35 | }, 36 | inspect: function(s) { 37 | return s+''; 38 | } 39 | } 40 | }; 41 | 42 | exports.require = function(it) { 43 | return (stubs)[it]; 44 | }; 45 | 46 | exports.console=exports.console || { 47 | warn:function(s){ stubs.sys.puts('(warn)'+s); }, 48 | log:stubs.sys.puts 49 | }; 50 | } else 51 | exports.require = require; 52 | return exports; 53 | })(typeof exports == 'object' ? exports : typeof window == 'object' ? window : {} ); 54 | -------------------------------------------------------------------------------- /tests/fail/OP_CONCAT.lua: -------------------------------------------------------------------------------- 1 | print("string+num" .. 0) 2 | print("string+table" .. {}) 3 | 4 | 5 | -------------------------------------------------------------------------------- /tests/fail/assert.lua: -------------------------------------------------------------------------------- 1 | assert(false, "Yep, assert works."); 2 | -------------------------------------------------------------------------------- /tests/fail/badmodule.lua: -------------------------------------------------------------------------------- 1 | module() 2 | -------------------------------------------------------------------------------- /tests/fail/badmodule2.lua: -------------------------------------------------------------------------------- 1 | module{} 2 | -------------------------------------------------------------------------------- /tests/fail/badmodule3.lua: -------------------------------------------------------------------------------- 1 | module(nil) 2 | -------------------------------------------------------------------------------- /tests/fail/basic_logic_fail.lua: -------------------------------------------------------------------------------- 1 | assert(3 < table) 2 | 3 | -------------------------------------------------------------------------------- /tests/fail/demo_bisect.lua: -------------------------------------------------------------------------------- 1 | -- bisect.lua 2 | -- bisection method for solving non-linear equations 3 | 4 | delta=1e-6 -- tolerance 5 | 6 | function bisect(f,a,b,fa,fb) 7 | local c=(a+b)/2 8 | io.write(n," c=",c," a=",a," b=",b,"\n") 9 | if c==a or c==b or math.abs(a-b) 1, "2 > 1"); 5 | assert(1 <= 2, "1 <= 2"); 6 | assert(2 <= 2, "2 <= 2"); 7 | -------------------------------------------------------------------------------- /tests/pass/bool.lua: -------------------------------------------------------------------------------- 1 | print(type(true)) 2 | print(type(false)) 3 | print(type(nil)) 4 | -------------------------------------------------------------------------------- /tests/pass/callmeta.lua: -------------------------------------------------------------------------------- 1 | local t = setmetatable({ name="test" }, 2 | { 3 | __index=function(self, k) 4 | if k == "metaname" then return "fromindex" end 5 | end, 6 | __call=function(self, x) 7 | return self.name .. " / " 8 | .. self.metaname 9 | .. " / x:" 10 | .. x 11 | end 12 | }) 13 | assert(t(5) == 'test / fromindex / x:5', t(5)) 14 | -------------------------------------------------------------------------------- /tests/pass/concat.lua: -------------------------------------------------------------------------------- 1 | assert(("foo".."bar") == "foobar"); 2 | -------------------------------------------------------------------------------- /tests/pass/demo_account.lua: -------------------------------------------------------------------------------- 1 | -- account.lua 2 | -- from PiL 1, Chapter 16 3 | 4 | Account = {balance = 0, name = "base"} 5 | Account.__index = Account; 6 | 7 | function Account:new (o, name) 8 | o = o or {name=name} 9 | setmetatable(o, self) 10 | return o 11 | end 12 | 13 | function Account:deposit (v) 14 | self.balance = self.balance + v 15 | end 16 | 17 | function Account:withdraw (v) 18 | if v > self.balance then error("insufficient funds on account "..self.name) end 19 | self.balance = self.balance - v 20 | end 21 | 22 | function Account:show (title) 23 | print(title or "", self.name, self.balance) 24 | end 25 | 26 | a = Account:new(nil,"demo") 27 | a:show("after creation") 28 | a:deposit(1000.00) 29 | a:show("after deposit") 30 | a:withdraw(100.00) 31 | a:show("after withdraw") 32 | 33 | -- this would raise an error 34 | --[[ 35 | b = Account:new(nil,"DEMO") 36 | b:withdraw(100.00) 37 | --]] 38 | 39 | -------------------------------------------------------------------------------- /tests/pass/demo_hello.lua: -------------------------------------------------------------------------------- 1 | -- hello.lua 2 | -- the first program in every language 3 | 4 | io.write("Hello world, from ",_VERSION,"!\n") 5 | 6 | -------------------------------------------------------------------------------- /tests/pass/demoscripts.lua: -------------------------------------------------------------------------------- 1 | print("_VERSION", _VERSION) 2 | 3 | -- 2011.08.22 : http://lua-users.org/wiki/DemoScripts 4 | 5 | -- Curried fripperies 6 | put_in = function (t) 7 | local f 8 | f = function (k) 9 | if k then t[k] = true; return f 10 | else return end -- if 11 | end -- function 12 | return f 13 | end -- function 14 | bag, enough = {} 15 | put_in (bag) "wibble" (57) "grumpkin" "foo" (enough) 16 | for k,v in pairs(bag) do print("bag has",k,"?",v) end 17 | 18 | --- the classic recursive example: 19 | function factorial(n) 20 | if n == 0 then 21 | return 1 22 | else 23 | return n * factorial(n-1) 24 | end 25 | end 26 | 27 | io.write("factorial of 10 is ", factorial(10), "\n") 28 | 29 | -- and its tail recursive variant 30 | fact = function (n) 31 | local f 32 | f = function (m,a) 33 | if m == 0 then return a end -- if 34 | return f(m-1,m*a) end -- function 35 | return f(n,1) end -- function 36 | print("factorial of 10 is",fact(10)) 37 | -------------------------------------------------------------------------------- /tests/pass/forloop.lua: -------------------------------------------------------------------------------- 1 | local ret = 0 2 | for i = 1, 10 do 3 | print("counting to 10...", i) 4 | ret = ret + i 5 | end 6 | assert(ret == 55, ret) 7 | 8 | ret=0 9 | for i = 1, 10,2 do 10 | print("counting to 10 by 2...", i) 11 | ret = ret + i 12 | end 13 | 14 | assert(ret == 25, ret) 15 | 16 | ret=0 17 | for i = 10, 1, -1 do 18 | print("counting down from 10...", i) 19 | ret = ret + i 20 | end 21 | assert(ret == 55, ret) 22 | print(i) 23 | 24 | function looponnvars(nvars) 25 | 26 | -- in ljs < 0.0011 this would mutate "nvars"... 27 | for i = nvars, 0, -1 do 28 | print("i,loop.nvars", i, nvars) 29 | end 30 | end 31 | 32 | function incnvars(nvars) 33 | nvars = nvars + 1 34 | end 35 | function test(a, nvars) 36 | local atstart = nvars 37 | print("test.nvars", nvars) 38 | incnvars(nvars) 39 | print("test.nvars", nvars) 40 | looponnvars(nvars) 41 | print("test.nvars", nvars) 42 | assert(atstart == nvars) 43 | end 44 | 45 | local nvars = 5 46 | print("main.nvars", nvars) 47 | test(1, nvars) 48 | print("main.nvars", nvars) 49 | assert(nvars == 5) -------------------------------------------------------------------------------- /tests/pass/gmatch.lua: -------------------------------------------------------------------------------- 1 | local i = 1; 2 | local s = "the rain in spain stays mainly in the plain"; 3 | local st = { "the", "rain", "in", "spain", "stays", "mainly", "in", "the", "plain" }; 4 | for word in string.gmatch(s, "%a+") do 5 | print(word) 6 | assert(word == st[i], word.." == "..st[i]); 7 | i = i + 1; 8 | end 9 | 10 | assert(i == 10, i.." == 10"); 11 | -------------------------------------------------------------------------------- /tests/pass/hello.lua: -------------------------------------------------------------------------------- 1 | local a, b = "One", "Deux"; 2 | 3 | function pp() 4 | local _b = b; 5 | a = "Une" 6 | local c = "Trois!"; 7 | local function b() 8 | return a, _b, c; 9 | end 10 | return b; 11 | end 12 | 13 | print(pp()()) 14 | 15 | print(a) 16 | -------------------------------------------------------------------------------- /tests/pass/indexmeta.lua: -------------------------------------------------------------------------------- 1 | local t = setmetatable({ name="test" }, 2 | { 3 | __index=function(self, k) 4 | if k == "metaname" then return "fromindex" end 5 | if k == "tabval" then return setmetatable({1234}, 6 | {__index={inner=true}}) end 7 | end, 8 | }) 9 | 10 | assert(t.metaname == "fromindex") 11 | assert(#t.tabval == 1, #t.tabval) 12 | assert(t.tabval[1] == 1234) 13 | assert(t.tabval.inner == true) 14 | -------------------------------------------------------------------------------- /tests/pass/jsffi.lua: -------------------------------------------------------------------------------- 1 | if ffi then 2 | --local ffi = require("ffi") 3 | ffi.jsdef[[ 4 | function warn(x) { console.warn(x); } 5 | function debug(x) { require('sys').debug(x); } 6 | function plusone(x) { return x+1; } 7 | function add(x,y) { return x+y; } 8 | ]] 9 | print("ffi.os=", ffi.os) 10 | print("ffi.js.debug=", ffi.js.debug); 11 | local emit 12 | if ffi.os == "node" then 13 | function emit(s) 14 | ffi.js.debug(s) 15 | end 16 | else 17 | function emit(s) 18 | ffi.js.warn(s) 19 | end 20 | end 21 | 22 | emit("hi!") 23 | emit(ffi.js.plusone(6)) 24 | 25 | assert(ffi.js.plusone(1)==2) 26 | assert(ffi.js.add(10,20)==30) 27 | end -------------------------------------------------------------------------------- /tests/pass/ldexp.lua: -------------------------------------------------------------------------------- 1 | assert(math.ldexp) 2 | assert(math.ldexp(5,5) == 160) 3 | assert(math.ldexp(1.2345678,2) == 4.9382712) 4 | -------------------------------------------------------------------------------- /tests/pass/length.lua: -------------------------------------------------------------------------------- 1 | assert(#("hello") == 5, "#('hello') == 5"); 2 | -------------------------------------------------------------------------------- /tests/pass/loop.lua: -------------------------------------------------------------------------------- 1 | for i=1,1000 do print(i) end 2 | -------------------------------------------------------------------------------- /tests/pass/meta__eq.lua: -------------------------------------------------------------------------------- 1 | 2 | -- http://lua-users.org/wiki/MetatableEvents 3 | t1a = {} 4 | t1b = {} 5 | t2 = {} 6 | mt1 = { __eq = function( o1, o2 ) return 'whee' end } 7 | mt2 = { __eq = function( o1, o2 ) return 'whee' end } 8 | 9 | setmetatable( t1a, mt1 ) 10 | setmetatable( t1b, mt1 ) 11 | setmetatable( t2, mt2 ) 12 | 13 | print( t1a == t1b ) --> true 14 | print( t1a == t2 ) --> false 15 | 16 | assert(t1a == t1b) 17 | assert(t1a ~= t2) 18 | 19 | local fooset = false 20 | function foo (o1, o2) 21 | print( '__eq call' ) 22 | fooset = true 23 | return false 24 | end 25 | 26 | t1 = {} 27 | setmetatable( t1, {__eq = foo} ) 28 | 29 | t2 = t1 30 | print( t1 == t2 ) --> true 31 | assert(t1 == t2) 32 | assert(not fooset) 33 | -- string '__eq call' not printed (and comparison result is true, not like the return value of foo(...)), so no foo(...) call here 34 | 35 | t3 = {} 36 | setmetatable( t3, {__eq = foo} ) 37 | if t1 == t3 then assert(fooset) end --> __eq call 38 | -- foo(...) was called 39 | -------------------------------------------------------------------------------- /tests/pass/nilglobal.lua: -------------------------------------------------------------------------------- 1 | print(hello) -------------------------------------------------------------------------------- /tests/pass/op_close.lua: -------------------------------------------------------------------------------- 1 | local f = {}; 2 | for i=1,2 do 3 | local p; 4 | f[i] = function (set) 5 | if set then 6 | p = set; 7 | end 8 | return p; 9 | end; 10 | end 11 | 12 | assert(f[1]("foo") == "foo"); 13 | assert(f[2]("bar") == "bar"); 14 | assert(f[1]() == "foo"); 15 | assert(f[2]() == "bar"); 16 | -------------------------------------------------------------------------------- /tests/pass/pairs.lua: -------------------------------------------------------------------------------- 1 | local ret = "" 2 | for k,v in pairs({1,'a',3}) do 3 | ret = ret .. k .. ":" .. v .. "|" 4 | end 5 | assert(ret == "1:1|2:a|3:3|", ret) 6 | 7 | 8 | local ret = "" 9 | for k,v in pairs({a=1,b='a'}) do 10 | ret = ret .. k .. ":" .. v .. "|" 11 | end 12 | assert(ret == "a:1|b:a|" or ret == "b:a|a:1|" , ret) 13 | 14 | 15 | local ret = "" 16 | for k,v in pairs{[0]='zero',[1]='one'} do 17 | ret = ret .. k .. ":" .. v .. "|" 18 | end 19 | assert(ret == "0:zero|1:one|" or ret == "1:one|0:zero|" , ret) 20 | 21 | -------------------------------------------------------------------------------- /tests/pass/require.lua: -------------------------------------------------------------------------------- 1 | package.preload.themodule = function(name) 2 | print("preload called for: ", name) 3 | module(name, package.seeall) 4 | print("_M", _M) 5 | function _M.afunc(x) return x .. x end 6 | end 7 | 8 | assert(not themodule) 9 | print(require'themodule') 10 | assert(require'themodule'.afunc) 11 | assert(themodule.afunc("x") == "xx") 12 | -------------------------------------------------------------------------------- /tests/pass/string_byte.lua: -------------------------------------------------------------------------------- 1 | assert(string.byte('A') == 65) 2 | -------------------------------------------------------------------------------- /tests/pass/string_find.lua: -------------------------------------------------------------------------------- 1 | assert(not string.find(".", "B", 1,1)) 2 | local two, three = string.find("asdf","sd",nil,true) 3 | assert(two == 2 and three == 3) 4 | 5 | assert(1 == string.find("asd.f", "."), string.find("asd.f", ".")) 6 | assert(3 == string.find("asd.f", ".", 3)) 7 | assert(4 == string.find("asd.f", ".", 3, true)) 8 | 9 | -- http://lua-users.org/wiki/PatternsTutorial 10 | 11 | local a,b = string.find('banana', 'an') -- find 1st occurance of 'an' 12 | assert(a == 2 and b == 3, a.."/"..b) 13 | 14 | assert(not string.find('banana', 'lua')) -- 'lua' will not be found 15 | 16 | local text = 'a word to the wise' 17 | local ret = string.sub(text, string.find(text, 'w...')) 18 | assert(ret == "word", ret) 19 | ret = string.sub(text, string.find(text, 'w...', 5)) -- bit further on... 20 | assert("wise" == ret) 21 | 22 | 23 | function findpattern(text, pattern, start) 24 | return string.sub(text, string.find(text, pattern, start)) 25 | end 26 | 27 | ret = findpattern('a word to the wise', 'w...') 28 | assert("word" == ret, ret) 29 | 30 | assert("wise" == findpattern('a word to the wise', 'w...', 5)) 31 | 32 | assert("T" == findpattern('The quick brown fox', '%a')) -- %a is all letters 33 | assert("2" == findpattern('it is 2003', '%d')) -- %d finds digits 34 | assert("l" == findpattern('UPPER lower', '%l')) -- %l finds lowercase characters 35 | assert("U" == findpattern('UPPER lower', '%u')) -- %u finds uppercase characters 36 | assert("Rl" == findpattern('UPPERlower', '%u%l')) -- upper followed by lower 37 | assert("234" == findpattern('123 234 345', '%d3%d')) -- digit 3 digit 38 | 39 | 40 | assert("n" == findpattern('banana', '[xyjkn]')) -- look for one of "xyjkn" 41 | assert("n" == findpattern('banana', '[j-q]')) -- equivalent to "jklmnopq" 42 | assert("2" == findpattern('it is 2003', '[%dabc]')) 43 | assert("n" == findpattern('banana', '[^ba]')) -- we don't want a or b 44 | assert("s" == findpattern('bananas', '[^a-n]')) -- forget a to n 45 | assert("2" == findpattern('it is 2003', '[^%l%s]')) -- not a lowercase or space 46 | 47 | 48 | assert("annnnn" == findpattern('bannnnnanas', 'an*')) 49 | 50 | assert(nil == string.find('banana', 'az+')) -- won't be found 51 | assert("an" == findpattern('bananas', 'an+')) 52 | 53 | assert("annnnn" == findpattern('bannnnnanas', 'an+'), (findpattern('bannnnnanas', 'an+'))) 54 | 55 | --fail/ assert("a" == findpattern('bananas', 'az-'), (findpattern('bananas', 'az-'))) 56 | --fail/ assert("a" == findpattern('bananas', 'an-')) 57 | --fail/ assert("a" == findpattern('bannnnanas', 'an-')) 58 | -------------------------------------------------------------------------------- /tests/pass/string_lower.lua: -------------------------------------------------------------------------------- 1 | assert(string.lower("A") == "a") 2 | assert(string.lower(5) == "5") -- lua allows this 3 | assert(string.lower("aA") == "aa") -------------------------------------------------------------------------------- /tests/pass/string_sub.lua: -------------------------------------------------------------------------------- 1 | assert("Lua user" == string.sub("Hello Lua user", 7)) -- from character 7 until the end 2 | assert("Lua" == string.sub("Hello Lua user", 7, 9)) -- from character 7 until and including 9 3 | 4 | local ret = string.sub("Hello Lua user", -8) 5 | assert("Lua user" == ret,ret.."/Lua user") -- 8 from the end until the end 6 | 7 | ret = string.sub("Hello Lua user", -8, 9) 8 | assert("Lua" == ret, ret) -- 8 from the end until 9 from the start 9 | 10 | ret = string.sub("Hello Lua user", -8, -6) 11 | assert("Lua" == ret, ret.."/Lua") -- 8 from the end until 6 from the end 12 | -------------------------------------------------------------------------------- /tests/pass/t.lua: -------------------------------------------------------------------------------- 1 | local a = { two = "two" }; 2 | a.one = "one" 3 | local b = "one"; 4 | print(a[b], a.two) 5 | -------------------------------------------------------------------------------- /tests/pass/tableconcat.lua: -------------------------------------------------------------------------------- 1 | local ret = table.concat({1,2,3},"|") 2 | assert(ret == "1|2|3", ret) 3 | 4 | -------------------------------------------------------------------------------- /tests/pass/tablekeys.lua: -------------------------------------------------------------------------------- 1 | local t = {} 2 | 3 | t[1] = 1 4 | assert(#t == 1) 5 | 6 | t[0] = 0 7 | assert(t[0] and #t == 1) 8 | 9 | t[5.5] = t 10 | assert(t[5.5] == t) 11 | 12 | --assert(t["5.5"] == nil) -- not yet, still figuring out js-side of tables 13 | -------------------------------------------------------------------------------- /tests/pass/test_test.lua: -------------------------------------------------------------------------------- 1 | local a, b = true, false; a = a and b; 2 | print(a); 3 | -------------------------------------------------------------------------------- /tests/pass/tostring.lua: -------------------------------------------------------------------------------- 1 | assert(tostring(3) == "3") 2 | assert(tostring('3') == "3") 3 | local t = setmetatable({ name="test" }, 4 | { __tostring=function(self) 5 | return "viameta:"..self.name 6 | end 7 | }); 8 | assert(tostring(t) == 'viameta:test') 9 | assert(string.find(tostring(getmetatable(t)), "^table:"), tostring(getmetatable(t))) 10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/pass/upvalue.lua: -------------------------------------------------------------------------------- 1 | local u; 2 | local f = {}; 3 | for i=1,2 do 4 | f[i] = function (set) 5 | if set then 6 | u = set; 7 | end 8 | return u; 9 | end; 10 | end 11 | 12 | assert(f[1]("foo") == "foo"); 13 | assert(f[2]() == "foo"); 14 | 15 | assert(f[2]("bar") == "bar"); 16 | assert(f[1]() == "bar"); 17 | 18 | local prevmeta = setmetatable({name="prevmeta"}, {__index={id="#prevmeta"}}) 19 | 20 | local upwithmeta = {name="upwithmeta"} 21 | function maker() 22 | return function() 23 | assert(getmetatable(upwithmeta), "upwithmeta has no metatable!") 24 | assert("#"..upwithmeta.name == upwithmeta.id) 25 | end 26 | end 27 | setmetatable(upwithmeta, 28 | {__index={id="#upwithmeta"}}) 29 | 30 | maker()() 31 | 32 | function taker() 33 | return function() 34 | assert(getmetatable(upwithmeta), "upwithmeta has no metatable!") 35 | upwithmeta = prevmeta 36 | print(upwithmeta.name, upwithmeta.id) 37 | assert("#prevmeta" == upwithmeta.id) 38 | end 39 | end 40 | taker()() 41 | local luaZ = {} 42 | function luaZ:make_getS(buff) 43 | print("buff",buff) 44 | local b = buff 45 | -- local data 46 | return function() -- chunk reader anonymous function here 47 | print("buff",b) 48 | if not b then return nil end 49 | local data = b 50 | b = nil 51 | return data 52 | end 53 | end 54 | 55 | local blah = "print(5)" 56 | local tmp = luaZ:make_getS(""..blah) 57 | local blah2 = tmp() 58 | assert(blah2 == blah, tostring(blah) .. "~="..tostring(blah2)) 59 | 60 | -------------------------------------------------------------------------------- /tests/pass/vararg.lua: -------------------------------------------------------------------------------- 1 | function foo(a, b, ...) 2 | assert(a == 1); 3 | assert(b == 2); 4 | local c, d = ...; 5 | assert(c == 3); 6 | assert(d == 4); 7 | return a, b, ...; 8 | end 9 | 10 | local a, b, c, d = foo(1, 2, 3, 4); 11 | assert(a == 1); 12 | assert(b == 2); 13 | print("c is", c); 14 | assert(c == 3); 15 | assert(d == 4); 16 | 17 | -------------------------------------------------------------------------------- /tests/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$LVMJS" ] ; then 4 | LVMJS="node tests/runljs.js" 5 | fi 6 | 7 | echo "LVMJS=$LVMJS" 8 | 9 | set -e 10 | 11 | failed=""; 12 | function failtest { 13 | # allow aggressive control-c to interrupt entire test run 14 | # (control-c will cause failtest to be called...) 15 | # (control-c on sleep will exit script) 16 | sleep .25 17 | echo "[FAILED] $1"; 18 | failed="$failed $1"; 19 | failed_count=$(($failed_count+1)) 20 | } 21 | test_count=0 22 | failed_count=0 23 | echo "---------------------------------------------" 24 | echo " tests/pass/*.lua" 25 | for script in tests/pass/*.lua; do 26 | ( luac "$script" && 27 | $LVMJS 2>&1 && 28 | echo " [ OK ] $script" >>/dev/stderr 29 | ) > /dev/null || failtest "$script"; 30 | test_count=$(($test_count+1)) 31 | done 32 | echo " -- tested $test_count .lua's" 33 | 34 | echo "---------------------------------------------" 35 | echo " tests/fail/*.lua (nada == pass)" 36 | st=$test_count 37 | for script in tests/fail/*.lua; do 38 | luac "$script" && $LVMJS >/dev/null 2>&1 && failtest "$script"; 39 | test_count=$(($test_count+1)) 40 | done 41 | echo " -- tested $(($test_count - $st)) .lua's" 42 | 43 | echo "---------------------------------------------" 44 | echo $(($test_count-$failed_count))"/$test_count TESTS PASSED"; 45 | if ! [ "$failed" == "" ]; then 46 | echo "$failed_count TESTS FAILED:"; 47 | echo "$failed"; 48 | exit 1; 49 | fi 50 | echo "" 51 | exit 0; 52 | -------------------------------------------------------------------------------- /tests/runLJS-min.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Tim Dedischew 2 | // Copyright (c) 2010 Matthew Wild 3 | 4 | var sys = require("sys"); 5 | var fs=require("fs"); 6 | 7 | var LVM = require("../LJS-min").LVM; 8 | var make_G = require("../LJS-min").make_G; 9 | var luaopen_bytecode51 = require('../LJS-min').luaopen_bytecode51; 10 | var luaopen_module51 = require('../LJS-min').luaopen_module51; 11 | 12 | sys.print = function(stuff) { process.stdout.write(stuff, 'binary'); }; 13 | 14 | try { 15 | var testvm = new LVM(); 16 | var _G = make_G(testvm); 17 | luaopen_module51(testvm, _G); 18 | luaopen_bytecode51(testvm, _G); 19 | var f = testvm.loadbytecode(fs.readFileSync("luac.out", "binary"), _G); 20 | 21 | var arg = []; 22 | for (var i=2; /* +2 bc [0] is process name [1] is script name*/ 23 | i < process.argv.length; i++) 24 | arg.push(testvm.LValue(process.argv[i])); 25 | 26 | if (arg.length == 0) 27 | _G.setIndex(testvm.LValue("arg"), testvm.LValue([])); 28 | 29 | var ret = testvm.call(f, arg); 30 | 31 | if(ret) 32 | sys.debug("Returned: "+sys.inspect(ret)); 33 | 34 | } catch(e) { 35 | var trace = testvm.traceback(); 36 | var currframe = trace[0]; 37 | if(currframe) 38 | sys.puts("lua(ljs): "+currframe.sourceName+":"+currframe.line+": "+e); 39 | else 40 | sys.puts(e); 41 | 42 | if(typeof(e) == "object" && "stack" in e) 43 | sys.puts(e.stack); 44 | else if (trace.length > 0) { 45 | var lines = []; 46 | try { 47 | lines = (fs.readFileSync(trace[0].sourceName, "binary")).split(/\n/); 48 | } catch(e) { 49 | sys.debug("ljs.node: was going to inspect lua source, but:" +e); 50 | } 51 | sys.puts("stack traceback:"); 52 | trace[trace.length-1].context = "in main chunk"; 53 | for (var i=0; i < trace.length; i++) { 54 | if (lines.length) { 55 | // naive function name deduction... 56 | trace[i].actualSourceLine = lines[trace[i].line-1]; 57 | if (!trace[i].context) { 58 | for (var l=0; l < 100 && trace[i].line-1-l >= 0; l++) { 59 | var upline = lines[trace[i].line-1-l]; 60 | var arr = upline.match( 61 | /function ([\w:]+)?/ 62 | ) || upline.match(/([\w:]+)\s*=\s*function/); 63 | if (arr) { 64 | trace[i].context = "perhaps in function '"+(arr[1]||arr[2])+"'"; 65 | break; 66 | } 67 | } 68 | } 69 | } 70 | sys.puts("\t"+trace[i].sourceName+":"+trace[i].line+": " 71 | +(trace[i].context||"")); 72 | 73 | if (i == 0 || trace[i].actualSourceLine && process.argv[2] == '-v') 74 | sys.debug("(sourceline)\t"+trace[i].actualSourceLine); 75 | } 76 | } 77 | process.exit(1); 78 | } 79 | -------------------------------------------------------------------------------- /tests/runljs.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Tim Dedischew 2 | // Copyright (c) 2010 Matthew Wild 3 | 4 | var sys = require("sys"); 5 | var fs=require("fs"); 6 | 7 | var LVM = require("../src/ljs.lvm").LVM; 8 | var make_G = require("../src/ljs.baselib").make_G; 9 | var luaopen_bytecode51 = require('../src/ljs.bytecode').luaopen_bytecode51; 10 | var luaopen_module51 = require('../src/ljs.module51').luaopen_module51; 11 | 12 | sys.print = function(stuff) { process.stdout.write(stuff, 'binary'); }; 13 | 14 | try { 15 | var testvm = new LVM(); 16 | var _G = make_G(testvm); 17 | luaopen_module51(testvm, _G); 18 | luaopen_bytecode51(testvm, _G); 19 | var f = testvm.loadbytecode(fs.readFileSync("luac.out", "binary"), _G); 20 | 21 | var arg = []; 22 | for (var i=2; /* +2 bc [0] is process name [1] is script name*/ 23 | i < process.argv.length; i++) 24 | arg.push(testvm.LValue(process.argv[i])); 25 | 26 | if (arg.length == 0) 27 | _G.setIndex(testvm.LValue("arg"), testvm.LValue([])); 28 | 29 | var ret = testvm.call(f, arg); 30 | 31 | if(ret) 32 | sys.debug("Returned: "+sys.inspect(ret)); 33 | 34 | } catch(e) { 35 | var trace = testvm.traceback(); 36 | var currframe = trace[0]; 37 | if(currframe) 38 | sys.puts("lua(ljs): "+currframe.sourceName+":"+currframe.line+": "+e); 39 | else 40 | sys.puts(e); 41 | 42 | if(typeof(e) == "object" && "stack" in e) 43 | sys.puts(e.stack); 44 | else if (trace.length > 0) { 45 | var lines = []; 46 | try { 47 | lines = (fs.readFileSync(trace[0].sourceName, "binary")).split(/\n/); 48 | } catch(e) { 49 | sys.debug("ljs.node: was going to inspect lua source, but:" +e); 50 | } 51 | sys.puts("stack traceback:"); 52 | trace[trace.length-1].context = "in main chunk"; 53 | for (var i=0; i < trace.length; i++) { 54 | if (lines.length) { 55 | // naive function name deduction... 56 | trace[i].actualSourceLine = lines[trace[i].line-1]; 57 | if (!trace[i].context) { 58 | for (var l=0; l < 100 && trace[i].line-1-l >= 0; l++) { 59 | var upline = lines[trace[i].line-1-l]; 60 | var arr = upline.match( 61 | /function ([\w:]+)?/ 62 | ) || upline.match(/([\w:]+)\s*=\s*function/); 63 | if (arr) { 64 | trace[i].context = "perhaps in function '"+(arr[1]||arr[2])+"'"; 65 | break; 66 | } 67 | } 68 | } 69 | } 70 | sys.puts("\t"+trace[i].sourceName+":"+trace[i].line+": " 71 | +(trace[i].context||"")); 72 | 73 | if (i == 0 || trace[i].actualSourceLine && process.argv[2] == '-v') 74 | sys.debug("(sourceline)\t"+trace[i].actualSourceLine); 75 | } 76 | } 77 | process.exit(1); 78 | } 79 | --------------------------------------------------------------------------------