local format, to_lua, from_lua, events, anselme, escape, hash, mark_constant, update_hashes, get_variable, find_function_variant_from_fqm, post_process_text local types = {} types.lua = { ["nil"] = { to_anselme = function(val) return { type = "nil", value = nil } end }, boolean = { to_anselme = function(val) return { type = "number", value = val and 1 or 0 } end }, number = { to_anselme = function(val) return { type = "number", value = val } end }, string = { to_anselme = function(val) return { type = "string", value = val } end }, table = { to_anselme = function(val) local is_map = false local l = {} local m = {} for _, v in ipairs(val) do local r, e = from_lua(v) if not r then return r, e end table.insert(l, r) end for k, v in pairs(val) do if not l[k] then is_map = true local kv, ke = from_lua(k) if not k then return k, ke end local vv, ve = from_lua(v) if not v then return v, ve end local h, err = hash(kv) if not h then return nil, err end m[h] = { kv, vv } end end if is_map then for i, v in ipairs(l) do local key = { type = "number", value = i } local h, err = hash(key) if not h then return nil, err end m[h] = { key, v } end return { type = "map", value = m } else return { type = "list", value = l } end end } } types.anselme = { ["nil"] = { format = function() return "" end, to_lua = function() return nil end, hash = function() return "nil()" end, mark_constant = function() end, }, number = { format = function(val) return tostring(val) end, to_lua = function(val) return val end, hash = function(val) return ("n(%s)"):format(val) end, mark_constant = function() end, }, string = { format = function(val) return tostring(val) end, to_lua = function(val) return val end, hash = function(val) return ("s(%s)"):format(val) end, mark_constant = function() end, }, pair = { format = function(val) local k, ke = format(val[1]) if not k then return k, ke end local v, ve = format(val[2]) if not v then return v, ve end return ("%s=%s"):format(k, v) end, to_lua = function(val, state) local k, ke = to_lua(val[1], state) if ke then return nil, ke end local v, ve = to_lua(val[2], state) if ve then return nil, ve end return { [k] = v } end, hash = function(val) local k, ke = hash(val[1]) if not k then return k, ke end local v, ve = hash(val[2]) if not v then return v, ve end return ("p(%s=%s)"):format(k, v) end, mark_constant = function(v) mark_constant(v.value[1]) mark_constant(v.value[2]) end, }, annotated = { format = function(val) local k, ke = format(val[1]) if not k then return k, ke end local v, ve = format(val[2]) if not v then return v, ve end return ("%s::%s"):format(k, v) end, to_lua = function(val, state) local k, ke = to_lua(val[1], state) if ke then return nil, ke end return k end, hash = function(val) local k, ke = hash(val[1]) if not k then return k, ke end local v, ve = hash(val[2]) if not v then return v, ve end return ("a(%s::%s)"):format(k, v) end, mark_constant = function(v) mark_constant(v.value[1]) mark_constant(v.value[2]) end, }, list = { mutable = true, format = function(val) local l = {} for _, v in ipairs(val) do local s, e = format(v) if not s then return s, e end table.insert(l, s) end return ("[%s]"):format(table.concat(l, ", ")) end, to_lua = function(val, state) local l = {} for _, v in ipairs(val) do local s, e = to_lua(v, state) if e then return nil, e end table.insert(l, s) end return l end, hash = function(val) local l = {} for _, v in ipairs(val) do local s, e = hash(v) if not s then return s, e end table.insert(l, s) end return ("l(%s)"):format(table.concat(l, ",")) end, mark_constant = function(v) v.constant = true for _, item in ipairs(v.value) do mark_constant(item) end end }, map = { mutable = true, format = function(val) local l = {} for _, v in pairs(val) do local ks, ke = format(v[1]) if not ks then return ks, ke end local vs, ve = format(v[2]) if not vs then return vs, ve end table.insert(l, ("%s=%s"):format(ks, vs)) end table.sort(l) return ("{%s}"):format(table.concat(l, ", ")) end, to_lua = function(val, state) local l = {} for _, v in pairs(val) do local kl, ke = to_lua(v[1], state) if ke then return nil, ke end local xl, xe = to_lua(v[2], state) if xe then return nil, xe end l[kl] = xl end return l end, hash = function(val) local l = {} for _, v in pairs(val) do local ks, ke = hash(v[1]) if not ks then return ks, ke end local vs, ve = hash(v[2]) if not vs then return vs, ve end table.insert(l, ("%s=%s"):format(ks, vs)) end table.sort(l) return ("m(%s)"):format(table.concat(l, ",")) end, mark_constant = function(v) v.constant = true for _, val in pairs(v.value) do mark_constant(val[1]) mark_constant(val[2]) end update_hashes(v) end, }, object = { mutable = true, format = function(val) local attributes = {} for name, v in pairs(val.attributes) do table.insert(attributes, ("%s=%s"):format(name:gsub("^"..escape(val.class)..".", ""), format(v))) end if #attributes > 0 then table.sort(attributes) return ("%%%s(%s)"):format(val.class, table.concat(attributes, ", ")) else return ("%%%s"):format(val.class) end end, to_lua = function(val, state) local r = {} local namespacePattern = "^"..escape(val.class).."%." -- set object properties for name, v in pairs(val.attributes) do local var, err = to_lua(v, state) if err then return nil, err end r[name:gsub(namespacePattern, "")] = var end -- set class properties local class, err = find_function_variant_from_fqm(val.class, state, nil) if not class then return nil, err end assert(#class == 1 and class[1].subtype == "class") class = class[1] for _, prop in ipairs(class.properties) do if not val.attributes[prop] then local var var, err = get_variable(state, prop) if not var then return nil, err end var, err = to_lua(var, state) if err then return nil, err end r[prop:gsub(namespacePattern, "")] = var end end return r end, hash = function(val) local attributes = {} for name, v in pairs(val.attributes) do table.insert(attributes, ("%s=%s"):format(name:gsub("^"..escape(val.class)..".", ""), format(v))) end table.sort(attributes) return ("%%(%s;%s)"):format(val.class, table.concat(attributes, ",")) end, mark_constant = function(v) v.constant = true end, }, ["function reference"] = { format = function(val) if #val > 1 then return ("&(%s)"):format(table.concat(val, ", ")) else return ("&%s"):format(table.concat(val, ", ")) end end, to_lua = nil, hash = function(val) return ("&f(%s)"):format(table.concat(val, ", ")) end, mark_constant = function() end, }, ["variable reference"] = { format = function(val) return ("&%s"):format(val) end, to_lua = nil, hash = function(val) return ("&v(%s)"):format(val) end, mark_constant = function() end, }, -- event buffer: can only be used outside of Anselme internal for text & flush events (through text buffers) ["event buffer"] = { format = function(val) -- triggered from subtexts local v, e = events:write_buffer(anselme.running.state, val) if not v then return v, e end return "" end, to_lua = function(val, state) local r = {} for _, event in ipairs(val) do if event.type == "text" then table.insert(r, { "text", post_process_text(state, event.value) }) elseif event.type == "flush" then table.insert(r, { "flush" }) else return nil, ("event %q in event buffer can't be converted to a Lua value"):format(event.type) end end return r end, hash = function(val) local l = {} for _, event in ipairs(val) do if event.type == "text" then local text = {} for _, t in ipairs(event.value) do local str = ("s(%s)"):format(t.text) local tags, e = hash(t.tags) if not tags then return nil, e end table.insert(text, ("%s#%s"):format(str, tags)) end table.insert(l, ("text(%s)"):format(table.concat(text, ","))) elseif event.type == "flush" then table.insert(l, "flush") else return nil, ("event %q in event buffer cannot be hashed"):format(event.type) end end return ("eb(%s)"):format(table.concat(l, ",")) end, mark_constant = function() end, }, } package.loaded[...] = types local common = require((...):gsub("stdlib%.types$", "interpreter.common")) format, to_lua, from_lua, events, hash, mark_constant, update_hashes, get_variable, post_process_text = common.format, common.to_lua, common.from_lua, common.events, common.hash, common.mark_constant, common.update_hashes, common.get_variable, common.post_process_text anselme = require((...):gsub("stdlib%.types$", "anselme")) local pcommon = require((...):gsub("stdlib%.types$", "parser.common")) escape, find_function_variant_from_fqm = pcommon.escape, pcommon.find_function_variant_from_fqm return types