mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-27 16:49:31 +00:00
Rename overloaded operators (add underscores)
* add suffix unary operators * add _! operator * _._ and _&_ operator overloading * allow calling function references with _! and access variables with _._ * rename . method operator to ! * various cleaning & improvements Documentation will follow at some point.
This commit is contained in:
parent
d5b1a9f225
commit
9bd9759115
16 changed files with 350 additions and 178 deletions
|
|
@ -164,10 +164,15 @@ local function eval(state, exp)
|
||||||
end
|
end
|
||||||
-- function reference: call the referenced function
|
-- function reference: call the referenced function
|
||||||
local variants = exp.variants
|
local variants = exp.variants
|
||||||
if exp.called_name == "()" and args[1].type == "function reference" then
|
local paren_call = exp.paren_call
|
||||||
|
if args[1] and args[1].type == "function reference" and (exp.called_name == "()" or exp.called_name == "_!") then
|
||||||
-- remove func ref as first arg
|
-- remove func ref as first arg
|
||||||
local refv = args[1].value
|
local refv = args[1].value
|
||||||
table.remove(args, 1)
|
table.remove(args, 1)
|
||||||
|
-- set paren_call for _!
|
||||||
|
if exp.called_name == "_!" then
|
||||||
|
paren_call = false
|
||||||
|
end
|
||||||
-- get variants of the referenced function
|
-- get variants of the referenced function
|
||||||
variants = {}
|
variants = {}
|
||||||
for _, ffqm in ipairs(refv) do
|
for _, ffqm in ipairs(refv) do
|
||||||
|
|
@ -333,7 +338,7 @@ local function eval(state, exp)
|
||||||
if selected_variant.variant then
|
if selected_variant.variant then
|
||||||
local fn = selected_variant.variant
|
local fn = selected_variant.variant
|
||||||
if fn.type == "checkpoint" then
|
if fn.type == "checkpoint" then
|
||||||
local r, e = run(state, fn.child, not exp.explicit_call)
|
local r, e = run(state, fn.child, not paren_call)
|
||||||
if not r then return r, e end
|
if not r then return r, e end
|
||||||
return r
|
return r
|
||||||
elseif fn.type == "function" then
|
elseif fn.type == "function" then
|
||||||
|
|
@ -361,9 +366,14 @@ local function eval(state, exp)
|
||||||
final_args[#final_args+1] = v
|
final_args[#final_args+1] = v
|
||||||
end
|
end
|
||||||
-- execute function
|
-- execute function
|
||||||
-- raw mode: pass raw anselme values to the Lua function
|
-- raw mode: pass raw anselme values to the Lua function; support return nil, err in case of error
|
||||||
if lua_fn.mode == "raw" then
|
if lua_fn.mode == "raw" then
|
||||||
ret = lua_fn.value(unpack(final_args))
|
local r, e = lua_fn.value(unpack(final_args))
|
||||||
|
if r then
|
||||||
|
ret = r
|
||||||
|
else
|
||||||
|
return nil, ("%s; in Lua function %q"):format(e, exp.called_name)
|
||||||
|
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
|
||||||
-- extract value from custom types
|
-- extract value from custom types
|
||||||
|
|
@ -372,7 +382,12 @@ local function eval(state, exp)
|
||||||
final_args[i] = arg.value[1]
|
final_args[i] = arg.value[1]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
ret = lua_fn.value(unpack(final_args))
|
local r, e = lua_fn.value(unpack(final_args))
|
||||||
|
if r then
|
||||||
|
ret = r
|
||||||
|
else
|
||||||
|
return nil, ("%s; in Lua function %q"):format(e, exp.called_name)
|
||||||
|
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
|
||||||
local l_lua = {}
|
local l_lua = {}
|
||||||
|
|
@ -397,7 +412,7 @@ local function eval(state, exp)
|
||||||
else
|
else
|
||||||
local e
|
local e
|
||||||
-- eval function from start
|
-- eval function from start
|
||||||
if exp.explicit_call or checkpoint.value == "" then
|
if paren_call or checkpoint.value == "" then
|
||||||
ret, e = run(state, fn.child)
|
ret, e = run(state, fn.child)
|
||||||
-- resume at last checkpoint
|
-- resume at last checkpoint
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -25,20 +25,28 @@ common = {
|
||||||
identifier_pattern = "%s*[^0-9%s"..disallowed_set.."][^"..disallowed_set.."]*",
|
identifier_pattern = "%s*[^0-9%s"..disallowed_set.."][^"..disallowed_set.."]*",
|
||||||
-- names allowed for a function that aren't valid identifiers, mainly for overloading operators
|
-- names allowed for a function that aren't valid identifiers, mainly for overloading operators
|
||||||
special_functions_names = {
|
special_functions_names = {
|
||||||
-- operators not included here:
|
-- operators not included here and why:
|
||||||
-- * assignment operators (:=, +=, -=, //=, /=, *=, %=, ^=): handled with its own syntax (function assignment)
|
-- * assignment operators (:=, +=, -=, //=, /=, *=, %=, ^=): handled with its own syntax (function assignment)
|
||||||
-- * list operator (,): is used when calling every functions, sounds like more trouble than it's worth
|
-- * list operator (,): is used when calling every functions, sounds like more trouble than it's worth
|
||||||
-- * |, & and ~ operators: are lazy and don't behave like regular functions
|
-- * |, & and ~ operators: are lazy and don't behave like regular functions
|
||||||
-- * # operator: need to set tag state before evaluating the left arg
|
-- * # operator: need to set tag state _before_ evaluating the left arg
|
||||||
-- * . operator: don't behave like regular functions either
|
|
||||||
";",
|
-- prefix unop
|
||||||
"!=", "==", ">=", "<=", "<", ">",
|
"-_", "!_",
|
||||||
"+", "-",
|
"&_",
|
||||||
"*", "//", "/", "%",
|
-- binop
|
||||||
"::", ":",
|
"_;_",
|
||||||
"!",
|
"_!=_", "_==_", "_>=_", "_<=_", "_<_", "_>_",
|
||||||
"^",
|
"_+_", "_-_",
|
||||||
"()", "{}"
|
"_*_", "_//_", "_/_", "_%_",
|
||||||
|
"_::_", "_:_",
|
||||||
|
"_^_",
|
||||||
|
"_._", "_!_",
|
||||||
|
-- suffix unop
|
||||||
|
"_!",
|
||||||
|
-- special
|
||||||
|
"()",
|
||||||
|
"{}"
|
||||||
},
|
},
|
||||||
-- escapement code and their value in strings
|
-- escapement code and their value in strings
|
||||||
-- I don't think there's a point in supporting form feed, carriage return, and other printer and terminal related codes
|
-- I don't think there's a point in supporting form feed, carriage return, and other printer and terminal related codes
|
||||||
|
|
@ -248,7 +256,7 @@ common = {
|
||||||
--- same as find_function_variant_from_fqm, but will search every function from the current namespace and up using find
|
--- same as find_function_variant_from_fqm, but will search every function from the current namespace and up using find
|
||||||
-- returns directly a function expression in case of success
|
-- returns directly a function expression in case of success
|
||||||
-- return nil, err otherwise
|
-- return nil, err otherwise
|
||||||
find_function = function(state, namespace, name, arg, explicit_call)
|
find_function = function(state, namespace, name, arg, paren_call, implicit_call)
|
||||||
local variants = {}
|
local variants = {}
|
||||||
local err = ("compatible function %q variant not found"):format(name)
|
local err = ("compatible function %q variant not found"):format(name)
|
||||||
local l = common.find_all(state.aliases, state.functions, namespace, name)
|
local l = common.find_all(state.aliases, state.functions, namespace, name)
|
||||||
|
|
@ -264,9 +272,10 @@ common = {
|
||||||
if #variants > 0 then
|
if #variants > 0 then
|
||||||
return {
|
return {
|
||||||
type = "function call",
|
type = "function call",
|
||||||
called_name = name,
|
called_name = name, -- name of the called function
|
||||||
explicit_call = explicit_call,
|
paren_call = paren_call, -- was call with parantheses?
|
||||||
variants = variants,
|
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)
|
argument = { -- wrap everything in a list literal to simplify later things (otherwise may be nil, single value, list constructor)
|
||||||
type = "list_brackets",
|
type = "list_brackets",
|
||||||
expression = arg
|
expression = arg
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
local identifier_pattern, format_identifier, find, escape, find_function, parse_text, find_all
|
local identifier_pattern, format_identifier, find, escape, find_function, parse_text, find_all, split
|
||||||
|
|
||||||
--- binop priority
|
--- binop priority
|
||||||
local binops_prio = {
|
local binops_prio = {
|
||||||
|
|
@ -12,10 +12,11 @@ local binops_prio = {
|
||||||
[8] = { "::", ":" },
|
[8] = { "::", ":" },
|
||||||
[9] = {}, -- unary operators
|
[9] = {}, -- unary operators
|
||||||
[10] = { "^" },
|
[10] = { "^" },
|
||||||
[11] = { "." }
|
[11] = { ".", "!" },
|
||||||
|
[12] = {}
|
||||||
}
|
}
|
||||||
-- unop priority
|
-- unop priority
|
||||||
local unops_prio = {
|
local prefix_unops_prio = {
|
||||||
[1] = {},
|
[1] = {},
|
||||||
[2] = {},
|
[2] = {},
|
||||||
[3] = {},
|
[3] = {},
|
||||||
|
|
@ -27,6 +28,21 @@ local unops_prio = {
|
||||||
[9] = { "-", "!" },
|
[9] = { "-", "!" },
|
||||||
[10] = {},
|
[10] = {},
|
||||||
[11] = {},
|
[11] = {},
|
||||||
|
[12] = { "&" }
|
||||||
|
}
|
||||||
|
local suffix_unops_prio = {
|
||||||
|
[1] = {},
|
||||||
|
[2] = {},
|
||||||
|
[3] = {},
|
||||||
|
[4] = {},
|
||||||
|
[5] = {},
|
||||||
|
[6] = {},
|
||||||
|
[7] = {},
|
||||||
|
[8] = {},
|
||||||
|
[9] = {},
|
||||||
|
[10] = {},
|
||||||
|
[11] = { "!" },
|
||||||
|
[12] = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
local function get_text_in_litteral(s, start_pos)
|
local function get_text_in_litteral(s, start_pos)
|
||||||
|
|
@ -132,33 +148,30 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
right = val
|
right = val
|
||||||
}
|
}
|
||||||
-- find compatible variant
|
-- find compatible variant
|
||||||
local variant, err = find_function(state, namespace, ":", args, true)
|
local variant, err = find_function(state, namespace, "_:_", args, true)
|
||||||
if not variant then return variant, err end
|
if not variant then return variant, err end
|
||||||
return expression(r, state, namespace, current_priority, variant)
|
return expression(r, state, namespace, current_priority, variant)
|
||||||
end
|
end
|
||||||
-- variables
|
-- variables
|
||||||
local var, vfqm = find(state.aliases, state.variables, namespace, name)
|
-- if name isn't a valid variable, suffix call: detect if a prefix is valid variable, suffix _._ call is handled in the binop section below
|
||||||
if var then
|
local nl = split(name)
|
||||||
return expression(r, state, namespace, current_priority, {
|
for i=#nl, 1, -1 do
|
||||||
type = "variable",
|
local name_prefix = table.concat(nl, ".", 1, i)
|
||||||
name = vfqm
|
local var, vfqm = find(state.aliases, state.variables, namespace, name_prefix)
|
||||||
})
|
if var then
|
||||||
end
|
if i < #nl then
|
||||||
-- suffix call: detect if prefix is valid variable, suffix call is handled in the binop section below
|
r = "."..table.concat(nl, ".", i+1, #nl)..r
|
||||||
local sname, suffix = name:match("^(.*)(%."..identifier_pattern..")$")
|
end
|
||||||
if sname then
|
return expression(r, state, namespace, current_priority, {
|
||||||
local svar, svfqm = find(state.aliases, state.variables, namespace, sname)
|
|
||||||
if svar then
|
|
||||||
return expression(suffix..r, state, namespace, current_priority, {
|
|
||||||
type = "variable",
|
type = "variable",
|
||||||
name = svfqm
|
name = vfqm
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- function call
|
-- function call
|
||||||
local args, explicit_call
|
local args, paren_call, implicit_call
|
||||||
if r:match("^%b()") then
|
if r:match("^%b()") then
|
||||||
explicit_call = true
|
paren_call = true
|
||||||
local content, rem = r:match("^(%b())(.*)$")
|
local content, rem = r:match("^(%b())(.*)$")
|
||||||
content = content:gsub("^%(", ""):gsub("%)$", "")
|
content = content:gsub("^%(", ""):gsub("%)$", "")
|
||||||
r = rem
|
r = rem
|
||||||
|
|
@ -169,38 +182,50 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
if not args then return args, err end
|
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 list"):format(err) end
|
||||||
end
|
end
|
||||||
elseif r:match("^%!") then -- optional, to call with no arg, no explicit call
|
else -- implicit call; will be changed if there happens to be a ! after in the suffix operator code
|
||||||
r = r:match("^%!(.*)$")
|
implicit_call = true
|
||||||
end
|
end
|
||||||
-- find compatible variant
|
-- find compatible variant
|
||||||
local variant, err = find_function(state, namespace, name, args, explicit_call)
|
local variant, err = find_function(state, namespace, name, args, paren_call, implicit_call)
|
||||||
if not variant then return variant, err end
|
if not variant then return variant, err end
|
||||||
return expression(r, state, namespace, current_priority, variant)
|
return expression(r, state, namespace, current_priority, variant)
|
||||||
-- function reference
|
|
||||||
elseif s:match("^%&"..identifier_pattern) then
|
|
||||||
local name, r = s:match("^%&("..identifier_pattern..")(.-)$")
|
|
||||||
name = format_identifier(name)
|
|
||||||
-- get all functions this name can reference
|
|
||||||
local lfnqm = find_all(state.aliases, state.functions, namespace, name)
|
|
||||||
if #lfnqm > 0 then
|
|
||||||
return expression(r, state, namespace, current_priority, {
|
|
||||||
type = "function reference",
|
|
||||||
names = lfnqm
|
|
||||||
})
|
|
||||||
end
|
|
||||||
return nil, ("can't find function %q to reference"):format(name)
|
|
||||||
end
|
end
|
||||||
-- unops
|
-- prefix unops
|
||||||
for prio, oplist in ipairs(unops_prio) do
|
for prio, oplist in ipairs(prefix_unops_prio) do
|
||||||
for _, op in ipairs(oplist) do
|
for _, op in ipairs(oplist) do
|
||||||
local escaped = escape(op)
|
local escaped = escape(op)
|
||||||
if s:match("^"..escaped) then
|
if s:match("^"..escaped) then
|
||||||
local right, r = expression(s:match("^"..escaped.."(.*)$"), state, namespace, prio)
|
local sright = s:match("^"..escaped.."(.*)$")
|
||||||
if not right then return nil, ("invalid expression after unop %q: %s"):format(op, r) end
|
-- function reference
|
||||||
-- find variant
|
if op == "&" and sright:match("^"..identifier_pattern) then
|
||||||
local variant, err = find_function(state, namespace, op, right, true)
|
local name, r = sright:match("^("..identifier_pattern..")(.-)$")
|
||||||
if not variant then return variant, err end
|
name = format_identifier(name)
|
||||||
return expression(r, state, namespace, current_priority, variant)
|
-- get all functions this name can reference
|
||||||
|
-- try prefixes until we find a valid function name
|
||||||
|
local nl = split(name)
|
||||||
|
for i=#nl, 1, -1 do
|
||||||
|
local name_prefix = table.concat(nl, ".", 1, i)
|
||||||
|
local lfnqm = find_all(state.aliases, state.functions, namespace, name_prefix)
|
||||||
|
if #lfnqm > 0 then
|
||||||
|
if i < #nl then
|
||||||
|
r = "."..table.concat(nl, ".", i+1, #nl)..r
|
||||||
|
end
|
||||||
|
return expression(r, state, namespace, current_priority, {
|
||||||
|
type = "function reference",
|
||||||
|
names = lfnqm
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil, ("can't find function %q to reference"):format(name)
|
||||||
|
-- normal prefix unop
|
||||||
|
else
|
||||||
|
local right, r = expression(sright, state, namespace, prio)
|
||||||
|
if not right then return nil, ("invalid expression after unop %q: %s"):format(op, r) end
|
||||||
|
-- find variant
|
||||||
|
local variant, err = find_function(state, namespace, op.."_", right, true)
|
||||||
|
if not variant then return variant, err end
|
||||||
|
return expression(r, state, namespace, current_priority, variant)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -214,12 +239,12 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
if s:match("^"..escaped) then
|
if s:match("^"..escaped) then
|
||||||
local sright = s:match("^"..escaped.."(.*)$")
|
local sright = s:match("^"..escaped.."(.*)$")
|
||||||
-- suffix call
|
-- suffix call
|
||||||
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)
|
||||||
local args, explicit_call
|
local args, paren_call
|
||||||
if r:match("^%b()") then
|
if r:match("^%b()") then
|
||||||
explicit_call = true
|
paren_call = true
|
||||||
local content, rem = r:match("^(%b())(.*)$")
|
local content, rem = r:match("^(%b())(.*)$")
|
||||||
content = content:gsub("^%(", ""):gsub("%)$", "")
|
content = content:gsub("^%(", ""):gsub("%)$", "")
|
||||||
r = rem
|
r = rem
|
||||||
|
|
@ -242,101 +267,127 @@ local function expression(s, state, namespace, current_priority, operating_on)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
-- find compatible variant
|
-- find compatible variant
|
||||||
local variant, err = find_function(state, namespace, name, args, explicit_call)
|
local variant, err = find_function(state, namespace, name, args, paren_call)
|
||||||
|
if not variant then return variant, err end
|
||||||
|
return expression(r, state, namespace, current_priority, variant)
|
||||||
|
-- namespace
|
||||||
|
elseif op == "." and sright:match("^"..identifier_pattern) then
|
||||||
|
local name, r = sright:match("^("..identifier_pattern..")(.-)$")
|
||||||
|
name = format_identifier(name)
|
||||||
|
-- find variant
|
||||||
|
local args = {
|
||||||
|
type = "list",
|
||||||
|
left = operating_on,
|
||||||
|
right = { type = "string", text = { name } }
|
||||||
|
}
|
||||||
|
local variant, err = find_function(state, namespace, "_._", args, true)
|
||||||
if not variant then return variant, err end
|
if not variant then return variant, err end
|
||||||
return expression(r, state, namespace, current_priority, variant)
|
return expression(r, state, namespace, current_priority, variant)
|
||||||
-- other binops
|
-- other binops
|
||||||
else
|
else
|
||||||
local right, r = expression(sright, state, namespace, prio)
|
local right, r = expression(sright, state, namespace, prio)
|
||||||
if not right then return nil, ("invalid expression after binop %q: %s"):format(op, r) end
|
if right then
|
||||||
-- list constructor
|
-- list constructor (can't do this through a function call since we need to build a list for its arguments)
|
||||||
if op == "," then
|
if op == "," then
|
||||||
return expression(r, state, namespace, current_priority, {
|
return expression(r, state, namespace, current_priority, {
|
||||||
type = "list",
|
type = "list",
|
||||||
left = operating_on,
|
left = operating_on,
|
||||||
right = right
|
right = right
|
||||||
})
|
})
|
||||||
-- special binops
|
-- special binops
|
||||||
elseif op == ":=" or op == "+=" or op == "-=" or op == "//=" or op == "/=" or op == "*=" or op == "%=" or op == "^=" then
|
elseif op == ":=" or op == "+=" or op == "-=" or op == "//=" or op == "/=" or op == "*=" or op == "%=" or op == "^=" then
|
||||||
-- rewrite assignment + arithmetic operators into a normal assignment
|
-- rewrite assignment + arithmetic operators into a normal assignment
|
||||||
if op ~= ":=" then
|
if op ~= ":=" then
|
||||||
|
local args = {
|
||||||
|
type = "list",
|
||||||
|
left = operating_on,
|
||||||
|
right = right
|
||||||
|
}
|
||||||
|
local variant, err = find_function(state, namespace, "_"..op:match("^(.*)%=$").."_", args, true)
|
||||||
|
if not variant then return variant, err end
|
||||||
|
right = variant
|
||||||
|
end
|
||||||
|
-- assign to a function
|
||||||
|
if operating_on.type == "function call" then
|
||||||
|
-- remove non-assignment functions
|
||||||
|
for i=#operating_on.variants, 1, -1 do
|
||||||
|
if not operating_on.variants[i].assignment then
|
||||||
|
table.remove(operating_on.variants, i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #operating_on.variants == 0 then
|
||||||
|
return nil, ("trying to perform assignment on function %s with no compatible assignment variant"):format(operating_on.called_name)
|
||||||
|
end
|
||||||
|
-- rewrite function to perform assignment
|
||||||
|
operating_on.assignment = right
|
||||||
|
return expression(r, state, namespace, current_priority, operating_on)
|
||||||
|
elseif operating_on.type ~= "variable" then
|
||||||
|
return nil, ("trying to perform assignment on a %s expression"):format(operating_on.type)
|
||||||
|
end
|
||||||
|
-- assign to a variable
|
||||||
|
return expression(r, state, namespace, current_priority, {
|
||||||
|
type = ":=",
|
||||||
|
left = operating_on,
|
||||||
|
right = right
|
||||||
|
})
|
||||||
|
elseif op == "&" or op == "|" or op == "~" or op == "#" then
|
||||||
|
return expression(r, state, namespace, current_priority, {
|
||||||
|
type = op,
|
||||||
|
left = operating_on,
|
||||||
|
right = right
|
||||||
|
})
|
||||||
|
-- normal binop
|
||||||
|
else
|
||||||
|
-- find variant
|
||||||
local args = {
|
local args = {
|
||||||
type = "list",
|
type = "list",
|
||||||
left = operating_on,
|
left = operating_on,
|
||||||
right = right
|
right = right
|
||||||
}
|
}
|
||||||
local variant, err = find_function(state, namespace, op:match("^(.*)%=$"), args, true)
|
local variant, err = find_function(state, namespace, "_"..op.."_", args, true)
|
||||||
if not variant then return variant, err end
|
if not variant then return variant, err end
|
||||||
right = variant
|
return expression(r, state, namespace, current_priority, variant)
|
||||||
end
|
end
|
||||||
-- assign to a function
|
|
||||||
if operating_on.type == "function call" then
|
|
||||||
-- remove non-assignment functions
|
|
||||||
for i=#operating_on.variants, 1, -1 do
|
|
||||||
if not operating_on.variants[i].assignment then
|
|
||||||
table.remove(operating_on.variants, i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if #operating_on.variants == 0 then
|
|
||||||
return nil, ("trying to perform assignment on function %s with no compatible assignment variant"):format(operating_on.called_name)
|
|
||||||
end
|
|
||||||
-- rewrite function to perform assignment
|
|
||||||
operating_on.assignment = right
|
|
||||||
return expression(r, state, namespace, current_priority, operating_on)
|
|
||||||
elseif operating_on.type ~= "variable" then
|
|
||||||
return nil, ("trying to perform assignment on a %s expression"):format(operating_on.type)
|
|
||||||
end
|
|
||||||
-- assign to a variable
|
|
||||||
return expression(r, state, namespace, current_priority, {
|
|
||||||
type = ":=",
|
|
||||||
left = operating_on,
|
|
||||||
right = right
|
|
||||||
})
|
|
||||||
elseif op == "&" or op == "|" or op == "~" or op == "#" then
|
|
||||||
return expression(r, state, namespace, current_priority, {
|
|
||||||
type = op,
|
|
||||||
left = operating_on,
|
|
||||||
right = right
|
|
||||||
})
|
|
||||||
-- normal binop
|
|
||||||
else
|
|
||||||
-- find variant
|
|
||||||
local args = {
|
|
||||||
type = "list",
|
|
||||||
left = operating_on,
|
|
||||||
right = right
|
|
||||||
}
|
|
||||||
local variant, err = find_function(state, namespace, op, args, true)
|
|
||||||
if not variant then return variant, err end
|
|
||||||
return expression(r, state, namespace, current_priority, variant)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- index / call
|
-- suffix unop
|
||||||
if s:match("^%b()") or s:match("^%!") then
|
for prio, oplist in ipairs(suffix_unops_prio) do
|
||||||
local args = operating_on
|
if prio > current_priority then
|
||||||
local explicit_call, r
|
for _, op in ipairs(oplist) do
|
||||||
-- call with args, explicit call
|
local escaped = escape(op)
|
||||||
if s:match("^%b()") then
|
if s:match("^"..escaped) then
|
||||||
explicit_call = true
|
local r = s:match("^"..escaped.."(.*)$")
|
||||||
local content
|
-- remove ! after a previously-assumed implicit function call
|
||||||
content, r = s:match("^(%b())(.*)$")
|
if op == "!" and operating_on.type == "function call" and operating_on.implicit_call then
|
||||||
content = content:gsub("^%(", ""):gsub("%)$", "")
|
operating_on.implicit_call = false
|
||||||
-- get arguments
|
return expression(r, state, namespace, current_priority, operating_on)
|
||||||
if content:match("[^%s]") then
|
-- normal suffix unop
|
||||||
local right, r_paren = expression(content, state, namespace)
|
else
|
||||||
if not right then return right, r_paren end
|
local variant, err = find_function(state, namespace, "_"..op, operating_on, true)
|
||||||
if r_paren:match("[^%s]") then return nil, ("unexpected %q at end of index/call expression"):format(r_paren) end
|
if not variant then return variant, err end
|
||||||
args = { type = "list", left = args, right = right }
|
return expression(r, state, namespace, current_priority, variant)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
-- call with no arg, no explicit call
|
|
||||||
elseif s:match("^%!") then
|
|
||||||
r = s:match("^%!(.*)$")
|
|
||||||
end
|
end
|
||||||
local variant, err = find_function(state, namespace, "()", args, explicit_call)
|
end
|
||||||
|
-- index / call
|
||||||
|
if s:match("^%b()") then
|
||||||
|
local args = operating_on
|
||||||
|
local content, r = s:match("^(%b())(.*)$")
|
||||||
|
content = content:gsub("^%(", ""):gsub("%)$", "")
|
||||||
|
-- get arguments
|
||||||
|
if content:match("[^%s]") then
|
||||||
|
local right, r_paren = expression(content, state, namespace)
|
||||||
|
if not right then return right, r_paren end
|
||||||
|
if r_paren:match("[^%s]") then return nil, ("unexpected %q at end of index/call expression"):format(r_paren) end
|
||||||
|
args = { type = "list", left = args, right = right }
|
||||||
|
end
|
||||||
|
local variant, err = find_function(state, namespace, "()", args, true)
|
||||||
if not variant then return variant, err end
|
if not variant then return variant, err end
|
||||||
return expression(r, state, namespace, current_priority, variant)
|
return expression(r, state, namespace, current_priority, variant)
|
||||||
end
|
end
|
||||||
|
|
@ -347,6 +398,6 @@ end
|
||||||
|
|
||||||
package.loaded[...] = expression
|
package.loaded[...] = expression
|
||||||
local common = require((...):gsub("expression$", "common"))
|
local common = require((...):gsub("expression$", "common"))
|
||||||
identifier_pattern, format_identifier, find, escape, find_function, parse_text, find_all = common.identifier_pattern, common.format_identifier, common.find, common.escape, common.find_function, common.parse_text, common.find_all
|
identifier_pattern, format_identifier, find, escape, find_function, parse_text, find_all, split = common.identifier_pattern, common.format_identifier, common.find, common.escape, common.find_function, common.parse_text, common.find_all, common.split
|
||||||
|
|
||||||
return expression
|
return expression
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ local function parse(state)
|
||||||
end
|
end
|
||||||
param.default = default_exp
|
param.default = default_exp
|
||||||
-- extract type annotation from default value
|
-- extract type annotation from default value
|
||||||
if default_exp.type == "function call" and default_exp.called_name == "::" then
|
if default_exp.type == "function call" and default_exp.called_name == "_::_" then
|
||||||
param.type_annotation = default_exp.argument.expression.right
|
param.type_annotation = default_exp.argument.expression.right
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
local truthy, anselme, compare, is_of_type, identifier_pattern, format_identifier
|
local truthy, anselme, compare, is_of_type, identifier_pattern, format_identifier, find, get_variable
|
||||||
|
|
||||||
local functions
|
local functions
|
||||||
functions = {
|
functions = {
|
||||||
-- discard left
|
-- discard left
|
||||||
[";(a, b)"] = {
|
["_;_(a, b)"] = {
|
||||||
mode = "raw",
|
mode = "raw",
|
||||||
value = function(a, b) return b end
|
value = function(a, b) return b end
|
||||||
},
|
},
|
||||||
-- comparaison
|
-- comparaison
|
||||||
["==(a, b)"] = {
|
["_==_(a, b)"] = {
|
||||||
mode = "raw",
|
mode = "raw",
|
||||||
value = function(a, b)
|
value = function(a, b)
|
||||||
return {
|
return {
|
||||||
|
|
@ -17,7 +17,7 @@ functions = {
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
["!=(a, b)"] = {
|
["_!=_(a, b)"] = {
|
||||||
mode = "raw",
|
mode = "raw",
|
||||||
value = function(a, b)
|
value = function(a, b)
|
||||||
return {
|
return {
|
||||||
|
|
@ -26,21 +26,21 @@ functions = {
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
[">(a::number, b::number)"] = function(a, b) return a > b end,
|
["_>_(a::number, b::number)"] = function(a, b) return a > b end,
|
||||||
["<(a::number, b::number)"] = function(a, b) return a < b end,
|
["_<_(a::number, b::number)"] = function(a, b) return a < b end,
|
||||||
[">=(a::number, b::number)"] = function(a, b) return a >= b end,
|
["_>=_(a::number, b::number)"] = function(a, b) return a >= b end,
|
||||||
["<=(a::number, b::number)"] = function(a, b) return a <= b end,
|
["_<=_(a::number, b::number)"] = function(a, b) return a <= b end,
|
||||||
-- arithmetic
|
-- arithmetic
|
||||||
["+(a::number, b::number)"] = function(a, b) return a + b end,
|
["_+_(a::number, b::number)"] = function(a, b) return a + b end,
|
||||||
["+(a::string, b::string)"] = function(a, b) return a .. b end,
|
["_+_(a::string, b::string)"] = function(a, b) return a .. b end,
|
||||||
["-(a::number, b::number)"] = function(a, b) return a - b end,
|
["_-_(a::number, b::number)"] = function(a, b) return a - b end,
|
||||||
["-(a::number)"] = function(a) return -a end,
|
["-_(a::number)"] = function(a) return -a end,
|
||||||
["*(a::number, b::number)"] = function(a, b) return a * b end,
|
["_*_(a::number, b::number)"] = function(a, b) return a * b end,
|
||||||
["/(a::number, b::number)"] = function(a, b) return a / b end,
|
["_/_(a::number, b::number)"] = function(a, b) return a / b end,
|
||||||
["//(a::number, b::number)"] = function(a, b) return math.floor(a / b) end,
|
["_//_(a::number, b::number)"] = function(a, b) return math.floor(a / b) end,
|
||||||
["^(a::number, b::number)"] = function(a, b) return a ^ b end,
|
["_^_(a::number, b::number)"] = function(a, b) return a ^ b end,
|
||||||
-- boolean
|
-- boolean
|
||||||
["!(a)"] = {
|
["!_(a)"] = {
|
||||||
mode = "raw",
|
mode = "raw",
|
||||||
value = function(a)
|
value = function(a)
|
||||||
return {
|
return {
|
||||||
|
|
@ -50,7 +50,7 @@ functions = {
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
-- pair
|
-- pair
|
||||||
[":(a, b)"] = {
|
["_:_(a, b)"] = {
|
||||||
mode = "raw",
|
mode = "raw",
|
||||||
value = function(a, b)
|
value = function(a, b)
|
||||||
return {
|
return {
|
||||||
|
|
@ -60,7 +60,7 @@ functions = {
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
-- type
|
-- type
|
||||||
["::(a, b)"] = {
|
["_::_(a, b)"] = {
|
||||||
mode = "raw",
|
mode = "raw",
|
||||||
value = function(a, b)
|
value = function(a, b)
|
||||||
return {
|
return {
|
||||||
|
|
@ -69,6 +69,22 @@ functions = {
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
|
-- namespace
|
||||||
|
["_._(r::function reference, name::string)"] = {
|
||||||
|
mode = "raw",
|
||||||
|
value = function(r, n)
|
||||||
|
local state = anselme.running.state
|
||||||
|
local rval = r.value
|
||||||
|
local name = n.value
|
||||||
|
for _, ffqm in ipairs(rval) do
|
||||||
|
local var, vfqm = find(state.aliases, state.variables, ffqm..".", name)
|
||||||
|
if var then
|
||||||
|
return get_variable(state, vfqm)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil, ("can't find variable %q in function reference (searched in namespaces: %s)"):format(name, table.concat(rval, ", "))
|
||||||
|
end
|
||||||
|
},
|
||||||
-- index
|
-- index
|
||||||
["()(l::list, i::number)"] = {
|
["()(l::list, i::number)"] = {
|
||||||
mode = "untyped raw",
|
mode = "untyped raw",
|
||||||
|
|
@ -122,6 +138,9 @@ 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...)"] = {
|
||||||
|
-- bypassed, this case is manually handled in the expression interpreter
|
||||||
|
},
|
||||||
-- format
|
-- format
|
||||||
["{}(v)"] = {
|
["{}(v)"] = {
|
||||||
mode = "raw",
|
mode = "raw",
|
||||||
|
|
@ -281,7 +300,7 @@ functions = {
|
||||||
|
|
||||||
package.loaded[...] = functions
|
package.loaded[...] = functions
|
||||||
local icommon = require((...):gsub("stdlib%.functions$", "interpreter.common"))
|
local icommon = require((...):gsub("stdlib%.functions$", "interpreter.common"))
|
||||||
truthy, compare, is_of_type = icommon.truthy, icommon.compare, icommon.is_of_type
|
truthy, compare, is_of_type, get_variable = icommon.truthy, icommon.compare, icommon.is_of_type, icommon.get_variable
|
||||||
local pcommon = require((...):gsub("stdlib%.functions$", "parser.common"))
|
local pcommon = require((...):gsub("stdlib%.functions$", "parser.common"))
|
||||||
identifier_pattern, format_identifier = pcommon.identifier_pattern, pcommon.format_identifier
|
identifier_pattern, format_identifier, find = pcommon.identifier_pattern, pcommon.format_identifier, pcommon.find
|
||||||
anselme = require((...):gsub("stdlib%.functions$", "anselme"))
|
anselme = require((...):gsub("stdlib%.functions$", "anselme"))
|
||||||
|
|
|
||||||
6
stdlib/languages/enUS.lua
Normal file
6
stdlib/languages/enUS.lua
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
return [[
|
||||||
|
(Built-in variables)
|
||||||
|
:alias 👁️ = "seen"
|
||||||
|
:alias 🔖 = "checkpoint"
|
||||||
|
:alias 🏁 = "reached"
|
||||||
|
]]
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
$ -(a, b)
|
$ _-_(a, b)
|
||||||
@"generic minus"
|
@"generic minus"
|
||||||
|
|
||||||
$ -(a::string, b::string)
|
$ _-_(a::string, b::string)
|
||||||
@a + " minus " + b
|
@a + " minus " + b
|
||||||
|
|
||||||
{2-5}
|
{2-5}
|
||||||
|
|
|
||||||
7
test/tests/binop assignement.ans
Normal file
7
test/tests/binop assignement.ans
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
:c = 1
|
||||||
|
|
||||||
|
{c}
|
||||||
|
|
||||||
|
~ c += 2
|
||||||
|
|
||||||
|
{c}
|
||||||
22
test/tests/binop assignement.lua
Normal file
22
test/tests/binop assignement.lua
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
local _={}
|
||||||
|
_[9]={}
|
||||||
|
_[8]={}
|
||||||
|
_[7]={tags=_[9],text="3"}
|
||||||
|
_[6]={tags=_[8],text="1"}
|
||||||
|
_[5]={_[7]}
|
||||||
|
_[4]={_[6]}
|
||||||
|
_[3]={"return"}
|
||||||
|
_[2]={"text",_[5]}
|
||||||
|
_[1]={"text",_[4]}
|
||||||
|
return {_[1],_[2],_[3]}
|
||||||
|
--[[
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "1"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "3"
|
||||||
|
} } }
|
||||||
|
{ "return" }
|
||||||
|
]]--
|
||||||
|
|
@ -4,19 +4,19 @@
|
||||||
|
|
||||||
1,2: {l}
|
1,2: {l}
|
||||||
|
|
||||||
~ l.insert(3)
|
~ l!insert(3)
|
||||||
|
|
||||||
1,2,3: {l}
|
1,2,3: {l}
|
||||||
|
|
||||||
§ a
|
§ a
|
||||||
|
|
||||||
~ l.insert(4)
|
~ l!insert(4)
|
||||||
|
|
||||||
1,2,3,4: {l}
|
1,2,3,4: {l}
|
||||||
|
|
||||||
§ b
|
§ b
|
||||||
|
|
||||||
~ l.insert(5)
|
~ l!insert(5)
|
||||||
|
|
||||||
1,2,3,4,5: {l}
|
1,2,3,4,5: {l}
|
||||||
|
|
||||||
|
|
|
||||||
13
test/tests/function reference chain call.ans
Normal file
13
test/tests/function reference chain call.ans
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
$ fn
|
||||||
|
@1
|
||||||
|
|
||||||
|
$ add(x)
|
||||||
|
@x+2
|
||||||
|
|
||||||
|
:c = &fn
|
||||||
|
|
||||||
|
{c!}
|
||||||
|
|
||||||
|
{fn!add}
|
||||||
|
|
||||||
|
{c!!add}
|
||||||
30
test/tests/function reference chain call.lua
Normal file
30
test/tests/function reference chain call.lua
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
local _={}
|
||||||
|
_[13]={}
|
||||||
|
_[12]={}
|
||||||
|
_[11]={}
|
||||||
|
_[10]={text="3",tags=_[13]}
|
||||||
|
_[9]={text="3",tags=_[12]}
|
||||||
|
_[8]={text="1",tags=_[11]}
|
||||||
|
_[7]={_[10]}
|
||||||
|
_[6]={_[9]}
|
||||||
|
_[5]={_[8]}
|
||||||
|
_[4]={"return"}
|
||||||
|
_[3]={"text",_[7]}
|
||||||
|
_[2]={"text",_[6]}
|
||||||
|
_[1]={"text",_[5]}
|
||||||
|
return {_[1],_[2],_[3],_[4]}
|
||||||
|
--[[
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "1"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "3"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "3"
|
||||||
|
} } }
|
||||||
|
{ "return" }
|
||||||
|
]]--
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
$ f(a)
|
$ f(a)
|
||||||
{a}
|
{a}
|
||||||
|
|
||||||
~ "ok".f
|
~ "ok"!f
|
||||||
|
|
||||||
~ "ok".f()
|
~ "ok"!f()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
$ f(a, b)
|
$ f(a, b)
|
||||||
{a}{b}
|
{a}{b}
|
||||||
|
|
||||||
~ "o".f("k")
|
~ "o"!f("k")
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
$ f(l...)
|
$ f(l...)
|
||||||
~ l.len
|
~ l!len
|
||||||
:a = 0
|
:a = 0
|
||||||
~ a := l(1)
|
~ a := l(1)
|
||||||
~ l.remove(1)
|
~ l!remove(1)
|
||||||
@a + f(l=l)
|
@a + f(l=l)
|
||||||
~~
|
~~
|
||||||
@0
|
@0
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
$ -(f)
|
$ -_(f)
|
||||||
@"generic minus"
|
@"generic minus"
|
||||||
|
|
||||||
$ -(f::string)
|
$ -_(f::string)
|
||||||
@"minus "+f
|
@"minus "+f
|
||||||
|
|
||||||
{-5}
|
{-5}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue