----------------------------------------------------------------------
----------------------------------------------------------------------
--- Metalua: $Id: compile.lua,v 1.7 2006/11/15 09:07:50 fab13n Exp $
+-- Metalua.
--
-- Summary: Compile ASTs to Lua 5.1 VM function prototype.
-- Largely based on:
--
----------------------------------------------------------------------
--
--- Copyright (c) 2006, Fabien Fleutot <metalua@gmail.com>.
+-- Copyright (c) 2006-2008, Fabien Fleutot <metalua@gmail.com>.
--
-- This software is released under the MIT Licence, see licence.txt
-- for details.
--
----------------------------------------------------------------------
--- History:
--- $Log: compile.lua,v $
--- Revision 1.7 2006/11/15 09:07:50 fab13n
--- debugged meta operators.
--- Added command line options handling.
---
--- Revision 1.6 2006/11/10 02:11:17 fab13n
--- compiler faithfulness to 5.1 improved
--- gg.expr extended
--- mlp.expr refactored
---
--- Revision 1.5 2006/11/09 09:39:57 fab13n
--- some cleanup
---
--- Revision 1.4 2006/11/07 21:29:02 fab13n
--- improved quasi-quoting
---
--- Revision 1.3 2006/11/07 04:37:59 fab13n
--- first bootstrapping version.
---
--- Revision 1.2 2006/11/05 15:08:34 fab13n
--- updated code generation, to be compliant with 5.1
---
-----------------------------------------------------------------------
module ("bytecode", package.seeall)
---require "lopcodes"
---require "lcode"
local debugf = function() end
--local debugf=printf
local stat = { }
local expr = { }
--- GENERAL FIXME: je ne gere pas les parentheses pour empecher l'unpack
--- des fonctions dans return et dans table.
-
--- GENERAL FIXME: growvector --> checkvector
-
MAX_INT = 2147483645 -- INT_MAX-2 for 32-bit systems (llimits.h)
MAXVARS = 200 -- (llimits.h)
MAXUPVALUES = 32 -- (llimits.h)
VARARG_ISVARARG = 2
VARARG_NEEDSARG = 4
-
-local function hasmultret (k) return k=="VCALL" or k=="VVARARG" end
+local function hasmultret (k)
+ return k=="VCALL" or k=="VVARARG"
+end
-----------------------------------------------------------------------
-- Some ASTs take expression lists as children; it should be
nparams = fs.freereg - (base + 1)
end
init_exp(v, "VCALL", luaK:codeABC(fs, "OP_CALL", base, nparams + 1, 2))
- luaK:fixline(fs, ast.line)
+ if ast.lineinfo then
+ luaK:fixline(fs, ast.lineinfo.first)
+ else
+ luaK:fixline(fs, ast.line)
+ end
fs.freereg = base + 1 -- call remove function and arguments and leaves
-- (unless changed) one result
end
------------------------------------------------------------------------
function stat.stat (fs, ast)
- if ast.line then fs.lastline = ast.line end
+ if ast.lineinfo then fs.lastline = ast.line or ast.lineinfo.last end
-- debugf (" - Statement %s", disp.ast (ast) )
if not ast.tag then chunk (fs, ast) else
------------------------------------------------------------------------
function stat.Break (fs, ast)
+-- if ast.lineinfo then fs.lastline = ast.line or ast.lineinfo.last end
+
local bl, upval = fs.bl, false
while bl and not bl.isbreakable do
if bl.upval then upval = true end
function stat.Invoke (fs, ast)
local v = { }
expr.Invoke (fs, ast, v)
---FIXME: didn't check that, just copied from stat.Call
+ --FIXME: didn't check that, just copied from stat.Call
luaP:SETARG_C (luaK:getcode(fs, v), 1)
end
function expr.expr (fs, ast, v)
if type(ast) ~= "table" then
error ("Expr AST expected, got "..table.tostring(ast)) end
- if ast.line then fs.lastline = ast.line end
+
+ if ast.lineinfo then fs.lastline = ast.line or ast.lineinfo.last end
+
--debugf (" - Expression %s", tostringv (ast))
local parser = expr[ast.tag]
if parser then parser (fs, ast, v)
------------------------------------------------------------------------
function expr.Function (fs, ast, v)
+ if ast.lineinfo then fs.lastline = ast.line or ast.lineinfo.last end
+
local new_fs = open_func(fs)
- if ast.line then new_fs.f.lineDefined = ast.line end
+ if ast.lineinfo then
+ new_fs.f.lineDefined, new_fs.f.lastLineDefined = ast.lineinfo.first, ast.lineinfo.last
+ end
parlist (new_fs, ast[1])
chunk (new_fs, ast[2])
close_func (new_fs)
------------------------------------------------------------------------
function expr.Op (fs, ast, v)
+ if ast.lineinfo then fs.lastline = ast.line or ast.lineinfo.last end
+ local op = ast[1]
+
if #ast == 2 then
expr.expr (fs, ast[2], v)
- luaK:prefix (fs, ast[1], v)
+ luaK:prefix (fs, op, v)
elseif #ast == 3 then
local v2 = { }
expr.expr (fs, ast[2], v)
- luaK:infix (fs, ast[1], v)
+ luaK:infix (fs, op, v)
expr.expr (fs, ast[3], v2)
- luaK:posfix (fs, ast[1], v, v2)
+ luaK:posfix (fs, op, v, v2)
else
error "Wrong arg number"
end
function expr.Index (fs, ast, v)
if #ast ~= 2 then error "generalized indexes not implemented" end
+ if ast.lineinfo then
+ fs.lastline = ast.line or ast.lineinfo.last
+ end
+
+ --assert(fs.lastline ~= 0, ast.tag)
+
expr.expr (fs, ast[1], v)
luaK:exp2anyreg (fs, v)
------------------------------------------------------------------------
--- function expr.Stat (fs, ast, v)
--- -- Protect temporary stack values as phony local vars:
--- -- this way, they won't be overwritten.
--- local save_nactvar = fs.nactvar
--- -- Eventually, the result should go on top of stack,
--- -- whose index is saved in dest_reg.
--- local dest_reg = fs.freereg
-
--- -- the part of actvar which is over nactvar might be filled with local var
--- -- indexes, although these variables don't have a register yet, typically in
--- -- `Local{ {...}, { `Stat{ ... } } }. Save them to restore them.
--- -- The computation of [last_unreg_var] is hackish because [fs.actvar] is
--- -- indexed form 0, and lua is much more comfortable with arrays starting
--- -- at 1.
--- local save_actvar = { }
--- local last_unreg_var = #fs.actvar
--- if last_unreg_var > 0 or fs.actvar[0] then
--- for i = fs.nactvar, last_unreg_var do
--- --printf("[STAT] save unregistered variable %i -> %i", i, fs.actvar[i])
--- save_actvar[i] = fs.actvar[i]
--- end
--- end
--- fs.nactvar = fs.freereg
--- --printf("[STAT] pretend that there are %i actvars. Entering block...", fs.nactvar)
--- enterblock (fs, { }, false)
--- chunk (fs, ast[1])
--- --printf("[STAT] result expr = %s", disp.ast (ast[2]))
--- expr.expr (fs, ast[2], v)
--- --printf("[STAT] v == %s must go to reg %i", tostringv(v), dest_reg)
--- -- Push the result on top of stack:
-
--- -- FIXME: s'il y a des upvalues dans le bloc, il ne faut pas ecrire directement
--- -- dans dest_reg, mais [1] ecrire dans l'actuel freereg [2] fermer le bloc et
--- -- [3] transferer l'ancien freereg dans dest_reg.
--- -- Sinon, le CLOSE ne sera pas appele, et les upvalue ne seront pas fermees.
-
--- luaK:exp2reg (fs, v, dest_reg)
--- --printf("[STAT] leaving block...")
--- leaveblock (fs)
--- --printf("[STAT] ...block leaved, nactvar back to %i", save_nactvar)
--- assert (dest_reg == fs.freereg)
--- -- Reserve the newly allocated stack level
--- fs.freereg=fs.freereg+1
--- -- Push back nactvar, so that intermediate stacked value stop
--- -- being protected.
--- fs.nactvar = save_nactvar
-
--- -- restore messed-up unregistered local vars
--- for i, j in pairs(save_actvar) do
--- fs.actvar[i] = j
--- end
--- end
-
function expr.Stat (fs, ast, v)
--printf(" * Stat: %i actvars, first freereg is %i", fs.nactvar, fs.freereg)
--printf(" actvars: %s", table.tostring(fs.actvar))
----------------------------------------------------------------------\r
--- Metalua: $Id: gg.lua,v 1.2 2006/11/15 09:07:50 fab13n Exp $\r
+-- Metalua.\r
--\r
-- Summary: parser generator. Collection of higher order functors,\r
-- which allow to build and combine parsers. Relies on a lexer\r
--\r
----------------------------------------------------------------------\r
--\r
--- Copyright (c) 2006, Fabien Fleutot <metalua@gmail.com>.\r
+-- Copyright (c) 2006-2008, Fabien Fleutot <metalua@gmail.com>.\r
--\r
-- This software is released under the MIT Licence, see licence.txt\r
-- for details.\r
--\r
----------------------------------------------------------------------\r
--- History:\r
--- $Log: gg.lua,v $\r
---\r
-----------------------------------------------------------------------\r
\r
--------------------------------------------------------------------------------\r
--\r
-------------------------------------------------------------------------------\r
local function raw_parse_sequence (lx, p)\r
local r = { }\r
+\r
+ local lineinfo = { first = lx:peek().lineinfo.first }\r
+ \r
for i=1, #p do\r
e=p[i]\r
if type(e) == "string" then \r
parse_error (lx, "Keyword '%s' expected", e) end\r
elseif is_parser (e) then\r
table.insert (r, e (lx)) \r
+ \r
+ lineinfo.last = lx.lastline\r
else \r
gg.parse_error (lx,"Sequence `%s': element #%i is not a string "..\r
"nor a parser: %s", \r
p.name, i, table.tostring(e))\r
end\r
end\r
+ lineinfo.last = lx.lastline\r
+\r
+ r.lineinfo = lineinfo\r
+\r
return r\r
end\r
\r
-- Generate a tracable parsing error (not implemented yet)\r
-------------------------------------------------------------------------------\r
function parse_error(lx, fmt, ...) \r
- local line = lx:peek().line or -1\r
+ local line = lx:peek().lineinfo.first or -1\r
local char = lx:peek().char or -1\r
local msg = string.format("line %i, char %i: "..fmt, line, char, ...) \r
local src = lx.src\r
function p:parse (lx)\r
-- Raw parsing:\r
local x = raw_parse_sequence (lx, self)\r
+ local lineinfo = x.lineinfo\r
\r
-- Builder application:\r
local builder, tb = self.builder, type (self.builder)\r
elseif tb == "function" or builder and builder.__call then x = builder(x)\r
elseif builder == nil then -- nothing\r
else error("Invalid builder of type "..tb.." in sequence") end\r
- return transform (x, self)\r
+ \r
+ x = transform (x, self)\r
+ if x then x.lineinfo = lineinfo end\r
+\r
+ return x \r
end\r
\r
-------------------------------------------------------------------\r
-- or false if no operator was found.\r
------------------------------------------------------\r
local function handle_suffix (e)\r
+ local lineinfo = { first = e.lineinfo and e.lineinfo.first or 0 }\r
local p2_func, p2 = get_parser_info (self.suffix)\r
if not p2 then return false end\r
if not p2.prec or p2.prec>=prec then\r
local op = p2_func(lx)\r
if not op then return false end\r
+ if op.lineinfo then \r
+ lineinfo.last = op.lineinfo.last \r
+ end\r
e = p2.builder (e, op)\r
- return transform (transform (e, p2), self)\r
+ e = transform (transform (e, p2), self)\r
+ e.lineinfo = lineinfo\r
+ return e\r
end\r
return false\r
end --</expr.parse.handle_suffix>\r
--[[--------------------------------------------------------------------
- $Id: lcode.lua,v 1.5 2006/11/15 09:07:50 fab13n Exp $
+ $Id$
lcode.lua
Lua 5 code generator in Lua
[FF] Slightly modified, mainly to produce Lua 5.1 bytecode.
- $Log: lcode.lua,v $
- Revision 1.5 2006/11/15 09:07:50 fab13n
- debugged meta operators.
- Added command line options handling.
-
- Revision 1.4 2006/11/09 09:39:57 fab13n
- some cleanup
-
- Revision 1.3 2006/11/07 04:37:59 fab13n
- first bootstrapping version.
-
- Revision 1.2 2006/11/05 15:08:34 fab13n
- updated code generation, to be compliant with 5.1
-
-
----------------------------------------------------------------------]]
--[[--------------------------------------------------------------------
self:dischargevars(fs, e)
--[FF] Allready in place (added for expr.Stat)
if e.k == "VNONRELOC" and e.info == fs.freereg then
+ --printf("Expression already in next reg %i: %s", fs.freereg, tostringv(e))
return end
self:freeexp(fs, e)
self:reserveregs(fs, 1)
------------------------------------------------------------------------
function luaK:fixline(fs, line)
--assert (line)
+ if not line then
+ --print(debug.traceback "fixline (line == nil)")
+ end
fs.f.lineinfo[fs.pc - 1] = line or 0
end
--
------------------------------------------------------------------------
function luaK:code(fs, i, line)
- assert (line)
+ if not line then
+ line = 0
+ --print(debug.traceback "line == nil")
+ end
local f = fs.f
do -- print it
end
self:dischargejpc(fs) -- 'pc' will change
- -- put new instruction in code array
---FF luaY:growvector(fs.L, f.code, fs.pc, f.sizecode, nil,
---FF luaY.MAX_INT, "code size overflow")
+
f.code[fs.pc] = i
- -- save corresponding line information
---FF luaY:growvector(fs.L, f.lineinfo, fs.pc, f.sizelineinfo, nil,
---FF luaY.MAX_INT, "code size overflow")
f.lineinfo[fs.pc] = line
+
+ if line == 0 then
+ f.lineinfo[fs.pc] = fs.lastline
+ if fs.lastline == 0 then
+ print(debug.traceback())
+ end
+ end
+
+ if f.lineinfo[fs.pc] == 0 then
+ f.lineinfo[fs.pc] = 42
+ end
+
local pc = fs.pc
fs.pc = fs.pc + 1
return pc
--[[--------------------------------------------------------------------
- $Id: ldump.lua,v 1.3 2006/11/07 04:38:00 fab13n Exp $
+ $Id$
ldump.lua
Save bytecodes in Lua
[FF] Slightly modified, mainly to produce Lua 5.1 bytecode.
- $Log: ldump.lua,v $
- Revision 1.3 2006/11/07 04:38:00 fab13n
- first bootstrapping version.
-
- Revision 1.2 2006/11/05 15:08:34 fab13n
- updated code generation, to be compliant with 5.1
-
-
----------------------------------------------------------------------]]
--[[--------------------------------------------------------------------
-- `Stat{ } keeps properly count of the number of local vars,
-- but fails to keep score of their debug info (names).
-- It therefore might happen that #f.localvars < f.sizelocvars, or
- -- that a variable's startpc and endpc fields are left unset.
+ -- that a variable's startpc and endpc fields are left unset.
+ -- FIXME: This might not be needed anymore, check the bug report
+ -- by J. Belmonte.
local var = f.locvars[i]
if not var then break end
-- printf("[DUMPLOCALS] dumping local var #%i = %s", i, table.tostring(var))
--was DumpVector
for i = 0, n - 1 do
self:DumpInt(f.lineinfo[i], D) -- was DumpBlock
+ print(i, f.lineinfo[i])
end
end
end
end
+function luaU:DumpDebug(f, D)
+ self:DumpLines(f, D)
+ self:DumpLocals(f, D)
+ self:DumpUpvalues(f, D)
+end
+
+
------------------------------------------------------------------------
-- dump child function prototypes from function prototype
--FF completely reworked for 5.1 format
if source == p then source = nil end
self:DumpString(source, D)
self:DumpInt(f.lineDefined, D)
- self:DumpInt(42, D) -- lastlinedefined, not implemented, FIXME
--- self:DumpInt(f.lastlineDefined, D)
+ self:DumpInt(f.lastLineDefined or 42, D)
self:DumpByte(f.nups, D)
self:DumpByte(f.numparams, D)
self:DumpByte(f.is_vararg, D)
self:DumpCode(f, D)
self:DumpConstants(f, D)
self:DumpProtos( f, D)
- self:DumpLines(f, D)
- self:DumpLocals(f, D)
- self:DumpUpvalues(f, D)
+ self:DumpDebug(f, D)
end
------------------------------------------------------------------------
if loc and i <= loc then ln = ln+1 end
if i <= self.i then self.line = self.line+1 else break end
end
- local a = { tag = tag, char=loc, line=ln, content }
+ local a = { tag = tag,
+ char = loc,
+ lineinfo = { first = ln, last = self.line },
+ line = self.line,
+ content }
+ -- FIXME [EVE] make lineinfo passing less memory consuming
+ -- FIXME [Fabien] suppress line/lineinfo.line redundancy.
if #self.attached_comments > 0 then
a.comments = self.attached_comments
self.attached_comments = nil
local a
for i=1,n do
a = _G.table.remove (self.peeked, 1)
- if a then debugf ("[L:%i K:%i T:%s %q]", a.line or -1, a.char or -1, a.tag or '<none>', a[1]) end
+ if a then
+ debugf ("[L:%i K:%i T:%s %q]", a.line or -1, a.char or -1,
+ a.tag or '<none>', a[1]) end
+ self.lastline = a.lineinfo.last
+ end
end
return a or eof_token
end
--[[--------------------------------------------------------------------
- $Id: lopcodes.lua,v 1.4 2006/11/10 02:11:17 fab13n Exp $
+ $Id$
lopcodes.lua
Lua 5 virtual machine opcodes in Lua
[FF] Slightly modified, mainly to produce Lua 5.1 bytecode.
- $Log: lopcodes.lua,v $
- Revision 1.4 2006/11/10 02:11:17 fab13n
- compiler faithfulness to 5.1 improved
- gg.expr extended
- mlp.expr refactored
-
- Revision 1.3 2006/11/07 04:38:00 fab13n
- first bootstrapping version.
-
- Revision 1.2 2006/11/05 15:08:34 fab13n
- updated code generation, to be compliant with 5.1
-
-
----------------------------------------------------------------------]]
--[[--------------------------------------------------------------------