aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lapi.js40
-rw-r--r--src/lbaselib.js3
-rw-r--r--src/lcode.js1266
-rw-r--r--src/lcorolib.js1
-rw-r--r--src/ldebug.js6
-rw-r--r--src/ldo.js78
-rw-r--r--src/lfunc.js24
-rw-r--r--src/linit.js1
-rw-r--r--src/ljstype.js29
-rw-r--r--src/llex.js609
-rw-r--r--src/lmathlib.js1
-rw-r--r--src/lobject.js197
-rw-r--r--src/lopcodes.js232
-rw-r--r--src/lparser.js1560
-rw-r--r--src/lstate.js2
-rw-r--r--src/ltablib.js1
-rw-r--r--src/lua.js73
-rw-r--r--src/lualib.js25
-rw-r--r--src/lundump.js3
-rw-r--r--src/lvm.js65
20 files changed, 4069 insertions, 147 deletions
diff --git a/src/lapi.js b/src/lapi.js
index d0c099f..36da7c6 100644
--- a/src/lapi.js
+++ b/src/lapi.js
@@ -1,18 +1,18 @@
-/* jshint esversion: 6 */
"use strict";
const assert = require('assert');
+const ldebug = require('./ldebug.js');
const ldo = require('./ldo.js');
+const lfunc = require('./lfunc.js');
+const llex = require('./llex.js');
const lobject = require('./lobject.js');
+const lstate = require('./lstate.js');
const ltm = require('./ltm.js');
-const lfunc = require('./lfunc.js');
const lua = require('./lua.js');
const luaconf = require('./luaconf.js');
-const lstate = require('./lstate.js');
-const lvm = require('./lvm.js');
const lundump = require('./lundump.js');
-const ldebug = require('./ldebug.js');
+const lvm = require('./lvm.js');
const MAXUPVAL = lfunc.MAXUPVAL;
const CT = lua.constant_types;
const TS = lua.thread_status;
@@ -138,7 +138,7 @@ const lua_settop = function(L, idx) {
const lua_pop = function(L, n) {
lua_settop(L, -n - 1);
-}
+};
const reverse = function(L, from, to) {
for (; from < to; from++, to--) {
@@ -150,7 +150,7 @@ const reverse = function(L, from, to) {
/*
** Let x = AB, where A is a prefix of length 'n'. Then,
-** rotate x n == BA. But BA == (A^r . B^r)^r.
+** rotate x n === BA. But BA === (A^r . B^r)^r.
*/
const lua_rotate = function(L, idx, n) {
let t = L.stack[L.top - 1];
@@ -357,7 +357,7 @@ const lua_settable = function(L, idx) {
};
const lua_setfield = function(L, idx, k) {
- auxsetstr(L, index2addr(L, idx), k)
+ auxsetstr(L, index2addr(L, idx), k);
};
const lua_seti = function(L, idx, n) {
@@ -515,11 +515,11 @@ const lua_rawlen = function(L, idx) {
};
const lua_tointeger = function(L, idx) {
- return lvm.tointeger(index2addr(L, idx))
+ return lvm.tointeger(index2addr(L, idx));
};
const lua_tonumber = function(L, idx) {
- return lvm.tonumber(index2addr(L, idx))
+ return lvm.tonumber(index2addr(L, idx));
};
const lua_tothread = function(L, idx) {
@@ -587,7 +587,7 @@ const f_call = function(L, ud) {
const lua_type = function(L, idx) {
let o = index2addr(L, idx);
- return o.ttnov(); // TODO: isvalid ? luaO_nilobject != nil tvalue ?
+ return o.ttnov(); // TODO: isvalid ? luaO_nilobject !== nil tvalue ?
};
const lua_typename = function(L, t) {
@@ -597,7 +597,7 @@ const lua_typename = function(L, t) {
const lua_isnoneornil = function(L, n) {
return lua_type(L, n) <= 0;
-}
+};
const lua_istable = function(L, idx) {
return index2addr(L, idx).ttistable();
@@ -626,13 +626,14 @@ const lua_rawequal = function(L, index1, index2) {
** 'load' and 'call' functions (run Lua code)
*/
-const lua_load = function(L, data, chunckname) {
+// TODO: reader is ignored because we don't implement ZIO
+const lua_load = function(L, reader, data, chunckname, mode) {
+ let z = new llex.MBuffer(data);
if (!chunckname) chunckname = "?";
-
- let status = ldo.luaD_protectedparser(L, data, chunckname);
- if (status === TS.LUA_OK) {
+ let status = ldo.luaD_protectedparser(L, z, chunckname, mode);
+ if (status === TS.LUA_OK) { /* no errors? */
let f = L.stack[L.top - 1]; /* get newly created function */
- if (f.nupvalues >= 1) { /* does it have an upvalue? */
+ if (f.nupvalues >= 1) { /* does it have an upvalue? */
/* get global table from registry */
let reg = L.l_G.l_registry;
let gt = reg.value.get(lua.LUA_RIDX_GLOBALS - 1);
@@ -640,7 +641,6 @@ const lua_load = function(L, data, chunckname) {
f.upvals[0].u.value = gt;
}
}
-
return status;
};
@@ -663,7 +663,7 @@ const lua_callk = function(L, nargs, nresults, ctx, k) {
ldo.luaD_callnoyield(L, func, nresults);
}
- if (nresults == lua.LUA_MULTRET && L.ci.top < L.top)
+ if (nresults === lua.LUA_MULTRET && L.ci.top < L.top)
L.ci.top = L.top;
};
@@ -714,7 +714,7 @@ const lua_pcallk = function(L, nargs, nresults, errfunc, ctx, k) {
status = TS.LUA_OK;
}
- if (nresults == lua.LUA_MULTRET && L.ci.top < L.top)
+ if (nresults === lua.LUA_MULTRET && L.ci.top < L.top)
L.ci.top = L.top;
return status;
diff --git a/src/lbaselib.js b/src/lbaselib.js
index 012f5c3..77638d0 100644
--- a/src/lbaselib.js
+++ b/src/lbaselib.js
@@ -1,4 +1,3 @@
-/* jshint esversion: 6 */
"use strict";
const assert = require('assert');
@@ -91,7 +90,7 @@ const luaB_rawset = function(L) {
const luaB_type = function(L) {
let t = lapi.lua_type(L, 1);
- lauxlib.luaL_argcheck(L, t != CT.LUA_TNONE, 1, "value expected");
+ lauxlib.luaL_argcheck(L, t !== CT.LUA_TNONE, 1, "value expected");
lapi.lua_pushstring(L, lapi.lua_typename(L, t));
return 1;
};
diff --git a/src/lcode.js b/src/lcode.js
new file mode 100644
index 0000000..d8b0252
--- /dev/null
+++ b/src/lcode.js
@@ -0,0 +1,1266 @@
+"use strict";
+
+const assert = require('assert');
+
+const llex = require('./llex.js');
+const llimit = require('./llimit.js');
+const lobject = require('./lobject.js');
+const lopcode = require('./lopcodes.js');
+const lparser = require('./lparser.js');
+const ltm = require('./ltm.js');
+const lua = require('./lua.js');
+const lvm = require('./lvm.js');
+const CT = lua.CT;
+const OpCodesI = lopcode.OpCodesI;
+const TValue = lobject.TValue;
+
+const luaO_arith = function(L, op, p1, p2, res) {
+ switch (op) {
+ case lvm.LUA_OPBAND: case lvm.LUA_OPBOR: case lvm.LUA_OPBXOR:
+ case lvm.LUA_OPSHL: case lvm.LUA_OPSHR:
+ case lvm.LUA_OPBNOT: { /* operate only on integers */
+ let i1 = lvm.tointeger(p1);
+ let i2 = lvm.tointeger(p2);
+ if (i1 !== false && i2 !== false) {
+ res.type = CT.LUA_TNUMINT;
+ res.value = lobject.intarith(L, op, i1, i2);
+ return;
+ }
+ else break; /* go to the end */
+ }
+ case lvm.LUA_OPDIV: case lvm.LUA_OPPOW: { /* operate only on floats */
+ let n1 = lvm.tonumber(p1);
+ let n2 = lvm.tonumber(p2);
+ if (n1 !== false && n2 !== false) {
+ res.type = CT.LUA_TNUMFLT;
+ res.value = lobject.numarith(L, op, n1, n2);
+ return;
+ }
+ else break; /* go to the end */
+ }
+ default: { /* other operations */
+ let n1 = lvm.tonumber(p1);
+ let n2 = lvm.tonumber(p2);
+ if (p1.ttisinteger() && p2.ttisinteger()) {
+ res.type = CT.LUA_TNUMINT;
+ res.value = lobject.intarith(L, op, p1.value, p2.value);
+ return;
+ }
+ else if (n1 !== false && n2 !== false) {
+ res.type = CT.LUA_TNUMFLT;
+ res.value = lobject.numarith(L, op, n1, n2);
+ return;
+ }
+ else break; /* go to the end */
+ }
+ }
+ /* could not perform raw operation; try metamethod */
+ assert(L !== null); /* should not fail when folding (compile time) */
+ ltm.luaT_trybinTM(L, p1, p2, res, (op - lua.LUA_OPADD) + ltm.TMS.TM_ADD);
+};
+
+/* Maximum number of registers in a Lua function (must fit in 8 bits) */
+const MAXREGS = 255;
+
+/*
+** Marks the end of a patch list. It is an invalid value both as an absolute
+** address, and as a list link (would link an element to itself).
+*/
+const NO_JUMP = -1;
+
+const BinOpr = {
+ OPR_ADD: 0,
+ OPR_SUB: 1,
+ OPR_MUL: 2,
+ OPR_MOD: 3,
+ OPR_POW: 4,
+ OPR_DIV: 5,
+ OPR_IDIV: 6,
+ OPR_BAND: 7,
+ OPR_BOR: 8,
+ OPR_BXOR: 9,
+ OPR_SHL: 10,
+ OPR_SHR: 11,
+ OPR_CONCAT: 12,
+ OPR_EQ: 13,
+ OPR_LT: 14,
+ OPR_LE: 15,
+ OPR_NE: 16,
+ OPR_GT: 17,
+ OPR_GE: 18,
+ OPR_AND: 19,
+ OPR_OR: 21,
+ OPR_NOBINOPR: 22
+};
+
+const UnOpr = {
+ OPR_MINUS: 0,
+ OPR_BNOT: 1,
+ OPR_NOT: 2,
+ OPR_LEN: 3,
+ OPR_NOUNOPR: 4
+};
+
+const hasjumps = function(e) {
+ return e.t !== e.f;
+};
+
+/*
+** If expression is a numeric constant, fills 'v' with its value
+** and returns true. Otherwise, returns false.
+*/
+const tonumeral = function(e, v) {
+ let ek = lparser.expkind;
+ if (hasjumps(e))
+ return false; /* not a numeral */
+ switch (e.k) {
+ case ek.VKINT:
+ if (v) {
+ v.type = CT.LUA_TNUMINT;
+ v.value = e.u.ival;
+ }
+ return true;
+ case ek.VKFLT:
+ if (v) {
+ v.type = CT.LUA_TNUMFLT;
+ v.value = e.u.nval;
+ }
+ return true;
+ default: return false;
+ }
+};
+
+/*
+** Create a OP_LOADNIL instruction, but try to optimize: if the previous
+** instruction is also OP_LOADNIL and ranges are compatible, adjust
+** range of previous instruction instead of emitting a new one. (For
+** instance, 'local a; local b' will generate a single opcode.)
+*/
+const luaK_nil = function(fs, from, n) {
+ let previous;
+ let l = from + n - 1; /* last register to set nil */
+ if (fs.pc > fs.lasttarget) { /* no jumps to current position? */
+ previous = fs.f.code[fs.pc-1];
+ if (previous.opcode === OpCodesI.OP_LOADNIL) { /* previous is LOADNIL? */
+ let pfrom = previous.A; /* get previous range */
+ let pl = pfrom + previous.B;
+ if ((pfrom <= from && from <= pl + 1) ||
+ (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */
+ if (pfrom < from) from = pfrom; /* from = min(from, pfrom) */
+ if (pl > l) l = pl; /* l = max(l, pl) */
+ lopcode.SETARG_A(previous, from);
+ lopcode.SETARG_B(previous, l - from);
+ return;
+ }
+ } /* else go through */
+ }
+ luaK_codeABC(fs, OpCodesI.OP_LOADNIL, from, n - 1, 0); /* else no optimization */
+};
+
+const getinstruction = function(fs, e) {
+ return fs.f.code[e.u.info];
+};
+
+/*
+** Gets the destination address of a jump instruction. Used to traverse
+** a list of jumps.
+*/
+const getjump = function(fs, pc) {
+ let offset = fs.f.code[pc].sBx;
+ if (offset === NO_JUMP) /* point to itself represents end of list */
+ return NO_JUMP; /* end of list */
+ else
+ return pc + 1 + offset; /* turn offset into absolute position */
+};
+
+/*
+** Fix jump instruction at position 'pc' to jump to 'dest'.
+** (Jump addresses are relative in Lua)
+*/
+const fixjump = function(fs, pc, dest) {
+ let jmp = fs.f.code[pc];
+ let offset = dest - (pc + 1);
+ assert(dest !== NO_JUMP);
+ if (Math.abs(offset) > lopcode.MAXARG_sBx)
+ llex.luaX_syntaxerror(fs.ls, "control structure too long");
+ lopcode.SETARG_sBx(jmp, offset);
+};
+
+/*
+** Concatenate jump-list 'l2' into jump-list 'l1'
+*/
+const luaK_concat = function(fs, l1, l2) {
+ if (l2 === NO_JUMP) return; /* nothing to concatenate? */
+ else if (l1 === NO_JUMP) /* no original list? */
+ l1 = l2;
+ else {
+ let list = l1;
+ let next = getjump(fs, list);
+ while (next !== NO_JUMP) { /* find last element */
+ list = next;
+ next = getjump(fs, list);
+ }
+ fixjump(fs, list, l2);
+ }
+
+ return l1;
+};
+
+/*
+** Create a jump instruction and return its position, so its destination
+** can be fixed later (with 'fixjump'). If there are jumps to
+** this position (kept in 'jpc'), link them all together so that
+** 'patchlistaux' will fix all them directly to the final destination.
+*/
+const luaK_jump = function (fs) {
+ let jpc = fs.jpc; /* save list of jumps to here */
+ fs.jpc = NO_JUMP; /* no more jumps to here */
+ let j = luaK_codeAsBx(fs, OpCodesI.OP_JMP, 0, NO_JUMP);
+ j = luaK_concat(fs, j, jpc); /* keep them on hold */
+ return j;
+};
+
+const luaK_jumpto = function(fs, t) {
+ return luaK_patchlist(fs, luaK_jump(fs), t);
+};
+
+/*
+** Code a 'return' instruction
+*/
+const luaK_ret = function(fs, first, nret) {
+ luaK_codeABC(fs, OpCodesI.OP_RETURN, first, nret + 1, 0);
+};
+
+/*
+** Code a "conditional jump", that is, a test or comparison opcode
+** followed by a jump. Return jump position.
+*/
+const condjump = function(fs, op, A, B, C) {
+ luaK_codeABC(fs, op, A, B, C);
+ return luaK_jump(fs);
+};
+
+/*
+** returns current 'pc' and marks it as a jump target (to avoid wrong
+** optimizations with consecutive instructions not in the same basic block).
+*/
+const luaK_getlabel = function(fs) {
+ fs.lasttarget = fs.pc;
+ return fs.pc;
+};
+
+/*
+** Returns the position of the instruction "controlling" a given
+** jump (that is, its condition), or the jump itself if it is
+** unconditional.
+*/
+const getjumpcontrol = function(fs, pc) {
+ if (pc >= 1 && lopcode.testTMode(fs.f.code[pc - 1].opcode))
+ return fs.f.code[pc - 1];
+ else
+ return fs.f.code[pc];
+};
+
+/*
+** Patch destination register for a TESTSET instruction.
+** If instruction in position 'node' is not a TESTSET, return 0 ("fails").
+** Otherwise, if 'reg' is not 'NO_REG', set it as the destination
+** register. Otherwise, change instruction to a simple 'TEST' (produces
+** no register value)
+*/
+const patchtestreg = function(fs, node, reg) {
+ let i = getjumpcontrol(fs, node);
+ if (i.opcode !== OpCodesI.OP_TESTSET)
+ return false; /* cannot patch other instructions */
+ if (reg !== lopcode.NO_REG && reg !== i.B)
+ lopcode.SETARG_A(i, reg);
+ else {
+ /* no register to put value or register already has the value;
+ change instruction to simple test */
+ i = lopcode.CREATE_ABC(OpCodesI.OP_TEST, i.B, 0, i.C);
+ }
+ return true;
+};
+
+/*
+** Traverse a list of tests ensuring no one produces a value
+*/
+const removevalues = function(fs, list) {
+ for (; list !== NO_JUMP; list = getjump(fs, list))
+ patchtestreg(fs, list, lopcode.NO_REG);
+};
+
+/*
+** Traverse a list of tests, patching their destination address and
+** registers: tests producing values jump to 'vtarget' (and put their
+** values in 'reg'), other tests jump to 'dtarget'.
+*/
+const patchlistaux = function(fs, list, vtarget, reg, dtarget) {
+ while (list !== NO_JUMP) {
+ let next = getjump(fs, list);
+ if (patchtestreg(fs, list, reg))
+ fixjump(fs, list, vtarget);
+ else
+ fixjump(fs, list, dtarget); /* jump to default target */
+ list = next;
+ }
+};
+
+/*
+** Ensure all pending jumps to current position are fixed (jumping
+** to current position with no values) and reset list of pending
+** jumps
+*/
+const dischargejpc = function(fs) {
+ patchlistaux(fs, fs.jpc, fs.pc, lopcode.NO_REG, fs.pc);
+ fs.jpc = NO_JUMP;
+};
+
+/*
+** Add elements in 'list' to list of pending jumps to "here"
+** (current position)
+*/
+const luaK_patchtohere = function(fs, list) {
+ luaK_getlabel(fs); /* mark "here" as a jump target */
+ fs.jpc = luaK_concat(fs, fs.jpc, list);
+};
+
+/*
+** Path all jumps in 'list' to jump to 'target'.
+** (The assert means that we cannot fix a jump to a forward address
+** because we only know addresses once code is generated.)
+*/
+const luaK_patchlist = function(fs, list, target) {
+ if (target === fs.pc) /* 'target' is current position? */
+ luaK_patchtohere(fs, list); /* add list to pending jumps */
+ else {
+ assert(target < fs.pc);
+ patchlistaux(fs, list, target, lopcode.NO_REG, target);
+ }
+};
+
+/*
+** Path all jumps in 'list' to close upvalues up to given 'level'
+** (The assertion checks that jumps either were closing nothing
+** or were closing higher levels, from inner blocks.)
+*/
+const luaK_patchclose = function(fs, list, level) {
+ level++; /* argument is +1 to reserve 0 as non-op */
+ for (; list !== NO_JUMP; list = getjump(fs, list)) {
+ let ins = fs.f.code[list];
+ assert(ins.opcode === OpCodesI.OP_JMP && (ins.A === 0 || ins.A >= level));
+ lopcode.SETARG_A(ins, level);
+ }
+};
+
+/*
+** Emit instruction 'i', checking for array sizes and saving also its
+** line information. Return 'i' position.
+*/
+const luaK_code = function(fs, i) {
+ let f = fs.f;
+ dischargejpc(fs); /* 'pc' will change */
+ /* put new instruction in code array */
+ f.code[fs.pc] = i;
+ f.lineinfo[fs.pc] = fs.ls.lastline;
+ return fs.pc++;
+};
+
+/*
+** Format and emit an 'iABC' instruction. (Assertions check consistency
+** of parameters versus opcode.)
+*/
+const luaK_codeABC = function(fs, o, a, b, c) {
+ assert(lopcode.getOpMode(o) === lopcode.iABC);
+ assert(lopcode.getBMode(o) !== lopcode.OpArgN || b === 0);
+ assert(lopcode.getCMode(o) !== lopcode.OpArgN || c === 0);
+ assert(a <= lopcode.MAXARG_A && b <= lopcode.MAXARG_B && c <= lopcode.MAXARG_C);
+ return luaK_code(fs, lopcode.CREATE_ABC(o, a, b, c));
+};
+
+/*
+** Format and emit an 'iABx' instruction.
+*/
+const luaK_codeABx = function(fs, o, a, bc) {
+ assert(lopcode.getOpMode(o) === lopcode.iABx || lopcode.getOpMode(o) === lopcode.iAsBx);
+ assert(lopcode.getCMode(o) === lopcode.OpArgN);
+ assert(a <= lopcode.MAXARG_A && bc <= lopcode.MAXARG_Bx);
+ return luaK_code(fs, lopcode.CREATE_ABx(o, a, bc));
+};
+
+const luaK_codeAsBx = function(fs,o,A,sBx) {
+ return luaK_codeABx(fs, o, A, (sBx) + lopcode.MAXARG_sBx);
+};
+
+/*
+** Emit an "extra argument" instruction (format 'iAx')
+*/
+const codeextraarg = function(fs, a) {
+ assert(a <= lopcode.MAXARG_Ax);
+ return luaK_code(fs, lopcode.CREATE_Ax(OpCodesI.OP_EXTRAARG, a));
+};
+
+/*
+** Emit a "load constant" instruction, using either 'OP_LOADK'
+** (if constant index 'k' fits in 18 bits) or an 'OP_LOADKX'
+** instruction with "extra argument".
+*/
+const luaK_codek = function(fs, reg, k) {
+ if (k <= lopcode.MAXARG_Bx)
+ return luaK_codeABx(fs, OpCodesI.OP_LOADK, reg, k);
+ else {
+ let p = luaK_codeABx(fs, OpCodesI.OP_LOADKX, reg, 0);
+ codeextraarg(fs, k);
+ return p;
+ }
+};
+
+/*
+** Check register-stack level, keeping track of its maximum size
+** in field 'maxstacksize'
+*/
+const luaK_checkstack = function(fs, n) {
+ let newstack = fs.freereg + n;
+ if (newstack > fs.f.maxstacksize) {
+ if (newstack >= MAXREGS)
+ llex.luaX_syntaxerror(fs.ls, "function or expression needs to many registers");
+ fs.f.maxstacksize = newstack;
+ }
+};
+
+/*
+** Reserve 'n' registers in register stack
+*/
+const luaK_reserveregs = function(fs, n) {
+ luaK_checkstack(fs, n);
+ fs.freereg += n;
+};
+
+/*
+** Free register 'reg', if it is neither a constant index nor
+** a local variable.
+*/
+const freereg = function(fs, reg) {
+ if (!lopcode.ISK(reg) && reg >= fs.nactvar) {
+ fs.freereg--;
+ assert(reg === fs.freereg);
+ }
+};
+
+/*
+** Free register used by expression 'e' (if any)
+*/
+const freeexp = function(fs, e) {
+ if (e.k === lparser.expkind.VNONRELOC)
+ freereg(fs, e.u.info);
+};
+
+/*
+** Free registers used by expressions 'e1' and 'e2' (if any) in proper
+** order.
+*/
+const freeexps = function(fs, e1, e2) {
+ let r1 = (e1.k === lparser.expkind.VNONRELOC) ? e1.u.info : -1;
+ let r2 = (e2.k === lparser.expkind.VNONRELOC) ? e2.u.info : -1;
+ if (r1 > r2) {
+ freereg(fs, r1);
+ freereg(fs, r2);
+ }
+ else {
+ freereg(fs, r2);
+ freereg(fs, r1);
+ }
+};
+
+
+/*
+** Add constant 'v' to prototype's list of constants (field 'k').
+** Use scanner's table to cache position of constants in constant list
+** and try to reuse constants. Because some values should not be used
+** as keys (nil cannot be a key, integer keys can collapse with float
+** keys), the caller must provide a useful 'key' for indexing the cache.
+*/
+const addk = function(fs, key, v) {
+ let f = fs.f;
+ let idx = fs.ls.h.__index(fs.ls.h, key); /* index scanner table */
+ if (idx && !idx.ttisnil()) { /* is there an index there? */
+ /* correct value? (warning: must distinguish floats from integers!) */
+ if (f.k[idx].ttype() === v.ttype() && f.k[idx].value === v.value)
+ return idx; /* reuse index */
+ }
+ /* constant not found; create a new entry */
+ let k = fs.nk;
+ fs.ls.h.__newindex(fs.ls.h, key, k);
+ f.k[k] = v;
+ fs.nk++;
+ return k;
+};
+
+/*
+** Add a string to list of constants and return its index.
+*/
+const luaK_stringK = function(fs, s) {
+ return addk(fs, s, s); /* use string itself as key */
+};
+
+
+/*
+** Add an integer to list of constants and return its index.
+** Integers use userdata as keys to avoid collision with floats with
+** same value; conversion to 'void*' is used only for hashing, so there
+** are no "precision" problems.
+*/
+const luaK_intK = function(fs, n) {
+ let k = new TValue(CT.LUA_TLNGSTR, `${n.value}`);
+ let o = new TValue(CT.LUA_TNUMINT, n.value);
+ return addk(fs, k, o);
+};
+
+/*
+** Add a float to list of constants and return its index.
+*/
+const luaK_numberK = function(fs, r) {
+ return addk(fs, r, r); /* use number itself as key */
+};
+
+
+/*
+** Add a boolean to list of constants and return its index.
+*/
+const boolK = function(fs, b) {
+ return addk(fs, b, b); /* use boolean itself as key */
+};
+
+
+/*
+** Add nil to list of constants and return its index.
+*/
+const nilK = function(fs) {
+ return addk(fs, new TValue(CT.LUA_TLNGSTR, `null`), new TValue(CT.LUA_TNIL, null));
+};
+
+/*
+** Fix an expression to return the number of results 'nresults'.
+** Either 'e' is a multi-ret expression (function call or vararg)
+** or 'nresults' is LUA_MULTRET (as any expression can satisfy that).
+*/
+const luaK_setreturns = function(fs, e, nresults) {
+ let ek = lparser.expkind;
+ if (e.k === ek.VCALL) { /* expression is an open function call? */
+ lopcode.SETARG_C(getinstruction(fs, e), nresults + 1);
+ }
+ else if (e.k === ek.VVARARG) {
+ let pc = getinstruction(fs, e);
+ lopcode.SETARG_B(pc, nresults + 1);
+ lopcode.SETARG_A(pc, fs.freereg);
+ luaK_reserveregs(fs, 1);
+ }
+ else assert(nresults === lua.LUA_MULTRET);
+};
+
+const luaK_setmultret = function(fs, e) {
+ luaK_setreturns(fs, e, lua.LUA_MULTRET);
+};
+
+/*
+** Fix an expression to return one result.
+** If expression is not a multi-ret expression (function call or
+** vararg), it already returns one result, so nothing needs to be done.
+** Function calls become VNONRELOC expressions (as its result comes
+** fixed in the base register of the call), while vararg expressions
+** become VRELOCABLE (as OP_VARARG puts its results where it wants).
+** (Calls are created returning one result, so that does not need
+** to be fixed.)
+*/
+const luaK_setoneret = function(fs, e) {
+ let ek = lparser.expkind;
+ if (e.k === ek.VCALL) { /* expression is an open function call? */
+ /* already returns 1 value */
+ assert(getinstruction(fs, e).C === 2);
+ e.k = ek.VNONRELOC; /* result has fixed position */
+ e.u.info = getinstruction(fs, e).A;
+ } else if (e.k === ek.VVARARG) {
+ lopcode.SETARG_B(getinstruction(fs, e), 2);
+ e.k = ek.VRELOCABLE; /* can relocate its simple result */
+ }
+};
+
+/*
+** Ensure that expression 'e' is not a variable.
+*/
+const luaK_dischargevars = function(fs, e) {
+ let ek = lparser.expkind;
+
+ switch (e.k) {
+ case ek.VLOCAL: { /* already in a register */
+ e.k = ek.VNONRELOC; /* becomes a non-relocatable value */
+ break;
+ }
+ case ek.VUPVAL: { /* move value to some (pending) register */
+ e.u.info = luaK_codeABC(fs, OpCodesI.OP_GETUPVAL, 0, e.u.info, 0);
+ e.k = ek.VRELOCABLE;
+ break;
+ }
+ case ek.VINDEXED: {
+ let op;
+ freereg(fs, e.u.ind.idx);
+ if (e.u.ind.vt === ek.VLOCAL) { /* is 't' in a register? */
+ freereg(fs, e.u.ind.t);
+ op = OpCodesI.OP_GETTABLE;
+ } else {
+ assert(e.u.ind.vt === ek.VUPVAL);
+ op = OpCodesI.OP_GETTABUP; /* 't' is in an upvalue */
+ }
+ e.u.info = luaK_codeABC(fs, op, 0, e.u.ind.t, e.u.ind.idx);
+ e.k = OpCodesI.VRELOCABLE;
+ break;
+ }
+ case ek.VVARARG: case ek.VCALL: {
+ luaK_setoneret(fs, e);
+ break;
+ }
+ default: break; /* there is one value available (somewhere) */
+ }
+};
+
+const code_loadbool = function(fs, A, b, jump) {
+ luaK_getlabel(fs); /* those instructions may be jump targets */
+ return luaK_codeABC(fs, OpCodesI.OP_LOADBOOL, A, b, jump);
+};
+
+/*
+** Ensures expression value is in register 'reg' (and therefore
+** 'e' will become a non-relocatable expression).
+*/
+const discharge2reg = function(fs, e, reg) {
+ let ek = lparser.expkind;
+ luaK_dischargevars(fs, e);
+ switch (e.k) {
+ case ek.VNIL: {
+ luaK_nil(fs, reg, 1);
+ break;
+ }
+ case ek.VFALSE: case ek.VTRUE: {
+ luaK_codeABC(fs, OpCodesI.OP_LOADBOOL, reg, e.k === ek.VTRUE, 0);
+ break;
+ }
+ case ek.VK: {
+ luaK_codek(fs, reg, e.u.info);
+ break;
+ }
+ case ek.VKFLT: {
+ luaK_codek(fs, reg, luaK_numberK(fs, e.u.nval));
+ break;
+ }
+ case ek.VKINT: {
+ luaK_codek(fs, reg, luaK_intK(fs, e.u.ival));
+ break;
+ }
+ case ek.VRELOCABLE: {
+ let pc = getinstruction(fs, e);
+ lopcode.SETARG_A(pc, reg); /* instruction will put result in 'reg' */
+ break;
+ }
+ case ek.VNONRELOC: {
+ if (reg !== e.u.info)
+ luaK_codeABC(fs, OpCodesI.OP_MOVE, reg, e.u.info, 0);
+ break;
+ }
+ default: {
+ assert(e.k === ek.VJMP);
+ return; /* nothing to do... */
+ }
+ }
+ e.u.info = reg;
+ e.k = ek.VNONRELOC;
+};
+
+/*
+** Ensures expression value is in any register.
+*/
+const discharge2anyreg = function(fs, e) {
+ if (e.k !== lparser.expkind.VNONRELOC) { /* no fixed register yet? */
+ luaK_reserveregs(fs, 1); /* get a register */
+ discharge2reg(fs, e, fs.freereg-1); /* put value there */
+ }
+};
+
+/*
+** check whether list has any jump that do not produce a value
+** or produce an inverted value
+*/
+const need_value = function(fs, list) {
+ for (; list !== NO_JUMP; list = getjump(fs, list)) {
+ let i = getjumpcontrol(fs, list);
+ if (i.opcode !== OpCodesI.OP_TESTSET) return true;
+ }
+ return false; /* not found */
+};
+
+/*
+** Ensures final expression result (including results from its jump
+** lists) is in register 'reg'.
+** If expression has jumps, need to patch these jumps either to
+** its final position or to "load" instructions (for those tests
+** that do not produce values).
+*/
+const exp2reg = function(fs, e, reg) {
+ let ek = lparser.expkind;
+ discharge2reg(fs, e, reg);
+ if (e.k === ek.VJMP) /* expression itself is a test? */
+ e.t = luaK_concat(fs, e.t, e.u.info); /* put this jump in 't' list */
+ if (hasjumps(e)) {
+ let final; /* position after whole expression */
+ let p_f = NO_JUMP; /* position of an eventual LOAD false */
+ let p_t = NO_JUMP; /* position of an eventual LOAD true */
+ if (need_value(fs, e.t) || need_value(fs, e.f)) {
+ let fj = (e.k === ek.VJMP) ? NO_JUMP : luaK_jump(fs);
+ p_f = code_loadbool(fs, reg, 0, 1);
+ p_t = code_loadbool(fs, reg, 1, 0);
+ luaK_patchtohere(fs, fj);
+ }
+ final = luaK_getlabel(fs);
+ patchlistaux(fs, e.f, final, reg, p_f);
+ patchlistaux(fs, e.t, final, reg, p_t);
+ }
+ e.f = e.t = NO_JUMP;
+ e.u.info = reg;
+ e.k = ek.VNONRELOC;
+};
+
+/*
+** Ensures final expression result (including results from its jump
+** lists) is in next available register.
+*/
+const luaK_exp2nextreg = function(fs, e) {
+ luaK_dischargevars(fs, e);
+ freeexp(fs, e);
+ luaK_reserveregs(fs, 1);
+ exp2reg(fs, e, fs.freereg - 1);
+};
+
+
+/*
+** Ensures final expression result (including results from its jump
+** lists) is in some (any) register and return that register.
+*/
+const luaK_exp2anyreg = function(fs, e) {
+ luaK_dischargevars(fs, e);
+ if (e.k === lparser.expkind.VNONRELOC) { /* expression already has a register? */
+ if (!hasjumps(e)) /* no jumps? */
+ return e.u.info; /* result is already in a register */
+ if (e.u.info >= fs.nactvar) { /* reg. is not a local? */
+ exp2reg(fs, e, e.u.info); /* put final result in it */
+ return e.u.info;
+ }
+ }
+ luaK_exp2nextreg(fs, e); /* otherwise, use next available register */
+ return e.u.info;
+};
+
+/*
+** Ensures final expression result is either in a register or in an
+** upvalue.
+*/
+const luaK_exp2anyregup = function(fs, e) {
+ if (e.k !== lparser.expkind.VUPVAL || hasjumps(e))
+ luaK_exp2anyreg(fs, e);
+};
+
+/*
+** Ensures final expression result is either in a register or it is
+** a constant.
+*/
+const luaK_exp2val = function(fs, e) {
+ if (hasjumps(e))
+ luaK_exp2anyreg(fs, e);
+ else
+ luaK_dischargevars(fs, e);
+};
+
+/*
+** Ensures final expression result is in a valid R/K index
+** (that is, it is either in a register or in 'k' with an index
+** in the range of R/K indices).
+** Returns R/K index.
+*/
+const luaK_exp2RK = function(fs, e) {
+ let ek = lparser.expkind;
+ let vk = false;
+ luaK_exp2val(fs, e);
+ switch (e.k) { /* move constants to 'k' */
+ case ek.VTRUE: e.u.info = boolK(fs, 1); vk = true; break;
+ case ek.VFALSE: e.u.info = boolK(fs, 0); vk = true; break;
+ case ek.VNIL: e.u.info = nilK(fs); vk = true; break;
+ case ek.VKINT: e.u.info = luaK_intK(fs, e.u.ival); vk = true; break;
+ case ek.VKFLT: e.u.info = luaK_numberK(fs, e.u.nval); vk = true; break;
+ case ek.VK: vk = true; break;
+ default: break;
+ }
+
+ if (vk) {
+ e.k = ek.VK;
+ if (e.u.info <= lopcode.MAXINDEXRK) /* constant fits in 'argC'? */
+ return lopcode.RKASK(e.u.info);
+ }
+
+ /* not a constant in the right range: put it in a register */
+ return luaK_exp2anyreg(fs, e);
+};
+
+/*
+** Generate code to store result of expression 'ex' into variable 'var'.
+*/
+const luaK_storevar = function(fs, vr, ex) {
+ let ek = lparser.expkind;
+ switch (vr.k) {
+ case ek.VLOCAL: {
+ freeexp(fs, ex);
+ exp2reg(fs, ex, vr.u.info); /* compute 'ex' into proper place */
+ return;
+ }
+ case ek.VUPVAL: {
+ let e = luaK_exp2anyreg(fs, ex);
+ luaK_codeABC(fs, OpCodesI.OP_SETUPVAL, e, vr.u.info, 0);
+ break;
+ }
+ case ek.VINDEXED: {
+ let op = (vr.u.ind.vt === ek.VLOCAL) ? OpCodesI.OP_SETTABLE : OpCodesI.OP_SETTABUP;
+ let e = luaK_exp2RK(fs, ex);
+ luaK_codeABC(fs, op, vr.u.ind.t, vr.u.ind.idx, e);
+ break;
+ }
+ }
+ freeexp(fs, ex);
+};
+
+
+/*
+** Emit SELF instruction (convert expression 'e' into 'e:key(e,').
+*/
+const luaK_self = function(fs, e, key) {
+ luaK_exp2anyreg(fs, e);
+ let ereg = e.u.info; /* register where 'e' was placed */
+ freeexp(fs, e);
+ e.u.info = fs.freereg; /* base register for op_self */
+ e.k = lparser.expkind.VNONRELOC; /* self expression has a fixed register */
+ luaK_reserveregs(fs, 2); /* function and 'self' produced by op_self */
+ luaK_codeABC(fs, OpCodesI.OP_SELF, e.u.info, ereg, luaK_exp2RK(fs, key));
+ freeexp(fs, key);
+};
+
+/*
+** Negate condition 'e' (where 'e' is a comparison).
+*/
+const negatecondition = function(fs, e) {
+ let pc = getjumpcontrol(fs, e.u.info);
+ assert(lopcode.testTMode(pc.opcode) && pc.opcode !== OpCodesI.OP_TESTSET && pc.opcode !== OpCodesI.OP_TEST);
+ lopcode.SETARG_A(pc, !(pc.A));
+};
+
+/*
+** Emit instruction to jump if 'e' is 'cond' (that is, if 'cond'
+** is true, code will jump if 'e' is true.) Return jump position.
+** Optimize when 'e' is 'not' something, inverting the condition
+** and removing the 'not'.
+*/
+const jumponcond = function(fs, e, cond) {
+ if (e.k === lparser.expkind.VRELOCABLE) {
+ let ie = getinstruction(fs, e);
+ if (ie.opcode === OpCodesI.OP_NOT) {
+ fs.pc--; /* remove previous OP_NOT */
+ return condjump(fs, OpCodesI.OP_TEST, ie.B, 0, !cond);
+ }
+ /* else go through */
+ }
+ discharge2anyreg(fs, e);
+ freeexp(fs, e);
+ return condjump(fs, OpCodesI.OP_TESTSET, lopcode.NO_REG, e.u.info, cond);
+};
+
+/*
+** Emit code to go through if 'e' is true, jump otherwise.
+*/
+const luaK_goiftrue = function(fs, e) {
+ let ek = lparser.expkind;
+ let pc; /* pc of new jump */
+ luaK_dischargevars(fs, e);
+ switch (e.k) {
+ case ek.VJMP: { /* condition? */
+ negatecondition(fs, e); /* jump when it is false */
+ pc = e.u.info; /* save jump position */
+ break;
+ }
+ case ek.VK: case ek.VKFLT: case ek.VKINT: case ek.VTRUE: {
+ pc = NO_JUMP; /* always true; do nothing */
+ break;
+ }
+ default: {
+ pc = jumponcond(fs, e, 0); /* jump when false */
+ break;
+ }
+ }
+ e.f = luaK_concat(fs, e.f, pc); /* insert new jump in false list */
+ luaK_patchtohere(fs, e.t); /* true list jumps to here (to go through) */
+ e.t = NO_JUMP;
+};
+
+/*
+** Emit code to go through if 'e' is false, jump otherwise.
+*/
+const luaK_goiffalse = function(fs, e) {
+ let ek = lparser.expkind;
+ let pc; /* pc of new jump */
+ luaK_dischargevars(fs, e);
+ switch (e.k) {
+ case ek.VJMP: {
+ pc = e.u.info; /* already jump if true */
+ break;
+ }
+ case ek.VNIL: case ek.VFALSE: {
+ pc = NO_JUMP; /* always false; do nothing */
+ break;
+ }
+ default: {
+ pc = jumponcond(fs, e, 1); /* jump if true */
+ break;
+ }
+ }
+ e.t = luaK_concat(fs, e.t, pc); /* insert new jump in 't' list */
+ luaK_patchtohere(fs, e.f); /* false list jumps to here (to go through) */
+ e.f = NO_JUMP;
+};
+
+/*
+** Code 'not e', doing constant folding.
+*/
+const codenot = function(fs, e) {
+ let ek = lparser.expkind;
+ luaK_dischargevars(fs, e);
+ switch (e.k) {
+ case ek.VNIL: case ek.VFALSE: {
+ e.k = ek.VTRUE; /* true === not nil === not false */
+ break;
+ }
+ case ek.VK: case ek.VKFLT: case ek.VKINT: case ek.VTRUE: {
+ e.k = ek.VFALSE; /* false === not "x" === not 0.5 === not 1 === not true */
+ break;
+ }
+ case ek.VJMP: {
+ negatecondition(fs, e);
+ break;
+ }
+ case ek.VRELOCABLE:
+ case ek.VNONRELOC: {
+ discharge2anyreg(fs, e);
+ freeexp(fs, e);
+ e.u.info = luaK_codeABC(fs, OpCodesI.OP_NOT, 0, e.u.info, 0);
+ e.k = ek.VRELOCABLE;
+ break;
+ }
+ }
+ /* interchange true and false lists */
+ { let temp = e.f; e.f = e.t; e.t = temp; }
+ removevalues(fs, e.f); /* values are useless when negated */
+ removevalues(fs, e.t);
+};
+
+/*
+** Create expression 't[k]'. 't' must have its final result already in a
+** register or upvalue.
+*/
+const luaK_indexed = function(fs, t, k) {
+ let ek = lparser.expkind;
+ assert(!hasjumps(t) && (lparser.vkisinreg(t.k) || t.k === ek.VUPVAL));
+ t.u.ind.t = t.u.info; /* register or upvalue index */
+ t.u.ind.idx = luaK_exp2RK(fs, k); /* R/K index for key */
+ t.u.ind.vt = (t.k === ek.VUPVAL) ? ek.VUPVAL : ek.VLOCAL;
+ t.k = ek.VINDEXED;
+};
+
+/*
+** Return false if folding can raise an error.
+** Bitwise operations need operands convertible to integers; division
+** operations cannot have 0 as divisor.
+*/
+const validop = function(op, v1, v2) {
+ switch (op) {
+ case lua.LUA_OPBAND: case lua.LUA_OPBOR: case lua.LUA_OPBXOR:
+ case lua.LUA_OPSHL: case lua.LUA_OPSHR: case lua.LUA_OPBNOT: { /* conversion errors */
+ return (lvm.tointeger(v1) && lvm.tointeger(v2));
+ }
+ case lua.LUA_OPDIV: case lua.LUA_OPIDIV: case lua.LUA_OPMOD: /* division by 0 */
+ return (v2.value !== 0);
+ default: return 1; /* everything else is valid */
+ }
+};
+
+/*
+** Try to "constant-fold" an operation; return 1 iff successful.
+** (In this case, 'e1' has the final result.)
+*/
+const constfolding = function(fs, op, e1, e2) {
+ let ek = lparser.expkind;
+ let v1 = new TValue();
+ let v2 = new TValue();
+ let res = new TValue();
+ if (!tonumeral(e1, v1) || !tonumeral(e2, v2) || !validop(op, v1, v2))
+ return 0; /* non-numeric operands or not safe to fold */
+ luaO_arith(fs.ls.L, op, v1, v2, res); /* does operation */
+ if (res.ttisinteger()) {
+ e1.k = ek.VKINT;
+ e1.u.ival = res.value;
+ }
+ else { /* folds neither NaN nor 0.0 (to avoid problems with -0.0) */
+ let n = res.value;
+ if (isNaN(n) || n === 0)
+ return false;
+ e1.k = ek.VKFLT;
+ e1.u.nval = n;
+ }
+ return true;
+};
+
+/*
+** Emit code for unary expressions that "produce values"
+** (everything but 'not').
+** Expression to produce final result will be encoded in 'e'.
+*/
+const codeunexpval = function(fs, op, e, line) {
+ let r = luaK_exp2anyreg(fs, e); /* opcodes operate only on registers */
+ freeexp(fs, e);
+ e.u.info = luaK_codeABC(fs, op, 0, r, 0); /* generate opcode */
+ e.k = lparser.expkind.VRELOCABLE; /* all those operations are relocatable */
+ luaK_fixline(fs, line);
+};
+
+/*
+** Emit code for binary expressions that "produce values"
+** (everything but logical operators 'and'/'or' and comparison
+** operators).
+** Expression to produce final result will be encoded in 'e1'.
+** Because 'luaK_exp2RK' can free registers, its calls must be
+** in "stack order" (that is, first on 'e2', which may have more
+** recent registers to be released).
+*/
+const codebinexpval = function(fs, op, e1, e2, line) {
+ let rk2 = luaK_exp2RK(fs, e2); /* both operands are "RK" */
+ let rk1 = luaK_exp2RK(fs, e1);
+ freeexps(fs, e1, e2);
+ e1.u.info = luaK_codeABC(fs, op, 0, rk1, rk2); /* generate opcode */
+ e1.k = lparser.expkind.VRELOCABLE; /* all those operations are relocatable */
+ luaK_fixline(fs, line);
+};
+
+
+/*
+** Emit code for comparisons.
+** 'e1' was already put in R/K form by 'luaK_infix'.
+*/
+const codecomp = function(fs, opr, e1, e2) {
+ let ek = lparser.expkind;
+ let rk1 = (e1.k === ek.VK) ? lopcode.RKASK(e1.u.info) : llimit.check_exp(e1.k === ek.VNONRELOC, e1.u.info);
+ let rk2 = luaK_exp2RK(fs, e2);
+ freeexps(fs, e1, e2);
+ switch (opr) {
+ case BinOpr.OPR_NE: { /* '(a ~= b)' ==> 'not (a === b)' */
+ e1.u.info = condjump(fs, OpCodesI.OP_EQ, 0, rk1, rk2);
+ break;
+ }
+ case BinOpr.OPR_GT: case BinOpr.OPR_GE: {
+ /* '(a > b)' ==> '(b < a)'; '(a >= b)' ==> '(b <= a)' */
+ let op = (opr - BinOpr.OPR_NE) + OpCodesI.OP_EQ;
+ e1.u.info = condjump(fs, op, 1, rk2, rk1); /* invert operands */
+ break;
+ }
+ default: { /* '==', '<', '<=' use their own opcodes */
+ let op = (opr - BinOpr.OPR_EQ) + OpCodesI.OP_EQ;
+ e1.u.info = condjump(fs, op, 1, rk1, rk2);
+ break;
+ }
+ }
+ e1.k = ek.VJMP;
+};
+
+/*
+** Apply prefix operation 'op' to expression 'e'.
+*/
+const luaK_prefix = function(fs, op, e, line) {
+ let ef = new lparser.expdesc();
+ ef.k = lparser.expkind.VKINT;
+ ef.u.ival = e.u.nval = e.u.info = 0;
+ e.t = NO_JUMP;
+ e.f = NO_JUMP;
+ switch (op) {
+ case UnOpr.OPR_MINUS: case UnOpr.OPR_BNOT: /* use 'ef' as fake 2nd operand */
+ if (constfolding(fs, op + lua.LUA_OPUNM, e, ef))
+ break;
+ /* FALLTHROUGH */
+ case UnOpr.OPR_LEN:
+ codeunexpval(fs, op + UnOpr.OP_UNM, e, line);
+ break;
+ case UnOpr.OPR_NOT: codenot(fs, e); break;
+ }
+};
+
+/*
+** Process 1st operand 'v' of binary operation 'op' before reading
+** 2nd operand.
+*/
+const luaK_infix = function(fs, op, v) {
+ switch (op) {
+ case BinOpr.OPR_AND: {
+ luaK_goiftrue(fs, v); /* go ahead only if 'v' is true */
+ break;
+ }
+ case BinOpr.OPR_OR: {
+ luaK_goiffalse(fs, v); /* go ahead only if 'v' is false */
+ break;
+ }
+ case BinOpr.OPR_CONCAT: {
+ luaK_exp2nextreg(fs, v); /* operand must be on the 'stack' */
+ break;
+ }
+ case BinOpr.OPR_ADD: case BinOpr.OPR_SUB:
+ case BinOpr.OPR_MUL: case BinOpr.OPR_DIV: case BinOpr.OPR_IDIV:
+ case BinOpr.OPR_MOD: case BinOpr.OPR_POW:
+ case BinOpr.OPR_BAND: case BinOpr.OPR_BOR: case BinOpr.OPR_BXOR:
+ case BinOpr.OPR_SHL: case BinOpr.OPR_SHR: {
+ if (!tonumeral(v, null))
+ luaK_exp2RK(fs, v);
+ /* else keep numeral, which may be folded with 2nd operand */
+ break;
+ }
+ default: {
+ luaK_exp2RK(fs, v);
+ break;
+ }
+ }
+};
+
+/*
+** Finalize code for binary operation, after reading 2nd operand.
+** For '(a .. b .. c)' (which is '(a .. (b .. c))', because
+** concatenation is right associative), merge second CONCAT into first
+** one.
+*/
+const luaK_posfix = function(fs, op, e1, e2, line) {
+ let ek = lparser.expkind;
+ switch (op) {
+ case BinOpr.OPR_AND: {
+ assert(e1.t === NO_JUMP); /* list closed by 'luK_infix' */
+ luaK_dischargevars(fs, e2);
+ e2.f = luaK_concat(fs, e2.f, e1.f);
+ // WARN: *e1 = *e2;
+ break;
+ }
+ case BinOpr.OPR_OR: {
+ assert(e1.f === NO_JUMP); /* list closed by 'luK_infix' */
+ luaK_dischargevars(fs, e2);
+ e2.t = luaK_concat(fs, e2.t, e1.t);
+ // WARN: *e1 = *e2;
+ break;
+ }
+ case BinOpr.OPR_CONCAT: {
+ let ins = getinstruction(fs, e2);
+ luaK_exp2val(fs, e2);
+ if (e2.k === ek.VRELOCABLE && ins.opcode === OpCodesI.OP_CONCAT) {
+ assert(e1.u.info === ins.B - 1);
+ freeexp(fs, e1);
+ lopcode.SETARG_B(ins, e1.u.info);
+ e1.k = ek.VRELOCABLE; e1.u.info = e2.u.info;
+ }
+ else {
+ luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */
+ codebinexpval(fs, OpCodesI.OP_CONCAT, e1, e2, line);
+ }
+ break;
+ }
+ case BinOpr.OPR_ADD: case BinOpr.OPR_SUB: case BinOpr.OPR_MUL: case BinOpr.OPR_DIV:
+ case BinOpr.OPR_IDIV: case BinOpr.OPR_MOD: case BinOpr.OPR_POW:
+ case BinOpr.OPR_BAND: case BinOpr.OPR_BOR: case BinOpr.OPR_BXOR:
+ case BinOpr.OPR_SHL: case BinOpr.OPR_SHR: {
+ if (!constfolding(fs, op + lua.LUA_OPADD, e1, e2))
+ codebinexpval(fs, op + OpCodesI.OP_ADD, e1, e2, line);
+ break;
+ }
+ case BinOpr.OPR_EQ: case BinOpr.OPR_LT: case BinOpr.OPR_LE:
+ case BinOpr.OPR_NE: case BinOpr.OPR_GT: case BinOpr.OPR_GE: {
+ codecomp(fs, op, e1, e2);
+ break;
+ }
+ }
+};
+
+/*
+** Change line information associated with current position.
+*/
+const luaK_fixline = function(fs, line) {
+ fs.f.lineinfo[fs.pc - 1] = line;
+};
+
+/*
+** Emit a SETLIST instruction.
+** 'base' is register that keeps table;
+** 'nelems' is #table plus those to be stored now;
+** 'tostore' is number of values (in registers 'base + 1',...) to add to
+** table (or LUA_MULTRET to add up to stack top).
+*/
+const luaK_setlist = function(fs, base, nelems, tostore) {
+ let c = (nelems - 1)/lopcode.LFIELDS_PER_FLUSH + 1;
+ let b = (tostore === lua.LUA_MULTRET) ? 0 : tostore;
+ assert(tostore !== 0 && tostore <= lopcode.LFIELDS_PER_FLUSH);
+ if (c <= lopcode.MAXARG_C)
+ luaK_codeABC(fs, OpCodesI.OP_SETLIST, base, b, c);
+ else if (c <= lopcode.MAXARG_Ax) {
+ luaK_codeABC(fs, OpCodesI.OP_SETLIST, base, b, 0);
+ codeextraarg(fs, c);
+ }
+ else
+ llex.luaX_syntaxerror(fs.ls, "constructor too long");
+ fs.freereg = base + 1; /* free registers with list values */
+};
+
+
+module.exports.BinOpr = BinOpr;
+module.exports.NO_JUMP = NO_JUMP;
+module.exports.UnOpr = UnOpr;
+module.exports.getinstruction = getinstruction;
+module.exports.luaK_checkstack = luaK_checkstack;
+module.exports.luaK_code = luaK_code;
+module.exports.luaK_codeABC = luaK_codeABC;
+module.exports.luaK_codeABx = luaK_codeABx;
+module.exports.luaK_codeAsBx = luaK_codeAsBx;
+module.exports.luaK_codek = luaK_codek;
+module.exports.luaK_concat = luaK_concat;
+module.exports.luaK_dischargevars = luaK_dischargevars;
+module.exports.luaK_exp2RK = luaK_exp2RK;
+module.exports.luaK_exp2anyreg = luaK_exp2anyreg;
+module.exports.luaK_exp2anyregup = luaK_exp2anyregup;
+module.exports.luaK_exp2nextreg = luaK_exp2nextreg;
+module.exports.luaK_exp2val = luaK_exp2val;
+module.exports.luaK_fixline = luaK_fixline;
+module.exports.luaK_getlabel = luaK_getlabel;
+module.exports.luaK_goiffalse = luaK_goiffalse;
+module.exports.luaK_goiftrue = luaK_goiftrue;
+module.exports.luaK_indexed = luaK_indexed;
+module.exports.luaK_infix = luaK_infix;
+module.exports.luaK_intK = luaK_intK;
+module.exports.luaK_jump = luaK_jump;
+module.exports.luaK_jumpto = luaK_jumpto;
+module.exports.luaK_nil = luaK_nil;
+module.exports.luaK_numberK = luaK_numberK;
+module.exports.luaK_patchclose = luaK_patchclose;
+module.exports.luaK_patchlist = luaK_patchlist;
+module.exports.luaK_patchtohere = luaK_patchtohere;
+module.exports.luaK_posfix = luaK_posfix;
+module.exports.luaK_prefix = luaK_prefix;
+module.exports.luaK_reserveregs = luaK_reserveregs;
+module.exports.luaK_ret = luaK_ret;
+module.exports.luaK_self = luaK_self;
+module.exports.luaK_setlist = luaK_setlist;
+module.exports.luaK_setmultret = luaK_setmultret;
+module.exports.luaK_setoneret = luaK_setoneret;
+module.exports.luaK_setreturns = luaK_setreturns;
+module.exports.luaK_storevar = luaK_storevar;
+module.exports.luaK_stringK = luaK_stringK; \ No newline at end of file
diff --git a/src/lcorolib.js b/src/lcorolib.js
index 05f3b00..b2d0de8 100644
--- a/src/lcorolib.js
+++ b/src/lcorolib.js
@@ -1,4 +1,3 @@
-/* jshint esversion: 6 */
"use strict";
const assert = require('assert');
diff --git a/src/ldebug.js b/src/ldebug.js
index f6d0633..091b896 100644
--- a/src/ldebug.js
+++ b/src/ldebug.js
@@ -60,7 +60,7 @@ const upvalname = function(p, uv) {
};
const funcinfo = function(ar, cl) {
- if (cl === null || cl.type == CT.LUA_TCCL) {
+ if (cl === null || cl.type === CT.LUA_TCCL) {
ar.source = "=[JS]";
ar.linedefined = -1;
ar.lastlinedefined = -1;
@@ -77,7 +77,7 @@ const funcinfo = function(ar, cl) {
};
const collectvalidlines = function(L, f) {
- if (f === null || f.c.type == CT.LUA_TCCL) {
+ if (f === null || f.c.type === CT.LUA_TCCL) {
L.stack[L.top++] = ldo.nil;
assert(L.top <= L.ci.top, "stack overflow");
} else {
@@ -123,7 +123,7 @@ const auxgetinfo = function(L, what, ar, f, ci) {
}
case 'u': {
ar.nups = f === null ? 0 : f.c.nupvalues;
- if (f === null || f.c.type == CT.LUA_TCCL) {
+ if (f === null || f.c.type === CT.LUA_TCCL) {
ar.isvararg = true;
ar.nparams = 0;
} else {
diff --git a/src/ldo.js b/src/ldo.js
index 8d12c9f..efb971d 100644
--- a/src/ldo.js
+++ b/src/ldo.js
@@ -3,15 +3,18 @@
const assert = require('assert');
-const lua = require('./lua.js');
+const BytecodeParser = require('./lundump.js');
+const lapi = require('./lapi.js');
+const ldebug = require('./ldebug.js');
+const lfunc = require('./lfunc.js');
+const llex = require('./llex.js');
+const llimit = require('./llimit.js');
const lobject = require('./lobject.js');
+const lparser = require('./lparser.js');
const lstate = require('./lstate.js');
-const llimit = require('./llimit.js');
const ltm = require('./ltm.js');
+const lua = require('./lua.js');
const lvm = require('./lvm.js');
-const lfunc = require('./lfunc.js');
-const BytecodeParser = require('./lundump.js');
-const ldebug = require('./ldebug.js');
const CT = lua.constant_types;
const TS = lua.thread_status;
const LUA_MULTRET = lua.LUA_MULTRET;
@@ -475,7 +478,7 @@ const lua_yield = function(L, n) {
const luaD_pcall = function(L, func, u, old_top, ef) {
let old_ci = L.ci;
- // TODO: lu_byte old_allowhooks = L->allowhook;
+ let old_allowhooks = L.allowhook;
let old_nny = L.nny;
let old_errfunc = L.errfunc;
L.errfunc = ef;
@@ -486,7 +489,7 @@ const luaD_pcall = function(L, func, u, old_top, ef) {
lfunc.luaF_close(L, old_top);
seterrorobj(L, status, old_top);
L.ci = old_ci;
- // TODO: L->allowhook = old_allowhooks;
+ L.allowhook = old_allowhooks;
L.nny = old_nny;
}
@@ -504,31 +507,64 @@ const luaD_callnoyield = function(L, off, nResults) {
L.nny--;
};
-// TODO: since we only handle binary, no need for a reader or mode
-const f_parser = function(L, data) {
- let p = new BytecodeParser(data);
- let cl = p.luaU_undump(L);
+/*
+** Execute a protected parser.
+*/
+class SParser {
+ constructor() { /* data to 'f_parser' */
+ this.z = new llex.MBuffer();
+ this.buff = new llex.MBuffer(); /* dynamic structure used by the scanner */
+ this.dyd = new lparser.Dyndata(); /* dynamic structures used by the parser */
+ this.mode = null;
+ this.name = null;
+ }
+}
+
+const checkmode = function(L, mode, x) {
+ if (mode && mode !== x) {
+ lapi.lua_pushstring(L, `attempt to load a ${x} chunk (mode is '${mode}')`);
+ luaD_throw(L, TS.LUA_ERRSYNTAX);
+ }
+};
- assert(cl.nupvalues == cl.p.upvalues.length);
+const f_parser = function(L, p) {
+ let cl;
+ let c = p.z.getc(); /* read first character */
+ if (String.fromCharCode(c) === lua.LUA_SIGNATURE.charAt(0)) {
+ checkmode(L, p.mode, "binary");
+ cl = new BytecodeParser(p.z.buffer).luaU_undump(L);
+ } else {
+ checkmode(L, p.mode, "text");
+ cl = lparser.luaY_parser(L, p.z, p.buff, p.dyd, p.name, c);
+ }
+ assert(cl.nupvalues === cl.p.upvalues.length);
lfunc.luaF_initupvals(L, cl);
};
-const luaD_protectedparser = function(L, data, name) {
- L.nny++;
+const luaD_protectedparser = function(L, z, name, mode) {
+ let p = new SParser();
+ L.nny++; /* cannot yield during parsing */
- let status = luaD_pcall(L, f_parser, data, L.top, L.errfunc);
+ p.z = z;
+ p.name = name;
+ p.mode = mode;
+ p.dyd.actvar.arr = [];
+ p.dyd.actvar.size = 0;
+ p.dyd.gt.arr = [];
+ p.dyd.gt.size = 0;
+ p.dyd.label.arr = [];
+ p.dyd.label.size = 0;
+
+ let status = luaD_pcall(L, f_parser, p, L.top, L.errfunc);
L.nny--;
return status;
};
+module.exports.SParser = SParser;
module.exports.adjust_varargs = adjust_varargs;
-module.exports.lua_isyieldable = lua_isyieldable;
-module.exports.lua_resume = lua_resume;
-module.exports.lua_yield = lua_yield;
-module.exports.lua_yieldk = lua_yieldk;
module.exports.luaD_call = luaD_call;
module.exports.luaD_callnoyield = luaD_callnoyield;
module.exports.luaD_pcall = luaD_pcall;
@@ -537,6 +573,10 @@ module.exports.luaD_precall = luaD_precall;
module.exports.luaD_protectedparser = luaD_protectedparser;
module.exports.luaD_rawrunprotected = luaD_rawrunprotected;
module.exports.luaD_throw = luaD_throw;
+module.exports.lua_isyieldable = lua_isyieldable;
+module.exports.lua_resume = lua_resume;
+module.exports.lua_yield = lua_yield;
+module.exports.lua_yieldk = lua_yieldk;
module.exports.moveresults = moveresults;
module.exports.nil = nil;
module.exports.stackerror = stackerror;
diff --git a/src/lfunc.js b/src/lfunc.js
index 3b3c8ce..227606d 100644
--- a/src/lfunc.js
+++ b/src/lfunc.js
@@ -1,6 +1,8 @@
/*jshint esversion: 6 */
"use strict";
-const assert = require('assert');
+const assert = require('assert');
+
+const lobject = require('./lobject.js');
class Proto {
@@ -53,6 +55,15 @@ class UpVal {
}
+const luaF_newLclosure = function(L, n) {
+ let c = new lobject.LClosure();
+ c.p = null;
+ c.nupvalues = n;
+ while (n--) c.upvals[n] = null;
+ return c;
+};
+
+
const findupval = function(L, level) {
let pp = L.openupval;
@@ -91,12 +102,16 @@ const luaF_close = function(L, level) {
}
};
+/*
+** fill a closure with new closed upvalues
+*/
const luaF_initupvals = function(L, cl) {
for (let i = 0; i < cl.nupvalues; i++) {
let uv = new UpVal(L);
uv.refcount = 1;
uv.u.value = null;
uv.v = uv.u.value;
+ cl.upvals[i] = uv;
}
};
@@ -108,7 +123,7 @@ const luaF_getlocalname = function(f, local_number, pc) {
for (let i = 0; i < f.locvars.length && f.locvars[i].startpc <= pc; i++) {
if (pc < f.locvars[i].endpc) { /* is variable active? */
local_number--;
- if (local_number == 0)
+ if (local_number === 0)
return f.locvars[i].varname;
}
}
@@ -116,10 +131,11 @@ const luaF_getlocalname = function(f, local_number, pc) {
}
+module.exports.MAXUPVAL = 255;
module.exports.Proto = Proto;
module.exports.UpVal = UpVal;
module.exports.findupval = findupval;
module.exports.luaF_close = luaF_close;
-module.exports.MAXUPVAL = 255;
+module.exports.luaF_getlocalname = luaF_getlocalname
module.exports.luaF_initupvals = luaF_initupvals;
-module.exports.luaF_getlocalname = luaF_getlocalname \ No newline at end of file
+module.exports.luaF_newLclosure = luaF_newLclosure; \ No newline at end of file
diff --git a/src/linit.js b/src/linit.js
index 36c0121..b926683 100644
--- a/src/linit.js
+++ b/src/linit.js
@@ -1,4 +1,3 @@
-/* jshint esversion: 6 */
"use strict";
const assert = require('assert');
diff --git a/src/ljstype.js b/src/ljstype.js
new file mode 100644
index 0000000..a92c8d0
--- /dev/null
+++ b/src/ljstype.js
@@ -0,0 +1,29 @@
+"use strict";
+
+const assert = require('assert');
+
+const lisdigit = function(c) {
+ return /^\d$/.test(c.charAt(0));
+};
+
+const lisxdigit = function(c) {
+ return /^[0-9a-fA-F]$/.test(c.charAt(0));
+};
+
+const lisspace = function(c) {
+ return /^\s$/.test(c.charAt(0));
+};
+
+const lislalpha = function(c) {
+ return /^[_a-zA-z]$/.test(c.charAt(0));
+};
+
+const lislalnum = function(c) {
+ return /^[_a-zA-z0-9]$/.test(c.charAt(0));
+};
+
+module.exports.lisdigit = lisdigit;
+module.exports.lislalnum = lislalnum;
+module.exports.lislalpha = lislalpha;
+module.exports.lisspace = lisspace;
+module.exports.lisxdigit = lisxdigit; \ No newline at end of file
diff --git a/src/llex.js b/src/llex.js
new file mode 100644
index 0000000..63a1fbb
--- /dev/null
+++ b/src/llex.js
@@ -0,0 +1,609 @@
+"use strict";
+
+const DataView = require('buffer-dataview');
+const assert = require('assert');
+
+const lapi = require('./lapi.js');
+const lauxlib = require('./lauxlib.js');
+const ldebug = require('./ldebug.js');
+const ldo = require('./ldo.js');
+const ljstype = require('./ljstype');
+const lobject = require('./lobject');
+const lua = require('./lua.js');
+const TValue = lobject.TValue;
+const CT = lua.constant_types;
+const TS = lua.thread_status;
+
+const FIRST_RESERVED = 257;
+
+const RESERVED = {
+ /* terminal symbols denoted by reserved words */
+ TK_AND: FIRST_RESERVED,
+ TK_BREAK: FIRST_RESERVED + 1,
+ TK_DO: FIRST_RESERVED + 2,
+ TK_ELSE: FIRST_RESERVED + 3,
+ TK_ELSEIF: FIRST_RESERVED + 4,
+ TK_END: FIRST_RESERVED + 5,
+ TK_FALSE: FIRST_RESERVED + 6,
+ TK_FOR: FIRST_RESERVED + 7,
+ TK_FUNCTION: FIRST_RESERVED + 8,
+ TK_GOTO: FIRST_RESERVED + 9,
+ TK_IF: FIRST_RESERVED + 10,
+ TK_IN: FIRST_RESERVED + 11,
+ TK_LOCAL: FIRST_RESERVED + 12,
+ TK_NIL: FIRST_RESERVED + 13,
+ TK_NOT: FIRST_RESERVED + 14,
+ TK_OR: FIRST_RESERVED + 15,
+ TK_REPEAT: FIRST_RESERVED + 16,
+ TK_RETURN: FIRST_RESERVED + 17,
+ TK_THEN: FIRST_RESERVED + 18,
+ TK_TRUE: FIRST_RESERVED + 19,
+ TK_UNTIL: FIRST_RESERVED + 20,
+ TK_WHILE: FIRST_RESERVED + 21,
+ /* other terminal symbols */
+ TK_IDIV: FIRST_RESERVED + 22,
+ TK_CONCAT: FIRST_RESERVED + 23,
+ TK_DOTS: FIRST_RESERVED + 24,
+ TK_EQ: FIRST_RESERVED + 25,
+ TK_GE: FIRST_RESERVED + 26,
+ TK_LE: FIRST_RESERVED + 27,
+ TK_NE: FIRST_RESERVED + 28,
+ TK_SHL: FIRST_RESERVED + 29,
+ TK_SHR: FIRST_RESERVED + 30,
+ TK_DBCOLON: FIRST_RESERVED + 31,
+ TK_EOS: FIRST_RESERVED + 32,
+ TK_FLT: FIRST_RESERVED + 33,
+ TK_INT: FIRST_RESERVED + 34,
+ TK_NAME: FIRST_RESERVED + 35,
+ TK_STRING: FIRST_RESERVED + 36
+};
+
+const R = RESERVED;
+
+const luaX_tokens = [
+ "and", "break", "do", "else", "elseif",
+ "end", "false", "for", "function", "goto", "if",
+ "in", "local", "nil", "not", "or", "repeat",
+ "return", "then", "true", "until", "while",
+ "//", "..", "...", "==", ">=", "<=", "~=",
+ "<<", ">>", "::", "<eof>",
+ "<number>", "<integer>", "<name>", "<string>"
+];
+
+const NUM_RESERVED = Object.keys(RESERVED).length;
+
+class MBuffer {
+ constructor(data) {
+ this.buffer = typeof data === "string" ? data.split('') : (data ? data : []);
+ this.n = this.buffer instanceof DataView ? this.buffer.byteLength : this.buffer.length;
+ this.off = 0;
+ }
+
+ getc() {
+ if (this.buffer instanceof DataView)
+ return this.n-- > 0 ? this.buffer.getUint8(this.off++, true) : -1;
+
+ return this.n-- > 0 ? this.buffer[this.off++] : -1;
+ }
+}
+
+class SemInfo {
+ constructor() {
+ this.r = NaN;
+ this.i = NaN;
+ this.ts = null;
+ }
+}
+
+class Token {
+ constructor() {
+ this.token = NaN;
+ this.seminfo = new SemInfo();
+ }
+}
+
+/* state of the lexer plus state of the parser when shared by all
+ functions */
+class LexState {
+ constructor() {
+ this.current = NaN; /* current character (charint) */
+ this.linenumber = NaN; /* input line counter */
+ this.lastline = NaN; /* line of last token 'consumed' */
+ this.t = new Token(); /* current token */
+ this.lookahead = new Token(); /* look ahead token */
+ this.fs = null; /* current function (parser) */
+ this.L = null;
+ this.z = new MBuffer();
+ this.buff = new MBuffer(); /* buffer for tokens */
+ this.h = null; /* to avoid collection/reuse strings */
+ this.dyd = null; /* dynamic structures used by the parser */
+ this.source = null; /* current source name */
+ this.envn = null; /* environment variable name */
+ }
+}
+
+const save = function(ls, c) {
+ let b = ls.buff;
+ if (b.n + 1 > b.buffer.length) {
+ if (b.buffer.length >= Number.MAX_SAFE_INTEGER/2)
+ lexerror(ls, "lexical element too long", 0);
+ }
+ b.buffer[b.n++] = c;
+};
+
+const luaX_token2str = function(ls, token) {
+ if (token < FIRST_RESERVED) { /* single-byte symbols? */
+ return `'${String.fromCharCode(token)}'`;
+ } else {
+ let s = luaX_tokens[token - FIRST_RESERVED];
+ if (token < R.TK_EOS) /* fixed format (symbols and reserved words)? */
+ return `'${s}'`;
+ else /* names, strings, and numerals */
+ return s;
+ }
+};
+
+const currIsNewline = function(ls) {
+ return ls.current === '\n' || ls.current === '\r';
+};
+
+const next = function(ls) {
+ ls.current = ls.z.getc();
+};
+
+const save_and_next = function(ls) {
+ save(ls, ls.current);
+ next(ls);
+};
+
+/*
+** increment line number and skips newline sequence (any of
+** \n, \r, \n\r, or \r\n)
+*/
+const inclinenumber = function(ls) {
+ let old = ls.current;
+ assert(currIsNewline(ls));
+ next(ls); /* skip '\n' or '\r' */
+ if (currIsNewline(ls) && ls.current !== old)
+ next(ls); /* skip '\n\r' or '\r\n' */
+ if (++ls.linenumber >= Number.MAX_SAFE_INTEGER)
+ lexerror(ls, "chunk has too many lines", 0);
+};
+
+const luaX_setinput = function(L, ls, z, source, firstchar) {
+ ls.t = {
+ token: 0,
+ seminfo: {
+ i: NaN,
+ r: NaN,
+ ts: null
+ }
+ };
+ ls.L = L;
+ ls.current = firstchar;
+ ls.lookahead = {
+ token: R.TK_EOS,
+ seminfo: {
+ i: NaN,
+ r: NaN,
+ ts: null
+ }
+ };
+ ls.z = z;
+ ls.fs = null;
+ ls.linenumber = 1;
+ ls.lastline = 1;
+ ls.source = source;
+ ls.envn = new TValue(CT.LUA_TLNGSTR, "_ENV");
+};
+
+const check_next1 = function(ls, c) {
+ if (ls.current === c) {
+ next(ls);
+ return true;
+ }
+
+ return false;
+};
+
+/*
+** Check whether current char is in set 'set' (with two chars) and
+** saves it
+*/
+const check_next2 = function(ls, set) {
+ if (ls.current === set.charAt(0) || ls.current === set.charAt(1)) {
+ save_and_next(ls);
+ return true;
+ }
+
+ return false;
+};
+
+const read_numeral = function(ls, seminfo) {
+ let expo = "Ee";
+ let first = ls.current;
+ assert(ljstype.lisdigit(ls.current));
+ save_and_next(ls);
+ if (first === '0' && check_next2(ls, "xX")) /* hexadecimal? */
+ expo = "Pp";
+
+ for (;;) {
+ if (check_next2(ls, expo)) /* exponent part? */
+ check_next2(ls, "-+"); /* optional exponent sign */
+ if (ljstype.lisxdigit(ls.current))
+ save_and_next(ls);
+ else if (ls.current === '.')
+ save_and_next(ls);
+ else break;
+ }
+
+ save(ls, '\0');
+
+ let obj = lobject.luaO_str2num(ls.buff.buffer);
+ if (obj === false) /* format error? */
+ lexerror(ls, "malformed number", R.TK_FLT);
+ if (obj.ttisinteger()) {
+ seminfo.i = obj.value;
+ return R.TK_INT;
+ } else {
+ assert(obj.ttisfloat());
+ seminfo.r = obj.value;
+ return R.TK_FLT;
+ }
+};
+
+const txtToken = function(ls, token) {
+ switch (token) {
+ case R.TK_NAME: case R.TK_STRING:
+ case R.TK_FLT: case R.TK_INT:
+ save(ls, '\0');
+ return `'${ls.buff.buffer.join('')}'`;
+ default:
+ return luaX_token2str(ls, token);
+ }
+};
+
+const lexerror = function(ls, msg, token) {
+ msg = ldebug.luaG_addinfo(ls.L, msg, ls.source, ls.linenumber);
+ if (token)
+ lapi.lua_pushstring(ls.L, `${msg} near ${txtToken(ls, token)}`);
+ ldo.luaD_throw(ls.L, TS.LUA_ERRSYNTAX);
+};
+
+const luaX_syntaxerror = function(ls, msg) {
+ lexerror(ls, msg, ls.t.token);
+};
+
+/*
+** skip a sequence '[=*[' or ']=*]'; if sequence is well formed, return
+** its number of '='s; otherwise, return a negative number (-1 iff there
+** are no '='s after initial bracket)
+*/
+const skip_sep = function(ls) {
+ let count = 0;
+ let s = ls.current;
+ assert(s === '[' || s === ']');
+ save_and_next(ls);
+ while (ls.current === '=') {
+ save_and_next(ls);
+ count++;
+ }
+ return ls.current === s ? count : (-count) - 1;
+};
+
+const read_long_string = function(ls, seminfo, sep) {
+ let line = ls.linenumber; /* initial line (for error message) */
+ save_and_next(ls); /* skip 2nd '[' */
+
+ if (currIsNewline(ls)) /* string starts with a newline? */
+ inclinenumber(ls); /* skip it */
+
+ let skip = false;
+ for (; !skip ;) {
+ switch (ls.current) {
+ case -1: { /* error */
+ let what = seminfo ? "string" : "comment";
+ let msg = `unfinished long ${what} (starting at line ${line})`;
+ lexerror(ls, msg, R.TK_EOS);
+ break;
+ }
+ case ']': {
+ if (skip_sep(ls) === sep) {
+ save_and_next(ls); /* skip 2nd ']' */
+ skip = true;
+ }
+ break;
+ }
+ case '\n': case '\r': {
+ save(ls, '\n');
+ inclinenumber(ls);
+ if (!seminfo) {
+ ls.buff.n = 0;
+ ls.buff.buffer = [];
+ }
+ break;
+ }
+ default: {
+ if (seminfo) save_and_next(ls);
+ else next(ls);
+ }
+ }
+ }
+
+ if (seminfo)
+ seminfo.ts = new TValue(CT.LUA_TLNGSTR, ls.buff.buffer.slice(2 + sep).join(''));
+};
+
+const esccheck = function(ls, c, msg) {
+ if (!c) {
+ if (ls.current !== -1)
+ save_and_next(ls); /* add current to buffer for error message */
+ lexerror(ls, msg, R.TK_STRING);
+ }
+};
+
+const gethexa = function(ls) {
+ save_and_next(ls);
+ esccheck(ls, ljstype.lisxdigit(ls.current), "hexadecimal digit expected");
+ return lobject.luaO_hexavalue(ls.current);
+};
+
+const readhexaesc = function(ls) {
+ let r = gethexa(ls);
+ r = (r << 4) + gethexa(ls);
+ ls.buff.n -= 2; /* remove saved chars from buffer */
+ return r;
+};
+
+const readutf8desc = function(ls) {
+ let i = 4; /* chars to be removed: '\', 'u', '{', and first digit */
+ save_and_next(ls); /* skip 'u' */
+ esccheck(ls, ls.current === '{', "missing '{'");
+ let r = gethexa(ls); /* must have at least one digit */
+
+ save_and_next(ls);
+ while (ljstype.lisxdigit(ls.current)) {
+ i++;
+ r = (r << 4) + lobject.luaO_hexavalue(ls.current);
+ esccheck(ls, r <= 0x10FFFF, "UTF-8 value too large");
+ save_and_next(ls);
+ }
+ esccheck(ls, ls.current === '}', "missing '}'");
+ next(ls); /* skip '}' */
+ ls.buff.n -= i; /* remove saved chars from buffer */
+ return r;
+};
+
+const utf8esc = function(ls) {
+ let buff = new Array(lobject.UTF8BUFFSZ);
+ let n = lobject.luaO_utf8esc(buff, readutf8desc(ls));
+ for (; n > 0; n--) /* add 'buff' to string */
+ save(ls, buff[lobject.UTF8BUFFSZ - n]);
+};
+
+const readdecesc = function(ls) {
+ let r = 0; /* result accumulator */
+ let i;
+ for (i = 0; i < 3 && ljstype.lisdigit(ls.current); i++) { /* read up to 3 digits */
+ r = 10 * r + parseInt(ls.current);
+ save_and_next(ls);
+ }
+ esccheck(ls, r <= 255, "decimal escape too large");
+ ls.buff.n -= i; /* remove read digits from buffer */
+ return r;
+};
+
+const read_string = function(ls, del, seminfo) {
+ save_and_next(ls); /* keep delimiter (for error messages) */
+
+ while (ls.current !== del) {
+ switch (ls.current) {
+ case -1:
+ lexerror(ls, "unfinished string", R.TK_EOS);
+ break;
+ case '\n':
+ case '\r':
+ lexerror(ls, "unfinished string", R.TK_STRING);
+ break;
+ case '\\': { /* escape sequences */
+ save_and_next(ls); /* keep '\\' for error messages */
+ let will;
+ let c;
+ switch(ls.current) {
+ case 'a': c = '\a'; will = 'read_save'; break;
+ case 'b': c = '\b'; will = 'read_save'; break;
+ case 'f': c = '\f'; will = 'read_save'; break;
+ case 'n': c = '\n'; will = 'read_save'; break;
+ case 'r': c = '\r'; will = 'read_save'; break;
+ case 't': c = '\t'; will = 'read_save'; break;
+ case 'v': c = '\v'; will = 'read_save'; break;
+ case 'x': c = readhexaesc(ls); will = 'read_save'; break;
+ case 'u': utf8esc(ls); will = 'read_save'; break;
+ case '\n': case '\r':
+ inclinenumber(ls); c = '\n'; will = 'read_save'; break;
+ case '\\': case '\"': case '\'':
+ c = ls.current; will = 'read_save'; break;
+ case -1: will = 'read_save'; break; /* will raise an error next loop */
+ case 'z': { /* zap following span of spaces */
+ ls.buff.n -= 1; /* remove '\\' */
+ next(ls); /* skip the 'z' */
+ while (ljstype.lisspace(ls.current)) {
+ if (currIsNewline(ls)) inclinenumber(ls);
+ else next(ls);
+ }
+ will = 'no_save'; break;
+ }
+ default: {
+ esccheck(ls, ljstype.lisdigit(ls.current), "invalid escape sequence");
+ c = readdecesc(ls); /* digital escape '\ddd' */
+ will = 'only_save'; break;
+ }
+ }
+
+ if (will === 'read_save')
+ next(ls);
+ else if (will === 'only_save') {
+ ls.buff.n -= 1; /* remove '\\' */
+ save(ls, c);
+ } else if (will === 'no_save')
+ break;
+ }
+ default:
+ save_and_next(ls);
+ }
+ }
+ save_and_next(ls); /* skip delimiter */
+ seminfo.ts = new TValue(CT.LUA_TLNGSTR, ls.buff.buffer.slice(1, ls.buff.buffer.length-1).join(''));
+};
+
+const isreserved = function(w) {
+ return luaX_tokens.slice(0, 22).indexOf(w) >= 0;
+};
+
+const llex = function(ls, seminfo) {
+ ls.buff.n = 0;
+ ls.buff.buffer = [];
+
+ for (;;) {
+ switch (ls.current) {
+ case '\n': case '\r': { /* line breaks */
+ inclinenumber(ls);
+ break;
+ }
+ case ' ': case '\f': case '\t': case '\v': { /* spaces */
+ next(ls);
+ break;
+ }
+ case '-': { /* '-' or '--' (comment) */
+ next(ls);
+ if (ls.current !== '-') return '-';
+ /* else is a comment */
+ next(ls);
+ if (ls.current === '[') { /* long comment? */
+ let sep = skip_sep(ls);
+ ls.buff.n = 0; /* 'skip_sep' may dirty the buffer */
+ ls.buff.buffer = [];
+ if (sep >= 0) {
+ read_long_string(ls, null, sep); /* skip long comment */
+ ls.buff.n = 0; /* previous call may dirty the buff. */
+ ls.buff.buffer = [];
+ break;
+ }
+ }
+
+ /* else short comment */
+ while (!currIsNewline(ls) && ls.current !== -1)
+ next(ls); /* skip until end of line (or end of file) */
+ break;
+ }
+ case '[': { /* long string or simply '[' */
+ let sep = skip_sep(ls);
+ if (sep.charCodeAt(0) >= 0) {
+ read_long_string(ls, seminfo, sep);
+ return R.TK_STRING;
+ } else if (sep !== -1) /* '[=...' missing second bracket */
+ lexerror(ls, "invalid long string delimiter", R.TK_STRING);
+ return '[';
+ }
+ case '=': {
+ next(ls);
+ if (check_next1(ls, '=')) return R.TK_EQ;
+ else return '=';
+ }
+ case '<': {
+ next(ls);
+ if (check_next1(ls, '=')) return R.TK_LE;
+ else if (check_next1(ls, '<')) return R.TK_SHL;
+ else return '<';
+ }
+ case '>': {
+ next(ls);
+ if (check_next1(ls, '=')) return R.TK_GE;
+ else if (check_next1(ls, '>')) return R.TK_SHR;
+ else return '>';
+ }
+ case '/': {
+ next(ls);
+ if (check_next1(ls, '/')) return R.TK_IDIV;
+ else return '/';
+ }
+ case '~': {
+ next(ls);
+ if (check_next1(ls, '=')) return R.TK_NE;
+ else return '~';
+ }
+ case ':': {
+ next(ls);
+ if (check_next1(ls, ':')) return R.TK_DBCOLON;
+ else return ':';
+ }
+ case '"': case '\'': { /* short literal strings */
+ read_string(ls, ls.current, seminfo);
+ return R.TK_STRING;
+ }
+ case '.': { /* '.', '..', '...', or number */
+ save_and_next(ls);
+ if (check_next1(ls, '.')) {
+ if (check_next1(ls, '.'))
+ return R.TK_DOTS; /* '...' */
+ else return R.TK_CONCAT; /* '..' */
+ }
+ else if (!ljstype.lisdigit(ls.current)) return '.';
+ else return read_numeral(ls, seminfo);
+ }
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9': {
+ return read_numeral(ls, seminfo);
+ }
+ case -1: {
+ return R.TK_EOS;
+ }
+ default: {
+ if (ljstype.lislalpha(ls.current)) { /* identifier or reserved word? */
+ do {
+ save_and_next(ls);
+ } while (ljstype.lislalnum(ls.current));
+
+ let ts = new TValue(CT.LUA_TLNGSTR, ls.buff.buffer.join(''));
+ seminfo.ts = ts;
+ let kidx = luaX_tokens.slice(0, 22).indexOf(ts.value);
+ if (kidx >= 0) /* reserved word? */
+ return kidx + FIRST_RESERVED;
+ else
+ return R.TK_NAME;
+ } else { /* single-char tokens (+ - / ...) */
+ let c = ls.current;
+ next(ls);
+ return c;
+ }
+ }
+ }
+ }
+};
+
+const luaX_next = function(ls) {
+ ls.lastline = ls.linenumber;
+ if (ls.lookahead.token !== R.TK_EOS) { /* is there a look-ahead token? */
+ ls.t = ls.lookahead; /* use this one */
+ ls.lookahead.token = R.TK_EOS; /* and discharge it */
+ } else
+ ls.t.token = llex(ls, ls.t.seminfo); /* read next token */
+};
+
+const luaX_lookahead = function(ls) {
+ assert(ls.lookahead.token === R.TK_EOS);
+ ls.lookahead.token = llex(ls. ls.lookahead.seminfo);
+ return ls.lookahead.token;
+};
+
+module.exports.FIRST_RESERVED = FIRST_RESERVED;
+module.exports.LexState = LexState;
+module.exports.MBuffer = MBuffer;
+module.exports.RESERVED = RESERVED;
+module.exports.isreserved = isreserved;
+module.exports.luaX_lookahead = luaX_lookahead;
+module.exports.luaX_next = luaX_next;
+module.exports.luaX_setinput = luaX_setinput;
+module.exports.luaX_syntaxerror = luaX_syntaxerror;
+module.exports.luaX_token2str = luaX_token2str;
+module.exports.luaX_tokens = luaX_tokens; \ No newline at end of file
diff --git a/src/lmathlib.js b/src/lmathlib.js
index 54bdc46..55f8416 100644
--- a/src/lmathlib.js
+++ b/src/lmathlib.js
@@ -1,4 +1,3 @@
-/* jshint esversion: 6 */
"use strict";
const assert = require('assert');
diff --git a/src/lobject.js b/src/lobject.js
index 086af00..bd234a1 100644
--- a/src/lobject.js
+++ b/src/lobject.js
@@ -1,8 +1,12 @@
-/*jshint esversion: 6 */
+ /*jshint esversion: 6 */
"use strict";
-const CT = require('./lua.js').constant_types;
-const UpVal = require('./lfunc.js').UpVal;
+const assert = require('assert');
+
+const ljstype = require('./ljstype.js');
+const lua = require('./lua.js');
+const CT = lua.constant_types;
+const UpVal = require('./lfunc.js').UpVal;
class TValue {
@@ -122,6 +126,7 @@ class Table extends TValue {
// Those lua values are used by value, others by reference
if (key instanceof TValue
&& [CT.LUA_TNIL,
+ CT.LUA_TBOOLEAN,
CT.LUA_TSTRING,
CT.LUA_TSHRSTR,
CT.LUA_TLNGSTR,
@@ -199,6 +204,18 @@ class CClosure extends TValue {
}
+/*
+** Description of a local variable for function prototypes
+** (used for debug information)
+*/
+class LocVar {
+ constructor() {
+ this.varname = null;
+ this.startpc = NaN; /* first point where variable is active */
+ this.endpc = NaN; /* first point where variable is dead */
+ }
+}
+
const RETS = "...";
const PRE = "[string \"";
const POS = "\"]";
@@ -223,7 +240,7 @@ const luaO_chunkid = function(source, bufflen) {
let nli = source.indexOf('\n'); /* find first new line (if any) */
let nl = nli ? source.slice(nli) : null;
out = `${PRE}`; /* add prefix */
- bufflen -= PRE.length - RETS.length; - POS.length + 1; /* save space for prefix+suffix+'\0' */
+ bufflen -= PRE.length + RETS.length + POS.length + 1; /* save space for prefix+suffix+'\0' */
if (l < bufflen && nl === null) { /* small one-line source? */
out += `${source}`; /* keep it */
} else {
@@ -237,8 +254,170 @@ const luaO_chunkid = function(source, bufflen) {
return out;
};
-module.exports.LClosure = LClosure;
-module.exports.CClosure = CClosure;
-module.exports.TValue = TValue;
-module.exports.Table = Table;
-module.exports.luaO_chunkid = luaO_chunkid; \ No newline at end of file
+const luaO_hexavalue = function(c) {
+ if (ljstype.lisdigit(c)) return c.charCodeAt(0) - '0'.charCodeAt(0);
+ else return (c.toLowerCase().charCodeAt(0) - 'a'.charCodeAt(0)) + 10;
+};
+
+const UTF8BUFFSZ = 8;
+
+const luaO_utf8desc = function(buff, x) {
+ let n = 1; /* number of bytes put in buffer (backwards) */
+ assert(x <= 0x10FFFF);
+ if (x < 0x80) /* ascii? */
+ buff[UTF8BUFFSZ - 1] = String.fromCharCode(x);
+ else { /* need continuation bytes */
+ let mfb = 0x3f; /* maximum that fits in first byte */
+ do {
+ buff[UTF8BUFFSZ - (n++)] = String.fromCharCode(0x80 | (x & 0x3f));
+ x >>= 6; /* remove added bits */
+ mfb >>= 1; /* now there is one less bit available in first byte */
+ } while (x > mfb); /* still needs continuation byte? */
+ buff[UTF8BUFFSZ - n] = String.fromCharCode((~mfb << 1) | x); /* add first byte */
+ }
+ return n;
+};
+
+const l_str2dloc = function(s, result, mode) {
+ result[0] = mode === 'x' ? parseInt(s, '16') : parseInt(s);
+ if (isNaN(result[0])) return null; /* nothing recognized? */
+ while (ljstype.lisspace(result[0])) result[0] = result[0].slice(1); /* skip trailing spaces */
+ return result[0] === '\0' ? result : null; /* OK if no trailing characters */
+};
+
+const l_str2d = function(s) {
+ let result = [null, null];
+ let pidx = /[.xXnN]/g.exec(s).index;
+ let pmode = pidx ? s.slice(pidx) : null;
+ let mode = pmode ? pmode.toLowerCase() : 0;
+ if (mode === 'n') /* reject 'inf' and 'nan' */
+ return null;
+ let end = l_str2dloc(s, result, mode)[0]; /* try to convert */
+ if (end === null) { /* failed? may be a different locale */
+ throw new Error("Locale not available to handle number"); // TODO
+ }
+ return [end, result[1]];
+};
+
+const MAXBY10 = Number.MAX_SAFE_INTEGER / 10;
+const MAXLASTD = Number.MAX_SAFE_INTEGER % 10;
+
+const l_str2int = function(s) {
+ let result = [null, null];
+ let a = 0;
+ let empty = true;
+ let neg;
+
+ while (ljstype.lisspace(s[0])) s = s.slice(1); /* skip initial spaces */
+ neg = s[0] === '-';
+
+ if (neg || s[0] === '+')
+ s = s.slice(1);
+
+ if (s[0] === '0' && (s[1] === 'x' || s[1] === 'X')) { /* hex? */
+ s = s.slice(2); /* skip '0x' */
+
+ for (; ljstype.lisxdigit(s[0]); s = s.slice(1)) {
+ a = a * 16 + luaO_hexavalue(s);
+ empty = false;
+ }
+ } else { /* decimal */
+ for (; ljstype.lisdigit(s[0]); s = s.slice(1)) {
+ let d = parseInt(s[0]);
+ if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg)) /* overflow? */
+ return null; /* do not accept it (as integer) */
+ a = a * 10 + d;
+ empty = false;
+ }
+ }
+
+ while (ljstype.lisspace(s[0])) s = s.slice(1); /* skip trailing spaces */
+
+ if (empty)/* TODO: || s[0] !== "") */ return null; /* something wrong in the numeral */
+ else {
+ result[1] = neg ? -a : a;
+ result[0] = s;
+ return result;
+ }
+};
+
+const luaO_str2num = function(s) {
+ let s2i = l_str2int(s);
+ let e = s2i[0];
+ let i = s2i[1];
+
+ if (e !== null) { /* try as an integer */
+ return new TValue(CT.LUA_TNUMINT, i);
+ } else { /* else try as a float */
+ s2i = l_str2d(s);
+ e = s2i[0];
+ i = s2i[1];
+
+ if (e !== null) {
+ return new TValue(CT.LUA_TNUMFLT, i);
+ } else
+ return false; /* conversion failed */
+ }
+};
+
+/*
+** converts an integer to a "floating point byte", represented as
+** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
+** eeeee !== 0 and (xxx) otherwise.
+*/
+const luaO_int2fb = function(x) {
+ let e = 0; /* exponent */
+ if (x < 8) return x;
+ while (x >= (8 << 4)) { /* coarse steps */
+ x = (x + 0xf) >> 4; /* x = ceil(x / 16) */
+ e += 4;
+ }
+ while (x >= (8 << 1)) { /* fine steps */
+ x = (x + 1) >> 1; /* x = ceil(x / 2) */
+ e++;
+ }
+ return ((e+1) << 3) | (x - 8);
+};
+
+const intarith = function(L, op, v1, v2) {
+ switch (op) {
+ case lua.LUA_OPADD: return (v1 + v2)|0;
+ case lua.LUA_OPSUB: return (v1 - v2)|0;
+ case lua.LUA_OPMUL: return (v1 * v2)|0;
+ case lua.LUA_OPMOD: return (v1 % v2)|0;
+ case lua.LUA_OPIDIV: return (v1 / v2)|0;
+ case lua.LUA_OPBAND: return (v1 & v2)|0;
+ case lua.LUA_OPBOR: return (v1 | v2)|0;
+ case lua.LUA_OPBXOR: return (v1 ^ v2)|0;
+ case lua.LUA_OPSHL: return (v1 << v2)|0;
+ case lua.LUA_OPSHR: return (v1 >> v2)|0;
+ case lua.LUA_OPUNM: return (-v1)|0;
+ case lua.LUA_OPBNOT: return (~v1)|0;
+ }
+};
+
+
+const numarith = function(L, op, v1, v2) {
+ switch (op) {
+ case lua.LUA_OPADD: return v1 + v2;
+ case lua.LUA_OPSUB: return v1 - v2;
+ case lua.LUA_OPMUL: return v1 * v2;
+ case lua.LUA_OPDIV: return v1 / v2;
+ case lua.LUA_OPPOW: return Math.pow(v1, v2);
+ case lua.LUA_OPIDIV: return (v1 / v2)|0;
+ case lua.LUA_OPUNM: return -v1;
+ case lua.LUA_OPMOD: return v1 % v2;
+ }
+};
+
+module.exports.CClosure = CClosure;
+module.exports.LClosure = LClosure;
+module.exports.LocVar = LocVar;
+module.exports.TValue = TValue;
+module.exports.Table = Table;
+module.exports.UTF8BUFFSZ = UTF8BUFFSZ;
+module.exports.luaO_chunkid = luaO_chunkid;
+module.exports.luaO_hexavalue = luaO_hexavalue;
+module.exports.luaO_int2fb = luaO_int2fb;
+module.exports.luaO_str2num = luaO_str2num;
+module.exports.luaO_utf8desc = luaO_utf8desc; \ No newline at end of file
diff --git a/src/lopcodes.js b/src/lopcodes.js
index 5723abb..bfcd993 100644
--- a/src/lopcodes.js
+++ b/src/lopcodes.js
@@ -51,6 +51,56 @@ const OpCodes = [
"OP_EXTRAARG"
];
+const OpCodesI = {
+ OP_MOVE: 0,
+ OP_LOADK: 1,
+ OP_LOADKX: 2,
+ OP_LOADBOOL: 3,
+ OP_LOADNIL: 4,
+ OP_GETUPVAL: 5,
+ OP_GETTABUP: 6,
+ OP_GETTABLE: 7,
+ OP_SETTABUP: 8,
+ OP_SETUPVAL: 9,
+ OP_SETTABLE: 10,
+ OP_NEWTABLE: 11,
+ OP_SELF: 12,
+ OP_ADD: 13,
+ OP_SUB: 14,
+ OP_MUL: 15,
+ OP_MOD: 16,
+ OP_POW: 17,
+ OP_DIV: 18,
+ OP_IDIV: 19,
+ OP_BAND: 20,
+ OP_BOR: 21,
+ OP_BXOR: 22,
+ OP_SHL: 23,
+ OP_SHR: 24,
+ OP_UNM: 25,
+ OP_BNOT: 26,
+ OP_NOT: 27,
+ OP_LEN: 28,
+ OP_CONCAT: 29,
+ OP_JMP: 30,
+ OP_EQ: 31,
+ OP_LT: 32,
+ OP_LE: 33,
+ OP_TEST: 34,
+ OP_TESTSET: 35,
+ OP_CALL: 36,
+ OP_TAILCALL: 37,
+ OP_RETURN: 38,
+ OP_FORLOOP: 39,
+ OP_FORPREP: 40,
+ OP_TFORCALL: 41,
+ OP_TFORLOOP: 42,
+ OP_SETLIST: 43,
+ OP_CLOSURE: 44,
+ OP_VARARG: 45,
+ OP_EXTRAARG: 46
+};
+
/*
** masks for instruction properties. The format is:
** bits 0-1: op mode
@@ -120,10 +170,26 @@ const luaP_opmodes = [
0 << 7 | 0 << 6 | OpArgU << 4 | OpArgU << 2 | iAx /* OP_EXTRAARG */
];
+const getOpMode = function(m) {
+ return luaP_opmodes[m] & 3;
+};
+
+const getBMode = function(m) {
+ return (luaP_opmodes[m] >> 4) & 3;
+};
+
+const getCMode = function(m) {
+ return (luaP_opmodes[m] >> 2) & 3;
+};
+
const testAMode = function(m) {
return luaP_opmodes[m] & (1 << 6);
};
+const testTMode = function(m) {
+ return luaP_opmodes[m] & (1 << 7);
+};
+
const SIZE_C = 9;
const SIZE_B = 9;
const SIZE_Bx = (SIZE_C + SIZE_B);
@@ -143,40 +209,158 @@ const MAXARG_A = ((1 << SIZE_A) - 1);
const MAXARG_B = ((1 << SIZE_B) - 1);
const MAXARG_C = ((1 << SIZE_C) - 1);
+/* this bit 1 means constant (0 means register) */
const BITRK = (1 << (SIZE_B - 1));
+/*
+** invalid register that fits in 8 bits
+*/
+const NO_REG = MAXARG_A;
+
+/* test whether value is a constant */
const ISK = function (x) {
return x & BITRK;
};
+/* gets the index of the constant */
const INDEXK = function (r) {
return r & ~BITRK;
};
+/* code a constant index as a RK value */
+const RKASK = function(x) {
+ return x | BITRK;
+};
+
+
+/* creates a mask with 'n' 1 bits at position 'p' */
+const MASK1 = function(n, p) {
+ return ((~((~0)<<(n)))<<(p));
+};
+
+/* creates a mask with 'n' 0 bits at position 'p' */
+const MASK0 = function(n, p) {
+ return (~MASK1(n, p));
+};
+
+const setarg = function(i, v, pos, size) {
+ i.code = (i.code & MASK0(size, pos)) | ((v << pos) & MASK1(size, pos));
+ fullins(i);
+};
+
+const SETARG_A = function(i,v) {
+ setarg(i, v, POS_A, SIZE_A);
+};
+
+const SETARG_B = function(i,v) {
+ setarg(i, v, POS_B, SIZE_B);
+};
+
+const SETARG_C = function(i,v) {
+ setarg(i, v, POS_C, SIZE_C);
+};
+
+const SETARG_Bx = function(i,v) {
+ setarg(i, v, POS_Bx, SIZE_Bx);
+};
+
+const SETARG_Ax = function(i,v) {
+ setarg(i, v, POS_Ax, SIZE_Ax);
+};
+
+const SETARG_sBx = function(i, b) {
+ SETARG_Bx(i, b + MAXARG_sBx);
+};
+
+/*
+** Pre-calculate all possible part of the instruction
+*/
+const fullins = function(ins) {
+ if (typeof ins === "number") {
+ return {
+ code: ins,
+ opcode: (ins >> POS_OP) & MASK1(SIZE_OP, 0),
+ A: (ins >> POS_A) & MASK1(SIZE_A, 0),
+ B: (ins >> POS_B) & MASK1(SIZE_B, 0),
+ C: (ins >> POS_C) & MASK1(SIZE_C, 0),
+ Bx: (ins >> POS_Bx) & MASK1(SIZE_Bx, 0),
+ Ax: (ins >> POS_Ax) & MASK1(SIZE_Ax, 0),
+ sBx: ((ins >> POS_Bx) & MASK1(SIZE_Bx, 0)) - MAXARG_sBx
+ };
+ } else {
+ let i = ins.code;
+ ins.opcode = (i >> POS_OP) & MASK1(SIZE_OP, 0);
+ ins.A = (i >> POS_A) & MASK1(SIZE_A, 0);
+ ins.B = (i >> POS_B) & MASK1(SIZE_B, 0);
+ ins.C = (i >> POS_C) & MASK1(SIZE_C, 0);
+ ins.Bx = (i >> POS_Bx) & MASK1(SIZE_Bx, 0);
+ ins.Ax = (i >> POS_Ax) & MASK1(SIZE_Ax, 0);
+ ins.sBx = ((i >> POS_Bx) & MASK1(SIZE_Bx, 0)) - MAXARG_sBx;
+ return ins;
+ }
+};
+
+const CREATE_ABC = function(o, a, b, c) {
+ return fullins(o << POS_OP | a << POS_A | b << POS_B | c << POS_C);
+};
+
+const CREATE_ABx = function(o, a, bc) {
+ return fullins(o << POS_OP | a << POS_A | bc << POS_Bx);
+};
+
+const CREATE_Ax = function(o, a) {
+ return fullins(o << POS_OP | a << POS_Ax);
+};
+
/* number of list items to accumulate before a SETLIST instruction */
const LFIELDS_PER_FLUSH = 50;
-module.exports.OpCodes = OpCodes;
-module.exports.SIZE_C = SIZE_C;
-module.exports.SIZE_B = SIZE_B;
-module.exports.SIZE_Bx = SIZE_Bx;
-module.exports.SIZE_A = SIZE_A;
-module.exports.SIZE_Ax = SIZE_Ax;
-module.exports.SIZE_OP = SIZE_OP;
-module.exports.POS_OP = POS_OP;
-module.exports.POS_A = POS_A;
-module.exports.POS_C = POS_C;
-module.exports.POS_B = POS_B;
-module.exports.POS_Bx = POS_Bx;
-module.exports.POS_Ax = POS_Ax;
-module.exports.MAXARG_Bx = MAXARG_Bx;
-module.exports.MAXARG_sBx = MAXARG_sBx;
-module.exports.MAXARG_Ax = MAXARG_Ax;
-module.exports.MAXARG_A = MAXARG_A;
-module.exports.MAXARG_B = MAXARG_B;
-module.exports.MAXARG_C = MAXARG_C;
-module.exports.BITRK = BITRK;
-module.exports.ISK = ISK;
-module.exports.INDEXK = INDEXK;
-module.exports.LFIELDS_PER_FLUSH = LFIELDS_PER_FLUSH;
-module.exports.testAMode = testAMode; \ No newline at end of file
+module.exports.BITRK = BITRK;
+module.exports.CREATE_ABC = CREATE_ABC;
+module.exports.CREATE_ABx = CREATE_ABx;
+module.exports.CREATE_Ax = CREATE_Ax;
+module.exports.INDEXK = INDEXK;
+module.exports.ISK = ISK;
+module.exports.LFIELDS_PER_FLUSH = LFIELDS_PER_FLUSH;
+module.exports.MAXARG_A = MAXARG_A;
+module.exports.MAXARG_Ax = MAXARG_Ax;
+module.exports.MAXARG_B = MAXARG_B;
+module.exports.MAXARG_Bx = MAXARG_Bx;
+module.exports.MAXARG_C = MAXARG_C;
+module.exports.MAXARG_sBx = MAXARG_sBx;
+module.exports.NO_REG = NO_REG;
+module.exports.OpArgK = OpArgK;
+module.exports.OpArgN = OpArgN;
+module.exports.OpArgR = OpArgR;
+module.exports.OpArgU = OpArgU;
+module.exports.OpCodes = OpCodes;
+module.exports.OpCodesI = OpCodesI;
+module.exports.POS_A = POS_A;
+module.exports.POS_Ax = POS_Ax;
+module.exports.POS_B = POS_B;
+module.exports.POS_Bx = POS_Bx;
+module.exports.POS_C = POS_C;
+module.exports.POS_OP = POS_OP;
+module.exports.RKASK = RKASK;
+module.exports.SETARG_A = SETARG_A;
+module.exports.SETARG_Ax = SETARG_Ax;
+module.exports.SETARG_B = SETARG_B;
+module.exports.SETARG_Bx = SETARG_Bx;
+module.exports.SETARG_C = SETARG_C;
+module.exports.SETARG_sBx = SETARG_sBx;
+module.exports.SIZE_A = SIZE_A;
+module.exports.SIZE_Ax = SIZE_Ax;
+module.exports.SIZE_B = SIZE_B;
+module.exports.SIZE_Bx = SIZE_Bx;
+module.exports.SIZE_C = SIZE_C;
+module.exports.SIZE_OP = SIZE_OP;
+module.exports.fullins = fullins;
+module.exports.getBMode = getBMode;
+module.exports.getCMode = getCMode;
+module.exports.getOpMode = getOpMode;
+module.exports.iABC = iABC;
+module.exports.iABx = iABx;
+module.exports.iAsBx = iAsBx;
+module.exports.iAx = iAx;
+module.exports.testAMode = testAMode;
+module.exports.testTMode = testTMode; \ No newline at end of file
diff --git a/src/lparser.js b/src/lparser.js
new file mode 100644
index 0000000..1e454e0
--- /dev/null
+++ b/src/lparser.js
@@ -0,0 +1,1560 @@
+"use strict";
+
+const assert = require('assert');
+
+const lcode = require('./lcode.js');
+const lfunc = require('./lfunc.js');
+const llex = require('./llex.js');
+const llimit = require('./llimit.js');
+const lobject = require('./lobject.js');
+const lopcode = require('./lopcodes.js');
+const lua = require('./lua.js');
+const BinOpr = lcode.BinOpr;
+const OpCodesI = lopcode.OpCodesI;
+const Proto = lfunc.Proto;
+const R = llex.RESERVED;
+const TValue = lobject.TValue;
+const Table = lobject.Table;
+const UnOpr = lcode.UnOpr;
+const UpVal = lfunc.UpVal;
+
+const MAXVARS = 200;
+
+const hasmultret = function(k) {
+ return k === expkind.VCALL || k === expkind.VVARARG;
+};
+
+class BlockCnt {
+ constructor() {
+ this.previous = null; /* chain */
+ this.firstlabel = NaN; /* index of first label in this block */
+ this.firstgoto = NaN; /* index of first pending goto in this block */
+ this.nactvar = NaN; /* # active locals outside the block */
+ this.upval = NaN; /* true if some variable in the block is an upvalue */
+ this.isloop = NaN; /* true if 'block' is a loop */
+ }
+}
+
+const expkind = {
+ VVOID: 0, /* when 'expdesc' describes the last expression a list,
+ this kind means an empty list (so, no expression) */
+ VNIL: 1, /* constant nil */
+ VTRUE: 2, /* constant true */
+ VFALSE: 3, /* constant false */
+ VK: 4, /* constant in 'k'; info = index of constant in 'k' */
+ VKFLT: 5, /* floating constant; nval = numerical float value */
+ VKINT: 6, /* integer constant; nval = numerical integer value */
+ VNONRELOC: 7, /* expression has its value in a fixed register;
+ info = result register */
+ VLOCAL: 8, /* local variable; info = local register */
+ VUPVAL: 9, /* upvalue variable; info = index of upvalue in 'upvalues' */
+ VINDEXED: 10, /* indexed variable;
+ ind.vt = whether 't' is register or upvalue;
+ ind.t = table register or upvalue;
+ ind.idx = key's R/K index */
+ VJMP: 11, /* expression is a test/comparison;
+ info = pc of corresponding jump instruction */
+ VRELOCABLE: 12, /* expression can put result in any register;
+ info = instruction pc */
+ VCALL: 13, /* expression is a function call; info = instruction pc */
+ VVARARG: 14 /* vararg expression; info = instruction pc */
+};
+
+const vkisvar = function(k) {
+ return expkind.VLOCAL <= k && k <= expkind.VINDEXED;
+};
+
+const vkisinreg = function(k) {
+ return k === expkind.VNONRELOC || k === expkind.VLOCAL;
+};
+
+class expdesc {
+ constructor() {
+ this.k = NaN;
+ this.u = {
+ ival: NaN, /* for VKINT */
+ nval: NaN, /* for VKFLT */
+ info: NaN, /* for generic use */
+ ind: { /* for indexed variables (VINDEXED) */
+ idx: NaN, /* index (R/K) */
+ t: NaN, /* table (register or upvalue) */
+ vt: NaN /* whether 't' is register (VLOCAL) or upvalue (VUPVAL) */
+ }
+ };
+ this.t = NaN; /* patch list of 'exit when true' */
+ this.f = NaN; /* patch list of 'exit when false' */
+ }
+}
+
+class FuncState {
+ constructor() {
+ this.f = null; /* current function header */
+ this.prev = null; /* enclosing function */
+ this.ls = null; /* lexical state */
+ this.bl = null; /* chain of current blocks */
+ this.pc = NaN; /* next position to code (equivalent to 'ncode') */
+ this.lasttarget = NaN; /* 'label' of last 'jump label' */
+ this.jpc = NaN; /* list of pending jumps to 'pc' */
+ this.nk = NaN; /* number of elements in 'k' */
+ this.np = NaN; /* number of elements in 'p' */
+ this.firstlocal = NaN; /* index of first local var (in Dyndata array) */
+ this.nlocvars = NaN; /* number of elements in 'f->locvars' */
+ this.nactvar = NaN; /* number of active local variables */
+ this.nups = NaN; /* number of upvalues */
+ this.freereg = NaN; /* first free register */
+ }
+}
+
+ /* description of active local variable */
+class Vardesc {
+ constructor() {
+ this.idx = NaN; /* variable index in stack */
+ }
+}
+
+
+/* description of pending goto statements and label statements */
+class Labeldesc {
+ constructor() {
+ this.name = null; /* label identifier */
+ this.pc = NaN; /* position in code */
+ this.line = NaN; /* line where it appeared */
+ this.nactvar = NaN; /* local level where it appears in current block */
+ }
+}
+
+
+/* list of labels or gotos */
+class Labellist {
+ constructor() {
+ this.arr = []; /* array */
+ this.n = NaN; /* number of entries in use */
+ this.size = NaN; /* array size */
+ }
+}
+
+/* dynamic structures used by the parser */
+class Dyndata {
+ constructor() {
+ this.actvar = { /* list of active local variables */
+ arr: [],
+ n: NaN,
+ size: NaN
+ };
+ this.gt = new Labellist();
+ this.label = new Labellist();
+ }
+}
+
+const semerror = function(ls, msg) {
+ ls.t.token = 0; /* remove "near <token>" from final message */
+ llex.luaX_syntaxerror(ls, msg);
+};
+
+const error_expected = function(ls, token) {
+ llex.luaX_syntaxerror(ls, `${llex.luaX_token2str(ls, token)} expected`);
+};
+
+const errorlimit = function(fs, limit, what) {
+ let L = fs.ls.L;
+ let line = fs.f.linedefined;
+ let where = (line === 0) ? "main function" : `function at line ${line}`;
+ let msg = `too many ${what} (limit is ${limit}) in ${where}`;
+ llex.luaX_syntaxerror(fs.ls, msg);
+};
+
+const checklimit = function(fs, v, l, what) {
+ if (v > l) errorlimit(fs, l, what);
+};
+
+const testnext = function(ls, c) {
+ if (ls.t.token === c) {
+ llex.luaX_next(ls);
+ return true;
+ }
+
+ return false;
+};
+
+const check = function(ls, c) {
+ if (ls.t.token !== c)
+ error_expected(ls, c);
+};
+
+const checknext = function(ls, c) {
+ check(ls, c);
+ llex.luaX_next(ls);
+};
+
+const check_condition = function(ls, c, msg) {
+ if (!c)
+ llex.luaX_syntaxerror(ls, msg);
+};
+
+const check_match = function(ls, what, who, where) {
+ if (!testnext(ls, what)) {
+ if (where === ls.linenumber)
+ error_expected(ls, what);
+ else
+ llex.luaX_syntaxerror(ls,
+ `${llex.luaX_token2str(ls, what)} expected (to close ${llex.luaX_token2str(ls, who)} at line ${where}`);
+ }
+};
+
+const str_checkname = function(ls) {
+ check(ls, R.TK_NAME);
+ let ts = ls.t.seminfo.ts;
+ llex.luaX_next(ls);
+ return ts;
+};
+
+const init_exp = function(e, k, i) {
+ e.f = e.t = lcode.NO_JUMP;
+ e.k = k;
+ e.u.info = i;
+};
+
+const codestring = function(ls, e, s) {
+ init_exp(e, expkind.VK, lcode.luaK_stringK(ls.fs, s));
+};
+
+const checkname = function(ls, e) {
+ codestring(ls, e, str_checkname(ls));
+};
+
+const registerlocalvar = function(ls, varname) {
+ let fs = ls.fs;
+ let f = fs.f;
+ f.locvars[fs.nlocvars] = new lobject.LocVar();
+ f.locvars[fs.nlocvars].varname = varname;
+ return fs.nlocvars++;
+};
+
+const new_localvar = function(ls, name) {
+ let fs = ls.fs;
+ let dyd = ls.dyd;
+ let reg = registerlocalvar(ls, name);
+ checklimit(fs, dyd.actvar.n + 1 - fs.firstlocal, MAXVARS, "local variables");
+ dyd.actvar.arr[dyd.actvar.n] = new Vardesc();
+ dyd.actvar.arr[dyd.actvar.n].idx = reg;
+ dyd.actvar.n++;
+};
+
+const new_localvarliteral = function(ls, name) {
+ new_localvar(ls, new TValue(lua.CT.LUA_TLNGSTR, name));
+};
+
+const getlocvar = function(fs, i) {
+ let idx = fs.ls.dyd.actvar.arr[fs.firstlocal + i].idx;
+ assert(idx < fs.nlocvars);
+ return fs.f.locvars[idx];
+};
+
+const adjustlocalvars = function(ls, nvars) {
+ let fs = ls.fs;
+ fs.nactvar = fs.nactvar + nvars;
+ for (; nvars; nvars--)
+ getlocvar(fs, fs.nactvar - nvars).startpc = fs.pc;
+};
+
+const removevars = function(fs, tolevel) {
+ fs.ls.dyd.actvar.n -= fs.nactvar - tolevel;
+ while (fs.nactvar > tolevel)
+ getlocvar(fs, --fs.nactvar).endpc = fs.pc;
+};
+
+const searchupvalue = function(fs, name) {
+ let up = fs.f.upvalues;
+ for (let i = 0; i < fs.nups; i++) {
+ if (up[i].name.value === name.value)
+ return i;
+ }
+ return -1; /* not found */
+};
+
+const newupvalue = function(fs, name, v) {
+ let f = fs.f;
+ checklimit(fs, fs.nups + 1, lfunc.MAXUPVAL, "upvalues");
+ f.upvalues[fs.nups] = new UpVal(fs.ls.L);
+ f.upvalues[fs.nups].instack = v.k === expkind.VLOCAL;
+ f.upvalues[fs.nups].idx = v.u.info;
+ f.upvalues[fs.nups].name = name;
+ return fs.nups++;
+};
+
+const searchvar = function(fs, n) {
+ for (let i = fs.nactvar - 1; i >= 0; i--) {
+ if (n.value === getlocvar(fs, i).varname.value)
+ return i;
+ }
+
+ return -1;
+};
+
+/*
+** Mark block where variable at given level was defined
+** (to emit close instructions later).
+*/
+const markupval = function(fs, level) {
+ let bl = fs.bl;
+ while (bl.nactvar > level)
+ bl = bl.previous;
+ bl.upval = 1;
+};
+
+/*
+** Find variable with given name 'n'. If it is an upvalue, add this
+** upvalue into all intermediate functions.
+*/
+const singlevaraux = function(fs, n, vr, base) {
+ if (fs === null) /* no more levels? */
+ init_exp(vr, expkind.VVOID, 0); /* default is global */
+ else {
+ let v = searchvar(fs, n); /* look up locals at current level */
+ if (v >= 0) { /* found? */
+ init_exp(vr, expkind.VLOCAL, v); /* variable is local */
+ if (!base)
+ markupval(fs, v); /* local will be used as an upval */
+ } else { /* not found as local at current level; try upvalues */
+ let idx = searchupvalue(fs, n); /* try existing upvalues */
+ if (idx < 0) { /* not found? */
+ singlevaraux(fs.prev, n, vr, 0); /* try upper levels */
+ if (vr.k === expkind.VVOID) /* not found? */
+ return; /* it is a global */
+ /* else was LOCAL or UPVAL */
+ idx = newupvalue(fs, n, vr); /* will be a new upvalue */
+ }
+ init_exp(vr, expkind.VUPVAL, idx); /* new or old upvalue */
+ }
+ }
+};
+
+const singlevar = function(ls, vr) {
+ let varname = str_checkname(ls);
+ let fs = ls.fs;
+ singlevaraux(fs, varname, vr, 1);
+ if (vr.k === expkind.VVOID) { /* global name? */
+ let key = new expdesc();
+ singlevaraux(fs, ls.envn, vr, 1); /* get environment variable */
+ assert(vr.k !== expkind.VVOID); /* this one must exist */
+ codestring(ls, key, varname); /* key is variable name */
+ lcode.luaK_indexed(fs, vr, key); /* env[varname] */
+ }
+};
+
+const adjust_assign = function(ls, nvars, nexps, e) {
+ let fs = ls.fs;
+ let extra = nvars - nexps;
+ if (hasmultret(e.k)) {
+ extra++; /* includes call itself */
+ if (extra < 0) extra = 0;
+ lcode.luaK_setreturns(fs, e, extra); /* last exp. provides the difference */
+ if (extra > 1) lcode.luaK_reserveregs(fs, extra - 1);
+ } else {
+ if (e.k !== expkind.VVOID) lcode.luaK_exp2nextreg(fs, e); /* close last expression */
+ if (extra > 0) {
+ let reg = fs.freereg;
+ lcode.luaK_reserveregs(fs, extra);
+ lcode.luaK_nil(fs, reg, extra);
+ }
+ }
+ if (nexps > nvars)
+ ls.fs.freereg -= nexps - nvars; /* remove extra values */
+};
+
+const enterlevel = function(ls) {
+ let L = ls.L;
+ ++L.nCcalls;
+ checklimit(ls.fs, L.nCcalls, llimit.LUAI_MAXCCALLS, "JS levels");
+};
+
+const leavelevel = function(ls) {
+ return ls.L.nCcalls--;
+};
+
+const closegoto = function(ls, g, label) {
+ let fs = ls.fs;
+ let gl = ls.dyd.gt;
+ let gt = gl.arr[g];
+ assert(gt.name.value === label.name.value);
+ if (gt.nactvar < label.nactvar) {
+ let vname = getlocvar(fs, gt.nactvar).varname;
+ semerror(ls, `<goto ${gt.name.value}> at line ${gt.line} jumps into the scope of local '${vname.value}'`);
+ }
+ lcode.luaK_patchlist(fs, gt.pc, label.pc);
+ /* remove goto from pending list */
+ for (let i = g; i < gl.n - 1; i++)
+ gl.arr[i] = gl.arr[i + 1];
+ gl.n--;
+};
+
+/*
+** try to close a goto with existing labels; this solves backward jumps
+*/
+const findlabel = function(ls, g) {
+ let bl = ls.fs.bl;
+ let dyd = ls.dyd;
+ let gt = dyd.gt.arr[g];
+ /* check labels in current block for a match */
+ for (let i = bl.firstlabel; i < dyd.label.n; i++) {
+ let lb = dyd.label.arr[i];
+ if (lb.name.value === gt.name.value) { /* correct label? */
+ if (gt.nactvar > lb.nactvar && (bl.upval || dyd.label.n > bl.firstlabel))
+ lcode.luaK_patchclose(ls.fs, gt.pc, lb.nactvar);
+ closegoto(ls, g, lb); /* close it */
+ return true;
+ }
+ }
+ return false; /* label not found; cannot close goto */
+};
+
+const newlabelentry = function(ls, l, name, line, pc) {
+ let n = l.n;
+ l.arr[n] = new Labeldesc();
+ l.arr[n].name = name;
+ l.arr[n].line = line;
+ l.arr[n].nactvar = ls.fs.nactvar;
+ l.arr[n].pc = pc;
+ l.n = n + 1;
+ return n;
+};
+
+/*
+** check whether new label 'lb' matches any pending gotos in current
+** block; solves forward jumps
+*/
+const findgotos = function(ls, lb) {
+ let gl = ls.dyd.gt;
+ let i = ls.fs.bl.firstgoto;
+ while (i < gl.n) {
+ if (gl.arr[i].name.value === lb.name.value)
+ closegoto(ls, i, lb);
+ else
+ i++;
+ }
+};
+
+/*
+** export pending gotos to outer level, to check them against
+** outer labels; if the block being exited has upvalues, and
+** the goto exits the scope of any variable (which can be the
+** upvalue), close those variables being exited.
+*/
+const movegotosout = function(fs, bl) {
+ let i = bl.firstgoto;
+ let gl = fs.ls.dydy.gt;
+ /* correct pending gotos to current block and try to close it
+ with visible labels */
+ while (i < gl.n) {
+ let gt = gl.arr[i];
+ if (gt.nactvar > bl.nactvar) {
+ if (bl.upval)
+ lcode.luaK_patchclose(fs, gt.pc, bl.nactvar);
+ gt.nactvar = bl.nactvar;
+ }
+ if (!findlabel(fs.ls, i))
+ i++; /* move to next one */
+ }
+};
+
+const enterblock = function(fs, bl, isloop) {
+ bl.isloop = isloop;
+ bl.nactvar = fs.nactvar;
+ bl.firstlabel = fs.ls.dyd.label.n;
+ bl.firstgoto = fs.ls.dyd.gt.n;
+ bl.upval = 0;
+ bl.previous = fs.bl;
+ fs.bl = bl;
+ assert(fs.freereg === fs.nactvar);
+};
+
+/*
+** create a label named 'break' to resolve break statements
+*/
+const breaklabel = function(ls) {
+ let n = new TValue(lua.CT.LUA_TLNGSTR, "break");
+ let l = newlabelentry(ls, ls.dyd.label, n, 0, ls.fs.pc);
+ findgotos(ls, ls.dyd.label.arr[l]);
+};
+
+/*
+** generates an error for an undefined 'goto'; choose appropriate
+** message when label name is a reserved word (which can only be 'break')
+*/
+const undefgoto = function(ls, gt) {
+ const msg = llex.isreserved(gt.name.value)
+ ? `<${gt.name.value}> at line ${gt.line} not inside a loop`
+ : `no visible label '${gt.name.value}' for <goto> at line ${gt.line}`;
+ semerror(ls, msg);
+};
+
+/*
+** adds a new prototype into list of prototypes
+*/
+const addprototype = function(ls) {
+ let clp = new Proto();
+ let L = ls.L;
+ let fs = ls.fs;
+ let f = fs.f; /* prototype of current function */
+ f.p[fs.np++] = clp;
+ return clp;
+};
+
+/*
+** codes instruction to create new closure in parent function.
+*/
+const codeclosure = function(ls, v) {
+ let fs = ls.fs.prev;
+ init_exp(v, expkind.VRELOCABLE, lcode.luaK_codeABx(fs, OpCodesI.OP_CLOSURE, 0, fs.np -1));
+ lcode.luaK_exp2nextreg(fs, v); /* fix it at the last register */
+};
+
+const open_func = function(ls, fs, bl) {
+ fs.prev = ls.fs; /* linked list of funcstates */
+ fs.ls = ls;
+ ls.fs = fs;
+ fs.pc = 0;
+ fs.lasttarget = 0;
+ fs.jpc = lcode.NO_JUMP;
+ fs.freereg = 0;
+ fs.nk = 0;
+ fs.np = 0;
+ fs.nups = 0;
+ fs.nlocvars = 0;
+ fs.nactvar = 0;
+ fs.firstlocal = ls.dyd.actvar.n;
+ fs.bl = null;
+ let f = new Proto();
+ f = fs.f;
+ f.source = ls.source;
+ f.maxstacksize = 2; /* registers 0/1 are always valid */
+ enterblock(fs, bl, false);
+};
+
+const leaveblock = function(fs) {
+ let bl = fs.bl;
+ let ls = fs.ls;
+ if (bl.previous && bl.upval) {
+ /* create a 'jump to here' to close upvalues */
+ let j = lcode.luaK_jump(fs);
+ lcode.luaK_patchclose(fs, j , bl.nactvar);
+ lcode.luaK_patchtohere(fs, j);
+ }
+
+ if (bl.isloop)
+ breaklabel(ls); /* close pending breaks */
+
+ fs.bl = bl.previous;
+ removevars(fs, bl.nactvar);
+ assert(bl.nactvar === fs.nactvar);
+ fs.freereg = fs.nactvar; /* free registers */
+ ls.dyd.label.n = bl.firstlabel; /* remove local labels */
+ if (bl.previous) /* inner block? */
+ movegotosout(fs, bl); /* update pending gotos to outer block */
+ else if (bl.firstgoto < ls.dyd.gt.n) /* pending gotos in outer block? */
+ undefgoto(ls, ls.dyd.gt.arr[bl.firstgoto]); /* error */
+};
+
+const close_func = function(ls) {
+ let L = ls.L;
+ let fs = ls.fs;
+ let f = fs.f;
+ lcode.luaK_ret(fs, 0, 0); /* final return */
+ leaveblock(fs);
+ ls.fs = fs.prev;
+};
+
+/*============================================================*/
+/* GRAMMAR RULES */
+/*============================================================*/
+
+const block_follow = function(ls, withuntil) {
+ switch (ls.t.token) {
+ case R.TK_ELSE: case R.TK_ELSEIF:
+ case R.TK_END: case R.TK_EOS:
+ return true;
+ case R.TK_UNTIL: return withuntil;
+ default: return false;
+ }
+};
+
+const statlist = function(ls) {
+ /* statlist -> { stat [';'] } */
+ while (!block_follow(ls, 1)) {
+ if (ls.t.token === R.TK_RETURN) {
+ statement(ls);
+ return; /* 'return' must be last statement */
+ }
+ statement(ls);
+ }
+};
+
+const fieldsel = function(ls, v) {
+ /* fieldsel -> ['.' | ':'] NAME */
+ let fs = ls.fs;
+ let key = new expdesc();
+ lcode.luaK_exp2anyregup(fs, v);
+ llex.luaX_next(ls); /* skip the dot or colon */
+ checkname(ls, key);
+ lcode.luaK_indexed(fs, v, key);
+};
+
+const yindex = function(ls, v) {
+ /* index -> '[' expr ']' */
+ llex.luaX_next(ls); /* skip the '[' */
+ expr(ls, v);
+ lcode.luaK_exp2val(ls.fs, v);
+ checknext(ls, ']');
+};
+
+/*
+** {======================================================================
+** Rules for Constructors
+** =======================================================================
+*/
+
+class ConsControl {
+ constructor() {
+ this.v = new expdesc(); /* last list item read */
+ this.t = new expdesc(); /* table descriptor */
+ this.nh = NaN; /* total number of 'record' elements */
+ this.na = NaN; /* total number of array elements */
+ this.tostore = NaN; /* number of array elements pending to be stored */
+ }
+}
+
+const recfield = function(ls, cc) {
+ /* recfield -> (NAME | '['exp1']') = exp1 */
+ let fs = ls.fs;
+ let reg = ls.fs.freereg;
+ let key = new expdesc();
+ let val = new expdesc();
+
+ if (ls.t.token === R.TK_NAME) {
+ checklimit(fs, cc.nh, Number.MAX_SAFE_INTEGER, "items in a constructor");
+ checkname(ls, key);
+ } else /* ls->t.token === '[' */
+ yindex(ls, key);
+ cc.nh++;
+ checknext(ls, '=');
+ let rkkey = lcode.luaK_exp2RK(fs, key);
+ expr(ls, val);
+ lcode.luaK_codeABC(fs, OpCodesI.OP_SETTABLE, cc.t.u.info, rkkey, lcode.luaK_exp2RK(fs, val));
+ fs.freereg = reg; /* free registers */
+};
+
+const closelistfield = function(fs, cc) {
+ if (cc.v.k === expkind.VVOID) return; /* there is no list item */
+ lcode.luaK_exp2nextreg(fs, cc.v);
+ cc.v.k = expkind.VVOID;
+ if (cc.tostore === lopcode.LFIELDS_PER_FLUSH) {
+ lcode.luaK_setlist(fs, cc.t.u.info, cc.na, cc.tostore); /* flush */
+ cc.tostore = 0; /* no more items pending */
+ }
+};
+
+const lastlistfield = function(fs, cc) {
+ if (cc.tostore === 0) return;
+ if (hasmultret(cc.v.k)) {
+ lcode.luaK_setmultret(fs, cc.v);
+ lcode.luaK_setlist(fs, cc.t.u.info, cc.na, lua.LUA_MULTRET);
+ cc.na--; /* do not count last expression (unknown number of elements) */
+ } else {
+ if (cc.v.k !== expkind.VVOID)
+ lcode.luaK_exp2nextreg(fs, cc.v);
+ lcode.luaK_setlist(fs, cc.t.u.info, cc.na, cc.tostore);
+ }
+};
+
+const listfield = function(ls, cc) {
+ /* listfield -> exp */
+ expr(ls, cc.v);
+ checklimit(ls.fs, cc.na, Number.MAX_SAFE_INTEGER, "items in a constructor");
+ cc.na++;
+ cc.tostore++;
+};
+
+const field = function(ls, cc) {
+ /* field -> listfield | recfield */
+ switch (ls.t.token) {
+ case R.TK_NAME: { /* may be 'listfield' or 'recfield' */
+ if (llex.luaX_lookahead(ls) !== '=') /* expression? */
+ listfield(ls, cc);
+ else
+ recfield(ls, cc);
+ break;
+ }
+ case '[': {
+ recfield(ls, cc);
+ break;
+ }
+ default: {
+ listfield(ls, cc);
+ break;
+ }
+ }
+};
+
+const constructor = function(ls, t) {
+ /* constructor -> '{' [ field { sep field } [sep] ] '}'
+ sep -> ',' | ';' */
+ let fs = ls.fs;
+ let line = ls.linenumber;
+ let pc = lcode.luaK_codeABC(fs, OpCodesI.OP_NEWTABLE, 0, 0, 0);
+ let cc = new ConsControl();
+ cc.na = cc.nh = cc.tostore = 0;
+ cc.t = t;
+ init_exp(t, expkind.VRELOCABLE, pc);
+ init_exp(cc.v, expkind.VVOID, 0); /* no value (yet) */
+ lcode.luaK_exp2nextreg(ls.fs, t); /* fix it at stack top */
+ checknext(ls, '{');
+ do {
+ assert(cc.v.k === expkind.VVOID || cc.tostore > 0);
+ if (ls.t.token === '}') break;
+ closelistfield(fs, cc);
+ field(ls, cc);
+ } while (testnext(ls, ',') || testnext(ls, ';'));
+ check_match(ls, '}', '{', line);
+ lastlistfield(fs, cc);
+ lopcode.SETARG_B(fs.f.code[pc], lobject.luaO_int2fb(cc.na)); /* set initial array size */
+ lopcode.SETARG_C(fs.f.code[pc], lobject.luaO_int2fb(cc.nh)); /* set initial table size */
+};
+
+/* }====================================================================== */
+
+const parlist = function(ls) {
+ /* parlist -> [ param { ',' param } ] */
+ let fs = ls.fs;
+ let f = fs.f;
+ let nparams = 0;
+ f.is_vararg = 0;
+ if (ls.t.token !== ')') { /* is 'parlist' not empty? */
+ do {
+ switch (ls.t.token) {
+ case R.TK_NAME: { /* param -> NAME */
+ new_localvar(ls, str_checkname(ls));
+ nparams++;
+ break;
+ }
+ case R.TK_DOTS: { /* param -> '...' */
+ llex.luaX_next(ls);
+ f.is_vararg = 1; /* declared vararg */
+ break;
+ }
+ default: llex.luaX_syntaxerror(ls, "<name> or '...' expected");
+ }
+ } while(!f.is_vararg && testnext(ls, ','));
+ }
+ adjustlocalvars(ls, nparams);
+ f.numparams = fs.nactvar;
+ lcode.luaK_reserveregs(fs, fs.nactvar); /* reserve register for parameters */
+};
+
+const body = function(ls, e, ismethod, line) {
+ /* body -> '(' parlist ')' block END */
+ let new_fs = new FuncState();
+ let bl = new BlockCnt();
+ new_fs.f = addprototype(ls);
+ new_fs.f.linedefined = line;
+ open_func(ls, new_fs, bl);
+ checknext(ls, '(');
+ if (ismethod) {
+ new_localvarliteral(ls, "self"); /* create 'self' parameter */
+ adjustlocalvars(ls, 1);
+ }
+ parlist(ls);
+ checknext(ls, ')');
+ statlist(ls);
+ new_fs.f.lastlinedefined = ls.linenumber;
+ check_match(ls, R.TK_END, R.TK_FUNCTION, line);
+ codeclosure(ls, e);
+ close_func(ls);
+};
+
+const explist = function(ls, v) {
+ /* explist -> expr { ',' expr } */
+ let n = 1; /* at least one expression */
+ expr(ls, v);
+ while (testnext(ls, ',')) {
+ lcode.luaK_exp2nextreg(ls.fs, v);
+ expr(ls, v);
+ n++;
+ }
+ return n;
+};
+
+const funcargs = function(ls, f, line) {
+ let fs = ls.fs;
+ let args = new expdesc();
+ switch (ls.t.token) {
+ case '(': { /* funcargs -> '(' [ explist ] ')' */
+ llex.luaX_next(ls);
+ if (ls.t.token === ')') /* arg list is empty? */
+ args.k = expkind.VVOID;
+ else {
+ explist(ls, args);
+ lcode.luaK_setmultret(fs, args);
+ }
+ check_match(ls, ')', '(', line);
+ break;
+ }
+ case '{': { /* funcargs -> constructor */
+ constructor(ls, args);
+ break;
+ }
+ case R.TK_STRING: { /* funcargs -> STRING */
+ codestring(ls, args, ls.t.seminfo.ts);
+ llex.luaX_next(ls); /* must use 'seminfo' before 'next' */
+ break;
+ }
+ default: {
+ llex.luaX_syntaxerror(ls, "function arguments expected");
+ }
+ }
+ assert(f.k === expkind.VNONRELOC);
+ let nparams;
+ let base = f.u.info; /* base register for call */
+ if (hasmultret(args.k))
+ nparams = lua.LUA_MULTRET; /* open call */
+ else {
+ if (args.k !== expkind.VVOID)
+ lcode.luaK_exp2nextreg(fs, args); /* close last argument */
+ nparams = fs.freereg - (base+1);
+ }
+ init_exp(f, expkind.VCALL, lcode.luaK_codeABC(fs, OpCodesI.OP_CALL, base, nparams+1, 2));
+ lcode.luaK_fixline(fs, line);
+ fs.freereg = base + 1; /* call remove function and arguments and leaves (unless changed) one result */
+};
+
+/*
+** {======================================================================
+** Expression parsing
+** =======================================================================
+*/
+
+const primaryexp = function(ls, v) {
+ /* primaryexp -> NAME | '(' expr ')' */
+ switch (ls.t.token) {
+ case '(': {
+ let line = ls.linenumber;
+ llex.luaX_next(ls);
+ expr(ls, v);
+ check_match(ls, ')', '(', line);
+ lcode.luaK_dischargevars(ls.fs, v);
+ return;
+ }
+ case R.TK_NAME: {
+ singlevar(ls, v);
+ return;
+ }
+ default: {
+ llex.luaX_syntaxerror(ls, "unexpected symbol");
+ }
+ }
+};
+
+const suffixedexp = function(ls, v) {
+ /* suffixedexp ->
+ primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */
+ let fs = ls.fs;
+ let line = ls.linenumber;
+ primaryexp(ls, v);
+ for (;;) {
+ switch (ls.t.token) {
+ case '.': { /* fieldsel */
+ fieldsel(ls, v);
+ break;
+ }
+ case '[': { /* '[' exp1 ']' */
+ let key = new expdesc();
+ lcode.luaK_exp2anyregup(fs, v);
+ yindex(ls, key);
+ lcode.luaK_indexed(fs, v, key);
+ break;
+ }
+ case ':': { /* ':' NAME funcargs */
+ let key = new expdesc();
+ llex.luaX_next(ls);
+ checkname(ls, key);
+ lcode.luaK_self(fs, v, key);
+ funcargs(ls, v, line);
+ break;
+ }
+ case '(': case R.TK_STRING: case '{': { /* funcargs */
+ lcode.luaK_exp2nextreg(fs, v);
+ funcargs(ls, v, line);
+ break;
+ }
+ default: return;
+ }
+ }
+};
+
+const simpleexp = function(ls, v) {
+ /* simpleexp -> FLT | INT | STRING | NIL | TRUE | FALSE | ... |
+ constructor | FUNCTION body | suffixedexp */
+ switch (ls.t.token) {
+ case R.TK_FLT: {
+ init_exp(v, expkind.VFLT, 0);
+ v.u.nval = ls.t.seminfo.r;
+ break;
+ }
+ case R.TK_INT: {
+ init_exp(v, expkind.VKINT, 0);
+ v.u.ival = ls.t.seminfo.i;
+ break;
+ }
+ case R.TK_STRING: {
+ codestring(ls, v, ls.t.seminfo.ts);
+ break;
+ }
+ case R.TK_NIL: {
+ init_exp(v, expkind.VNIL, 0);
+ break;
+ }
+ case R.TK_TRUE: {
+ init_exp(v, expkind.VTRUE, 0);
+ break;
+ }
+ case R.TK_FALSE: {
+ init_exp(v, expkind.VFALSE, 0);
+ break;
+ }
+ case R.TK_DOTS: { /* vararg */
+ let fs = ls.fs;
+ check_condition(ls, fs.f.is_vararg, "cannot use '...' outside a vararg function");
+ init_exp(v, expkind.VVARARG, lcode.luaK_codeABC(fs, OpCodesI.OP_VARARG, 0, 1, 0));
+ break;
+ }
+ case '{': { /* constructor */
+ constructor(ls, v);
+ return;
+ }
+ case R.TK_FUNCTION: {
+ llex.luaX_next(ls);
+ body(ls, v, 0, ls.linenumber);
+ return;
+ }
+ default: {
+ suffixedexp(ls, v);
+ return;
+ }
+ }
+ llex.luaX_next(ls);
+};
+
+const getunopr = function(op) {
+ switch (op) {
+ case R.TK_NOT: return UnOpr.OPR_NOT;
+ case '-': return UnOpr.OPR_MINUS;
+ case '~': return UnOpr.OPR_BNOT;
+ case '#': return UnOpr.OPR_LEN;
+ default: return UnOpr.OPR_NOUNOPR;
+ }
+};
+
+const getbinopr = function(op) {
+ switch (op) {
+ case '+': return BinOpr.OPR_ADD;
+ case '-': return BinOpr.OPR_SUB;
+ case '*': return BinOpr.OPR_MUL;
+ case '%': return BinOpr.OPR_MOD;
+ case '^': return BinOpr.OPR_POW;
+ case '/': return BinOpr.OPR_DIV;
+ case R.TK_IDIV: return BinOpr.OPR_IDIV;
+ case '&': return BinOpr.OPR_BAND;
+ case '|': return BinOpr.OPR_BOR;
+ case '~': return BinOpr.OPR_BXOR;
+ case R.TK_SHL: return BinOpr.OPR_SHL;
+ case R.TK_SHR: return BinOpr.OPR_SHR;
+ case R.TK_CONCAT: return BinOpr.OPR_CONCAT;
+ case R.TK_NE: return BinOpr.OPR_NE;
+ case R.TK_EQ: return BinOpr.OPR_EQ;
+ case '<': return BinOpr.OPR_LT;
+ case R.TK_LE: return BinOpr.OPR_LE;
+ case '>': return BinOpr.OPR_GT;
+ case R.TK_GE: return BinOpr.OPR_GE;
+ case R.TK_AND: return BinOpr.OPR_AND;
+ case R.TK_OR: return BinOpr.OPR_OR;
+ default: return BinOpr.OPR_NOBINOPR;
+ }
+};
+
+const priority = [ /* ORDER OPR */
+ {left: 10, right: 10}, {left: 10, right: 10}, /* '+' '-' */
+ {left: 11, right: 11}, {left: 11, right: 11}, /* '*' '%' */
+ {left: 14, right: 13}, /* '^' (right associative) */
+ {left: 11, right: 11}, {left: 11, right: 11}, /* '/' '//' */
+ {left: 6, right: 6}, {left: 4, right: 4}, {left: 5, right: 5}, /* '&' '|' '~' */
+ {left: 7, right: 7}, {left: 7, right: 7}, /* '<<' '>>' */
+ {left: 9, right: 8}, /* '..' (right associative) */
+ {left: 3, right: 3}, {left: 3, right: 3}, {left: 3, right: 3}, /* ==, <, <= */
+ {left: 3, right: 3}, {left: 3, right: 3}, {left: 3, right: 3}, /* ~=, >, >= */
+ {left: 2, right: 2}, {left: 1, right: 1} /* and, or */
+];
+
+const UNARY_PRIORITY = 12;
+
+/*
+** subexpr -> (simpleexp | unop subexpr) { binop subexpr }
+** where 'binop' is any binary operator with a priority higher than 'limit'
+*/
+const subexpr = function(ls, v, limit) {
+ enterlevel(ls);
+ let uop = getunopr(ls.t.token);
+ if (uop !== UnOpr.OPR_NOUNOPR) {
+ let line = ls.linenumber;
+ llex.luaX_next(ls);
+ subexpr(ls, v, UNARY_PRIORITY);
+ lcode.luaK_prefix(ls.fs, uop, v, line);
+ } else
+ simpleexp(ls, v);
+ /* expand while operators have priorities higher than 'limit' */
+ let op = getbinopr(ls.t.token);
+ while (op !== BinOpr.OPR_NOBINOPR && priority[op].left > limit) {
+ let v2 = new expdesc();
+ let line = ls.linenumber;
+ llex.luaX_next(ls);
+ lcode.luaK_infix(ls.fs, op, v);
+ /* read sub-expression with higher priority */
+ let nextop = subexpr(ls, v2, priority[op].right);
+ lcode.luaK_posfix(ls.fs, op, v, v2, line);
+ op = nextop;
+ }
+ leavelevel(ls);
+ return op; /* return first untreated operator */
+};
+
+const expr = function(ls, v) {
+ subexpr(ls, v, 0);
+};
+
+/* }==================================================================== */
+
+
+
+/*
+** {======================================================================
+** Rules for Statements
+** =======================================================================
+*/
+
+const block = function(ls) {
+ /* block -> statlist */
+ let fs = ls.fs;
+ let bl = new BlockCnt();
+ enterblock(fs, bl, 0);
+ statlist(ls);
+ leaveblock(fs);
+};
+
+/*
+** structure to chain all variables in the left-hand side of an
+** assignment
+*/
+class LHS_assign {
+ constructor() {
+ this.prev = null;
+ this.v = new expdesc(); /* variable (global, local, upvalue, or indexed) */
+ }
+}
+
+/*
+** check whether, in an assignment to an upvalue/local variable, the
+** upvalue/local variable is begin used in a previous assignment to a
+** table. If so, save original upvalue/local value in a safe place and
+** use this safe copy in the previous assignment.
+*/
+const check_conflict = function(ls, lh, v) {
+ let fs = ls.fs;
+ let extra = fs.freereg; /* eventual position to save local variable */
+ let conflict = false;
+ for (; lh; lh = lh.prev) { /* check all previous assignments */
+ if (lh.v.k === expkind.VINDEXED) { /* assigning to a table? */
+ /* table is the upvalue/local being assigned now? */
+ if (lh.v.u.ind.vt === v.k && lh.v.u.ind.t === v.u.info) {
+ conflict = true;
+ lh.v.u.ind.vt = expkind.VLOCAL;
+ lh.v.u.ind.t = extra; /* previous assignment will use safe copy */
+ }
+ /* index is the local being assigned? (index cannot be upvalue) */
+ if (v.k === expkind.VLOCAL && lh.v.u.ind.idx === v.u.info) {
+ conflict = true;
+ lh.v.u.ind.idx = extra; /* previous assignment will use safe copy */
+ }
+ }
+ }
+ if (conflict) {
+ /* copy upvalue/local value to a temporary (in position 'extra') */
+ let op = v.k === expkind.VLOCAL ? OpCodesI.OP_MOVE : OpCodesI.OP_GETUPVAL;
+ lcode.luaK_codeABC(fs, op, extra, v.u.info, 0);
+ lcode.luaK_reserveregs(fs, 1);
+ }
+};
+
+const assignment = function(ls, lh, nvars) {
+ let e = new expdesc();
+ check_condition(ls, vkisvar(lh.v.k), "syntax error");
+ if (testnext(ls, ',')) { /* assignment -> ',' suffixedexp assignment */
+ let nv = new LHS_assign();
+ nv.prev = lh;
+ suffixedexp(ls, nv.v);
+ if (nv.v.k !== expkind.VINDEXED)
+ check_conflict(ls, lh, nv.v);
+ checklimit(ls.fs, nvars + ls.L.nCcalls, llimit.LUAI_MAXCCALLS, "JS levels");
+ assignment(ls, nv, nvars + 1);
+ } else { /* assignment -> '=' explist */
+ checknext(ls, '=');
+ let nexps = explist(ls, e);
+ if (nexps !== nvars)
+ adjust_assign(ls, nvars, nexps, e);
+ else {
+ lcode.luaK_setoneret(ls.fs, e); /* close last expression */
+ lcode.luaK_storevar(ls.fs, lh.v, e);
+ return; /* avoid default */
+ }
+ }
+ init_exp(e, expkind.VNONRELOC, ls.fs.freereg-1); /* default assignment */
+ lcode.luaK_storevar(ls.fs, lh.v, e);
+};
+
+const cond = function(ls) {
+ /* cond -> exp */
+ let v = expdesc();
+ expr(ls, v); /* read condition */
+ if (v.k === expkind.VNIL) v.k = expkind.VFALSE; /* 'falses' are all equal here */
+ lcode.luaK_goiftrue(ls.fs, v);
+ return v;
+};
+
+const gotostat = function(ls, pc) {
+ let line = ls.linenumber;
+ let label;
+ if (testnext(ls, R.TK_GOTO))
+ label = str_checkname(ls);
+ else {
+ llex.luaX_next(ls); /* skip break */
+ label = new TValue(lua.CT.LUA_TLNGSTR, "break");
+ }
+ let g = newlabelentry(ls, ls.dyd.gt, label, line, pc);
+ findlabel(ls, g); /* close it if label already defined */
+};
+
+/* check for repeated labels on the same block */
+const checkrepeated = function(fs, ll, label) {
+ for (let i = fs.bl.firstlabel; i < ll.n; i++) {
+ if (label.value === ll.arr[i].name.value) {
+ semerror(fs.ls, `label '${label}' already defined on line ${ll.arr[i].line}`);
+ }
+ }
+};
+
+/* skip no-op statements */
+const skipnoopstat = function(ls) {
+ while (ls.t.token === ';' || ls.t.token === R.TK_DBCOLON)
+ statement(ls);
+};
+
+const labelstat = function(ls, label, line) {
+ /* label -> '::' NAME '::' */
+ let fs = ls.fs;
+ let ll = ls.dyd.label;
+ let l; /* index of new label being created */
+ checkrepeated(fs, ll, label); /* check for repeated labels */
+ checknext(ls, R.TK_DBCOLON); /* skip double colon */
+ /* create new entry for this label */
+ l = newlabelentry(ls, ll, label, line, lcode.luaK_getlabel(fs));
+ skipnoopstat(ls); /* skip other no-op statements */
+ if (block_follow(ls, 0)) { /* label is last no-op statement in the block? */
+ /* assume that locals are already out of scope */
+ ll.arr[l].nactvar = fs.bl.nactvar;
+ }
+ findgotos(ls, ll.arr[l]);
+};
+
+const whilestat = function(ls, line) {
+ /* whilestat -> WHILE cond DO block END */
+ let fs = ls.fs;
+ let bl = new BlockCnt();
+ llex.luaX_next(ls); /* skip WHILE */
+ let whileinit = lcode.luaK_getlabel(fs);
+ let condexit = cond(ls);
+ enterblock(fs, bl, 1);
+ checknext(ls, R.TK_DO);
+ block(ls);
+ lcode.luaK_jumpto(fs, whileinit);
+ check_match(ls, R.TK_END, R.TK_WHILE, line);
+ leaveblock(fs);
+ lcode.luaK_patchtohere(fs, condexit); /* false conditions finish the loop */
+};
+
+const repeatstat = function(ls, line) {
+ /* repeatstat -> REPEAT block UNTIL cond */
+ let fs = ls.fs;
+ let repeat_init = lcode.luaK_getlabel(fs);
+ let bl1 = new BlockCnt();
+ let bl2 = new BlockCnt();
+ enterblock(fs, bl1, 1); /* loop block */
+ enterblock(fs, bl2, 0); /* scope block */
+ llex.luaX_next(ls); /* skip REPEAT */
+ statlist(ls);
+ check_match(ls, R.TK_UNTIL, R.TK_REPEAT, line);
+ let condexit = cond(ls); /* read condition (inside scope block) */
+ if (bl2.upval) /* upvalues? */
+ lcode.luaK_patchclose(fs, condexit, bl2.nactvar);
+ leaveblock(fs); /* finish scope */
+ lcode.luaK_patchlist(fs, condexit, repeat_init); /* close the loop */
+ leaveblock(fs); /* finish loop */
+};
+
+const exp1 = function(ls) {
+ let e = new expdesc();
+ expr(ls, e);
+ lcode.luaK_exp2nextreg(ls.fs, e);
+ assert(e.k === expkind.VNONRELOC);
+ let reg = e.u.info;
+ return reg;
+};
+
+const forbody = function(ls, base, line, nvars, isnum) {
+ /* forbody -> DO block */
+ let bl = new BlockCnt();
+ let fs = ls.fs;
+ let endfor;
+ adjustlocalvars(ls, 3); /* control variables */
+ checknext(ls, R.TK_DO);
+ let prep = isnum ? lcode.luaK_codeAsBx(fs, OpCodesI.OP_FORPREP, base, lcode.NO_JUMP) : lcode.luaK_jump(fs);
+ enterblock(fs, bl, 0); /* scope for declared variables */
+ adjustlocalvars(ls, nvars);
+ lcode.luaK_reserveregs(fs, nvars);
+ block(ls);
+ leaveblock(fs); /* end of scope for declared variables */
+ lcode.luaK_patchtohere(fs, prep);
+ if (isnum) /* end of scope for declared variables */
+ endfor = lcode.luaK_codeAsBx(fs, OpCodesI.OP_FORLOOP, base, lcode.NO_JUMP);
+ else { /* generic for */
+ lcode.luaK_codeABC(fs, OpCodesI.OP_TFORLOOP, base + 2, lcode.NO_JUMP);
+ lcode.luaK_fixline(fs, line);
+ endfor = lcode.luaK_codeAsBx(fs, OpCodesI.OP_TFORLOOP, base + 2, lcode.NO_JUMP);
+ }
+ lcode.luaK_patchlist(fs, endfor, prep + 1);
+ lcode.luaK_fixline(fs, line);
+};
+
+const fornum = function(ls, varname, line) {
+ /* fornum -> NAME = exp1,exp1[,exp1] forbody */
+ let fs = ls.fs;
+ let base = fs.freereg;
+ new_localvarliteral(ls, "(for index)");
+ new_localvarliteral(ls, "(for limit)");
+ new_localvarliteral(ls, "(for step)");
+ new_localvar(ls, varname);
+ checknext(ls, '=');
+ exp1(ls); /* initial value */
+ checknext(ls, ',');
+ exp1(ls); /* limit */
+ if (testnext(ls, ','))
+ exp1(ls); /* optional step */
+ else { /* default step = 1 */
+ lcode.luaK_codek(fs, fs.freereg, lcode.luaK_intK(fs, 1));
+ lcode.luaK_reserveregs(fs, 1);
+ }
+ forbody(ls, base, line, 1, 1);
+};
+
+const forlist = function(ls, indexname) {
+ /* forlist -> NAME {,NAME} IN explist forbody */
+ let fs = ls.fs;
+ let e = new expdesc();
+ let nvars = 4; /* gen, state, control, plus at least one declared var */
+ let base = fs.freereg();
+ /* create control variables */
+ new_localvarliteral(ls, "(for generator)");
+ new_localvarliteral(ls, "(for state)");
+ new_localvarliteral(ls, "(for control)");
+ /* create declared variables */
+ new_localvar(ls, indexname);
+ while (testnext(ls, ',')) {
+ new_localvar(ls, str_checkname(ls));
+ nvars++;
+ }
+ checknext(ls, R.TK_IN);
+ let line = ls.linenumber;
+ adjust_assign(ls, 3, explist(ls, e), e);
+ lcode.luaK_checkstack(fs, 3); /* extra space to call generator */
+ forbody(ls, base, line, nvars - 3, 0);
+};
+
+const forstat = function(ls, line) {
+ /* forstat -> FOR (fornum | forlist) END */
+ let fs = ls.fs;
+ let bl = new BlockCnt();
+ enterblock(fs, bl, 1); /* scope for loop and control variables */
+ llex.luaX_next(ls); /* skip 'for' */
+ let varname = str_checkname(ls); /* first variable name */
+ switch (ls.t.token) {
+ case '=': fornum(ls, varname, line); break;
+ case ',': case R.TK_IN: forlist(ls, varname); break;
+ default: llex.luaX_syntaxerror(ls, "'=' or 'in' expected");
+ }
+ check_match(ls, R.TK_END, R.TK_FOR, line);
+ leaveblock(fs); /* loop scope ('break' jumps to this point) */
+};
+
+const test_then_block = function(ls, escapelist) {
+ /* test_then_block -> [IF | ELSEIF] cond THEN block */
+ let bl = new BlockCnt();
+ let fs = ls.fs;
+ let v = new expdesc();
+ let jf; /* instruction to skip 'then' code (if condition is false) */
+
+ llex.luaX_next(ls); /* skip IF or ELSEIF */
+ expr(ls, v); /* read condition */
+ checknext(ls, R.TK_THEN);
+
+ if (ls.t.token === R.TK_GOTO || ls.t.token === R.TK_BREAK) {
+ lcode.luaK_goiffalse(ls.fs, v); /* will jump to label if condition is true */
+ enterblock(fs, bl, false); /* must enter block before 'goto' */
+ gotostat(ls, v.t); /* handle goto/break */
+ skipnoopstat(ls); /* skip other no-op statements */
+ if (block_follow(ls, 0)) { /* 'goto' is the entire block? */
+ leaveblock(fs);
+ return; /* and that is it */
+ } else /* must skip over 'then' part if condition is false */
+ jf = lcode.luaK_jump(fs);
+ } else { /* regular case (not goto/break) */
+ lcode.luaK_goiftrue(ls.fs, v); /* skip over block if condition is false */
+ enterblock(fs, bl, false);
+ jf = v.f;
+ }
+
+ statlist(ls); /* 'then' part */
+ leaveblock(fs);
+ if (ls.t.token === R.TK_ELSE || ls.t.token === R.TK_ELSEIF) /* followed by 'else'/'elseif'? */
+ escapelist = lcode.luaK_concat(fs, escapelist, lcode.luaK_jump(fs)); /* must jump over it */
+ lcode.luaK_patchtohere(fs, jf);
+};
+
+const ifstat = function(ls, line) {
+ /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */
+ let fs = ls.fs;
+ let escapelist = lcode.NO_JUMP; /* exit list for finished parts */
+ test_then_block(ls, escapelist); /* IF cond THEN block */
+ while (ls.t.token === R.TK_ELSEIF)
+ test_then_block(ls, escapelist); /* ELSEIF cond THEN block */
+ if (testnext(ls, R.TK_ELSE))
+ block(ls); /* 'else' part */
+ check_match(ls, R.TK_END, R.TK_IF, line);
+ lcode.luaK_patchtohere(fs, escapelist); /* patch escape list to 'if' end */
+};
+
+const localfunc = function(ls) {
+ let b = new expdesc();
+ let fs = ls.fs;
+ new_localvar(ls, str_checkname(ls)); /* new local variable */
+ adjustlocalvars(ls, 1); /* enter its scope */
+ body(ls, b, 0, ls.linenumber); /* function created in next register */
+ /* debug information will only see the variable after this point! */
+ getlocvar(fs, b.u.info).startpc = fs.pc;
+};
+
+const localstat = function(ls) {
+ /* stat -> LOCAL NAME {',' NAME} ['=' explist] */
+ let nvars = 0;
+ let nexps;
+ let e = new expdesc();
+ do {
+ new_localvar(ls, str_checkname(ls));
+ nvars++;
+ } while (testnext(ls, ','));
+ if (testnext(ls, '='))
+ nexps = explist(ls, e);
+ else {
+ e.k = expkind.VVOID;
+ nexps = 0;
+ }
+ adjust_assign(ls, nvars, nexps, e);
+ adjustlocalvars(ls, nvars);
+};
+
+const funcname = function(ls, v) {
+ /* funcname -> NAME {fieldsel} [':' NAME] */
+ let ismethod = 0;
+ singlevar(ls, v);
+ while (ls.t.token === '.')
+ fieldsel(ls, v);
+ if (ls.t.token === ':') {
+ ismethod = 1;
+ fieldsel(ls, v);
+ }
+ return ismethod;
+};
+
+const funcstat = function(ls, line) {
+ /* funcstat -> FUNCTION funcname body */
+ let v = new expdesc();
+ let b = new expdesc();
+ llex.luaX_next(ls); /* skip FUNCTION */
+ let ismethod = funcname(ls, v);
+ body(ls, b, ismethod, line);
+ lcode.luaK_storevar(ls.fs, v, b);
+ lcode.luaK_fixline(ls.fs, line); /* definition "happens" in the first line */
+};
+
+const exprstat= function(ls) {
+ /* stat -> func | assignment */
+ let fs = ls.fs;
+ let v = new LHS_assign();
+ suffixedexp(ls, v.v);
+ if (ls.t.token === '=' || ls.t.token === ',') { /* stat . assignment ? */
+ v.prev = null;
+ assignment(ls, v, 1);
+ }
+ else { /* stat -> func */
+ check_condition(ls, v.v.k === expkind.VCALL, "syntax error");
+ lopcode.SETARG_C(lcode.getinstruction(fs, v.v), 1); /* call statement uses no results */
+ }
+};
+
+const retstat = function(ls) {
+ /* stat -> RETURN [explist] [';'] */
+ let fs = ls.fs;
+ let e = new expdesc();
+ let first, nret; /* registers with returned values */
+ if (block_follow(ls, 1) || ls.t.token === ';')
+ first = nret = 0; /* return no values */
+ else {
+ nret = explist(ls, e); /* optional return values */
+ if (hasmultret(e.k)) {
+ lcode.luaK_setmultret(fs, e);
+ if (e.k === expkind.VCALL && nret === 1) { /* tail call? */
+ lopcode.SET_OPCODE(lcode.getinstruction(fs, e), OpCodesI.OP_TAILCALL);
+ assert(lcode.getinstruction(fs, e).A === fs.nactvar);
+ }
+ first = fs.nactvar;
+ nret = lua.LUA_MULTRET; /* return all values */
+ } else {
+ if (nret === 1) /* only one single value? */
+ first = lcode.luaK_exp2anyreg(fs, e);
+ else {
+ lcode.luaK_exp2nextreg(fs, e); /* values must go to the stack */
+ first = fs.nactvar; /* return all active values */
+ assert(nret === fs.freereg - first);
+ }
+ }
+ }
+ lcode.luaK_ret(fs, first, nret);
+ testnext(ls, ';'); /* skip optional semicolon */
+};
+
+const statement = function(ls) {
+ let line = ls.linenumber; /* may be needed for error messages */
+ enterlevel(ls);
+ switch(ls.t.token) {
+ case ';': { /* stat -> ';' (empty statement) */
+ llex.luaX_next(ls); /* skip ';' */
+ break;
+ }
+ case R.TK_IF: { /* stat -> ifstat */
+ ifstat(ls, line);
+ break;
+ }
+ case R.TK_WHILE: { /* stat -> whilestat */
+ whilestat(ls, line);
+ break;
+ }
+ case R.TK_DO: { /* stat -> DO block END */
+ llex.luaX_next(ls); /* skip DO */
+ block(ls);
+ check_match(ls, R.TK_END, R.TK_DO, line);
+ break;
+ }
+ case R.TK_FOR: { /* stat -> forstat */
+ forstat(ls, line);
+ break;
+ }
+ case R.TK_REPEAT: { /* stat -> repeatstat */
+ repeatstat(ls, line);
+ break;
+ }
+ case R.TK_FUNCTION: { /* stat -> funcstat */
+ funcstat(ls, line);
+ break;
+ }
+ case R.TK_LOCAL: { /* stat -> localstat */
+ llex.luaX_next(ls); /* skip LOCAL */
+ if (testnext(ls, R.TK_FUNCTION)) /* local function? */
+ localfunc(ls);
+ else
+ localstat(ls);
+ break;
+ }
+ case R.TK_DBCOLON: { /* stat -> label */
+ llex.luaX_next(ls); /* skip double colon */
+ labelstat(ls, str_checkname(ls), line);
+ break;
+ }
+ case R.TK_RETURN: { /* skip double colon */
+ llex.luaX_next(ls); /* skip RETURN */
+ retstat(ls);
+ break;
+ }
+ case R.TK_BREAK: /* stat -> breakstat */
+ case R.TK_GOTO: { /* stat -> 'goto' NAME */
+ gotostat(ls, lcode.luaK_jump(ls.fs));
+ break;
+ }
+ default: { /* stat -> func | assignment */
+ exprstat(ls);
+ break;
+ }
+ }
+
+ assert(ls.fs.f.maxstacksize >= ls.fs.freereg && ls.fs.freereg >= ls.fs.nactvar);
+ ls.fs.freereg = ls.fs.nactvar; /* free registers */
+ leavelevel(ls);
+};
+
+/*
+** compiles the main function, which is a regular vararg function with an
+** upvalue named LUA_ENV
+*/
+const mainfunc = function(ls, fs) {
+ let bl = new BlockCnt();
+ let v = new expdesc();
+ open_func(ls, fs, bl);
+ fs.f.is_vararg = true; /* main function is always declared vararg */
+ init_exp(v, expkind.VLOCAL, 0); /* create and... */
+ newupvalue(fs, ls.envn, v); /* ...set environment upvalue */
+ llex.luaX_next(ls); /* read first token */
+ statlist(ls); /* parse main body */
+ check(ls, R.TK_EOS);
+ close_func(ls);
+};
+
+const luaY_parser = function(L, z, buff, dyd, name, firstchar) {
+ let lexstate = new llex.LexState();
+ let funcstate = new FuncState();
+ let cl = lfunc.luaF_newLclosure(L, 1); /* create main closure */
+ L.stack[L.top++] = cl;
+ lexstate.h = new Table(); /* create table for scanner */
+ L.stack[L.top++] = lexstate.h;
+ funcstate.f = cl.p = new Proto(L);
+ funcstate.f.source = new TValue(lua.CT.LUA_TLNGSTR, name);
+ lexstate.buff = buff;
+ lexstate.dyd = dyd;
+ dyd.actvar.n = dyd.gt.n = dyd.label.n = 0;
+ llex.luaX_setinput(L, lexstate, z, funcstate.f.source, firstchar);
+ mainfunc(lexstate, funcstate);
+ assert(!funcstate.prev && funcstate.nups === 1 && !lexstate.fs);
+ /* all scopes should be correctly finished */
+ assert(dyd.actvar.n === 0 && dyd.gt.n === 0 && dyd.label.n === 0);
+ L.top--; /* remove scanner's table */
+ return cl; /* closure is on the stack, too */
+};
+
+
+module.exports.Dyndata = Dyndata;
+module.exports.expkind = expkind;
+module.exports.luaY_parser = luaY_parser;
+module.exports.vkisinreg = vkisinreg; \ No newline at end of file
diff --git a/src/lstate.js b/src/lstate.js
index 9a9c9e0..1fc7765 100644
--- a/src/lstate.js
+++ b/src/lstate.js
@@ -105,7 +105,7 @@ const init_registry = function(L, g) {
/*
** open parts of the state that may cause memory-allocation errors.
-** ('g->version' != NULL flags that the state was completely build)
+** ('g->version' !== NULL flags that the state was completely build)
*/
const f_luaopen = function(L) {
let g = L.l_G;
diff --git a/src/ltablib.js b/src/ltablib.js
index fa43523..a7963d3 100644
--- a/src/ltablib.js
+++ b/src/ltablib.js
@@ -1,4 +1,3 @@
-/* jshint esversion: 6 */
"use strict";
const assert = require('assert');
diff --git a/src/lua.js b/src/lua.js
index c7d1fd7..ef5385c 100644
--- a/src/lua.js
+++ b/src/lua.js
@@ -5,6 +5,9 @@ const assert = require('assert');
const lualib = require('./lualib.js');
const luaconf = require('./luaconf.js');
+/* mark for precompiled code ('<esc>Lua') */
+const LUA_SIGNATURE = "\x1bLua";
+
const LUA_VERSION_MAJOR = "5";
const LUA_VERSION_MINOR = "3";
const LUA_VERSION_NUM = 503;
@@ -25,8 +28,10 @@ const FENGARI_RELEASE = FENGARI_VERSION + "." + FENGARI_VERSION_RELEASE;
const FENGARI_COPYRIGHT = FENGARI_RELEASE + " Copyright (C) 2017 BenoƮt Giannangeli\nBased on: " + LUA_COPYRIGHT;
const FENGARI_AUTHORS = "B. Giannangeli";
+const LUA_VERSUFFIX = "_" + LUA_VERSION_MAJOR + "_" + LUA_VERSION_MINOR;
+
const LUA_INIT_VAR = "LUA_INIT";
-const LUA_INITVARVERSION = LUA_INIT_VAR + lualib.LUA_VERSUFFIX;
+const LUA_INITVARVERSION = LUA_INIT_VAR + LUA_VERSUFFIX;
const thread_status = {
LUA_OK: 0,
@@ -52,6 +57,8 @@ const constant_types = {
LUA_NUMTAGS: 9
};
+const CT = constant_types;
+
constant_types.LUA_TSHRSTR = constant_types.LUA_TSTRING | (0 << 4); /* short strings */
constant_types.LUA_TLNGSTR = constant_types.LUA_TSTRING | (1 << 4); /* long strings */
@@ -62,6 +69,25 @@ constant_types.LUA_TLCL = constant_types.LUA_TFUNCTION | (0 << 4); /* Lua closu
constant_types.LUA_TLCF = constant_types.LUA_TFUNCTION | (1 << 4); /* light C function */
constant_types.LUA_TCCL = constant_types.LUA_TFUNCTION | (2 << 4); /* C closure */
+/*
+** Comparison and arithmetic functions
+*/
+
+const LUA_OPADD = 0; /* ORDER TM, ORDER OP */
+const LUA_OPSUB = 1;
+const LUA_OPMUL = 2;
+const LUA_OPMOD = 3;
+const LUA_OPPOW = 4;
+const LUA_OPDIV = 5;
+const LUA_OPIDIV = 6;
+const LUA_OPBAND = 7;
+const LUA_OPBOR = 8;
+const LUA_OPBXOR = 9;
+const LUA_OPSHL = 10;
+const LUA_OPSHR = 11;
+const LUA_OPUNM = 12;
+const LUA_OPBNOT = 13;
+
const LUA_OPEQ = 0;
const LUA_OPLT = 1;
const LUA_OPLE = 2;
@@ -87,25 +113,26 @@ const print_version = function() {
class lua_Debug {
constructor() {
- // int event;
- // const char *name; /* (n) */
- // const char *namewhat; /* (n) 'global', 'local', 'field', 'method' */
- // const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */
- // const char *source; /* (S) */
- // int currentline; /* (l) */
- // int linedefined; /* (S) */
- // int lastlinedefined; /* (S) */
- // unsigned char nups; /* (u) number of upvalues */
- // unsigned char nparams;/* (u) number of parameters */
- // char isvararg; /* (u) */
- // char istailcall; /* (t) */
- // char short_src[LUA_IDSIZE]; /* (S) */
+ this.event = NaN;
+ this.name = null; /* (n) */
+ this.namewhat = null; /* (n) 'global', 'local', 'field', 'method' */
+ this.what = null; /* (S) 'Lua', 'C', 'main', 'tail' */
+ this.source = null; /* (S) */
+ this.currentline = NaN; /* (l) */
+ this.linedefined = NaN; /* (S) */
+ this.lastlinedefined = NaN; /* (S) */
+ this.nups = NaN; /* (u) number of upvalues */
+ this.nparams = NaN; /* (u) number of parameters */
+ this.isvararg = NaN; /* (u) */
+ this.istailcall = NaN; /* (t) */
+ this.short_src = null; /* (S) */
/* private part */
- // struct CallInfo *i_ci; /* active function */
+ this.i_ci = null; /* active function */
}
}
+module.exports.CT = CT;
module.exports.FENGARI_AUTHORS = FENGARI_AUTHORS;
module.exports.FENGARI_COPYRIGHT = FENGARI_COPYRIGHT;
module.exports.FENGARI_RELEASE = FENGARI_RELEASE;
@@ -121,19 +148,35 @@ module.exports.LUA_INIT_VAR = LUA_INIT_VAR;
module.exports.LUA_MINSTACK = LUA_MINSTACK;
module.exports.LUA_MULTRET = -1;
module.exports.LUA_NUMTAGS = LUA_NUMTAGS;
+module.exports.LUA_OPADD = LUA_OPADD;
+module.exports.LUA_OPBAND = LUA_OPBAND;
+module.exports.LUA_OPBNOT = LUA_OPBNOT;
+module.exports.LUA_OPBOR = LUA_OPBOR;
+module.exports.LUA_OPBXOR = LUA_OPBXOR;
+module.exports.LUA_OPDIV = LUA_OPDIV;
module.exports.LUA_OPEQ = LUA_OPEQ;
+module.exports.LUA_OPIDIV = LUA_OPIDIV;
module.exports.LUA_OPLE = LUA_OPLE;
module.exports.LUA_OPLT = LUA_OPLT;
+module.exports.LUA_OPMOD = LUA_OPMOD;
+module.exports.LUA_OPMUL = LUA_OPMUL;
+module.exports.LUA_OPPOW = LUA_OPPOW;
+module.exports.LUA_OPSHL = LUA_OPSHL;
+module.exports.LUA_OPSHR = LUA_OPSHR;
+module.exports.LUA_OPSUB = LUA_OPSUB;
+module.exports.LUA_OPUNM = LUA_OPUNM;
module.exports.LUA_REGISTRYINDEX = LUA_REGISTRYINDEX;
module.exports.LUA_RELEASE = LUA_RELEASE;
module.exports.LUA_RIDX_GLOBALS = LUA_RIDX_GLOBALS;
module.exports.LUA_RIDX_LAST = LUA_RIDX_LAST;
module.exports.LUA_RIDX_MAINTHREAD = LUA_RIDX_MAINTHREAD;
+module.exports.LUA_SIGNATURE = LUA_SIGNATURE;
module.exports.LUA_VERSION = LUA_VERSION;
module.exports.LUA_VERSION_MAJOR = LUA_VERSION_MAJOR;
module.exports.LUA_VERSION_MINOR = LUA_VERSION_MINOR;
module.exports.LUA_VERSION_NUM = LUA_VERSION_NUM;
module.exports.LUA_VERSION_RELEASE = LUA_VERSION_RELEASE;
+module.exports.LUA_VERSUFFIX = LUA_VERSUFFIX;
module.exports.constant_types = constant_types;
module.exports.lua_Debug = lua_Debug;
module.exports.lua_upvalueindex = lua_upvalueindex;
diff --git a/src/lualib.js b/src/lualib.js
index d87cfbc..da9b290 100644
--- a/src/lualib.js
+++ b/src/lualib.js
@@ -2,21 +2,17 @@
"use strict";
const assert = require('assert');
-const lua = require('./lua.js');
-
-
-const LUA_VERSUFFIX = "_" + lua.LUA_VERSION_MAJOR + "_" + lua.LUA_VERSION_MINOR;
const LUA_COLIBNAME = "coroutine";
-const LUA_TABLIBNAME = "table"
-const LUA_IOLIBNAME = "io"
-const LUA_OSLIBNAME = "os"
-const LUA_STRLIBNAME = "string"
-const LUA_UTF8LIBNAME = "utf8"
-const LUA_BITLIBNAME = "bit32"
-const LUA_MATHLIBNAME = "math"
-const LUA_DBLIBNAME = "debug"
-const LUA_LOADLIBNAME = "package"
+const LUA_TABLIBNAME = "table";
+const LUA_IOLIBNAME = "io";
+const LUA_OSLIBNAME = "os";
+const LUA_STRLIBNAME = "string";
+const LUA_UTF8LIBNAME = "utf8";
+const LUA_BITLIBNAME = "bit32";
+const LUA_MATHLIBNAME = "math";
+const LUA_DBLIBNAME = "debug";
+const LUA_LOADLIBNAME = "package";
module.exports.LUA_BITLIBNAME = LUA_BITLIBNAME;
@@ -28,5 +24,4 @@ module.exports.LUA_MATHLIBNAME = LUA_MATHLIBNAME;
module.exports.LUA_OSLIBNAME = LUA_OSLIBNAME;
module.exports.LUA_STRLIBNAME = LUA_STRLIBNAME;
module.exports.LUA_TABLIBNAME = LUA_TABLIBNAME;
-module.exports.LUA_UTF8LIBNAME = LUA_UTF8LIBNAME;
-module.exports.LUA_VERSUFFIX = LUA_VERSUFFIX; \ No newline at end of file
+module.exports.LUA_UTF8LIBNAME = LUA_UTF8LIBNAME; \ No newline at end of file
diff --git a/src/lundump.js b/src/lundump.js
index b79e6ea..5a6d8c4 100644
--- a/src/lundump.js
+++ b/src/lundump.js
@@ -5,6 +5,7 @@ const DataView = require('buffer-dataview');
const fs = require('fs');
const assert = require('assert');
+const lua = require('./lua.js');
const LClosure = require('./lobject.js').LClosure;
const TValue = require('./lobject.js').TValue;
const Proto = require('./lfunc.js').Proto;
@@ -256,7 +257,7 @@ class BytecodeParser {
}
checkHeader() {
- if (this.readString(4) !== "\x1bLua")
+ if (this.readString(4) !== lua.LUA_SIGNATURE)
throw new Error("bad LUA_SIGNATURE, expected '<esc>Lua'");
if (this.readByte() !== 0x53)
diff --git a/src/lvm.js b/src/lvm.js
index d987cc1..0ff433e 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -792,6 +792,10 @@ const luaV_equalobj = function(L, t1, t2) {
return L.stack[L.top].l_isfalse() ? 0 : 1;
};
+const luaV_rawequalobj = function(t1, t2) {
+ return luaV_equalobj(null, t1, t2);
+};
+
const forlimit = function(obj, step) {
let stopnow = false;
let ilimit = luaV_tointeger(obj, step < 0 ? 2 : 1);
@@ -818,9 +822,9 @@ const forlimit = function(obj, step) {
/*
** try to convert a value to an integer, rounding according to 'mode':
-** mode == 0: accepts only integral values
-** mode == 1: takes the floor of the number
-** mode == 2: takes the ceil of the number
+** mode === 0: accepts only integral values
+** mode === 1: takes the floor of the number
+** mode === 2: takes the ceil of the number
*/
const luaV_tointeger = function(obj, mode) {
if (obj.ttisfloat()) {
@@ -831,7 +835,7 @@ const luaV_tointeger = function(obj, mode) {
if (mode === 0)
return false; /* fails if mode demands integral value */
else if (mode > 1) /* needs ceil? */
- f += 1; /* convert floor to ceil (remember: n != f) */
+ f += 1; /* convert floor to ceil (remember: n !== f) */
}
return f|0;
@@ -1072,29 +1076,30 @@ const luaV_finishset = function(L, t, key, val, slot, recur) {
}
-module.exports.dojump = dojump;
-module.exports.donextjump = donextjump;
-module.exports.forlimit = forlimit;
-module.exports.gettable = gettable;
-module.exports.l_strcmp = l_strcmp;
-module.exports.LEintfloat = LEintfloat;
-module.exports.LEnum = LEnum;
-module.exports.LTintfloat = LTintfloat;
-module.exports.LTnum = LTnum;
-module.exports.luaV_concat = luaV_concat;
-module.exports.luaV_equalobj = luaV_equalobj;
-module.exports.luaV_execute = luaV_execute;
-module.exports.luaV_finishOp = luaV_finishOp;
-module.exports.luaV_finishset = luaV_finishset;
-module.exports.luaV_lessequal = luaV_lessequal;
-module.exports.luaV_lessthan = luaV_lessthan;
-module.exports.luaV_objlen = luaV_objlen;
-module.exports.luaV_tointeger = luaV_tointeger;
-module.exports.RA = RA;
-module.exports.RB = RB;
-module.exports.RC = RC;
-module.exports.RKB = RKB;
-module.exports.RKC = RKC;
-module.exports.settable = settable;
-module.exports.tointeger = tointeger;
-module.exports.tonumber = tonumber; \ No newline at end of file
+module.exports.LEintfloat = LEintfloat;
+module.exports.LEnum = LEnum;
+module.exports.LTintfloat = LTintfloat;
+module.exports.LTnum = LTnum;
+module.exports.RA = RA;
+module.exports.RB = RB;
+module.exports.RC = RC;
+module.exports.RKB = RKB;
+module.exports.RKC = RKC;
+module.exports.dojump = dojump;
+module.exports.donextjump = donextjump;
+module.exports.forlimit = forlimit;
+module.exports.gettable = gettable;
+module.exports.l_strcmp = l_strcmp;
+module.exports.luaV_concat = luaV_concat;
+module.exports.luaV_equalobj = luaV_equalobj;
+module.exports.luaV_execute = luaV_execute;
+module.exports.luaV_finishOp = luaV_finishOp;
+module.exports.luaV_finishset = luaV_finishset;
+module.exports.luaV_lessequal = luaV_lessequal;
+module.exports.luaV_lessthan = luaV_lessthan;
+module.exports.luaV_objlen = luaV_objlen;
+module.exports.luaV_rawequalobj = luaV_rawequalobj;
+module.exports.luaV_tointeger = luaV_tointeger;
+module.exports.settable = settable;
+module.exports.tointeger = tointeger;
+module.exports.tonumber = tonumber; \ No newline at end of file