|
Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
-- interactive.lua - an interactive mode for Lua, written in Lua.
-- By Eduardo Ochs <eduardoochs@gmail.com>
-- Version: 2007mar17
-- http://angg.twu.net/LUA/interactive.lua
-- http://angg.twu.net/LUA/interactive.lua.html
-- (find-es "lua5")
-- (find-es "lua5" "incompletep")
-- (find-bgprocess "inkscape ~/LUA/interactive.svg")
-- _ _ _
-- (_)_ __ ___ ___ _ __ ___ _ __ | | ___| |_ ___
-- | | '_ \ / __/ _ \| '_ ` _ \| '_ \| |/ _ \ __/ _ \
-- | | | | | (_| (_) | | | | | | |_) | | __/ || __/
-- |_|_| |_|\___\___/|_| |_| |_| .__/|_|\___|\__\___|
-- |_|
-- (find-angg "LUA/lua50init.lua")
-- (find-angg "LUA/lua50init.lua" "preparef2n")
-- (find-es "lua5" "signal")
-- (find-es "lua5" "xpcall")
-- The default, top-level interactive mode for Lua has several nice
-- features:
-- (1) It detects incomplete inputs, and knows how to ask for more
-- lines.
-- (2) It supports one special prefix: "=".
-- (3) It can display stack dumps ("tracebacks") in case of errors;
-- they are relatively nice. Problems: tail calls; what to do when
-- the information in those tracebacks is not enough?
-- (4) If the interactive mode is waiting for input then a "^C" leaves
-- Lua, but if a script is running it is aborted and we get the
-- prompt again.
--
-- However:
-- (5) There's no way to call that interactive interpreter from a Lua
-- script - the closest that we have is debug.debug(), that does
-- not handle multi-line commands or the "=" prefix.
--
-- This file implements an interactive mode written in pure Lua that
-- can be called from anywhere in a Lua script and that handles all
-- the issues above, except (4) the "^C"s, as that would need either
-- C or the posix library.
--
-- At some points here we use "PP" instead of "print". "PP" can print
-- some complex data structures, but it gets into infinite loops when
-- faced with circular data structures... and as "^C"s don't behave as
-- nicely as they should there's no way to just break out of these
-- infinite loops without leaving Lua. Which mean: take care!
-- loadstring2: like loadstring, but with two error messages (or sort of)
-- (find-luamanualw3m "#pdf-loadstring")
loadstring2 = function (str, chunkname)
local compiledcode, errormsg = loadstring(str, chunkname)
local pat = ": unexpected symbol near '<eof>'$"
if errormsg then
if string.find(errmsg, ": unexpected symbol near '<eof>'$") then
return nil, nil, "incomplete"
else
return nil, errormsg, nil
end
end
return compiledcode, nil, nil
end
* (eepitch-lua51)
= string.sub("abcd", 1, 2)
iline = ""
iprefix = ""
icode = ""
icompiled = nil
ierrormsg = nil
ichunkname = "istdin"
interactive1 = function ()
local iincomplete
local pat = ": unexpected symbol near '<eof>'$"
io.write("i> ")
iline = io.read()
if not iline then return end -- eof
if string.sub(iline, 1, 1) == "=" then
iprefix = "="
icode = "return " .. string.sub(iline, 2)
else
iprefix = ""
icode = iline
end
while true do
icompiled, ierrormsg = loadstring(icode, "(interactive)")
iincomplete = ierrormsg and string.find(ierrormsg, pat)
if not iincomplete then break end
io.write("i>> ")
iline = io.read()
if not iline then return end -- eof
icode = icode .. "\n" .. iline
end
if ierrormsg then
print(ierrormsg)
else
local iresult
return true
-- if iprefix == "=" then print(icompiled()) end
-- if iprefix == "" then icompiled() end
end
end
= interactive1()
= PP(2,
3,
4)
icompiled()
PP(icompiled())
if errormsg then
if
iprompt1 = "d> "
iprompt2 = "d>> "
iline = ""
iline1 = ""
ilines = {}
iprefixes = { "", "=", "==", "%D", "%:" }
ibestprefix = ""
iline1noprefix = ""
ihasprefix = function (prefix, str)
local plen, slen = string.len(prefix), string.len(str)
return plen <= slen and string.sub(str, 1, plen) == prefix
end
itestprefix = function (prefix, line)
local plen, bplen = string.len(prefix), string.len(ibestprefix)
if plen > bplen and ihasprefix(plen, line) then
ibestprefix = prefix
end
end
igetbestprefix = function ()
ibestprefix = ""
for _,prefix in ipairs(iprefixes) do
itestprefix(prefix, ilastlineread)
end
iline1noprefix = string.sub(ilastlineread, string.len(ibestprefix) + 1)
end
ireadp = function (prompt) io.write(prompt); ilastlineread = io.read() end
iread2 = function () ireadp(iprompt2); tinsert(ilines, ilastlineread) end
iread1 = function ()
ireadp(iprompt2)
igetbestprefix();
bestprefix = function (prefixes, line)
local bestprefix = ""
local bplen = string.len(bestprefix)
for _,prefix in ipairs(iprefixes) do
local plen = string.len(prefix)
if plen > bplen and string.sub(line, 1, plen) == prefix then
bestprefix = prefix
bplen = string.len(bestprefix)
end
end
return bestprefix, string.sub(line, bplen + 1)
end
itestprefix(prefix, ilastlineread)
end
iread1
iread1 = function () ireadp(iprompt1); tinsert(ilines, iline) end
iloadstring = function ()
icode = table.concat(userlines, "\n")
icompiled, ierror = loadstring(icode, ichunkname)
end
itestincomplete = function ()
local pat = ": unexpected symbol near '<eof>'$"
if ierror and string.find(ierror, pat) then ierror2 = "incomplete" end
end
itestabort = function () if iline == "." then ierror2 = "abort" end end
ilastlineread = ""
igetlongestprefix =
ibestprefix = ""
if string.len(prefix) <= string.len(str) and string.sub(str, string.len(prefix)
* (eepitch-lua51)
= string.sub("foo", 1, 1)
= string.sub("foo", 1, 2)
-- (find-luamanualw3m "#pdf-string.sub")
-- (find-es "lua5")
-- (find-es "lua5" "0-based")
iprefix = function
istatus =
local pat = ": unexpected symbol near '<eof>'$"
userstatus = usererror and string.find(usererror, pat) and "incomplete"
end
got line?
is it an abort?/
if not errormsg then
return compiledcode, nil, nil
else
if errormsg and string.find(errmsg, pat) then
return nil, nil, "incomplete"
else
return comp
return compiledcode, errormsg, incomplete
end
incompletepat = ": unexpected symbol near '<eof>'$"
incompletep = function (errmsg)
if errmsg and string.find(errmsg, incompletepat) then return true end
end
myloadstring = function (T)
T.code, T.err = loadstring(table.concat(T, "\n"))
T.incomplete = incompletep(T.err)
return T
end
myreadmore = function (T)
io.write(prompt or (table.getn(T) == 0 and "-> " or "->> "))
local line = io.read()
if line then table.insert(T, line); return true end
end
myreadlines = function (T)
if myreadmore(T) then
while myloadstring(T).incomplete and myreadmore(T) do end
end
return T
end
-- (find-angg "LUA/lua50init.lua" "loadtcl")
-- (find-luamanualw3m "#pdf-debug.debug")
-- (find-luamanualw3m "#6" "interactive mode")
-- (find-luamanualw3m "#6" "incomplete statement")
-- (find-expcommand "interpreter")
-- (find-lua51file "src/lua.c")
-- (find-lua51file "src/lua.c" "interactive mode")
-- (find-lua51file "src/lua.c" "case 'i':")
-- (find-lua51file "src/lua.c" "collectargs(argv, &has_i, &has_v, &has_e);")
-- (find-lua51tag "dotty")
-- (find-lua51tag "loadline")