From 0de3c69e5d65e12e15a0bd5fd4af92f174fa8216 Mon Sep 17 00:00:00 2001 From: kikito Date: Sat, 14 Sep 2013 18:12:47 +0200 Subject: [PATCH] made inspect.lua not object-oriented --- inspect.lua | 263 ++++++++++++++++++++++++++-------------------------- 1 file changed, 130 insertions(+), 133 deletions(-) diff --git a/inspect.lua b/inspect.lua index 335f3d5..1040061 100644 --- a/inspect.lua +++ b/inspect.lua @@ -45,7 +45,7 @@ local controlCharsTranslation = { local function unescapeChar(c) return controlCharsTranslation[c] end local function unescape(str) - local result, _ = string.gsub( str, "(%c)", unescapeChar ) + local result, _ = string.gsub(str, "(%c)", unescapeChar) return result end @@ -84,8 +84,7 @@ local function sortKeys(a, b) end local function getDictionaryKeys(t) - local length = #t - local keys = {} + local keys, length = {}, #t for k,_ in pairs(t) do if isDictionaryKey(k, length) then table.insert(keys, k) end end @@ -95,12 +94,12 @@ end local function getToStringResultSafely(t, mt) local __tostring = type(mt) == 'table' and rawget(mt, '__tostring') - local string, status + local str, ok if type(__tostring) == 'function' then - status, string = pcall(__tostring, t) - string = status and string or 'error: ' .. tostring(string) + ok, str = pcall(__tostring, t) + str = ok and str or 'error: ' .. tostring(str) end - return string + if type(str) == 'string' and #str > 0 then return str end end local maxIdsMetaTable = { @@ -118,161 +117,159 @@ local idsMetaTable = { 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 not self.tableAppearances[t] then - self.tableAppearances[t] = 1 + if not tableAppearances[t] then + tableAppearances[t] = 1 for k,v in pairs(t) do - self:countTableAppearances(k) - self:countTableAppearances(v) + countTableAppearances(k, tableAppearances) + countTableAppearances(v, tableAppearances) end - self:countTableAppearances(getmetatable(t)) + countTableAppearances(getmetatable(t), tableAppearances) else - self.tableAppearances[t] = self.tableAppearances[t] + 1 + tableAppearances[t] = tableAppearances[t] + 1 end end + + return tableAppearances end -function Inspector:tabify() - self:puts("\n", string.rep(" ", self.level)) - return self -end +------------------------------------------------------------------- +function inspect.dump(rootObject, depth) + depth = depth or math.huge -function Inspector:up() - self.level = self.level - 1 -end + local buffer = {} + local blen = 0 -- buffer length + local level = 0 + local maxIds = setmetatable({}, maxIdsMetaTable) + local ids = setmetatable({}, idsMetaTable) -function Inspector:down() - self.level = self.level + 1 -end + local tableAppearances = countTableAppearances(rootObject) -function Inspector:puts(...) - local args = {...} - local len = #self.buffer - for i=1, #args do - len = len + 1 - self.buffer[len] = tostring(args[i]) + local function down(f) + level = level + 1 + f() + level = level - 1 end - return self -end -function Inspector:commaControl(needsComma) - if needsComma then self:puts(',') end - return true -end - -function Inspector:putTable(t) - if self:alreadyVisited(t) then - self:puts('') - elseif self.depth and self.level >= self.depth then - self:puts('{...}') - else - if self.tableAppearances[t] > 1 then - self:puts('<',self:getId(t),'>') + local function puts(...) + local args = {...} + for i=1, #args do + blen = blen + 1 + buffer[blen] = tostring(args[i]) end - self:puts('{') - self:down() + end - local length = #t - local mt = getmetatable(t) + local function tabify() + puts("\n", string.rep(" ", level)) + end - local string = getToStringResultSafely(t, mt) - 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 function commaControl(needsComma) + if needsComma then puts(',') end + return true + end - local needsComma = false - for i=1, length do - needsComma = self:commaControl(needsComma) - self:puts(' '):putValue(t[i]) - end + local function alreadyVisited(v) + return ids[type(v)][v] ~= nil + end - local dictKeys = getDictionaryKeys(t) - - for _,k in ipairs(dictKeys) do - needsComma = self:commaControl(needsComma) - self:tabify():putKey(k):puts(' = '):putValue(t[k]) - end - - if mt then - needsComma = self:commaControl(needsComma) - self:tabify():puts(' = '):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(' ') + local function getId(v) + local tv = type(v) + local id = ids[tv][v] + if not id then + id = maxIds[tv] + 1 + maxIds[tv] = id + ids[tv][v] = id end - self:puts('}') + return id end - return self -end -function Inspector:alreadyVisited(v) - return self.ids[type(v)][v] ~= nil -end + local putValue -- forward declaration that needs to go before putTable & putKey -function Inspector:getId(v) - local tv = type(v) - local id = self.ids[tv][v] - if not id then - id = self.maxIds[tv] + 1 - self.maxIds[tv] = id - self.ids[tv][v] = id + local function putKey(k) + if isIdentifier(k) then return puts(k) end + puts( "[" ) + putValue(k) + puts("]") end - return id -end -function Inspector:putValue(v) - local tv = type(v) + local function putTable(t) + if alreadyVisited(t) then + puts('
') + 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(' = ') + 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 - 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 -function Inspector:putKey(k) - 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 }) +setmetatable(inspect, { __call = function(_, ...) return inspect.dump(...) end }) return inspect