function prettyPrint(value) local function helper(val, indent, visited) -- Handle non-tables and non-userdata if type(val) ~= 'table' and type(val) ~= 'userdata' then return tostring(val) end -- Detect cycles (tables or userdata) if visited[val] then return "{...}" end visited[val] = true -- Check if it's iterable (table or userdata with __pairs) local is_iterable = false local iterator_func = nil if type(val) == 'table' then is_iterable = true iterator_func = pairs elseif type(val) == 'userdata' then -- Check for __pairs metamethod (Lua 5.2+) local mt = debug.getmetatable(val) if mt and mt.__pairs then is_iterable = true iterator_func = mt.__pairs(val) end end -- If not iterable, just return tostring if not is_iterable then visited[val] = nil -- Clean up visited return tostring(val) end -- Build key-value pairs local entries = {} local nextIndent = indent + 1 for k, v in iterator_func(val) do local keyStr = helper(k, nextIndent, visited) local valStr = helper(v, nextIndent, visited) local entry = string.rep(" ", nextIndent) .. keyStr .. ": " .. valStr table.insert(entries, entry) end -- Format output local result = "{\n" if #entries > 0 then result = result .. table.concat(entries, ",\n") .. "\n" end result = result .. string.rep(" ", indent) .. "}" visited[val] = nil -- Allow reuse in different branches return result end print(helper(value, 0, {})) end local test = { numbers = { 1, 2, 3 }, nested = { a = "apple", b = { c = "cherry" } }, func = function() end, self = nil } test.self = test prettyPrint(test)