diff options
author | Benoit Giannangeli <benoit.giannangeli@boursorama.fr> | 2017-02-28 11:56:00 +0100 |
---|---|---|
committer | Benoit Giannangeli <benoit.giannangeli@boursorama.fr> | 2017-02-28 15:49:13 +0100 |
commit | dfac155a3f8b650222fa784303e599f4e50abe67 (patch) | |
tree | e6eb2b1040e79991fbd2d6b4ff0a92ededc36275 /src/lparser.js | |
parent | 8d576081a53ee1a866491b423c5373951aa6a7c4 (diff) | |
download | fengari-dfac155a3f8b650222fa784303e599f4e50abe67.tar.gz fengari-dfac155a3f8b650222fa784303e599f4e50abe67.tar.bz2 fengari-dfac155a3f8b650222fa784303e599f4e50abe67.zip |
grammar
Diffstat (limited to 'src/lparser.js')
-rw-r--r-- | src/lparser.js | 685 |
1 files changed, 685 insertions, 0 deletions
diff --git a/src/lparser.js b/src/lparser.js index a7e4209..154310b 100644 --- a/src/lparser.js +++ b/src/lparser.js @@ -61,6 +61,10 @@ const expkind = { VVARARG: 14 /* vararg expression; info = instruction pc */ }; +const vkisvar = function(k) { + return expkind.VLOCAL <= k && k <= expkind.VINDEXED; +}; + class expdesc { constructor() { this.k = NaN; @@ -174,18 +178,53 @@ 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].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++].idx = reg; +}; + +const new_localvarliteral = function(ls, name) { + new_localvar(ls, new TValue(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 === name) + return i; + } + return -1; /* not found */ +}; + const newupvalue = function(fs, name, v) { let f = fs.f; checklimit(fs, fs.nups + 1, lfunc.MAXUPVAL, "upvalues"); @@ -196,6 +235,86 @@ const newupvalue = function(fs, name, v) { return fs.nups++; }; +const searchvar = function(fs, n) { + for (let i = fs.nactvar - 1; i >= 0; i--) { + if (n === getlocvar(fs, i).varname) + 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; @@ -321,6 +440,27 @@ const undefgoto = function(ls, gt) { 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) { this.f = new Proto(); fs.prev = ls.fs; /* linked list of funcstates */ @@ -402,6 +542,16 @@ const statlist = function(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 '[' */ @@ -523,12 +673,176 @@ const constructor = function(ls, t) { 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 */ @@ -668,6 +982,278 @@ 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(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 === ll.arr[i].name) { + 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(); @@ -715,6 +1301,105 @@ const ifstat = function(ls, 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); |