From 2d1df4714e2736dbde7855ddcd76b4c1de822fa5 Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Mon, 23 Jan 2012 21:58:02 +1000 Subject: Added a big bunch of example lua scripts for testing the speed of lua compiling. --- .../testLua/yueliang-0.4.1/orig-5.1.3/lparser.lua | 1747 ++++++++++++++++++++ 1 file changed, 1747 insertions(+) create mode 100644 LuaSL/testLua/yueliang-0.4.1/orig-5.1.3/lparser.lua (limited to 'LuaSL/testLua/yueliang-0.4.1/orig-5.1.3/lparser.lua') diff --git a/LuaSL/testLua/yueliang-0.4.1/orig-5.1.3/lparser.lua b/LuaSL/testLua/yueliang-0.4.1/orig-5.1.3/lparser.lua new file mode 100644 index 0000000..2535056 --- /dev/null +++ b/LuaSL/testLua/yueliang-0.4.1/orig-5.1.3/lparser.lua @@ -0,0 +1,1747 @@ +--[[-------------------------------------------------------------------- + + lparser.lua + Lua 5 parser in Lua + This file is part of Yueliang. + + Copyright (c) 2005-2007 Kein-Hong Man + The COPYRIGHT file describes the conditions + under which this software may be distributed. + + See the ChangeLog for more information. + +----------------------------------------------------------------------]] + +--[[-------------------------------------------------------------------- +-- Notes: +-- * some unused C code that were not converted are kept as comments +-- * LUA_COMPAT_VARARG option changed into a comment block +-- * for value/size specific code added, look for 'NOTE: ' +-- +-- Not implemented: +-- * luaX_newstring not needed by this Lua implementation +-- * luaG_checkcode() in lua_assert is not currently implemented +-- +-- Added: +-- * some constants added from various header files +-- * luaY.LUA_QS used in error_expected, check_match (from luaconf.h) +-- * luaY:LUA_QL needed for error messages (from luaconf.h) +-- * luaY:growvector (from lmem.h) -- skeleton only, limit checking +-- * luaY.SHRT_MAX (from ) for registerlocalvar +-- * luaY:newproto (from lfunc.c) +-- * luaY:int2fb (from lobject.c) +-- * NOTE: HASARG_MASK, for implementing a VARARG_HASARG bit operation +-- * NOTE: value-specific code for VARARG_NEEDSARG to replace a bitop +-- +-- Changed in 5.1.x: +-- * various code changes are not detailed... +-- * names of constants may have changed, e.g. added a LUAI_ prefix +-- * struct expkind: added VKNUM, VVARARG; VCALL's info changed? +-- * struct expdesc: added nval +-- * struct FuncState: upvalues data type changed to upvaldesc +-- * macro hasmultret is new +-- * function checklimit moved to parser from lexer +-- * functions anchor_token, errorlimit, checknext are new +-- * checknext is new, equivalent to 5.0.x's check, see check too +-- * luaY:next and luaY:lookahead moved to lexer +-- * break keyword no longer skipped in luaY:breakstat +-- * function new_localvarstr replaced by new_localvarliteral +-- * registerlocalvar limits local variables to SHRT_MAX +-- * create_local deleted, new_localvarliteral used instead +-- * constant LUAI_MAXUPVALUES increased to 60 +-- * constants MAXPARAMS, LUA_MAXPARSERLEVEL, MAXSTACK removed +-- * function interface changed: singlevaraux, singlevar +-- * enterlevel and leavelevel uses nCcalls to track call depth +-- * added a name argument to main entry function, luaY:parser +-- * function luaY_index changed to yindex +-- * luaY:int2fb()'s table size encoding format has been changed +-- * luaY:log2() no longer needed for table constructors +-- * function code_params deleted, functionality folded in parlist +-- * vararg flags handling (is_vararg) changes; also see VARARG_* +-- * LUA_COMPATUPSYNTAX section for old-style upvalues removed +-- * repeatstat() calls chunk() instead of block() +-- * function interface changed: cond, test_then_block +-- * while statement implementation considerably simplified; MAXEXPWHILE +-- and EXTRAEXP no longer required, no limits to the complexity of a +-- while condition +-- * repeat, forbody statement implementation has major changes, +-- mostly due to new scoping behaviour of local variables +-- * OPR_MULT renamed to OPR_MUL +----------------------------------------------------------------------]] + +--requires luaP, luaX, luaK +luaY = {} + +--[[-------------------------------------------------------------------- +-- Expression descriptor +-- * expkind changed to string constants; luaY:assignment was the only +-- function to use a relational operator with this enumeration +-- VVOID -- no value +-- VNIL -- no value +-- VTRUE -- no value +-- VFALSE -- no value +-- VK -- info = index of constant in 'k' +-- VKNUM -- nval = numerical value +-- VLOCAL -- info = local register +-- VUPVAL, -- info = index of upvalue in 'upvalues' +-- VGLOBAL -- info = index of table; aux = index of global name in 'k' +-- VINDEXED -- info = table register; aux = index register (or 'k') +-- VJMP -- info = instruction pc +-- VRELOCABLE -- info = instruction pc +-- VNONRELOC -- info = result register +-- VCALL -- info = instruction pc +-- VVARARG -- info = instruction pc +} ----------------------------------------------------------------------]] + +--[[-------------------------------------------------------------------- +-- * expdesc in Lua 5.1.x has a union u and another struct s; this Lua +-- implementation ignores all instances of u and s usage +-- struct expdesc: +-- k -- (enum: expkind) +-- info, aux -- (int, int) +-- nval -- (lua_Number) +-- t -- patch list of 'exit when true' +-- f -- patch list of 'exit when false' +----------------------------------------------------------------------]] + +--[[-------------------------------------------------------------------- +-- struct upvaldesc: +-- k -- (lu_byte) +-- info -- (lu_byte) +----------------------------------------------------------------------]] + +--[[-------------------------------------------------------------------- +-- state needed to generate code for a given function +-- struct FuncState: +-- f -- current function header (table: Proto) +-- h -- table to find (and reuse) elements in 'k' (table: Table) +-- prev -- enclosing function (table: FuncState) +-- ls -- lexical state (table: LexState) +-- L -- copy of the Lua state (table: lua_State) +-- bl -- chain of current blocks (table: BlockCnt) +-- pc -- next position to code (equivalent to 'ncode') +-- lasttarget -- 'pc' of last 'jump target' +-- jpc -- list of pending jumps to 'pc' +-- freereg -- first free register +-- nk -- number of elements in 'k' +-- np -- number of elements in 'p' +-- nlocvars -- number of elements in 'locvars' +-- nactvar -- number of active local variables +-- upvalues[LUAI_MAXUPVALUES] -- upvalues (table: upvaldesc) +-- actvar[LUAI_MAXVARS] -- declared-variable stack +----------------------------------------------------------------------]] + +------------------------------------------------------------------------ +-- constants used by parser +-- * picks up duplicate values from luaX if required +------------------------------------------------------------------------ +luaY.LUA_QS = luaX.LUA_QS or "'%s'" -- (from luaconf.h) + +luaY.SHRT_MAX = 32767 -- (from ) +luaY.LUAI_MAXVARS = 200 -- (luaconf.h) +luaY.LUAI_MAXUPVALUES = 60 -- (luaconf.h) +luaY.MAX_INT = luaX.MAX_INT or 2147483645 -- (from llimits.h) + -- * INT_MAX-2 for 32-bit systems +luaY.LUAI_MAXCCALLS = 200 -- (from luaconf.h) + +luaY.VARARG_HASARG = 1 -- (from lobject.h) +-- NOTE: HASARG_MASK is value-specific +luaY.HASARG_MASK = 2 -- this was added for a bitop in parlist() +luaY.VARARG_ISVARARG = 2 +-- NOTE: there is some value-specific code that involves VARARG_NEEDSARG +luaY.VARARG_NEEDSARG = 4 + +luaY.LUA_MULTRET = -1 -- (lua.h) + +--[[-------------------------------------------------------------------- +-- other functions +----------------------------------------------------------------------]] + +------------------------------------------------------------------------ +-- LUA_QL describes how error messages quote program elements. +-- CHANGE it if you want a different appearance. (from luaconf.h) +------------------------------------------------------------------------ +function luaY:LUA_QL(x) + return "'"..x.."'" +end + +------------------------------------------------------------------------ +-- this is a stripped-down luaM_growvector (from lmem.h) which is a +-- macro based on luaM_growaux (in lmem.c); all the following does is +-- reproduce the size limit checking logic of the original function +-- so that error behaviour is identical; all arguments preserved for +-- convenience, even those which are unused +-- * set the t field to nil, since this originally does a sizeof(t) +-- * size (originally a pointer) is never updated, their final values +-- are set by luaY:close_func(), so overall things should still work +------------------------------------------------------------------------ +function luaY:growvector(L, v, nelems, size, t, limit, e) + if nelems >= limit then + error(e) -- was luaG_runerror + end +end + +------------------------------------------------------------------------ +-- initialize a new function prototype structure (from lfunc.c) +-- * used only in open_func() +------------------------------------------------------------------------ +function luaY:newproto(L) + local f = {} -- Proto + -- luaC_link(L, obj2gco(f), LUA_TPROTO); /* GC */ + f.k = {} + f.sizek = 0 + f.p = {} + f.sizep = 0 + f.code = {} + f.sizecode = 0 + f.sizelineinfo = 0 + f.sizeupvalues = 0 + f.nups = 0 + f.upvalues = {} + f.numparams = 0 + f.is_vararg = 0 + f.maxstacksize = 0 + f.lineinfo = {} + f.sizelocvars = 0 + f.locvars = {} + f.lineDefined = 0 + f.lastlinedefined = 0 + f.source = nil + return f +end + +------------------------------------------------------------------------ +-- 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. +------------------------------------------------------------------------ +function luaY:int2fb(x) + local e = 0 -- exponent + while x >= 16 do + x = math.floor((x + 1) / 2) + e = e + 1 + end + if x < 8 then + return x + else + return ((e + 1) * 8) + (x - 8) + end +end + +--[[-------------------------------------------------------------------- +-- parser functions +----------------------------------------------------------------------]] + +------------------------------------------------------------------------ +-- true of the kind of expression produces multiple return values +------------------------------------------------------------------------ +function luaY:hasmultret(k) + return k == "VCALL" or k == "VVARARG" +end + +------------------------------------------------------------------------ +-- convenience function to access active local i, returns entry +------------------------------------------------------------------------ +function luaY:getlocvar(fs, i) + return fs.f.locvars[ fs.actvar[i] ] +end + +------------------------------------------------------------------------ +-- check a limit, string m provided as an error message +------------------------------------------------------------------------ +function luaY:checklimit(fs, v, l, m) + if v > l then self:errorlimit(fs, l, m) end +end + +--[[-------------------------------------------------------------------- +-- nodes for block list (list of active blocks) +-- struct BlockCnt: +-- previous -- chain (table: BlockCnt) +-- breaklist -- list of jumps out of this loop +-- nactvar -- # active local variables outside the breakable structure +-- upval -- true if some variable in the block is an upvalue (boolean) +-- isbreakable -- true if 'block' is a loop (boolean) +----------------------------------------------------------------------]] + +------------------------------------------------------------------------ +-- prototypes for recursive non-terminal functions +------------------------------------------------------------------------ +-- prototypes deleted; not required in Lua + +------------------------------------------------------------------------ +-- reanchor if last token is has a constant string, see close_func() +-- * used only in close_func() +------------------------------------------------------------------------ +function luaY:anchor_token(ls) + if ls.t.token == "TK_NAME" or ls.t.token == "TK_STRING" then + -- not relevant to Lua implementation of parser + -- local ts = ls.t.seminfo + -- luaX_newstring(ls, getstr(ts), ts->tsv.len); /* C */ + end +end + +------------------------------------------------------------------------ +-- throws a syntax error if token expected is not there +------------------------------------------------------------------------ +function luaY:error_expected(ls, token) + luaX:syntaxerror(ls, + string.format(self.LUA_QS.." expected", luaX:token2str(ls, token))) +end + +------------------------------------------------------------------------ +-- prepares error message for display, for limits exceeded +-- * used only in checklimit() +------------------------------------------------------------------------ +function luaY:errorlimit(fs, limit, what) + local msg = (fs.f.linedefined == 0) and + string.format("main function has more than %d %s", limit, what) or + string.format("function at line %d has more than %d %s", + fs.f.linedefined, limit, what) + luaX:lexerror(fs.ls, msg, 0) +end + +------------------------------------------------------------------------ +-- tests for a token, returns outcome +-- * return value changed to boolean +------------------------------------------------------------------------ +function luaY:testnext(ls, c) + if ls.t.token == c then + luaX:next(ls) + return true + else + return false + end +end + +------------------------------------------------------------------------ +-- check for existence of a token, throws error if not found +------------------------------------------------------------------------ +function luaY:check(ls, c) + if ls.t.token ~= c then + self:error_expected(ls, c) + end +end + +------------------------------------------------------------------------ +-- verify existence of a token, then skip it +------------------------------------------------------------------------ +function luaY:checknext(ls, c) + self:check(ls, c) + luaX:next(ls) +end + +------------------------------------------------------------------------ +-- throws error if condition not matched +------------------------------------------------------------------------ +function luaY:check_condition(ls, c, msg) + if not c then luaX:syntaxerror(ls, msg) end +end + +------------------------------------------------------------------------ +-- verifies token conditions are met or else throw error +------------------------------------------------------------------------ +function luaY:check_match(ls, what, who, where) + if not self:testnext(ls, what) then + if where == ls.linenumber then + self:error_expected(ls, what) + else + luaX:syntaxerror(ls, string.format( + self.LUA_QS.." expected (to close "..self.LUA_QS.." at line %d)", + luaX:token2str(ls, what), luaX:token2str(ls, who), where)) + end + end +end + +------------------------------------------------------------------------ +-- expect that token is a name, return the name +------------------------------------------------------------------------ +function luaY:str_checkname(ls) + self:check(ls, "TK_NAME") + local ts = ls.t.seminfo + luaX:next(ls) + return ts +end + +------------------------------------------------------------------------ +-- initialize a struct expdesc, expression description data structure +------------------------------------------------------------------------ +function luaY:init_exp(e, k, i) + e.f, e.t = luaK.NO_JUMP, luaK.NO_JUMP + e.k = k + e.info = i +end + +------------------------------------------------------------------------ +-- adds given string s in string pool, sets e as VK +------------------------------------------------------------------------ +function luaY:codestring(ls, e, s) + self:init_exp(e, "VK", luaK:stringK(ls.fs, s)) +end + +------------------------------------------------------------------------ +-- consume a name token, adds it to string pool, sets e as VK +------------------------------------------------------------------------ +function luaY:checkname(ls, e) + self:codestring(ls, e, self:str_checkname(ls)) +end + +------------------------------------------------------------------------ +-- creates struct entry for a local variable +-- * used only in new_localvar() +------------------------------------------------------------------------ +function luaY:registerlocalvar(ls, varname) + local fs = ls.fs + local f = fs.f + self:growvector(ls.L, f.locvars, fs.nlocvars, f.sizelocvars, + nil, self.SHRT_MAX, "too many local variables") + -- loop to initialize empty f.locvar positions not required + f.locvars[fs.nlocvars] = {} -- LocVar + f.locvars[fs.nlocvars].varname = varname + -- luaC_objbarrier(ls.L, f, varname) /* GC */ + local nlocvars = fs.nlocvars + fs.nlocvars = fs.nlocvars + 1 + return nlocvars +end + +------------------------------------------------------------------------ +-- creates a new local variable given a name and an offset from nactvar +-- * used in fornum(), forlist(), parlist(), body() +------------------------------------------------------------------------ +function luaY:new_localvarliteral(ls, v, n) + self:new_localvar(ls, v, n) +end + +------------------------------------------------------------------------ +-- register a local variable, set in active variable list +------------------------------------------------------------------------ +function luaY:new_localvar(ls, name, n) + local fs = ls.fs + self:checklimit(fs, fs.nactvar + n + 1, self.LUAI_MAXVARS, "local variables") + fs.actvar[fs.nactvar + n] = self:registerlocalvar(ls, name) +end + +------------------------------------------------------------------------ +-- adds nvars number of new local variables, set debug information +------------------------------------------------------------------------ +function luaY:adjustlocalvars(ls, nvars) + local fs = ls.fs + fs.nactvar = fs.nactvar + nvars + for i = nvars, 1, -1 do + self:getlocvar(fs, fs.nactvar - i).startpc = fs.pc + end +end + +------------------------------------------------------------------------ +-- removes a number of locals, set debug information +------------------------------------------------------------------------ +function luaY:removevars(ls, tolevel) + local fs = ls.fs + while fs.nactvar > tolevel do + fs.nactvar = fs.nactvar - 1 + self:getlocvar(fs, fs.nactvar).endpc = fs.pc + end +end + +------------------------------------------------------------------------ +-- returns an existing upvalue index based on the given name, or +-- creates a new upvalue struct entry and returns the new index +-- * used only in singlevaraux() +------------------------------------------------------------------------ +function luaY:indexupvalue(fs, name, v) + local f = fs.f + for i = 0, f.nups - 1 do + if fs.upvalues[i].k == v.k and fs.upvalues[i].info == v.info then + lua_assert(f.upvalues[i] == name) + return i + end + end + -- new one + self:checklimit(fs, f.nups + 1, self.LUAI_MAXUPVALUES, "upvalues") + self:growvector(fs.L, f.upvalues, f.nups, f.sizeupvalues, + nil, self.MAX_INT, "") + -- loop to initialize empty f.upvalues positions not required + f.upvalues[f.nups] = name + -- luaC_objbarrier(fs->L, f, name); /* GC */ + lua_assert(v.k == "VLOCAL" or v.k == "VUPVAL") + -- this is a partial copy; only k & info fields used + fs.upvalues[f.nups] = { k = v.k, info = v.info } + local nups = f.nups + f.nups = f.nups + 1 + return nups +end + +------------------------------------------------------------------------ +-- search the local variable namespace of the given fs for a match +-- * used only in singlevaraux() +------------------------------------------------------------------------ +function luaY:searchvar(fs, n) + for i = fs.nactvar - 1, 0, -1 do + if n == self:getlocvar(fs, i).varname then + return i + end + end + return -1 -- not found +end + +------------------------------------------------------------------------ +-- * mark upvalue flags in function states up to a given level +-- * used only in singlevaraux() +------------------------------------------------------------------------ +function luaY:markupval(fs, level) + local bl = fs.bl + while bl and bl.nactvar > level do bl = bl.previous end + if bl then bl.upval = true end +end + +------------------------------------------------------------------------ +-- handle locals, globals and upvalues and related processing +-- * search mechanism is recursive, calls itself to search parents +-- * used only in singlevar() +------------------------------------------------------------------------ +function luaY:singlevaraux(fs, n, var, base) + if fs == nil then -- no more levels? + self:init_exp(var, "VGLOBAL", luaP.NO_REG) -- default is global variable + return "VGLOBAL" + else + local v = self:searchvar(fs, n) -- look up at current level + if v >= 0 then + self:init_exp(var, "VLOCAL", v) + if base == 0 then + self:markupval(fs, v) -- local will be used as an upval + end + return "VLOCAL" + else -- not found at current level; try upper one + if self:singlevaraux(fs.prev, n, var, 0) == "VGLOBAL" then + return "VGLOBAL" + end + var.info = self:indexupvalue(fs, n, var) -- else was LOCAL or UPVAL + var.k = "VUPVAL" -- upvalue in this level + return "VUPVAL" + end--if v + end--if fs +end + +------------------------------------------------------------------------ +-- consume a name token, creates a variable (global|local|upvalue) +-- * used in prefixexp(), funcname() +------------------------------------------------------------------------ +function luaY:singlevar(ls, var) + local varname = self:str_checkname(ls) + local fs = ls.fs + if self:singlevaraux(fs, varname, var, 1) == "VGLOBAL" then + var.info = luaK:stringK(fs, varname) -- info points to global name + end +end + +------------------------------------------------------------------------ +-- adjust RHS to match LHS in an assignment +-- * used in assignment(), forlist(), localstat() +------------------------------------------------------------------------ +function luaY:adjust_assign(ls, nvars, nexps, e) + local fs = ls.fs + local extra = nvars - nexps + if self:hasmultret(e.k) then + extra = extra + 1 -- includes call itself + if extra <= 0 then extra = 0 end + luaK:setreturns(fs, e, extra) -- last exp. provides the difference + if extra > 1 then luaK:reserveregs(fs, extra - 1) end + else + if e.k ~= "VVOID" then luaK:exp2nextreg(fs, e) end -- close last expression + if extra > 0 then + local reg = fs.freereg + luaK:reserveregs(fs, extra) + luaK:_nil(fs, reg, extra) + end + end +end + +------------------------------------------------------------------------ +-- tracks and limits parsing depth, assert check at end of parsing +------------------------------------------------------------------------ +function luaY:enterlevel(ls) + ls.L.nCcalls = ls.L.nCcalls + 1 + if ls.L.nCcalls > self.LUAI_MAXCCALLS then + luaX:lexerror(ls, "chunk has too many syntax levels", 0) + end +end + +------------------------------------------------------------------------ +-- tracks parsing depth, a pair with luaY:enterlevel() +------------------------------------------------------------------------ +function luaY:leavelevel(ls) + ls.L.nCcalls = ls.L.nCcalls - 1 +end + +------------------------------------------------------------------------ +-- enters a code unit, initializes elements +------------------------------------------------------------------------ +function luaY:enterblock(fs, bl, isbreakable) + bl.breaklist = luaK.NO_JUMP + bl.isbreakable = isbreakable + bl.nactvar = fs.nactvar + bl.upval = false + bl.previous = fs.bl + fs.bl = bl + lua_assert(fs.freereg == fs.nactvar) +end + +------------------------------------------------------------------------ +-- leaves a code unit, close any upvalues +------------------------------------------------------------------------ +function luaY:leaveblock(fs) + local bl = fs.bl + fs.bl = bl.previous + self:removevars(fs.ls, bl.nactvar) + if bl.upval then + luaK:codeABC(fs, "OP_CLOSE", bl.nactvar, 0, 0) + end + -- a block either controls scope or breaks (never both) + lua_assert(not bl.isbreakable or not bl.upval) + lua_assert(bl.nactvar == fs.nactvar) + fs.freereg = fs.nactvar -- free registers + luaK:patchtohere(fs, bl.breaklist) +end + +------------------------------------------------------------------------ +-- implement the instantiation of a function prototype, append list of +-- upvalues after the instantiation instruction +-- * used only in body() +------------------------------------------------------------------------ +function luaY:pushclosure(ls, func, v) + local fs = ls.fs + local f = fs.f + self:growvector(ls.L, f.p, fs.np, f.sizep, nil, + luaP.MAXARG_Bx, "constant table overflow") + -- loop to initialize empty f.p positions not required + f.p[fs.np] = func.f + fs.np = fs.np + 1 + -- luaC_objbarrier(ls->L, f, func->f); /* C */ + self:init_exp(v, "VRELOCABLE", luaK:codeABx(fs, "OP_CLOSURE", 0, fs.np - 1)) + for i = 0, func.f.nups - 1 do + local o = (func.upvalues[i].k == "VLOCAL") and "OP_MOVE" or "OP_GETUPVAL" + luaK:codeABC(fs, o, 0, func.upvalues[i].info, 0) + end +end + +------------------------------------------------------------------------ +-- opening of a function +------------------------------------------------------------------------ +function luaY:open_func(ls, fs) + local L = ls.L + local f = self:newproto(ls.L) + fs.f = f + fs.prev = ls.fs -- linked list of funcstates + fs.ls = ls + fs.L = L + ls.fs = fs + fs.pc = 0 + fs.lasttarget = -1 + fs.jpc = luaK.NO_JUMP + fs.freereg = 0 + fs.nk = 0 + fs.np = 0 + fs.nlocvars = 0 + fs.nactvar = 0 + fs.bl = nil + f.source = ls.source + f.maxstacksize = 2 -- registers 0/1 are always valid + fs.h = {} -- constant table; was luaH_new call + -- anchor table of constants and prototype (to avoid being collected) + -- sethvalue2s(L, L->top, fs->h); incr_top(L); /* C */ + -- setptvalue2s(L, L->top, f); incr_top(L); +end + +------------------------------------------------------------------------ +-- closing of a function +------------------------------------------------------------------------ +function luaY:close_func(ls) + local L = ls.L + local fs = ls.fs + local f = fs.f + self:removevars(ls, 0) + luaK:ret(fs, 0, 0) -- final return + -- luaM_reallocvector deleted for f->code, f->lineinfo, f->k, f->p, + -- f->locvars, f->upvalues; not required for Lua table arrays + f.sizecode = fs.pc + f.sizelineinfo = fs.pc + f.sizek = fs.nk + f.sizep = fs.np + f.sizelocvars = fs.nlocvars + f.sizeupvalues = f.nups + --lua_assert(luaG_checkcode(f)) -- currently not implemented + lua_assert(fs.bl == nil) + ls.fs = fs.prev + -- the following is not required for this implementation; kept here + -- for completeness + -- L->top -= 2; /* remove table and prototype from the stack */ + -- last token read was anchored in defunct function; must reanchor it + if fs then self:anchor_token(ls) end +end + +------------------------------------------------------------------------ +-- parser initialization function +-- * note additional sub-tables needed for LexState, FuncState +------------------------------------------------------------------------ +function luaY:parser(L, z, buff, name) + local lexstate = {} -- LexState + lexstate.t = {} + lexstate.lookahead = {} + local funcstate = {} -- FuncState + funcstate.upvalues = {} + funcstate.actvar = {} + -- the following nCcalls initialization added for convenience + L.nCcalls = 0 + lexstate.buff = buff + luaX:setinput(L, lexstate, z, name) + self:open_func(lexstate, funcstate) + funcstate.f.is_vararg = self.VARARG_ISVARARG -- main func. is always vararg + luaX:next(lexstate) -- read first token + self:chunk(lexstate) + self:check(lexstate, "TK_EOS") + self:close_func(lexstate) + lua_assert(funcstate.prev == nil) + lua_assert(funcstate.f.nups == 0) + lua_assert(lexstate.fs == nil) + return funcstate.f +end + +--[[-------------------------------------------------------------------- +-- GRAMMAR RULES +----------------------------------------------------------------------]] + +------------------------------------------------------------------------ +-- parse a function name suffix, for function call specifications +-- * used in primaryexp(), funcname() +------------------------------------------------------------------------ +function luaY:field(ls, v) + -- field -> ['.' | ':'] NAME + local fs = ls.fs + local key = {} -- expdesc + luaK:exp2anyreg(fs, v) + luaX:next(ls) -- skip the dot or colon + self:checkname(ls, key) + luaK:indexed(fs, v, key) +end + +------------------------------------------------------------------------ +-- parse a table indexing suffix, for constructors, expressions +-- * used in recfield(), primaryexp() +------------------------------------------------------------------------ +function luaY:yindex(ls, v) + -- index -> '[' expr ']' + luaX:next(ls) -- skip the '[' + self:expr(ls, v) + luaK:exp2val(ls.fs, v) + self:checknext(ls, "]") +end + +--[[-------------------------------------------------------------------- +-- Rules for Constructors +----------------------------------------------------------------------]] + +--[[-------------------------------------------------------------------- +-- struct ConsControl: +-- v -- last list item read (table: struct expdesc) +-- t -- table descriptor (table: struct expdesc) +-- nh -- total number of 'record' elements +-- na -- total number of array elements +-- tostore -- number of array elements pending to be stored +----------------------------------------------------------------------]] + +------------------------------------------------------------------------ +-- parse a table record (hash) field +-- * used in constructor() +------------------------------------------------------------------------ +function luaY:recfield(ls, cc) + -- recfield -> (NAME | '['exp1']') = exp1 + local fs = ls.fs + local reg = ls.fs.freereg + local key, val = {}, {} -- expdesc + if ls.t.token == "TK_NAME" then + self:checklimit(fs, cc.nh, self.MAX_INT, "items in a constructor") + self:checkname(ls, key) + else -- ls->t.token == '[' + self:yindex(ls, key) + end + cc.nh = cc.nh + 1 + self:checknext(ls, "=") + local rkkey = luaK:exp2RK(fs, key) + self:expr(ls, val) + luaK:codeABC(fs, "OP_SETTABLE", cc.t.info, rkkey, luaK:exp2RK(fs, val)) + fs.freereg = reg -- free registers +end + +------------------------------------------------------------------------ +-- emit a set list instruction if enough elements (LFIELDS_PER_FLUSH) +-- * used in constructor() +------------------------------------------------------------------------ +function luaY:closelistfield(fs, cc) + if cc.v.k == "VVOID" then return end -- there is no list item + luaK:exp2nextreg(fs, cc.v) + cc.v.k = "VVOID" + if cc.tostore == luaP.LFIELDS_PER_FLUSH then + luaK:setlist(fs, cc.t.info, cc.na, cc.tostore) -- flush + cc.tostore = 0 -- no more items pending + end +end + +------------------------------------------------------------------------ +-- emit a set list instruction at the end of parsing list constructor +-- * used in constructor() +------------------------------------------------------------------------ +function luaY:lastlistfield(fs, cc) + if cc.tostore == 0 then return end + if self:hasmultret(cc.v.k) then + luaK:setmultret(fs, cc.v) + luaK:setlist(fs, cc.t.info, cc.na, self.LUA_MULTRET) + cc.na = cc.na - 1 -- do not count last expression (unknown number of elements) + else + if cc.v.k ~= "VVOID" then + luaK:exp2nextreg(fs, cc.v) + end + luaK:setlist(fs, cc.t.info, cc.na, cc.tostore) + end +end + +------------------------------------------------------------------------ +-- parse a table list (array) field +-- * used in constructor() +------------------------------------------------------------------------ +function luaY:listfield(ls, cc) + self:expr(ls, cc.v) + self:checklimit(ls.fs, cc.na, self.MAX_INT, "items in a constructor") + cc.na = cc.na + 1 + cc.tostore = cc.tostore + 1 +end + +------------------------------------------------------------------------ +-- parse a table constructor +-- * used in funcargs(), simpleexp() +------------------------------------------------------------------------ +function luaY:constructor(ls, t) + -- constructor -> '{' [ field { fieldsep field } [ fieldsep ] ] '}' + -- field -> recfield | listfield + -- fieldsep -> ',' | ';' + local fs = ls.fs + local line = ls.linenumber + local pc = luaK:codeABC(fs, "OP_NEWTABLE", 0, 0, 0) + local cc = {} -- ConsControl + cc.v = {} + cc.na, cc.nh, cc.tostore = 0, 0, 0 + cc.t = t + self:init_exp(t, "VRELOCABLE", pc) + self:init_exp(cc.v, "VVOID", 0) -- no value (yet) + luaK:exp2nextreg(ls.fs, t) -- fix it at stack top (for gc) + self:checknext(ls, "{") + repeat + lua_assert(cc.v.k == "VVOID" or cc.tostore > 0) + if ls.t.token == "}" then break end + self:closelistfield(fs, cc) + local c = ls.t.token + + if c == "TK_NAME" then -- may be listfields or recfields + luaX:lookahead(ls) + if ls.lookahead.token ~= "=" then -- expression? + self:listfield(ls, cc) + else + self:recfield(ls, cc) + end + elseif c == "[" then -- constructor_item -> recfield + self:recfield(ls, cc) + else -- constructor_part -> listfield + self:listfield(ls, cc) + end + until not self:testnext(ls, ",") and not self:testnext(ls, ";") + self:check_match(ls, "}", "{", line) + self:lastlistfield(fs, cc) + luaP:SETARG_B(fs.f.code[pc], self:int2fb(cc.na)) -- set initial array size + luaP:SETARG_C(fs.f.code[pc], self:int2fb(cc.nh)) -- set initial table size +end + +-- }====================================================================== + +------------------------------------------------------------------------ +-- parse the arguments (parameters) of a function declaration +-- * used in body() +------------------------------------------------------------------------ +function luaY:parlist(ls) + -- parlist -> [ param { ',' param } ] + local fs = ls.fs + local f = fs.f + local nparams = 0 + f.is_vararg = 0 + if ls.t.token ~= ")" then -- is 'parlist' not empty? + repeat + local c = ls.t.token + if c == "TK_NAME" then -- param -> NAME + self:new_localvar(ls, self:str_checkname(ls), nparams) + nparams = nparams + 1 + elseif c == "TK_DOTS" then -- param -> `...' + luaX:next(ls) +-- [[ +-- #if defined(LUA_COMPAT_VARARG) + -- use `arg' as default name + self:new_localvarliteral(ls, "arg", nparams) + nparams = nparams + 1 + f.is_vararg = self.VARARG_HASARG + self.VARARG_NEEDSARG +-- #endif +--]] + f.is_vararg = f.is_vararg + self.VARARG_ISVARARG + else + luaX:syntaxerror(ls, " or "..self:LUA_QL("...").." expected") + end + until f.is_vararg ~= 0 or not self:testnext(ls, ",") + end--if + self:adjustlocalvars(ls, nparams) + -- NOTE: the following works only when HASARG_MASK is 2! + f.numparams = fs.nactvar - (f.is_vararg % self.HASARG_MASK) + luaK:reserveregs(fs, fs.nactvar) -- reserve register for parameters +end + +------------------------------------------------------------------------ +-- parse function declaration body +-- * used in simpleexp(), localfunc(), funcstat() +------------------------------------------------------------------------ +function luaY:body(ls, e, needself, line) + -- body -> '(' parlist ')' chunk END + local new_fs = {} -- FuncState + new_fs.upvalues = {} + new_fs.actvar = {} + self:open_func(ls, new_fs) + new_fs.f.lineDefined = line + self:checknext(ls, "(") + if needself then + self:new_localvarliteral(ls, "self", 0) + self:adjustlocalvars(ls, 1) + end + self:parlist(ls) + self:checknext(ls, ")") + self:chunk(ls) + new_fs.f.lastlinedefined = ls.linenumber + self:check_match(ls, "TK_END", "TK_FUNCTION", line) + self:close_func(ls) + self:pushclosure(ls, new_fs, e) +end + +------------------------------------------------------------------------ +-- parse a list of comma-separated expressions +-- * used is multiple locations +------------------------------------------------------------------------ +function luaY:explist1(ls, v) + -- explist1 -> expr { ',' expr } + local n = 1 -- at least one expression + self:expr(ls, v) + while self:testnext(ls, ",") do + luaK:exp2nextreg(ls.fs, v) + self:expr(ls, v) + n = n + 1 + end + return n +end + +------------------------------------------------------------------------ +-- parse the parameters of a function call +-- * contrast with parlist(), used in function declarations +-- * used in primaryexp() +------------------------------------------------------------------------ +function luaY:funcargs(ls, f) + local fs = ls.fs + local args = {} -- expdesc + local nparams + local line = ls.linenumber + local c = ls.t.token + if c == "(" then -- funcargs -> '(' [ explist1 ] ')' + if line ~= ls.lastline then + luaX:syntaxerror(ls, "ambiguous syntax (function call x new statement)") + end + luaX:next(ls) + if ls.t.token == ")" then -- arg list is empty? + args.k = "VVOID" + else + self:explist1(ls, args) + luaK:setmultret(fs, args) + end + self:check_match(ls, ")", "(", line) + elseif c == "{" then -- funcargs -> constructor + self:constructor(ls, args) + elseif c == "TK_STRING" then -- funcargs -> STRING + self:codestring(ls, args, ls.t.seminfo) + luaX:next(ls) -- must use 'seminfo' before 'next' + else + luaX:syntaxerror(ls, "function arguments expected") + return + end + lua_assert(f.k == "VNONRELOC") + local base = f.info -- base register for call + if self:hasmultret(args.k) then + nparams = self.LUA_MULTRET -- open call + else + if args.k ~= "VVOID" then + luaK:exp2nextreg(fs, args) -- close last argument + end + nparams = fs.freereg - (base + 1) + end + self:init_exp(f, "VCALL", luaK:codeABC(fs, "OP_CALL", base, nparams + 1, 2)) + luaK:fixline(fs, line) + fs.freereg = base + 1 -- call remove function and arguments and leaves + -- (unless changed) one result +end + +--[[-------------------------------------------------------------------- +-- Expression parsing +----------------------------------------------------------------------]] + +------------------------------------------------------------------------ +-- parses an expression in parentheses or a single variable +-- * used in primaryexp() +------------------------------------------------------------------------ +function luaY:prefixexp(ls, v) + -- prefixexp -> NAME | '(' expr ')' + local c = ls.t.token + if c == "(" then + local line = ls.linenumber + luaX:next(ls) + self:expr(ls, v) + self:check_match(ls, ")", "(", line) + luaK:dischargevars(ls.fs, v) + elseif c == "TK_NAME" then + self:singlevar(ls, v) + else + luaX:syntaxerror(ls, "unexpected symbol") + end--if c + return +end + +------------------------------------------------------------------------ +-- parses a prefixexp (an expression in parentheses or a single variable) +-- or a function call specification +-- * used in simpleexp(), assignment(), exprstat() +------------------------------------------------------------------------ +function luaY:primaryexp(ls, v) + -- primaryexp -> + -- prefixexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } + local fs = ls.fs + self:prefixexp(ls, v) + while true do + local c = ls.t.token + if c == "." then -- field + self:field(ls, v) + elseif c == "[" then -- '[' exp1 ']' + local key = {} -- expdesc + luaK:exp2anyreg(fs, v) + self:yindex(ls, key) + luaK:indexed(fs, v, key) + elseif c == ":" then -- ':' NAME funcargs + local key = {} -- expdesc + luaX:next(ls) + self:checkname(ls, key) + luaK:_self(fs, v, key) + self:funcargs(ls, v) + elseif c == "(" or c == "TK_STRING" or c == "{" then -- funcargs + luaK:exp2nextreg(fs, v) + self:funcargs(ls, v) + else + return + end--if c + end--while +end + +------------------------------------------------------------------------ +-- parses general expression types, constants handled here +-- * used in subexpr() +------------------------------------------------------------------------ +function luaY:simpleexp(ls, v) + -- simpleexp -> NUMBER | STRING | NIL | TRUE | FALSE | ... | + -- constructor | FUNCTION body | primaryexp + local c = ls.t.token + if c == "TK_NUMBER" then + self:init_exp(v, "VKNUM", 0) + v.nval = ls.t.seminfo + elseif c == "TK_STRING" then + self:codestring(ls, v, ls.t.seminfo) + elseif c == "TK_NIL" then + self:init_exp(v, "VNIL", 0) + elseif c == "TK_TRUE" then + self:init_exp(v, "VTRUE", 0) + elseif c == "TK_FALSE" then + self:init_exp(v, "VFALSE", 0) + elseif c == "TK_DOTS" then -- vararg + local fs = ls.fs + self:check_condition(ls, fs.f.is_vararg ~= 0, + "cannot use "..self:LUA_QL("...").." outside a vararg function"); + -- NOTE: the following substitutes for a bitop, but is value-specific + local is_vararg = fs.f.is_vararg + if is_vararg >= self.VARARG_NEEDSARG then + fs.f.is_vararg = is_vararg - self.VARARG_NEEDSARG -- don't need 'arg' + end + self:init_exp(v, "VVARARG", luaK:codeABC(fs, "OP_VARARG", 0, 1, 0)) + elseif c == "{" then -- constructor + self:constructor(ls, v) + return + elseif c == "TK_FUNCTION" then + luaX:next(ls) + self:body(ls, v, false, ls.linenumber) + return + else + self:primaryexp(ls, v) + return + end--if c + luaX:next(ls) +end + +------------------------------------------------------------------------ +-- Translates unary operators tokens if found, otherwise returns +-- OPR_NOUNOPR. getunopr() and getbinopr() are used in subexpr(). +-- * used in subexpr() +------------------------------------------------------------------------ +function luaY:getunopr(op) + if op == "TK_NOT" then + return "OPR_NOT" + elseif op == "-" then + return "OPR_MINUS" + elseif op == "#" then + return "OPR_LEN" + else + return "OPR_NOUNOPR" + end +end + +------------------------------------------------------------------------ +-- Translates binary operator tokens if found, otherwise returns +-- OPR_NOBINOPR. Code generation uses OPR_* style tokens. +-- * used in subexpr() +------------------------------------------------------------------------ +luaY.getbinopr_table = { + ["+"] = "OPR_ADD", + ["-"] = "OPR_SUB", + ["*"] = "OPR_MUL", + ["/"] = "OPR_DIV", + ["%"] = "OPR_MOD", + ["^"] = "OPR_POW", + ["TK_CONCAT"] = "OPR_CONCAT", + ["TK_NE"] = "OPR_NE", + ["TK_EQ"] = "OPR_EQ", + ["<"] = "OPR_LT", + ["TK_LE"] = "OPR_LE", + [">"] = "OPR_GT", + ["TK_GE"] = "OPR_GE", + ["TK_AND"] = "OPR_AND", + ["TK_OR"] = "OPR_OR", +} +function luaY:getbinopr(op) + local opr = self.getbinopr_table[op] + if opr then return opr else return "OPR_NOBINOPR" end +end + +------------------------------------------------------------------------ +-- the following priority table consists of pairs of left/right values +-- for binary operators (was a static const struct); grep for ORDER OPR +-- * the following struct is replaced: +-- static const struct { +-- lu_byte left; /* left priority for each binary operator */ +-- lu_byte right; /* right priority */ +-- } priority[] = { /* ORDER OPR */ +------------------------------------------------------------------------ +luaY.priority = { + {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, -- `+' `-' `/' `%' + {10, 9}, {5, 4}, -- power and concat (right associative) + {3, 3}, {3, 3}, -- equality + {3, 3}, {3, 3}, {3, 3}, {3, 3}, -- order + {2, 2}, {1, 1} -- logical (and/or) +} + +luaY.UNARY_PRIORITY = 8 -- priority for unary operators + +------------------------------------------------------------------------ +-- Parse subexpressions. Includes handling of unary operators and binary +-- operators. A subexpr is given the rhs priority level of the operator +-- immediately left of it, if any (limit is -1 if none,) and if a binop +-- is found, limit is compared with the lhs priority level of the binop +-- in order to determine which executes first. +------------------------------------------------------------------------ + +------------------------------------------------------------------------ +-- subexpr -> (simpleexp | unop subexpr) { binop subexpr } +-- where 'binop' is any binary operator with a priority higher than 'limit' +-- * for priority lookups with self.priority[], 1=left and 2=right +-- * recursively called +-- * used in expr() +------------------------------------------------------------------------ +function luaY:subexpr(ls, v, limit) + self:enterlevel(ls) + local uop = self:getunopr(ls.t.token) + if uop ~= "OPR_NOUNOPR" then + luaX:next(ls) + self:subexpr(ls, v, self.UNARY_PRIORITY) + luaK:prefix(ls.fs, uop, v) + else + self:simpleexp(ls, v) + end + -- expand while operators have priorities higher than 'limit' + local op = self:getbinopr(ls.t.token) + while op ~= "OPR_NOBINOPR" and self.priority[luaK.BinOpr[op] + 1][1] > limit do + local v2 = {} -- expdesc + luaX:next(ls) + luaK:infix(ls.fs, op, v) + -- read sub-expression with higher priority + local nextop = self:subexpr(ls, v2, self.priority[luaK.BinOpr[op] + 1][2]) + luaK:posfix(ls.fs, op, v, v2) + op = nextop + end + self:leavelevel(ls) + return op -- return first untreated operator +end + +------------------------------------------------------------------------ +-- Expression parsing starts here. Function subexpr is entered with the +-- left operator (which is non-existent) priority of -1, which is lower +-- than all actual operators. Expr information is returned in parm v. +-- * used in multiple locations +------------------------------------------------------------------------ +function luaY:expr(ls, v) + self:subexpr(ls, v, 0) +end + +-- }==================================================================== + +--[[-------------------------------------------------------------------- +-- Rules for Statements +----------------------------------------------------------------------]] + +------------------------------------------------------------------------ +-- checks next token, used as a look-ahead +-- * returns boolean instead of 0|1 +-- * used in retstat(), chunk() +------------------------------------------------------------------------ +function luaY:block_follow(token) + if token == "TK_ELSE" or token == "TK_ELSEIF" or token == "TK_END" + or token == "TK_UNTIL" or token == "TK_EOS" then + return true + else + return false + end +end + +------------------------------------------------------------------------ +-- parse a code block or unit +-- * used in multiple functions +------------------------------------------------------------------------ +function luaY:block(ls) + -- block -> chunk + local fs = ls.fs + local bl = {} -- BlockCnt + self:enterblock(fs, bl, false) + self:chunk(ls) + lua_assert(bl.breaklist == luaK.NO_JUMP) + self:leaveblock(fs) +end + +------------------------------------------------------------------------ +-- structure to chain all variables in the left-hand side of an +-- assignment +-- struct LHS_assign: +-- prev -- (table: struct LHS_assign) +-- v -- variable (global, local, upvalue, or indexed) (table: expdesc) +------------------------------------------------------------------------ + +------------------------------------------------------------------------ +-- check whether, in an assignment to a local variable, the local variable +-- is needed in a previous assignment (to a table). If so, save original +-- local value in a safe place and use this safe copy in the previous +-- assignment. +-- * used in assignment() +------------------------------------------------------------------------ +function luaY:check_conflict(ls, lh, v) + local fs = ls.fs + local extra = fs.freereg -- eventual position to save local variable + local conflict = false + while lh do + if lh.v.k == "VINDEXED" then + if lh.v.info == v.info then -- conflict? + conflict = true + lh.v.info = extra -- previous assignment will use safe copy + end + if lh.v.aux == v.info then -- conflict? + conflict = true + lh.v.aux = extra -- previous assignment will use safe copy + end + end + lh = lh.prev + end + if conflict then + luaK:codeABC(fs, "OP_MOVE", fs.freereg, v.info, 0) -- make copy + luaK:reserveregs(fs, 1) + end +end + +------------------------------------------------------------------------ +-- parse a variable assignment sequence +-- * recursively called +-- * used in exprstat() +------------------------------------------------------------------------ +function luaY:assignment(ls, lh, nvars) + local e = {} -- expdesc + -- test was: VLOCAL <= lh->v.k && lh->v.k <= VINDEXED + local c = lh.v.k + self:check_condition(ls, c == "VLOCAL" or c == "VUPVAL" or c == "VGLOBAL" + or c == "VINDEXED", "syntax error") + if self:testnext(ls, ",") then -- assignment -> ',' primaryexp assignment + local nv = {} -- LHS_assign + nv.v = {} + nv.prev = lh + self:primaryexp(ls, nv.v) + if nv.v.k == "VLOCAL" then + self:check_conflict(ls, lh, nv.v) + end + self:checklimit(ls.fs, nvars, self.LUAI_MAXCCALLS - ls.L.nCcalls, + "variables in assignment") + self:assignment(ls, nv, nvars + 1) + else -- assignment -> '=' explist1 + self:checknext(ls, "=") + local nexps = self:explist1(ls, e) + if nexps ~= nvars then + self:adjust_assign(ls, nvars, nexps, e) + if nexps > nvars then + ls.fs.freereg = ls.fs.freereg - (nexps - nvars) -- remove extra values + end + else + luaK:setoneret(ls.fs, e) -- close last expression + luaK:storevar(ls.fs, lh.v, e) + return -- avoid default + end + end + self:init_exp(e, "VNONRELOC", ls.fs.freereg - 1) -- default assignment + luaK:storevar(ls.fs, lh.v, e) +end + +------------------------------------------------------------------------ +-- parse condition in a repeat statement or an if control structure +-- * used in repeatstat(), test_then_block() +------------------------------------------------------------------------ +function luaY:cond(ls) + -- cond -> exp + local v = {} -- expdesc + self:expr(ls, v) -- read condition + if v.k == "VNIL" then v.k = "VFALSE" end -- 'falses' are all equal here + luaK:goiftrue(ls.fs, v) + return v.f +end + +------------------------------------------------------------------------ +-- parse a break statement +-- * used in statements() +------------------------------------------------------------------------ +function luaY:breakstat(ls) + -- stat -> BREAK + local fs = ls.fs + local bl = fs.bl + local upval = false + while bl and not bl.isbreakable do + if bl.upval then upval = true end + bl = bl.previous + end + if not bl then + luaX:syntaxerror(ls, "no loop to break") + end + if upval then + luaK:codeABC(fs, "OP_CLOSE", bl.nactvar, 0, 0) + end + bl.breaklist = luaK:concat(fs, bl.breaklist, luaK:jump(fs)) +end + +------------------------------------------------------------------------ +-- parse a while-do control structure, body processed by block() +-- * with dynamic array sizes, MAXEXPWHILE + EXTRAEXP limits imposed by +-- the function's implementation can be removed +-- * used in statements() +------------------------------------------------------------------------ +function luaY:whilestat(ls, line) + -- whilestat -> WHILE cond DO block END + local fs = ls.fs + local bl = {} -- BlockCnt + luaX:next(ls) -- skip WHILE + local whileinit = luaK:getlabel(fs) + local condexit = self:cond(ls) + self:enterblock(fs, bl, true) + self:checknext(ls, "TK_DO") + self:block(ls) + luaK:patchlist(fs, luaK:jump(fs), whileinit) + self:check_match(ls, "TK_END", "TK_WHILE", line) + self:leaveblock(fs) + luaK:patchtohere(fs, condexit) -- false conditions finish the loop +end + +------------------------------------------------------------------------ +-- parse a repeat-until control structure, body parsed by chunk() +-- * used in statements() +------------------------------------------------------------------------ +function luaY:repeatstat(ls, line) + -- repeatstat -> REPEAT block UNTIL cond + local fs = ls.fs + local repeat_init = luaK:getlabel(fs) + local bl1, bl2 = {}, {} -- BlockCnt + self:enterblock(fs, bl1, true) -- loop block + self:enterblock(fs, bl2, false) -- scope block + luaX:next(ls) -- skip REPEAT + self:chunk(ls) + self:check_match(ls, "TK_UNTIL", "TK_REPEAT", line) + local condexit = self:cond(ls) -- read condition (inside scope block) + if not bl2.upval then -- no upvalues? + self:leaveblock(fs) -- finish scope + luaK:patchlist(ls.fs, condexit, repeat_init) -- close the loop + else -- complete semantics when there are upvalues + self:breakstat(ls) -- if condition then break + luaK:patchtohere(ls.fs, condexit) -- else... + self:leaveblock(fs) -- finish scope... + luaK:patchlist(ls.fs, luaK:jump(fs), repeat_init) -- and repeat + end + self:leaveblock(fs) -- finish loop +end + +------------------------------------------------------------------------ +-- parse the single expressions needed in numerical for loops +-- * used in fornum() +------------------------------------------------------------------------ +function luaY:exp1(ls) + local e = {} -- expdesc + self:expr(ls, e) + local k = e.k + luaK:exp2nextreg(ls.fs, e) + return k +end + +------------------------------------------------------------------------ +-- parse a for loop body for both versions of the for loop +-- * used in fornum(), forlist() +------------------------------------------------------------------------ +function luaY:forbody(ls, base, line, nvars, isnum) + -- forbody -> DO block + local bl = {} -- BlockCnt + local fs = ls.fs + self:adjustlocalvars(ls, 3) -- control variables + self:checknext(ls, "TK_DO") + local prep = isnum and luaK:codeAsBx(fs, "OP_FORPREP", base, luaK.NO_JUMP) + or luaK:jump(fs) + self:enterblock(fs, bl, false) -- scope for declared variables + self:adjustlocalvars(ls, nvars) + luaK:reserveregs(fs, nvars) + self:block(ls) + self:leaveblock(fs) -- end of scope for declared variables + luaK:patchtohere(fs, prep) + local endfor = isnum and luaK:codeAsBx(fs, "OP_FORLOOP", base, luaK.NO_JUMP) + or luaK:codeABC(fs, "OP_TFORLOOP", base, 0, nvars) + luaK:fixline(fs, line) -- pretend that `OP_FOR' starts the loop + luaK:patchlist(fs, isnum and endfor or luaK:jump(fs), prep + 1) +end + +------------------------------------------------------------------------ +-- parse a numerical for loop, calls forbody() +-- * used in forstat() +------------------------------------------------------------------------ +function luaY:fornum(ls, varname, line) + -- fornum -> NAME = exp1,exp1[,exp1] forbody + local fs = ls.fs + local base = fs.freereg + self:new_localvarliteral(ls, "(for index)", 0) + self:new_localvarliteral(ls, "(for limit)", 1) + self:new_localvarliteral(ls, "(for step)", 2) + self:new_localvar(ls, varname, 3) + self:checknext(ls, '=') + self:exp1(ls) -- initial value + self:checknext(ls, ",") + self:exp1(ls) -- limit + if self:testnext(ls, ",") then + self:exp1(ls) -- optional step + else -- default step = 1 + luaK:codeABx(fs, "OP_LOADK", fs.freereg, luaK:numberK(fs, 1)) + luaK:reserveregs(fs, 1) + end + self:forbody(ls, base, line, 1, true) +end + +------------------------------------------------------------------------ +-- parse a generic for loop, calls forbody() +-- * used in forstat() +------------------------------------------------------------------------ +function luaY:forlist(ls, indexname) + -- forlist -> NAME {,NAME} IN explist1 forbody + local fs = ls.fs + local e = {} -- expdesc + local nvars = 0 + local base = fs.freereg + -- create control variables + self:new_localvarliteral(ls, "(for generator)", nvars) + nvars = nvars + 1 + self:new_localvarliteral(ls, "(for state)", nvars) + nvars = nvars + 1 + self:new_localvarliteral(ls, "(for control)", nvars) + nvars = nvars + 1 + -- create declared variables + self:new_localvar(ls, indexname, nvars) + nvars = nvars + 1 + while self:testnext(ls, ",") do + self:new_localvar(ls, self:str_checkname(ls), nvars) + nvars = nvars + 1 + end + self:checknext(ls, "TK_IN") + local line = ls.linenumber + self:adjust_assign(ls, 3, self:explist1(ls, e), e) + luaK:checkstack(fs, 3) -- extra space to call generator + self:forbody(ls, base, line, nvars - 3, false) +end + +------------------------------------------------------------------------ +-- initial parsing for a for loop, calls fornum() or forlist() +-- * used in statements() +------------------------------------------------------------------------ +function luaY:forstat(ls, line) + -- forstat -> FOR (fornum | forlist) END + local fs = ls.fs + local bl = {} -- BlockCnt + self:enterblock(fs, bl, true) -- scope for loop and control variables + luaX:next(ls) -- skip `for' + local varname = self:str_checkname(ls) -- first variable name + local c = ls.t.token + if c == "=" then + self:fornum(ls, varname, line) + elseif c == "," or c == "TK_IN" then + self:forlist(ls, varname) + else + luaX:syntaxerror(ls, self:LUA_QL("=").." or "..self:LUA_QL("in").." expected") + end + self:check_match(ls, "TK_END", "TK_FOR", line) + self:leaveblock(fs) -- loop scope (`break' jumps to this point) +end + +------------------------------------------------------------------------ +-- parse part of an if control structure, including the condition +-- * used in ifstat() +------------------------------------------------------------------------ +function luaY:test_then_block(ls) + -- test_then_block -> [IF | ELSEIF] cond THEN block + luaX:next(ls) -- skip IF or ELSEIF + local condexit = self:cond(ls) + self:checknext(ls, "TK_THEN") + self:block(ls) -- `then' part + return condexit +end + +------------------------------------------------------------------------ +-- parse an if control structure +-- * used in statements() +------------------------------------------------------------------------ +function luaY:ifstat(ls, line) + -- ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END + local fs = ls.fs + local escapelist = luaK.NO_JUMP + local flist = self:test_then_block(ls) -- IF cond THEN block + while ls.t.token == "TK_ELSEIF" do + escapelist = luaK:concat(fs, escapelist, luaK:jump(fs)) + luaK:patchtohere(fs, flist) + flist = self:test_then_block(ls) -- ELSEIF cond THEN block + end + if ls.t.token == "TK_ELSE" then + escapelist = luaK:concat(fs, escapelist, luaK:jump(fs)) + luaK:patchtohere(fs, flist) + luaX:next(ls) -- skip ELSE (after patch, for correct line info) + self:block(ls) -- 'else' part + else + escapelist = luaK:concat(fs, escapelist, flist) + end + luaK:patchtohere(fs, escapelist) + self:check_match(ls, "TK_END", "TK_IF", line) +end + +------------------------------------------------------------------------ +-- parse a local function statement +-- * used in statements() +------------------------------------------------------------------------ +function luaY:localfunc(ls) + local v, b = {}, {} -- expdesc + local fs = ls.fs + self:new_localvar(ls, self:str_checkname(ls), 0) + self:init_exp(v, "VLOCAL", fs.freereg) + luaK:reserveregs(fs, 1) + self:adjustlocalvars(ls, 1) + self:body(ls, b, false, ls.linenumber) + luaK:storevar(fs, v, b) + -- debug information will only see the variable after this point! + self:getlocvar(fs, fs.nactvar - 1).startpc = fs.pc +end + +------------------------------------------------------------------------ +-- parse a local variable declaration statement +-- * used in statements() +------------------------------------------------------------------------ +function luaY:localstat(ls) + -- stat -> LOCAL NAME {',' NAME} ['=' explist1] + local nvars = 0 + local nexps + local e = {} -- expdesc + repeat + self:new_localvar(ls, self:str_checkname(ls), nvars) + nvars = nvars + 1 + until not self:testnext(ls, ",") + if self:testnext(ls, "=") then + nexps = self:explist1(ls, e) + else + e.k = "VVOID" + nexps = 0 + end + self:adjust_assign(ls, nvars, nexps, e) + self:adjustlocalvars(ls, nvars) +end + +------------------------------------------------------------------------ +-- parse a function name specification +-- * used in funcstat() +------------------------------------------------------------------------ +function luaY:funcname(ls, v) + -- funcname -> NAME {field} [':' NAME] + local needself = false + self:singlevar(ls, v) + while ls.t.token == "." do + self:field(ls, v) + end + if ls.t.token == ":" then + needself = true + self:field(ls, v) + end + return needself +end + +------------------------------------------------------------------------ +-- parse a function statement +-- * used in statements() +------------------------------------------------------------------------ +function luaY:funcstat(ls, line) + -- funcstat -> FUNCTION funcname body + local v, b = {}, {} -- expdesc + luaX:next(ls) -- skip FUNCTION + local needself = self:funcname(ls, v) + self:body(ls, b, needself, line) + luaK:storevar(ls.fs, v, b) + luaK:fixline(ls.fs, line) -- definition 'happens' in the first line +end + +------------------------------------------------------------------------ +-- parse a function call with no returns or an assignment statement +-- * used in statements() +------------------------------------------------------------------------ +function luaY:exprstat(ls) + -- stat -> func | assignment + local fs = ls.fs + local v = {} -- LHS_assign + v.v = {} + self:primaryexp(ls, v.v) + if v.v.k == "VCALL" then -- stat -> func + luaP:SETARG_C(luaK:getcode(fs, v.v), 1) -- call statement uses no results + else -- stat -> assignment + v.prev = nil + self:assignment(ls, v, 1) + end +end + +------------------------------------------------------------------------ +-- parse a return statement +-- * used in statements() +------------------------------------------------------------------------ +function luaY:retstat(ls) + -- stat -> RETURN explist + local fs = ls.fs + local e = {} -- expdesc + local first, nret -- registers with returned values + luaX:next(ls) -- skip RETURN + if self:block_follow(ls.t.token) or ls.t.token == ";" then + first, nret = 0, 0 -- return no values + else + nret = self:explist1(ls, e) -- optional return values + if self:hasmultret(e.k) then + luaK:setmultret(fs, e) + if e.k == "VCALL" and nret == 1 then -- tail call? + luaP:SET_OPCODE(luaK:getcode(fs, e), "OP_TAILCALL") + lua_assert(luaP:GETARG_A(luaK:getcode(fs, e)) == fs.nactvar) + end + first = fs.nactvar + nret = self.LUA_MULTRET -- return all values + else + if nret == 1 then -- only one single value? + first = luaK:exp2anyreg(fs, e) + else + luaK:exp2nextreg(fs, e) -- values must go to the 'stack' + first = fs.nactvar -- return all 'active' values + lua_assert(nret == fs.freereg - first) + end + end--if + end--if + luaK:ret(fs, first, nret) +end + +------------------------------------------------------------------------ +-- initial parsing for statements, calls a lot of functions +-- * returns boolean instead of 0|1 +-- * used in chunk() +------------------------------------------------------------------------ +function luaY:statement(ls) + local line = ls.linenumber -- may be needed for error messages + local c = ls.t.token + if c == "TK_IF" then -- stat -> ifstat + self:ifstat(ls, line) + return false + elseif c == "TK_WHILE" then -- stat -> whilestat + self:whilestat(ls, line) + return false + elseif c == "TK_DO" then -- stat -> DO block END + luaX:next(ls) -- skip DO + self:block(ls) + self:check_match(ls, "TK_END", "TK_DO", line) + return false + elseif c == "TK_FOR" then -- stat -> forstat + self:forstat(ls, line) + return false + elseif c == "TK_REPEAT" then -- stat -> repeatstat + self:repeatstat(ls, line) + return false + elseif c == "TK_FUNCTION" then -- stat -> funcstat + self:funcstat(ls, line) + return false + elseif c == "TK_LOCAL" then -- stat -> localstat + luaX:next(ls) -- skip LOCAL + if self:testnext(ls, "TK_FUNCTION") then -- local function? + self:localfunc(ls) + else + self:localstat(ls) + end + return false + elseif c == "TK_RETURN" then -- stat -> retstat + self:retstat(ls) + return true -- must be last statement + elseif c == "TK_BREAK" then -- stat -> breakstat + luaX:next(ls) -- skip BREAK + self:breakstat(ls) + return true -- must be last statement + else + self:exprstat(ls) + return false -- to avoid warnings + end--if c +end + +------------------------------------------------------------------------ +-- parse a chunk, which consists of a bunch of statements +-- * used in parser(), body(), block(), repeatstat() +------------------------------------------------------------------------ +function luaY:chunk(ls) + -- chunk -> { stat [';'] } + local islast = false + self:enterlevel(ls) + while not islast and not self:block_follow(ls.t.token) do + islast = self:statement(ls) + self:testnext(ls, ";") + lua_assert(ls.fs.f.maxstacksize >= ls.fs.freereg and + ls.fs.freereg >= ls.fs.nactvar) + ls.fs.freereg = ls.fs.nactvar -- free registers + end + self:leavelevel(ls) +end + +-- }====================================================================== -- cgit v1.1