Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
-- This file: -- http://angg.twu.net/LUA/myxpcall.lua.html -- http://angg.twu.net/LUA/myxpcall.lua -- (find-angg "LUA/myxpcall.lua") -- Author: Eduardo Ochs <eduardoochs@gmail.com> -- -- This is obsolete! See: -- (find-angg "edrxrepl/edrxpcall.lua") -- «.Class» (to "Class") -- «.MyXpcall» (to "MyXpcall") -- «.MyXpcall-class» (to "MyXpcall-class") -- «.MyXpcall-tests» (to "MyXpcall-tests") -- «.Repl» (to "Repl") -- «.Repl-tests» (to "Repl-tests") -- Some functions from my initfile. See: -- (find-angg "LUA/lua50init.lua" "pack-and-unpack") -- (find-angg "LUA/lua50init.lua" "splitlines-5.3") -- (find-angg "LUA/lua50init.lua" "split") pack = table.pack or function (...) return {n=select("#", ...), ...} end unpack = unpack or table.unpack myunpack = function (arg) return unpack(arg, 1, arg.n) end split = function (str, pat) local arr = {} string.gsub(str, pat or "([^%s]+)", function (word) table.insert(arr, word) end) return arr end splitlines = function (bigstr) local arr = split(bigstr, "([^\n]*)\n?") if _VERSION:sub(5) < "5.3" then table.remove(arr) end return arr end -- «Class» (to ".Class") -- Commented version: -- (find-angg "dednat6/dednat6/eoo.lua" "Class") Class = { type = "Class", __call = function (class, o) return setmetatable(o, class) end, } setmetatable(Class, Class) -- __ __ __ __ _ _ -- | \/ |_ _\ \/ /_ __ ___ __ _| | | -- | |\/| | | | |\ /| '_ \ / __/ _` | | | -- | | | | |_| |/ \| |_) | (_| (_| | | | -- |_| |_|\__, /_/\_\ .__/ \___\__,_|_|_| -- |___/ |_| -- -- «MyXpcall» (to ".MyXpcall") -- See: (find-lua51manual "#pdf-xpcall" "xpcall (f, err)") -- (find-lua52manual "#pdf-xpcall" "xpcall (f, msgh [, arg1, ยทยทยท])") -- (find-lua51manual "#pdf-debug.traceback") -- (find-lua51manual "#pdf-error") -- (find-es "lua5" "xpcall-2020") -- -- If a Lua REPL calls F2, which calls F1, which calls F0, which -- yields an error, we have this call diagram: -- -- REPL -- : \-> F2 -- : \-> F1 -- : \-> F0 -- : \-> error -- : |-> print(debug.traceback()) -- v <---------------/ -- -- Lua's "xpcall" lets us do something similar to that outside the Lua -- REPL, but in a way that I found very difficult to decypher and -- quite difficult to use. My MyXpcall class is a wrapper around -- xpcall that lets me use xpcall in a way that 1) has good defaults, -- 2) is super-easy to hack, 3) lets me change the defaults easily, 4) -- saves all the intermediate results. If I do -- -- myx = MyXpcall:new() -- -- then the call diagram of myx:call(F2) on errors is: -- -- myx:call(F2) -- : \-> F2 -- : \-> F1 -- : \-> F0 -- : \-> myx.errhandler -- : |-> debug.traceback -- : |-> print(myx:shortertraceback()) -- : <---------------/ -- v -- -- and these fields get set: -- -- myx.xp_results: the results of xpcall(wrapper_around_F2) -- myx.eh_args: the arguments given to myx.errhandler -- myx.tb: the string returned by debug.traceback() -- -- when F2 succeeds these fields get set: -- -- myx.f_results: the results of F2(...) -- myx.xp_results: the results of xpcall(wrapper_around_F2) -- -- For more details see -- the code and the tests. -- «MyXpcall-class» (to ".MyXpcall-class") -- Also in: (find-angg "LUA/lua50init.lua" "MyXpcall") -- MyXpcall = Class { type = "MyXpcall", new = function (T) return MyXpcall(T or {lvl = 3}) end; __index = { call = function (myx, f, ...) return myx:call0(f, ...):ret() end, call0 = function (myx, f, ...) local f_args = pack(...) local g = function () myx.f_results = pack(f(unpack(f_args))) end myx.xp_results = pack(xpcall(g, myx:errhandler())) return myx end, success = function (myx) return myx.xp_results[1] end, ret = function (myx) if myx:success() then return unpack(myx.f_results) end end, errhandler = function (myx) return function (...) myx.eh_args = pack(...) myx.tb = debug.traceback(myx:tbargs()) print(myx:shortertraceback()) return "eh22", "eh33", "eh44" -- only the first is used end end, tbargs = function (myx) return myx.eh_args[1], myx.lvl end, shortertraceback = function (myx) local lines = splitlines(myx.tb) return table.concat(lines, "\n", 1, #lines - 6) end, }, } -- «MyXpcall-tests» (to ".MyXpcall-tests") --[[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "myxpcall.lua" fcode = function (n) return format("F%d = function (...) return 0,F%d(...) end", n+1, n) end -- Test calling a function -- that yields an error -- F0 = function (...) PP(...); error("Errrr!") end for i=0,20 do print(fcode(i)) end for i=0,20 do eval(fcode(i)) end F20(2, 3, 4) myx = MyXpcall.new():call0(F20, 2, 3, 4) PP(myx:ret()) PPV(keys(myx)) -- Test calling a function -- that succeeds -- F0 = function (...) PP(...); return 22,33 end myx = MyXpcall.new():call0(F20, 2, 3, 4) PP(myx:ret()) PPV(keys(myx)) --]] -- ____ _ -- | _ \ ___ _ __ | | -- | |_) / _ \ '_ \| | -- | _ < __/ |_) | | -- |_| \_\___| .__/|_| -- |_| -- -- «Repl» (to ".Repl") -- Also in: (find-angg "LUA/lua50init.lua" "Repl") -- A simple REPL for Lua that handles errors well enough. -- It uses MyXpcall to run code. -- Usage: -- -- REPL = Repl:new(); REPL:repl() -- -- To exit the REPL set REPL.stop to true. Repl = Class { type = "Repl", new = function () return Repl({}) end, __index = { bigstr = function (r) return table.concat(r.lines, "\n") end, errincomplete = function (r, err) return err:find(" near '<eof>'$") end, incompletep0 = function (r, bigstr) local f, err = loadstring(bigstr) return (f == nil) and r:errincomplete(err) end, code = function (r) return r:bigstr():gsub("^=", "return ") end, incompletep = function (r) return r:incompletep0(r:code()) end, read00 = function (r, prompt) io.write(prompt); return io.read() end, read0 = function (r, prompt) table.insert(r.lines, r:read00(prompt)) end, read1 = function (r) return r:read0 ">>> " end, read2 = function (r) return r:read0 "... " end, read = function (r) r.lines = {} r:read1() while r:incompletep() do r:read2() end return r end, evalprint = function (r) r.f, r.err = loadstring(r:code()) if not r.f then print(r.err); return r end r.myx = MyXpcall.new():call0(r.f) if r.myx:success() and r:bigstr():match("^=") then r:print() end return r end, print = function (r) print(unpack(r.myx.f_results)) end, repl = function (r) while not r.stop do r:read():evalprint() end end, }, } -- «Repl-tests» (to ".Repl-tests") --[[ * (eepitch-shell) * (eepitch-kill) * (eepitch-shell) LUA_INIT="" lua5.1 LUA_INIT="" lua5.2 LUA_INIT="" lua5.3 LUA_INIT="" lua5.4 PP() * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "myxpcall.lua" REPL = Repl.new() REPL:repl() print( 1+2 ) = 1+2 PPV(REPL.myx) print(REPL.myx.tb) fcode = function (n) return format("F%d = function (...) return 0,F%d(...) end", n+1, n) end F0 = function (...) PP(...); error("Errrr!") end for i=0,20 do print(fcode(i)) end for i=0,20 do eval(fcode(i)) end F20(2, 3, 4) REPL.stop = 1 --]]