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)`.
* `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]'.
Every type is immutable, except `list`.
@ -835,7 +837,7 @@ This only works on strings:
`a | b`: or operator, lazy
##### Functions
##### Functions and function references
`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.
##### Variable references
`&var`: returns a variable reference to the given variable.
`a!`: returns the value associated with the reference variable.
##### Various
`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
`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.
@ -903,7 +911,7 @@ This only works on strings:
#### 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

View file

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

View file

@ -148,12 +148,18 @@ local function eval(state, exp)
-- variable
elseif exp.type == "variable" then
return get_variable(state, exp.name)
-- function
-- references
elseif exp.type == "function reference" then
return {
type = "function reference",
value = exp.names
}
elseif exp.type == "variable reference" then
return {
type = "variable reference",
value = exp.name
}
-- function
elseif exp.type == "function call" then
-- eval args: list_brackets
local args = {}
@ -372,7 +378,7 @@ local function eval(state, exp)
if r then
ret = r
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
-- untyped raw mode: same as raw, but strips custom types from the arguments
elseif lua_fn.mode == "untyped raw" then
@ -386,7 +392,7 @@ local function eval(state, exp)
if r then
ret = r
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
-- normal mode: convert args to Lua and convert back Lua value to Anselme
elseif lua_fn.mode == nil then

View file

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

View file

@ -190,15 +190,27 @@ local function expression(s, state, namespace, current_priority, operating_on)
local escaped = escape(op)
if s:match("^"..escaped) then
local sright = s:match("^"..escaped.."(.*)$")
-- function reference
-- function and variable reference
if op == "&" and sright:match("^"..identifier_pattern) then
local name, r = sright:match("^("..identifier_pattern..")(.-)$")
name = format_identifier(name)
-- 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)
for i=#nl, 1, -1 do
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)
if #lfnqm > 0 then
if i < #nl then

View file

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

View file

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

View file

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

View file

@ -145,7 +145,22 @@ types.anselme = {
return k
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
["event buffer"] = {
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" }
]]--