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. --- .../yueliang-0.4.1/nat-5.0.3/lparser_mk3b.lua | 1121 ++++++++++++++++++++ 1 file changed, 1121 insertions(+) create mode 100644 LuaSL/testLua/yueliang-0.4.1/nat-5.0.3/lparser_mk3b.lua (limited to 'LuaSL/testLua/yueliang-0.4.1/nat-5.0.3/lparser_mk3b.lua') diff --git a/LuaSL/testLua/yueliang-0.4.1/nat-5.0.3/lparser_mk3b.lua b/LuaSL/testLua/yueliang-0.4.1/nat-5.0.3/lparser_mk3b.lua new file mode 100644 index 0000000..a46edc6 --- /dev/null +++ b/LuaSL/testLua/yueliang-0.4.1/nat-5.0.3/lparser_mk3b.lua @@ -0,0 +1,1121 @@ +--[[-------------------------------------------------------------------- + + lparser.lua + Lua 5 parser in Lua + This file is part of Yueliang. + + Copyright (c) 2008 Kein-Hong Man + The COPYRIGHT file describes the conditions + under which this software may be distributed. + + See the ChangeLog for more information. + +----------------------------------------------------------------------]] + +--[[-------------------------------------------------------------------- +-- Notes: +-- * this is a Lua 5.0.x parser skeleton, for llex_mk3.lua lexer +-- * written as a module factory with recognizable lparser.c roots +-- * builds some data, performs logging for educational purposes +-- * target is to have relatively efficient and clear code +-- * needs one parameter, a lexer module that implements: +-- luaX:lex() - returns appropriate [token, semantic info] pairs +-- luaX.ln - current line number +-- luaX:errorline(s, [line]) - dies with error message +-- +-- Usage example: +-- lex_init = require("llex_mk3.lua") +-- parser_init = require("lparser_mk3.lua") +-- local luaX = lex_init(chunk, "=string") +-- local luaY = parser_init(luaX) +-- local fs = luaY:parser() +-- +-- Development notes: +-- * see test_parser-5.0.lua for grammar elements based on lparser.c +-- * lparser has a few extra items to help parsing/syntax checking +-- (a) line number (error reporting), lookahead token storage +-- (b) per-prototype states needs a storage list +-- (c) 'break' needs a per-block flag in a stack +-- (d) 'kind' (v.k) testing needed in expr_stat() and assignment() +-- for disambiguation, thus v.k manipulation is retained +-- (e) one line # var (lastln) for ambiguous (split line) function +-- call checking +-- (f) most line number function call args retained for future use +-- (g) Lua 4 compatibility code completely removed +-- (h) minimal variable management code to differentiate each type +-- * parsing starts from the end of this file in luaY:parser() +-- +----------------------------------------------------------------------]] + +return +function(luaX) +--[[-------------------------------------------------------------------- +-- structures and data initialization +----------------------------------------------------------------------]] + + local line -- start line # for error messages + local lastln -- last line # for ambiguous syntax chk + local tok, seminfo -- token, semantic info pair + local peek_tok, peek_sem -- ditto, for lookahead + local fs -- function state + local top_fs = {} -- top-level function state + local luaY = {} + -------------------------------------------------------------------- + local block_follow = {} -- lookahead check in chunk(), returnstat() + for v in string.gfind("else elseif end until ", "%S+") do + block_follow[v] = true + end + -------------------------------------------------------------------- + local stat_call = {} -- lookup for calls in stat() + for v in string.gfind("if while do for repeat function local return break", "%S+") do + stat_call[v] = v.."_stat" + end + -------------------------------------------------------------------- + local binopr_left = {} -- binary operators, left priority + local binopr_right = {} -- binary operators, right priority + for op, lt, rt in string.gfind([[ +{+ 6 6}{- 6 6}{* 7 7}{/ 7 7}{^ 10 9}{.. 5 4} +{~= 3 3}{== 3 3}{< 3 3}{<= 3 3}{> 3 3}{>= 3 3} +{and 2 2}{or 1 1} +]], "{(%S+)%s(%d+)%s(%d+)}") do + binopr_left[op] = lt + 0 + binopr_right[op] = rt + 0 + end + local unopr = { ["not"] = true, ["-"] = true, } -- unary operators + +--[[-------------------------------------------------------------------- +-- logging: this logging function is for educational purposes +-- * logged data can be retrieved from the returned data structure +-- * or, replace self:log() instances with your custom code... +----------------------------------------------------------------------]] + + function luaY:log(msg) + local log = top_fs.log + if not log then log = {}; top_fs.log = log end + table.insert(top_fs.log, msg) + end + +--[[-------------------------------------------------------------------- +-- support functions +----------------------------------------------------------------------]] + + -------------------------------------------------------------------- + -- reads in next token + -------------------------------------------------------------------- + function luaY:next() + lastln = luaX.ln + if peek_tok then -- is there a look-ahead token? if yes, use it + tok, seminfo = peek_tok, peek_sem + peek_tok = nil + else + tok, seminfo = luaX:lex() -- read next token + end + end + -------------------------------------------------------------------- + -- peek at next token (single lookahead for table constructor) + -------------------------------------------------------------------- + function luaY:lookahead() + peek_tok, peek_sem = luaX:lex() + return peek_tok + end + + ------------------------------------------------------------------------ + -- throws a syntax error + ------------------------------------------------------------------------ + function luaY:syntaxerror(msg) + local tok = tok + if tok ~= "" and tok ~= "" then + if tok == "" then tok = seminfo end + tok = "'"..tok.."'" + end + luaX:errorline(msg.." near "..tok) + end + -------------------------------------------------------------------- + -- throws a syntax error if token expected is not there + -------------------------------------------------------------------- + function luaY:error_expected(token) + self:syntaxerror("'"..token.."' expected") + end + + -------------------------------------------------------------------- + -- verifies token conditions are met or else throw error + -------------------------------------------------------------------- + function luaY:check_match(what, who, where) + if not self:testnext(what) then + if where == luaX.ln then + self:error_expected(what) + else + self:syntaxerror("'"..what.."' expected (to close '"..who.."' at line "..where..")") + end + end + end + -------------------------------------------------------------------- + -- tests for a token, returns outcome + -- * return value changed to boolean + -------------------------------------------------------------------- + function luaY:testnext(c) + if tok == c then self:next(); return true end + end + -------------------------------------------------------------------- + -- throws error if condition not matched + -------------------------------------------------------------------- + function luaY:check_condition(c, msg) + if not c then self:syntaxerror(msg) end + end + -------------------------------------------------------------------- + -- check for existence of a token, throws error if not found + -------------------------------------------------------------------- + function luaY:check(c) + if not self:testnext(c) then self:error_expected(c) end + end + + -------------------------------------------------------------------- + -- expect that token is a name, return the name + -------------------------------------------------------------------- + function luaY:str_checkname() + self:check_condition(tok == "", " expected") + local ts = seminfo + self:next() + self:log(" str_checkname: '"..ts.."'") + return ts + end + -------------------------------------------------------------------- + -- adds given string s in string pool, sets e as VK + -------------------------------------------------------------------- + function luaY:codestring(e, s) + e.k = "VK" + self:log(" codestring: "..string.format("%q", s)) + end + -------------------------------------------------------------------- + -- consume a name token, adds it to string pool + -------------------------------------------------------------------- + function luaY:checkname(e) + self:log(" checkname:") + self:codestring(e, self:str_checkname()) + end + +--[[-------------------------------------------------------------------- +-- state management functions with open/close pairs +----------------------------------------------------------------------]] + + -------------------------------------------------------------------- + -- enters a code unit, initializes elements + -------------------------------------------------------------------- + function luaY:enterblock(isbreakable) + local bl = {} -- per-block state + bl.isbreakable = isbreakable + bl.prev = fs.bl + bl.locallist = {} + fs.bl = bl + self:log(">> enterblock(isbreakable="..tostring(isbreakable)..")") + end + -------------------------------------------------------------------- + -- leaves a code unit, close any upvalues + -------------------------------------------------------------------- + function luaY:leaveblock() + local bl = fs.bl + fs.bl = bl.prev + self:log("<< leaveblock") + end + -------------------------------------------------------------------- + -- opening of a function + -------------------------------------------------------------------- + function luaY:open_func() + local new_fs -- per-function state + if not fs then -- top_fs is created early + new_fs = top_fs + else + new_fs = {} + end + new_fs.prev = fs -- linked list of function states + new_fs.bl = nil + new_fs.locallist = {} + fs = new_fs + self:log(">> open_func") + end + -------------------------------------------------------------------- + -- closing of a function + -------------------------------------------------------------------- + function luaY:close_func() + fs = fs.prev + self:log("<< close_func") + end + +--[[-------------------------------------------------------------------- +-- variable (global|local|upvalue) handling +-- * a pure parser does not really need this, but if we want to produce +-- useful output, might as well write minimal code to manage this... +-- * entry point is singlevar() for variable lookups +-- * three entry points for local variable creation, in order to keep +-- to original C calls, but the extra arguments such as positioning +-- are removed as we are not allocating registers -- we are only +-- doing simple classification +-- * lookup tables (bl.locallist) are maintained awkwardly in the basic +-- block data structures, PLUS the function data structure (this is +-- an inelegant hack, since bl is nil for the top level of a function) +----------------------------------------------------------------------]] + + -------------------------------------------------------------------- + -- register a local variable, set in active variable list + -- * used in new_localvarstr(), parlist(), fornum(), forlist(), + -- localfunc(), localstat() + -------------------------------------------------------------------- + function luaY:new_localvar(name) + local bl = fs.bl + local locallist + if bl then + locallist = bl.locallist + else + locallist = fs.locallist + end + locallist[name] = true + self:log(" new_localvar: '"..name.."'") + end + -------------------------------------------------------------------- + -- creates a new local variable given a name + -- * used in fornum(), forlist() for loop variables; in create_local() + -------------------------------------------------------------------- + function luaY:new_localvarstr(name) + self:new_localvar(name) + end + -------------------------------------------------------------------- + -- creates a single local variable and activates it + -- * used only in code_params() for "arg", body() for "self" + -------------------------------------------------------------------- + function luaY:create_local(name) + self:new_localvarstr(name) + end + + -------------------------------------------------------------------- + -- search the local variable namespace of the given fs for a match + -- * a simple lookup only, no active variable list kept, so no useful + -- index value can be returned by this function + -- * used only in singlevaraux() + -------------------------------------------------------------------- + function luaY:searchvar(fs, n) + local bl = fs.bl + if bl then + locallist = bl.locallist + while locallist do + if locallist[n] then return 1 end -- found + bl = bl.prev + locallist = bl and bl.locallist + end + end + locallist = fs.locallist + if locallist[n] then return 1 end -- found + return -1 -- not found + 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? + var.k = "VGLOBAL" -- default is global variable + else + local v = self:searchvar(fs, n) -- look up at current level + if v >= 0 then + var.k = "VLOCAL" + else -- not found at current level; try upper one + self:singlevaraux(fs.prev, n, var, 0) + if var.k == "VGLOBAL" then + -- handle global var processing here + else -- LOCAL or UPVAL + var.k = "VUPVAL" + end + end--if v + end--if fs + end + -------------------------------------------------------------------- + -- consume a name token, creates a variable (global|local|upvalue) + -- * used in prefixexp(), funcname() + -------------------------------------------------------------------- + function luaY:singlevar(v) + local varname = self:str_checkname() + self:singlevaraux(fs, varname, v, 1) + self:log(" singlevar(kind): '"..v.k.."'") + end + +--[[-------------------------------------------------------------------- +-- other parsing functions +-- * for table constructor, parameter list, argument list +----------------------------------------------------------------------]] + + -------------------------------------------------------------------- + -- parse a function name suffix, for function call specifications + -- * used in primaryexp(), funcname() + -------------------------------------------------------------------- + function luaY:field(v) + -- field -> ['.' | ':'] NAME + local key = {} + self:log(" field: operator="..tok) + self:next() -- skip the dot or colon + self:checkname(key) + v.k = "VINDEXED" + end + -------------------------------------------------------------------- + -- parse a table indexing suffix, for constructors, expressions + -- * used in recfield(), primaryexp() + -------------------------------------------------------------------- + function luaY:index(v) + -- index -> '[' expr ']' + self:log(">> index: begin '['") + self:next() -- skip the '[' + self:expr(v) + self:check("]") + self:log("<< index: end ']'") + end + -------------------------------------------------------------------- + -- parse a table record (hash) field + -- * used in constructor() + -------------------------------------------------------------------- + function luaY:recfield(cc) + -- recfield -> (NAME | '['exp1']') = exp1 + local key, val = {}, {} + if tok == "" then + self:log("recfield: name") + self:checkname(key) + else-- tok == '[' + self:log("recfield: [ exp1 ]") + self:index(key) + end + self:check("=") + self:expr(val) + end + -------------------------------------------------------------------- + -- emit a set list instruction if enough elements (LFIELDS_PER_FLUSH) + -- * note: retained in this skeleton because it modifies cc.v.k + -- * used in constructor() + -------------------------------------------------------------------- + function luaY:closelistfield(cc) + if cc.v.k == "VVOID" then return end -- there is no list item + cc.v.k = "VVOID" + end + -------------------------------------------------------------------- + -- parse a table list (array) field + -- * used in constructor() + -------------------------------------------------------------------- + function luaY:listfield(cc) + self:log("listfield: expr") + self:expr(cc.v) + end + -------------------------------------------------------------------- + -- parse a table constructor + -- * used in funcargs(), simpleexp() + -------------------------------------------------------------------- + function luaY:constructor(t) + -- constructor -> '{' [ field { fieldsep field } [ fieldsep ] ] '}' + -- field -> recfield | listfield + -- fieldsep -> ',' | ';' + self:log(">> constructor: begin") + local line = luaX.ln + local cc = {} + cc.v = {} + cc.t = t + t.k = "VRELOCABLE" + cc.v.k = "VVOID" + self:check("{") + repeat + self:testnext(";") -- compatibility only + if tok == "}" then break end + -- closelistfield(cc) here + local c = tok + if c == "" then -- may be listfields or recfields + if self:lookahead() ~= "=" then -- look ahead: expression? + self:listfield(cc) + else + self:recfield(cc) + end + elseif c == "[" then -- constructor_item -> recfield + self:recfield(cc) + else -- constructor_part -> listfield + self:listfield(cc) + end + until not self:testnext(",") and not self:testnext(";") + self:check_match("}", "{", line) + -- lastlistfield(cc) here + self:log("<< constructor: end") + end + -------------------------------------------------------------------- + -- parse the arguments (parameters) of a function declaration + -- * used in body() + -------------------------------------------------------------------- + function luaY:parlist() + -- parlist -> [ param { ',' param } ] + self:log(">> parlist: begin") + local dots = false + if tok ~= ")" then -- is 'parlist' not empty? + repeat + local c = tok + if c == "..." then + self:log("parlist: ... (dots)") + dots = true + self:next() + elseif c == "" then + self:new_localvar(self:str_checkname()) + else + self:syntaxerror(" or '...' expected") + end + until dots or not self:testnext(",") + end + -- was code_params() + if dots then + self:create_local("arg") + end + self:log("<< parlist: end") + end + -------------------------------------------------------------------- + -- parse the parameters of a function call + -- * contrast with parlist(), used in function declarations + -- * used in primaryexp() + -------------------------------------------------------------------- + function luaY:funcargs(f) + local args = {} + local line = luaX.ln + local c = tok + if c == "(" then -- funcargs -> '(' [ explist1 ] ')' + self:log(">> funcargs: begin '('") + if line ~= lastln then + self:syntaxerror("ambiguous syntax (function call x new statement)") + end + self:next() + if tok == ")" then -- arg list is empty? + args.k = "VVOID" + else + self:explist1(args) + end + self:check_match(")", "(", line) + elseif c == "{" then -- funcargs -> constructor + self:log(">> funcargs: begin '{'") + self:constructor(args) + elseif c == "" then -- funcargs -> STRING + self:log(">> funcargs: begin ") + self:codestring(args, seminfo) + self:next() -- must use 'seminfo' before 'next' + else + self:syntaxerror("function arguments expected") + return + end--if c + f.k = "VCALL" + self:log("<< funcargs: end -- expr is a VCALL") + end + +--[[-------------------------------------------------------------------- +-- mostly expression functions +----------------------------------------------------------------------]] + + -------------------------------------------------------------------- + -- parses an expression in parentheses or a single variable + -- * used in primaryexp() + -------------------------------------------------------------------- + function luaY:prefixexp(v) + -- prefixexp -> NAME | '(' expr ')' + local c = tok + if c == "(" then + self:log(">> prefixexp: begin ( expr ) ") + local line = self.ln + self:next() + self:expr(v) + self:check_match(")", "(", line) + self:log("<< prefixexp: end ( expr ) ") + elseif c == "" then + self:log("prefixexp: ") + self:singlevar(v) + else + self:syntaxerror("unexpected symbol") + end--if c + end + -------------------------------------------------------------------- + -- parses a prefixexp (an expression in parentheses or a single + -- variable) or a function call specification + -- * used in simpleexp(), assignment(), expr_stat() + -------------------------------------------------------------------- + function luaY:primaryexp(v) + -- primaryexp -> + -- prefixexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } + self:prefixexp(v) + while true do + local c = tok + if c == "." then -- field + self:log("primaryexp: '.' field") + self:field(v) + elseif c == "[" then -- '[' exp1 ']' + self:log("primaryexp: [ exp1 ]") + local key = {} + self:index(key) + elseif c == ":" then -- ':' NAME funcargs + self:log("primaryexp: : funcargs") + local key = {} + self:next() + self:checkname(key) + self:funcargs(v) + elseif c == "(" or c == "" or c == "{" then -- funcargs + self:log("primaryexp: "..c.." funcargs") + self:funcargs(v) + else + return + end--if c + end--while + end + -------------------------------------------------------------------- + -- parses general expression types, constants handled here + -- * used in subexpr() + -------------------------------------------------------------------- + function luaY:simpleexp(v) + -- simpleexp -> NUMBER | STRING | NIL | TRUE | FALSE | constructor + -- | FUNCTION body | primaryexp + local c = tok + if c == "" then + self:log("simpleexp: ="..seminfo) + v.k = "VK" + self:next() -- must use 'seminfo' before 'next' + elseif c == "" then + self:log("simpleexp: ="..seminfo) + self:codestring(v, seminfo) + self:next() -- must use 'seminfo' before 'next' + elseif c == "nil" then + self:log("simpleexp: nil") + v.k = "VNIL" + self:next() + elseif c == "true" then + self:log("simpleexp: true") + v.k = "VTRUE" + self:next() + elseif c == "false" then + self:log("simpleexp: false") + v.k = "VFALSE" + self:next() + elseif c == "{" then -- constructor + self:log("simpleexp: constructor") + self:constructor(v) + elseif c == "function" then + self:log("simpleexp: function") + self:next() + self:body(v, false, luaX.ln) + else + self:primaryexp(v) + end--if c + end + ------------------------------------------------------------------------ + -- 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. + -- * recursively called + -- * used in expr() + ------------------------------------------------------------------------ + function luaY:subexpr(v, limit) + -- subexpr -> (simpleexp | unop subexpr) { binop subexpr } + -- * where 'binop' is any binary operator with a priority + -- higher than 'limit' + local op = tok + local uop = unopr[op] + if uop then + self:log(" subexpr: uop='"..op.."'") + self:next() + self:subexpr(v, 8) -- UNARY_PRIORITY + else + self:simpleexp(v) + end + -- expand while operators have priorities higher than 'limit' + op = tok + local binop = binopr_left[op] + while binop and binop > limit do + local v2 = {} + self:log(">> subexpr: binop='"..op.."'") + self:next() + -- read sub-expression with higher priority + local nextop = self:subexpr(v2, binopr_right[op]) + self:log("<< subexpr: -- evaluate") + op = nextop + binop = binopr_left[op] + end + 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 cond(), explist1(), index(), recfield(), listfield(), + -- prefixexp(), while_stat(), exp1() + -------------------------------------------------------------------- + function luaY:expr(v) + -- expr -> subexpr + self:log("expr:") + self:subexpr(v, -1) + end + +--[[-------------------------------------------------------------------- +-- third level parsing functions +----------------------------------------------------------------------]] + + -------------------------------------------------------------------- + -- parse a variable assignment sequence + -- * recursively called + -- * used in expr_stat() + -------------------------------------------------------------------- + function luaY:assignment(v) + local e = {} + local c = v.v.k + self:check_condition(c == "VLOCAL" or c == "VUPVAL" or c == "VGLOBAL" + or c == "VINDEXED", "syntax error") + if self:testnext(",") then -- assignment -> ',' primaryexp assignment + local nv = {} -- expdesc + nv.v = {} + self:log("assignment: ',' -- next LHS element") + self:primaryexp(nv.v) + -- lparser.c deals with some register usage conflict here + self:assignment(nv) + else -- assignment -> '=' explist1 + self:check("=") + self:log("assignment: '=' -- RHS elements follows") + self:explist1(e) + return -- avoid default + end + e.k = "VNONRELOC" + end + -------------------------------------------------------------------- + -- parse a for loop body for both versions of the for loop + -- * used in fornum(), forlist() + -------------------------------------------------------------------- + function luaY:forbody(line, isnum) + self:check("do") + self:enterblock(true) -- loop block + self:block() + self:leaveblock() + end + -------------------------------------------------------------------- + -- parse a numerical for loop, calls forbody() + -- * used in for_stat() + -------------------------------------------------------------------- + function luaY:fornum(varname, line) + -- fornum -> NAME = exp1, exp1 [, exp1] DO body + self:new_localvar(varname) + self:new_localvarstr("(for limit)") + self:new_localvarstr("(for step)") + self:log(">> fornum: begin") + self:check("=") + self:log("fornum: index start") + self:exp1() -- initial value + self:check(",") + self:log("fornum: index stop") + self:exp1() -- limit + if self:testnext(",") then + self:log("fornum: index step") + self:exp1() -- optional step + else + -- default step = 1 + end + self:log("fornum: body") + self:forbody(line, true) + self:log("<< fornum: end") + end + -------------------------------------------------------------------- + -- parse a generic for loop, calls forbody() + -- * used in for_stat() + -------------------------------------------------------------------- + function luaY:forlist(indexname) + -- forlist -> NAME {, NAME} IN explist1 DO body + self:log(">> forlist: begin") + local e = {} + self:new_localvarstr("(for generator)") + self:new_localvarstr("(for state)") + self:new_localvar(indexname) + while self:testnext(",") do + self:new_localvar(self:str_checkname()) + end + self:check("in") + local line = line + self:log("forlist: explist1") + self:explist1(e) + self:log("forlist: body") + self:forbody(line, false) + self:log("<< forlist: end") + end + -------------------------------------------------------------------- + -- parse a function name specification + -- * used in func_stat() + -------------------------------------------------------------------- + function luaY:funcname(v) + -- funcname -> NAME {field} [':' NAME] + self:log(">> funcname: begin") + local needself = false + self:singlevar(v) + while tok == "." do + self:log("funcname: -- '.' field") + self:field(v) + end + if tok == ":" then + self:log("funcname: -- ':' field") + needself = true + self:field(v) + end + self:log("<< funcname: end") + return needself + end + -------------------------------------------------------------------- + -- parse the single expressions needed in numerical for loops + -- * used in fornum() + -------------------------------------------------------------------- + function luaY:exp1() + -- exp1 -> expr + local e = {} + self:log(">> exp1: begin") + self:expr(e) + self:log("<< exp1: end") + end + -------------------------------------------------------------------- + -- parse condition in a repeat statement or an if control structure + -- * used in repeat_stat(), test_then_block() + -------------------------------------------------------------------- + function luaY:cond(v) + -- cond -> expr + self:log(">> cond: begin") + self:expr(v) -- read condition + self:log("<< cond: end") + end + -------------------------------------------------------------------- + -- parse part of an if control structure, including the condition + -- * used in if_stat() + -------------------------------------------------------------------- + function luaY:test_then_block(v) + -- test_then_block -> [IF | ELSEIF] cond THEN block + self:next() -- skip IF or ELSEIF + self:log("test_then_block: test condition") + self:cond(v) + self:check("then") + self:log("test_then_block: then block") + self:block() -- 'then' part + end + -------------------------------------------------------------------- + -- parse a local function statement + -- * used in local_stat() + -------------------------------------------------------------------- + function luaY:localfunc() + -- localfunc -> NAME body + local v, b = {} + self:log("localfunc: begin") + self:new_localvar(self:str_checkname()) + v.k = "VLOCAL" + self:log("localfunc: body") + self:body(b, false, luaX.ln) + self:log("localfunc: end") + end + -------------------------------------------------------------------- + -- parse a local variable declaration statement + -- * used in local_stat() + -------------------------------------------------------------------- + function luaY:localstat() + -- localstat -> NAME {',' NAME} ['=' explist1] + self:log(">> localstat: begin") + local e = {} + repeat + self:new_localvar(self:str_checkname()) + until not self:testnext(",") + if self:testnext("=") then + self:log("localstat: -- assignment") + self:explist1(e) + else + e.k = "VVOID" + end + self:log("<< localstat: end") + end + -------------------------------------------------------------------- + -- parse a list of comma-separated expressions + -- * used in return_stat(), localstat(), funcargs(), assignment(), + -- forlist() + -------------------------------------------------------------------- + function luaY:explist1(e) + -- explist1 -> expr { ',' expr } + self:log(">> explist1: begin") + self:expr(e) + while self:testnext(",") do + self:log("explist1: ',' -- continuation") + self:expr(e) + end + self:log("<< explist1: end") + end + -------------------------------------------------------------------- + -- parse function declaration body + -- * used in simpleexp(), localfunc(), func_stat() + -------------------------------------------------------------------- + function luaY:body(e, needself, line) + -- body -> '(' parlist ')' chunk END + self:open_func() + self:log("body: begin") + self:check("(") + if needself then + self:create_local("self") + end + self:log("body: parlist") + self:parlist() + self:check(")") + self:log("body: chunk") + self:chunk() + self:check_match("end", "function", line) + self:log("body: end") + self:close_func() + end + -------------------------------------------------------------------- + -- parse a code block or unit + -- * used in do_stat(), while_stat(), repeat_stat(), forbody(), + -- test_then_block(), if_stat() + -------------------------------------------------------------------- + function luaY:block() + -- block -> chunk + self:log("block: begin") + self:enterblock(false) + self:chunk() + self:leaveblock() + self:log("block: end") + end + +--[[-------------------------------------------------------------------- +-- second level parsing functions, all with '_stat' suffix +-- * stat() -> *_stat() +----------------------------------------------------------------------]] + + -------------------------------------------------------------------- + -- initial parsing for a for loop, calls fornum() or forlist() + -- * used in stat() + -------------------------------------------------------------------- + function luaY:for_stat() + -- stat -> for_stat -> fornum | forlist + local line = line + self:log("for_stat: begin") + self:enterblock(false) -- block to control variable scope + self:next() -- skip 'for' + local varname = self:str_checkname() -- first variable name + local c = tok + if c == "=" then + self:log("for_stat: numerical loop") + self:fornum(varname, line) + elseif c == "," or c == "in" then + self:log("for_stat: list-based loop") + self:forlist(varname) + else + self:syntaxerror("'=' or 'in' expected") + end + self:check_match("end", "for", line) + self:leaveblock() + self:log("for_stat: end") + end + -------------------------------------------------------------------- + -- parse a while-do control structure, body processed by block() + -- * used in stat() + -------------------------------------------------------------------- + function luaY:while_stat() + -- stat -> while_stat -> WHILE cond DO block END + local line = line + local v = {} + self:next() -- skip WHILE + self:log("while_stat: begin/condition") + self:expr(v) -- parse condition + self:enterblock(true) + self:check("do") + self:log("while_stat: block") + self:block() + self:check_match("end", "while", line) + self:leaveblock() + self:log("while_stat: end") + end + -------------------------------------------------------------------- + -- parse a repeat-until control structure, body parsed by block() + -- * used in stat() + -------------------------------------------------------------------- + function luaY:repeat_stat() + -- stat -> repeat_stat -> REPEAT block UNTIL cond + local line = line + local v = {} + self:log("repeat_stat: begin") + self:enterblock(true) + self:next() + self:block() + self:check_match("until", "repeat", line) + self:log("repeat_stat: condition") + self:cond(v) + self:leaveblock() + self:log("repeat_stat: end") + end + -------------------------------------------------------------------- + -- parse an if control structure + -- * used in stat() + -------------------------------------------------------------------- + function luaY:if_stat() + -- stat -> if_stat -> IF cond THEN block + -- {ELSEIF cond THEN block} [ELSE block] END + local line = line + local v = {} + self:log("if_stat: if...then") + self:test_then_block(v) -- IF cond THEN block + while tok == "elseif" do + self:log("if_stat: elseif...then") + self:test_then_block(v) -- ELSEIF cond THEN block + end + if tok == "else" then + self:log("if_stat: else...") + self:next() -- skip ELSE + self:block() -- 'else' part + end + self:check_match("end", "if", line) + self:log("if_stat: end") + end + -------------------------------------------------------------------- + -- parse a return statement + -- * used in stat() + -------------------------------------------------------------------- + function luaY:return_stat() + -- stat -> return_stat -> RETURN explist + local e = {} + self:next() -- skip RETURN + local c = tok + if block_follow[c] or c == ";" then + -- return no values + self:log("return_stat: no return values") + else + self:log("return_stat: begin") + self:explist1(e) -- optional return values + self:log("return_stat: end") + end + end + -------------------------------------------------------------------- + -- parse a break statement + -- * used in stat() + -------------------------------------------------------------------- + function luaY:break_stat() + -- stat -> break_stat -> BREAK + local bl = fs.bl + self:next() -- skip BREAK + while bl and not bl.isbreakable do -- find a breakable block + bl = bl.prev + end + if not bl then + self:syntaxerror("no loop to break") + end + self:log("break_stat: -- break out of loop") + end + -------------------------------------------------------------------- + -- parse a function call with no returns or an assignment statement + -- * the struct with .prev is used for name searching in lparse.c, + -- so it is retained for now; present in assignment() also + -- * used in stat() + -------------------------------------------------------------------- + function luaY:expr_stat() + -- stat -> expr_stat -> func | assignment + local v = {} + v.v = {} + self:primaryexp(v.v) + if v.v.k == "VCALL" then -- stat -> func + -- call statement uses no results + self:log("expr_stat: function call k='"..v.v.k.."'") + else -- stat -> assignment + self:log("expr_stat: assignment k='"..v.v.k.."'") + v.prev = nil + self:assignment(v) + end + end + -------------------------------------------------------------------- + -- parse a function statement + -- * used in stat() + -------------------------------------------------------------------- + function luaY:function_stat() + -- stat -> function_stat -> FUNCTION funcname body + local line = line + local v, b = {}, {} + self:log("function_stat: begin") + self:next() -- skip FUNCTION + local needself = self:funcname(v) + self:log("function_stat: body needself='"..tostring(needself).."'") + self:body(b, needself, line) + self:log("function_stat: end") + end + -------------------------------------------------------------------- + -- parse a simple block enclosed by a DO..END pair + -- * used in stat() + -------------------------------------------------------------------- + function luaY:do_stat() + -- stat -> do_stat -> DO block END + self:next() -- skip DO + self:log("do_stat: begin") + self:block() + self:log("do_stat: end") + self:check_match("end", "do", line) + end + -------------------------------------------------------------------- + -- parse a statement starting with LOCAL + -- * used in stat() + -------------------------------------------------------------------- + function luaY:local_stat() + -- stat -> local_stat -> LOCAL FUNCTION localfunc + -- -> LOCAL localstat + self:next() -- skip LOCAL + if self:testnext("function") then -- local function? + self:log("local_stat: local function") + self:localfunc() + else + self:log("local_stat: local statement") + self:localstat() + end + end + +--[[-------------------------------------------------------------------- +-- main function, top level parsing functions +-- * [entry] -> parser() -> chunk() -> stat() +----------------------------------------------------------------------]] + + -------------------------------------------------------------------- + -- initial parsing for statements, calls '_stat' suffixed functions + -- * used in chunk() + -------------------------------------------------------------------- + function luaY:stat() + line = luaX.ln + local c = tok + local fn = stat_call[c] + -- handles: if while do for repeat function local return break + if fn then + self:log("-- STATEMENT: begin '"..c.."' line="..line) + self[fn](self) + self:log("-- STATEMENT: end '"..c.."'") + -- return or break must be last statement + if c == "return" or c == "break" then return true end + else + self:log("-- STATEMENT: begin 'expr' line="..line) + self:expr_stat() + self:log("-- STATEMENT: end 'expr'") + end + self:log("") + return false + end + -------------------------------------------------------------------- + -- parse a chunk, which consists of a bunch of statements + -- * used in parser(), body(), block() + -------------------------------------------------------------------- + function luaY:chunk() + -- chunk -> { stat [';'] } + self:log("chunk:") + local islast = false + while not islast and not block_follow[tok] do + islast = self:stat() + self:testnext(";") + end + end + -------------------------------------------------------------------- + -- performs parsing, returns parsed data structure + -------------------------------------------------------------------- + function luaY:parser() + self:log("-- TOP: begin") + self:open_func() + self:log("") + self:next() -- read first token + self:chunk() + self:check_condition(tok == "", " expected") + self:close_func() + self:log("-- TOP: end") + return top_fs + end + -------------------------------------------------------------------- + return luaY -- return actual module to user, done +end -- cgit v1.1