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

[internal] do block parsing at the same time as expression parsing

This commit is contained in:
Étienne Fildadut 2024-01-15 17:10:30 +01:00
parent 09b73301b4
commit 1ea06c63eb
50 changed files with 240 additions and 249 deletions

View file

@ -16,6 +16,7 @@ local Call
Call = ast.abstract.Node { Call = ast.abstract.Node {
type = "call", type = "call",
explicit = true, -- false for implicit calls
func = nil, func = nil,
arguments = nil, -- ArgumentTuple arguments = nil, -- ArgumentTuple

View file

@ -15,12 +15,18 @@ Source = class {
increment = function(self, n, ...) increment = function(self, n, ...)
self.position = self.position + n self.position = self.position + n
end, end,
increment_line = function(self, n, ...)
self.line = self.line + n
end,
count = function(self, capture, ...) count = function(self, capture, ...)
self:increment(utf8.len(capture)) return capture, self:consume(capture, ...)
return capture, ...
end, end,
consume = function(self, capture, ...) consume = function(self, capture, ...)
self:increment(utf8.len(capture)) for _ in capture:gmatch(".-\n") do
self.position = 1
self:increment_line(1)
end
self:increment(utf8.len(capture:match("[^\n]*$")))
return ... return ...
end, end,

View file

@ -1,67 +0,0 @@
--- transform raw code string into a nested tree of lines
local utf8 = utf8 or require("lua-utf8")
local Source = require("anselme.parser.Source")
local function indented_to_tree(indented)
local tree = {}
local current_parent = tree
local current_level = 0
local last_line_empty = nil
for _, l in ipairs(indented) do
-- indentation of empty line is determined using the next line
-- (consecutive empty lines are merged into one)
if l.content == "" then
last_line_empty = l
else
-- raise indentation
if l.level > current_level then
if #current_parent == 0 then -- can't add children to nil
error(("invalid indentation; at %s"):format(l.source))
end
current_parent = current_parent[#current_parent]
current_level = l.level
-- lower indentation
elseif l.level < current_level then
current_parent = tree
current_level = 0
while current_level < l.level do -- find correct level starting back from the root
current_parent = current_parent[#current_parent]
current_level = current_parent[1].level
end
if current_level ~= l.level then
error(("invalid indentation; at %s"):format(l.source))
end
end
-- add line
if last_line_empty then
last_line_empty.level = current_level
table.insert(current_parent, last_line_empty)
last_line_empty = nil
end
table.insert(current_parent, l)
end
end
return tree
end
local function code_to_indented(code, source_name)
local indented = {}
local i = 1
for line in (code.."\n"):gmatch("(.-)\n") do
local indent, rem = line:match("^(%s*)(.-)$")
local indent_len = utf8.len(indent)
table.insert(indented, { level = indent_len, content = rem, source = Source:new(source_name, i, 1+indent_len) })
i = i + 1
end
return indented
end
return function(code, source_name)
return indented_to_tree(code_to_indented(code, source_name or "?"))
end

View file

@ -0,0 +1,70 @@
local expression_to_ast = require("anselme.parser.expression.to_ast")
local ast = require("anselme.ast")
local PartialScope, Block, Flush, Call, Identifier = ast.PartialScope, ast.Block, ast.Flush, ast.Call, ast.Identifier
local function block(source, str)
local start_source = source:clone()
if not str:match("^\n") then
str = "\n"..str
source:increment_line(-1)
end
local levels = { { indentation = utf8.len(str:match("^\n([ \t]*)")), block = Block:new() } }
local current_level = levels[#levels]
local rem = str
local last_line_empty
while rem:match("^\n") do
local line = source:consume(rem:match("^(\n)(.*)$"))
local new_indentation = utf8.len(line:match("^([ \t]*)"))
-- indentation of empty line is determined using the next line
-- (consecutive empty lines are merged into one)
if line:match("^\n") then
rem = line
last_line_empty = true
elseif line:match("[^%s]") then
-- raise indentation
if new_indentation > current_level.indentation then
local child_block = Block:new()
local cur_exps = current_level.block.expressions
cur_exps[#cur_exps] = PartialScope:attach_block(cur_exps[#cur_exps], child_block):set_source(source)
table.insert(levels, { indentation = new_indentation, block = child_block })
current_level = levels[#levels]
-- lower indentation
elseif new_indentation < current_level.indentation then
while new_indentation < current_level.indentation do
table.remove(levels)
current_level = levels[#levels]
end
if new_indentation ~= current_level.indentation then
error(("invalid indentation; at %s"):format(source))
end
end
-- parse line
local s, exp
s, exp, rem = pcall(expression_to_ast, source, line)
if not s then error(("invalid expression in block: %s"):format(exp), 0) end
-- single implicit _: line was empty (e.g. single comment in the line)
if Call:is(exp) and not exp.explicit and Identifier:is(exp.func) and exp.func.name == "_" then
exp = Flush:new()
end
-- add line
if last_line_empty then
current_level.block:add(Flush:new())
last_line_empty = nil
end
current_level.block:add(exp)
else -- end-of-file
rem = ""
end
end
return levels[1].block:set_source(start_source), rem
end
return block

View file

@ -11,7 +11,7 @@ comment = primary {
local content_list = {} local content_list = {}
while not rem:match("^%*%/") do while not rem:match("^%*%/") do
local content local content
content, rem = rem:match("^([^%/%*]*)(.-)$") content, rem = rem:match("^([^\n%/%*]*)(.-)$")
-- cut the text prematurely at limit_pattern if relevant -- cut the text prematurely at limit_pattern if relevant
if limit_pattern and content:match(limit_pattern) then if limit_pattern and content:match(limit_pattern) then
@ -32,6 +32,10 @@ comment = primary {
table.insert(content_list, "/*") table.insert(content_list, "/*")
table.insert(content_list, subcomment) table.insert(content_list, subcomment)
table.insert(content_list, "*/") table.insert(content_list, "*/")
-- consumed everything until end-of-line/file, close your eyes and imagine the text has been closed
elseif rem:match("^\n") or not rem:match("[^%s]") then
rem = "*/" .. rem
source:increment(-2)
-- no end token after the comment -- no end token after the comment
elseif not rem:match("^%*%/") then elseif not rem:match("^%*%/") then
-- single * or /, keep on commentin' -- single * or /, keep on commentin'
@ -39,12 +43,9 @@ comment = primary {
local s local s
s, rem = source:count(rem:match("^([%*%/])(.-)$")) s, rem = source:count(rem:match("^([%*%/])(.-)$"))
table.insert(content_list, s) table.insert(content_list, s)
-- anything other than end-of-line -- anything else
elseif rem:match("[^%s]") then
error(("unexpected %q at end of comment"):format(rem), 0)
-- consumed everything until end-of-line, close your eyes and imagine the text has been closed
else else
rem = rem .. "*/" error(("unexpected %q at end of comment"):format(rem:match("^[^\n]*")), 0)
end end
end end
end end

View file

@ -21,16 +21,16 @@ return primary {
-- value check -- value check
local value_check local value_check
if rem:match("^%s*::") then if rem:match("^[ \t]*::") then
local scheck = source:consume(rem:match("^(%s*::%s*)(.*)$")) local scheck = source:consume(rem:match("^([ \t]*::[ \t]*)(.*)$"))
value_check, rem = expression_to_ast(source, scheck, limit_pattern, value_check_priority) value_check, rem = expression_to_ast(source, scheck, limit_pattern, value_check_priority)
end end
-- default value -- default value
local default local default
if not no_default_value then if not no_default_value then
if rem:match("^%s*=") then if rem:match("^[ \t]*=") then
local sdefault = source:consume(rem:match("^(%s*=%s*)(.*)$")) local sdefault = source:consume(rem:match("^([ \t]*=[ \t]*)(.*)$"))
default, rem = expression_to_ast(source, sdefault, limit_pattern, assignment_priority) default, rem = expression_to_ast(source, sdefault, limit_pattern, assignment_priority)
end end
end end

View file

@ -16,27 +16,27 @@ return primary {
-- i would LOVE to reuse the regular list parsing code for this, but unfortunately the list parsing code -- i would LOVE to reuse the regular list parsing code for this, but unfortunately the list parsing code
-- itself depends on this and expect this to be available quite early, and it's ANNOYING -- itself depends on this and expect this to be available quite early, and it's ANNOYING
while not rem:match("^%s*%)") do while not rem:match("^[ \t]*%)") do
-- parameter -- parameter
local func_param local func_param
func_param, rem = function_parameter:expect(source, rem, limit_pattern) func_param, rem = function_parameter:expect(source, rem, limit_pattern)
-- next! comma separator -- next! comma separator
if not rem:match("^%s*%)") then if not rem:match("^[ \t]*%)") then
if not rem:match("^%s*,") then if not rem:match("^[ \t]*,") then
error(("unexpected %q at end of argument list"):format(rem), 0) error(("unexpected %q at end of argument list"):format(rem:match("^[^\n]*")), 0)
end end
rem = source:consume(rem:match("^(%s*,)(.*)$")) rem = source:consume(rem:match("^([ \t]*,)(.*)$"))
end end
-- add -- add
parameters:insert(func_param) parameters:insert(func_param)
end end
rem = rem:match("^%s*%)(.*)$") rem = rem:match("^[ \t]*%)(.*)$")
-- assigment param -- assigment param
if rem:match("^%s*=") then if rem:match("^[ \t]*=") then
rem = source:consume(rem:match("^(%s*=%s*)(.*)$")) rem = source:consume(rem:match("^([ \t]*=[ \t]*)(.*)$"))
local func_param local func_param
func_param, rem = function_parameter_no_default:expect(source, rem, limit_pattern) func_param, rem = function_parameter_no_default:expect(source, rem, limit_pattern)

View file

@ -32,8 +32,8 @@ local function_parameter_maybe_parenthesis = function_parameter_no_default {
local exp, rem = function_parameter_no_default:parse(source, str, limit_pattern) local exp, rem = function_parameter_no_default:parse(source, str, limit_pattern)
if not rem:match("^%s*%)") then error(("unexpected %q at end of parenthesis"):format(rem), 0) end if not rem:match("^[ \t]*%)") then error(("unexpected %q at end of parenthesis"):format(rem:match("^[^\n]*")), 0) end
rem = source:consume(rem:match("^(%s*%))(.-)$")) rem = source:consume(rem:match("^([ \t]*%))(.-)$"))
return exp, rem return exp, rem
else else
@ -49,7 +49,7 @@ local function_parameter_maybe_parenthesis = function_parameter_no_default {
local function search_prefix_signature(modifiers, source, str, limit_pattern) local function search_prefix_signature(modifiers, source, str, limit_pattern)
for _, pfx in ipairs(prefixes) do for _, pfx in ipairs(prefixes) do
local prefix = pfx[1] local prefix = pfx[1]
local prefix_pattern = "%s*"..escape(prefix).."%s*" local prefix_pattern = "[ \t]*"..escape(prefix).."[ \t]*"
if str:match("^"..prefix_pattern) then if str:match("^"..prefix_pattern) then
-- operator name -- operator name
local rem = source:consume(str:match("^("..prefix_pattern..")(.*)$")) local rem = source:consume(str:match("^("..prefix_pattern..")(.*)$"))
@ -82,7 +82,7 @@ local function search_infix_signature(modifiers, source, str, limit_pattern)
for _, ifx in ipairs(infixes) do for _, ifx in ipairs(infixes) do
local infix = ifx[1] local infix = ifx[1]
local infix_pattern = "%s*"..escape(infix).."%s*" local infix_pattern = "[ \t]*"..escape(infix).."[ \t]*"
if rem:match("^"..infix_pattern) then if rem:match("^"..infix_pattern) then
-- operator name -- operator name
rem = src:consume(rem:match("^("..infix_pattern..")(.*)$")) rem = src:consume(rem:match("^("..infix_pattern..")(.*)$"))
@ -119,7 +119,7 @@ local function search_suffix_signature(modifiers, source, str, limit_pattern)
for _, sfx in ipairs(suffixes) do for _, sfx in ipairs(suffixes) do
local suffix = sfx[1] local suffix = sfx[1]
local suffix_pattern = "%s*"..escape(suffix).."%s*" local suffix_pattern = "[ \t]*"..escape(suffix).."[ \t]*"
if rem:match("^"..suffix_pattern) then if rem:match("^"..suffix_pattern) then
-- operator name -- operator name
rem = src:count(rem:match("^("..suffix_pattern..")(.*)$")) rem = src:count(rem:match("^("..suffix_pattern..")(.*)$"))

View file

@ -2,8 +2,8 @@ local primary = require("anselme.parser.expression.primary.primary")
local Identifier = require("anselme.ast.Identifier") local Identifier = require("anselme.ast.Identifier")
local disallowed_set = (".~`^+-=<>/[]*{}|\\_!?,;:()\"@&$#%"):gsub("[^%w]", "%%%1") local disallowed_set = ("\n.~`^+-=<>/[]*{}|\\_!?,;:()\"@&$#%"):gsub("[^%w]", "%%%1")
local identifier_pattern = "%s*[^0-9%s'"..disallowed_set.."][^"..disallowed_set.."]*" local identifier_pattern = "[ \t]*[^0-9%s'"..disallowed_set.."][^"..disallowed_set.."]*"
local common = require("anselme.common") local common = require("anselme.common")
local trim, escape = common.trim, common.escape local trim, escape = common.trim, common.escape
@ -17,7 +17,7 @@ for _, suffix in ipairs(regular_operators.suffixes) do table.insert(operators, "
-- all valid identifier patterns -- all valid identifier patterns
local identifier_patterns = { identifier_pattern } local identifier_patterns = { identifier_pattern }
for _, operator in ipairs(operators) do table.insert(identifier_patterns, "%s*"..escape(operator)) end for _, operator in ipairs(operators) do table.insert(identifier_patterns, "[ \t]*"..escape(operator)) end
return primary { return primary {
match = function(self, str) match = function(self, str)

View file

@ -0,0 +1,17 @@
local primary = require("anselme.parser.expression.primary.primary")
local ast = require("anselme.ast")
local Identifier, Call, ArgumentTuple = ast.Identifier, ast.Call, ast.ArgumentTuple
return primary {
match = function(self, str)
return str:match("^\n")
end,
parse = function(self, source, str)
-- implicit _, do not consume the newline
local r = Call:new(Identifier:new("_"), ArgumentTuple:new()):set_source(source)
r.explicit = false
return r, str
end
}

View file

@ -1,5 +1,7 @@
--- try to parse a primary expression --- try to parse a primary expression
local comment = require("anselme.parser.expression.comment")
local function r(name) local function r(name)
return require("anselme.parser.expression.primary."..name), nil return require("anselme.parser.expression.primary."..name), nil
end end
@ -14,6 +16,7 @@ local primaries = {
r("identifier"), r("identifier"),
r("anchor"), r("anchor"),
r("block_identifier"), r("block_identifier"),
r("implicit_block_identifier"),
r("tuple"), r("tuple"),
r("struct"), r("struct"),
@ -31,6 +34,11 @@ return {
-- returns exp, rem if expression found -- returns exp, rem if expression found
-- returns nil if no expression found -- returns nil if no expression found
search = function(self, source, str, limit_pattern) search = function(self, source, str, limit_pattern)
str = source:consume(str:match("^([ \t]*)(.*)$"))
-- if there is a comment, restart the parsing after the comment ends
local c, c_rem = comment:search(source, str, limit_pattern)
if c then return self:search(source, c_rem, limit_pattern) end
-- search primary
for _, primary in ipairs(primaries) do for _, primary in ipairs(primaries) do
local exp, rem = primary:search(source, str, limit_pattern) local exp, rem = primary:search(source, str, limit_pattern)
if exp then return exp, rem end if exp then return exp, rem end

View file

@ -22,7 +22,7 @@ return primary {
local s local s
s, exp, rem = pcall(expression_to_ast, source, rem, "%)") s, exp, rem = pcall(expression_to_ast, source, rem, "%)")
if not s then error("invalid expression inside parentheses: "..exp, 0) end if not s then error("invalid expression inside parentheses: "..exp, 0) end
if not rem:match("^%s*%)") then error(("unexpected %q at end of parenthesis"):format(rem), 0) end if not rem:match("^%s*%)") then error(("unexpected %q at end of parenthesis"):format(rem:match("^[^\n]*")), 0) end
end end
rem = source:consume(rem:match("^(%s*%))(.*)$")) rem = source:consume(rem:match("^(%s*%))(.*)$"))

View file

@ -37,7 +37,7 @@ return primary {
while not rem:match("^"..stop_pattern) do while not rem:match("^"..stop_pattern) do
local text_source = source:clone() local text_source = source:clone()
local text local text
text, rem = rem:match("^([^%{%\\"..stop_pattern.."]*)(.-)$") -- get all text until something potentially happens text, rem = rem:match("^([^\n%{%\\"..stop_pattern.."]*)(.-)$") -- get all text until something potentially happens
-- cut the text prematurely at limit_pattern if relevant -- cut the text prematurely at limit_pattern if relevant
if self.allow_implicit_stop and limit_pattern and text:match(limit_pattern) then if self.allow_implicit_stop and limit_pattern and text:match(limit_pattern) then
@ -50,23 +50,25 @@ return primary {
interpolation:insert(String:new(text):set_source(text_source)) interpolation:insert(String:new(text):set_source(text_source))
-- interpolated expression
if rem:match("^%{") then if rem:match("^%{") then
local ok, exp local ok, exp
ok, exp, rem = pcall(expression_to_ast, source, source:consume(rem:match("^(%{)(.*)$")), "%}") ok, exp, rem = pcall(expression_to_ast, source, source:consume(rem:match("^(%{)(.*)$")), "%}")
if not ok then error("invalid expression inside interpolation: "..exp, 0) end if not ok then error("invalid expression inside interpolation: "..exp, 0) end
if not rem:match("^%s*%}") then error(("unexpected %q at end of interpolation"):format(rem), 0) end if not rem:match("^[ \t]*%}") then error(("unexpected %q at end of interpolation"):format(rem:match("^[^\n]*")), 0) end
rem = source:consume(rem:match("^(%s*%})(.*)$")) rem = source:consume(rem:match("^([ \t]*%})(.*)$"))
interpolation:insert(exp) interpolation:insert(exp)
-- escape sequence
elseif rem:match("^\\") then elseif rem:match("^\\") then
text, rem = source:consume(rem:match("^(\\(.))(.*)$")) text, rem = source:consume(rem:match("^(\\(.))(.*)$"))
interpolation:insert(String:new(escape_code[text] or text)) interpolation:insert(String:new(escape_code[text] or text))
-- consumed everything until end-of-line/file, implicit stop allowed, close your eyes and imagine the text has been closed
elseif self.allow_implicit_stop and (rem:match("^\n") or not rem:match("[^%s]")) then
rem = self.stop_char .. rem
source:increment(-1)
-- no end token after the comment
elseif not rem:match("^"..stop_pattern) then elseif not rem:match("^"..stop_pattern) then
if not self.allow_implicit_stop or rem:match("[^%s]") then error(("unexpected %q at end of "..self.type):format(rem:match("^[^\n]*")), 0)
error(("unexpected %q at end of "..self.type):format(rem), 0)
-- consumed everything until end-of-line, implicit stop allowed, close your eyes and imagine the text has been closed
else
rem = rem .. self.stop_char
end
end end
end end
rem = source:consume(rem:match("^("..stop_pattern..")(.*)$")) rem = source:consume(rem:match("^("..stop_pattern..")(.*)$"))

View file

@ -5,7 +5,7 @@ local TextInterpolation, Translatable = ast.TextInterpolation, ast.Translatable
return string { return string {
type = "text", type = "text",
start_pattern = "|%s?", start_pattern = "|[ \t]?",
stop_char = "|", stop_char = "|",
allow_implicit_stop = true, allow_implicit_stop = true,
interpolation = TextInterpolation, interpolation = TextInterpolation,
@ -16,7 +16,7 @@ return string {
-- remove terminal space -- remove terminal space
local last = interpolation.list[#interpolation.list] local last = interpolation.list[#interpolation.list]
if ast.String:is(last) then last.string = last.string:gsub("%s$", "") end if ast.String:is(last) then last.string = last.string:gsub("[ \t]$", "") end
return Translatable:new(interpolation):set_source(start_source), rem return Translatable:new(interpolation):set_source(start_source), rem
end end

View file

@ -22,18 +22,18 @@ return primary {
local end_match = escape(end_char) local end_match = escape(end_char)
local l local l
if not rem:match("^%s*"..end_match) then if not rem:match("^[ \t]*"..end_match) then
local s local s
s, l, rem = pcall(expression_to_ast, source, rem, end_match) s, l, rem = pcall(expression_to_ast, source, rem, end_match, nil)
if not s then error("invalid expression in list: "..l, 0) end if not s then error("invalid expression in list: "..l, 0) end
end end
if not Tuple:is(l) or l.explicit then l = Tuple:new(l) end -- single or no element if not Tuple:is(l) or l.explicit then l = Tuple:new(l) end -- single or no element
if not rem:match("^%s*"..end_match) then if not rem:match("^[ \t]*"..end_match) then
error(("unexpected %q at end of list"):format(rem), 0) error(("unexpected %q at end of list"):format(rem:match("^[^\n]*")), 0)
end end
rem = source:consume(rem:match("^(%s*"..end_match..")(.*)$")) rem = source:consume(rem:match("^([ \t]*"..end_match..")(.*)$"))
l.explicit = true l.explicit = true
return l:set_source(start_source), rem return l:set_source(start_source), rem

View file

@ -14,7 +14,7 @@ return infix {
match = function(self, str, current_priority, primary) match = function(self, str, current_priority, primary)
local escaped = escape(self.operator) local escaped = escape(self.operator)
return self.priority > current_priority and str:match("^"..escaped) and identifier:match(str:match("^"..escaped.."%s*(.-)$")) return self.priority > current_priority and str:match("^"..escaped) and identifier:match(str:match("^"..escaped.."[ \t]*(.-)$"))
end, end,
build_ast = function(self, left, right) build_ast = function(self, left, right)

View file

@ -18,6 +18,8 @@ return infix {
parse = function(self, source, str, limit_pattern, current_priority, primary) parse = function(self, source, str, limit_pattern, current_priority, primary)
local start_source = source:clone() local start_source = source:clone()
local right, rem = identifier:parse(source, str, limit_pattern) local right, rem = identifier:parse(source, str, limit_pattern)
return Call:new(Identifier:new(self.identifier), ArgumentTuple:new(primary, right)):set_source(start_source), rem local r = Call:new(Identifier:new(self.identifier), ArgumentTuple:new(primary, right)):set_source(start_source)
r.explicit = false
return r, rem
end end
} }

View file

@ -23,7 +23,7 @@ return secondary {
local sright = source:consume(str:match("^("..escaped..")(.*)$")) local sright = source:consume(str:match("^("..escaped..")(.*)$"))
local s, right, rem = pcall(expression_to_ast, source, sright, limit_pattern, self.priority) local s, right, rem = pcall(expression_to_ast, source, sright, limit_pattern, self.priority)
if not s then error(("invalid expression after binary operator %q: %s"):format(self.operator, right), 0) end if not s then error(("invalid expression after infix operator %q: %s"):format(self.operator, right), 0) end
return self:build_ast(primary, right):set_source(start_source), rem return self:build_ast(primary, right):set_source(start_source), rem
end, end,

View file

@ -20,8 +20,8 @@ return infix {
local escaped = escape(self.operator) local escaped = escape(self.operator)
local rem = str local rem = str
while rem:match("^%s*"..escaped) do while rem:match("^[ \t]*"..escaped) do
rem = source:consume(rem:match("^(%s*"..escaped..")(.*)$")) rem = source:consume(rem:match("^([ \t]*"..escaped..")(.*)$"))
local s, right local s, right
s, right, rem = pcall(expression_to_ast, source, rem, limit_pattern, self.priority) s, right, rem = pcall(expression_to_ast, source, rem, limit_pattern, self.priority)

View file

@ -1,11 +1,13 @@
--- try to parse a secondary expression --- try to parse a secondary expression
local comment = require("anselme.parser.expression.comment")
local function r(name) local function r(name)
return require("anselme.parser.expression.secondary."..name), nil return require("anselme.parser.expression.secondary."..name), nil
end end
local secondaries = { local secondaries = {
-- binary infix operators -- binary infix operators,
r("infix.semicolon"), r("infix.semicolon"),
r("infix.tuple"), r("infix.tuple"),
r("infix.tag"), r("infix.tag"),
@ -49,8 +51,16 @@ end
return { return {
-- returns exp, rem if expression found -- returns exp, rem if expression found
-- returns nil if no expression found -- returns nil if no expression found
-- returns nil, err if error
search = function(self, source, str, limit_pattern, current_priority, primary) search = function(self, source, str, limit_pattern, current_priority, primary)
str = source:consume(str:match("^([ \t]*)(.*)$"))
-- if there is a comment, restart the parsing after the comment ends
local c, c_rem = comment:search(source, str, limit_pattern)
if c then
local ce, ce_rem = self:search(source, c_rem, limit_pattern, current_priority, primary)
if ce then return ce, ce_rem
else return primary, c_rem end -- noop
end
-- search secondary
for _, secondary in ipairs(secondaries) do for _, secondary in ipairs(secondaries) do
local exp, rem = secondary:search(source, str, limit_pattern, current_priority, primary) local exp, rem = secondary:search(source, str, limit_pattern, current_priority, primary)
if exp then return exp, rem end if exp then return exp, rem end

View file

@ -27,7 +27,7 @@ return secondary {
exp, rem = parenthesis:parse(source, str, limit_pattern) exp, rem = parenthesis:parse(source, str, limit_pattern)
if Nil:is(exp) then if Nil:is(exp) then
if str:match("^%(%s*%(%s*%)%s*%)") then -- special case: single nil argument if str:match("^%([ \t]*%([ \t]*%)[ \t]*%)") then -- special case: single nil argument
exp = Tuple:new(Nil:new()) exp = Tuple:new(Nil:new())
else -- no arguments else -- no arguments
exp = Tuple:new() exp = Tuple:new()

View file

@ -2,16 +2,10 @@
local primary, secondary local primary, secondary
local comment = require("anselme.parser.expression.comment")
-- parse an expression, starting from a secondary element operating on operating_on_primary -- parse an expression, starting from a secondary element operating on operating_on_primary
-- returns expr, remaining -- returns expr, remaining
local function from_secondary(source, s, limit_pattern, current_priority, operating_on_primary) local function from_secondary(source, s, limit_pattern, current_priority, operating_on_primary)
s = source:consume(s:match("^(%s*)(.*)$"))
current_priority = current_priority or 0 current_priority = current_priority or 0
-- if there is a comment, restart the parsing after the comment ends
local c, c_rem = comment:search(source, s, limit_pattern)
if c then return from_secondary(source, c_rem, limit_pattern, current_priority, operating_on_primary) end
-- secondary elements -- secondary elements
local exp, rem = secondary:search(source, s, limit_pattern, current_priority, operating_on_primary) local exp, rem = secondary:search(source, s, limit_pattern, current_priority, operating_on_primary)
if exp then return from_secondary(source, rem, limit_pattern, current_priority, exp) end if exp then return from_secondary(source, rem, limit_pattern, current_priority, exp) end
@ -24,18 +18,13 @@ end
-- limit_pattern: set to a string pattern that will trigger the end of elements that would otherwise consume everything until end-of-line (pattern is not consumed) -- limit_pattern: set to a string pattern that will trigger the end of elements that would otherwise consume everything until end-of-line (pattern is not consumed)
-- fallback_exp: if no primary expression can be found, will return this instead. Used to avoid raising an error where an empty or comment-only expression is allowed. -- fallback_exp: if no primary expression can be found, will return this instead. Used to avoid raising an error where an empty or comment-only expression is allowed.
-- return expr, remaining -- return expr, remaining
local function expression_to_ast(source, s, limit_pattern, current_priority, fallback_exp) local function expression_to_ast(source, s, limit_pattern, current_priority)
s = source:consume(s:match("^(%s*)(.*)$"))
current_priority = current_priority or 0 current_priority = current_priority or 0
-- if there is a comment, restart the parsing after the comment ends
local c, c_rem = comment:search(source, s, limit_pattern)
if c then return expression_to_ast(source, c_rem, limit_pattern, current_priority, fallback_exp) end
-- primary elements -- primary elements
local exp, rem = primary:search(source, s, limit_pattern) local exp, rem = primary:search(source, s, limit_pattern)
if exp then return from_secondary(source, rem, limit_pattern, current_priority, exp) end if exp then return from_secondary(source, rem, limit_pattern, current_priority, exp) end
-- no valid primary expression -- no valid primary expression
if fallback_exp then return fallback_exp, s end error(("no valid expression after %q"):format(s), 0)
error(("no valid expression before %q"):format(s), 0)
end end
package.loaded[...] = expression_to_ast package.loaded[...] = expression_to_ast
@ -44,7 +33,7 @@ primary = require("anselme.parser.expression.primary")
secondary = require("anselme.parser.expression.secondary") secondary = require("anselme.parser.expression.secondary")
-- return expr, remaining -- return expr, remaining
return function(source, s, limit_pattern, current_priority, operating_on_primary, fallback_exp) return function(source, s, limit_pattern, current_priority, operating_on_primary)
if operating_on_primary then return from_secondary(source, s, limit_pattern, current_priority, operating_on_primary) if operating_on_primary then return from_secondary(source, s, limit_pattern, current_priority, operating_on_primary)
else return expression_to_ast(source, s, limit_pattern, current_priority, fallback_exp) end else return expression_to_ast(source, s, limit_pattern, current_priority) end
end end

View file

@ -1,11 +1,15 @@
local code_to_tree = require("anselme.parser.code_to_tree") local block = require("anselme.parser.expression.block")
local tree_to_ast = require("anselme.parser.tree_to_ast") local Source = require("anselme.parser.Source")
local function expect_end(exp, rem)
if rem:match("[^%s]") then
error(("expected end of expression before %q"):format(rem))
end
return exp
end
-- parse code (string) with the associated source (Source) -- parse code (string) with the associated source (Source)
-- the returned AST tree is stateless and can be stored/evaluated/etc as you please -- the returned AST tree is stateless and can be stored/evaluated/etc as you please
return function(code, source) return function(code, source)
local tree = code_to_tree(code, source) return expect_end(block(Source:new(source, 1, 1), code))
local block = tree_to_ast(tree)
return block
end end

View file

@ -1,52 +0,0 @@
--- transform a tree of lines into raw AST
local tree_to_block
local ast = require("anselme.ast")
local Block, Flush, PartialScope
local expression_to_ast = require("anselme.parser.expression.to_ast")
-- wrapper for expression_to_ast to check that there is no crap remaining after the expression has been parsed
-- return AST
local function expect_end(exp, rem)
if rem:match("[^%s]") then
error(("expected end of expression before %q"):format(rem))
end
return exp
end
local function expect_end_block(exp, rem)
if rem:match("[^%s]") and not rem:match("^ ?_$") then
error(("expected end of expression before %q"):format(rem))
end
return exp
end
-- return AST
local function line_to_expression(content, tree)
if #tree > 0 then
local child_block = tree_to_block(tree)
return PartialScope:attach_block(expect_end_block(expression_to_ast(tree.source:clone(), content.." _", " _$")), child_block):set_source(tree.source)
else
return expect_end(expression_to_ast(tree.source:clone(), content, nil, nil, nil, Flush:new())):set_source(tree.source)
end
end
-- return AST (Block)
tree_to_block = function(tree)
local block = Block:new()
for _, l in ipairs(tree) do
local s, expression = pcall(line_to_expression, l.content, l)
if not s then error(("%s; at %s"):format(expression, l.source), 0) end
block:add(expression)
end
return block
end
package.loaded[...] = tree_to_block
Block, Flush, PartialScope = ast.Block, ast.Flush, ast.PartialScope
return tree_to_block

View file

@ -10,7 +10,7 @@
--- error --- --- error ---
cancel merge cancel merge
↳ from test/tests/checkpoint merging mutable value.ans:24:6 in call: error("cancel merge") ↳ from test/tests/checkpoint merging mutable value.ans:24:6 in call: error("cancel merge")
↳ from ? in block: :l = *[1, 2]… ↳ from test/tests/checkpoint merging mutable value.ans:1:1 in block: :l = *[1, 2]…
--# post run check #-- --# post run check #--
--- text --- --- text ---
| {}"1,2,3,4: " {}"*[1, 2, 3, 4]" {}"" | | {}"1,2,3,4: " {}"*[1, 2, 3, 4]" {}"" |

View file

@ -10,7 +10,7 @@
--- error --- --- error ---
cancel merge cancel merge
↳ from test/tests/checkpoint merging variable.ans:24:6 in call: error("cancel merge") ↳ from test/tests/checkpoint merging variable.ans:24:6 in call: error("cancel merge")
↳ from ? in block: :l = 1… ↳ from test/tests/checkpoint merging variable.ans:1:1 in block: :l = 1…
--# post run check #-- --# post run check #--
--- text --- --- text ---
| {}"3: " {}"3" {}"" | | {}"3: " {}"3" {}"" |

View file

@ -5,6 +5,6 @@
no variable "y" defined in closure no variable "y" defined in closure
↳ from test/tests/closure define nested.ans:12:4 in call: f . "y" ↳ from test/tests/closure define nested.ans:12:4 in call: f . "y"
↳ from test/tests/closure define nested.ans:12:1 in text interpolation: | {f . "y"} | ↳ from test/tests/closure define nested.ans:12:1 in text interpolation: | {f . "y"} |
↳ from ? in block: :f = ($() _)… ↳ from test/tests/closure define nested.ans:1:1 in block: :f = ($() _)…
--# saved #-- --# saved #--
{} {}

View file

@ -2,6 +2,6 @@
--- error --- --- error ---
trying to change the value of constant a trying to change the value of constant a
↳ from test/tests/constant variable.ans:5:3 in call: a = 52 ↳ from test/tests/constant variable.ans:5:3 in call: a = 52
↳ from ? in block: ::a = 3… ↳ from test/tests/constant variable.ans:1:1 in block: ::a = 3…
--# saved #-- --# saved #--
{} {}

View file

@ -6,6 +6,6 @@
--- error --- --- error ---
value check failure for weigh; 32 does not satisfy $(x) type(x) == t value check failure for weigh; 32 does not satisfy $(x) type(x) == t
↳ from test/tests/constrained variable assignement.ans:9:7 in call: weigh = 32 ↳ from test/tests/constrained variable assignement.ans:9:7 in call: weigh = 32
↳ from ? in block: :weigh::is("kg") = type(5, "kg")… ↳ from test/tests/constrained variable assignement.ans:1:1 in block: :weigh::is("kg") = type(5, "kg")…
--# saved #-- --# saved #--
{} {}

View file

@ -2,6 +2,6 @@
--- error --- --- error ---
a is already defined in the current scope a is already defined in the current scope
↳ from test/tests/define override function.ans:4:4 in call: :a = 2 ↳ from test/tests/define override function.ans:4:4 in call: :a = 2
↳ from ? in block: :a = ($() _)… ↳ from test/tests/define override function.ans:1:1 in block: :a = ($() _)…
--# saved #-- --# saved #--
{} {}

View file

@ -2,6 +2,6 @@
--- error --- --- error ---
can't add an overload variant to non-overloadable variable a defined in the same scope can't add an overload variant to non-overloadable variable a defined in the same scope
↳ from test/tests/define override variable.ans:3:1 in call: :a = ($() _) ↳ from test/tests/define override variable.ans:3:1 in call: :a = ($() _)
↳ from ? in block: :a = 2… ↳ from test/tests/define override variable.ans:1:1 in block: :a = 2…
--# saved #-- --# saved #--
{} {}

View file

@ -2,6 +2,6 @@
--- error --- --- error ---
a is already defined in the current scope a is already defined in the current scope
↳ from test/tests/define override.ans:3:4 in call: :a = 2 ↳ from test/tests/define override.ans:3:4 in call: :a = 2
↳ from ? in block: :a = 5… ↳ from test/tests/define override.ans:1:1 in block: :a = 5…
--# saved #-- --# saved #--
{} {}

View file

@ -2,6 +2,6 @@
--- error --- --- error ---
nope nope
↳ from test/tests/error.ans:1:6 in call: error("nope") ↳ from test/tests/error.ans:1:6 in call: error("nope")
↳ from ? in block: error("nope") ↳ from test/tests/error.ans:1:1 in block: error("nope")
--# saved #-- --# saved #--
{} {}

View file

@ -8,6 +8,6 @@
identifier "z" is undefined in branch test/tests/exported variable nested.ans - run identifier "z" is undefined in branch test/tests/exported variable nested.ans - run
↳ from test/tests/exported variable nested.ans:12:3 in identifier: z ↳ from test/tests/exported variable nested.ans:12:3 in identifier: z
↳ from test/tests/exported variable nested.ans:12:1 in text interpolation: | {z} | ↳ from test/tests/exported variable nested.ans:12:1 in text interpolation: | {z} |
↳ from ? in block: :f = ($() _)… ↳ from test/tests/exported variable nested.ans:1:1 in block: :f = ($() _)…
--# saved #-- --# saved #--
{} {}

View file

@ -11,9 +11,9 @@
value check failure for parameter s value check failure for parameter s
↳ from stdlib/for.ans:3:18 in call: iter(var) ↳ from stdlib/for.ans:3:18 in call: iter(var)
↳ from stdlib/for.ans:3:12 in call: :iterator = iter(var) ↳ from stdlib/for.ans:3:12 in call: :iterator = iter(var)
↳ from stdlib/for.ans:2:1 in block: :iterator = iter(var)… ↳ from stdlib/for.ans:3:1 in block: :iterator = iter(var)…
↳ from stdlib/for.ans:2:71 in call: _ ↳ from stdlib/for.ans:2:70 in call: _
↳ from test/tests/for invalid iterator.ans:1:4 in call: for(:x, 42) ↳ from test/tests/for invalid iterator.ans:1:4 in call: for(:x, 42)
↳ from ? in block: for(:x, 42)… ↳ from test/tests/for invalid iterator.ans:1:1 in block: for(:x, 42)…
--# saved #-- --# saved #--
{} {}

View file

@ -2,6 +2,6 @@
--- error --- --- error ---
can't call function f: expected 2 arguments, received 1 can't call function f: expected 2 arguments, received 1
↳ from test/tests/function args arity check fail.ans:4:2 in call: f("ok") ↳ from test/tests/function args arity check fail.ans:4:2 in call: f("ok")
↳ from ? in block: :f = ($(a, b) _)… ↳ from test/tests/function args arity check fail.ans:1:1 in block: :f = ($(a, b) _)…
--# saved #-- --# saved #--
{} {}

View file

@ -2,6 +2,6 @@
--- error --- --- error ---
a function with parameters $(a, b) is already defined in the overload a function with parameters $(a, b) is already defined in the overload
↳ from test/tests/function conflict.ans:5:1 in call: :f = ($(a, b) 0) ↳ from test/tests/function conflict.ans:5:1 in call: :f = ($(a, b) 0)
↳ from ? in block: :f = ($(a, b) 0)… ↳ from test/tests/function conflict.ans:1:1 in block: :f = ($(a, b) 0)…
--# saved #-- --# saved #--
{} {}

View file

@ -10,6 +10,6 @@
• $(name::($(x) type(x) == t)) (from test/tests/function custom type dispatch error.ans:4:1): • $(name::($(x) type(x) == t)) (from test/tests/function custom type dispatch error.ans:4:1):
value check failure for parameter name value check failure for parameter name
↳ from test/tests/function custom type dispatch error.ans:14:2 in call: a(type(5, "nope")) ↳ from test/tests/function custom type dispatch error.ans:14:2 in call: a(type(5, "nope"))
↳ from ? in block: :french name = "french name"… ↳ from test/tests/function custom type dispatch error.ans:1:1 in block: :french name = "french name"…
--# saved #-- --# saved #--
{} {}

View file

@ -3,6 +3,6 @@
identifier "b" is undefined in branch test/tests/function scope wrong.ans - run identifier "b" is undefined in branch test/tests/function scope wrong.ans - run
↳ from test/tests/function scope wrong.ans:4:7 in identifier: b ↳ from test/tests/function scope wrong.ans:4:7 in identifier: b
↳ from test/tests/function scope wrong.ans:4:1 in text interpolation: | a: {b} | ↳ from test/tests/function scope wrong.ans:4:1 in text interpolation: | a: {b} |
↳ from ? in block: :a = ($() _)… ↳ from test/tests/function scope wrong.ans:1:1 in block: :a = ($() _)…
--# saved #-- --# saved #--
{} {}

View file

@ -21,6 +21,6 @@
value check failure for parameter c value check failure for parameter c
↳ from test/tests/function separate variable from variants.ans:10:4 in call: f . "a" ↳ from test/tests/function separate variable from variants.ans:10:4 in call: f . "a"
↳ from test/tests/function separate variable from variants.ans:10:1 in text interpolation: | {f . "a"} = 2 | ↳ from test/tests/function separate variable from variants.ans:10:1 in text interpolation: | {f . "a"} = 2 |
↳ from ? in block: :f = ($() _)… ↳ from test/tests/function separate variable from variants.ans:1:1 in block: :f = ($() _)…
--# saved #-- --# saved #--
{} {}

View file

@ -4,6 +4,6 @@
• $(x::is number) (from test/tests/function type dispatch ambigous.ans:1:1) • $(x::is number) (from test/tests/function type dispatch ambigous.ans:1:1)
• $(a::is number) (from test/tests/function type dispatch ambigous.ans:4:1) • $(a::is number) (from test/tests/function type dispatch ambigous.ans:4:1)
↳ from test/tests/function type dispatch ambigous.ans:7:3 in call: fn(5) ↳ from test/tests/function type dispatch ambigous.ans:7:3 in call: fn(5)
↳ from ? in block: :fn = ($(x::is number) _)… ↳ from test/tests/function type dispatch ambigous.ans:1:1 in block: :fn = ($(x::is number) _)…
--# saved #-- --# saved #--
{} {}

View file

@ -12,6 +12,6 @@
--- error --- --- error ---
list index out of bounds list index out of bounds
↳ from test/tests/list assignement.ans:21:6 in call: x(5) = 0 ↳ from test/tests/list assignement.ans:21:6 in call: x(5) = 0
↳ from ? in block: :x = *[1, 2]… ↳ from test/tests/list assignement.ans:1:1 in block: :x = *[1, 2]…
--# saved #-- --# saved #--
{} {}

View file

@ -11,6 +11,6 @@
tuple index out of bounds tuple index out of bounds
↳ from test/tests/list index.ans:11:4 in call: x(-4) ↳ from test/tests/list index.ans:11:4 in call: x(-4)
↳ from test/tests/list index.ans:11:1 in text interpolation: | {x(-4)} | ↳ from test/tests/list index.ans:11:1 in text interpolation: | {x(-4)} |
↳ from ? in block: :x = [1, 2, 3]… ↳ from test/tests/list index.ans:1:1 in block: :x = [1, 2, 3]…
--# saved #-- --# saved #--
{} {}

View file

@ -3,6 +3,6 @@
no variable "b" defined in environment no variable "b" defined in environment
↳ from test/tests/load file error.ans:3:4 in call: e . "b" ↳ from test/tests/load file error.ans:3:4 in call: e . "b"
↳ from test/tests/load file error.ans:3:1 in text interpolation: | {e . "b"} | ↳ from test/tests/load file error.ans:3:1 in text interpolation: | {e . "b"} |
↳ from ? in block: :e = load("test/tests/import/test.ans")… ↳ from test/tests/load file error.ans:1:1 in block: :e = load("test/tests/import/test.ans")…
--# saved #-- --# saved #--
{} {}

View file

@ -2,18 +2,18 @@
--- error --- --- error ---
abort abort
↳ from test/tests/merge nested mutable error bis.ans:14:7 in call: error("abort") ↳ from test/tests/merge nested mutable error bis.ans:14:7 in call: error("abort")
↳ from test/tests/merge nested mutable error bis.ans:3:1 in block: insert(a, b)… ↳ from test/tests/merge nested mutable error bis.ans:4:1 in block: insert(a, b)…
↳ from test/tests/merge nested mutable error bis.ans:3:18 in call: _ ↳ from test/tests/merge nested mutable error bis.ans:3:18 in call: _
↳ from stdlib/script.ans:31:6 in call: fn! ↳ from stdlib/script.ans:31:6 in call: fn!
↳ from stdlib/script.ans:29:3 in block: resume target = ()… ↳ from stdlib/script.ans:30:1 in block: resume target = ()…
↳ from stdlib/script.ans:29:7 in call: else! ↳ from stdlib/script.ans:29:7 in call: else!
↳ from stdlib/script.ans:25:2 in block: if(fn . "current checkpoint")… ↳ from stdlib/script.ans:26:1 in block: if(fn . "current checkpoint")…
↳ from stdlib/script.ans:25:9 in call: _ ↳ from stdlib/script.ans:25:8 in call: _
↳ from stdlib/script.ans:39:9 in call: value(s)! ↳ from stdlib/script.ans:39:9 in call: value(s)!
↳ from stdlib/script.ans:38:1 in block: value(s)! ↳ from stdlib/script.ans:39:1 in block: value(s)!
↳ from stdlib/script.ans:38:20 in call: _ ↳ from stdlib/script.ans:38:19 in call: _
↳ from test/tests/merge nested mutable error bis.ans:19:2 in call: f! ↳ from test/tests/merge nested mutable error bis.ans:19:2 in call: f!
↳ from ? in block: :a = *[1]… ↳ from test/tests/merge nested mutable error bis.ans:1:1 in block: :a = *[1]…
--# post run check #-- --# post run check #--
--- text --- --- text ---
| {}"[1,[2,3]]: " {}"*[1, *[2, 3]]" {}"" | | {}"[1,[2,3]]: " {}"*[1, *[2, 3]]" {}"" |

View file

@ -2,18 +2,18 @@
--- error --- --- error ---
abort abort
↳ from test/tests/merge nested mutable error.ans:14:7 in call: error("abort") ↳ from test/tests/merge nested mutable error.ans:14:7 in call: error("abort")
↳ from test/tests/merge nested mutable error.ans:3:1 in block: insert(a, b)… ↳ from test/tests/merge nested mutable error.ans:4:1 in block: insert(a, b)…
↳ from test/tests/merge nested mutable error.ans:3:18 in call: _ ↳ from test/tests/merge nested mutable error.ans:3:18 in call: _
↳ from stdlib/script.ans:31:6 in call: fn! ↳ from stdlib/script.ans:31:6 in call: fn!
↳ from stdlib/script.ans:29:3 in block: resume target = ()… ↳ from stdlib/script.ans:30:1 in block: resume target = ()…
↳ from stdlib/script.ans:29:7 in call: else! ↳ from stdlib/script.ans:29:7 in call: else!
↳ from stdlib/script.ans:25:2 in block: if(fn . "current checkpoint")… ↳ from stdlib/script.ans:26:1 in block: if(fn . "current checkpoint")…
↳ from stdlib/script.ans:25:9 in call: _ ↳ from stdlib/script.ans:25:8 in call: _
↳ from stdlib/script.ans:39:9 in call: value(s)! ↳ from stdlib/script.ans:39:9 in call: value(s)!
↳ from stdlib/script.ans:38:1 in block: value(s)! ↳ from stdlib/script.ans:39:1 in block: value(s)!
↳ from stdlib/script.ans:38:20 in call: _ ↳ from stdlib/script.ans:38:19 in call: _
↳ from test/tests/merge nested mutable error.ans:19:2 in call: f! ↳ from test/tests/merge nested mutable error.ans:19:2 in call: f!
↳ from ? in block: :a = *[1]… ↳ from test/tests/merge nested mutable error.ans:1:1 in block: :a = *[1]…
--# post run check #-- --# post run check #--
--- text --- --- text ---
| {}"[1,[2,3]]: " {}"*[1, *[2, 3]]" {}"" | | {}"[1,[2,3]]: " {}"*[1, *[2, 3]]" {}"" |

View file

@ -16,15 +16,15 @@
--- error --- --- error ---
t t
↳ from test/tests/scope checkpoint mutable bis error.ans:32:7 in call: error("t") ↳ from test/tests/scope checkpoint mutable bis error.ans:32:7 in call: error("t")
↳ from test/tests/scope checkpoint mutable bis error.ans:7:1 in block: insert(t, len(l) + 1)… ↳ from test/tests/scope checkpoint mutable bis error.ans:8:1 in block: insert(t, len(l) + 1)…
↳ from test/tests/scope checkpoint mutable bis error.ans:7:8 in call: _ ↳ from test/tests/scope checkpoint mutable bis error.ans:7:7 in call: _
↳ from test/tests/scope checkpoint mutable bis error.ans:19:4 in call: f(t) ↳ from test/tests/scope checkpoint mutable bis error.ans:19:4 in call: f(t)
↳ from test/tests/scope checkpoint mutable bis error.ans:15:2 in block: | REC |… ↳ from test/tests/scope checkpoint mutable bis error.ans:16:1 in block: | REC |…
↳ from test/tests/scope checkpoint mutable bis error.ans:15:4 in call: if(n < 1) ↳ from test/tests/scope checkpoint mutable bis error.ans:15:4 in call: if(n < 1)
↳ from test/tests/scope checkpoint mutable bis error.ans:7:1 in block: insert(t, len(l) + 1)… ↳ from test/tests/scope checkpoint mutable bis error.ans:8:1 in block: insert(t, len(l) + 1)…
↳ from test/tests/scope checkpoint mutable bis error.ans:7:8 in call: _ ↳ from test/tests/scope checkpoint mutable bis error.ans:7:7 in call: _
↳ from test/tests/scope checkpoint mutable bis error.ans:41:2 in call: f(l) ↳ from test/tests/scope checkpoint mutable bis error.ans:41:2 in call: f(l)
↳ from ? in block: :x = *[99]… ↳ from test/tests/scope checkpoint mutable bis error.ans:1:1 in block: :x = *[99]…
--# post run check #-- --# post run check #--
--- text --- --- text ---
| {}"AFTER ERROR" | | {}"AFTER ERROR" |

View file

@ -12,15 +12,15 @@
--- error --- --- error ---
t t
↳ from test/tests/scope checkpoint mutable error.ans:23:7 in call: error("t") ↳ from test/tests/scope checkpoint mutable error.ans:23:7 in call: error("t")
↳ from test/tests/scope checkpoint mutable error.ans:5:1 in block: insert(t, len(l) + 1)… ↳ from test/tests/scope checkpoint mutable error.ans:6:1 in block: insert(t, len(l) + 1)…
↳ from test/tests/scope checkpoint mutable error.ans:5:8 in call: _ ↳ from test/tests/scope checkpoint mutable error.ans:5:7 in call: _
↳ from test/tests/scope checkpoint mutable error.ans:17:4 in call: f(t) ↳ from test/tests/scope checkpoint mutable error.ans:17:4 in call: f(t)
↳ from test/tests/scope checkpoint mutable error.ans:13:2 in block: | REC |… ↳ from test/tests/scope checkpoint mutable error.ans:14:1 in block: | REC |…
↳ from test/tests/scope checkpoint mutable error.ans:13:4 in call: if(n < 1) ↳ from test/tests/scope checkpoint mutable error.ans:13:4 in call: if(n < 1)
↳ from test/tests/scope checkpoint mutable error.ans:5:1 in block: insert(t, len(l) + 1)… ↳ from test/tests/scope checkpoint mutable error.ans:6:1 in block: insert(t, len(l) + 1)…
↳ from test/tests/scope checkpoint mutable error.ans:5:8 in call: _ ↳ from test/tests/scope checkpoint mutable error.ans:5:7 in call: _
↳ from test/tests/scope checkpoint mutable error.ans:32:2 in call: f(l) ↳ from test/tests/scope checkpoint mutable error.ans:32:2 in call: f(l)
↳ from ? in block: :l = *[1]… ↳ from test/tests/scope checkpoint mutable error.ans:1:1 in block: :l = *[1]…
--# post run check #-- --# post run check #--
--- text --- --- text ---
| {}"AFTER ERROR" | | {}"AFTER ERROR" |

View file

@ -16,15 +16,15 @@
--- error --- --- error ---
t t
↳ from test/tests/scope checkpoint mutable ter error.ans:34:7 in call: error("t") ↳ from test/tests/scope checkpoint mutable ter error.ans:34:7 in call: error("t")
↳ from test/tests/scope checkpoint mutable ter error.ans:7:1 in block: insert(t, len(l) + 1)… ↳ from test/tests/scope checkpoint mutable ter error.ans:8:1 in block: insert(t, len(l) + 1)…
↳ from test/tests/scope checkpoint mutable ter error.ans:7:8 in call: _ ↳ from test/tests/scope checkpoint mutable ter error.ans:7:7 in call: _
↳ from test/tests/scope checkpoint mutable ter error.ans:19:4 in call: f(t) ↳ from test/tests/scope checkpoint mutable ter error.ans:19:4 in call: f(t)
↳ from test/tests/scope checkpoint mutable ter error.ans:15:2 in block: | REC |… ↳ from test/tests/scope checkpoint mutable ter error.ans:16:1 in block: | REC |…
↳ from test/tests/scope checkpoint mutable ter error.ans:15:4 in call: if(n < 1) ↳ from test/tests/scope checkpoint mutable ter error.ans:15:4 in call: if(n < 1)
↳ from test/tests/scope checkpoint mutable ter error.ans:7:1 in block: insert(t, len(l) + 1)… ↳ from test/tests/scope checkpoint mutable ter error.ans:8:1 in block: insert(t, len(l) + 1)…
↳ from test/tests/scope checkpoint mutable ter error.ans:7:8 in call: _ ↳ from test/tests/scope checkpoint mutable ter error.ans:7:7 in call: _
↳ from test/tests/scope checkpoint mutable ter error.ans:43:2 in call: f(l) ↳ from test/tests/scope checkpoint mutable ter error.ans:43:2 in call: f(l)
↳ from ? in block: :x = *[99]… ↳ from test/tests/scope checkpoint mutable ter error.ans:1:1 in block: :x = *[99]…
--# post run check #-- --# post run check #--
--- text --- --- text ---
| {}"AFTER ERROR" | | {}"AFTER ERROR" |

View file

@ -7,6 +7,6 @@
--- error --- --- error ---
trying to change the value of constant d trying to change the value of constant d
↳ from test/tests/symbol alias constant.ans:12:3 in call: d = 5 ↳ from test/tests/symbol alias constant.ans:12:3 in call: d = 5
↳ from ? in block: :l = *[1, 2, 3]… ↳ from test/tests/symbol alias constant.ans:1:1 in block: :l = *[1, 2, 3]…
--# saved #-- --# saved #--
{} {}