AylinCE Grandmaster Cheater Supreme
Reputation: 37
Joined: 16 Feb 2017 Posts: 1532
|
Posted: Thu Nov 13, 2025 11:26 am Post subject: Lua Deep Table Introspection and Call Path Analyzer .. |
|
|
A nested table analysis and call path analysis code for testing table dumps and call paths of long, nested tables in Lua or in an application.
This is a helpful function for those who create very long tables, allowing them to check where they left off and how to call them.
I won't go into much detail.
You will understand what it does with a dump analysis code, a sample table and output.
Dump, analysis:
| Code: | --[[
Function: deepDumpWithPath
Description: Recursively prints the contents of a Lua table, including nested subtables,
without depth restrictions. It also displays the full **call path** (syntax) for accessing each element. Safely handles circular references.
Parameters:
tbl: (table) - The main table to inspect.
indent: (number) [Optional] - The current indentation level (defaults to 0).
currentPath: (string) [Optional] - The accumulated call path string
(e.g., "myTable[1].settings"). Defaults to "table".
seen: (table) [Optional] - Internal table to track circular references.
--]]
function deepDumpWithPath(tbl, indent, currentPath, seen)
indent = indent or 0
seen = seen or {}
currentPath = currentPath or "table" -- Default starting path
local currentIndent = string.rep(" ", indent)
-- 1. Table Check and Circular Reference Check
if type(tbl) ~= "table" then
print(currentIndent .. "[ERROR: Expected table, received: " .. type(tbl) .. "]")
return
end
if seen[tbl] then
print(currentIndent .. "{" .. currentPath .. " = ... CIRCULAR REFERENCE DETECTED ...}")
return
end
-- Mark the current table as seen
seen[tbl] = true
print(currentIndent .. "{ > " .. currentPath .. " < .. (TABLE START)") -- Print the table path at the opening brace
-- 2. Traverse Elements
for k, v in pairs(tbl) do
local keyString
local nextPath
local nextIndent = string.rep(" -", indent + 1)
-- Format the key and build the next call path
if type(k) == "string" then
-- String keys: Use brackets for consistency, but build path for dot notation if possible
if string.match(k, "^[a-zA-Z_][a-zA-Z0-9_]*$") then
keyString = string.format('["%s"]', k)
nextPath = currentPath .. "." .. k -- Suitable for object access (e.g., CE objects)
else
keyString = string.format('["%s"]', k)
nextPath = currentPath .. string.format('["%s"]', k)
end
else
-- Numeric keys: object[index]
keyString = string.format("[%s]", tostring(k))
nextPath = currentPath .. string.format("[%s]", tostring(k))
end
-- Process based on Value Type
if type(v) == "table" then
print(nextIndent .. keyString .. " = [table]")
-- Recursive call: Pass the new path and increased indentation
deepDumpWithPath(v, indent + 2, nextPath, seen)
elseif type(v) == "function" then
-- Special output for functions
print(nextIndent .. keyString .. " = [Function] -- CALL: " .. nextPath .. "()")
elseif type(v) == "userdata" then
-- Special output for UserData/CE Objects
print(nextIndent .. keyString .. " = [UserData] -- PATH: " .. nextPath)
else
-- Simple types (string, number, boolean, etc.)
print(nextIndent .. keyString .. " = " .. tostring(v) .. " -- PATH: " .. nextPath)
end
end
-- 3. Close Table
print(currentIndent .. "} > " .. currentPath .. " < .. (TABLE END)")
end |
-- ************************ E X A M P L E T A B L E ************************--
| Code: | -- Global assumption for CE's AddressList object for demonstration purposes
--local AddressList = AddressList -- Use the actual CE object in CE environment
local GameConfig_V3 = {
-- 1. Main Configuration Structure
["GeneralSettings"] = {
Resolution = "1920x1080",
FPS_Cap = 144,
["DebugMode_Enabled"] = true,
-- Array with numerical indices (Error Codes)
ErrorCodes = {401, 404, 500, [4] = 503}
},
-- 2. Resources and Functions
Resources = {
-- Function: Simulates a method to format player names
formatName = function(name) return string.upper(name) end,
-- UserData Simulation (as seen in CE)
CurrentAddressList = AddressList, -- References a CE UserData object
-- Mixed Table (String and Numerical keys)
Materials = {
Wood = 500,
Stone = 250,
["Special Dust"] = 10,
[99] = "Unused slot"
}
},
-- 3. Deeply Nested Player Data
PlayerData = {
["Player_1"] = {
-- Nested Array of Tables
Inventory = {
{id = 101, name = "Health Potion", count = 3},
{id = 205, name = "Magic Scroll", count = 1}
},
Position = {x = 12.5, y = 45.1, z = 0.0},
["isOnline"] = true,
-- String-keyed sub-table
Stats = {
HP = 100,
MP = 50,
Attack = 15
}
},
["Player_2"] = {
Inventory = {}, -- Empty table array
Position = {x = 88.0, y = 12.0}
}
},
-- 4. Circular Reference (for safety testing)
Reference = nil
}
-- Create the circular reference (Table references itself!)
GameConfig_V3.Reference = GameConfig_V3 |
-- ********************************* U S E ***********************************--
| Code: | | deepDumpWithPath(GameConfig_V3, 0, "GameConfig_V3") |
-- ****************************** R E S U L T ********************************--
| Code: | { > GameConfig_V3 < .. (TABLE START)
-["PlayerData"] = [table]
{ > GameConfig_V3.PlayerData < .. (TABLE START)
- - -["Player_2"] = [table]
{ > GameConfig_V3.PlayerData.Player_2 < .. (TABLE START)
- - - - -["Position"] = [table]
{ > GameConfig_V3.PlayerData.Player_2.Position < .. (TABLE START)
- - - - - - -["y"] = 12.0 -- PATH: GameConfig_V3.PlayerData.Player_2.Position.y
- - - - - - -["x"] = 88.0 -- PATH: GameConfig_V3.PlayerData.Player_2.Position.x
} > GameConfig_V3.PlayerData.Player_2.Position < .. (TABLE END)
- - - - -["Inventory"] = [table]
{ > GameConfig_V3.PlayerData.Player_2.Inventory < .. (TABLE START)
} > GameConfig_V3.PlayerData.Player_2.Inventory < .. (TABLE END)
} > GameConfig_V3.PlayerData.Player_2 < .. (TABLE END)
- - -["Player_1"] = [table]
{ > GameConfig_V3.PlayerData.Player_1 < .. (TABLE START)
- - - - -["Stats"] = [table]
{ > GameConfig_V3.PlayerData.Player_1.Stats < .. (TABLE START)
- - - - - - -["HP"] = 100 -- PATH: GameConfig_V3.PlayerData.Player_1.Stats.HP
- - - - - - -["MP"] = 50 -- PATH: GameConfig_V3.PlayerData.Player_1.Stats.MP
- - - - - - -["Attack"] = 15 -- PATH: GameConfig_V3.PlayerData.Player_1.Stats.Attack
} > GameConfig_V3.PlayerData.Player_1.Stats < .. (TABLE END)
- - - - -["isOnline"] = true -- PATH: GameConfig_V3.PlayerData.Player_1.isOnline
- - - - -["Position"] = [table]
{ > GameConfig_V3.PlayerData.Player_1.Position < .. (TABLE START)
- - - - - - -["y"] = 45.1 -- PATH: GameConfig_V3.PlayerData.Player_1.Position.y
- - - - - - -["x"] = 12.5 -- PATH: GameConfig_V3.PlayerData.Player_1.Position.x
- - - - - - -["z"] = 0.0 -- PATH: GameConfig_V3.PlayerData.Player_1.Position.z
} > GameConfig_V3.PlayerData.Player_1.Position < .. (TABLE END)
- - - - -["Inventory"] = [table]
{ > GameConfig_V3.PlayerData.Player_1.Inventory < .. (TABLE START)
- - - - - - -[1] = [table]
{ > GameConfig_V3.PlayerData.Player_1.Inventory[1] < .. (TABLE START)
- - - - - - - - -["name"] = Health Potion -- PATH: GameConfig_V3.PlayerData.Player_1.Inventory[1].name
- - - - - - - - -["id"] = 101 -- PATH: GameConfig_V3.PlayerData.Player_1.Inventory[1].id
- - - - - - - - -["count"] = 3 -- PATH: GameConfig_V3.PlayerData.Player_1.Inventory[1].count
} > GameConfig_V3.PlayerData.Player_1.Inventory[1] < .. (TABLE END)
- - - - - - -[2] = [table]
{ > GameConfig_V3.PlayerData.Player_1.Inventory[2] < .. (TABLE START)
- - - - - - - - -["name"] = Magic Scroll -- PATH: GameConfig_V3.PlayerData.Player_1.Inventory[2].name
- - - - - - - - -["id"] = 205 -- PATH: GameConfig_V3.PlayerData.Player_1.Inventory[2].id
- - - - - - - - -["count"] = 1 -- PATH: GameConfig_V3.PlayerData.Player_1.Inventory[2].count
} > GameConfig_V3.PlayerData.Player_1.Inventory[2] < .. (TABLE END)
} > GameConfig_V3.PlayerData.Player_1.Inventory < .. (TABLE END)
} > GameConfig_V3.PlayerData.Player_1 < .. (TABLE END)
} > GameConfig_V3.PlayerData < .. (TABLE END)
-["GeneralSettings"] = [table]
{ > GameConfig_V3.GeneralSettings < .. (TABLE START)
- - -["DebugMode_Enabled"] = true -- PATH: GameConfig_V3.GeneralSettings.DebugMode_Enabled
- - -["ErrorCodes"] = [table]
{ > GameConfig_V3.GeneralSettings.ErrorCodes < .. (TABLE START)
- - - - -[1] = 401 -- PATH: GameConfig_V3.GeneralSettings.ErrorCodes[1]
- - - - -[2] = 404 -- PATH: GameConfig_V3.GeneralSettings.ErrorCodes[2]
- - - - -[3] = 500 -- PATH: GameConfig_V3.GeneralSettings.ErrorCodes[3]
- - - - -[4] = 503 -- PATH: GameConfig_V3.GeneralSettings.ErrorCodes[4]
} > GameConfig_V3.GeneralSettings.ErrorCodes < .. (TABLE END)
- - -["FPS_Cap"] = 144 -- PATH: GameConfig_V3.GeneralSettings.FPS_Cap
- - -["Resolution"] = 1920x1080 -- PATH: GameConfig_V3.GeneralSettings.Resolution
} > GameConfig_V3.GeneralSettings < .. (TABLE END)
-["Resources"] = [table]
{ > GameConfig_V3.Resources < .. (TABLE START)
- - -["formatName"] = [Function] -- CALL: GameConfig_V3.Resources.formatName()
- - -["Materials"] = [table]
{ > GameConfig_V3.Resources.Materials < .. (TABLE START)
- - - - -["Wood"] = 500 -- PATH: GameConfig_V3.Resources.Materials.Wood
- - - - -["Special Dust"] = 10 -- PATH: GameConfig_V3.Resources.Materials["Special Dust"]
- - - - -[99] = Unused slot -- PATH: GameConfig_V3.Resources.Materials[99]
- - - - -["Stone"] = 250 -- PATH: GameConfig_V3.Resources.Materials.Stone
} > GameConfig_V3.Resources.Materials < .. (TABLE END)
- - -["CurrentAddressList"] = [UserData] -- PATH: GameConfig_V3.Resources.CurrentAddressList
} > GameConfig_V3.Resources < .. (TABLE END)
-["Reference"] = [table]
{GameConfig_V3.Reference = ... CIRCULAR REFERENCE DETECTED ...}
} > GameConfig_V3 < .. (TABLE END) |
Of course, kudos to all the coders who create tables of this size.
Until we meet again with another crazy topic or solution, enjoy!
_________________
|
|