mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-28 00:59:31 +00:00
Preprend every definition line type with a colon, remove function decorator, add immediately ran definition line
This commit is contained in:
parent
7b756ad092
commit
d1f49d1894
117 changed files with 686 additions and 658 deletions
|
|
@ -62,15 +62,15 @@ local function parse(state)
|
|||
line.expression = exp
|
||||
-- variable pending definition: expression will be evaluated when variable is needed
|
||||
if line.type == "definition" then
|
||||
state.variables[line.fqm].value.expression = line.expression
|
||||
state.variables[line.name].value.expression = line.expression
|
||||
-- parse constraints
|
||||
if line.constraint then
|
||||
local type_exp, rem2 = expression(line.constraint, state, namespace)
|
||||
if not type_exp then return nil, ("in type constraint, %s; at %s"):format(rem2, line.source) end
|
||||
if rem2:match("[^%s]") then
|
||||
return nil, ("unexpected characters after variable %q: %q; at %s"):format(line.fqm, rem2, line.source)
|
||||
return nil, ("unexpected characters after variable %q: %q; at %s"):format(line.name, rem2, line.source)
|
||||
end
|
||||
state.variable_constraints[line.fqm] = { pending = type_exp }
|
||||
state.variable_constraints[line.name] = { pending = type_exp }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -53,77 +53,112 @@ local function parse_line(line, state, namespace, parent_function)
|
|||
r.type = "choice"
|
||||
r.child = true
|
||||
r.text = l:match("^>%s*(.-)$")
|
||||
-- function & checkpoint
|
||||
elseif l:match("^%$") or l:match("^§") or l:match("^%%") then -- § is a 2-bytes caracter, DO NOT USE LUA PATTERN OPERATORS as they operate on single bytes
|
||||
r.type = "function"
|
||||
r.child = true
|
||||
-- subtype options
|
||||
local allow_params = true
|
||||
local allow_assign = true
|
||||
local keep_in_ast = false
|
||||
if l:match("^%$") then
|
||||
r.subtype = "function"
|
||||
r.resume_boundary = true
|
||||
elseif l:match("^%%") then
|
||||
r.subtype = "class"
|
||||
r.resume_boundary = true
|
||||
allow_params = false
|
||||
allow_assign = false
|
||||
elseif l:match("^§") then
|
||||
r.subtype = "checkpoint"
|
||||
allow_params = false
|
||||
allow_assign = false
|
||||
keep_in_ast = true
|
||||
r.parent_function = parent_function -- store parent function and run checkpoint when line is read
|
||||
else
|
||||
error("unknown function line type")
|
||||
-- definition
|
||||
elseif l:match("^:") then
|
||||
local lr = l:match("^:(.*)$")
|
||||
-- immediately run variable
|
||||
local run_immediately = false
|
||||
if lr:match("^~") then
|
||||
lr = lr:match("^~(.*)$")
|
||||
run_immediately = true
|
||||
end
|
||||
-- don't keep function node in block AST
|
||||
if not keep_in_ast then
|
||||
r.remove_from_block_ast = true
|
||||
end
|
||||
-- lua function
|
||||
if r.subtype == "function" and state.global_state.link_next_function_definition_to_lua_function then
|
||||
r.lua_function = state.global_state.link_next_function_definition_to_lua_function
|
||||
state.global_state.link_next_function_definition_to_lua_function = nil
|
||||
end
|
||||
-- get identifier
|
||||
local lc = l:match("^[%$%%](.-)$") or l:match("^§(.-)$")
|
||||
local identifier, rem = lc:match("^("..identifier_pattern..")(.-)$")
|
||||
if not identifier then
|
||||
for _, name in ipairs(special_functions_names) do
|
||||
identifier, rem = lc:match("^(%s*"..escape(name).."%s*)(.-)$")
|
||||
if identifier then break end
|
||||
-- function & checkpoint
|
||||
if lr:match("^%$") or lr:match("^%!") or lr:match("^%%") then -- § is a 2-bytes caracter, DO NOT USE LUA PATTERN OPERATORS as they operate on single bytes
|
||||
r.type = "function"
|
||||
r.child = true
|
||||
-- subtype options
|
||||
local allow_params = true
|
||||
local allow_assign = true
|
||||
local keep_in_ast = false
|
||||
if lr:match("^%$") then
|
||||
r.subtype = "function"
|
||||
r.resume_boundary = true
|
||||
elseif lr:match("^%%") then
|
||||
r.subtype = "class"
|
||||
r.resume_boundary = true
|
||||
allow_params = false
|
||||
allow_assign = false
|
||||
elseif lr:match("^%!") then
|
||||
r.subtype = "checkpoint"
|
||||
allow_params = false
|
||||
allow_assign = false
|
||||
keep_in_ast = true
|
||||
r.parent_function = parent_function -- store parent function and run checkpoint when line is read
|
||||
else
|
||||
error("unknown function line type")
|
||||
end
|
||||
end
|
||||
if not identifier then
|
||||
return nil, ("no valid identifier in function definition line %q; at %s"):format(lc, line.source)
|
||||
end
|
||||
-- format identifier
|
||||
local fqm = ("%s%s"):format(namespace, format_identifier(identifier))
|
||||
local func_namespace = fqm .. "."
|
||||
-- get alias
|
||||
local ok_alias
|
||||
ok_alias, rem = maybe_alias(rem, fqm, namespace, line, state)
|
||||
if not ok_alias then return ok_alias, rem end
|
||||
-- anything else are argument, isolate function it its own namespace
|
||||
-- (to not mix its args and variables with the main variant)
|
||||
if rem:match("[^%s]") then
|
||||
func_namespace = ("%s(%s)."):format(fqm, tostring(r))
|
||||
r.private_namespace = true
|
||||
end
|
||||
-- define function
|
||||
if state.variables[fqm] then return nil, ("trying to define %s %s, but a variable with the same name exists; at %s"):format(r.type, fqm, line.source) end
|
||||
r.namespace = func_namespace
|
||||
r.name = fqm
|
||||
-- get params
|
||||
r.params = {}
|
||||
if allow_params and rem:match("^%b()") then
|
||||
r.scoped = true
|
||||
local content
|
||||
content, rem = rem:match("^(%b())%s*(.*)$")
|
||||
content = content:gsub("^%(", ""):gsub("%)$", "")
|
||||
for param in content:gmatch("[^%,]+") do
|
||||
-- don't keep function node in block AST
|
||||
if not keep_in_ast then
|
||||
r.remove_from_block_ast = true
|
||||
end
|
||||
-- lua function
|
||||
if r.subtype == "function" and state.global_state.link_next_function_definition_to_lua_function then
|
||||
r.lua_function = state.global_state.link_next_function_definition_to_lua_function
|
||||
state.global_state.link_next_function_definition_to_lua_function = nil
|
||||
end
|
||||
-- get identifier
|
||||
local lc = lr:match("^[%$%%%!](.-)$")
|
||||
local identifier, rem = lc:match("^("..identifier_pattern..")(.-)$")
|
||||
if not identifier then
|
||||
for _, name in ipairs(special_functions_names) do
|
||||
identifier, rem = lc:match("^(%s*"..escape(name).."%s*)(.-)$")
|
||||
if identifier then break end
|
||||
end
|
||||
end
|
||||
if not identifier then
|
||||
return nil, ("no valid identifier in function definition line %q; at %s"):format(lc, line.source)
|
||||
end
|
||||
-- format identifier
|
||||
local fqm = ("%s%s"):format(namespace, format_identifier(identifier))
|
||||
local func_namespace = fqm .. "."
|
||||
-- get alias
|
||||
local ok_alias
|
||||
ok_alias, rem = maybe_alias(rem, fqm, namespace, line, state)
|
||||
if not ok_alias then return ok_alias, rem end
|
||||
-- anything else are argument, isolate function it its own namespace
|
||||
-- (to not mix its args and variables with the main variant)
|
||||
if rem:match("[^%s]") then
|
||||
func_namespace = ("%s(%s)."):format(fqm, tostring(r))
|
||||
r.private_namespace = true
|
||||
end
|
||||
-- define function
|
||||
if state.variables[fqm] then return nil, ("trying to define %s %s, but a variable with the same name exists; at %s"):format(r.type, fqm, line.source) end
|
||||
r.namespace = func_namespace
|
||||
r.name = fqm
|
||||
-- get params
|
||||
r.params = {}
|
||||
if allow_params and rem:match("^%b()") then
|
||||
r.scoped = true
|
||||
local content
|
||||
content, rem = rem:match("^(%b())%s*(.*)$")
|
||||
content = content:gsub("^%(", ""):gsub("%)$", "")
|
||||
for param in content:gmatch("[^%,]+") do
|
||||
-- get identifier
|
||||
local param_identifier, param_rem = param:match("^("..identifier_pattern..")(.-)$")
|
||||
if not param_identifier then return nil, ("no valid identifier in function parameter %q; at %s"):format(param, line.source) end
|
||||
param_identifier = format_identifier(param_identifier)
|
||||
-- format identifier
|
||||
local param_fqm = ("%s%s"):format(func_namespace, param_identifier)
|
||||
-- get alias
|
||||
local ok_param_alias, param_alias
|
||||
ok_param_alias, param_rem, param_alias = maybe_alias(param_rem, param_fqm, func_namespace, line, state)
|
||||
if not ok_param_alias then return ok_param_alias, param_rem end
|
||||
-- get potential type constraints and default value
|
||||
local type_constraint, default
|
||||
if param_rem:match("^::") then
|
||||
type_constraint = param_rem:match("^::(.*)$")
|
||||
elseif param_rem:match("^=") then
|
||||
default = param_rem:match("^=(.*)$")
|
||||
elseif param_rem:match("[^%s]") then
|
||||
return nil, ("unexpected characters after parameter %q: %q; at %s"):format(param_fqm, param_rem, line.source)
|
||||
end
|
||||
-- add parameter
|
||||
table.insert(r.params, { name = param_identifier, alias = param_alias, full_name = param_fqm, type_constraint = type_constraint, default = default, vararg = nil })
|
||||
end
|
||||
end
|
||||
-- get assignment param
|
||||
if allow_assign and rem:match("^%:%=") then
|
||||
local param = rem:match("^%:%=(.*)$")
|
||||
-- get identifier
|
||||
local param_identifier, param_rem = param:match("^("..identifier_pattern..")(.-)$")
|
||||
if not param_identifier then return nil, ("no valid identifier in function parameter %q; at %s"):format(param, line.source) end
|
||||
|
|
@ -134,199 +169,178 @@ local function parse_line(line, state, namespace, parent_function)
|
|||
local ok_param_alias, param_alias
|
||||
ok_param_alias, param_rem, param_alias = maybe_alias(param_rem, param_fqm, func_namespace, line, state)
|
||||
if not ok_param_alias then return ok_param_alias, param_rem end
|
||||
-- get potential type constraints and default value
|
||||
local type_constraint, default
|
||||
-- get potential type constraint
|
||||
local type_constraint
|
||||
if param_rem:match("^::") then
|
||||
type_constraint = param_rem:match("^::(.*)$")
|
||||
elseif param_rem:match("^=") then
|
||||
default = param_rem:match("^=(.*)$")
|
||||
elseif param_rem:match("[^%s]") then
|
||||
return nil, ("unexpected characters after parameter %q: %q; at %s"):format(param_fqm, param_rem, line.source)
|
||||
end
|
||||
-- add parameter
|
||||
table.insert(r.params, { name = param_identifier, alias = param_alias, full_name = param_fqm, type_constraint = type_constraint, default = default, vararg = nil })
|
||||
r.assignment = { name = param_identifier, alias = param_alias, full_name = param_fqm, type_constraint = type_constraint, default = nil, vararg = nil }
|
||||
elseif rem:match("[^%s]") then
|
||||
return nil, ("expected end-of-line at end of function definition line, but got %q; at %s"):format(rem, line.source)
|
||||
end
|
||||
end
|
||||
-- get assignment param
|
||||
if allow_assign and rem:match("^%:%=") then
|
||||
local param = rem:match("^%:%=(.*)$")
|
||||
-- get identifier
|
||||
local param_identifier, param_rem = param:match("^("..identifier_pattern..")(.-)$")
|
||||
if not param_identifier then return nil, ("no valid identifier in function parameter %q; at %s"):format(param, line.source) end
|
||||
param_identifier = format_identifier(param_identifier)
|
||||
-- format identifier
|
||||
local param_fqm = ("%s%s"):format(func_namespace, param_identifier)
|
||||
-- get alias
|
||||
local ok_param_alias, param_alias
|
||||
ok_param_alias, param_rem, param_alias = maybe_alias(param_rem, param_fqm, func_namespace, line, state)
|
||||
if not ok_param_alias then return ok_param_alias, param_rem end
|
||||
-- get potential type constraint
|
||||
local type_constraint
|
||||
if param_rem:match("^::") then
|
||||
type_constraint = param_rem:match("^::(.*)$")
|
||||
elseif param_rem:match("[^%s]") then
|
||||
return nil, ("unexpected characters after parameter %q: %q; at %s"):format(param_fqm, param_rem, line.source)
|
||||
-- calculate arity
|
||||
local minarity, maxarity = #r.params, #r.params
|
||||
for _, param in ipairs(r.params) do -- params with default values
|
||||
if param.default then
|
||||
minarity = minarity - 1
|
||||
end
|
||||
end
|
||||
-- add parameter
|
||||
r.assignment = { name = param_identifier, alias = param_alias, full_name = param_fqm, type_constraint = type_constraint, default = nil, vararg = nil }
|
||||
elseif rem:match("[^%s]") then
|
||||
return nil, ("expected end-of-line at end of function definition line, but got %q; at %s"):format(rem, line.source)
|
||||
end
|
||||
-- calculate arity
|
||||
local minarity, maxarity = #r.params, #r.params
|
||||
for _, param in ipairs(r.params) do -- params with default values
|
||||
if param.default then
|
||||
-- varargs
|
||||
if maxarity > 0 and r.params[maxarity].full_name:match("%.%.%.$") then
|
||||
r.params[maxarity].name = r.params[maxarity].name:match("^(.*)%.%.%.$")
|
||||
r.params[maxarity].full_name = r.params[maxarity].full_name:match("^(.*)%.%.%.$")
|
||||
r.params[maxarity].vararg = true
|
||||
minarity = minarity - 1
|
||||
maxarity = math.huge
|
||||
end
|
||||
end
|
||||
-- varargs
|
||||
if maxarity > 0 and r.params[maxarity].full_name:match("%.%.%.$") then
|
||||
r.params[maxarity].name = r.params[maxarity].name:match("^(.*)%.%.%.$")
|
||||
r.params[maxarity].full_name = r.params[maxarity].full_name:match("^(.*)%.%.%.$")
|
||||
r.params[maxarity].vararg = true
|
||||
minarity = minarity - 1
|
||||
maxarity = math.huge
|
||||
end
|
||||
r.arity = { minarity, maxarity }
|
||||
r.signature = signature(r)
|
||||
r.pretty_signature = pretty_signature(r)
|
||||
-- check for signature conflict with functions with the same fqm
|
||||
if state.functions[fqm] then
|
||||
for _, variant in ipairs(state.functions[fqm]) do
|
||||
if r.signature == variant.signature then
|
||||
return nil, ("trying to define %s %s, but another function with same signature %s exists; at %s"):format(r.type, r.pretty_signature, variant.pretty_signature, line.source)
|
||||
r.arity = { minarity, maxarity }
|
||||
r.signature = signature(r)
|
||||
r.pretty_signature = pretty_signature(r)
|
||||
-- check for signature conflict with functions with the same fqm
|
||||
if state.functions[fqm] then
|
||||
for _, variant in ipairs(state.functions[fqm]) do
|
||||
if r.signature == variant.signature then
|
||||
return nil, ("trying to define %s %s, but another function with same signature %s exists; at %s"):format(r.type, r.pretty_signature, variant.pretty_signature, line.source)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- define variables
|
||||
if not line.children then line.children = {} end
|
||||
-- define 👁️ variable
|
||||
local seen_alias = state.global_state.builtin_aliases["👁️"]
|
||||
if seen_alias then
|
||||
table.insert(line.children, 1, { content = (":👁️:%s=0"):format(seen_alias), source = line.source })
|
||||
else
|
||||
table.insert(line.children, 1, { content = ":👁️=0", source = line.source })
|
||||
end
|
||||
if r.subtype ~= "checkpoint" then
|
||||
-- define 🔖 variable
|
||||
local checkpoint_alias = state.global_state.builtin_aliases["🔖"]
|
||||
if checkpoint_alias then
|
||||
table.insert(line.children, 1, { content = (":🔖:%s=()"):format(checkpoint_alias), source = line.source })
|
||||
-- define variables
|
||||
if not line.children then line.children = {} end
|
||||
-- define 👁️ variable
|
||||
local seen_alias = state.global_state.builtin_aliases["👁️"]
|
||||
if seen_alias then
|
||||
table.insert(line.children, 1, { content = (":👁️:%s=0"):format(seen_alias), source = line.source })
|
||||
else
|
||||
table.insert(line.children, 1, { content = ":🔖=()", source = line.source })
|
||||
table.insert(line.children, 1, { content = ":👁️=0", source = line.source })
|
||||
end
|
||||
-- custom code injection
|
||||
if r.scoped then
|
||||
if state.inject.scoped_function_start then
|
||||
for i, ll in ipairs(state.inject.scoped_function_start) do
|
||||
if r.subtype ~= "checkpoint" then
|
||||
-- define 🔖 variable
|
||||
local checkpoint_alias = state.global_state.builtin_aliases["🔖"]
|
||||
if checkpoint_alias then
|
||||
table.insert(line.children, 1, { content = (":🔖:%s=()"):format(checkpoint_alias), source = line.source })
|
||||
else
|
||||
table.insert(line.children, 1, { content = ":🔖=()", source = line.source })
|
||||
end
|
||||
-- custom code injection
|
||||
if r.scoped then
|
||||
if state.inject.scoped_function_start then
|
||||
for i, ll in ipairs(state.inject.scoped_function_start) do
|
||||
table.insert(line.children, 1+i, copy(ll))
|
||||
end
|
||||
end
|
||||
if state.inject.scoped_function_end then
|
||||
for _, ll in ipairs(state.inject.scoped_function_end) do
|
||||
table.insert(line.children, copy(ll))
|
||||
end
|
||||
end
|
||||
else
|
||||
if state.inject.function_start then
|
||||
for i, ll in ipairs(state.inject.function_start) do
|
||||
table.insert(line.children, 1+i, copy(ll))
|
||||
end
|
||||
end
|
||||
if state.inject.function_end then
|
||||
for _, ll in ipairs(state.inject.function_end) do
|
||||
table.insert(line.children, copy(ll))
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif r.subtype == "checkpoint" then
|
||||
-- define 🏁 variable
|
||||
local reached_alias = state.global_state.builtin_aliases["🏁"]
|
||||
if reached_alias then
|
||||
table.insert(line.children, 1, { content = (":🏁:%s=0"):format(reached_alias), source = line.source })
|
||||
else
|
||||
table.insert(line.children, 1, { content = ":🏁=0", source = line.source })
|
||||
end
|
||||
-- custom code injection
|
||||
if state.inject.checkpoint_start then
|
||||
for i, ll in ipairs(state.inject.checkpoint_start) do
|
||||
table.insert(line.children, 1+i, copy(ll))
|
||||
end
|
||||
end
|
||||
if state.inject.scoped_function_end then
|
||||
for _, ll in ipairs(state.inject.scoped_function_end) do
|
||||
table.insert(line.children, copy(ll))
|
||||
end
|
||||
end
|
||||
else
|
||||
if state.inject.function_start then
|
||||
for i, ll in ipairs(state.inject.function_start) do
|
||||
table.insert(line.children, 1+i, copy(ll))
|
||||
end
|
||||
end
|
||||
if state.inject.function_end then
|
||||
for _, ll in ipairs(state.inject.function_end) do
|
||||
if state.inject.checkpoint_end then
|
||||
for _, ll in ipairs(state.inject.checkpoint_end) do
|
||||
table.insert(line.children, copy(ll))
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif r.subtype == "checkpoint" then
|
||||
-- define 🏁 variable
|
||||
local reached_alias = state.global_state.builtin_aliases["🏁"]
|
||||
if reached_alias then
|
||||
table.insert(line.children, 1, { content = (":🏁:%s=0"):format(reached_alias), source = line.source })
|
||||
else
|
||||
table.insert(line.children, 1, { content = ":🏁=0", source = line.source })
|
||||
end
|
||||
-- custom code injection
|
||||
if state.inject.checkpoint_start then
|
||||
for i, ll in ipairs(state.inject.checkpoint_start) do
|
||||
table.insert(line.children, 1+i, copy(ll))
|
||||
-- define args
|
||||
for _, param in ipairs(r.params) do
|
||||
if not state.variables[param.full_name] then
|
||||
state.variables[param.full_name] = {
|
||||
type = "undefined argument",
|
||||
value = nil
|
||||
}
|
||||
else
|
||||
return nil, ("trying to define parameter %q, but a variable with the same name exists; at %s"):format(param.full_name, line.source)
|
||||
end
|
||||
end
|
||||
if state.inject.checkpoint_end then
|
||||
for _, ll in ipairs(state.inject.checkpoint_end) do
|
||||
table.insert(line.children, copy(ll))
|
||||
if r.assignment then
|
||||
if not state.variables[r.assignment.full_name] then
|
||||
state.variables[r.assignment.full_name] = {
|
||||
type = "undefined argument",
|
||||
value = nil
|
||||
}
|
||||
else
|
||||
return nil, ("trying to define parameter %q, but a variable with the same name exists; at %s"):format(r.assignment.full_name, line.source)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- define args
|
||||
for _, param in ipairs(r.params) do
|
||||
if not state.variables[param.full_name] then
|
||||
state.variables[param.full_name] = {
|
||||
type = "undefined argument",
|
||||
value = nil
|
||||
}
|
||||
-- define new function, no other variant yet
|
||||
if not state.functions[fqm] then
|
||||
state.functions[fqm] = { r }
|
||||
-- overloading
|
||||
else
|
||||
return nil, ("trying to define parameter %q, but a variable with the same name exists; at %s"):format(param.full_name, line.source)
|
||||
table.insert(state.functions[fqm], r)
|
||||
end
|
||||
end
|
||||
if r.assignment then
|
||||
if not state.variables[r.assignment.full_name] then
|
||||
state.variables[r.assignment.full_name] = {
|
||||
type = "undefined argument",
|
||||
value = nil
|
||||
}
|
||||
else
|
||||
return nil, ("trying to define parameter %q, but a variable with the same name exists; at %s"):format(r.assignment.full_name, line.source)
|
||||
end
|
||||
end
|
||||
-- define new function, no other variant yet
|
||||
if not state.functions[fqm] then
|
||||
state.functions[fqm] = { r }
|
||||
-- overloading
|
||||
-- variable and constants
|
||||
else
|
||||
table.insert(state.functions[fqm], r)
|
||||
end
|
||||
-- definition
|
||||
elseif l:match("^:") then
|
||||
r.type = "definition"
|
||||
r.remove_from_block_ast = true
|
||||
local rem = l:match("^:(.*)$")
|
||||
-- check if constant
|
||||
if rem:match("^:") then
|
||||
rem = rem:match("^:(.*)$")
|
||||
r.constant = true
|
||||
end
|
||||
-- get identifier
|
||||
local identifier
|
||||
identifier, rem = rem:match("^("..identifier_pattern..")(.-)$")
|
||||
if not identifier then return nil, ("no valid identifier at start of definition line %q; at %s"):format(l, line.source) end
|
||||
-- format identifier
|
||||
local fqm = ("%s%s"):format(namespace, format_identifier(identifier))
|
||||
-- get alias
|
||||
local ok_alias
|
||||
ok_alias, rem = maybe_alias(rem, fqm, namespace, line, state)
|
||||
if not ok_alias then return ok_alias, rem end
|
||||
-- type constraint
|
||||
if rem:match("^::(.-)=") then
|
||||
r.constraint, rem = rem:match("^::%s*(.-)%s*(=.*)$")
|
||||
end
|
||||
-- get expression
|
||||
local exp = rem:match("^=(.*)$")
|
||||
if not exp then return nil, ("expected \"= expression\" after %q in definition line; at %s"):format(rem, line.source) end
|
||||
-- define identifier
|
||||
if state.functions[fqm] then return nil, ("trying to define variable %q, but a function with the same name exists; at %s"):format(fqm, line.source) end
|
||||
if state.variables[fqm] then
|
||||
if state.variables[fqm].type == "pending definition" then
|
||||
return nil, ("trying to define variable %q but it is already defined at %s; at %s"):format(fqm, state.variables[fqm].value.source, line.source)
|
||||
else
|
||||
return nil, ("trying to define variable %q but it is already defined; at %s"):format(fqm, line.source)
|
||||
r.type = "definition"
|
||||
r.remove_from_block_ast = true
|
||||
local rem = lr
|
||||
-- check if constant
|
||||
if rem:match("^:") then
|
||||
rem = rem:match("^:(.*)$")
|
||||
r.constant = true
|
||||
end
|
||||
-- get identifier
|
||||
local identifier
|
||||
identifier, rem = rem:match("^("..identifier_pattern..")(.-)$")
|
||||
if not identifier then return nil, ("no valid identifier at start of definition line %q; at %s"):format(l, line.source) end
|
||||
-- format identifier
|
||||
local fqm = ("%s%s"):format(namespace, format_identifier(identifier))
|
||||
-- get alias
|
||||
local ok_alias
|
||||
ok_alias, rem = maybe_alias(rem, fqm, namespace, line, state)
|
||||
if not ok_alias then return ok_alias, rem end
|
||||
-- type constraint
|
||||
if rem:match("^::(.-)=") then
|
||||
r.constraint, rem = rem:match("^::%s*(.-)%s*(=.*)$")
|
||||
end
|
||||
-- get expression
|
||||
local exp = rem:match("^=(.*)$")
|
||||
if not exp then return nil, ("expected \"= expression\" after %q in definition line; at %s"):format(rem, line.source) end
|
||||
-- define identifier
|
||||
if state.functions[fqm] then return nil, ("trying to define variable %q, but a function with the same name exists; at %s"):format(fqm, line.source) end
|
||||
if state.variables[fqm] then
|
||||
if state.variables[fqm].type == "pending definition" then
|
||||
return nil, ("trying to define variable %q but it is already defined at %s; at %s"):format(fqm, state.variables[fqm].value.source, line.source)
|
||||
else
|
||||
return nil, ("trying to define variable %q but it is already defined; at %s"):format(fqm, line.source)
|
||||
end
|
||||
end
|
||||
r.name = fqm
|
||||
r.expression = exp
|
||||
state.variables[fqm] = { type = "pending definition", value = { expression = nil, source = r.source } }
|
||||
if r.constant then state.variable_constants[fqm] = true end
|
||||
end
|
||||
-- add expression line after to perform the immediate execution
|
||||
if run_immediately then
|
||||
line.line_after = { content = "~ "..r.name, source = line.source }
|
||||
end
|
||||
r.fqm = fqm
|
||||
r.expression = exp
|
||||
state.variables[fqm] = { type = "pending definition", value = { expression = nil, source = r.source } }
|
||||
if r.constant then state.variable_constants[fqm] = true end
|
||||
-- tag
|
||||
elseif l:match("^%#") then
|
||||
r.type = "tag"
|
||||
|
|
@ -376,7 +390,7 @@ end
|
|||
-- * nil, err: in case of error
|
||||
local function parse_block(indented, state, namespace, parent_function)
|
||||
local block = { type = "block" }
|
||||
for _, l in ipairs(indented) do
|
||||
for i, l in ipairs(indented) do
|
||||
-- parsable line
|
||||
local ast, err = parse_line(l, state, namespace, parent_function)
|
||||
if err then return nil, err end
|
||||
|
|
@ -403,6 +417,11 @@ local function parse_block(indented, state, namespace, parent_function)
|
|||
ast.child = r
|
||||
end
|
||||
end
|
||||
|
||||
-- insert line after
|
||||
if l.line_after then
|
||||
table.insert(indented, i+1, l.line_after)
|
||||
end
|
||||
end
|
||||
return block
|
||||
end
|
||||
|
|
@ -416,17 +435,7 @@ local function transform_indented(indented)
|
|||
if l.content:match("^%(") then
|
||||
table.remove(indented, i)
|
||||
else
|
||||
-- function decorator
|
||||
if l.content:match("^.-[^\\]%$"..identifier_pattern.."$") then
|
||||
local name
|
||||
l.content, name = l.content:match("^(.-[^\\])%$("..identifier_pattern..")$")
|
||||
indented[i] = { content = "~"..name, source = l.source }
|
||||
table.insert(indented, i+1, { content = "$"..name, source = l.source, children = { l } })
|
||||
i = i + 1 -- $ line should not contain any decorator anymore
|
||||
else
|
||||
i = i + 1 -- only increment when no decorator, as there may be several decorators per line
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
-- indented block
|
||||
if l.children then
|
||||
transform_indented(l.children)
|
||||
|
|
@ -497,7 +506,7 @@ parse_indented = function(s, fnname, source)
|
|||
return nil, ("invalid function name %q"):format(fnname)
|
||||
end
|
||||
indented = {
|
||||
{ content = "$ "..fnname, source = ("%s:%s"):format(source, 0), children = indented },
|
||||
{ content = ":$ "..fnname, source = ("%s:%s"):format(source, 0), children = indented },
|
||||
}
|
||||
end
|
||||
-- transform ast
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue