diff --git a/anselme/ast/Function.lua b/anselme/ast/Function.lua index 7f6fa3e..a122a19 100644 --- a/anselme/ast/Function.lua +++ b/anselme/ast/Function.lua @@ -66,7 +66,7 @@ Function = Overloadable { local upvalues = {} self.expression:traverse(list_upvalues, upvalues) if scope:defined(state, ast.Identifier:new("_")) then - scope:get(state, ast.Identifier:new("_")):traverse(list_upvalues) + scope:get(state, ast.Identifier:new("_")):traverse(list_upvalues, upvalues) end -- cache upvalues so they aren't affected by future redefinition in a parent scope @@ -113,14 +113,12 @@ Function = Overloadable { state.scope:push(self.scope) calling_environment_manager:push(state, calling_environment) - resume_manager:push(state, target) - -- push function scope state.scope:push() + resume_manager:push(state, target) local exp = self.expression:eval(state) - state.scope:pop() - resume_manager:pop(state) + state.scope:pop() calling_environment_manager:pop(state) state.scope:pop() diff --git a/anselme/state/event_manager.lua b/anselme/state/event_manager.lua index 54a22d2..37af412 100644 --- a/anselme/state/event_manager.lua +++ b/anselme/state/event_manager.lua @@ -31,7 +31,7 @@ return class { -- will flush if an event of a different type is present in the buffer write = function(self, state, event) -- discard if requested - if state.scope:defined_in_current(discard_next_events_symbol) then + if state.scope:defined(discard_next_events_identifier) then local discard_type = state.scope:get(discard_next_events_identifier):to_lua() if discard_type == event.type then return @@ -50,12 +50,12 @@ return class { state.scope:set(last_event_type_identifier, String:new(event.type)) state.scope:get(event_buffer_identifier):insert(state, event) end, - -- same as :write, but will not actually write the event, instead discarding all immediately following event of the same type in the same scope - write_and_discard_following = function(self, state, event) - if not state.scope:defined_in_current(discard_next_events_symbol) then - state.scope:define(discard_next_events_symbol, String:new(event.type)) + -- same as :write, but will not actually write the event, instead discarding all immediately following event of the same type in the same scope and children + write_and_discard_following = function(self, state, event, scope) + if not scope:defined_in_current(state, discard_next_events_symbol) then + scope:define(state, discard_next_events_symbol, String:new(event.type)) else - state.scope:set(discard_next_events_identifier, String:new(event.type)) + scope:set(state, discard_next_events_identifier, String:new(event.type)) end end, diff --git a/anselme/state/resume_manager.lua b/anselme/state/resume_manager.lua index fe9dc30..2c992e2 100644 --- a/anselme/state/resume_manager.lua +++ b/anselme/state/resume_manager.lua @@ -4,7 +4,9 @@ local ast = require("anselme.ast") local Nil, Identifier, ResumeTarget, Boolean -- stack of resumable contexts -local resume_target_identifier, resume_target_symbol, resume_no_continue_identifier, resume_no_continue_symbol +local resume_target_identifier, resume_target_symbol +local resume_no_continue_identifier, resume_no_continue_symbol +local resume_environment_identifier, resume_environment_symbol local resume_manager = class { init = false, @@ -12,18 +14,20 @@ local resume_manager = class { -- push a new resume context: all run code between this and the next push will try to resume to target push = 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:push_partial(resume_target_identifier, resume_no_continue_identifier, resume_environment_identifier) state.scope:define(resume_target_symbol, target) state.scope:define(resume_no_continue_symbol, Boolean:new(false)) + state.scope:define(resume_environment_symbol, state.scope:capture()) 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:push_partial(resume_target_identifier, resume_no_continue_identifier, resume_environment_identifier) state.scope:define(resume_target_symbol, target) state.scope:define(resume_no_continue_symbol, Boolean:new(true)) + state.scope:define(resume_environment_symbol, state.scope:capture()) end, -- pop the current resume context pop = function(self, state) @@ -48,6 +52,11 @@ local resume_manager = class { -- (assumes that we are currently :resuming) no_continue = function(self, state) return state.scope:get(resume_no_continue_identifier):to_lua(state) + end, + -- returns the environment that was on top of the stack when the resume started + -- (assumes that we are currently :resuming) + resuming_environment = function(self, state) + return state.scope:get(resume_environment_identifier) end } @@ -58,6 +67,9 @@ Nil, Identifier, ResumeTarget, Boolean = ast.Nil, ast.Identifier, ast.abstract.R resume_target_identifier = Identifier:new("_resume_target") resume_target_symbol = resume_target_identifier:to_symbol() +resume_environment_identifier = Identifier:new("_resume_environment") +resume_environment_symbol = resume_environment_identifier:to_symbol() + resume_no_continue_identifier = Identifier:new("_resume_no_continue") resume_no_continue_symbol = resume_no_continue_identifier:to_symbol() diff --git a/anselme/stdlib/text.lua b/anselme/stdlib/text.lua index d5d6a6e..1ee0325 100644 --- a/anselme/stdlib/text.lua +++ b/anselme/stdlib/text.lua @@ -4,6 +4,7 @@ local Nil, Choice, PartialScope, ArgumentTuple = ast.Nil, ast.Choice, ast.Partia local event_manager = require("anselme.state.event_manager") local translation_manager = require("anselme.state.translation_manager") local tag_manager = require("anselme.state.tag_manager") +local resume_manager = require("anselme.state.resume_manager") return { -- text @@ -21,7 +22,7 @@ return { function(state, text, func) if func:contains_current_resume_target(state) then func:call(state, ArgumentTuple:new()) - event_manager:write_and_discard_following(state, Choice:new(text, func)) + event_manager:write_and_discard_following(state, Choice:new(text, func), resume_manager:resuming_environment(state)) else event_manager:write(state, Choice:new(text, func)) end