1
0
Fork 0
mirror of https://github.com/Reuh/anselme.git synced 2025-10-27 16:49:31 +00:00

Persist LuaFunctions & add tests

This commit is contained in:
Étienne Fildadut 2024-01-03 19:45:36 +01:00
parent a46ac380e8
commit 9a38dfa34f
11 changed files with 108 additions and 19 deletions

View file

@ -75,14 +75,6 @@ LuaFunction = ast.abstract.Runtime(Overloadable) {
to_lua = function(self, state)
return self.func
end,
-- TODO: binser does not serialize lua function upvalues!
_serialize = function(self)
error("LuaFunction can not be serialized")
end,
_deserialize = function(self)
error("LuaFunction can not be serialized")
end
}
package.loaded[...] = LuaFunction

View file

@ -306,6 +306,22 @@ Node = class {
binser.register(self, self.type)
end
end,
-- return a serialized representation of the node
-- can redefine _serialize and _deserialize to customize the serialization, see binser docs
serialize = function(self, state)
package.loaded["anselme.serializer_state"] = state
local r = binser.serialize(self)
package.loaded["anselme.serializer_state"] = nil
return r
end,
-- return the deserialized Node
-- class method
deserialize = function(self, state, str, index)
package.loaded["anselme.serializer_state"] = state
local r = binser.deserializeN(str, 1, index)
package.loaded["anselme.serializer_state"] = nil
return r
end,
__tostring = function(self) return self:format() end,

View file

@ -1,3 +1,5 @@
-- TODO: upstream
-- binser.lua
--[[
@ -39,6 +41,8 @@ local floor = math.floor
local frexp = math.frexp
local unpack = unpack or table.unpack
local huge = math.huge
local getupvalue = debug.getupvalue
local setupvalue = debug.setupvalue
-- Lua 5.3 frexp polyfill
-- From https://github.com/excessive/cpml/blob/master/modules/utils.lua
@ -220,6 +224,7 @@ local function newbinser()
-- RESOURCE = 211
-- INT64 = 212
-- TABLE WITH META = 213
-- FUNCTION WITH UPVALUES = 214
local mts = {}
local ids = {}
@ -342,9 +347,19 @@ local function newbinser()
visited[x] = visited[NEXT]
visited[NEXT] = visited[NEXT] + 1
local str = dump(x)
accum[#accum + 1] = "\210"
accum[#accum + 1] = "\214"
accum[#accum + 1] = number_to_str(#str)
accum[#accum + 1] = str
local upvalues = {}
local i = 1
repeat
local name, value = getupvalue(x, i)
if name and name ~= "_ENV" then
upvalues[name] = value
end
i = i + 1
until not name
types.table(upvalues, visited, accum)
end
end
@ -433,13 +448,26 @@ local function newbinser()
local ret = deserializers[name](unpack(args))
visited[#visited + 1] = ret
return ret, nextindex
elseif t == 210 then
elseif t == 210 or t == 214 then
local length, dataindex = number_from_str(str, index + 1)
local nextindex = dataindex + length
if not (length >= 0) then error("Bad string length") end
if #str < nextindex - 1 then error("Expected more bytes of string") end
local ret = loadstring(sub(str, dataindex, nextindex - 1))
visited[#visited + 1] = ret
if t == 214 then
local upvalues
upvalues, nextindex = deserialize_value(str, nextindex, visited)
if type(upvalues) ~= "table" then error("Expected function upvalues") end
local i = 1
repeat
local name = getupvalue(ret, i)
if name and upvalues[name] ~= nil then
setupvalue(ret, i, upvalues[name])
end
i = i + 1
until not name
end
return ret, nextindex
elseif t == 211 then
local resname, nextindex = deserialize_value(str, index + 1, visited)

View file

@ -12,7 +12,7 @@ local parser = require("anselme.parser")
local binser = require("anselme.lib.binser")
local assert0 = require("anselme.common").assert0
local anselme
local Identifier, Return
local Identifier, Return, Node
local State
State = class {
@ -114,19 +114,15 @@ State = class {
-- This can be loaded back later using `:load`.
save = function(self)
local struct = persistent_manager:get_struct(self)
package.loaded["anselme.serializer_state"] = self
local r = binser.serialize(anselme.versions.save, struct)
package.loaded["anselme.serializer_state"] = nil
return r
return binser.serialize(anselme.versions.save) .. struct:serialize(self)
end,
--- Load a string generated by `:save`.
--
-- Variables that already exist will be overwritten with the loaded data.
load = function(self, save)
package.loaded["anselme.serializer_state"] = self
local version, struct = binser.deserializeN(save, 2)
package.loaded["anselme.serializer_state"] = nil
local version, nextindex = binser.deserializeN(save, 1)
if version ~= anselme.versions.save then print("Loading a save file generated by a different Anselme version, things may break!") end
local struct = Node:deserialize(self, save, nextindex)
for key, val in struct:iter() do
persistent_manager:set(self, key, val)
end
@ -246,6 +242,6 @@ State = class {
package.loaded[...] = State
anselme = require("anselme")
local ast = require("anselme.ast")
Identifier, Return = ast.Identifier, ast.Return
Identifier, Return, Node = ast.Identifier, ast.Return, ast.abstract.Node
return State

View file

@ -0,0 +1,7 @@
--# run #--
--- text ---
| {}"" {}"bar1" {}"" |
--- return ---
()
--# saved #--
{}

View file

@ -0,0 +1,7 @@
--# run #--
--- text ---
| {}"" {}"2" {}"" |
--- return ---
()
--# saved #--
{}

View file

@ -0,0 +1,11 @@
--# run #--
--- text ---
| {}"" {}"43" {}"" |
--- text ---
| {}"" {}"[2, \"\", 5]" {}"" |
--- text ---
| {}"" {}"*{\"f\":\"b\", 1:2, 2:\"\", 3:\"ok\"}" {}"" |
--- return ---
()
--# saved #--
{}

View file

@ -88,6 +88,8 @@ local function run(path, interactive)
run_loop(parallel_state, write_output, interactive)
write_output("--# main script #--")
end)
state:define("serialize", "(value)", function(state, value) return ast.String:new(value:serialize(state)) end, true)
state:define("deserialize", "(str::string)", function(state, str) return ast.abstract.Node:deserialize(state, str.string) end, true)
local run_state = state:branch()

View file

@ -0,0 +1,10 @@
:f = ()
_
:$_+_(a::string, b)
"{a}{b}"
:a=1
f = $(x)
x+a
|{(f!serialize!deserialize)("bar")}

View file

@ -0,0 +1,7 @@
:f = ()
_
:a=1
f = $()
1+a
|{(f!serialize!deserialize)!}

13
test/tests/serialize.ans Normal file
View file

@ -0,0 +1,13 @@
:x = 43
|{x!serialize!deserialize}
:l = [2, "", 5]
|{l!serialize!deserialize}
:t = *{2, "", 5}
t(3) = "ok"
t("f") = "b"
|{t!serialize!deserialize}