Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
-- This file:
--   http://angg.twu.net/LUA/GetInfo.lua.html
--   http://angg.twu.net/LUA/GetInfo.lua
--           (find-angg "LUA/GetInfo.lua")
-- Author: Eduardo Ochs <eduardoochs@gmail.com>
-- Version: 2021sep25

-- 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.
--
-- TODO: the version in the init file is more recent than this - update.

-- «.dependencies»	(to "dependencies")
-- «.GetInfo»		(to "GetInfo")
-- «.GetInfo-tests»	(to "GetInfo-tests")
-- «.GetInfos»		(to "GetInfos")
-- «.old-tests»		(to "old-tests")



-- «dependencies»  (to ".dependencies")
-- (find-angg "emacs-lua/")
-- (find-angg "emacs-lua/EdrxRepl.lua")
-- (find-angg "emacs-lua/DFS.lua")
-- Path.prepend("path", "~/emacs-lua/?.lua")
-- require "EdrxRepl"
-- require "DFS"


-- «GetInfo»  (to ".GetInfo")
-- Also here: (find-angg "LUA/lua50init.lua" "GetInfo")
-- (find-lua51manual "#5.9" "The Debug Library")
-- (find-lua51manual "#lua_getinfo")
-- (find-lua51manual "#lua_getlocal")
-- (find-lua51manual "#pdf-debug.getlocal")
-- (find-lua51manual "#pdf-debug.setlocal")
-- (find-es "lua5" "debug.getinfo")
--  'n': fills in the field name and namewhat;
--  'S': fills in the fields source, short_src, linedefined, lastlinedefined, and what;
--  'l': fills in the field currentline;
--  'u': fills in the field nups;
--  'f': pushes onto the stack the function that is running at the given level;
--  'L': pushes onto the stack a table whose indices are the numbers of the lines...
--
GetInfo = Class {
  type = "GetInfo",
  what = "nSluf",
  new  = function () return GetInfo({}) end,
  --
  level_nil = function ()
      for i=0,1000 do if debug.getinfo(i, "f") == nil then return i end end
    end,
  level_f = function (i, j, f)
      for k=i,j do if debug.getinfo(i, "f").func == f then return k end end
    end,
  atlevel = function (lvl, getvalues)
      local gi = debug.getinfo(lvl, GetInfo.what)
      if not gi then return end
      if getvalues then gi.values = {} end
      for i=1,1000 do
	local name,value = debug.getlocal(lvl, i)
        if not name then break end
	gi[i] = name
        if getvalues then gi.values[i] = value end
      end
      return GetInfo(gi)
    end,
  __tostring = function (gi)
      return gi:funname().." :: "..gi:vars()
    end,
  __index = {
    funname = function (gi) return gi.name or "(noname)" end,
    vars = function (gi)
        return table.concat(gi, " ")
      end,
    --
    -- gi:varns("nameoffirstarg") == 1
    -- gi:v    ("nameoffirstarg") == firstarg
    -- gi:vs()  .nameoffirstarg   == firstarg
    -- gi:vs()  .badname          == nil
    -- gi:v    ("badname")        == error
    varns = function (gi)
        local namens = {}
        for i,name in ipairs(gi) do namens[name] = i end
        return namens
      end,
    vs = function (gi)
        local values = {}
        for i,name in ipairs(gi) do values[name] = gi.values[i] end
        return values
      end,
    v = function (gi, name)
        local n = gi:varns()[name] or error("Bad var name: "..tostring(name))
        return gi.values[n]
      end,
  },
}

-- «GetInfo-tests»  (to ".GetInfo-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "GetInfo.lua"
f   = function (a1,a2)       local l1,l2;   ff(41,42)    end
ff  = function (aa1,aa2,...) local ll1,ll2; fff(411,422) end
fff = function (aaa1,aaa2)
    print(GetInfo.atlevel(0):vars());  -- ?
    print(GetInfo.atlevel(1):vars());  -- GetInfo.atlevel
    print(GetInfo.atlevel(2):vars());  -- fff
    print(GetInfo.atlevel(3):vars());  -- ff
    print(GetInfo.atlevel(4):vars());  -- f
    PP(GetInfo.atlevel(3))
    PP(GetInfo.atlevel(3, "getvalues"))
    gis = GetInfos:new("getvalues")
  end
f()
= gis[0]
= gis[2]
= gis
= gis:firstsuch(function (gi) return gi.name == "fff" end)
  gis:setbase  (function (gi) return gi.name == "fff" end)
= gis:tostring(gis.base+2, gis.base)
= gis:tostring(gis.base, gis.base+2)
=  gis.base
=  gis[gis.base]
PP(gis[gis.base])
PP(gis[gis.base]:varns())
=  gis[gis.base]:v("aaa1")
=  gis[gis.base]:vs().aaa1
=  gis[gis.base]:vs().badname
=  gis[gis.base]:v("badname")

_fff   = gis[gis.base]
_fff_v = gis[gis.base]:vs()
= _fff
= _fff_v.aaa1

--]]


-- «GetInfos»  (to ".GetInfos")
-- Also here: (find-angg "LUA/lua50init.lua" "GetInfos")
--
GetInfos = Class {
  type = "GetInfos",
  new  = function (getvalues) return GetInfos({}):getinfos(getvalues) end,
  newv = function () return GetInfos().new("getvalues") end,
  __tostring = function (gis) return gis:tostring() end,
  __index = {
    getinfos = function (gis, getvalues)
        gis.infos = {}
        for i=0,1000 do
          gis[i] = GetInfo.atlevel(i, getvalues)
          if not gis[i] then return gis end
        end
      end,
    tostring = function (gis, a, b, dir)
        a,b = (a or #gis),(b or 0)
        dir = dir or (a <= b and 1 or -1)
        local f = function (i) return format("%d -> %s", i, tostring(gis[i])) end
        return mapconcat(f, seq(a, b, dir), "\n")
      end,
    firstsuch = function (gis, f)
        for i=0,#gis do
          if f(gis[i], i) then return i end
        end
      end,
    setbase = function (gis, f)
        local z = gis:firstsuch(f)
        if not z then error("setbase: not found") end
        gis.base = z
        return gis
      end,
  },
}

-- «GetInfo-tests»  (to ".GetInfo-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "GetInfo.lua"
f   = function (a1,a2)       local l1,l2;   ff(41,42)    end
ff  = function (aa1,aa2,...) local ll1,ll2; fff(411,422) end
fff = function (aaa1,aaa2)
    gis = GetInfos:new("getvalues")
  end
f()
= gis
  gis:setbase  (function (gi) return gi.name == "fff" end)
= gis:tostring(gis.base+2, gis.base)
= gis:tostring(gis.base, gis.base+2)
_fff   = gis[gis.base]
_fff_v = gis[gis.base]:vs()
= _fff
= _fff_v.aaa1

* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "GetInfo.lua"
f   = function (a1,a2)       local l1,l2;   ff(41,42)    end
ff  = function (aa1,aa2,...) local ll1,ll2; fff(411,422) end
fff = function (aaa1,aaa2)
    print(GetInfo.atlevel(0):vars());  -- ?
    print(GetInfo.atlevel(1):vars());  -- GetInfo.atlevel
    print(GetInfo.atlevel(2):vars());  -- fff
    print(GetInfo.atlevel(3):vars());  -- ff
    print(GetInfo.atlevel(4):vars());  -- f
    PP(GetInfo.atlevel(3))
    PP(GetInfo.atlevel(3, "getvalues"))
    gis = GetInfos:new("getvalues")
  end
f()
= gis[0]
= gis[2]
= gis
= gis:firstsuch(function (gi) return gi.name == "fff" end)
  gis:setbase  (function (gi) return gi.name == "fff" end)
= gis:tostring(gis.base+2, gis.base)
= gis:tostring(gis.base, gis.base+2)
=  gis.base
=  gis[gis.base]
PP(gis[gis.base])
PP(gis[gis.base]:varns())
=  gis[gis.base]:v("aaa1")
=  gis[gis.base]:vs().aaa1
=  gis[gis.base]:vs().badname
=  gis[gis.base]:v("badname")

_fff   = gis[gis.base]
_fff_v = gis[gis.base]:vs()
= _fff
= _fff_v.aaa1

--]]





-- «old-tests»  (to ".old-tests")

f = function (a, b)
    local c, d
    g(5, 6)
    print("f ok")
  end
g = function (aa, bb, ...)
    local cc, dd
    h(5, 6)
    print("g ok")
  end
h = function ()
    REPL = EdrxRepl.new()
    REPL:repl()              -- enter the repl
  end

--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "GetInfo.lua"

f(10, 20)
gis = GetInfos.new()

PPV(gis.infos[4])
PPV(gis.infos[4]:vars())

for i,gi in gis:gen() do print(i, gi:vars()) end
for i,gi in gis:gen() do PP(i, gi.name, gi.what, gi.namewhat) end
for i,gi in gis:gen() do PP(i, gi.short_src, gi.source) end
PPV(gis.infos[5])

d = DFS.new():nameluatables():nameallfunctions()
for i,gi in gis:gen() do PP(i, gi.name, gi.what, gi.namewhat) end
for i,gi in gis:gen() do PP(i, d:getname(gi.func), gi:vars()) end

gis = gis:after(EdrxRepl.__index.repl)
for i,gi in gis:gen() do PP(i, gi.name, gi.what, gi.namewhat) end
for i,gi in gis:gen() do PP(i, d:getname(gi.func), gi:vars()) end

level_nil  = GetInfo.level_nil()
level_repl = GetInfo.level_f(0, level_nil - 1, EdrxRepl.__index.repl)
= level_nil, level_repl

--> /home/edrx/emacs-lua/EdrxRepl.lua:124: attempt to call method 'outsuccess' (a nil value)


--]]