---------------------------------------------------------------- -- serialize.lua -- -- Exports: -- -- orderedPairs : deterministically ordered version of pairs() -- -- serialize : convert Lua value to string in Lua syntax -- ---------------------------------------------------------------- -- orderedPairs: iterate over table elements in deterministic order. First, -- array elements are returned, then remaining elements sorted by the key's -- type and value. -- compare any two Lua values, establishing a complete ordering local function ltAny(a,b) local ta, tb = type(a), type(b) if ta ~= tb then return ta < tb end if ta == "string" or ta == "number" then return a < b end return tostring(a) < tostring(b) end local inext = ipairs{} local function orderedPairs(t) local keys = {} local keyIndex = 1 local counting = true local function _next(seen, s) local v if counting then -- return next array index s, v = inext(t, s) if s ~= nil then seen[s] = true return s,v end counting = false -- construct sorted unseen keys for k,v in pairs(t) do if not seen[k] then table.insert(keys, k) end end table.sort(keys, ltAny) end -- return next unseen table element s = keys[keyIndex] if s ~= nil then keyIndex = keyIndex + 1 v = t[s] end return s, v end return _next, {}, 0 end -- avoid 'nan', 'inf', and '-inf' local numtostring = { [tostring(-1/0)] = "-1/0", [tostring(1/0)] = "1/0", [tostring(0/0)] = "0/0" } setmetatable(numtostring, { __index = function (t, k) return k end }) -- serialize: Serialize a Lua data structure -- -- x = value to serialize -- out = function to be called repeatedly with strings, or -- table into which strings should be inserted, or -- nil => return a string -- iter = function to iterate over table elements, or -- "s" to sort elements by key, or -- nil for default (fastest) -- -- Notes: -- * Does not support self-referential data structures. -- * Does not optimize for repeated sub-expressions. -- * Does not preserve topology; only values. -- * Does not handle types other than nil, number, boolean, string, table -- local function serialize(x, out, iter) local visited = {} local iter = iter=="s" and orderedPairs or iter or pairs assert(type(iter) == "function") local function _serialize(x) if type(x) == "string" then out(string.format("%q", x)) elseif type(x) == "number" then out(numtostring[tostring(x)]) elseif type(x) == "boolean" or type(x) == "nil" then out(tostring(x)) elseif type(x) == "table" then if visited[x] then error("serialize: recursive structure") end visited[x] = true local first, nextIndex = true, 1 out "{" for k,v in iter(x) do if first then first = false else out "," end if k == nextIndex then nextIndex = nextIndex + 1 else if type(k) == "string" and k:match("^[%a_][%w_]*$") then out(k.."=") else out "[" _serialize(k) out "]=" end end _serialize(v) end out "}" visited[x] = false else error("serialize: unsupported type") end end local result if not out then result = {} out = result end if type(out) == "table" then local t = out function out(s) table.insert(t,s) end end _serialize(x) if result then return table.concat(result) end end return { orderedPairs = orderedPairs, serialize = serialize }