Removed the partial AST generation on invalid inputs
diff --git a/metalua/compiler.lua b/metalua/compiler.lua index 96c4e3d..c54ed52 100644 --- a/metalua/compiler.lua +++ b/metalua/compiler.lua
@@ -17,9 +17,9 @@ -- -------------------------------------------------------------------------------- ---*-lua-*----------------------------------------------------------------------- +-------------------------------------------------------------------------------- -- --- Convert between various code representation formats. Some atomic +-- Convert between various code representation formats. Atomic -- converters are written in extenso, others are composed automatically -- by chaining the atomic ones together in a closure. -- @@ -29,10 +29,10 @@ -- * luastring: these sources as a single string. -- * lexstream: a stream of lexemes. -- * ast: an abstract syntax tree. --- * proto: a (Yueliang) struture containing a high level --- representation of bytecode. Largely based on the +-- * proto: a (Yueliang) struture containing a high level +-- representation of bytecode. Largely based on the -- Proto structure in Lua's VM --- * luacstring: a string dump of the function, as taken by +-- * luacstring: a string dump of the function, as taken by -- loadstring() and produced by string.dump(). -- * function: an executable lua function in RAM. -- @@ -63,90 +63,6 @@ M.order = table.transpose(M.sequence) -local function check_ast(kind, ast) - if not ast then return check_ast('block', kind) end - assert(type(ast)=='table', "wrong AST type") - local cfg = {} - local function error2ast(error_node, ...) - if not error_node.earlier then - if error_node.tag=='Error' then - cfg.errorfound = true - cfg.errormsg = error_node[1] - - -- Try to extract error position in source - local li = error_node.lineinfo and error_node.lineinfo.first - - -- Fill positions if undefined or not narrow enough - if li and ( not cfg.positions or cfg.positions.offset < li.offset ) then - cfg.positions = { - column = li.column, - line = li.line, - offset = li.offset - } - end - else - -- This block is for dealing with errors which are not error - -- nodes. It would be soooo nice to get rid of it. - -- TODO: Try to remove this bug when issue #20 is fixed - local li - for _, n in ipairs{ error_node, ... } do - if n.lineinfo then - li = n.lineinfo - cfg.errorfound = true - break - end - end - local posmsg - if li then - local column = li.first.column - local line = li.first.line - local offset = li.first.offset - posmsg = string.format("line %d, char %d, offset %d", - line, column, offset) - cfg.positions = { - column = column, - line = line, - offset = offset - } - else - posmsg = "unknown source position" - end - local msg = "Invalid node ".. - (error_node.tag and "tag "..tostring(error_node.tag) or "without tag").. - (posmsg and " at "..posmsg or "") - cfg.errormsg = msg - end - end - end - local f = require 'metalua.treequery.walk' [kind] - cfg.malformed=error2ast - cfg.unknown= error2ast - cfg.error= error2ast - f(cfg, ast) - return cfg.errorfound == nil, cfg.errormsg, cfg.positions -end - -M.check_ast = check_ast - -local function find_error(ast, nested) - checks('table', '?table') - nested = nested or { } - if nested[ast] then return "Cyclic AST" end - nested[ast]=true - if ast.tag=='Error' then - local pos = tostring(ast.lineinfo.first) - return pos..": "..ast[1] - end - for _, item in ipairs(ast) do - if type(item)=='table' then - local err=find_error(item) - if err then return err end - end - end - nested[ast]=nil - return nil -end - local CONV = { } -- conversion metatable __index function CONV :srcfile_to_src(x, name) @@ -176,8 +92,6 @@ function CONV :ast_to_proto(ast, name) checks('metalua.compiler', 'table', '?string') --table.print(ast, 'nohash', 1) io.flush() - local err = find_error(ast) - if err then error(err) end local f = require 'metalua.compiler.bytecode.compile'.ast_to_proto return f(ast, name), name end
diff --git a/metalua/compiler/bytecode/compile.lua b/metalua/compiler/bytecode/compile.lua index 1e2c972..7b9116c 100644 --- a/metalua/compiler/bytecode/compile.lua +++ b/metalua/compiler/bytecode/compile.lua
@@ -20,41 +20,10 @@ ---------------------------------------------------------------------- -- --- WARNING! You're entering a hackish area, proceed at your own risks! +-- This code mainly results from the borrowing, then ruthless abuse, of +-- Yueliang's implementation of Lua 5.0 compiler. -- --- This code partly results from the borrowing, then ruthless abuse, of --- Yueliang's implementation of Lua 5.0 compiler. I claim --- responsibility for all of the ugly, dirty stuff that you might spot --- in it. --- --- Eventually, this code will be rewritten, either in Lua or more --- probably in C. Meanwhile, if you're interested into digging --- metalua's sources, this is not the best part to invest your time --- on. --- --- End of warning. --- ----------------------------------------------------------------------- - ----------------------------------------------------------------------- --- Metalua. --- --- Summary: Compile ASTs to Lua 5.1 VM function prototype. --- Largely based on: --- --- * Yueliang (http://luaforge.net/projects/yueliang), --- yueliang-0.1.2/orig-5.0.2/lparser.lua --- --- * Lua 5.1 sources (http://www.lua.org), src/lparser.c --- ----------------------------------------------------------------------- --- --- Copyright (c) 2006-2008, Fabien Fleutot <metalua@gmail.com>. --- --- This software is released under the MIT Licence, see licence.txt --- for details. --- ----------------------------------------------------------------------- +--------------------------------------------------------------------- local luaK = require 'metalua.compiler.bytecode.lcode' local luaP = require 'metalua.compiler.bytecode.lopcodes' @@ -748,9 +717,7 @@ first = fs.nactvar nret = luaK.LUA_MULTRET -- return all values elseif nret == 1 then - if ast[1].tag ~= "Error" then - first = luaK:exp2anyreg(fs, e) - end + first = luaK:exp2anyreg(fs, e) else --printf("* Return multiple vals in nextreg %i", fs.freereg) luaK:exp2nextreg(fs, e) -- values must go to the 'stack' @@ -1275,8 +1242,6 @@ --printf(" * End of Stat") end -function expr.Error() end - ------------------------------------------------------------------------ -- Main function: ast --> proto ------------------------------------------------------------------------ @@ -1292,13 +1257,4 @@ return fs.f, source end -local function Error (fs, ast, v) - local msg = string.format ("Error node in AST at position %s: %s", - tostring(ast.lineinfo), - table.tostring(ast, nohash)) - error(msg) -end - -expr.Error, stat.Error = Error, Error - return M \ No newline at end of file
diff --git a/metalua/compiler/parser/expr.lua b/metalua/compiler/parser/expr.lua index 935d2c9..b277f4e 100644 --- a/metalua/compiler/parser/expr.lua +++ b/metalua/compiler/parser/expr.lua
@@ -112,7 +112,7 @@ else msg = "Unexpected expr token " .. table.tostring (a, 'nohash') end - return gg.parse_error (lx, msg) + gg.parse_error (lx, msg) end return a end @@ -120,29 +120,29 @@ -------------------------------------------------------------------------------- -- Builder generator for operators. Wouldn't be worth it if "|x|" notation --- were allowed, but then lua 5.1 wouldn't compile it +-- were allowed, but then lua 5.1 wouldn't compile it -------------------------------------------------------------------------------- -- opf1 = |op| |_,a| `Op{ op, a } -local function opf1 (op) return +local function opf1 (op) return function (_,a) return { tag="Op", op, a } end end -- opf2 = |op| |a,_,b| `Op{ op, a, b } -local function opf2 (op) return +local function opf2 (op) return function (a,_,b) return { tag="Op", op, a, b } end end -- opf2r = |op| |a,_,b| `Op{ op, b, a } -- (args reversed) -local function opf2r (op) return +local function opf2r (op) return function (a,_,b) return { tag="Op", op, b, a } end end local function op_ne(a, _, b) -- This version allows to remove the "ne" operator from the AST definition. -- However, it doesn't always produce the exact same bytecode as Lua 5.1. - return { tag="Op", "not", + return { tag="Op", "not", { tag="Op", "eq", a, b, lineinfo= { first = a.lineinfo.first, last = b.lineinfo.last } } } end - + -------------------------------------------------------------------------------- -- @@ -202,7 +202,7 @@ return {tag="Invoke", obj, mlp_misc.id2string(m_name), unpack(args)} end}, { "+{", mlp_meta.quote_content, "}", builder = function (f, arg) return {tag="Call", f, arg[1] } end }, - default = { name="opt_string_arg", parse = mlp_misc.opt_string, builder = function(f, arg) + default = { name="opt_string_arg", parse = mlp_misc.opt_string, builder = function(f, arg) return {tag="Call", f, arg } end } } } return M \ No newline at end of file
diff --git a/metalua/compiler/parser/ext.lua b/metalua/compiler/parser/ext.lua index e6cd1f2..9e4c776 100644 --- a/metalua/compiler/parser/ext.lua +++ b/metalua/compiler/parser/ext.lua
@@ -110,7 +110,7 @@ return { tag="Op", op, a, b } end local function f(a,b) - if #a ~= #b then return gg.parse_error "assymetric operator+assignment" end + if #a ~= #b then gg.parse_error "assymetric operator+assignment" end local right = { } local r = { tag="Set", a, right } for i=1, #a do right[i] = { tag="Op", op, a[i], b[i] } end
diff --git a/metalua/compiler/parser/misc.lua b/metalua/compiler/parser/misc.lua index 04c4f98..27bd5d3 100644 --- a/metalua/compiler/parser/misc.lua +++ b/metalua/compiler/parser/misc.lua
@@ -77,7 +77,7 @@ if lx:is_keyword (a, "-{") then local v = splice(lx) if v.tag ~= "Id" and v.tag ~= "Splice" then - return gg.parse_error(lx, "Bad id splice") + gg.parse_error(lx, "Bad id splice") end return v elseif a.tag == "Id" then return lx:next() @@ -111,9 +111,8 @@ -- Morally, this is what I want: -- return `String{ `Index{ `Splice{ id[1] }, `Number 1 } } -- That is, without sugar: - return {tag="String", {tag="Index", {tag="Splice", id[1] }, + return {tag="String", {tag="Index", {tag="Splice", id[1] }, {tag="Number", 1 } } } - elseif id.tag == 'Error' then return id else error ("Identifier expected: "..table.tostring(id, 'nohash')) end end @@ -125,7 +124,7 @@ if lx:is_keyword (a, "-{") then local v = splice(lx) if v.tag ~= "String" and v.tag ~= "Splice" then - return gg.parse_error(lx,"Bad string splice") + gg.parse_error(lx,"Bad string splice") end return v elseif a.tag == "String" then return lx:next() @@ -157,11 +156,11 @@ local function chunk (lx) if lx:peek().tag == 'Eof' then return { } -- handle empty files - else + else M.skip_initial_sharp_comment (lx) local chunk = mlp.block (lx) - if lx:peek().tag ~= "Eof" then - table.insert(chunk, gg.parse_error(lx, "End-of-file expected")) + if lx:peek().tag ~= "Eof" then + gg.parse_error(lx, "End-of-file expected") end return chunk end
diff --git a/metalua/compiler/parser/stat.lua b/metalua/compiler/parser/stat.lua index fc9a064..3c671db 100644 --- a/metalua/compiler/parser/stat.lua +++ b/metalua/compiler/parser/stat.lua
@@ -56,7 +56,7 @@ M.block_terminators = { "else", "elseif", "end", "until", ")", "}", "]" } -- FIXME: this must be handled from within GG!!! -function M.block_terminators :add(x) +function M.block_terminators :add(x) if type (x) == "table" then for _, y in ipairs(x) do self :add (y) end else table.insert (self, x) end end @@ -84,15 +84,15 @@ -- sync if it was relying on a copy. -------------------------------------------------------------------------------- local return_expr_list_parser = gg.multisequence{ - { ";" , builder = function() return { } end }, - default = gg.list { + { ";" , builder = function() return { } end }, + default = gg.list { expr, separators = ",", terminators = M.block_terminators } } local for_vars_list = gg.list{ name = "for variables list", primary = mlp_misc.id, - separators = ",", + separators = ",", terminators = "in" } -------------------------------------------------------------------------------- @@ -101,19 +101,19 @@ -------------------------------------------------------------------------------- function M.for_header (lx) local vars = mlp.id_list(lx) - if lx :is_keyword (lx:peek(), "=") then - if #vars ~= 1 then - return gg.parse_error (lx, "numeric for only accepts one variable") + if lx :is_keyword (lx:peek(), "=") then + if #vars ~= 1 then + gg.parse_error (lx, "numeric for only accepts one variable") end lx:next() -- skip "=" local exprs = mlp.expr_list (lx) if #exprs < 2 or #exprs > 3 then - return gg.parse_error (lx, "numeric for requires 2 or 3 boundaries") + gg.parse_error (lx, "numeric for requires 2 or 3 boundaries") end return { tag="Fornum", vars[1], unpack (exprs) } else if not lx :is_keyword (lx :next(), "in") then - return gg.parse_error (lx, '"=" or "in" expected in for loop') + gg.parse_error (lx, '"=" or "in" expected in for loop') end local exprs = mlp.expr_list (lx) return { tag="Forin", vars, exprs } @@ -133,7 +133,7 @@ -------------------------------------------------------------------------------- -- Function def parser helper: ( : id )? -------------------------------------------------------------------------------- -local method_name = gg.onkeyword{ name = "method invocation", ":", mlp_misc.id, +local method_name = gg.onkeyword{ name = "method invocation", ":", mlp_misc.id, transformers = { function(x) return x and x.tag=='Id' and mlp_misc.id2string(x) end } } -------------------------------------------------------------------------------- @@ -141,33 +141,29 @@ -------------------------------------------------------------------------------- local function funcdef_builder(x) - local name = x[1] or gg.earlier_error() - local method = x[2] - local func = x[3] or gg.earlier_error() + local name, method, func = unpack(x) - - if method then + if method then name = { tag="Index", name, method, lineinfo = { first = name.lineinfo.first, last = method.lineinfo.last } } - table.insert (func[1], 1, {tag="Id", "self"}) + table.insert (func[1], 1, {tag="Id", "self"}) end - local r = { tag="Set", {name}, {func} } + local r = { tag="Set", {name}, {func} } r[1].lineinfo = name.lineinfo r[2].lineinfo = func.lineinfo return r -end +end -------------------------------------------------------------------------------- -- if statement builder -------------------------------------------------------------------------------- local function if_builder (x) - local cb_pairs, else_block, r = x[1], x[2], {tag="If"} - if cb_pairs.tag=='Error' then return cb_pairs end -- propagate errors - local n_pairs = #cb_pairs + local cond_block_pairs, else_block, r = x[1], x[2], {tag="If"} + local n_pairs = #cond_block_pairs for i = 1, n_pairs do - local cond, block = unpack(cb_pairs[i]) + local cond, block = unpack(cond_block_pairs[i]) r[2*i-1], r[2*i] = cond, block end if else_block then table.insert(r, #r+1, else_block) end @@ -214,20 +210,21 @@ else assert (#e > 0) if #e > 1 then - return gg.parse_error (lx, + gg.parse_error (lx, "comma is not a valid statement separator; statement can be ".. "separated by semicolons, or not separated at all") end if e[1].tag ~= "Call" and e[1].tag ~= "Invoke" then local typename if e[1].tag == 'Id' then typename = '("'..e[1][1]..'") is an identifier' - elseif e[1].tag == 'Op' then + elseif e[1].tag == 'Op' then typename = "is an arithmetic operation" else typename = "is of type '"..(e[1].tag or "<list>").."'" end - return gg.parse_error (lx, "This expression " .. typename .. - "; a statement was expected, and only function and method call ".. - "expressions can be used as statements"); + gg.parse_error (lx, + "This expression %s; ".. + "a statement was expected, and only function and method call ".. + "expressions can be used as statements", typename); end return e[1] end @@ -235,14 +232,14 @@ M.local_stat_parser = gg.multisequence{ -- local function <name> <func_val> - { "function", mlp_misc.id, func_val, builder = - function(x) + { "function", mlp_misc.id, func_val, builder = + function(x) local vars = { x[1], lineinfo = x[1].lineinfo } local vals = { x[2], lineinfo = x[2].lineinfo } - return { tag="Localrec", vars, vals } + return { tag="Localrec", vars, vals } end }, -- local <id_list> ( = <expr_list> )? - default = gg.sequence{ + default = gg.sequence{ gg.list{ primary = annot.opt(mlp_misc.id, 'tf'), separators = ',' }, @@ -256,17 +253,17 @@ -------------------------------------------------------------------------------- -- statement -------------------------------------------------------------------------------- -M.stat = gg.multisequence { +M.stat = gg.multisequence { name = "statement", - { "do", M.block, "end", builder = + { "do", M.block, "end", builder = function (x) return { tag="Do", unpack (x[1]) } end }, - { "for", M.for_header, "do", M.block, "end", builder = + { "for", M.for_header, "do", M.block, "end", builder = function (x) x[1][#x[1]+1] = x[2]; return x[1] end }, { "function", func_name, method_name, func_val, builder=funcdef_builder }, { "while", expr, "do", M.block, "end", builder = "While" }, { "repeat", M.block, "until", expr, builder = "Repeat" }, { "local", M.local_stat_parser, builder = unpack }, - { "return", return_expr_list_parser, builder = + { "return", return_expr_list_parser, builder = function(x) x[1].tag='Return'; return x[1] end }, { "break", builder = function() return { tag="Break" } end }, { "-{", mlp_meta.splice_content, "}", builder = unpack },
diff --git a/metalua/compiler/parser/table.lua b/metalua/compiler/parser/table.lua index 713edbc..373a192 100644 --- a/metalua/compiler/parser/table.lua +++ b/metalua/compiler/parser/table.lua
@@ -19,15 +19,6 @@ -------------------------------------------------------------------------------- -- --- Copyright (c) 2006-2012, Fabien Fleutot <metalua@gmail.com>. --- --- This software is released under the MIT Licence, see licence.txt --- for details. --- --------------------------------------------------------------------------------- - --------------------------------------------------------------------------------- --- -- Exported API: -- * [M.bracket_field()] -- * [M.field()] @@ -60,17 +51,16 @@ function M.field (lx) if lx :is_keyword (lx :peek(), "[") then return M.bracket_field (lx) end local e = _expr (lx) - if lx :is_keyword (lx :peek(), "=") then + if lx :is_keyword (lx :peek(), "=") then lx :next(); -- skip the "=" -- Allowing only the right type of key, here `Id local etag = e.tag if etag ~= 'Id' then - local message = string.format('Identifier expected, got %s.', etag) - return gg.parse_error(lx, message) + gg.parse_error(lx, 'Identifier expected, got %s.', etag) end local key = mlp.id2string(e) local val = _expr(lx) - local r = { tag="Pair", key, val } + local r = { tag="Pair", key, val } r.lineinfo = { first = key.lineinfo.first, last = val.lineinfo.last } return r else return e end @@ -79,9 +69,9 @@ -------------------------------------------------------------------------------- -- table constructor, without enclosing braces; returns a full table object -------------------------------------------------------------------------------- -M.content = gg.list { - primary = function(...) return M.field(...) end, - separators = { ",", ";" }, +M.content = gg.list { + primary = function(...) return M.field(...) end, + separators = { ",", ";" }, terminators = "}", builder = "Table" }
diff --git a/metalua/grammar/generator.lua b/metalua/grammar/generator.lua index 078b291..67972bf 100644 --- a/metalua/grammar/generator.lua +++ b/metalua/grammar/generator.lua
@@ -66,25 +66,11 @@ ------------------------------------------------------------------------------- -- parser metatable, which maps __call to method parse, and adds some -- error tracing boilerplate. --- --- TODO: parsers don't throw errors anymore, they return `Error{ } nodes instead. --- Therefore the accumulation of error positions won't work anymore. --- Instead, the mlc.check_ast() function should retrace the whole path to --- the actual error position. --- ------------------------------------------------------------------------------- local parser_metatable = { } function parser_metatable :__call (lx, ...) - --return self :parse(lx, ...) - local r = self :parse (lx, ...) - if r and type(r) ~= 'table' then - printf("Grammar generator: parser %s returned non-table %s at %s", - table.tostring(self.name), - table.tostring(r), - table.tostring((...):peek())) - end - return r + return self :parse (lx, ...) end ------------------------------------------------------------------------------- @@ -110,39 +96,26 @@ ------------------------------------------------------------------------------- -- Parse a sequence, without applying builder nor transformers. --- Won't fail: if the parsing can't be completed, the missing results --- will be filled with Error nodes. --- --- TODO: This introduces a new assumption in gg that it must produce --- AST as results. This should be decoupled by passing an error handler --- from outside. ------------------------------------------------------------------------------- local function raw_parse_sequence (lx, p) - local r = { } - local failed = false - for i=1, #p do - local e=p[i] - if failed then - if type(e)=="string" then table.insert(r, M.earlier_error(lx)) end - elseif type(e) == "string" then - local kw = lx :next() - if not lx :is_keyword (kw, e) then - table.insert(r, { - tag = 'Error', - lineinfo = kw.lineinfo, - "A keyword was expected, probably `"..e.."'."}) - failed=true - end - elseif M.is_parser (e) then - local x = e(lx) - if type(x)=='table' and x.tag=='Error' then failed=true end - table.insert (r, x) - else -- Invalid parser definition, this is not a parsing error, it must fail. - return M.parse_error (lx,"Sequence `%s': element #%i is neither a string ".. - "nor a parser: %s", p.name, i, table.tostring(e)) - end - end - return r + local r = { } + for i=1, #p do + local e=p[i] + if type(e) == "string" then + local kw = lx :next() + if not lx :is_keyword (kw, e) then + M.parse_error( + lx, "A keyword was expected, probably `%s'.", e) + end + elseif M.is_parser (e) then + table.insert (r, e(lx)) + else -- Invalid parser definition, this is *not* a parsing error + error(string.format( + "Sequence `%s': element #%i is neither a string nor a parser: %s", + p.name, i, table.tostring(e))) + end + end + return r end ------------------------------------------------------------------------------- @@ -185,35 +158,20 @@ line, column, offset = -1, -1, -1 end - local msg = string.format("line %i, char %i: "..fmt, line, column, ...) + local msg = string.format("line %i, char %i: "..fmt, line, column, ...) local src = lx.src if offset>0 and src then local i, j = offset, offset while src:sub(i,i) ~= '\n' and i>=0 do i=i-1 end - while src:sub(j,j) ~= '\n' and j<=#src do j=j+1 end + while src:sub(j,j) ~= '\n' and j<=#src do j=j+1 end local srcline = src:sub (i+1, j-1) local idx = string.rep (" ", column).."^" msg = string.format("%s\n>>> %s\n>>> %s", msg, srcline, idx) end - lx :kill() - return { tag='Error', msg , lineinfo = positions } + --lx :kill() + error(msg) end -function M.wrap_error(lx, nchildren, tag, ...) - local li = lx :peek() .lineinfo - local r = { tag=tag or 'Error', lineinfo=li } - local children = {...} - for i=1, nchildren do - r[i] = children[i] or M.earlier_error(lx) - end - return r -end - -function M.earlier_error(lx) - local li = lx and lx :peek().lineinfo - return { tag='Error', "earlier error", lineinfo=li, error=true, earlier=true } -end - ------------------------------------------------------------------------------- -- -- Sequence parser generator @@ -232,7 +190,7 @@ -- * [transformers]: a list of AST->AST functions, applied in order on ASTs -- returned by the parser. -- --- * Table-part entries corresponds to keywords (strings) and subparsers +-- * Table-part entries corresponds to keywords (strings) and subparsers -- (function and callable objects). -- -- After creation, the following fields are added: @@ -322,7 +280,7 @@ -- * [kind] == "multisequence" -- ------------------------------------------------------------------------------- -function M.multisequence (p) +function M.multisequence (p) M.make_parser ("multisequence", p) ------------------------------------------------------------------- @@ -332,7 +290,7 @@ -- compile if necessary: local keyword = type(s)=='table' and s[1] if type(s)=='table' and not M.is_parser(s) then M.sequence(s) end - if M.is_parser(s)~='sequence' or type(keyword)~='string' then + if M.is_parser(s)~='sequence' or type(keyword)~='string' then if self.default then -- two defaults error ("In a multisequence parser, all but one sequences ".. "must start with a keyword") @@ -355,8 +313,8 @@ ------------------------------------------------------------------- -- Remove the sequence starting with keyword [kw :: string] ------------------------------------------------------------------- - function p :del (kw) - if not self.sequences[kw] then + function p :del (kw) + if not self.sequences[kw] then eprintf("*** Warning: trying to delete sequence starting ".. "with %q from a multisequence having no such ".. "entry ***", kw) end @@ -534,7 +492,7 @@ -- Check for non-associative operators, and complain if applicable. ----------------------------------------- elseif p2.assoc=="none" and p2.prec==prec then - return M.parse_error (lx, "non-associative operator!") + M.parse_error (lx, "non-associative operator!") ----------------------------------------- -- No infix operator suitable at that precedence @@ -632,23 +590,21 @@ function p :parse (lx) ------------------------------------------------------ - -- Used to quickly check whether there's a terminator + -- Used to quickly check whether there's a terminator -- or a separator immediately ahead ------------------------------------------------------ - local function peek_is_in (keywords) + local function peek_is_in (keywords) return keywords and lx:is_keyword(lx:peek(), unpack(keywords)) end local x = { } local fli = lx :lineinfo_right() -- if there's a terminator to start with, don't bother trying - if not peek_is_in (self.terminators) then + if not peek_is_in (self.terminators) then repeat local item = self.primary(lx) table.insert (x, item) -- read one element until - -- Don't go on after an error - type(item)=='table' and item.tag=='Error' or -- There's a separator list specified, and next token isn't in it. -- Otherwise, consume it with [lx:next()] self.separators and not(peek_is_in (self.separators) and lx:next()) or @@ -659,8 +615,8 @@ end local lli = lx:lineinfo_left() - - -- Apply the builder. It can be a string, or a callable value, + + -- Apply the builder. It can be a string, or a callable value, -- or simply nothing. local b = self.builder if b then @@ -752,7 +708,7 @@ if type(x)=="string" then table.insert (p.keywords, x) else assert (not p.primary and M.is_parser (x)); p.primary = x end end - if not next (p.keywords) then + if not next (p.keywords) then eprintf("Warning, no keyword to trigger gg.onkeyword") end assert (p.primary, 'no primary parser in gg.onkeyword') return p @@ -839,7 +795,7 @@ local li = content.lineinfo or { } fli, lli = li.first or fli, li.last or lli if #content == 0 then - return gg.parse_error (lx, "`%s' must not be empty.", self.name or "list") + M.parse_error (lx, "`%s' must not be empty.", self.name or "list") else return transform (content, self, fli, lli) end
diff --git a/metalua/repl.mlua b/metalua/repl.mlua index 204482e..9dfa7cc 100644 --- a/metalua/repl.mlua +++ b/metalua/repl.mlua
@@ -25,6 +25,8 @@ local M = { } +mlc = require 'metalua.compiler'.new() + local readline do -- set readline() to a line reader, either editline otr a default @@ -61,38 +63,30 @@ table.insert(lines, line) src = table.concat (lines, "\n") until #line>0 - lx = mlc :src_to_lexstream(src) success, ast = pcall(mlc.lexstream_to_ast, mlc, lx) if success then - local check_status, check_msg = pcall(check_ast, ast) - if not check_status then - print "Invalid AST:" - print (check_msg) - lines = { } - else - success, f = pcall(mlc.ast_to_function, mlc, ast, '=stdin') - if success then - results = { xpcall(f, debug.traceback) } - success = table.remove (results, 1) - if success then - -- Success! - table.iforeach(|x| table.print(x, LINE_WIDTH), results) - lines = { } - else - print "Evaluation error:" - print (results[1]) - lines = { } - end - else - print "Can't compile into bytecode:" - print (f) - lines = { } - end - end + success, f = pcall(mlc.ast_to_function, mlc, ast, '=stdin') + if success then + results = { xpcall(f, debug.traceback) } + success = table.remove (results, 1) + if success then + -- Success! + table.iforeach(|x| table.print(x, LINE_WIDTH), results) + lines = { } + else + print "Evaluation error:" + print (results[1]) + lines = { } + end + else + print "Can't compile into bytecode:" + print (f) + lines = { } + end else - -- If lx has been read entirely, try to read another - -- line before failing. + -- If lx has been read entirely, try to read + -- another line before failing. if not reached_eof(lx, ast) then print "Can't compile source into AST:" print (ast)