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