Just came across this excellent piece of code to help with debugging (https://github.com/kikito/inspect.lua):
This library transforms any Lua value into a human-readable representation. It is especially useful for debugging errors in tables.
The objective here is human understanding (i.e. for debugging), not serialization or compactness.
- Code: Select all
- local inspect ={
 _VERSION = 'inspect.lua 3.0.0',
 _URL = 'http://github.com/kikito/inspect.lua',
 _DESCRIPTION = 'human-readable representations of tables',
 _LICENSE = [[
 MIT LICENSE
 Copyright (c) 2013 Enrique García Cota
 Permission is hereby granted, free of charge, to any person obtaining a
 copy of this software and associated documentation files (the
 "Software"), to deal in the Software without restriction, including
 without limitation the rights to use, copy, modify, merge, publish,
 distribute, sublicense, and/or sell copies of the Software, and to
 permit persons to whom the Software is furnished to do so, subject to
 the following conditions:
 The above copyright notice and this permission notice shall be included
 in all copies or substantial portions of the Software.
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 ]]
 }
 inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end})
 inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end})
 -- Apostrophizes the string if it has quotes, but not aphostrophes
 -- Otherwise, it returns a regular quoted string
 local function smartQuote(str)
 if str:match('"') and not str:match("'") then
 return "'" .. str .. "'"
 end
 return '"' .. str:gsub('"', '\\"') .. '"'
 end
 local controlCharsTranslation = {
 ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
 ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v"
 }
 local function escape(str)
 local result = str:gsub("\\", "\\\\"):gsub("(%c)", controlCharsTranslation)
 return result
 end
 local function isIdentifier(str)
 return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" )
 end
 local function isSequenceKey(k, length)
 return type(k) == 'number'
 and 1 <= k
 and k <= length
 and math.floor(k) == k
 end
 local defaultTypeOrders = {
 ['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)
 -- strings and numbers are sorted numerically/alphabetically
 if ta == tb and (ta == 'string' or ta == 'number') then return a < b end
 local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb]
 -- Two default types are compared according to the defaultTypeOrders table
 if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb]
 elseif dta then return true -- default types before custom ones
 elseif dtb then return false -- custom types after default ones
 end
 -- custom types are sorted out alphabetically
 return ta < tb
 end
 local function getNonSequentialKeys(t)
 local keys, length = {}, #t
 for k,_ in pairs(t) do
 if not isSequenceKey(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 rawget(mt, '__tostring')
 local str, ok
 if type(__tostring) == 'function' then
 ok, str = pcall(__tostring, t)
 str = ok and str or 'error: ' .. tostring(str)
 end
 if type(str) == 'string' and #str > 0 then return str end
 end
 local maxIdsMetaTable = {
 __index = function(self, typeName)
 rawset(self, typeName, 0)
 return 0
 end
 }
 local idsMetaTable = {
 __index = function (self, typeName)
 local col = setmetatable({}, {__mode = "kv"})
 rawset(self, typeName, col)
 return col
 end
 }
 local function countTableAppearances(t, tableAppearances)
 tableAppearances = tableAppearances or setmetatable({}, {__mode = "k"})
 if type(t) == 'table' then
 if not tableAppearances[t] then
 tableAppearances[t] = 1
 for k,v in pairs(t) do
 countTableAppearances(k, tableAppearances)
 countTableAppearances(v, tableAppearances)
 end
 countTableAppearances(getmetatable(t), tableAppearances)
 else
 tableAppearances[t] = tableAppearances[t] + 1
 end
 end
 return tableAppearances
 end
 local copySequence = function(s)
 local copy, len = {}, #s
 for i=1, len do copy[i] = s[i] end
 return copy, len
 end
 local function makePath(path, ...)
 local keys = {...}
 local newPath, len = copySequence(path)
 for i=1, #keys do
 newPath[len + i] = keys[i]
 end
 return newPath
 end
 local function processRecursive(process, item, path)
 if item == nil then return nil end
 local processed = process(item, path)
 if type(processed) == 'table' then
 local processedCopy = {}
 local processedKey
 for k,v in pairs(processed) do
 processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY))
 if processedKey ~= nil then
 processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey))
 end
 end
 local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE))
 setmetatable(processedCopy, mt)
 processed = processedCopy
 end
 return processed
 end
 -------------------------------------------------------------------
 local Inspector = {}
 local Inspector_mt = {__index = Inspector}
 function Inspector:puts(...)
 local args = {...}
 local buffer = self.buffer
 local len = #buffer
 for i=1, #args do
 len = len + 1
 buffer[len] = tostring(args[i])
 end
 end
 function Inspector:down(f)
 self.level = self.level + 1
 f()
 self.level = self.level - 1
 end
 function Inspector:tabify()
 self:puts(self.newline, string.rep(self.indent, self.level))
 end
 function Inspector:alreadyVisited(v)
 return self.ids[type(v)][v] ~= nil
 end
 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
 end
 return id
 end
 function Inspector:putKey(k)
 if isIdentifier(k) then return self:puts(k) end
 self:puts("[")
 self:putValue(k)
 self:puts("]")
 end
 function Inspector:putTable(t)
 if t == inspect.KEY or t == inspect.METATABLE then
 self:puts(tostring(t))
 elseif self:alreadyVisited(t) then
 self:puts('<table ', self:getId(t), '>')
 elseif self.level >= self.depth then
 self:puts('{...}')
 else
 if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end
 local nonSequentialKeys = getNonSequentialKeys(t)
 local length = #t
 local mt = getmetatable(t)
 local toStringResult = getToStringResultSafely(t, mt)
 self:puts('{')
 self:down(function()
 if toStringResult then
 self:puts(' -- ', escape(toStringResult))
 if length >= 1 then self:tabify() end
 end
 local count = 0
 for i=1, length do
 if count > 0 then self:puts(',') end
 self:puts(' ')
 self:putValue(t[i])
 count = count + 1
 end
 for _,k in ipairs(nonSequentialKeys) do
 if count > 0 then self:puts(',') end
 self:tabify()
 self:putKey(k)
 self:puts(' = ')
 self:putValue(t[k])
 count = count + 1
 end
 if mt then
 if count > 0 then self:puts(',') end
 self:tabify()
 self:puts('<metatable> = ')
 self:putValue(mt)
 end
 end)
 if #nonSequentialKeys > 0 or mt then -- result is multi-lined. Justify closing }
 self:tabify()
 elseif length > 0 then -- array tables have one extra space before closing }
 self:puts(' ')
 end
 self:puts('}')
 end
 end
 function Inspector:putValue(v)
 local tv = type(v)
 if tv == 'string' then
 self:puts(smartQuote(escape(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
 -------------------------------------------------------------------
 function inspect.inspect(root, options)
 options = options or {}
 local depth = options.depth or math.huge
 local newline = options.newline or '\n'
 local indent = options.indent or ' '
 local process = options.process
 if process then
 root = processRecursive(process, root, {})
 end
 local inspector = setmetatable({
 depth = depth,
 buffer = {},
 level = 0,
 ids = setmetatable({}, idsMetaTable),
 maxIds = setmetatable({}, maxIdsMetaTable),
 newline = newline,
 indent = indent,
 tableAppearances = countTableAppearances(root)
 }, Inspector_mt)
 inspector:putValue(root)
 return table.concat(inspector.buffer)
 end
 setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end })
 return inspect
Basically, you just save it as "inspect.lua", add it to your Scripts folder, and then use it in your own Scripts as follows:
- Code: Select all
- local inspect = require 'inspect';
 function someFunc ( ) -- This can be any Function
 myTable = { "WHATEVER" }; -- This can be any Table, or even any Lua Value
 local sTmp = inspect ( myTable );
 sky.lprint ( sTmp );
 end
When run against a Table, it will display the contents of the Table something like this:
- Code: Select all
- iCount = 6
 {
 ["1"] = {
 id = "0",
 values = {
 typ = "Dummy",
 val = "0"
 }
 },
 ["2"] = {
 id = "4",
 values = {
 typ = "team",
 val = "2"
 }
 },
 ["3"] = {
 values = {
 typ = "target"
 }
 },
 ["4"] = {
 values = {
 typ = "targetid",
 val = "-1"
 }
 },
 ["5"] = {
 values = {
 typ = "team",
 val = "1"
 }
 },
 ["6"] = {
 id = "6",
 values = {
 typ = "team",
 val = "2"
 }
 }
 }
Pretty neat
 
 Just remember that this is for DEBUGGING ONLY, and should be removed from your final Scripts as it WILL NOT work in an end Game (since the "require" call won't work in an end Game).
Regards
Shando
 
				




