made inspect.lua not object-oriented

This commit is contained in:
kikito 2013-09-14 18:12:47 +02:00
parent 7a0dae850d
commit 0de3c69e5d

View File

@ -45,7 +45,7 @@ local controlCharsTranslation = {
local function unescapeChar(c) return controlCharsTranslation[c] end local function unescapeChar(c) return controlCharsTranslation[c] end
local function unescape(str) local function unescape(str)
local result, _ = string.gsub( str, "(%c)", unescapeChar ) local result, _ = string.gsub(str, "(%c)", unescapeChar)
return result return result
end end
@ -84,8 +84,7 @@ local function sortKeys(a, b)
end end
local function getDictionaryKeys(t) local function getDictionaryKeys(t)
local length = #t local keys, length = {}, #t
local keys = {}
for k,_ in pairs(t) do for k,_ in pairs(t) do
if isDictionaryKey(k, length) then table.insert(keys, k) end if isDictionaryKey(k, length) then table.insert(keys, k) end
end end
@ -95,12 +94,12 @@ end
local function getToStringResultSafely(t, mt) local function getToStringResultSafely(t, mt)
local __tostring = type(mt) == 'table' and rawget(mt, '__tostring') local __tostring = type(mt) == 'table' and rawget(mt, '__tostring')
local string, status local str, ok
if type(__tostring) == 'function' then if type(__tostring) == 'function' then
status, string = pcall(__tostring, t) ok, str = pcall(__tostring, t)
string = status and string or 'error: ' .. tostring(string) str = ok and str or 'error: ' .. tostring(str)
end end
return string if type(str) == 'string' and #str > 0 then return str end
end end
local maxIdsMetaTable = { local maxIdsMetaTable = {
@ -118,161 +117,159 @@ local idsMetaTable = {
end end
} }
local Inspector = {} local function countTableAppearances(t, tableAppearances)
tableAppearances = tableAppearances or setmetatable({}, {__mode = "k"})
function Inspector:new(t, depth)
local inspector = {
buffer = {},
depth = depth,
level = 0,
tableAppearances = setmetatable({}, {__mode = "k"}),
maxIds = setmetatable({}, maxIdsMetaTable),
ids = setmetatable({}, idsMetaTable),
}
setmetatable(inspector, {__index = Inspector})
inspector:countTableAppearances(t)
return inspector:putValue(t)
end
function Inspector:countTableAppearances(t)
if type(t) == 'table' then if type(t) == 'table' then
if not self.tableAppearances[t] then if not tableAppearances[t] then
self.tableAppearances[t] = 1 tableAppearances[t] = 1
for k,v in pairs(t) do for k,v in pairs(t) do
self:countTableAppearances(k) countTableAppearances(k, tableAppearances)
self:countTableAppearances(v) countTableAppearances(v, tableAppearances)
end end
self:countTableAppearances(getmetatable(t)) countTableAppearances(getmetatable(t), tableAppearances)
else else
self.tableAppearances[t] = self.tableAppearances[t] + 1 tableAppearances[t] = tableAppearances[t] + 1
end end
end end
return tableAppearances
end end
function Inspector:tabify() -------------------------------------------------------------------
self:puts("\n", string.rep(" ", self.level)) function inspect.dump(rootObject, depth)
return self depth = depth or math.huge
end
function Inspector:up() local buffer = {}
self.level = self.level - 1 local blen = 0 -- buffer length
end local level = 0
local maxIds = setmetatable({}, maxIdsMetaTable)
local ids = setmetatable({}, idsMetaTable)
function Inspector:down() local tableAppearances = countTableAppearances(rootObject)
self.level = self.level + 1
end
function Inspector:puts(...) local function down(f)
local args = {...} level = level + 1
local len = #self.buffer f()
for i=1, #args do level = level - 1
len = len + 1
self.buffer[len] = tostring(args[i])
end end
return self
end
function Inspector:commaControl(needsComma) local function puts(...)
if needsComma then self:puts(',') end local args = {...}
return true for i=1, #args do
end blen = blen + 1
buffer[blen] = tostring(args[i])
function Inspector:putTable(t)
if self:alreadyVisited(t) then
self:puts('<table ', self:getId(t), '>')
elseif self.depth and self.level >= self.depth then
self:puts('{...}')
else
if self.tableAppearances[t] > 1 then
self:puts('<',self:getId(t),'>')
end end
self:puts('{') end
self:down()
local length = #t local function tabify()
local mt = getmetatable(t) puts("\n", string.rep(" ", level))
end
local string = getToStringResultSafely(t, mt) local function commaControl(needsComma)
if type(string) == 'string' and #string > 0 then if needsComma then puts(',') end
self:puts(' -- ', unescape(string)) return true
if length >= 1 then self:tabify() end -- tabify the array values end
end
local needsComma = false local function alreadyVisited(v)
for i=1, length do return ids[type(v)][v] ~= nil
needsComma = self:commaControl(needsComma) end
self:puts(' '):putValue(t[i])
end
local dictKeys = getDictionaryKeys(t) local function getId(v)
local tv = type(v)
for _,k in ipairs(dictKeys) do local id = ids[tv][v]
needsComma = self:commaControl(needsComma) if not id then
self:tabify():putKey(k):puts(' = '):putValue(t[k]) id = maxIds[tv] + 1
end maxIds[tv] = id
ids[tv][v] = id
if mt then
needsComma = self:commaControl(needsComma)
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 end
self:puts('}') return id
end end
return self
end
function Inspector:alreadyVisited(v) local putValue -- forward declaration that needs to go before putTable & putKey
return self.ids[type(v)][v] ~= nil
end
function Inspector:getId(v) local function putKey(k)
local tv = type(v) if isIdentifier(k) then return puts(k) end
local id = self.ids[tv][v] puts( "[" )
if not id then putValue(k)
id = self.maxIds[tv] + 1 puts("]")
self.maxIds[tv] = id
self.ids[tv][v] = id
end end
return id
end
function Inspector:putValue(v) local function putTable(t)
local tv = type(v) if alreadyVisited(t) then
puts('<table ', getId(t), '>')
elseif level >= depth then
puts('{...}')
else
if tableAppearances[t] > 1 then puts('<', getId(t), '>') end
local dictKeys = getDictionaryKeys(t)
local length = #t
local mt = getmetatable(t)
local to_string_result = getToStringResultSafely(t, mt)
puts('{')
down(function()
if to_string_result then
puts(' -- ', unescape(to_string_result))
if length >= 1 then tabify() end -- tabify the array values
end
local needsComma = false
for i=1, length do
needsComma = commaControl(needsComma)
puts(' ')
putValue(t[i])
end
for _,k in ipairs(dictKeys) do
needsComma = commaControl(needsComma)
tabify()
putKey(k)
puts(' = ')
putValue(t[k])
end
if mt then
needsComma = commaControl(needsComma)
tabify()
puts('<metatable> = ')
putValue(mt)
end
end)
if #dictKeys > 0 or mt then -- dictionary table. Justify closing }
tabify()
elseif length > 0 then -- array tables have one extra space before closing }
puts(' ')
end
puts('}')
end
if tv == 'string' then
self:puts(smartQuote(unescape(v)))
elseif tv == 'number' or tv == 'boolean' or tv == 'nil' then
self:puts(tostring(v))
elseif tv == 'table' then
self:putTable(v)
else
self:puts('<',tv,' ',self:getId(v),'>')
end end
return self
-- putvalue is forward-declared before putTable & putKey
putValue = function(v)
local tv = type(v)
if tv == 'string' then
puts(smartQuote(unescape(v)))
elseif tv == 'number' or tv == 'boolean' or tv == 'nil' then
puts(tostring(v))
elseif tv == 'table' then
putTable(v)
else
puts('<',tv,' ',getId(v),'>')
end
end
putValue(rootObject)
return table.concat(buffer)
end end
function Inspector:putKey(k) setmetatable(inspect, { __call = function(_, ...) return inspect.dump(...) end })
if isIdentifier(k) then return self:puts(k) end
return self:puts( "[" ):putValue(k):puts("]")
end
function Inspector:tostring()
return table.concat(self.buffer)
end
setmetatable(inspect, { __call = function(_,t,depth)
return Inspector:new(t, depth):tostring()
end })
return inspect return inspect