mirror of
https://github.com/kikito/inspect.lua.git
synced 2024-12-15 14:34:21 +00:00
176 lines
4.4 KiB
Lua
176 lines
4.4 KiB
Lua
-----------------------------------------------------------------------------------------------------------------------
|
|
-- inspect.lua - v1.0 (2011-04)
|
|
-- Enrique García Cota - enrique.garcia.cota [AT] gmail [DOT] com
|
|
-- human-readable representations of tables.
|
|
-- inspired by http://lua-users.org/wiki/TableSerialization
|
|
-----------------------------------------------------------------------------------------------------------------------
|
|
|
|
-- public function
|
|
|
|
-- Apostrophizes the string if it has quotes, but not aphostrophes
|
|
-- Otherwise, it returns regular a requilar quoted string
|
|
local function smartQuote(str)
|
|
if string.match( string.gsub(str,"[^'\"]",""), '^"+$' ) then
|
|
return "'" .. str .. "'"
|
|
end
|
|
return string.format("%q", str )
|
|
end
|
|
|
|
local controlCharsTranslation = {
|
|
["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
|
|
["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v", ["\\"] = "\\\\"
|
|
}
|
|
|
|
local function unescapeChar(c) return controlCharsTranslation[c] end
|
|
|
|
local function unescape(str)
|
|
local result, _ = string.gsub( str, "(%c)", unescapeChar )
|
|
return result
|
|
end
|
|
|
|
local function isIdentifier(str)
|
|
return string.match( str, "^[_%a][_%a%d]*$" )
|
|
end
|
|
|
|
local function isArrayKey(k, length)
|
|
return type(k)=='number' and 1 <= k and k <= length
|
|
end
|
|
|
|
local function isDictionaryKey(k, length)
|
|
return not isArrayKey(k, length)
|
|
end
|
|
|
|
local sortOrdersByType = {
|
|
['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4,
|
|
['function'] = 5, ['userdata'] = 6, ['thread'] = 7
|
|
}
|
|
|
|
function sortKeys(a,b)
|
|
local ta, tb = type(a), type(b)
|
|
if ta ~= tb then return sortOrdersByType[ta] < sortOrdersByType[tb] end
|
|
if ta == 'string' or ta == 'number' then return a < b end
|
|
return false
|
|
end
|
|
|
|
local function getDictionaryKeys(t)
|
|
local length = #t
|
|
local keys = {}
|
|
for k,_ in pairs(t) do
|
|
if isDictionaryKey(k, length) then table.insert(keys,k) end
|
|
end
|
|
table.sort(keys, sortKeys)
|
|
return keys
|
|
end
|
|
|
|
local Inspector = {}
|
|
|
|
function Inspector:new(v, depth)
|
|
local inspector = setmetatable( { buffer = {}, depth = depth, level = 0 }, {
|
|
__index = Inspector,
|
|
__tostring = function(instance) return table.concat(instance.buffer) end
|
|
} )
|
|
return inspector:putValue(v)
|
|
end
|
|
|
|
function Inspector:puts(...)
|
|
local args = {...}
|
|
for i=1, #args do
|
|
table.insert(self.buffer, tostring(args[i]))
|
|
end
|
|
return self
|
|
end
|
|
|
|
function Inspector:tabify()
|
|
self:puts("\n", string.rep(" ", self.level))
|
|
return self
|
|
end
|
|
|
|
function Inspector:up()
|
|
self.level = self.level - 1
|
|
end
|
|
|
|
function Inspector:down()
|
|
self.level = self.level + 1
|
|
end
|
|
|
|
function Inspector:putComma(comma)
|
|
if comma then self:puts(',') end
|
|
return true
|
|
end
|
|
|
|
function Inspector:putTable(t)
|
|
if self.level >= self.depth then
|
|
self:puts('{...}')
|
|
else
|
|
self:puts('{')
|
|
self:down()
|
|
|
|
local length = #t
|
|
local mt = getmetatable(t)
|
|
local __tostring = type(mt) == 'table' and mt.__tostring
|
|
local string = type(__tostring) == 'function' and __tostring(t)
|
|
|
|
if type(string) == 'string' and #string > 0 then
|
|
self:puts(' -- ', unescape(string))
|
|
if length >= 1 then self:tabify() end -- tabify the array values
|
|
end
|
|
|
|
local comma = false
|
|
for i=1, length do
|
|
comma = self:putComma(comma)
|
|
self:puts(' '):putValue(t[i])
|
|
end
|
|
|
|
local dictKeys = getDictionaryKeys(t)
|
|
|
|
for _,k in ipairs(dictKeys) do
|
|
comma = self:putComma(comma)
|
|
self:tabify():putKey(k):puts(' = '):putValue(t[k])
|
|
end
|
|
|
|
if mt then
|
|
comma = self:putComma(comma)
|
|
self:tabify():puts('<metatable> = '):putValue(mt)
|
|
end
|
|
self:up()
|
|
|
|
if #dictKeys > 0 or mt then -- dictionary table. Justify closing }
|
|
self:tabify()
|
|
elseif length > 0 then -- array tables have one extra space before closing }
|
|
self:puts(' ')
|
|
end
|
|
self:puts('}')
|
|
end
|
|
return self
|
|
end
|
|
|
|
function Inspector:putValue(v)
|
|
local tv = type(v)
|
|
|
|
if tv == 'string' then
|
|
self:puts(smartQuote(unescape(v)))
|
|
elseif tv == 'number' or tv == 'boolean' then
|
|
self:puts(tostring(v))
|
|
elseif tv == 'table' then
|
|
self:putTable(v)
|
|
else
|
|
self:puts('<',tv,'>')
|
|
end
|
|
return self
|
|
end
|
|
|
|
function Inspector:putKey(k)
|
|
if type(k) == "string" and isIdentifier(k) then
|
|
return self:puts(k)
|
|
end
|
|
return self:puts( "[" ):putValue(k):puts("]")
|
|
end
|
|
|
|
local function inspect(t, depth)
|
|
depth = depth or 4
|
|
return tostring(Inspector:new(t, depth))
|
|
end
|
|
|
|
return inspect
|
|
|