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

Improve alias system

This commit is contained in:
Étienne Fildadut 2021-04-08 16:34:33 +02:00
parent b0d7a0bfb5
commit 51e8c82181
6 changed files with 165 additions and 40 deletions

View file

@ -3,6 +3,21 @@ local expression
local escapeCache = {}
local common
--- rewrite name to use defined aliases (under namespace only)
-- namespace should not contain aliases
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))
if aliases[n] then
name_list[i] = aliases[n]:match("[^%.]+$")
end
end
return table.concat(name_list, ".")
end
common = {
--- valid identifier pattern
identifier_pattern = "[^%%%/%*%+%-%(%)%!%&%|%=%$%§%?%>%<%:%{%}%[%]%,%\"]+",
@ -26,24 +41,27 @@ common = {
return address
end,
--- find a variable/function in a list, going up through the namespace hierarchy
find = function(list, namespace, name)
-- will apply aliases
find = function(aliases, list, namespace, name)
local ns = common.split(namespace)
for i=#ns, 1, -1 do
local fqm = ("%s.%s"):format(table.concat(ns, ".", 1, i), name)
local current_namespace = table.concat(ns, ".", 1, i)
local fqm = ("%s.%s"):format(current_namespace, replace_aliases(aliases, current_namespace, name))
if list[fqm] then
return list[fqm], fqm
end
end
-- root namespace
name = replace_aliases(aliases, "", name)
if list[name] then
return list[name], name
end
return nil, ("can't find %q in namespace %s"):format(name, namespace)
end,
--- transform an identifier into a clean version (trim & alias)
--- transform an identifier into a clean version (trim each part)
format_identifier = function(identifier, state)
local r = identifier:gsub("[^%.]+", function(str)
str = common.trim(str)
return state.aliases[str] or str
return common.trim(str)
end)
return r
end,

View file

@ -92,7 +92,7 @@ local function expression(s, state, namespace, currentPriority, operatingOn)
local name, r = s:match("^("..identifier_pattern..")(.-)$")
name = format_identifier(name, state)
-- variables
local var, vfqm = find(state.variables, namespace, name)
local var, vfqm = find(state.aliases, state.variables, namespace, name)
if var then
return expression(r, state, namespace, currentPriority, {
type = "variable",
@ -103,7 +103,7 @@ local function expression(s, state, namespace, currentPriority, operatingOn)
-- suffix call: detect if prefix is valid variable, suffix call is handled in the binop section below
local sname, suffix = name:match("^(.*)(%."..identifier_pattern..")$")
if sname then
local svar, svfqm = find(state.variables, namespace, sname)
local svar, svfqm = find(state.aliases, state.variables, namespace, sname)
if svar then
return expression(suffix..r, state, namespace, currentPriority, {
type = "variable",
@ -113,7 +113,7 @@ local function expression(s, state, namespace, currentPriority, operatingOn)
end
end
-- functions
local funcs, ffqm = find(state.functions, namespace, name)
local funcs, ffqm = find(state.aliases, state.functions, namespace, name)
if funcs then
local args, explicit_call
if r:match("^%b()") then
@ -162,7 +162,7 @@ local function expression(s, state, namespace, currentPriority, operatingOn)
if op == "." and sright:match("^"..identifier_pattern) then
local name, r = sright:match("^("..identifier_pattern..")(.-)$")
name = format_identifier(name, state)
local funcs, ffqm = find(state.functions, namespace, name)
local funcs, ffqm = find(state.aliases, state.functions, namespace, name)
if funcs then
local args, explicit_call
if r:match("^%b()") then

View file

@ -29,9 +29,25 @@ local function parse_line(line, state, namespace)
l, name = l:match("^(.-)%s*§(.-)$")
local identifier, rem = name:match("^("..identifier_pattern..")(.-)$")
if not identifier then return nil, ("no valid identifier in paragraph decorator %q; at %s"):format(identifier, line.source) end
if rem:match("[^%s]") then return nil, ("expected end-of-line after identifier in paragraph decorator, but got %q; at %s"):format(rem, line.source) end
-- format identifier
local fqm = ("%s%s"):format(namespace, format_identifier(identifier, state))
-- get alias
if rem:match("^%:") then
local content = rem:sub(2)
local alias, rem2 = content:match("^("..identifier_pattern..")(.-)$")
if not alias then return nil, ("expected an identifier in alias in paragraph decorator, but got %q; at %s"):format(content, line.source) end
if rem2:match("[^%s]") then return nil, ("expected end-of-line after identifier in alias in paragraph decorator, but got %q; at %s"):format(rem2, line.source) end
-- format alias
local aliasfqm = ("%s%s"):format(namespace, format_identifier(alias, state))
-- define alias
if state.aliases[aliasfqm] ~= nil and state.aliases[aliasfqm] ~= fqm then
return nil, ("trying to define alias %q for variable %q, but already exist and refer to different variable %q; at %s"):format(aliasfqm, fqm, state.aliases[aliasfqm], line.source)
end
state.aliases[aliasfqm] = fqm
elseif rem:match("[^%s]") then
return nil, ("expected end-of-line after identifier in paragraph decorator, but got %q; at %s"):format(rem, line.source)
end
-- define paragraph
namespace = fqm.."."
r.paragraph = true
r.parent_function = true
@ -50,6 +66,15 @@ local function parse_line(line, state, namespace)
value = 0
}
end
-- define alias for 👁️
local seen_alias = state.builtin_aliases["👁️"]
if seen_alias then
local alias = ("%s.%s"):format(fqm, seen_alias)
if state.aliases[alias] ~= nil and state.aliases[alias] then
return nil, ("trying to define alias %q for variable %q, but already exist and refer to different variable %q; at %s"):format(alias, fqm..".👁️", state.aliases[alias], line.source)
end
state.aliases[alias] = fqm..".👁️"
end
else
table.insert(state.functions[fqm], {
arity = 0,
@ -89,6 +114,20 @@ local function parse_line(line, state, namespace)
if not identifier then return nil, ("no valid identifier in paragraph/function definition line %q; at %s"):format(lc, line.source) end
-- format identifier
local fqm = ("%s%s"):format(namespace, format_identifier(identifier, state))
-- get alias
if rem:match("^%:") then
local content = rem:sub(2)
local alias
alias, rem = content:match("^("..identifier_pattern..")(.-)$")
if not alias then return nil, ("expected an identifier in alias in paragraph/function definition line, but got %q; at %s"):format(content, line.source) end
-- format alias
local aliasfqm = ("%s%s"):format(namespace, format_identifier(alias, state))
-- define alias
if state.aliases[aliasfqm] ~= nil and state.aliases[aliasfqm] ~= fqm then
return nil, ("trying to define alias %q for function/paragraph %q, but already exist and refer to %q; at %s"):format(aliasfqm, fqm, state.aliases[aliasfqm], line.source)
end
state.aliases[aliasfqm] = fqm
end
-- get params
r.params = {}
if r.type == "function" and rem:match("^%b()$") then
@ -113,12 +152,6 @@ local function parse_line(line, state, namespace)
-- don't keep function node in block AST
if r.type == "function" then
r.remove_from_block_ast = true
if not state.variables[fqm..".🏁"] then
state.variables[fqm..".🏁"] = {
type = "string",
value = ""
}
end
end
-- define function and variables
r.namespace = fqm.."."
@ -130,14 +163,44 @@ local function parse_line(line, state, namespace)
vararg = vararg,
value = r
}
-- new function (no overloading yet)
if not state.functions[fqm] then
state.functions[fqm] = { r.variant }
-- define 👁️ variable
if not state.variables[fqm..".👁️"] then
state.variables[fqm..".👁️"] = {
type = "number",
value = 0
}
end
-- define alias for 👁️
local seen_alias = state.builtin_aliases["👁️"]
if seen_alias then
local alias = ("%s.%s"):format(fqm, seen_alias)
if state.aliases[alias] ~= nil and state.aliases[alias] then
return nil, ("trying to define alias %q for variable %q, but already exist and refer to different variable %q; at %s"):format(alias, fqm..".👁️", state.aliases[alias], line.source)
end
state.aliases[alias] = fqm..".👁️"
end
if r.type == "function" then
-- define 🏁 variable
if not state.variables[fqm..".🏁"] then
state.variables[fqm..".🏁"] = {
type = "string",
value = ""
}
end
-- define alias for 🏁
local checkpoint_alias = state.builtin_aliases["🏁"]
if checkpoint_alias then
local alias = ("%s.%s"):format(fqm, checkpoint_alias)
if state.aliases[alias] ~= nil and state.aliases[alias] then
return nil, ("trying to define alias %q for variable %q, but already exist and refer to different variable %q; at %s"):format(alias, fqm..".🏁", state.aliases[alias], line.source)
end
state.aliases[alias] = fqm..".🏁"
end
end
-- overloading
else
-- check for arity conflict
for _, variant in ipairs(state.functions[fqm]) do
@ -181,9 +244,25 @@ local function parse_line(line, state, namespace)
-- get identifier
local identifier, rem2 = rem:match("^("..identifier_pattern..")(.-)$")
if not identifier then return nil, ("no valid identifier after expression in definition line %q; at %s"):format(rem, line.source) end
if rem2:match("[^%s]") then return nil, ("expected end-of-line after identifier in definition line, but got %q; at %s"):format(rem2, line.source) end
-- format identifier & define
-- format identifier
local fqm = ("%s%s"):format(namespace, format_identifier(identifier, state))
-- get alias
if rem2:match("^%:") then
local content = rem2:sub(2)
local alias, rem3 = content:match("^("..identifier_pattern..")(.-)$")
if not alias then return nil, ("expected an identifier in alias in definition line, but got %q; at %s"):format(content, line.source) end
if rem3:match("[^%s]") then return nil, ("expected end-of-line after identifier in alias in definition line, but got %q; at %s"):format(rem3, line.source) end
-- format alias
local aliasfqm = ("%s%s"):format(namespace, format_identifier(alias, state))
-- define alias
if state.aliases[aliasfqm] ~= nil and state.aliases[aliasfqm] ~= fqm then
return nil, ("trying to define alias %s for variable %s, but already exist and refer to different variable %s; at %s"):format(aliasfqm, fqm, state.aliases[aliasfqm], line.source)
end
state.aliases[aliasfqm] = fqm
elseif rem2:match("[^%s]") then
return nil, ("expected end-of-line after identifier in definition line, but got %q; at %s"):format(rem2, line.source)
end
-- define identifier
if state.functions[fqm] then return nil, ("trying to define variable %s, but a function with the same name exists; at %s"):format(fqm, line.source) end
if not state.variables[fqm] or state.variables[fqm].type == "undefined argument" then
local v, e = eval(state, exp)