[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: In praise of globals
- From: Philipp Janda <siffiejoe@...>
- Date: Fri, 26 Apr 2013 07:03:58 +0200
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
+