From 425decdf7e9284d15aa726e3ae96b9942fb0e3ea Mon Sep 17 00:00:00 2001 From: IronClawTrem Date: Sun, 16 Feb 2020 03:40:06 +0000 Subject: create tremded branch --- scripts/inspect.lua | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 scripts/inspect.lua (limited to 'scripts/inspect.lua') diff --git a/scripts/inspect.lua b/scripts/inspect.lua new file mode 100644 index 0000000..892d91b --- /dev/null +++ b/scripts/inspect.lua @@ -0,0 +1,218 @@ +----------------------------------------------------------------------------------------------------------------------- +-- inspect.lua - v1.1.1 (2011-01) +-- Enrique GarcĂ­a Cota - enrique.garcia.cota [AT] gmail [DOT] com +-- human-readable representations of tables. +-- inspired by http://lua-users.org/wiki/TableSerialization +----------------------------------------------------------------------------------------------------------------------- + +-- Apostrophizes the string if it has quotes, but not aphostrophes +-- Otherwise, it returns a regular 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 +} + +local 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 function getToStringResultSafely(t, mt) + local __tostring = type(mt) == 'table' and mt.__tostring + local string, status + if type(__tostring) == 'function' then + status, string = pcall(__tostring, t) + string = status and string or 'error: ' .. tostring(string) + end + return string +end + +local Inspector = {} + +function Inspector:new(v, depth) + local inspector = { + buffer = {}, + depth = depth, + level = 0, + counters = { + ['function'] = 0, + ['userdata'] = 0, + ['thread'] = 0, + ['table'] = 0 + }, + pools = { + ['function'] = setmetatable({}, {__mode = "kv"}), + ['userdata'] = setmetatable({}, {__mode = "kv"}), + ['thread'] = setmetatable({}, {__mode = "kv"}), + ['table'] = setmetatable({}, {__mode = "kv"}) + } + } + + setmetatable( inspector, { + __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:alreadySeen(t) then + self:puts('') + elseif self.level >= self.depth then + self:puts('{...}') + else + self:puts('<',self:getOrCreateCounter(t),'>{') + self:down() + + local length = #t + local mt = getmetatable(t) + + 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 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(' = '):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:alreadySeen(v) + local tv = type(v) + return self.pools[tv][v] ~= nil +end + +function Inspector:getOrCreateCounter(v) + local tv = type(v) + local current = self.pools[tv][v] + if not current then + current = self.counters[tv] + 1 + self.counters[tv] = current + self.pools[tv][v] = current + end + return current +end + +function Inspector:putValue(v) + local tv = type(v) + + 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:getOrCreateCounter(v),'>') + 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 + -- cgit