]> git.lizzy.rs Git - metalua.git/commitdiff
EVE patch for line number reporting on runtime errors
authorFabien Fleutot <fabien@macfabien.local>
Mon, 21 Jan 2008 21:35:16 +0000 (22:35 +0100)
committerFabien Fleutot <fabien@macfabien.local>
Mon, 21 Jan 2008 21:35:16 +0000 (22:35 +0100)
src/compiler/compile.lua
src/compiler/gg.lua
src/compiler/lcode.lua
src/compiler/ldump.lua
src/compiler/lexer.lua
src/compiler/lopcodes.lua

index 9c893a9f74a8d212e0b2440a122536908c110fa3..895c741ddee60be97424238d242a3617f54da525 100644 (file)
@@ -17,7 +17,7 @@
 ----------------------------------------------------------------------
 
 ----------------------------------------------------------------------
--- 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
@@ -70,11 +44,6 @@ local debugf = function() end
 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)
@@ -86,8 +55,9 @@ VARARG_HASARG   = 1
 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
@@ -422,7 +392,11 @@ local function funcargs (fs, ast, v, idx_from)
     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
@@ -699,7 +673,7 @@ 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
@@ -720,6 +694,8 @@ stat.Do = block
 ------------------------------------------------------------------------
 
 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
@@ -960,7 +936,7 @@ 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
 
@@ -1069,7 +1045,9 @@ 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)
@@ -1130,8 +1108,12 @@ end
 ------------------------------------------------------------------------
 
 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)
@@ -1141,15 +1123,18 @@ end
 ------------------------------------------------------------------------
 
 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
@@ -1180,6 +1165,12 @@ 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)
 
@@ -1198,59 +1189,6 @@ end
 
 ------------------------------------------------------------------------
 
--- 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))
index 4d26c4d399ae988e422bf7fd13034931e77fb358..32ca00f13ad1da15c11b44f2b73721f2b0915d89 100644 (file)
@@ -1,5 +1,5 @@
 ----------------------------------------------------------------------\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
@@ -7,16 +7,12 @@
 --\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
@@ -89,6 +85,9 @@ end
 -------------------------------------------------------------------------------\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
@@ -96,12 +95,18 @@ local function raw_parse_sequence (lx, p)
             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
@@ -130,7 +135,7 @@ end
 -- 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
@@ -181,6 +186,7 @@ function sequence (p)
    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
@@ -188,7 +194,11 @@ function sequence (p)
       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
@@ -455,13 +465,19 @@ function expr (p)
       -- 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
index 6d61f94a2f2e108df8bb9aac92adcac3d08ae359..11203afca00d74100fd69521ee0bb5f5c95f6955 100644 (file)
@@ -18,7 +18,7 @@
 
 --[[--------------------------------------------------------------------
 
-  $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
-
-
 ----------------------------------------------------------------------]]
 
 --[[--------------------------------------------------------------------
@@ -605,6 +590,7 @@ function luaK:exp2nextreg(fs, e)
   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)
@@ -964,6 +950,9 @@ end
 ------------------------------------------------------------------------
 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
 
@@ -971,7 +960,10 @@ 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
@@ -983,14 +975,21 @@ function luaK:code(fs, i, line)
   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
index 80f08a7ef5b2a221eb36b37d8529bb5cd06e42e8..60b9074f7c882f38edf1c1eb01310142687f9848 100644 (file)
@@ -18,7 +18,7 @@
 
 --[[--------------------------------------------------------------------
 
-  $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
-
-
 ----------------------------------------------------------------------]]
 
 --[[--------------------------------------------------------------------
@@ -282,7 +274,9 @@ function luaU:DumpLocals(f, D)
     -- `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))
@@ -301,6 +295,7 @@ function luaU:DumpLines(f, D)
   --was DumpVector
   for i = 0, n - 1 do
     self:DumpInt(f.lineinfo[i], D)  -- was DumpBlock
+    print(i, f.lineinfo[i])
   end
 end
 
@@ -350,6 +345,13 @@ function luaU:DumpProtos (f, D)
   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
@@ -362,8 +364,7 @@ function luaU:DumpFunction(f, p, D)
   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)
@@ -371,9 +372,7 @@ function luaU:DumpFunction(f, p, 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
 
 ------------------------------------------------------------------------
index e871100002efde838c32fbea24e1714e4930ac46..03856119d760dc64efc468316a5df945948e887a 100644 (file)
@@ -113,7 +113,13 @@ function lexer:extract ()
          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
@@ -308,7 +314,11 @@ function lexer:next (n)
    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
index dcb1d7844bae78fdd256a814d777c571a15dfbd1..5e4b964cfc91df6b0074db23f54baba6343d1f57 100644 (file)
@@ -18,7 +18,7 @@
 
 --[[--------------------------------------------------------------------
 
-  $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
-
-
 ----------------------------------------------------------------------]]
 
 --[[--------------------------------------------------------------------