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: