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

View file

@ -1,7 +1,7 @@
local ast = require("ast") local ast = require("ast")
local Identifier, Quote local Identifier, Quote
local attached_block_identifier local attached_block_identifier, attached_block_symbol
local AttachBlock = ast.abstract.Node { local AttachBlock = ast.abstract.Node {
type = "attach block", type = "attach block",
@ -26,7 +26,7 @@ local AttachBlock = ast.abstract.Node {
_eval = function(self, state) _eval = function(self, state)
state.scope:push_partial(attached_block_identifier) 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) local exp = self.expression:eval(state)
state.scope:pop() state.scope:pop()
@ -35,7 +35,7 @@ local AttachBlock = ast.abstract.Node {
_prepare = function(self, state) _prepare = function(self, state)
state.scope:push_partial(attached_block_identifier) 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) self.expression:prepare(state)
state.scope:pop() state.scope:pop()
end end
@ -45,5 +45,6 @@ package.loaded[...] = AttachBlock
Identifier, Quote = ast.Identifier, ast.Quote Identifier, Quote = ast.Identifier, ast.Quote
attached_block_identifier = Identifier:new("_") attached_block_identifier = Identifier:new("_")
attached_block_symbol = attached_block_identifier:to_symbol()
return AttachBlock return AttachBlock

View file

@ -52,14 +52,14 @@ Call = ast.abstract.Node {
if Identifier:is(self.func) then if Identifier:is(self.func) then
local name, arity = self.func.name, self.arguments.arity local name, arity = self.func.name, self.arguments.arity
if infix[name] and arity == 2 then if infix[name] and arity == 2 then
local left = self.arguments.list[1]:format(...) local left = self.arguments.positional[1]:format(...)
local right = self.arguments.list[2]:format_right(...) local right = self.arguments.positional[2]:format_right(...)
return ("%s %s %s"):format(left, name:match("^_(.*)_$"), right) return ("%s %s %s"):format(left, name:match("^_(.*)_$"), right)
elseif prefix[name] and arity == 1 then 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) return ("%s%s"):format(name:match("^(.*)_$"), right)
elseif suffix[name] and arity == 1 then 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("^_(.*)$")) return ("%s%s"):format(left, name:match("^_(.*)$"))
end end
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 -- 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. -- 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.) -- used for infix operators where the evaluation of the right term depends of the left one (lazy boolean operators, conditionals, etc.)

View file

@ -25,7 +25,7 @@ return Runtime(AutoCall, Event) {
for _, e in ipairs(self.list) do for _, e in ipairs(self.list) do
table.insert(t, ("%s%s"):format(e[2]:format(...), e[1]:format(...))) table.insert(t, ("%s%s"):format(e[2]:format(...), e[1]:format(...)))
end end
return ("| %s |"):format(table.concat(t, " ")) return ("| %s|"):format(table.concat(t, " "))
end, end,
-- Text comes from TextInterpolation which already evals the contents -- Text comes from TextInterpolation which already evals the contents

View file

@ -190,7 +190,7 @@ Node = class {
end, end,
-- return a pretty string representation of the node. -- 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. -- 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. -- 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 -- 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 end
--# class creation logic #-- --# class creation logic #--
local new_class, class_mt local class_mt
new_class = function(...) local function new_class(...)
local class = {} local class = {}
local include = {...} local include = {...}
for i=1, #include do for i=1, #include do

View file

@ -2,10 +2,8 @@
local primary = require("parser.expression.primary.primary") local primary = require("parser.expression.primary.primary")
local StringInterpolation = require("ast.StringInterpolation")
local ast = require("ast") local ast = require("ast")
local String = ast.String local String, StringInterpolation = ast.String, ast.StringInterpolation
local expression_to_ast = require("parser.expression.to_ast") 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 if type_check:match(rem, 0, nil_val) then
local exp local exp
exp, rem = type_check:parse(source, rem, nil, 0, nil_val) 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 end
return ident:to_symbol{ constant = constant, persistent = persistent, exported = exported, type_check = type_check_exp }:set_source(source), rem 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, end,
build_ast = function(self, left, right) 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 return Call:new(left.func, left.arguments) -- recreate Call since we modified left.arguments
end, end,
} }

View file

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

View file

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