home | news | discuss | issues | license LURE: show |
|
---|---|
Recrusively printing table itemsMatias Guijarro wrote:
So first of all I tried to set a new metatable for tables, As Shmuel pointed out, table metatables are per-table, so they don't really work for what you want to do.
A way that will work is to simply replace the tostring function itself. Replace it with a function that calls your table-to-string function for tables, and calls the original tostring for everything else. Roberto's Programming in Lua book explains how to write the table-to-string function. (The first edition is available online: http://www.lua.org/pil/12.1.html.) As it happens, I happened to write an implementation of just the functionality you're looking for a few days ago. It is below, but I encourage you to try whipping together at least a simple version of your own before looking at mine. - Aaron This script makes tostring convert tables to a representation of their contents. |
|
The real tostring: |
local _tostring = tostring
|
Characters that have non-numeric backslash-escaped versions: |
local BsChars = {
["\a"] = "\\a",
["\b"] = "\\b",
["\f"] = "\\f",
["\n"] = "\\n",
["\r"] = "\\r",
["\t"] = "\\t",
["\v"] = "\\v",
["\""] = "\\\"",
["\\"] = "\\\\"}
|
Is Str an "escapeable" character (a non-printing character other than space, a backslash, or a double quote)? |
local function IsEscapeable(Char)
return string.find(Char, "[^%w%p]") -- Non-alphanumeric, non-punct.
and Char ~= " " -- Don't count spaces.
or string.find(Char, '[\\"]') -- A backslash or quote.
end
|
Converts an "escapeable" character (a non-printing character, backslash, or double quote) to its backslash-escaped version; the second argument is used so that numeric character codes can have one or two digits unless three are necessary, which means that the returned value may represent both the character in question and the digit after it: |
local function EscapeableToEscaped(Char, FollowingDigit)
if IsEscapeable(Char) then
local Format = FollowingDigit == "" and "\\%d" or "\\%03d" .. FollowingDigit
return BsChars[Char]
or string.format(Format, string.byte(Char))
else
return Char .. FollowingDigit
end
end
|
Quotes a string in a Lua- and human-readable way. (This is a
replacement for string.format's |
local function StrToStr(Str)
return '"' .. string.gsub(Str, "(.)(%d?)", EscapeableToEscaped) .. '"'
end
|
Quote a string into lua form (including the non-printable characters from 0-31, and from 127-255). |
local function quote(_)
local fmt = string.format
local _ = fmt("%q", _)
_ = _)
_ = string.gsub(_, "\\\n", "\\n")
_ = _ = string.gsub(_, "[%z\1-\31,\127-\255]", function (x)
return fmt("\\%03d",string.byte(x))
end)
return _
end
StrToStr = quote
|
Lua keywords: |
local Keywords = {["and"] = true, ["break"] = true, ["do"] = true,
["else"] = true, ["elseif"] = true, ["end"] = true, ["false"] = true,
["for"] = true, ["function"] = true, ["if"] = true, ["in"] = true,
["local"] = true, ["nil"] = true, ["not"] = true, ["or"] = true,
["repeat"] = true, ["return"] = true, ["then"] = true,
["true"] = true, ["until"] = true, ["while"] = true}
|
Is Str an identifier? |
local function IsIdent(Str)
return not Keywords[Str] and string.find(Str, "^[%a_][%w_]*$")
end
|
Converts a non-table to a Lua- and human-readable string: |
local function ScalarToStr(Val)
local Ret
local Type = type(Val)
if Type == "string" then
Ret = StrToStr(Val)
elseif Type == "function" or Type == "userdata" or Type == "thread" then
Ret = "<" .. __tostring(Val) .. ">"
else
Ret = _tostring(Val)
end
return Ret
end
local function private(str)
return type(str) == "string" and str:sub(1,1) == "_" end
|
Converts a table to a Lua- and human-readable string. |
local function TblToStr(Tbl, Seen)
Seen = Seen or {}
local Ret = {}
if not Seen[Tbl] then
Seen[Tbl] = true
local LastArrayKey = 0
for Key, Val in pairs(Tbl) do
if not private(Key) then
if type(Key) == "table" then
Key = "[" .. TblToStr(Key, Seen) .. "]"
elseif not IsIdent(Key) then
if type(Key) == "number" and Key == LastArrayKey + 1 then
|
Don't mess with Key if it's an array key. |
LastArrayKey = Key
else
Key = "[" .. ScalarToStr(Key) .. "]"
end
end
if type(Val) == "table" then
Val = TblToStr(Val, Seen)
else
Val = ScalarToStr(Val)
end
Ret[#Ret + 1] =
(type(Key) == "string"
and (Key .. "= ") -- Explicit key.
or "") -- Implicit array key.
.. Val
end
end
Ret = "{" .. table.concat(Ret, ", ") .. "}"
else
Ret = "<cycle to " .. __tostring(Tbl) .. ">"
end
return Ret
end
|
A replacement for tostring that prints tables in Lua- and human-readable format: |
function tostring(Val)
return type(Val) == "table"
and TblToStr(Val)
or _tostring(Val)
end
|
A test function: |
local function _tostringTest()
local Fnc = table.sort --function() end
local Tbl = {}
Tbl[Tbl] = Tbl
local Tbls = {
{},
{1, true, 3},
{Foo = "Bar"},
{["*Foo*"] = "Bar"},
{Fnc},
{[{}] = {}},
{1, 2, 3, {4, 5}},
Tbl,
}
local Strs = {
"{}",
"{1, true, 3}",
'{Foo= "Bar"}',
'{["*Foo*"]= "Bar"}',
'{<' .. __tostring(Fnc) .. '>}',
"{[{}]= {}}",
"{1, 2, 3, {4, 5}}",
"{[<cycle to " .. __tostring(Tbl) .. ">]= <cycle to "
.. __tostring(Tbl) .. ">}",
}
for I, Tbl in ipairs(Tbls) do
print(tostring(Tbl))
assert(tostring(Tbl) == Strs[I],
"Assertion failed on " .. __tostring(tostring(Tbl)))
end
end
return {tests=_tostringTest}
|