Skip to content

Instantly share code, notes, and snippets.

@hoelzro
Created November 17, 2011 16:13
Show Gist options
  • Save hoelzro/1373552 to your computer and use it in GitHub Desktop.
Save hoelzro/1373552 to your computer and use it in GitHub Desktop.
Pure Lua persistence of functions with upvalues
local format = string.format
local tjoin = table.concat
local tostring = tostring
local type = type
local floor = math.floor
local pairs = pairs
local ipairs = ipairs
local error = error
module 'pretty'
local keywords = {
['and'] = true,
['break'] = true,
['do'] = true,
['else'] = true,
['elseif'] = true,
['end'] = true,
['false'] = true,
['for'] = true,
['function'] = 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 isinteger(n)
return type(n) == 'number' and floor(n) == n
end
local function isident(s)
return type(s) == 'string' and not keywords[s] and s:match('^[a-zA-Z_][a-zA-Z0-9_]*$')
end
local function helper(v, chunks, indent)
local t = type(v)
if t == 'nil' or t == 'boolean' or t == 'number' then
chunks[#chunks + 1] = tostring(v)
elseif t == 'string' then
chunks[#chunks + 1] = format('%q', v)
elseif t == 'table' then
local lastintkey = 0
chunks[#chunks + 1] = '{\n'
for i, v in ipairs(v) do
for j = 1, indent do
chunks[#chunks + 1] = ' '
end
helper(v, chunks, indent + 1)
chunks[#chunks + 1] = ',\n'
lastintkey = i
end
for k, v in pairs(v) do
if not (isinteger(k) and k <= lastintkey and k > 0) then
for j = 1, indent do
chunks[#chunks + 1] = ' '
end
if isident(k) then
chunks[#chunks + 1] = k
else
chunks[#chunks + 1] = '['
helper(k, chunks, indent + 1)
chunks[#chunks + 1] = ']'
end
chunks[#chunks + 1] = ' = '
helper(v, chunks, indent + 1)
chunks[#chunks + 1] = ',\n'
end
end
for j = 1, indent - 1 do
chunks[#chunks + 1] = ' '
end
chunks[#chunks + 1] = '}'
else
chunks[#chunks + 1] = '"' .. tostring(v) .. '"'
end
end
-- Data::Dumper-like options
-- persist multi-line strings with [[ ]]?
function print(v, chunks, indent)
local chunks = {}
helper(v, chunks, 1)
return tjoin(chunks, '')
end
local pretty = require 'pretty'
local function getcounter()
local count = 0
return function()
count = count + 1
return count
end
end
local function getupvalues(f)
local upvalues = {}
local which = 1
local name, value
repeat
name, value = debug.getupvalue(f, which)
which = which + 1
if name then
upvalues[name] = value
end
until not name
return upvalues
end
local function dumpfunction(f)
local upvalues = pretty.print(getupvalues(f))
return string.format('%5d%s%s', #upvalues, upvalues, string.dump(f))
end
local counter = getcounter()
for i = 1, 10 do
counter()
end
local f = assert(io.open('lua-function.out', 'w'))
f:write(dumpfunction(counter))
f:close()
local f = assert(io.open('lua-function.out', 'r'))
local chunk = f:read '*a'
f:close()
local len = chunk:sub(1, 5)
len = len:gsub('^%s+', '')
len = tonumber(len)
local upvalues = chunk:sub(6, 6 + len - 1)
upvalues = assert(loadstring('return ' .. upvalues))()
local func = chunk:sub(6 + len)
func = assert(loadstring(func))
local name
local which = 1
repeat
name = debug.setupvalue(func, which, nil)
if name then
debug.setupvalue(func, which, upvalues[name])
end
which = which + 1
until not name
print(func())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment