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:
parent
037654ebcf
commit
acb8945dec
11 changed files with 138 additions and 45 deletions
14
REFERENCE.md
14
REFERENCE.md
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -7,4 +7,5 @@ return [[
|
|||
:list="list"
|
||||
:pair="pair"
|
||||
:function reference="function reference"
|
||||
:variable reference="variable reference"
|
||||
]]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
5
test/tests/variable reference get value.ans
Normal file
5
test/tests/variable reference get value.ans
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
:x = 52
|
||||
|
||||
:y = &x
|
||||
|
||||
{y!}
|
||||
14
test/tests/variable reference get value.lua
Normal file
14
test/tests/variable reference get value.lua
Normal 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" }
|
||||
]]--
|
||||
Loading…
Add table
Add a link
Reference in a new issue