|
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
--]]