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

Add variable reference and use them in alias(ref, id)

This commit is contained in:
Étienne Fildadut 2021-12-09 14:11:18 +01:00
parent 037654ebcf
commit acb8945dec
11 changed files with 138 additions and 45 deletions

View file

@ -534,6 +534,8 @@ Default types are:
* `function reference`: reference to one or more function(s) with a given name. Can be defined using `&function name`, which will create a reference to every function with this name accessible from the current namespace. Can be called as if it was the original function using `func ref!` and `func ref(args)`. * `function reference`: reference to one or more function(s) with a given name. Can be defined using `&function name`, which will create a reference to every function with this name accessible from the current namespace. Can be called as if it was the original function using `func ref!` and `func ref(args)`.
* `variable reference`: reference to a single variable with a given name. Can be defined using `&variable name`, which will create a reference to the closest variable with this name accessible from the current namespace. Can get the referenced variable value using `var ref!`.
* `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]'. * `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]'.
Every type is immutable, except `list`. Every type is immutable, except `list`.
@ -835,7 +837,7 @@ This only works on strings:
`a | b`: or operator, lazy `a | b`: or operator, lazy
##### Functions ##### Functions and function references
`fn(args)`: call the function, checkpoint or function reference with the given arguments. `fn(args)`: call the function, checkpoint or function reference with the given arguments.
@ -845,6 +847,12 @@ This only works on strings:
`a!fn(args)`: call the function or function reference with the variable as first argument. Parantheses are optional. `a!fn(args)`: call the function or function reference with the variable as first argument. Parantheses are optional.
##### Variable references
`&var`: returns a variable reference to the given variable.
`a!`: returns the value associated with the reference variable.
##### Various ##### Various
`a ; b`: evaluate a, discard its result, then evaluate b. Returns the result of b. `a ; b`: evaluate a, discard its result, then evaluate b. Returns the result of b.
@ -891,7 +899,7 @@ This only works on strings:
##### Various ##### Various
`alias(identifier::string, alias::string)`: define an alias `alias` for variable `identifier`. Expect fully qualified names. `alias(ref::reference, alias::string)`: define an alias `alias` for the variable or function referenced by `ref`. Expect fully qualified names for the alias.
`rand([m[, n]])`: when called whitout arguments, returns a random float in [0,1). Otherwise, returns a random number in [m,n]; m=1 if not given. `rand([m[, n]])`: when called whitout arguments, returns a random float in [0,1). Otherwise, returns a random number in [m,n]; m=1 if not given.
@ -903,7 +911,7 @@ This only works on strings:
#### Built-in variables #### Built-in variables
Variables for default types (each is associated to a string of the internal variable type name): `nil`, `number`, `string`, `list`, `pair`, `function reference`. Variables for default types (each is associated to a string of the internal variable type name): `nil`, `number`, `string`, `list`, `pair`, `function reference`, `variable reference`.
#### Built-in languages #### Built-in languages

View file

@ -197,6 +197,8 @@ local interpreter_methods = {
anselme.running = previous anselme.running = previous
if not success then if not success then
return nil, event return nil, event
elseif event == "error" then
return nil, data
elseif event ~= "return" then elseif event ~= "return" then
return nil, ("evaluated expression generated an %q event; at %s"):format(event, self.state.interpreter.running_line.source) return nil, ("evaluated expression generated an %q event; at %s"):format(event, self.state.interpreter.running_line.source)
else else

View file

@ -148,12 +148,18 @@ local function eval(state, exp)
-- variable -- variable
elseif exp.type == "variable" then elseif exp.type == "variable" then
return get_variable(state, exp.name) return get_variable(state, exp.name)
-- function -- references
elseif exp.type == "function reference" then elseif exp.type == "function reference" then
return { return {
type = "function reference", type = "function reference",
value = exp.names value = exp.names
} }
elseif exp.type == "variable reference" then
return {
type = "variable reference",
value = exp.name
}
-- function
elseif exp.type == "function call" then elseif exp.type == "function call" then
-- eval args: list_brackets -- eval args: list_brackets
local args = {} local args = {}
@ -372,7 +378,7 @@ local function eval(state, exp)
if r then if r then
ret = r ret = r
else else
return nil, ("%s; in Lua function %q"):format(e, exp.called_name) return nil, ("%s; in Lua function %q"):format(e or "raw function returned nil and no error message", exp.called_name)
end end
-- untyped raw mode: same as raw, but strips custom types from the arguments -- untyped raw mode: same as raw, but strips custom types from the arguments
elseif lua_fn.mode == "untyped raw" then elseif lua_fn.mode == "untyped raw" then
@ -386,7 +392,7 @@ local function eval(state, exp)
if r then if r then
ret = r ret = r
else else
return nil, ("%s; in Lua function %q"):format(e, exp.called_name) return nil, ("%s; in Lua function %q"):format(e or "untyped raw function returned nil and no error message", exp.called_name)
end end
-- normal mode: convert args to Lua and convert back Lua value to Anselme -- normal mode: convert args to Lua and convert back Lua value to Anselme
elseif lua_fn.mode == nil then elseif lua_fn.mode == nil then

View file

@ -6,16 +6,19 @@ local common
--- rewrite name to use defined aliases (under namespace only) --- rewrite name to use defined aliases (under namespace only)
-- namespace should not contain aliases -- namespace should not contain aliases
-- returns the final fqm
local replace_aliases = function(aliases, namespace, name) local replace_aliases = function(aliases, namespace, name)
namespace = namespace == "" and "" or namespace.."."
local name_list = common.split(name) local name_list = common.split(name)
for i=1, #name_list, 1 do local prefix = namespace
local n = ("%s%s"):format(namespace, table.concat(name_list, ".", 1, i)) for i=1, #name_list, 1 do -- search alias for each part of the fqm
local n = ("%s%s%s"):format(prefix, prefix == "" and "" or ".", name_list[i])
if aliases[n] then if aliases[n] then
name_list[i] = aliases[n]:match("[^%.]+$") prefix = aliases[n]
else
prefix = n
end end
end end
return table.concat(name_list, ".") return prefix
end end
local disallowed_set = ("~`^+-=<>/[]*{}|\\_!?,;:()\"@&$#%"):gsub("[^%w]", "%%%1") local disallowed_set = ("~`^+-=<>/[]*{}|\\_!?,;:()\"@&$#%"):gsub("[^%w]", "%%%1")
@ -93,7 +96,7 @@ common = {
local ns = common.split(namespace) local ns = common.split(namespace)
for i=#ns, 1, -1 do for i=#ns, 1, -1 do
local current_namespace = table.concat(ns, ".", 1, i) local current_namespace = table.concat(ns, ".", 1, i)
local fqm = ("%s.%s"):format(current_namespace, replace_aliases(aliases, current_namespace, name)) local fqm = replace_aliases(aliases, current_namespace, name)
if list[fqm] then if list[fqm] then
return list[fqm], fqm return list[fqm], fqm
end end
@ -112,7 +115,7 @@ common = {
local ns = common.split(namespace) local ns = common.split(namespace)
for i=#ns, 1, -1 do for i=#ns, 1, -1 do
local current_namespace = table.concat(ns, ".", 1, i) local current_namespace = table.concat(ns, ".", 1, i)
local fqm = ("%s.%s"):format(current_namespace, replace_aliases(aliases, current_namespace, name)) local fqm = replace_aliases(aliases, current_namespace, name)
if list[fqm] then if list[fqm] then
table.insert(l, fqm) table.insert(l, fqm)
end end

View file

@ -190,15 +190,27 @@ local function expression(s, state, namespace, current_priority, operating_on)
local escaped = escape(op) local escaped = escape(op)
if s:match("^"..escaped) then if s:match("^"..escaped) then
local sright = s:match("^"..escaped.."(.*)$") local sright = s:match("^"..escaped.."(.*)$")
-- function reference -- function and variable reference
if op == "&" and sright:match("^"..identifier_pattern) then if op == "&" and sright:match("^"..identifier_pattern) then
local name, r = sright:match("^("..identifier_pattern..")(.-)$") local name, r = sright:match("^("..identifier_pattern..")(.-)$")
name = format_identifier(name) name = format_identifier(name)
-- get all functions this name can reference -- get all functions this name can reference
-- try prefixes until we find a valid function name -- try prefixes until we find a valid function or variable name
local nl = split(name) local nl = split(name)
for i=#nl, 1, -1 do for i=#nl, 1, -1 do
local name_prefix = table.concat(nl, ".", 1, i) local name_prefix = table.concat(nl, ".", 1, i)
-- variable ref
local var, vfqm = find(state.aliases, state.variables, namespace, name_prefix)
if var then
if i < #nl then
r = "."..table.concat(nl, ".", i+1, #nl)..r
end
return expression(r, state, namespace, current_priority, {
type = "variable reference",
name = vfqm
})
end
-- function ref
local lfnqm = find_all(state.aliases, state.functions, namespace, name_prefix) local lfnqm = find_all(state.aliases, state.functions, namespace, name_prefix)
if #lfnqm > 0 then if #lfnqm > 0 then
if i < #nl then if i < #nl then

View file

@ -7,4 +7,5 @@ return [[
:list="list" :list="list"
:pair="pair" :pair="pair"
:function reference="function reference" :function reference="function reference"
:variable reference="variable reference"
]] ]]

View file

@ -145,9 +145,15 @@ functions = {
["()(fn::function reference, l...)"] = { ["()(fn::function reference, l...)"] = {
-- bypassed, this case is manually handled in the expression interpreter -- bypassed, this case is manually handled in the expression interpreter
}, },
["_!(fn::function reference, l...)"] = { ["_!(fn::function reference)"] = {
-- bypassed, this case is manually handled in the expression interpreter -- bypassed, this case is manually handled in the expression interpreter
}, },
["_!(fn::variable reference)"] = {
mode = "untyped raw",
value = function(v)
return anselme.running.state.variables[v.value]
end
},
-- format -- format
["{}(v)"] = { ["{}(v)"] = {
mode = "raw", mode = "raw",
@ -156,21 +162,40 @@ functions = {
end end
}, },
-- alias -- alias
["alias(identifier::string, alias::string)"] = { ["alias(ref::function reference, alias::string)"] = {
value = function(identifier, alias) mode = "untyped raw",
value = function(ref, alias)
-- check identifiers -- check identifiers
local fqm = identifier:match("^"..identifier_pattern.."$") alias = alias.value
if not fqm then error(("%q is not a valid identifier"):format(identifier)) end local aliasfqm = alias:match("^"..identifier_pattern.."$")
fqm = format_identifier(fqm) if not aliasfqm then error(("%q is not a valid identifier for an alias"):format(alias)) end
aliasfqm = format_identifier(aliasfqm)
-- define alias
for _, fnfqm in ipairs(ref.value) do
local aliases = anselme.running.state.aliases
if aliases[aliasfqm] ~= nil and aliases[aliasfqm] ~= fnfqm then
error(("trying to define alias %q for %q, but already exist and refer to %q"):format(aliasfqm, fnfqm, aliases[alias]))
end
aliases[aliasfqm] = fnfqm
end
return { type = "nil" }
end
},
["alias(ref::variable reference, alias::string)"] = {
mode = "untyped raw",
value = function(ref, alias)
-- check identifiers
alias = alias.value
local aliasfqm = alias:match("^"..identifier_pattern.."$") local aliasfqm = alias:match("^"..identifier_pattern.."$")
if not aliasfqm then error(("%q is not a valid identifier for an alias"):format(alias)) end if not aliasfqm then error(("%q is not a valid identifier for an alias"):format(alias)) end
aliasfqm = format_identifier(aliasfqm) aliasfqm = format_identifier(aliasfqm)
-- define alias -- define alias
local aliases = anselme.running.state.aliases local aliases = anselme.running.state.aliases
if aliases[aliasfqm] ~= nil and aliases[aliasfqm] ~= fqm then if aliases[aliasfqm] ~= nil and aliases[aliasfqm] ~= ref.value then
error(("trying to define alias %q for %q, but already exist and refer to %q"):format(aliasfqm, fqm, aliases[alias])) error(("trying to define alias %q for %q, but already exist and refer to %q"):format(aliasfqm, ref.value, aliases[alias]))
end end
aliases[aliasfqm] = fqm aliases[aliasfqm] = ref.value
return { type = "nil" }
end end
}, },
-- pair methods -- pair methods

View file

@ -1,27 +1,29 @@
return [[ return [[
(Types) (Types)
~ "nil"!alias("nul") ~ &nil!alias("nul")
~ "number"!alias("nombre") ~ &number!alias("nombre")
~ "string"!alias("texte") ~ &string!alias("texte")
~ "list"!alias("liste") ~ &list!alias("liste")
~ "pair"!alias("paire") ~ &pair!alias("paire")
~ &function reference!alias("réference de fonction")
~ &variable reference!alias("réference de variable")
(Built-in functions) (Built-in functions)
(~ "alias"!alias("alias") (~ &alias!alias("alias")
~ "name"!alias("nom") ~ &name!alias("nom")
~ "value"!alias("valeur") ~ &value!alias("valeur")
~ "len"!alias("longueur") ~ &len!alias("longueur")
~ "insert"!alias("ajouter") ~ &insert!alias("ajouter")
~ "remove"!alias("retirer") ~ &remove!alias("retirer")
~ "find"!alias("trouver") ~ &find!alias("trouver")
~ "error"!alias("erreur") ~ &error!alias("erreur")
~ "rand"!alias("aléa") ~ &rand!alias("aléa")
~ "raw"!alias("brut") ~ &raw!alias("brut")
(~ "type"!alias("type") (~ &type!alias("type")
~ "is of type"!alias("est de type") ~ &is of type!alias("est de type")
~ "cycle"!alias("cycler") ~ &cycle!alias("cycler")
~ "random"!alias("aléatoire") ~ &random!alias("aléatoire")
~ "next"!alias("séquence") ~ &next!alias("séquence")
(Built-in variables) (Built-in variables)
:alias 👁 = "vu" :alias 👁 = "vu"

View file

@ -145,7 +145,22 @@ types.anselme = {
return k return k
end end
}, },
["function reference"] = nil, ["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
},
["variable reference"] = {
format = function(val)
return ("&%s"):format(val)
end,
to_lua = nil
},
-- internal types -- internal types
["event buffer"] = { ["event buffer"] = {
format = function(val) -- triggered from subtexts format = function(val) -- triggered from subtexts

View file

@ -0,0 +1,5 @@
:x = 52
:y = &x
{y!}

View file

@ -0,0 +1,14 @@
local _={}
_[5]={}
_[4]={tags=_[5],text="52"}
_[3]={_[4]}
_[2]={"return"}
_[1]={"text",_[3]}
return {_[1],_[2]}
--[[
{ "text", { {
tags = {},
text = "52"
} } }
{ "return" }
]]--