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

Add maps; remove map emulation functionality from list; function and tags now internally use maps instead of lists

This commit is contained in:
Étienne Fildadut 2022-09-09 21:39:37 +09:00
parent bac5cdde01
commit 95462391e3
20 changed files with 699 additions and 139 deletions

View file

@ -303,7 +303,7 @@ $ show(object::&class)
~ object!show
```
* `#`: tag line. Can be followed by an [expression](#expressions); otherwise nil expression is assumed. The results of the [expression](#expressions) will be added to the tags send along with any `choice` or `text` event sent from its children. Can be nested.
* `#`: tag line. Can be followed by an [expression](#expressions); otherwise nil expression is assumed. The results of the [expression](#expressions) will be wrapped in a map and added to the tags send along with any `choice` or `text` event sent from its children. Can be nested.
```
# color="red"
@ -636,9 +636,11 @@ Default types are:
* `list`: a list of values. Mutable. Types can be mixed. Can be defined between square brackets and use comma as a separator '[1,2,3,4]'.
* `map`: a map of keys-values. Mutable. Types can be mixed. Can be defined between curly braces and use comma as a separator, using pairs to define the key-values pairs (otherwise the numeric position of the element is used as a key). '{1=1,2=2,3,4="heh",foo="bar"}'.
* `object`: an object/record. Mutable. Can be created by calling a class function.
Every type is immutable, except `list` and `object`.
Every type is immutable, except `list`, `map` and `object`.
How conversions are handled from Anselme to Lua:
@ -648,7 +650,9 @@ How conversions are handled from Anselme to Lua:
* `string` -> `string`
* `list` -> `table`. Pair elements in the list will be assigned as a key-value pair in the Lua list and its index skipped in the sequential part, e.g. `[1,2,"key"="value",3]` -> `{1,2,3,key="value"}`.
* `list` -> `table` (purely sequential table).
* `map` -> `table` (will map each key to a key in the Lua table).
* `pair` -> `table`, with a single key-value pair.
@ -660,7 +664,7 @@ How conservions are handled from Lua to Anselme:
* `string` -> `string`
* `table` -> `list`. First add the sequential part of the table in the list, then add pairs for the remaining elements, e.g. `{1,2,key="value",3}` -> `[1,2,3,"key"="value"]`
* `table` -> `list` or `map`. Converted to a list if the table is purely sequential, otherwise returns a map; e.g. `{1,2,key="value",3}` -> `{1=1,2=2,3=3,key="value"}` and `{1,2,3}` -> [1,2,3]
* `boolean` -> `number`, 0 for false, 1 for true.
@ -819,7 +823,7 @@ $ f(a, b, c)
{f(1,2,3)} = {f(c=3,b=2,a=1)} = {f(1,2,c=3)}
```
Anselme actually treat argument list are regular lists; named arguments are actually pairs. Arguments are evaluated left-to-right.
Anselme actually treat argument maps are regular maps; named arguments are actually pairs and positional arguments are implicitely converted to pairs with their position as a key. Arguments are evaluated left-to-right. The call will error if one of the keys in the map is not a string or number.
This means that pairs can't be passed directly as arguments to a function (as they will be considered named arguments). If you want to use pairs, always wrap them in a list.
@ -953,7 +957,7 @@ Built-in operators:
`a := b`: evaluate b, assign its value to identifier `a`. Returns the new value.
`a(index) := b`: evaluate b, assign its value to element of specific index in list `a`. Element is searched using the same method as list index operator `a(b)`; if indexing using a string and an associated pair doesn't exist, add a new one at the end of the list. Returns the new value.
`a(index) := b`: evaluate b, assign its value to element of specific index in list/map `a`. Element is searched using the same method as list/map index operator `a(b)`; in the case of list, also allows to add a new element to the list by giving `len(a)+1` as the index. In the case of a map, if b is nil `()`, deletes the key-value pair from the map. Returns the new value.
`a.b := v`: if a is a function reference or an object, modify the b variable in the reference function or object.
@ -1025,13 +1029,15 @@ This only works on strings:
`a :: b`: evaluate a and b, returns a new annotated value with a as value and b as the annotation. This annotation will be checked in type constraints.
`a # b`: evaluates b, then evaluates a whith b added to the active tags. Returns a.
`a # b`: evaluates b, then evaluates a with b added to the active tags (wrap b in a map and merges it with the current tag map). Returns a.
`a.b`: if a is a function reference, returns the first found variable (or reference to a subfunction) named `b` in the referenced function namespace. When overloading this operator, if `b` is an identifier, the operator will interpret it as a string (instead of returning the evaluated value of the variable eventually associated to the identifier).
`object.b`: if object is an object, returns the first found variable (or reference to a subfunction) named `b` in the object, or, if the object does not contain it, its base class.
`a(b)`: evaluate b (number), returns the value with this index in a (list). Use 1-based indexing. If b is a string, will search the first pair in the list with this string as its name. Operator is named `()`.
`list(b)`: evaluate b (number), returns the value with this index in the list. Use 1-based indexing. If a negative value is given, will look from the end of the list (`-1` is the last element, `-2` the one before, etc.). Error on invalid index. Operator is named `()`.
`map(b)`: evaluate b, returns the value with this key in the map. If the key is not present in the map, returns `nil`.
`{}(v)`: function called when formatting a value in a text interpolation for printing.

View file

@ -48,6 +48,14 @@ local function post_process_text(state, text)
return r
end
local function random_identifier()
local r = ""
for _=1, 16 do -- that's live 10^31 possibilities, ought to be enough for anyone
r = r .. string.char(math.random(32, 126))
end
return r
end
common = {
--- merge interpreter state with global state
merge_state = function(state)
@ -129,18 +137,11 @@ common = {
end,
--- mark a value as constant, recursively affecting all the potentially mutable subvalues
mark_constant = function(v)
if v.type == "list" then
v.constant = true
for _, item in ipairs(v.value) do
common.mark_constant(item)
end
elseif v.type == "object" then
v.constant = true
elseif v.type == "pair" or v.type == "annotated" then
common.mark_constant(v.value[1])
common.mark_constant(v.value[2])
elseif v.type ~= "nil" and v.type ~= "number" and v.type ~= "string" and v.type ~= "function reference" and v.type ~= "variable reference" then
error("unknown type")
if atypes[v.type] and atypes[v.type].mark_constant then
atypes[v.type].mark_constant(v)
if v.hash_id then v.hash_id = nil end -- no longer need to compare by id
else
error(("don't know how to mark type %s as constant"):format(v.type))
end
end,
--- returns a variable's value, evaluating a pending expression if neccessary
@ -311,6 +312,8 @@ common = {
end
end
return true
elseif a.constant and a.type == "map" then
return common.hash(a) == common.hash(b)
elseif a.constant and a.type == "object" then
if a.value.class ~= b.value.class then
return false
@ -348,6 +351,35 @@ common = {
return nil, ("no formatter for type %q"):format(val.type)
end
end,
--- compute a hash for a value.
-- A hash is a Lua string such as, given two values, they are considered equal by Anselme if and only if their hash are considered equal by Lua.
-- Will generate random identifiers for mutable values (equality test by reference) in order for the identifier to stay the same accross checkpoints and
-- other potential variable copies.
-- str: if success
-- nil, err: if error
hash = function(val)
if atypes[val.type] and atypes[val.type].hash then
if atypes[val.type].mutable and not val.constant then
if not val.hash_id then val.hash_id = random_identifier() end
return ("mut(%s)"):format(val.hash_id)
else
return atypes[val.type].hash(val.value)
end
else
return nil, ("no hasher for type %q"):format(val.type)
end
end,
--- recompute all the hases in a map.
-- str: if success
-- nil, err: if error
update_hashes = function(map)
for k, v in pairs(map.value) do
local hash, e = common.hash(v[1])
if not hash then return nil, e end
map[k] = nil
map[hash] = v
end
end,
--- convert anselme value to lua
-- lua value: if success (may be nil!)
-- nil, err: if error
@ -373,7 +405,8 @@ common = {
-- nil, err: if error
eval_text = function(state, text)
local l = {}
common.eval_text_callback(state, text, function(str) table.insert(l, str) end)
local s, e = common.eval_text_callback(state, text, function(str) table.insert(l, str) end)
if not s then return nil, e end
return table.concat(l)
end,
--- same as eval_text, but instead of building a Lua string, call callback for every evaluated part of the text
@ -436,15 +469,14 @@ common = {
end,
--- tag management
tags = {
--- push new tags on top of the stack, from Anselme values
--- push new tags on top of the stack, from Anselme values. val is expected to be a map.
push = function(self, state, val)
local new = { type = "list", value = {} }
local new = { type = "map", value = {} }
-- copy
local last = self:current(state)
for _, v in ipairs(last.value) do table.insert(new.value, v) end
for k, v in pairs(last.value) do new.value[k] = v end
-- append new values
if val.type ~= "list" then val = { type = "list", value = { val } } end
for _, v in ipairs(val.value) do table.insert(new.value, v) end
for k, v in pairs(val.value) do new.value[k] = v end
-- add
table.insert(state.interpreter.tags, new)
end,
@ -458,7 +490,7 @@ common = {
end,
--- return current lua tags table
current = function(self, state)
return state.interpreter.tags[#state.interpreter.tags] or { type = "list", value = {} }
return state.interpreter.tags[#state.interpreter.tags] or { type = "map", value = {} }
end,
--- returns length of tags stack
len = function(self, state)

View file

@ -1,5 +1,5 @@
local expression
local to_lua, from_lua, eval_text, truthy, format, pretty_type, get_variable, tags, eval_text_callback, events, flatten_list, set_variable, scope, check_constraint
local to_lua, from_lua, eval_text, truthy, format, pretty_type, get_variable, tags, eval_text_callback, events, flatten_list, set_variable, scope, check_constraint, hash
local run
@ -52,6 +52,30 @@ local function eval(state, exp)
value = {}
}
end
-- map defined in brackets
elseif exp.type == "map_brackets" then
-- get constructing list
local list, e = eval(state, { type = "list_brackets", expression = exp.expression })
if not list then return nil, e end
-- make map
local map = {}
for i, v in ipairs(list.value) do
local key, value
if v.type == "pair" then
key = v.value[1]
value = v.value[2]
else
key = { type = "number", value = i }
value = v
end
local h, err = hash(key)
if not h then return nil, err end
map[h] = { key, value }
end
return {
type = "map",
value = map
}
-- list defined using , operator
elseif exp.type == "list" then
local flat = flatten_list(exp)
@ -142,7 +166,7 @@ local function eval(state, exp)
}
-- tag
elseif exp.type == "#" then
local right, righte = eval(state, exp.right)
local right, righte = eval(state, { type = "map_brackets", expression = exp.right })
if not right then return nil, righte end
tags:push(state, right)
local left, lefte = eval(state, exp.left)
@ -165,12 +189,24 @@ local function eval(state, exp)
}
-- function
elseif exp.type == "function call" then
-- eval args: list_brackets
-- eval args: map_brackets
local args = {}
local last_contiguous_positional = 0
if exp.argument then
local arg, arge = eval(state, exp.argument)
if not arg then return nil, arge end
args = arg.value
-- map into args table
for _, v in pairs(arg.value) do
if v[1].type == "string" or v[1].type == "number" then
args[v[1].value] = v[2]
else
return nil, ("unexpected key of type %s in argument map; keys must be string or number"):format(v[1].type)
end
end
-- get length of contiguous positional arguments (#args may not be always be equal depending on implementation...)
for i, _ in ipairs(args) do
last_contiguous_positional = i
end
end
-- function reference: call the referenced function
local variants = exp.variants
@ -191,13 +227,6 @@ local function eval(state, exp)
end
end
end
-- map named arguments
local named_args = {}
for i, arg in ipairs(args) do
if arg.type == "pair" and arg.value[1].type == "string" then
named_args[arg.value[1].value] = { i, arg.value[2] }
end
end
-- eval assignment arg
local assignment
if exp.assignment then
@ -222,21 +251,21 @@ local function eval(state, exp)
for j, param in ipairs(fn.params) do
local val
-- named
if param.alias and named_args[param.alias] then
val = named_args[param.alias][2]
used_args[named_args[param.alias][1]] = true
elseif named_args[param.name] then
val = named_args[param.name][2]
used_args[named_args[param.name][1]] = true
if param.alias and args[param.alias] then
val = args[param.alias]
used_args[param.alias] = true
elseif args[param.name] then
val = args[param.name]
used_args[param.name] = true
-- vararg
elseif param.vararg then
val = { type = "list", value = {} }
for k=j, #args do
for k=j, last_contiguous_positional do
table.insert(val.value, args[k])
used_args[k] = true
end
-- positional
elseif args[j] and args[j].type ~= "pair" then
elseif args[j] then
val = args[j]
used_args[j] = true
end
@ -264,8 +293,8 @@ local function eval(state, exp)
end
-- check for unused arguments
if ok then
for i, arg in ipairs(args) do
if not used_args[i] then
for key, arg in pairs(args) do
if not used_args[key] then
ok = false
if arg.type == "pair" and arg.value[1].type == "string" then
table.insert(tried_function_error_messages, ("%s: unexpected %s argument"):format(fn.pretty_signature, arg.value[1].value))
@ -473,11 +502,10 @@ local function eval(state, exp)
end
-- no matching function found
local args_txt = {}
for _, arg in ipairs(args) do
for key, arg in pairs(args) do
local s = ""
if arg.type == "pair" and arg.value[1].type == "string" then
s = s .. ("%s="):format(arg.value[1].value)
arg = arg.value[2]
if type(key) == "string" or (type(key) == "number" and key > last_contiguous_positional) then
s = s .. ("%s="):format(key)
end
s = s .. pretty_type(arg)
table.insert(args_txt, s)
@ -511,6 +539,6 @@ run = require((...):gsub("expression$", "interpreter")).run
expression = require((...):gsub("interpreter%.expression$", "parser.expression"))
flatten_list = require((...):gsub("interpreter%.expression$", "parser.common")).flatten_list
local common = require((...):gsub("expression$", "common"))
to_lua, from_lua, eval_text, truthy, format, pretty_type, get_variable, tags, eval_text_callback, events, set_variable, scope, check_constraint = common.to_lua, common.from_lua, common.eval_text, common.truthy, common.format, common.pretty_type, common.get_variable, common.tags, common.eval_text_callback, common.events, common.set_variable, common.scope, common.check_constraint
to_lua, from_lua, eval_text, truthy, format, pretty_type, get_variable, tags, eval_text_callback, events, set_variable, scope, check_constraint, hash = common.to_lua, common.from_lua, common.eval_text, common.truthy, common.format, common.pretty_type, common.get_variable, common.tags, common.eval_text_callback, common.events, common.set_variable, common.scope, common.check_constraint, common.hash
return eval

View file

@ -287,7 +287,7 @@ common = {
implicit_call = implicit_call, -- was call implicitely (no ! or parentheses)?
variants = variants, -- list of potential variants
argument = { -- wrap everything in a list literal to simplify later things (otherwise may be nil, single value, list constructor)
type = "list_brackets",
type = "map_brackets",
expression = arg
}
}

View file

@ -132,6 +132,21 @@ local function expression(s, state, namespace, current_priority, operating_on)
type = "list_brackets",
expression = exp
})
-- map parenthesis
elseif s:match("^%b{}") then
local content, r = s:match("^(%b{})(.*)$")
content = content:gsub("^%{", ""):gsub("%}$", "")
local exp
if content:match("[^%s]") then
local r_paren
exp, r_paren = expression(content, state, namespace)
if not exp then return nil, "invalid expression inside map parentheses: "..r_paren end
if r_paren:match("[^%s]") then return nil, ("unexpected %q at end of map parenthesis expression"):format(r_paren) end
end
return expression(r, state, namespace, current_priority, {
type = "map_brackets",
expression = exp
})
-- identifier
elseif s:match("^"..identifier_pattern) then
local name, r = s:match("^("..identifier_pattern..")(.-)$")
@ -182,7 +197,7 @@ local function expression(s, state, namespace, current_priority, operating_on)
local err
args, err = expression(content, state, namespace)
if not args then return args, err end
if err:match("[^%s]") then return nil, ("unexpected %q at end of argument list"):format(err) end
if err:match("[^%s]") then return nil, ("unexpected %q at end of argument map"):format(err) end
end
else -- implicit call; will be changed if there happens to be a ! after in the suffix operator code
implicit_call = true
@ -267,7 +282,7 @@ local function expression(s, state, namespace, current_priority, operating_on)
local err
args, err = expression(content, state, namespace)
if not args then return args, err end
if err:match("[^%s]") then return nil, ("unexpected %q at end of argument list"):format(err) end
if err:match("[^%s]") then return nil, ("unexpected %q at end of argument map"):format(err) end
end
end
-- add first argument

View file

@ -332,11 +332,7 @@ local function parse_line(line, state, namespace, parent_function)
r.type = "tag"
r.child = true
local expr = l:match("^%#(.*)$")
if expr:match("[^%s]") then
r.expression = expr
else
r.expression = "()"
end
r.expression = ("{%s}"):format(expr)
-- return
elseif l:match("^%@") then
r.type = "return"

View file

@ -5,6 +5,7 @@ return [[
:number="number"
:string="string"
:list="list"
:map="map"
:pair="pair"
:function reference="function reference"
:variable reference="variable reference"

View file

@ -1,4 +1,4 @@
local truthy, anselme, compare, is_of_type, identifier_pattern, format_identifier, find, get_variable, mark_as_modified, set_variable, check_mutable, copy, mark_constant
local truthy, anselme, compare, is_of_type, identifier_pattern, format_identifier, find, get_variable, mark_as_modified, set_variable, check_mutable, copy, mark_constant, hash
local lua_functions
lua_functions = {
@ -173,19 +173,25 @@ lua_functions = {
["()(l::list, i::number)"] = {
mode = "unannotated raw",
value = function(l, i)
return l.value[i.value] or { type = "nil", value = nil }
local index = i.value
if index < 0 then index = #l.value + 1 + index end
if index > #l.value or index == 0 then return nil, "list index out of bounds" end
return l.value[index] or { type = "nil", value = nil }
end
},
["()(l::list, i::string)"] = {
mode = "unannotated raw",
["()(l::map, k)"] = {
mode = "raw",
value = function(l, i)
for _, v in ipairs(l.value) do
if v.type == "pair" and compare(v.value[1], i) then
return v.value[2]
end
end
local lv = l.type == "annotated" and l.value[1] or l
local h, err = hash(i)
if not h then return nil, err end
local v = lv.value[h]
if v then
return v[2]
else
return { type = "nil", value = nil }
end
end
},
-- index assignment
["()(l::list, i::number) := v"] = {
@ -194,30 +200,34 @@ lua_functions = {
local lv = l.type == "annotated" and l.value[1] or l
local iv = i.type == "annotated" and i.value[1] or i
if lv.constant then return nil, "can't change the contents of a constant list" end
lv.value[iv.value] = v
local index = iv.value
if index < 0 then index = #lv.value + 1 + index end
if index > #lv.value + 1 or index == 0 then return nil, "list assignment index out of bounds" end
lv.value[index] = v
mark_as_modified(anselme.running.state, lv.value)
return v
end
},
["()(l::list, k::string) := v"] = {
["()(l::map, k) := v::nil"] = {
mode = "raw",
value = function(l, k, v)
local lv = l.type == "annotated" and l.value[1] or l
local kv = k.type == "annotated" and k.value[1] or k
if lv.constant then return nil, "can't change the contents of a constant list" end
-- update index
for _, x in ipairs(lv.value) do
if x.type == "pair" and compare(x.value[1], kv) then
x.value[2] = v
mark_as_modified(anselme.running.state, x.value) -- FIXME i thought pairs were immutable...
if lv.constant then return nil, "can't change the contents of a constant map" end
local h, err = hash(k)
if not h then return nil, err end
lv.value[h] = nil
mark_as_modified(anselme.running.state, lv.value)
return v
end
end
-- new index
table.insert(lv.value, {
type = "pair",
value = { kv, v }
})
},
["()(l::map, k) := v"] = {
mode = "raw",
value = function(l, k, v)
local lv = l.type == "annotated" and l.value[1] or l
if lv.constant then return nil, "can't change the contents of a constant map" end
local h, err = hash(k)
if not h then return nil, err end
lv.value[h] = { k, v }
mark_as_modified(anselme.running.state, lv.value)
return v
end
@ -435,7 +445,7 @@ local functions = {
package.loaded[...] = functions
local icommon = require((...):gsub("stdlib%.functions$", "interpreter.common"))
truthy, compare, is_of_type, get_variable, mark_as_modified, set_variable, check_mutable, mark_constant = icommon.truthy, icommon.compare, icommon.is_of_type, icommon.get_variable, icommon.mark_as_modified, icommon.set_variable, icommon.check_mutable, icommon.mark_constant
truthy, compare, is_of_type, get_variable, mark_as_modified, set_variable, check_mutable, mark_constant, hash = icommon.truthy, icommon.compare, icommon.is_of_type, icommon.get_variable, icommon.mark_as_modified, icommon.set_variable, icommon.check_mutable, icommon.mark_constant, icommon.hash
local pcommon = require((...):gsub("stdlib%.functions$", "parser.common"))
identifier_pattern, format_identifier, find = pcommon.identifier_pattern, pcommon.format_identifier, pcommon.find
anselme = require((...):gsub("stdlib%.functions$", "anselme"))

View file

@ -1,4 +1,4 @@
local format, to_lua, from_lua, events, anselme, escape
local format, to_lua, from_lua, events, anselme, escape, hash, mark_constant, update_hashes
local types = {}
types.lua = {
@ -36,7 +36,9 @@ types.lua = {
},
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
@ -44,21 +46,34 @@ types.lua = {
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
table.insert(l, {
type = "pair",
value = { kv, vv }
})
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
}
}
@ -69,7 +84,11 @@ types.anselme = {
end,
to_lua = function()
return nil
end
end,
hash = function()
return "nil()"
end,
mark_constant = function() end,
},
number = {
format = function(val)
@ -77,7 +96,11 @@ types.anselme = {
end,
to_lua = function(val)
return val
end
end,
hash = function(val)
return ("n(%s)"):format(val)
end,
mark_constant = function() end,
},
string = {
format = function(val)
@ -85,9 +108,14 @@ types.anselme = {
end,
to_lua = function(val)
return val
end
end,
hash = function(val)
return ("s(%s)"):format(val)
end,
mark_constant = function() end,
},
list = {
mutable = true,
format = function(val)
local l = {}
for _, v in ipairs(val) do
@ -99,25 +127,74 @@ types.anselme = {
end,
to_lua = function(val)
local l = {}
-- handle non-pair before pairs as LuaJIT's table.insert will always insert after the last element even if there are some nil before unlike PUC
for _, v in ipairs(val) do
if v.type ~= "pair" then
local s, e = to_lua(v)
if not s and e then return s, e end
table.insert(l, s)
end
end
return l
end,
hash = function(val)
local l = {}
for _, v in ipairs(val) do
if v.type == "pair" then
local k, ke = to_lua(v.value[1])
if not k and ke then return k, ke end
local x, xe = to_lua(v.value[2])
if not x and xe then return x, xe end
l[k] = x
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)
local l = {}
for _, v in pairs(val) do
local kl, ke = to_lua(v[1])
if not kl and ke then return kl, ke end
local xl, xe = to_lua(v[2])
if not xl and xe then return xl, 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,
},
pair = {
format = function(val)
@ -133,7 +210,18 @@ types.anselme = {
local v, ve = to_lua(val[2])
if not v and ve then return v, ve end
return { [k] = v }
end
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)
@ -147,7 +235,18 @@ types.anselme = {
local k, ke = to_lua(val[1])
if not k and ke then return k, ke end
return k
end
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,
},
["function reference"] = {
format = function(val)
@ -157,27 +256,49 @@ types.anselme = {
return ("&%s"):format(table.concat(val, ", "))
end
end,
to_lua = nil
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
to_lua = nil,
hash = function(val)
return ("&v(%s)"):format(val)
end,
mark_constant = function() 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 = nil
to_lua = nil,
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,
},
-- internal types
["event buffer"] = {
@ -191,7 +312,7 @@ types.anselme = {
package.loaded[...] = types
local common = require((...):gsub("stdlib%.types$", "interpreter.common"))
format, to_lua, from_lua, events = common.format, common.to_lua, common.from_lua, common.events
format, to_lua, from_lua, events, hash, mark_constant, update_hashes = common.format, common.to_lua, common.from_lua, common.events, common.hash, common.mark_constant, common.update_hashes
anselme = require((...):gsub("stdlib%.types$", "anselme"))
escape = require((...):gsub("stdlib%.types$", "parser.common")).escape

View file

@ -1,7 +1,7 @@
:person = "personne"
$ Person(name, age)
@[name=name, age=age]::person
@{name=name, age=age}::person
:abject = Person("Darmanin", 38)

View file

@ -6,15 +6,16 @@
{x}
{x("foo") := "a"}
{x(2) := 5}
{x}
{x("bar") := "b"}
{x(-1) := 12}
{x}
{x("foo") := "c"}
{x(3) := 99}
{x}
{x(5) := 0}

View file

@ -8,12 +8,12 @@ _[32]={}
_[31]={}
_[30]={}
_[29]={}
_[28]={tags=_[37],text="[3, 2, foo=c, bar=b]"}
_[27]={tags=_[36],text="c"}
_[26]={tags=_[35],text="[3, 2, foo=a, bar=b]"}
_[25]={tags=_[34],text="b"}
_[24]={tags=_[33],text="[3, 2, foo=a]"}
_[23]={tags=_[32],text="a"}
_[28]={tags=_[37],text="[3, 12, 99]"}
_[27]={tags=_[36],text="99"}
_[26]={tags=_[35],text="[3, 12]"}
_[25]={tags=_[34],text="12"}
_[24]={tags=_[33],text="[3, 5]"}
_[23]={tags=_[32],text="5"}
_[22]={tags=_[31],text="[3, 2]"}
_[21]={tags=_[30],text="3"}
_[20]={tags=_[29],text="[1, 2]"}
@ -26,7 +26,7 @@ _[14]={_[23]}
_[13]={_[22]}
_[12]={_[21]}
_[11]={_[20]}
_[10]={"return"}
_[10]={"error","list assignment index out of bounds; in Lua function \"()\"; at test/tests/list assignement.ans:21"}
_[9]={"text",_[19]}
_[8]={"text",_[18]}
_[7]={"text",_[17]}
@ -52,27 +52,27 @@ return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10]}
} } }
{ "text", { {
tags = {},
text = "a"
text = "5"
} } }
{ "text", { {
tags = {},
text = "[3, 2, foo=a]"
text = "[3, 5]"
} } }
{ "text", { {
tags = {},
text = "b"
text = "12"
} } }
{ "text", { {
tags = {},
text = "[3, 2, foo=a, bar=b]"
text = "[3, 12]"
} } }
{ "text", { {
tags = {},
text = "c"
text = "99"
} } }
{ "text", { {
tags = {},
text = "[3, 2, foo=c, bar=b]"
text = "[3, 12, 99]"
} } }
{ "return" }
{ "error", 'list assignment index out of bounds; in Lua function "()"; at test/tests/list assignement.ans:21' }
]]--

11
test/tests/list index.ans Normal file
View file

@ -0,0 +1,11 @@
:x = [1,2,3]
{x}
{x(1)} == {x(-3)}
{x(2)} == {x(-2)}
{x(3)} == {x(-1)}
{x(-4)}

68
test/tests/list index.lua Normal file
View file

@ -0,0 +1,68 @@
local _={}
_[29]={}
_[28]={}
_[27]={}
_[26]={}
_[25]={}
_[24]={}
_[23]={}
_[22]={}
_[21]={}
_[20]={}
_[19]={text="3",tags=_[29]}
_[18]={text=" == ",tags=_[28]}
_[17]={text="3",tags=_[27]}
_[16]={text="2",tags=_[26]}
_[15]={text=" == ",tags=_[25]}
_[14]={text="2",tags=_[24]}
_[13]={text="1",tags=_[23]}
_[12]={text=" == ",tags=_[22]}
_[11]={text="1",tags=_[21]}
_[10]={text="[1, 2, 3]",tags=_[20]}
_[9]={_[17],_[18],_[19]}
_[8]={_[14],_[15],_[16]}
_[7]={_[11],_[12],_[13]}
_[6]={_[10]}
_[5]={"error","list index out of bounds; in Lua function \"()\"; at test/tests/list index.ans:11"}
_[4]={"text",_[9]}
_[3]={"text",_[8]}
_[2]={"text",_[7]}
_[1]={"text",_[6]}
return {_[1],_[2],_[3],_[4],_[5]}
--[[
{ "text", { {
tags = {},
text = "[1, 2, 3]"
} } }
{ "text", { {
tags = {},
text = "1"
}, {
tags = {},
text = " == "
}, {
tags = {},
text = "1"
} } }
{ "text", { {
tags = {},
text = "2"
}, {
tags = {},
text = " == "
}, {
tags = {},
text = "2"
} } }
{ "text", { {
tags = {},
text = "3"
}, {
tags = {},
text = " == "
}, {
tags = {},
text = "3"
} } }
{ "error", 'list index out of bounds; in Lua function "()"; at test/tests/list index.ans:11' }
]]--

View file

@ -0,0 +1,20 @@
:x = {1,2}
{x}
{x(1) := 3}
{x}
{x("foo") := "a"}
{x}
{x("bar") := "b"}
{x}
{x("foo") := "c"}
{x}

View file

@ -0,0 +1,78 @@
local _={}
_[37]={}
_[36]={}
_[35]={}
_[34]={}
_[33]={}
_[32]={}
_[31]={}
_[30]={}
_[29]={}
_[28]={text="{1=3, 2=2, bar=b, foo=c}",tags=_[37]}
_[27]={text="c",tags=_[36]}
_[26]={text="{1=3, 2=2, bar=b, foo=a}",tags=_[35]}
_[25]={text="b",tags=_[34]}
_[24]={text="{1=3, 2=2, foo=a}",tags=_[33]}
_[23]={text="a",tags=_[32]}
_[22]={text="{1=3, 2=2}",tags=_[31]}
_[21]={text="3",tags=_[30]}
_[20]={text="{1=1, 2=2}",tags=_[29]}
_[19]={_[28]}
_[18]={_[27]}
_[17]={_[26]}
_[16]={_[25]}
_[15]={_[24]}
_[14]={_[23]}
_[13]={_[22]}
_[12]={_[21]}
_[11]={_[20]}
_[10]={"return"}
_[9]={"text",_[19]}
_[8]={"text",_[18]}
_[7]={"text",_[17]}
_[6]={"text",_[16]}
_[5]={"text",_[15]}
_[4]={"text",_[14]}
_[3]={"text",_[13]}
_[2]={"text",_[12]}
_[1]={"text",_[11]}
return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10]}
--[[
{ "text", { {
tags = {},
text = "{1=1, 2=2}"
} } }
{ "text", { {
tags = {},
text = "3"
} } }
{ "text", { {
tags = {},
text = "{1=3, 2=2}"
} } }
{ "text", { {
tags = {},
text = "a"
} } }
{ "text", { {
tags = {},
text = "{1=3, 2=2, foo=a}"
} } }
{ "text", { {
tags = {},
text = "b"
} } }
{ "text", { {
tags = {},
text = "{1=3, 2=2, bar=b, foo=a}"
} } }
{ "text", { {
tags = {},
text = "c"
} } }
{ "text", { {
tags = {},
text = "{1=3, 2=2, bar=b, foo=c}"
} } }
{ "return" }
]]--

View file

@ -0,0 +1,29 @@
:x = {4}
x={x}
:a = {1,2,3,(x)=4}
:c = a
1={a==c}
a(x)={a(x)}
§ ch a
a(x)={a(x)}
§ ch b
~ x(2) := 3
a(x)={a(x)}
§ ch c
a(x)={a(x)}
~ x:={4}
no={a(x)}

View file

@ -0,0 +1,92 @@
local _={}
_[41]={}
_[40]={}
_[39]={}
_[38]={}
_[37]={}
_[36]={}
_[35]={}
_[34]={}
_[33]={}
_[32]={}
_[31]={}
_[30]={}
_[29]={}
_[28]={tags=_[41],text="no="}
_[27]={tags=_[40],text="4"}
_[26]={tags=_[39],text="a(x)="}
_[25]={tags=_[38],text="4"}
_[24]={tags=_[37],text="a(x)="}
_[23]={tags=_[36],text="4"}
_[22]={tags=_[35],text="a(x)="}
_[21]={tags=_[34],text="4"}
_[20]={tags=_[33],text="a(x)="}
_[19]={tags=_[32],text="1"}
_[18]={tags=_[31],text="1="}
_[17]={tags=_[30],text="{1=4}"}
_[16]={tags=_[29],text="x="}
_[15]={_[28]}
_[14]={_[26],_[27]}
_[13]={_[24],_[25]}
_[12]={_[22],_[23]}
_[11]={_[20],_[21]}
_[10]={_[18],_[19]}
_[9]={_[16],_[17]}
_[8]={"return"}
_[7]={"text",_[15]}
_[6]={"text",_[14]}
_[5]={"text",_[13]}
_[4]={"text",_[12]}
_[3]={"text",_[11]}
_[2]={"text",_[10]}
_[1]={"text",_[9]}
return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8]}
--[[
{ "text", { {
tags = {},
text = "x="
}, {
tags = {},
text = "{1=4}"
} } }
{ "text", { {
tags = {},
text = "1="
}, {
tags = {},
text = "1"
} } }
{ "text", { {
tags = {},
text = "a(x)="
}, {
tags = {},
text = "4"
} } }
{ "text", { {
tags = {},
text = "a(x)="
}, {
tags = {},
text = "4"
} } }
{ "text", { {
tags = {},
text = "a(x)="
}, {
tags = {},
text = "4"
} } }
{ "text", { {
tags = {},
text = "a(x)="
}, {
tags = {},
text = "4"
} } }
{ "text", { {
tags = {},
text = "no="
} } }
{ "return" }
]]--

7
test/tests/map index.ans Normal file
View file

@ -0,0 +1,7 @@
:t = {ahah=23,k=23,12}
{t} == \{3=12, ahah=23, k=23}
{t(3)} == 12
{t("ahah")} == 23

45
test/tests/map index.lua Normal file
View file

@ -0,0 +1,45 @@
local _={}
_[19]={}
_[18]={}
_[17]={}
_[16]={}
_[15]={}
_[14]={}
_[13]={tags=_[19],text=" == 23"}
_[12]={tags=_[18],text="23"}
_[11]={tags=_[17],text=" == 12"}
_[10]={tags=_[16],text="12"}
_[9]={tags=_[15],text=" == {3=12, ahah=23, k=23}"}
_[8]={tags=_[14],text="{3=12, ahah=23, k=23}"}
_[7]={_[12],_[13]}
_[6]={_[10],_[11]}
_[5]={_[8],_[9]}
_[4]={"return"}
_[3]={"text",_[7]}
_[2]={"text",_[6]}
_[1]={"text",_[5]}
return {_[1],_[2],_[3],_[4]}
--[[
{ "text", { {
tags = {},
text = "{3=12, ahah=23, k=23}"
}, {
tags = {},
text = " == {3=12, ahah=23, k=23}"
} } }
{ "text", { {
tags = {},
text = "12"
}, {
tags = {},
text = " == 12"
} } }
{ "text", { {
tags = {},
text = "23"
}, {
tags = {},
text = " == 23"
} } }
{ "return" }
]]--