Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
-- This file: -- http://anggtwu.net/LUA/DGetInfo1.lua.html -- http://anggtwu.net/LUA/DGetInfo1.lua -- (find-angg "LUA/DGetInfo1.lua") -- Author: Eduardo Ochs <eduardoochs@gmail.com> -- -- (defun e () (interactive) (find-angg "LUA/DGetInfo1.lua")) -- Also here: (find-angg "LUA/lua50init.lua" "DGetInfo") -- (find-angg "LUA/lua50init.lua" "DGetInfos") -- Supersedes: (find-angg "LUA/GetInfo.lua") -- (find-angg "LUA/GetInfo2.lua") -- May use: (find-angg "LUA/Prosody1.lua") -- Superseded by: -- (find-angg "LUA/PreTraceback1.lua") -- -- Introduction -- ============ -- For many years I thought that debug.getinfo was horribly hard to -- use, because we have to make the right adjustments to the "lvl" -- parameter in each call to it... then in 2021 I realized that we can -- use it in this way: we first run all the "debug.getinfo"s and -- "debug.getlocal"s that we can, to get all the data in the call -- stack, and we put all that data in an array of tables; then we can -- use that data to inspect it or to produce tracebacks. Calls to -- "debug.setlocal" still need a well-adjusted "lvl", but I use that -- very rarely. -- -- How to use this: a call to GetInfos.new() collects all data from -- the call stack and returns a table with otype "GetInfos" of tables -- with otype "GetInfo". See the tests. -- «.DGetInfo» (to "DGetInfo") -- «.DGetInfo-method» (to "DGetInfo-method") -- «.DGetInfo-luatb» (to "DGetInfo-luatb") -- «.DGetInfo-tests» (to "DGetInfo-tests") -- «.DGetInfos» (to "DGetInfos") -- «.DGetInfos-tests» (to "DGetInfos-tests") -- ____ ____ _ ___ __ -- | _ \ / ___| ___| |_|_ _|_ __ / _| ___ -- | | | | | _ / _ \ __|| || '_ \| |_ / _ \ -- | |_| | |_| | __/ |_ | || | | | _| (_) | -- |____/ \____|\___|\__|___|_| |_|_| \___/ -- -- «DGetInfo» (to ".DGetInfo") -- Idea: running something like -- -- dgi = DGetInfo.atlevel(99, "getvalues") -- -- calls debug.getinfo and debug.getlocal to get a lot of information -- about the stack frame at level 99, and puts that information in a -- static object that is easy to inspect. This class is used by the -- class DGetInfos, defined below. -- DGetInfo = Class { type = "DGetInfo", what = "nSluf", new = function (A) return DGetInfo(A or {}) end, -- atlevel = function (lvl, getvalues) local dgi = debug.getinfo(lvl, DGetInfo.what) if not dgi then return end if getvalues then dgi.values = {} end for i=1,1000 do local name,value = debug.getlocal(lvl, i) if not name then break end dgi[i] = name if getvalues then dgi.values[i] = value end end return DGetInfo(dgi) end, fromfunction = function (f) local dgi = debug.getinfo(f, DGetInfo.what) return DGetInfo(dgi) end, -- -- __tostring = function (dgi) return dgi:tostring() end, __index = { tostring = function (dgi) return dgi:tb() end, -- -- «DGetInfo-method» (to ".DGetInfo-method") -- method = "fvtb", -- method = "prosodytb", -- (find-angg "LUA/Prosody1.lua" "Prosody") method = "luatb", tb = function (dgi) return dgi[dgi.method](dgi) end, tbi = function (dgi, i) return format("%2d -> %s", i, dgi:tb()) end, prosodytb = function (dgi) return Prosody.traceback1(dgi) end, fvtb = function (dgi) return dgi:funname().." :: "..dgi:vars() end, -- -- «DGetInfo-luatb» (to ".DGetInfo-luatb") -- See: (find-angg ".emacs" "find-luatb") luatb = function (dgi) if dgi.short_src == "[C]" then return dgi:luatb_C() end if dgi.what == "main" then return dgi:luatb_main() end if dgi.short_src == "(tail call)" then return dgi:luatb_tailcall() end return dgi:luatb_other() end, luatb_tailcall = function (dgi) return "[Lua] tail call" end, luatb_main = function (dgi) return "[Lua] "..dgi.short_src .." line "..dgi.currentline end, luatb_C = function (dgi) return "[ C ] " ..dgi.namewhat .." C function "..dgi:luatb_name() end, luatb_other0 = function (dgi) return (dgi:shortsrc() or "") .." "..(dgi:luatb_line1() or "") .." "..(dgi:luatb_line2() or "") .." "..(dgi.namewhat or "") .." "..(dgi.name or "") end, luatb_other = function (dgi) return format('(find-luatb \"%s\")', rtrim(dgi:luatb_other0())) end, luatb_name = function (dgi) return dgi.name and format("%q", dgi.name) or "(unknown name)" end, luatb_line1 = function (dgi) return dgi.linedefined end, luatb_line2 = function (dgi) if dgi.currentline == -1 then return dgi.lastlinedefined end return dgi.currentline end, -- shortsrc = function (dgi) return dgi.short_src and ee_shorten(dgi.short_src) end, funname = function (dgi) return dgi.name or "(noname)" end, vars = function (dgi) return table.concat(dgi, " ") end, varns = function (dgi) local namens = {} for i,name in ipairs(dgi) do namens[name] = i end return namens end, vs = function (dgi) local values = VTableP({}) for i,name in ipairs(dgi) do values[name] = dgi.values[i] end return values end, v = function (dgi, name) local n = dgi:varns()[name] or error("Bad var name: "..tostring(name)) return dgi.values[n] end, -- find_fline = function (dgi, line) local src = dgi:shortsrc() return format("(find-fline \"%s\" %d)", src, line) end, fline = function (dgi) local l0 = dgi.linedefined local l1 = dgi.currentline local l2 = dgi.lastlinedefined return dgi:find_fline(l1) end, -- -- 2022jul17: info = function (dgi, tostr) return pformat("spec: %s\nsrc: %s\n%s", dgi, dgi:fline(), dgi:infovalns(tostr)) end, infovaln = function (dgi, n, tostr) tostr = tostr or mytostring return format(" %d %q: %s", n, dgi[n], tostr(dgi.values[n])) end, infovalns = function (dgi, tostr) local f = function (i) return dgi:infovaln(i, tostr) end return mapconcat(f, seq(1, #dgi), "\n") end, }, } -- «DGetInfo-tests» (to ".DGetInfo-tests") --[[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "DGetInfo1.lua" dg = DGetInfo.fromfunction(split) = dg PPPV(dg) = DGetInfo.fromfunction(split):tostring() = DGetInfo.fromfunction(print):tostring() --]] -- ____ ____ _ ___ __ -- | _ \ / ___| ___| |_|_ _|_ __ / _| ___ ___ -- | | | | | _ / _ \ __|| || '_ \| |_ / _ \/ __| -- | |_| | |_| | __/ |_ | || | | | _| (_) \__ \ -- |____/ \____|\___|\__|___|_| |_|_| \___/|___/ -- -- «DGetInfos» (to ".DGetInfos") -- Also here: (find-angg "LUA/lua50init.lua" "DGetInfos") -- Idea: running something like -- -- dgis = DGetInfos.newv() -- -- runs lots of "debug.getinfo()"s and "debug.getlocal"s via DGetInfo, -- and returns a static structure that can be inspected in a repl, -- both inside an error handler and post-mortem. -- DGetInfos = Class { type = "DGetInfos", new = function (getvalues) return DGetInfos({}):getinfos(getvalues) end, newv = function () return DGetInfos.new("getvalues") end, __tostring = function (dgis) return dgis:tostring() end, __index = { getinfos = function (dgis, getvalues) dgis.infos = {} for i=0,1000 do dgis[i] = DGetInfo.atlevel(i, getvalues) if not dgis[i] then return dgis end end end, firstsuch = function (dgis, f) for i=0,#dgis do if f(dgis[i], i) then return i end end end, setbase = function (dgis, f) local z = dgis:firstsuch(f) if not z then error("setbase: not found") end dgis.base = z return dgis end, -- seq = function (dgis, a, b, dir) a,b = (a or #dgis),(b or 0) dir = dir or (a <= b and 1 or -1) return seq(a, b, dir) end, tostring = function (dgis, a, b, dir) local f = function (i) return dgis[i]:tbi(i) end return mapconcat(f, dgis:seq(a, b, dir), "\n") end, -- tb = function (dgis, a, b, dir) return dgis:tostring(a, b, dir) end, tbn = function (dgis, a, b, dir) return dgis:tostring(a, b, dir) end, }, } -- «DGetInfos-tests» (to ".DGetInfos-tests") --[[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "DGetInfo1.lua" dgis = DGetInfos.newv() = dgis DGetInfo.__index.method = "luatb" = dgis = dgis[4] PPPV(dgis[4]) DGetInfo.__index.method = "fvtb" = dgis DGetInfo.__index.method = "prosodytb" = dgis -- err dofile "Prosody1.lua" = dgis --]] -- Local Variables: -- coding: utf-8-unix -- End: