Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
-- This file:
--   http://anggtwu.net/LUA/PreTraceback1.lua.html
--   http://anggtwu.net/LUA/PreTraceback1.lua
--          (find-angg "LUA/PreTraceback1.lua")
-- Author: Eduardo Ochs <eduardoochs@gmail.com>
-- Version: 2024jan24
--
-- This file defines three classes: PreTraceback, DGetFrame and
-- DGetPairs. A pretraceback object is:
--
--   a) like the result of debug.traceback(), but before the
--      conversion to a string,
--   b) a table of stack frames, indexed by "levels",
--   c) shown as a series of lines like "lvl -> <f>", where the
--      "<f>" is produced by the class PrintFunction.
--
-- Each of these stack frames is a dgetframe object. Each dgetframe is
-- built by running debug.getinfo(lvl,"nSluf"), adding information
-- about the local variables and upvalues at that level, and
-- converting the result to the DGetFrame class.
--
-- The information about local variables in a dgetframe object is
-- stored in a dgetpairs object and the information about its upvalues
-- is stored in another dgetpairs object. Each "pair" in a dgetpairs
-- object is actually a triple made of an index (an "i"), a name, and
-- a value. The two main ways of printing these triples are:
--
--   1) dpg:ins(), that prints the "i"s and the names,
--   2) dpg:invs(), that prints the "i"s, the names, and the values.
--
-- The class DGetFrame can also be used to inspect the upvalues of
-- functions that are not in a stack frame. A dgetframe object created
-- with
--
--   dg = DGetFrame.fromfunction(f, true, true)
--
-- will have a field .upvalues but its field .locals will be dummy.
--
-- Uses:
--   (find-angg "LUA/PrintFunction1.lua")
-- Supersedes:
--   (find-angg "LUA/DGetInfo1.lua")
--
-- (defun e () (interactive) (find-angg "LUA/PreTraceback1.lua"))

-- «.DGetPairs»			(to "DGetPairs")
-- «.DGetPairs-tests»		(to "DGetPairs-tests")
-- «.DGetFrame»			(to "DGetFrame")
-- «.DGetFrame-tests»		(to "DGetFrame-tests")
-- «.PreTraceback»		(to "PreTraceback")
-- «.PreTraceback-tests»	(to "PreTraceback-tests")

require "PrintFunction1"     -- (find-angg "LUA/PrintFunction1.lua")



--  ____   ____      _   ____       _          
-- |  _ \ / ___| ___| |_|  _ \ __ _(_)_ __ ___
-- | | | | |  _ / _ \ __| |_) / _` | | '__/ __|
-- | |_| | |_| |  __/ |_|  __/ (_| | | |  \__ \
-- |____/ \____|\___|\__|_|   \__,_|_|_|  |___/
--
-- A dgetpairs object is a very low-level thing: a pretraceback is a
-- table of dgetframe objects, and each dgetframe object has two
-- dgetpairs objects.
--
-- A pretraceback object is a table of stack frames, each represented
-- as a dgetframe object; each stack frame can have information about
-- local variables and upvalues; in a dgetframe object the information
-- about local variables is stored in a dgetpairs object and the
-- information about its upvalues is stored in another dgetpairs
-- object.
--
-- Each entry in a dgetpairs object is a triple made of an index (an
-- "i"), a name, and a value. The two main ways of printing these
-- triples are:
--
--   1) dpg:ins(), that prints the "i"s and the names,
--   2) dpg:invs(), that prints the "i"s, the names, and the values.
--
-- Note that: 1) a function or a stack frame can have different local
-- variables with the same names, and 2) sometimes we want to store
-- the names of these local variable but not their values... DGetPairs
-- has to handle all this.
--
-- «DGetPairs»  (to ".DGetPairs")
-- Also here: (find-angg "LUA/lua50init.lua" "DGetPairs")

DGetPairs = Class {
  type = "DGetPairs",
  new = function (storevalues)
      return DGetPairs {names={}, values=(storevalues and {})}
    end,
  __tostring = function (dg) return dg:tostring() end,
  __index = {
    n   = function (dgp) return #dgp.names end,
    seq = function (dgp) return HTable(seq(1,dgp:n())) end,
    set = function (dgp,i,name,value)
        if not name then return end
        dgp.names[i] = name
        if dgp.values then dgp.values[i] = value end
        return dgp
      end,
    iname = function (dgp,i)
        return pformat("%d:%s", i, dgp.names[i])
      end,
    inamevalue = function (dgp,i)
        return pformat("%d:%s:%s", i, dgp.names[i], dgp.values[i])
      end,
    inames = function (dgp, sep)
        local f = function (i) return dgp:iname(i) end
        return mapconcat(f, dgp:seq(), sep or " ")
      end,
    inamesvalues = function (dgp, sep)
        if not dgp.values then return dgp:inames(sep) end
        local f = function (i) return dgp:inamevalue(i) end
        return mapconcat(f, dgp:seq(), sep or "\n")
      end,
    ins      = function (dgp, sep) return dgp:inames(sep) end,
    invs     = function (dgp, sep) return dgp:inamesvalues(sep) end,
    tostring = function (dgp, sep) return dgp:ins(sep) end,
  },
}

-- «DGetPairs-tests»  (to ".DGetPairs-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "PreTraceback1.lua"
dgp = DGetPairs.new()
dgp = DGetPairs.new("storevalues")
PPV(dgp)
= dgp:set(1,"a",10)
= dgp:set(2,"b",20)
= dgp:set(3)
= dgp:n()
= dgp:tostring()
= dgp:tostring("\n")
= dgp
= dgp:seq()

--]]


--  ____   ____      _   _____                         
-- |  _ \ / ___| ___| |_|  ___| __ __ _ _ __ ___   ___ 
-- | | | | |  _ / _ \ __| |_ | '__/ _` | '_ ` _ \ / _ \
-- | |_| | |_| |  __/ |_|  _|| | | (_| | | | | | |  __/
-- |____/ \____|\___|\__|_|  |_|  \__,_|_| |_| |_|\___|
--
-- A dgetframe object describes a stack frame. In low-level terms, a
-- dgetframe object contains the result of running a
-- debug.getinfo(level,"nSluf") on a certain level of the stack, plus
-- the information on the local variables and upvalues at that level.
--                                                     
-- A dgetframe object can also describe the result of running
-- debug.getinfo(f,"nSluf") on a function f.
--                                                     
-- «DGetFrame»  (to ".DGetFrame")
-- Also here: (find-angg "LUA/lua50init.lua" "DGetFrame")

DGetFrame = Class {
  type    = "DGetFrame",
  atlevel = function (lvl, storevalues, storeupvalues)
      local  dg = debug.getinfo(lvl, "nSluf")
      if not dg then return end
      dg.locals   = DGetPairs.new(storevalues)
      dg.upvalues = DGetPairs.new(storeupvalues)
      for i=1,1000 do
	local name,value = debug.getlocal(lvl, i)
        if not dg.locals:set(i,name,value) then break end
      end
      return DGetFrame(dg):getupvalues()
    end,
  fromfunction = function (f, storevalues, storeupvalues)
      local dg    = debug.getinfo(f, "nSluf")
      dg.locals   = DGetPairs.new(storevalues)   -- dummy
      dg.upvalues = DGetPairs.new(storeupvalues)
      return DGetFrame(dg):getupvalues()
    end,
  getframes = function (storevalues, storeupvalues)
      local A = {}
      for i=0,1000 do
        A[i] = DGetFrame.atlevel(i, storevalues, storeupvalues)
        if not A[i] then return A end
        end
      end,
  __tostring = function (dgf) return dgf:tostring() end,
  __index = {
    getupvalues = function (dgf,n)
        for i=1,(n or dgf.nups) do
           local name,value = debug.getupvalue(dgf.func, i)
           if not dgf.upvalues:set(i,name,value) then break end
         end
         return dgf
      end,
    delpairs = function (dgf)
        dgf = copy(dgf)
        dgf.locals   = nil
        dgf.upvalues = nil
        return dgf
      end,
    toprintfunction = function (dgf) return PrintFunction(dgf:delpairs()) end,
    tostring = function (dgf) return tostring(dgf:toprintfunction()) end,
    pf = function (dgf) return dgf:toprintfunction() end,
    v  = function (dgf) return dgf:toprintfunction():v() end,
  },
}

-- «DGetFrame-tests»  (to ".DGetFrame-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "PreTraceback1.lua"
dg = DGetFrame.atlevel(2)
= dg
= dg:toprintfunction()      -- compact
= dg:toprintfunction():v()  -- vertical

-- Inspect upvalues:
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "PreTraceback1.lua"

mksetget = function ()
    local u,v -- new upvalues
    local set = function (a,b) u,v=a,b end
    local get = function () return u,v end
    return set,get
  end

set1,get1 = mksetget()
set2,get2 = mksetget()
set1(20,42); set2(99,200)
= get1()
= get2()

dgf = DGetFrame.fromfunction(set1, true, true)
= dgf
= dgf.locals
= dgf.upvalues
= dgf.upvalues:invs()

--]]


--  ____          _____                   _                _    
-- |  _ \ _ __ __|_   _| __ __ _  ___ ___| |__   __ _  ___| | __
-- | |_) | '__/ _ \| || '__/ _` |/ __/ _ \ '_ \ / _` |/ __| |/ /
-- |  __/| | |  __/| || | | (_| | (_|  __/ |_) | (_| | (__|   < 
-- |_|   |_|  \___||_||_|  \__,_|\___\___|_.__/ \__,_|\___|_|\_\
--
-- In Lua the function debug.traceback returns a string:
--
--   (find-lua51manual "#pdf-debug.traceback")
--
-- A pretraceback object contains the data of that traceback - i.e., a
-- low-resolution picture of the call stack - before that data is
-- converted to a string. The default __tostring method of
-- PreTraceback uses a very compact format in which each stack frame
-- is printed as a single line, but we can also print stack frames in
-- more detailed ways, and inspect their local variables and upvalues.
--
-- «PreTraceback»  (to ".PreTraceback")
-- Also here: (find-angg "LUA/lua50init.lua" "PreTraceback")
-- See:       (find-angg "LUA/lua50init.lua" "pformat")

PreTraceback = Class {
  type = "PreTraceback",
  new  = function (getvalues, getupvalues)
      local frames = DGetFrame.getframes(getvalues, getupvalues)
      return PreTraceback(frames)
    end,
  __tostring = function (pt) return pt:tostring() end,
  __index = {
    seq = function (pt, a, b, dir)
        a,b = (a or #pt),(b or 0)
        dir = dir or (a <= b and 1 or -1)
        return seq(a, b, dir)
      end,
    tostring = function (pt, a, b, dir)
        local f = function (i) return pformat("%s -> %s", i, pt[i]) end
        return mapconcat(f, pt:seq(a, b, dir), "\n")
      end,
  },
}

-- «PreTraceback-tests»  (to ".PreTraceback-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "PreTraceback1.lua"
  pt = PreTraceback.new()
= pt
= pt[5]:toprintfunction():v()
= pt[4]:toprintfunction():v()
= pt[3]:toprintfunction():v()
= pt[3]:toprintfunction()
= pt[3]

--]]





-- Local Variables:
-- coding:  utf-8-unix
-- End: