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")