mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-27 16:49:31 +00:00
Add object type, dot operator can return function references
This commit is contained in:
parent
69b9e17020
commit
47f95fc04a
18 changed files with 599 additions and 49 deletions
|
|
@ -189,9 +189,8 @@ common = {
|
|||
end,
|
||||
--- returns true if a variable should be persisted on save
|
||||
-- will exclude: undefined variables, variables in scoped functions, internal anselme variables
|
||||
should_keep_variable = function(state, name)
|
||||
local v = state.variables[name]
|
||||
return v.type ~= "undefined argument" and v.type ~= "pending definition" and name:match("^"..identifier_pattern.."$") and not name:match("^anselme%.")
|
||||
should_keep_variable = function(state, name, value)
|
||||
return value.type ~= "undefined argument" and value.type ~= "pending definition" and name:match("^"..identifier_pattern.."$") and not name:match("^anselme%.")
|
||||
end,
|
||||
--- check truthyness of an anselme value
|
||||
truthy = function(val)
|
||||
|
|
|
|||
|
|
@ -208,16 +208,10 @@ local function eval(state, exp)
|
|||
local tried_function_error_messages = {}
|
||||
local selected_variant = { depths = { assignment = nil }, variant = nil, args_to_set = nil }
|
||||
for _, fn in ipairs(variants) do
|
||||
-- checkpoint: no args, nothing to select on
|
||||
if fn.type == "checkpoint" then
|
||||
if not selected_variant.variant then
|
||||
selected_variant.depths = {}
|
||||
selected_variant.variant = fn
|
||||
else
|
||||
return nil, ("checkpoint call %q is ambigous; may be at least either:\n\t%s\n\t%s"):format(exp.called_name, fn.pretty_signature, selected_variant.variant.pretty_signature)
|
||||
end
|
||||
-- function
|
||||
elseif fn.type == "function" then
|
||||
if fn.type ~= "function" then
|
||||
return nil, ("unknown function type %q"):format(fn.type)
|
||||
-- functions
|
||||
else
|
||||
if not fn.assignment or exp.assignment then
|
||||
local ok = true
|
||||
-- get and set args
|
||||
|
|
@ -342,18 +336,20 @@ local function eval(state, exp)
|
|||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
return nil, ("unknown function type %q"):format(fn.type)
|
||||
end
|
||||
end
|
||||
-- function successfully selected: run
|
||||
if selected_variant.variant then
|
||||
local fn = selected_variant.variant
|
||||
if fn.type == "checkpoint" then
|
||||
if fn.type ~= "function" then
|
||||
return nil, ("unknown function type %q"):format(fn.type)
|
||||
-- checkpoint: no args and resume execution
|
||||
elseif fn.subtype == "checkpoint" then
|
||||
local r, e = run(state, fn.child, not paren_call)
|
||||
if not r then return r, e end
|
||||
return r
|
||||
elseif fn.type == "function" then
|
||||
-- other functions
|
||||
else
|
||||
local ret
|
||||
-- push scope
|
||||
-- NOTE: if error happens between here and scope:pop, will leave the stack a mess
|
||||
|
|
@ -452,6 +448,28 @@ local function eval(state, exp)
|
|||
type = "number",
|
||||
value = seen.value + 1
|
||||
})
|
||||
-- for classes: build resulting object
|
||||
if fn.subtype == "class" then
|
||||
local object = {
|
||||
type = "type",
|
||||
value = {
|
||||
{
|
||||
type = "object",
|
||||
value = {
|
||||
class = fn.name,
|
||||
attributes = {}
|
||||
}
|
||||
},
|
||||
{
|
||||
type = "function reference",
|
||||
value = { fn.name }
|
||||
}
|
||||
}
|
||||
}
|
||||
if ret and ret.type == "nil" then
|
||||
ret = object
|
||||
end
|
||||
end
|
||||
-- pop scope
|
||||
if fn.scoped then
|
||||
scope:pop(state, fn)
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ run_line = function(state, line)
|
|||
elseif line.type == "flush_events" then
|
||||
local v, e = events:flush(state)
|
||||
if not v then return v, ("%s; in event flush at %s"):format(e, line.source) end
|
||||
elseif line.type == "checkpoint" then
|
||||
elseif line.type == "function" and line.subtype == "checkpoint" then
|
||||
local reached, reachede = get_variable(state, line.namespace.."🏁")
|
||||
if not reached then return nil, reachede end
|
||||
set_variable(state, line.namespace.."🏁", {
|
||||
|
|
@ -150,9 +150,9 @@ run_block = function(state, block, resume_from_there, i, j)
|
|||
i = i + 1
|
||||
end
|
||||
-- if we are exiting a checkpoint block, mark it as ran and update checkpoint
|
||||
-- (when resuming from a checkpoint, execution is resumed from inside the checkpoint, the line.type=="checkpoint" check in run_line is never called)
|
||||
-- (when resuming from a checkpoint, execution is resumed from inside the checkpoint, the line.subtype=="checkpoint" check in run_line is never called)
|
||||
-- (and we want this to be done after executing the checkpoint block anyway)
|
||||
if block.parent_line and block.parent_line.type == "checkpoint" then
|
||||
if block.parent_line and block.parent_line.type == "function" and block.parent_line.subtype == "checkpoint" then
|
||||
local parent_line = block.parent_line
|
||||
local reached, reachede = get_variable(state, parent_line.namespace.."🏁")
|
||||
if not reached then return nil, reachede end
|
||||
|
|
@ -183,7 +183,7 @@ run_block = function(state, block, resume_from_there, i, j)
|
|||
-- if parent is a choice, will ignore choices that belong to the same block (like the whole block was executed naturally from a higher parent)
|
||||
-- if parent if a condition, will mark it as a success (skipping following else-conditions) (for the same reasons as for choices)
|
||||
-- if parent pushed a tag, will pop it (tags from parents are added to the stack in run())
|
||||
if resume_from_there and block.parent_line and block.parent_line.type ~= "function" then
|
||||
if resume_from_there and block.parent_line and not block.parent_line.resume_boundary then
|
||||
local parent_line = block.parent_line
|
||||
if parent_line.type == "choice" then
|
||||
state.interpreter.skip_choices_until_flush = true
|
||||
|
|
@ -209,7 +209,7 @@ local function run(state, block, resume_from_there, i, j)
|
|||
local tags_to_add = {}
|
||||
-- go up in hierarchy in ascending order until function boundary
|
||||
local parent_line = block.parent_line
|
||||
while parent_line and parent_line.type ~= "function" do
|
||||
while parent_line and not parent_line.resume_boundary do
|
||||
if parent_line.type == "tag" then
|
||||
local v, e = eval(state, parent_line.expression)
|
||||
if not v then return v, ("%s; at %s"):format(e, parent_line.source) end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue