lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Am 26.04.2013 03:15 schröbte Philipp Janda:

"Features" of this prototype:
-   I needed to introduce another magic upvalue (currently named
___MAGIC___), which you must not use in your interactive code or all
hell might break loose.

Got rid off the magic upvalue.

-   A top-level return will close all locals. This was a) a lot easier
to implement, and b) helps avoid hitting the upvalue limit if you start
to paste lots of code into the interactive interpreter. That means, that
if you want to paste your module code, you must omit the final return
statement if you want to access the locals in the module later on. The
special "=<expr>" syntax should work as expected, though.
-   The code needs to compile each chunk multiple times and slice it
using string.*, lbci.*, and debug.* functions -- I hope you didn't
expect the interactive interpreter to be fast ...
-   I haven't tested much, *especially* anything _ENV-related.

David Manura's original lua.lua is MIT-licensed, my modifications of it
are hereby placed in the public domain.


Rest still applies.

Have fun!

Philipp


Philipp


diff -Naurd old/lua.lua new/lua.lua
--- old/lua.lua	2013-04-26 02:32:47.709924914 +0200
+++ new/lua.lua	2013-04-26 06:59:12.964227035 +0200
@@ -27,13 +27,13 @@
 local assert = assert
 local collectgarbage = collectgarbage
 local loadfile = loadfile
-local loadstring = loadstring
+local loadstring = load
 local pcall = pcall
 local rawget = rawget
 local select = select
 local tostring = tostring
 local type = type
-local unpack = unpack
+local unpack = table.unpack
 local xpcall = xpcall
 local io_stderr = io.stderr
 local io_stdout = io.stdout
@@ -42,6 +42,15 @@
 local string_sub = string.sub
 local os_getenv = os.getenv
 local os_exit = os.exit
+local require = require
+local bci = require( "bci" )
+local bci_getheader = bci.getheader
+local bci_getlocal = bci.getlocal
+local bci_getupvalue = bci.getupvalue
+local debug = require( "debug" )
+local db_getupvalue = debug.getupvalue
+local db_upvaluejoin = debug.upvaluejoin
+local tconcat = table.concat
 
 
 local progname = LUA_PROGNAME
@@ -161,7 +170,7 @@
 
 local function incomplete (msg)
   if msg then
-    local ender = LUA_QL("<eof>")
+    local ender = "<eof>"
     if string_sub(msg, -#ender) == ender then
       return true
     end
@@ -170,6 +179,44 @@
 end
 
 
+local function funclocals (chunk)
+  local names = {}
+  local header = bci_getheader(chunk)
+  for i = 1, header.upvalues do
+    local name = bci_getupvalue(chunk, i)
+    if not names[ name ] then
+      names[ #names+1 ] = name
+      names[ name ] = #names
+    end
+  end
+  for i = 1, header.locals do
+    local name = bci_getlocal(chunk, i)
+    if name:sub( 1, 1 ) ~= "(" and not names[ name ] then
+      names[ #names+1 ] = name
+      names[ name ] = #names
+    end
+  end
+  return names
+end
+
+local function make_list (f)
+  local names = funclocals(f)
+  local h = tconcat(names, ", ")
+  return h
+end
+
+local function find_upvalue (f, name)
+  local i = 1
+  repeat
+    local up_name = db_getupvalue(f, i)
+    if up_name == name then
+      return i
+    end
+    i = i + 1
+  until up_name == nil
+end
+
+
 local function pushline (firstline)
   local prmt = get_prompt(firstline)
   io_stdout:write(prmt)
@@ -177,19 +224,42 @@
   local b = io_stdin:read'*l'
   if not b then return end -- no input
   if firstline and string_sub(b, 1, 1) == '=' then
-    return "return " .. string_sub(b, 2)  -- change '=' to `return'
+    return "return " .. string_sub(b, 2), true -- change '=' to `return'
   else
     return b
   end
 end
 
 
-local function loadline ()
-  local b = pushline(true)
+local function loadline (lastlocals)
+  local b, returns = pushline(true)
   if not b then return -1 end  -- no input
+  local header = make_list(lastlocals)
   local f, msg
   while true do  -- repeat until gets a complete line
-    f, msg = loadstring(b, "=stdin")
+    local h = header ~= "" and "local "..header.."\n" or ""
+    f, msg = loadstring(h..b, "=stdin")
+    if f then
+      local newheader = make_list(f)
+      local common = "return (function("..header..") return function() "
+      local nh = returns and newheader or ""
+      f = assert(loadstring(common..b..
+                 "\nreturn function() return "..newheader..
+                 " end end end)()", b) or
+                 loadstring(common.."return function() return "..nh..
+                 " end, (function() "..b.."\nend)() end end)()", b))()
+      local i = 1
+      repeat
+        local name, val = db_getupvalue(lastlocals, i)
+        if name then
+          local n = find_upvalue(f, name)
+          if n then
+            db_upvaluejoin(f, n, lastlocals, i)
+          end
+          i = i + 1
+        end
+      until name == nil
+    end
     if not incomplete(msg) then break end  -- cannot try to add lines?
     local b2 = pushline(false)
     if not b2 then -- no more input?
@@ -207,21 +277,25 @@
 local function dotty ()
   local oldprogname = progname
   progname = nil
+  local lastlocals = function() end
   while true do
     local result
-    local status, msg = loadline()
+    local status, msg = loadline(lastlocals)
     if status == -1 then break end
     if status then
       result = tuple(docall(status))
       status, msg = result[1], result[2]
     end
     report(status, msg)
-    if status and result.n > 1 then  -- any result to print?
-      status, msg = pcall(_G.print, unpack(result, 2, result.n))
-      if not status then
-        l_message(progname, string_format(
-            "error calling %s (%s)",
-            LUA_QL("print"), msg))
+    if status and result.n > 1 then
+      lastlocals = result[2]
+      if result.n > 2 then  -- any result to print?
+        status, msg = pcall(_G.print, unpack(result, 3, result.n))
+        if not status then
+          l_message(progname, string_format(
+              "error calling %s (%s)",
+              LUA_QL("print"), msg))
+        end
       end
     end
   end
@@ -354,3 +428,4 @@
   else dofile(nil)  -- executes stdin as a file
   end
 end
+