1 --------------------------------------------------------------------------------
2 -- Serialize an object into a source code string. This string, when passed as
3 -- an argument to loadstring()(), returns an object structurally identical
4 -- to the original one. The following are currently supported:
5 -- * strings, numbers, booleans, nil
6 -- * functions without upvalues
7 -- * tables thereof. Tables can have shared part, but can't be recursive yet.
8 -- Caveat: metatables and environments aren't saved.
9 --------------------------------------------------------------------------------
11 local no_identity = { number=1, boolean=1, string=1, nil=1 }
13 function serialize (x)
15 local gensym_max = 0 -- index of the gensym() symbol generator
16 local seen_once = { } -- element->true set of elements seen exactly once in the table
17 local multiple = { } -- element->varname set of elements seen more than once
18 local nested = { } -- transient, set of elements currently being traversed
20 local function gensym()
21 gensym_max = gensym_max + 1 ; return gensym_max
24 -----------------------------------------------------------------------------
25 -- First pass, list the tables and functions which appear more than once in x
26 -----------------------------------------------------------------------------
27 local function mark_multiple_occurences (x)
28 if no_identity [type(x)] then return end
29 if seen_once [x] then seen_once [x], multiple [x] = nil, true
30 elseif multiple [x] then -- pass
31 else seen_once [x] = true end
33 if type (x) == 'table' then
35 for k, v in pairs (x) do
36 if nested [k] or nexted [v]
37 then error "Can't serialize recursive tables yet" end
38 mark_multiple_occurences (k)
39 mark_multiple_occurences (v)
45 local dumped = { } -- multiply occuring values already dumped in localdefs
46 local localdefs = { } -- already dumped local definitions as source code lines
48 -----------------------------------------------------------------------------
49 -- Second pass, dump the object; subparts occuring multiple times are dumped
50 -- in local variables which can be referenced multiple times;
51 -- care is taken to dump locla vars in asensible order.
52 -----------------------------------------------------------------------------
53 local function dump_val(x)
55 if x==nil then return 'nil'
56 elseif t=="number" then return tostring(x)
57 elseif t=="string" then return string.format("%q", x)
58 elseif t=="boolean" then return x and "true" or "false"
59 elseif t=="function" then
60 return string.format ("loadstring(%q,'@serialized')", string.dump (x))
61 elseif t=="table" then
63 --------------------------------------------------------------------
64 -- if x occurs multiple times, dump the local var rather than the
65 -- value. If it's the first time it's dumped, also dump the content
67 --------------------------------------------------------------------
68 local function check_multiple (x)
69 if not multiple[x] then return dump_val (x) end
70 local var = dumped [x]
71 if var then return "_[" .. var .. "]" end
72 local val = dump_val(x)
74 table.insert(localdefs, "_["..var.."]="..val)
76 return "_[" .. var .. "]"
79 local idx_dumped = { }
80 for i, v in ipairs(x) do
81 table.insert (acc, check_multiple(v))
84 for k, v in pairs(x) do
85 if not idx_dumped[k] then
86 table.insert (acc, "[" .. check_multiple(k) .. "] = " .. check_multiple(v))
89 return "{ "..table.concat(acc,", ").." }"
91 error ("Can't serialize data of type "..t)
95 mark_multiple_occurences (x)
96 local toplevel = dump_val (x)
97 if next (localdefs) then
98 return "local _={ }\n" ..
99 table.concat (localdefs, "\n") ..
100 "\nreturn " .. toplevel
102 return "return " .. toplevel