2015-05-08 08:43:06 +00:00
|
|
|
local pairs, ipairs, tostring, type, concat, dump, floor, format = pairs, ipairs, tostring, type, table.concat, string.dump, math.floor, string.format
|
2013-11-03 21:50:05 +00:00
|
|
|
|
|
|
|
local function getchr(c)
|
|
|
|
return "\\" .. c:byte()
|
|
|
|
end
|
|
|
|
|
|
|
|
local function make_safe(text)
|
|
|
|
return ("%q"):format(text):gsub('\n', 'n'):gsub("[\128-\255]", getchr)
|
|
|
|
end
|
|
|
|
|
2014-01-26 09:54:43 +00:00
|
|
|
local oddvals = {[tostring(1/0)] = '1/0', [tostring(-1/0)] = '-1/0', [tostring(0/0)] = '0/0'}
|
2013-11-03 21:50:05 +00:00
|
|
|
local function write(t, memo, rev_memo)
|
|
|
|
local ty = type(t)
|
2015-05-08 08:43:06 +00:00
|
|
|
if ty == 'number' then
|
|
|
|
t = format("%.17g", t)
|
2013-11-03 21:50:05 +00:00
|
|
|
return oddvals[t] or t
|
2015-05-20 00:22:35 +00:00
|
|
|
elseif ty == 'boolean' or ty == 'nil' then
|
2015-05-08 08:43:06 +00:00
|
|
|
return tostring(t)
|
2013-11-03 21:50:05 +00:00
|
|
|
elseif ty == 'string' then
|
|
|
|
return make_safe(t)
|
|
|
|
elseif ty == 'table' or ty == 'function' then
|
|
|
|
if not memo[t] then
|
|
|
|
local index = #rev_memo + 1
|
|
|
|
memo[t] = index
|
|
|
|
rev_memo[index] = t
|
|
|
|
end
|
2014-12-23 22:56:46 +00:00
|
|
|
return '_[' .. memo[t] .. ']'
|
2013-11-03 21:50:05 +00:00
|
|
|
else
|
|
|
|
error("Trying to serialize unsupported type " .. ty)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local kw = {['and'] = true, ['break'] = true, ['do'] = true, ['else'] = true,
|
|
|
|
['elseif'] = true, ['end'] = true, ['false'] = true, ['for'] = true,
|
|
|
|
['function'] = true, ['goto'] = true, ['if'] = true, ['in'] = true,
|
|
|
|
['local'] = true, ['nil'] = true, ['not'] = true, ['or'] = true,
|
|
|
|
['repeat'] = true, ['return'] = true, ['then'] = true, ['true'] = true,
|
|
|
|
['until'] = true, ['while'] = true}
|
|
|
|
local function write_key_value_pair(k, v, memo, rev_memo, name)
|
|
|
|
if type(k) == 'string' and k:match '^[_%a][_%w]*$' and not kw[k] then
|
2015-05-20 00:13:34 +00:00
|
|
|
return (name and name .. '.' or '') .. k ..'=' .. write(v, memo, rev_memo)
|
2013-11-03 21:50:05 +00:00
|
|
|
else
|
2015-05-20 00:13:34 +00:00
|
|
|
return (name or '') .. '[' .. write(k, memo, rev_memo) .. ']=' .. write(v, memo, rev_memo)
|
2013-11-03 21:50:05 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- fun fact: this function is not perfect
|
|
|
|
-- it has a few false positives sometimes
|
|
|
|
-- but no false negatives, so that's good
|
|
|
|
local function is_cyclic(memo, sub, super)
|
|
|
|
local m = memo[sub]
|
|
|
|
local p = memo[super]
|
|
|
|
return m and p and m < p
|
|
|
|
end
|
|
|
|
|
|
|
|
local function write_table_ex(t, memo, rev_memo, srefs, name)
|
|
|
|
if type(t) == 'function' then
|
2015-05-20 00:13:34 +00:00
|
|
|
return '_[' .. name .. ']=loadstring' .. make_safe(dump(t))
|
2013-11-03 21:50:05 +00:00
|
|
|
end
|
2015-05-25 12:15:58 +00:00
|
|
|
local m = {}
|
|
|
|
local mi = 1
|
2013-11-03 21:50:05 +00:00
|
|
|
for i = 1, #t do -- don't use ipairs here, we need the gaps
|
|
|
|
local v = t[i]
|
|
|
|
if v == t or is_cyclic(memo, v, t) then
|
|
|
|
srefs[#srefs + 1] = {name, i, v}
|
2015-05-25 12:15:58 +00:00
|
|
|
m[mi] = 'nil'
|
2013-11-03 21:50:05 +00:00
|
|
|
mi = mi + 1
|
|
|
|
else
|
2015-05-25 12:15:58 +00:00
|
|
|
m[mi] = write(v, memo, rev_memo)
|
|
|
|
mi = mi + 1
|
2013-11-03 21:50:05 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
for k,v in pairs(t) do
|
|
|
|
if type(k) ~= 'number' or floor(k) ~= k or k < 1 or k > #t then
|
|
|
|
if v == t or k == t or is_cyclic(memo, v, t) or is_cyclic(memo, k, t) then
|
|
|
|
srefs[#srefs + 1] = {name, k, v}
|
|
|
|
else
|
2015-05-25 12:15:58 +00:00
|
|
|
m[mi] = write_key_value_pair(k, v, memo, rev_memo)
|
|
|
|
mi = mi + 1
|
2013-11-03 21:50:05 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2015-05-25 12:15:58 +00:00
|
|
|
return '_[' .. name .. ']={' .. concat(m, ',') .. '}'
|
2013-11-03 21:50:05 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
return function(t)
|
|
|
|
local memo = {[t] = 0}
|
|
|
|
local rev_memo = {[0] = t}
|
|
|
|
local srefs = {}
|
|
|
|
local result = {}
|
|
|
|
|
|
|
|
-- phase 1: recursively descend the table structure
|
|
|
|
local n = 0
|
|
|
|
while rev_memo[n] do
|
|
|
|
result[n + 1] = write_table_ex(rev_memo[n], memo, rev_memo, srefs, n)
|
|
|
|
n = n + 1
|
|
|
|
end
|
|
|
|
|
|
|
|
-- phase 2: reverse order
|
|
|
|
for i = 1, n*.5 do
|
|
|
|
local j = n - i + 1
|
|
|
|
result[i], result[j] = result[j], result[i]
|
|
|
|
end
|
|
|
|
|
|
|
|
-- phase 3: add all the tricky cyclic stuff
|
|
|
|
for i, v in ipairs(srefs) do
|
|
|
|
n = n + 1
|
2014-12-23 22:56:46 +00:00
|
|
|
result[n] = write_key_value_pair(v[2], v[3], memo, rev_memo, '_[' .. v[1] .. ']')
|
2013-11-03 21:50:05 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- phase 4: add something about returning the main table
|
2015-05-20 00:13:34 +00:00
|
|
|
if result[n]:sub(1, 5) == '_[0]=' then
|
|
|
|
result[n] = 'return ' .. result[n]:sub(6)
|
2013-11-03 21:50:05 +00:00
|
|
|
else
|
2014-12-23 22:56:46 +00:00
|
|
|
result[n + 1] = 'return _[0]'
|
2013-11-03 21:50:05 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- phase 5: just concatenate everything
|
2014-12-23 22:56:46 +00:00
|
|
|
result = concat(result, '\n')
|
2015-05-20 00:13:34 +00:00
|
|
|
return n > 1 and 'local _={}\n' .. result or result
|
2013-11-03 21:50:05 +00:00
|
|
|
end
|