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