mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-27 16:49:31 +00:00
Exported variables predefinition: replace prepare system with generic resume system
This commit is contained in:
parent
e2ec105a4b
commit
3edf65dc2a
19 changed files with 155 additions and 136 deletions
|
|
@ -1,16 +1,13 @@
|
||||||
local ast = require("anselme.ast")
|
local ast = require("anselme.ast")
|
||||||
|
|
||||||
local resume_manager
|
|
||||||
|
|
||||||
local Anchor
|
local Anchor
|
||||||
Anchor = ast.abstract.Node {
|
Anchor = ast.abstract.ResumeTarget {
|
||||||
type = "anchor",
|
type = "anchor",
|
||||||
|
|
||||||
name = nil,
|
name = nil,
|
||||||
|
|
||||||
init = function(self, name)
|
init = function(self, name)
|
||||||
self.name = name
|
self.name = name
|
||||||
self._list_anchors_cache = { [name] = true }
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
_hash = function(self)
|
_hash = function(self)
|
||||||
|
|
@ -20,16 +17,8 @@ Anchor = ast.abstract.Node {
|
||||||
_format = function(self, ...)
|
_format = function(self, ...)
|
||||||
return "#"..self.name
|
return "#"..self.name
|
||||||
end,
|
end,
|
||||||
|
|
||||||
_eval = function(self, state)
|
|
||||||
if self:contains_resume_target(state) then
|
|
||||||
resume_manager:set_reached(state)
|
|
||||||
end
|
|
||||||
return Anchor:new(self.name)
|
|
||||||
end
|
|
||||||
}
|
}
|
||||||
|
|
||||||
package.loaded[...] = Anchor
|
package.loaded[...] = Anchor
|
||||||
resume_manager = require("anselme.state.resume_manager")
|
|
||||||
|
|
||||||
return Anchor
|
return Anchor
|
||||||
|
|
|
||||||
|
|
@ -36,17 +36,18 @@ local Block = ast.abstract.Node {
|
||||||
_eval = function(self, state)
|
_eval = function(self, state)
|
||||||
local r
|
local r
|
||||||
state.scope:push()
|
state.scope:push()
|
||||||
if self:contains_resume_target(state) then
|
if self:contains_current_resume_target(state) then
|
||||||
local anchor = resume_manager:get(state)
|
local target = resume_manager:get(state)
|
||||||
|
local no_continue = resume_manager:no_continue(state)
|
||||||
local resumed = false
|
local resumed = false
|
||||||
for _, e in ipairs(self.expressions) do
|
for _, e in ipairs(self.expressions) do
|
||||||
if e:contains_anchor(anchor) then resumed = true end
|
if e:contains_resume_target(target) then resumed = true end
|
||||||
if resumed then
|
if resumed then
|
||||||
r = e:eval(state)
|
r = e:eval(state)
|
||||||
if AutoCall:issub(r) then
|
if AutoCall:issub(r) then
|
||||||
r = r:call(state, ArgumentTuple:new())
|
r = r:call(state, ArgumentTuple:new())
|
||||||
end
|
end
|
||||||
if Return:is(r) then
|
if Return:is(r) or no_continue then
|
||||||
break -- pass on to parent block until we reach a function boundary
|
break -- pass on to parent block until we reach a function boundary
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -65,14 +66,6 @@ local Block = ast.abstract.Node {
|
||||||
state.scope:pop()
|
state.scope:pop()
|
||||||
return r or Nil:new()
|
return r or Nil:new()
|
||||||
end,
|
end,
|
||||||
|
|
||||||
_prepare = function(self, state)
|
|
||||||
state.scope:push()
|
|
||||||
for _, e in ipairs(self.expressions) do
|
|
||||||
e:prepare(state)
|
|
||||||
end
|
|
||||||
state.scope:pop()
|
|
||||||
end
|
|
||||||
}
|
}
|
||||||
|
|
||||||
package.loaded[...] = Block
|
package.loaded[...] = Block
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ local ast = require("anselme.ast")
|
||||||
local Overloadable, Runtime = ast.abstract.Overloadable, ast.abstract.Runtime
|
local Overloadable, Runtime = ast.abstract.Overloadable, ast.abstract.Runtime
|
||||||
local Definition
|
local Definition
|
||||||
|
|
||||||
|
local resume_manager
|
||||||
|
|
||||||
local Closure
|
local Closure
|
||||||
Closure = Runtime(Overloadable) {
|
Closure = Runtime(Overloadable) {
|
||||||
type = "closure",
|
type = "closure",
|
||||||
|
|
@ -21,8 +23,14 @@ Closure = Runtime(Overloadable) {
|
||||||
self.exported_scope = state.scope:capture()
|
self.exported_scope = state.scope:capture()
|
||||||
|
|
||||||
-- pre-define exports
|
-- pre-define exports
|
||||||
for sym, exp in pairs(self.func.exports) do
|
for _, target in pairs(self:list_resume_targets()) do
|
||||||
Definition:new(sym, exp):eval(state)
|
if Definition:is(target) and target.symbol.exported then
|
||||||
|
resume_manager:push_no_continue(state, target)
|
||||||
|
state.scope:push() -- create temp func scope, in case non-export definitions are done in the resume
|
||||||
|
self.func.expression:eval(state)
|
||||||
|
state.scope:pop()
|
||||||
|
resume_manager:pop(state)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
state.scope:pop()
|
state.scope:pop()
|
||||||
|
|
@ -54,5 +62,6 @@ Closure = Runtime(Overloadable) {
|
||||||
|
|
||||||
package.loaded[...] = Closure
|
package.loaded[...] = Closure
|
||||||
Definition = ast.Definition
|
Definition = ast.Definition
|
||||||
|
resume_manager = require("anselme.state.resume_manager")
|
||||||
|
|
||||||
return Closure
|
return Closure
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ local Nil, Overloadable
|
||||||
|
|
||||||
local operator_priority = require("anselme.common").operator_priority
|
local operator_priority = require("anselme.common").operator_priority
|
||||||
|
|
||||||
local Definition = ast.abstract.Node {
|
local Definition = ast.abstract.ResumeTarget {
|
||||||
type = "definition",
|
type = "definition",
|
||||||
|
|
||||||
symbol = nil,
|
symbol = nil,
|
||||||
|
|
@ -46,17 +46,6 @@ local Definition = ast.abstract.Node {
|
||||||
|
|
||||||
return Nil:new()
|
return Nil:new()
|
||||||
end,
|
end,
|
||||||
|
|
||||||
_prepare = function(self, state)
|
|
||||||
local symbol, val = self.symbol, self.expression
|
|
||||||
symbol:prepare(state)
|
|
||||||
val:prepare(state)
|
|
||||||
|
|
||||||
-- predefine exported variables
|
|
||||||
if symbol.exported then
|
|
||||||
self:eval(state)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
}
|
||||||
|
|
||||||
package.loaded[...] = Definition
|
package.loaded[...] = Definition
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,6 @@ Function = Overloadable {
|
||||||
parameters = nil, -- ParameterTuple
|
parameters = nil, -- ParameterTuple
|
||||||
expression = nil,
|
expression = nil,
|
||||||
|
|
||||||
exports = nil, -- { [sym] = exp, ... }, exctracted from expression during :prepare
|
|
||||||
|
|
||||||
init = function(self, parameters, expression, exports)
|
init = function(self, parameters, expression, exports)
|
||||||
self.parameters = parameters
|
self.parameters = parameters
|
||||||
self.expression = ReturnBoundary:new(expression)
|
self.expression = ReturnBoundary:new(expression)
|
||||||
|
|
@ -55,7 +53,8 @@ Function = Overloadable {
|
||||||
|
|
||||||
state.scope:pop()
|
state.scope:pop()
|
||||||
|
|
||||||
-- reminder: don't do any additionnal processing here as that won't be executed when resuming self.expression
|
-- reminder: don't do any additionnal processing here as that won't be executed when resuming self.expression directly
|
||||||
|
-- which is done in a few places, notably to predefine exports in Closure
|
||||||
-- instead wrap it in some additional node, like our friend ReturnBoundary
|
-- instead wrap it in some additional node, like our friend ReturnBoundary
|
||||||
|
|
||||||
return exp
|
return exp
|
||||||
|
|
@ -64,18 +63,6 @@ Function = Overloadable {
|
||||||
_eval = function(self, state)
|
_eval = function(self, state)
|
||||||
return Closure:new(Function:new(self.parameters:eval(state), self.expression, self.exports), state)
|
return Closure:new(Function:new(self.parameters:eval(state), self.expression, self.exports), state)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
_prepare = function(self, state)
|
|
||||||
state.scope:push_export() -- recreate scope context that will be created by closure
|
|
||||||
|
|
||||||
state.scope:push()
|
|
||||||
self.parameters:prepare(state)
|
|
||||||
self.expression:prepare(state)
|
|
||||||
state.scope:pop()
|
|
||||||
|
|
||||||
self.exports = state.scope:capture():list_exported(state)
|
|
||||||
state.scope:pop()
|
|
||||||
end,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
package.loaded[...] = Function
|
package.loaded[...] = Function
|
||||||
|
|
|
||||||
|
|
@ -29,12 +29,6 @@ Identifier = ast.abstract.Node {
|
||||||
to_symbol = function(self, modifiers)
|
to_symbol = function(self, modifiers)
|
||||||
return Symbol:new(self.name, modifiers)
|
return Symbol:new(self.name, modifiers)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
_prepare = function(self, state)
|
|
||||||
if state.scope:defined(self) then
|
|
||||||
state.scope:get(self):prepare(state)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
}
|
||||||
|
|
||||||
package.loaded[...] = Identifier
|
package.loaded[...] = Identifier
|
||||||
|
|
|
||||||
|
|
@ -56,13 +56,6 @@ PartialScope = ast.abstract.Node {
|
||||||
return exp
|
return exp
|
||||||
end,
|
end,
|
||||||
|
|
||||||
_prepare = function(self, state)
|
|
||||||
state.scope:push_partial(unpack(self._identifiers))
|
|
||||||
for sym, val in pairs(self.definitions) do state.scope:define(sym, val) end
|
|
||||||
self.expression:prepare(state)
|
|
||||||
state.scope:pop()
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- class method: if the identifier is currently defined, wrap node in an PartialScope so the identifier is still defined in this node
|
-- class method: if the identifier is currently defined, wrap node in an PartialScope so the identifier is still defined in this node
|
||||||
-- used to e.g. preserve the defined _ block without the need to build a full closure
|
-- used to e.g. preserve the defined _ block without the need to build a full closure
|
||||||
-- used e.g. for -> translation, as we want to preserve _ while still executing the translation in the Translatable scope and not restore a different scope from a closure
|
-- used e.g. for -> translation, as we want to preserve _ while still executing the translation in the Translatable scope and not restore a different scope from a closure
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ local utf8 = utf8 or require("lua-utf8")
|
||||||
|
|
||||||
local uuid = require("anselme.common").uuid
|
local uuid = require("anselme.common").uuid
|
||||||
|
|
||||||
local State, Runtime, Call, Identifier, ArgumentTuple
|
local Call, Identifier, ArgumentTuple
|
||||||
local resume_manager
|
local resume_manager
|
||||||
|
|
||||||
local custom_call_identifier
|
local custom_call_identifier
|
||||||
|
|
@ -37,9 +37,6 @@ traverse = {
|
||||||
set_source = function(self, source)
|
set_source = function(self, source)
|
||||||
self:set_source(source)
|
self:set_source(source)
|
||||||
end,
|
end,
|
||||||
prepare = function(self, state)
|
|
||||||
self:prepare(state)
|
|
||||||
end,
|
|
||||||
merge = function(self, state, cache)
|
merge = function(self, state, cache)
|
||||||
self:merge(state, cache)
|
self:merge(state, cache)
|
||||||
end,
|
end,
|
||||||
|
|
@ -48,6 +45,11 @@ traverse = {
|
||||||
end,
|
end,
|
||||||
list_translatable = function(self, t)
|
list_translatable = function(self, t)
|
||||||
self:list_translatable(t)
|
self:list_translatable(t)
|
||||||
|
end,
|
||||||
|
list_resume_targets = function(self, add_to_node)
|
||||||
|
for hash, target in pairs(self:list_resume_targets()) do
|
||||||
|
add_to_node._list_resume_targets_cache[hash] = target
|
||||||
|
end
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,52 +102,29 @@ Node = class {
|
||||||
return self
|
return self
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- prepare the AST after parsing and before evaluation
|
-- returns a reversed list { [target hash] = true, ... } of the resume targets contained in this node and its children
|
||||||
-- this behave like a cached :traverse through the AST, except this keeps track of the scope stack and preserve evaluation order
|
-- this is cached, redefine _list_resume_targets if needed, not this function
|
||||||
-- i.e. when :prepare is called on a node, it should be in a similar scope stack context and order as will be when it will be evaluated
|
list_resume_targets = function(self)
|
||||||
-- used to predefine exported variables and other compile-time variable handling
|
if not self._list_resume_targets_cache then
|
||||||
-- note: the state here is a temporary state only used during the prepare step
|
self._list_resume_targets_cache = {}
|
||||||
-- the actual preparation is done in _prepare
|
self:_list_resume_targets()
|
||||||
-- (this can mutate the node as needed and is automatically called after each parse)
|
|
||||||
prepare = function(self, state)
|
|
||||||
assert(not Runtime:issub(self), ("can't prepare a %s node that should only exist at runtime"):format(self.type))
|
|
||||||
state = state or State:new()
|
|
||||||
if self._prepared then return end
|
|
||||||
local s, r = pcall(self._prepare, self, state)
|
|
||||||
if s then
|
|
||||||
self._prepared = true
|
|
||||||
else
|
|
||||||
error(format_error(state, self, r), 0)
|
|
||||||
end
|
end
|
||||||
|
return self._list_resume_targets_cache
|
||||||
end,
|
end,
|
||||||
_prepared = false, -- indicate that the node was prepared and :prepare should nop
|
_list_resume_targets_cache = nil, -- list resume target cache { [target hash] = target, ... }
|
||||||
-- prepare this node. can mutate the node (considered to be part of construction).
|
-- add resume targets to _list_resume_targets_cache
|
||||||
_prepare = function(self, state)
|
_list_resume_targets = function(self)
|
||||||
self:traverse(traverse.prepare, state)
|
self:traverse(traverse.list_resume_targets, self)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- returns a reversed list { [anchor] = true, ... } of the anchors contained in this node and its children
|
-- returns true if the node or its children contains the resume target
|
||||||
_list_anchors = function(self)
|
contains_resume_target = function(self, target)
|
||||||
if not self._list_anchors_cache then
|
return not not self:list_resume_targets()[target:hash()]
|
||||||
self._list_anchors_cache = {}
|
|
||||||
self:traverse(function(v)
|
|
||||||
for name in pairs(v:_list_anchors()) do
|
|
||||||
self._list_anchors_cache[name] = true
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
return self._list_anchors_cache
|
|
||||||
end,
|
|
||||||
_list_anchors_cache = nil,
|
|
||||||
|
|
||||||
-- returns true if the node or its children contains the anchor
|
|
||||||
contains_anchor = function(self, anchor)
|
|
||||||
return not not self:_list_anchors()[anchor.name]
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- returns true if we are currently trying to resume to an anchor target contained in the current node
|
-- returns true if we are currently trying to resume to a resume target contained in the current node
|
||||||
contains_resume_target = function(self, state)
|
contains_current_resume_target = function(self, state)
|
||||||
return resume_manager:resuming(state) and self:contains_anchor(resume_manager:get(state))
|
return resume_manager:resuming(state) and self:contains_resume_target(resume_manager:get(state))
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- generate a list of translatable nodes that appear in this node
|
-- generate a list of translatable nodes that appear in this node
|
||||||
|
|
@ -332,10 +311,9 @@ Node = class {
|
||||||
-- Thus, any require here that may require other Nodes shall be done here. This method is called in anselme.lua after everything else is required.
|
-- Thus, any require here that may require other Nodes shall be done here. This method is called in anselme.lua after everything else is required.
|
||||||
_i_hate_cycles = function(self)
|
_i_hate_cycles = function(self)
|
||||||
local ast = require("anselme.ast")
|
local ast = require("anselme.ast")
|
||||||
Runtime, Call, Identifier, ArgumentTuple = ast.abstract.Runtime, ast.Call, ast.Identifier, ast.ArgumentTuple
|
Call, Identifier, ArgumentTuple = ast.Call, ast.Identifier, ast.ArgumentTuple
|
||||||
custom_call_identifier = Identifier:new("_!")
|
custom_call_identifier = Identifier:new("_!")
|
||||||
|
|
||||||
State = require("anselme.state.State")
|
|
||||||
resume_manager = require("anselme.state.resume_manager")
|
resume_manager = require("anselme.state.resume_manager")
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
|
|
||||||
27
anselme/ast/abstract/ResumeTarget.lua
Normal file
27
anselme/ast/abstract/ResumeTarget.lua
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
-- nodes that can be resumed to
|
||||||
|
|
||||||
|
local ast = require("anselme.ast")
|
||||||
|
local Node = ast.abstract.Node
|
||||||
|
|
||||||
|
local resume_manager
|
||||||
|
|
||||||
|
local ResumeTarget = Node {
|
||||||
|
type = "resume target",
|
||||||
|
init = false,
|
||||||
|
|
||||||
|
_list_resume_targets = function(self)
|
||||||
|
self._list_resume_targets_cache[self:hash()] = self
|
||||||
|
end,
|
||||||
|
|
||||||
|
eval = function(self, state)
|
||||||
|
if self:contains_current_resume_target(state) then
|
||||||
|
resume_manager:set_reached(state)
|
||||||
|
end
|
||||||
|
return Node.eval(self, state)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
package.loaded[...] = ResumeTarget
|
||||||
|
resume_manager = require("anselme.state.resume_manager")
|
||||||
|
|
||||||
|
return ResumeTarget
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
-- indicate a Runtime node: it should not exist in the AST generated by the parser but only as a result of an evaluation or call
|
-- indicate a Runtime node: it should not exist in the AST generated by the parser but only as a result of an evaluation or call
|
||||||
-- is assumed to be already evaluated and prepared (will actually error on prepare)
|
-- is assumed to be already evaluated; we reserve the right to error if a Runtime node occurs in something that was never evaluated
|
||||||
|
|
||||||
local ast = require("anselme.ast")
|
local ast = require("anselme.ast")
|
||||||
|
|
||||||
|
|
@ -8,5 +8,4 @@ return ast.abstract.Node {
|
||||||
init = false,
|
init = false,
|
||||||
|
|
||||||
_evaluated = true,
|
_evaluated = true,
|
||||||
_prepared = true
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@ end
|
||||||
|
|
||||||
return primary {
|
return primary {
|
||||||
match = function(self, str)
|
match = function(self, str)
|
||||||
return str:match("^%::?[&@]?%$")
|
return str:match("^%::?&?@?%$")
|
||||||
end,
|
end,
|
||||||
|
|
||||||
parse = function(self, source, str, limit_pattern)
|
parse = function(self, source, str, limit_pattern)
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ local Nil = ast.Nil
|
||||||
|
|
||||||
return primary {
|
return primary {
|
||||||
match = function(self, str)
|
match = function(self, str)
|
||||||
if str:match("^%::?[&@]?") then
|
if str:match("^%::?&?@?") then
|
||||||
return identifier:match(str:match("^%::?[&@]?(.-)$"))
|
return identifier:match(str:match("^%::?&?@?(.-)$"))
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end,
|
end,
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,5 @@ return function(code, source)
|
||||||
local tree = code_to_tree(code, source)
|
local tree = code_to_tree(code, source)
|
||||||
local block = tree_to_ast(tree)
|
local block = tree_to_ast(tree)
|
||||||
|
|
||||||
block:prepare()
|
|
||||||
|
|
||||||
return block
|
return block
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,64 @@
|
||||||
local class = require("anselme.lib.class")
|
local class = require("anselme.lib.class")
|
||||||
|
|
||||||
local ast = require("anselme.ast")
|
local ast = require("anselme.ast")
|
||||||
local Nil, Identifier, Anchor
|
local Nil, Identifier, ResumeTarget, Boolean
|
||||||
|
|
||||||
-- stack of resumable contexts
|
-- stack of resumable contexts
|
||||||
local resume_anchor_identifier, resume_anchor_symbol
|
local resume_target_identifier, resume_target_symbol, resume_no_continue_identifier, resume_no_continue_symbol
|
||||||
|
|
||||||
local resume_manager = class {
|
local resume_manager = class {
|
||||||
init = false,
|
init = false,
|
||||||
|
|
||||||
-- push a new resume context: all run code between this and the next push will try to resume to anchor
|
-- push a new resume context: all run code between this and the next push will try to resume to target
|
||||||
push = function(self, state, anchor)
|
push = function(self, state, target)
|
||||||
assert(Anchor:is(anchor), "can only resume to an anchor target") -- well technically it wouldn't be hard to allow to resume to any node, but I feel like there's already enough stuff in Anselme that was done just because it could be done
|
assert(ResumeTarget:issub(target), "can only resume to a resume target")
|
||||||
state.scope:push_partial(resume_anchor_identifier)
|
state.scope:push_partial(resume_target_identifier, resume_no_continue_identifier)
|
||||||
state.scope:define(resume_anchor_symbol, anchor)
|
state.scope:define(resume_target_symbol, target)
|
||||||
|
state.scope:define(resume_no_continue_symbol, Boolean:new(false))
|
||||||
|
end,
|
||||||
|
-- same as :push, but the resume will stop immediately after reaching the target or a node containing the target
|
||||||
|
-- (we will stop even if the node is not directly reached - this is used to run a specific line containing a node,
|
||||||
|
-- notably for Definition of exported variables)
|
||||||
|
push_no_continue = function(self, state, target)
|
||||||
|
assert(ResumeTarget:issub(target), "can only resume to a resume target")
|
||||||
|
state.scope:push_partial(resume_target_identifier, resume_no_continue_identifier)
|
||||||
|
state.scope:define(resume_target_symbol, target)
|
||||||
|
state.scope:define(resume_no_continue_symbol, Boolean:new(true))
|
||||||
end,
|
end,
|
||||||
-- pop the current resume context
|
-- pop the current resume context
|
||||||
pop = function(self, state)
|
pop = function(self, state)
|
||||||
state.scope:pop()
|
state.scope:pop()
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- returns true if we are currently trying to resume to an anchor
|
-- returns true if we are currently trying to resume to a target
|
||||||
resuming = function(self, state)
|
resuming = function(self, state)
|
||||||
return state.scope:defined(resume_anchor_identifier) and not Nil:is(state.scope:get(resume_anchor_identifier))
|
return state.scope:defined(resume_target_identifier) and not Nil:is(state.scope:get(resume_target_identifier))
|
||||||
end,
|
end,
|
||||||
-- returns the anchor we are trying to resume to
|
-- returns the target we are trying to resume to
|
||||||
|
-- (assumes that we are currently :resuming)
|
||||||
get = function(self, state)
|
get = function(self, state)
|
||||||
return state.scope:get(resume_anchor_identifier)
|
return state.scope:get(resume_target_identifier)
|
||||||
end,
|
end,
|
||||||
-- mark the anchor as reached and stop the resume
|
-- mark the target as reached and stop the resume
|
||||||
|
-- (assumes that we are currently :resuming)
|
||||||
set_reached = function(self, state)
|
set_reached = function(self, state)
|
||||||
state.scope:set(resume_anchor_identifier, Nil:new())
|
state.scope:set(resume_target_identifier, Nil:new())
|
||||||
end,
|
end,
|
||||||
|
-- indicate if the evaluation should stop after reaching a node containing the target
|
||||||
|
-- (assumes that we are currently :resuming)
|
||||||
|
no_continue = function(self, state)
|
||||||
|
return state.scope:get(resume_no_continue_identifier):to_lua(state)
|
||||||
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
package.loaded[...] = resume_manager
|
package.loaded[...] = resume_manager
|
||||||
|
|
||||||
Nil, Identifier, Anchor = ast.Nil, ast.Identifier, ast.Anchor
|
Nil, Identifier, ResumeTarget, Boolean = ast.Nil, ast.Identifier, ast.abstract.ResumeTarget, ast.Boolean
|
||||||
|
|
||||||
resume_anchor_identifier = Identifier:new("_resume_anchor")
|
resume_target_identifier = Identifier:new("_resume_target")
|
||||||
resume_anchor_symbol = resume_anchor_identifier:to_symbol()
|
resume_target_symbol = resume_target_identifier:to_symbol()
|
||||||
|
|
||||||
|
resume_no_continue_identifier = Identifier:new("_resume_no_continue")
|
||||||
|
resume_no_continue_symbol = resume_no_continue_identifier:to_symbol()
|
||||||
|
|
||||||
return resume_manager
|
return resume_manager
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ return {
|
||||||
{
|
{
|
||||||
"_|>_", "(txt::text, fn)",
|
"_|>_", "(txt::text, fn)",
|
||||||
function(state, text, func)
|
function(state, text, func)
|
||||||
if func:contains_resume_target(state) then
|
if func:contains_current_resume_target(state) then
|
||||||
func:call(state, ArgumentTuple:new())
|
func:call(state, ArgumentTuple:new())
|
||||||
event_manager:write_and_discard_on_flush(state, Choice:new(text, func))
|
event_manager:write_and_discard_on_flush(state, Choice:new(text, func))
|
||||||
else
|
else
|
||||||
|
|
|
||||||
11
test/results/exported variable nested.ans
Normal file
11
test/results/exported variable nested.ans
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
--# run #--
|
||||||
|
--- text ---
|
||||||
|
| {}"" {}"42" {}"" |
|
||||||
|
--- error ---
|
||||||
|
[0m[31m[0m[31m[0m[31m[0m[31m./anselme/stdlib/closure.lua:15: no exported variable "y" defined in closure[0m
|
||||||
|
↳ from [4mtest/tests/exported variable nested.ans:10:4[0m in call: [2mf . "y"[0m[0m
|
||||||
|
↳ from [4mtest/tests/exported variable nested.ans:10:1[0m in text interpolation: [2m| {f . "y"} |[0m[0m
|
||||||
|
↳ from [4mtest/tests/exported variable nested.ans:10:1[0m in translatable: [2m| {f . "y"} |[0m[0m
|
||||||
|
↳ from [4m?[0m in block: [2m:f = ($() _)…[0m
|
||||||
|
--# saved #--
|
||||||
|
{}
|
||||||
11
test/results/symbol alias exported.ans
Normal file
11
test/results/symbol alias exported.ans
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
--# run #--
|
||||||
|
--- text ---
|
||||||
|
| {}"c=" {}"2" {}" (2)" |
|
||||||
|
| {}"l=" {}"*[1, 2, 3]" {}" (*[1,2,3])" |
|
||||||
|
--- text ---
|
||||||
|
| {}"c=" {}"5" {}" (5)" |
|
||||||
|
| {}"l=" {}"*[1, 5, 3]" {}" (*[1,5,3])" |
|
||||||
|
--- return ---
|
||||||
|
()
|
||||||
|
--# saved #--
|
||||||
|
{}
|
||||||
10
test/tests/exported variable nested.ans
Normal file
10
test/tests/exported variable nested.ans
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
:f = $
|
||||||
|
| kk
|
||||||
|
:@x = 42
|
||||||
|
:y = $
|
||||||
|
:@z = 12
|
||||||
|
| ko
|
||||||
|
|
||||||
|
|{f.x}
|
||||||
|
|
||||||
|
|{f.y}
|
||||||
11
test/tests/symbol alias exported.ans
Normal file
11
test/tests/symbol alias exported.ans
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
:@l = *[1,2,3]
|
||||||
|
|
||||||
|
:&@c = l(2)
|
||||||
|
|
||||||
|
| c={c} (2)
|
||||||
|
| l={l} (*[1,2,3])
|
||||||
|
|
||||||
|
c = 5
|
||||||
|
|
||||||
|
| c={c} (5)
|
||||||
|
| l={l} (*[1,5,3])
|
||||||
Loading…
Add table
Add a link
Reference in a new issue