Files
SpiderCut/lib/knife/serialize.lua
2021-12-18 20:10:30 +01:00

79 lines
2.3 KiB
Lua

local tinsert, tconcat, infinity = table.insert, table.concat, math.huge
return function (value)
local intro, outro, ready, known = {}, {}, {}, {}
local knownCount = 0
local writer = {}
-- get writer delegate for this value's type
local function getWriter (value)
return writer[type(value)]
end
-- check if a value has a representation yet
local function isReady (value)
return type(value) ~= 'table' or ready[value]
end
-- serialize tables
function writer.table (value)
if known[value] then
return known[value]
end
knownCount = knownCount + 1
local variable = ('v%i'):format(knownCount)
known[value] = variable
local parts = {}
for k, v in pairs(value) do
local writeKey, writeValue = getWriter(k), getWriter(v)
if writeKey and writeValue then
local key, value = writeKey(k), writeValue(v)
if isReady(k) and isReady(v) then
tinsert(parts, ('[%s]=%s'):format(key, value))
else
tinsert(outro, ('%s[%s]=%s'):format(variable, key, value))
end
end
end
local fields = tconcat(parts, ',')
tinsert(intro, ('local %s={%s}'):format(variable, fields))
ready[value] = true
return variable
end
-- preserve sign bit on NaN, since Lua prints "nan" or "-nan"
local function writeNan (n)
return tostring(n) == tostring(0/0) and '0/0' or '-(0/0)'
end
-- serialize numbers
function writer.number (value)
return value == infinity and '1/0'
or value == -infinity and '-1/0'
or value ~= value and writeNan(value)
or ('%.17G'):format(value)
end
-- serialize strings
function writer.string (value)
return ('%q'):format(value)
end
-- serialize booleans
writer.boolean = tostring
-- concatenate array, joined by and terminated with line break
local function lines (t)
return #t == 0 and '' or tconcat(t, '\n') .. '\n'
end
-- generate serialized result
local write = getWriter(value)
local result = write and write(value) or 'nil'
return lines(intro) .. lines(outro) .. 'return ' .. result
end