lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


This one is http://www.lua.org/pil/12.1.html generalized to accept
tables as indices as well as values; the save function gives you a
string representation of your table that you can load using loadstring
or loadfile. I don't recall how much testing I did regarding function
environments but they're disabled by default.

On 3/7/10, Andre Leiradella <aleirade@sct.microlink.com.br> wrote:
> This version handles nested tables: http://lua-users.org/wiki/PickleTable
>
> Cheers,
>
> Andre
>
> On 07/03/2010 17:25, Nevin Flanagan wrote:
>> One way to serialize tables in Lua is to write the actual table
>> constructor text into the file. This has the advantage that you can
>> then use Lua itself to load the table from the file contents.
>>
>> This is a simplified version that does not handle nested tables:
>>
>> function table.pickle(t, fileName)
>> local output = io.open(fileName, "w")
>> output:write("return {\n")
>> for k, v in pairs(t) do
>> if (type(k) == 'string' or type(k) == 'number' or type(k) == 'boolean')
>> and (type(v) == 'string' or type(v) == 'number' or type(v) ==
>> 'boolean'') then
>> if type(k) == 'string' then
>> k = string.format("%q", k)
>> else
>> k = tostring(k)
>> end
>> if type(v) == 'string' then
>> v = string.format("%q", v)
>> else
>> v = tostring(v)
>> end
>> output:write(string.format("[%s] = %s,\n", k, v)
>> end
>> end
>> output:close()
>> end
>>
>>
>> NFF
>
>
--
-- serialize.lua
-- 20091024
--
-- supports tables with arbitrary topology, and numbers, strings, booleans
-- and functions (without upvales but with environments) either as keys or 
-- values
--

--require 'extensive testing'

local table = table
local type = type
local tostring = tostring
local pairs = pairs
local string = string
local setmetatable = setmetatable
local getmetatable = getmetatable
local getfenv = getfenv
local _G = _G

-- _export is added to the module to facilitate exporting the (other) functions
-- to the global namespace; one can also choose which ones by providing their
-- names when calling _export
local function export(m)
    m._export = function(...)
        if ... then
            for _,name in ipairs({...}) do
                if m[name] then 
                    _G[name] = m[name]
                else
                    error(name ..' not in '..m._NAME)
                end
            end
        else
            for name, object in pairs(m) do
				if string.sub(name,1,1) ~= '_' then
                    _G[name] = object
                end
            end
        end
    end
end

module('serialize',export)
_VERSION = 'serialize20091024'

local function addString(sb,...)
	table.insert(sb,table.concat({...}))
end

local T
local s
local VISIT
local sb
local count
local useEnvs

local function getNewRef()
	count = count + 1
	return count
end

local function getRef(t)
	local ref = T[t]
	if not ref then
		ref = getNewRef()
		T[t] = ref
	end
	return ref
end


local function saveNonTable(o)
  local o_type = type(o)
  if o_type == "string" then
    sb:add(string.format("%q", o))
  elseif o_type == "function" then
    local env = getfenv(o)
    if env == _G then env = false end
    env = useEnvs and env
    local env_ref
    if env then
        s:put(env)
        env_ref = getRef(env)
        sb:add('setfenv(')
    end
    sb:add('loadstring(',string.format("%q",string.dump(o)),')')
    if env then
        sb:add(',T[',env_ref,'])\n')
    end
  else   -- assume tostring will yield something decent
    sb:add(tostring(o))
  end
end

local function saveTable(input)
	local seen = VISIT[input]
	if seen then return end
	local ref = getRef(input)
	for key, value in pairs(input) do
		sb:add('T[',ref,'][')
		if type(key) == 'table' then
			s:put(key)
			local key_ref = getRef(key)
			sb:add('T[',key_ref,']')
		else
			saveNonTable(key)
		end
		sb:add(']=')
		if type(value) == 'table' then
			s:put(value)
			local value_ref = getRef(value)
			sb:add('T[',value_ref,']')
		else
			saveNonTable(value)
		end
		sb:add('\n')
	end
    local mt = getmetatable(input)
    if mt then
        s:put(mt)
        local mt_ref = getRef(mt)
        sb:add('setmetatable(T[',ref,'],T[',mt_ref,'])\n')
    end
end

function save(t, name, env_flag)
    useEnvs = env_flag
	sb = {add=addString}
	count = 0
	T={}
	s = {
        len = function(self) return #self end;
        put = table.insert;
        get = table.remove;
    }
	VISIT = setmetatable({},
		{__index=function(t,k)
			t[k] = true
			return false
			end})
	local target = name and tostring(name) or 'data'
	sb:add(string.format([=[
%s%s
do
local T = setmetatable({},
{__index=function(t,k)
	local new = {}
	t[k] = new
	return new
end})
%s = T[1]
]=],name and '' or 'local ',target,target))
	s:put(t)
	while(s:len() > 0) do
		saveTable(s:get())
	end
	sb:add('end\n')
	sb:add(string.format('return %s\n',target))
    local ret = table.concat(sb)
	T = nil
	s = nil
	VISIT = nil
	sb = nil
	count = nil
	useEnvs = nil
	return ret
end