]> git.lizzy.rs Git - metalua.git/commitdiff
first version of table source serializer
authorunknown <Fabien@.(none)>
Thu, 18 Dec 2008 12:21:23 +0000 (13:21 +0100)
committerunknown <Fabien@.(none)>
Thu, 18 Dec 2008 12:21:23 +0000 (13:21 +0100)
src/lib/serialize.lua [new file with mode: 0644]

diff --git a/src/lib/serialize.lua b/src/lib/serialize.lua
new file mode 100644 (file)
index 0000000..cc8c45e
--- /dev/null
@@ -0,0 +1,88 @@
+--------------------------------------------------------------------------------
+-- Serialize an object into a source code string. This string, when passed as
+-- an argument to loadstring()(), returns an object structurally identical
+-- to the original one. The following are currently supported:
+-- * strings, numbers, booleans, nil
+-- * functions without upvalues
+-- * tables thereof. Tables cna have shared part, but can't be recursive yet.
+-- Caveat: metatables and environments aren't saved.
+--------------------------------------------------------------------------------
+
+local no_identity = table.transpose { 'number', 'boolean', 'string', 'nil' }
+
+function serialize (x)
+
+   local gensym_max =  0  -- index of the gensym() symbol generator
+   local seen_once  = { } -- element->true set of elements seen exactly once in the table
+   local multiple   = { } -- element->varname set of elements seen more than once
+   local nested     = { } -- transient, set of elements currently being traversed
+
+   local function gensym()
+      gensym_max = gensym_max + 1 ;  return "_" .. gensym_max
+   end
+
+   -----------------------------------------------------------------------------
+   -- First pass, list the tables and functions which appear more than once in x
+   -----------------------------------------------------------------------------
+   local function mark_multiple_occurences (x)
+      if no_identity [type(x)] then return end
+      if     seen_once [x]    then seen_once [x], multiple [x] = nil, gensym()
+      elseif multiple [x]     then -- pass
+      elseif seen_once [x] = true end
+      
+      if type (x) == 'table' then
+         nested [x] = true
+         for k, v in pairs (x) do
+            if nested [k] then error "Can't serialize recursive tables yet" end
+            mark_multiple_occurences (k)
+            mark_multiple_occurences (v)
+         end
+         nested [x] = nil
+      end
+   end
+
+   local dumped    = { } -- multiply occuring values already dumped in localdefs
+   local localdefs = { } -- already dumped local definitions as source code lines
+
+   -----------------------------------------------------------------------------
+   -- Second pass, dump the object; subparts occuring multiple times are dumped
+   -- in local variables which can be referenced multiple times;
+   -- care is taken to dump locla vars in asensible order.
+   -----------------------------------------------------------------------------
+   local function dump_val(x)
+      local  t = type(x)
+      if     x==nil        then return 'nil'
+      elseif t=="number"   then return tostring(x)
+      elseif t=="string"   then return string.format("%q", x)
+      elseif t=="boolean"  then return x and "true" or "false"
+      elseif t=="function" then
+         return string.format ("loadstring(%q,'@serialized')", string.dump (x))
+      elseif t=="table" then
+         local acc = { }
+         for k, v in pairs(x) do
+            --------------------------------------------------------------------
+            -- if x occurs multiple times, dump the local var rather than the
+            -- value. If it's the first time it's dumped, also dump the content
+            -- in localdefs.
+            --------------------------------------------------------------------            
+            local function check_multiple (x)
+               local var = multiple [x]
+               if not var then return dump_val (x) end            -- Occuring only once              
+               if dumped [var] then return var end  -- multiple occ, but already dumped
+               table.insert (localdefs, "local " .. var .. " = " .. dump_val (x)) -- dump
+               dumped [var] = true
+               return var
+            end
+            table.insert (acc, "[" .. check_multiple(k) .. "] = " .. check_multiple(v))
+         end
+         return "{ "..table.concat(acc,"; ").." }"
+      else
+         error ("Can't serialize data of type "..t)
+      end
+   end
+          
+   mark_multiple_occurences (x)
+   local toplevel = dump_val (x)
+   return next (localdefs) and table.concat (localdefs, "\n") .. "\nreturn " .. toplevel
+      or "return "..toplevel
+end