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

ArgumentTuple cleanup

This commit is contained in:
Étienne Fildadut 2023-12-23 00:22:00 +01:00
parent fe351b5ca4
commit ffadc0dd69
12 changed files with 88 additions and 105 deletions

View file

@ -7,51 +7,48 @@ local ArgumentTuple
ArgumentTuple = ast.abstract.Node {
type = "argument tuple",
list = nil, -- list of expr
named = nil, -- { [string] = expr, ... }
assignment = nil, -- expr
arity = 0,
arguments = nil,
positional = nil, -- list of expr - can be sparse! but for each hole there should be an associated named arg
named = nil, -- { [string name] = arg1, [pos number] = string name, ... }
assignment = nil, -- expr; always the last argument if set
arity = 0, -- number of arguments, i.e. number of positional+named+assignment arguments
init = function(self, ...)
self.list = { ... }
self.positional = { ... }
self.named = {}
self.arity = #self.list
self.arity = #self.positional
end,
insert_positional = function(self, position, val) -- only for construction
local l = {}
for k, v in pairs(self.list) do
if k >= position then l[k+1] = v
else l[k] = v end
end
l[position] = val
self.list = l
add_positional = function(self, val) -- only for construction
assert(not (self.positional[self.arity+1]) or self.assignment)
self.arity = self.arity + 1
self.positional[self.arity] = val
end,
set_positional = function(self, position, val) -- only for construction
assert(not self.list[position])
self.list[position] = val
self.arity = self.arity + 1
end,
set_named = function(self, identifier, val) -- only for construction
add_named = function(self, identifier, val) -- only for construction
local name = identifier.name
assert(not self.named[name])
assert(not (self.named[name] or self.assignment))
self.arity = self.arity + 1
self.named[name] = val
self.arity = self.arity + 1
self.named[self.arity] = name
end,
set_assignment = function(self, val) -- only for construction
add_assignment = function(self, val) -- only for construction
assert(not self.assignment)
self.assignment = val
self.arity = self.arity + 1
self.assignment = val
self.format_priority = operator_priority["_=_"]
end,
_format = function(self, state, priority, ...)
local l = {}
for _, e in pairs(self.list) do
table.insert(l, e:format(state, operator_priority["_,_"], ...))
for i=1, self.arity do
if self.positional[i] then
table.insert(l, self.positional[i]:format(state, operator_priority["_,_"], ...))
elseif self.named[i] then
local name = self.named[i]
table.insert(l, name.."="..self.named[name]:format_right(state, operator_priority["_=_"], ...))
else
break
end
for n, e in pairs(self.named) do
table.insert(l, n.."="..e:format_right(state, operator_priority["_=_"], ...))
end
local s = ("(%s)"):format(table.concat(l, ", "))
if self.assignment then
@ -61,59 +58,43 @@ ArgumentTuple = ast.abstract.Node {
end,
traverse = function(self, fn, ...)
for _, e in pairs(self.list) do
fn(e, ...)
end
for _, e in pairs(self.named) do
fn(e, ...)
end
if self.assignment then
for i=1, self.arity do
if self.positional[i] then
fn(self.positional[i], ...)
elseif self.named[i] then
fn(self.named[self.named[i]], ...)
else
fn(self.assignment, ...)
end
end,
-- need to redefine hash to include a table.sort as pairs() in :traverse is non-deterministic
-- as well as doesn't account for named arguments names
_hash = function(self)
local t = {}
for _, e in pairs(self.list) do
table.insert(t, e:hash())
end
for n, e in pairs(self.named) do
table.insert(t, ("%s=%s"):format(n, e:hash()))
end
if self.assignment then
table.insert(t, self.assignment:hash())
end
table.sort(t)
return ("%s<%s>"):format(self.type, table.concat(t, ";"))
end,
_eval = function(self, state)
local r = ArgumentTuple:new()
for i, e in pairs(self.list) do
r:set_positional(i, e:eval(state))
for i=1, self.arity do
if self.positional[i] then
r:add_positional(self.positional[i]:eval(state))
elseif self.named[i] then
r:add_named(Identifier:new(self.named[i]), self.named[self.named[i]]:eval(state))
else
r:add_assignment(self.assignment:eval(state))
end
for n, e in pairs(self.named) do
r:set_named(Identifier:new(n), e:eval(state))
end
if self.assignment then
r:set_assignment(self.assignment:eval(state))
end
return r
end,
-- recreate new argumenttuple with a first positional argument added
with_first_argument = function(self, first)
local r = ArgumentTuple:new()
r:set_positional(1, first)
for i, e in pairs(self.list) do
r:set_positional(i+1, e)
r:add_positional(first)
for i=1, self.arity do
if self.positional[i] then
r:add_positional(self.positional[i])
elseif self.named[i] then
r:add_named(Identifier:new(self.named[i]), self.named[self.named[i]])
else
r:add_assignment(self.assignment)
end
for n, e in pairs(self.named) do
r:set_named(Identifier:new(n), e)
end
if self.assignment then
r:set_assignment(self.assignment)
end
return r
end,
@ -140,9 +121,9 @@ ArgumentTuple = ast.abstract.Node {
for i, param in ipairs(params.list) do
-- search in args
local arg
if self.list[i] then
if self.positional[i] then
used_list[i] = true
arg = self.list[i]
arg = self.positional[i]
elseif self.named[param.identifier.name] then
used_named[param.identifier.name] = true
arg = self.named[param.identifier.name]
@ -166,14 +147,17 @@ ArgumentTuple = ast.abstract.Node {
end
end
-- check for unused arguments
for i in pairs(self.list) do
for i=1, self.arity do
if self.positional[i] then
if not used_list[i] then
return false, ("%sth positional argument is unused"):format(i)
end
elseif self.named[i] then
if not used_named[self.named[i]] then
return false, ("named argument %s is unused"):format(self.named[i])
end
for n in pairs(self.named) do
if not used_named[n] then
return false, ("named argument %s is unused"):format(n)
else
break
end
end
if self.assignment and not used_assignment then
@ -186,8 +170,8 @@ ArgumentTuple = ast.abstract.Node {
-- assume :match_parameter_tuple was already called and returned true
bind_parameter_tuple = function(self, state, params)
for i, arg in ipairs(params.list) do
if self.list[i] then
state.scope:define(arg.identifier:to_symbol(), self.list[i])
if self.positional[i] then
state.scope:define(arg.identifier:to_symbol(), self.positional[i])
elseif self.named[arg.identifier.name] then
state.scope:define(arg.identifier:to_symbol(), self.named[arg.identifier.name])
elseif i == params.max_arity and params.assignment then

View file

@ -1,7 +1,7 @@
local ast = require("ast")
local Identifier, Quote
local attached_block_identifier
local attached_block_identifier, attached_block_symbol
local AttachBlock = ast.abstract.Node {
type = "attach block",
@ -26,7 +26,7 @@ local AttachBlock = ast.abstract.Node {
_eval = function(self, state)
state.scope:push_partial(attached_block_identifier)
state.scope:define(attached_block_identifier:to_symbol(), Quote:new(self.block)) -- _ is always wrapped in a Call when it appears
state.scope:define(attached_block_symbol, Quote:new(self.block)) -- _ is always wrapped in a Call when it appears
local exp = self.expression:eval(state)
state.scope:pop()
@ -35,7 +35,7 @@ local AttachBlock = ast.abstract.Node {
_prepare = function(self, state)
state.scope:push_partial(attached_block_identifier)
state.scope:define(attached_block_identifier:to_symbol(), Quote:new(self.block))
state.scope:define(attached_block_symbol, Quote:new(self.block))
self.expression:prepare(state)
state.scope:pop()
end
@ -45,5 +45,6 @@ package.loaded[...] = AttachBlock
Identifier, Quote = ast.Identifier, ast.Quote
attached_block_identifier = Identifier:new("_")
attached_block_symbol = attached_block_identifier:to_symbol()
return AttachBlock

View file

@ -52,14 +52,14 @@ Call = ast.abstract.Node {
if Identifier:is(self.func) then
local name, arity = self.func.name, self.arguments.arity
if infix[name] and arity == 2 then
local left = self.arguments.list[1]:format(...)
local right = self.arguments.list[2]:format_right(...)
local left = self.arguments.positional[1]:format(...)
local right = self.arguments.positional[2]:format_right(...)
return ("%s %s %s"):format(left, name:match("^_(.*)_$"), right)
elseif prefix[name] and arity == 1 then
local right = self.arguments.list[1]:format_right(...)
local right = self.arguments.positional[1]:format_right(...)
return ("%s%s"):format(name:match("^(.*)_$"), right)
elseif suffix[name] and arity == 1 then
local left = self.arguments.list[1]:format(...)
local left = self.arguments.positional[1]:format(...)
return ("%s%s"):format(left, name:match("^_(.*)$"))
end
end

View file

@ -1,5 +1,6 @@
-- prevent an expression from being immediately evaluated, and instead only evaluate it when the node is explicitely called
-- it can be used to evaluate the expression on demand, as if the quote call AST was simply replaced by the unevaluated associated expression AST (like a macro)
-- it can be used to evaluate the expression on demand, as if the quote call AST was simply replaced by the unevaluated associated expression AST.
-- kinda like a function, but no parameters, no closure and no new scope
-- keep in mind that this thus bypass any scoping rule, closure, etc.
--
-- used for infix operators where the evaluation of the right term depends of the left one (lazy boolean operators, conditionals, etc.)

View file

@ -190,7 +190,7 @@ Node = class {
end,
-- return a pretty string representation of the node.
-- for non-runtime nodes (what was generated by a parse without any evaluation), this should return valid Anselme code that is functionnally equivalent to the parsed code. note that it currently does not preserve comment.
-- for non-runtime nodes (what was generated by a parse without any evaluation), this should return valid Anselme code that is functionnally equivalent to the parsed code. note that it currently does not preserve comment. the returned code should additionally always be the same given the same non-runtime input.
-- redefine _format, not this - note that _format is a mandary method for all nodes.
-- state is optional and should only be relevant for runtime nodes; if specified, only show what is relevant for the current branch.
-- indentation_level and parent_priority are optional value that respectively keep track in nester :format calls of the indentation level (number) and parent operator priority (number); if the node has a strictly lower priority than the parent node, parentheses will be added

View file

@ -70,9 +70,9 @@ local function add_to_set(set, val)
end
--# class creation logic #--
local new_class, class_mt
local class_mt
new_class = function(...)
local function new_class(...)
local class = {}
local include = {...}
for i=1, #include do

View file

@ -2,10 +2,8 @@
local primary = require("parser.expression.primary.primary")
local StringInterpolation = require("ast.StringInterpolation")
local ast = require("ast")
local String = ast.String
local String, StringInterpolation = ast.String, ast.StringInterpolation
local expression_to_ast = require("parser.expression.to_ast")

View file

@ -32,7 +32,7 @@ return primary {
if type_check:match(rem, 0, nil_val) then
local exp
exp, rem = type_check:parse(source, rem, nil, 0, nil_val)
type_check_exp = exp.arguments.list[2]
type_check_exp = exp.arguments.positional[2]
end
return ident:to_symbol{ constant = constant, persistent = persistent, exported = exported, type_check = type_check_exp }:set_source(source), rem

View file

@ -18,7 +18,7 @@ return infix {
end,
build_ast = function(self, left, right)
left.arguments:set_assignment(right)
left.arguments:add_assignment(right)
return Call:new(left.func, left.arguments) -- recreate Call since we modified left.arguments
end,
}

View file

@ -19,8 +19,7 @@ return infix {
build_ast = function(self, left, right)
if Call:is(right) then
right.arguments:insert_positional(1, left)
return right
return Call:new(right.func, right.arguments:with_first_argument(left))
else
return Call:new(right, ArgumentTuple:new(left))
end

View file

@ -27,11 +27,11 @@ return secondary {
exp = Tuple:new(exp)
end
for i, v in ipairs(exp.list) do
for _, v in ipairs(exp.list) do
if Assignment:is(v) then
args:set_named(v.identifier, v.expression)
args:add_named(v.identifier, v.expression)
else
args:set_positional(i, v)
args:add_positional(v)
end
end