diff --git a/ast/ArgumentTuple.lua b/ast/ArgumentTuple.lua index c242743..0497911 100644 --- a/ast/ArgumentTuple.lua +++ b/ast/ArgumentTuple.lua @@ -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["_,_"], ...)) - end - for n, e in pairs(self.named) do - table.insert(l, n.."="..e:format_right(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 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, ...) + 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 - 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, _eval = function(self, state) local r = ArgumentTuple:new() - for i, e in pairs(self.list) do - r:set_positional(i, e: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)) + 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 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) - 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) + 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 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 - if not used_list[i] then - return false, ("%sth positional argument is unused"):format(i) - end - end - for n in pairs(self.named) do - if not used_named[n] then - return false, ("named argument %s is unused"):format(n) + 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 + 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 diff --git a/ast/AttachBlock.lua b/ast/AttachBlock.lua index a61a582..e8285cb 100644 --- a/ast/AttachBlock.lua +++ b/ast/AttachBlock.lua @@ -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 diff --git a/ast/Call.lua b/ast/Call.lua index 3f9ac0e..54513a4 100644 --- a/ast/Call.lua +++ b/ast/Call.lua @@ -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 diff --git a/ast/Quote.lua b/ast/Quote.lua index f4ca356..700bb8e 100644 --- a/ast/Quote.lua +++ b/ast/Quote.lua @@ -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.) diff --git a/ast/Text.lua b/ast/Text.lua index c29df8c..4495e64 100644 --- a/ast/Text.lua +++ b/ast/Text.lua @@ -25,7 +25,7 @@ return Runtime(AutoCall, Event) { for _, e in ipairs(self.list) do table.insert(t, ("%s%s"):format(e[2]:format(...), e[1]:format(...))) end - return ("| %s |"):format(table.concat(t, " ")) + return ("| %s|"):format(table.concat(t, " ")) end, -- Text comes from TextInterpolation which already evals the contents diff --git a/ast/abstract/Node.lua b/ast/abstract/Node.lua index d68043a..171fba5 100644 --- a/ast/abstract/Node.lua +++ b/ast/abstract/Node.lua @@ -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 diff --git a/class.lua b/class.lua index e6aa342..5b26418 100644 --- a/class.lua +++ b/class.lua @@ -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 diff --git a/parser/expression/primary/string.lua b/parser/expression/primary/string.lua index 6d38f9e..4b095c6 100644 --- a/parser/expression/primary/string.lua +++ b/parser/expression/primary/string.lua @@ -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") diff --git a/parser/expression/primary/symbol.lua b/parser/expression/primary/symbol.lua index 05a40aa..5221624 100644 --- a/parser/expression/primary/symbol.lua +++ b/parser/expression/primary/symbol.lua @@ -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 diff --git a/parser/expression/secondary/infix/assignment_call.lua b/parser/expression/secondary/infix/assignment_call.lua index 8501e3a..493a868 100644 --- a/parser/expression/secondary/infix/assignment_call.lua +++ b/parser/expression/secondary/infix/assignment_call.lua @@ -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, } diff --git a/parser/expression/secondary/infix/call.lua b/parser/expression/secondary/infix/call.lua index 400aece..5b330db 100644 --- a/parser/expression/secondary/infix/call.lua +++ b/parser/expression/secondary/infix/call.lua @@ -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 diff --git a/parser/expression/secondary/suffix/call.lua b/parser/expression/secondary/suffix/call.lua index d456a82..a751401 100644 --- a/parser/expression/secondary/suffix/call.lua +++ b/parser/expression/secondary/suffix/call.lua @@ -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