mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-27 16:49:31 +00:00
ArgumentTuple cleanup
This commit is contained in:
parent
fe351b5ca4
commit
ffadc0dd69
12 changed files with 88 additions and 105 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue