use crate::proc::Namer;
use std::rc::Rc;
enum Nesting {
Loop,
Switch {
variable: Rc<String>,
continue_encountered: bool,
},
}
pub(super) enum ExitControlFlow {
None,
Continue {
variable: Rc<String>,
},
Break {
variable: Rc<String>,
},
}
#[derive(Default)]
pub(super) struct ContinueCtx {
stack: Vec<Nesting>,
}
impl ContinueCtx {
pub fn clear(&mut self) {
self.stack.clear();
}
pub fn enter_loop(&mut self) {
self.stack.push(Nesting::Loop);
}
pub fn exit_loop(&mut self) {
if !matches!(self.stack.pop(), Some(Nesting::Loop)) {
unreachable!("ContinueCtx stack out of sync");
}
}
pub fn enter_switch(&mut self, namer: &mut Namer) -> Option<Rc<String>> {
match self.stack.last() {
None => None,
Some(&Nesting::Loop { .. }) => {
let variable = Rc::new(namer.call("should_continue"));
self.stack.push(Nesting::Switch {
variable: Rc::clone(&variable),
continue_encountered: false,
});
Some(variable)
}
Some(&Nesting::Switch { ref variable, .. }) => {
self.stack.push(Nesting::Switch {
variable: Rc::clone(variable),
continue_encountered: false,
});
None
}
}
}
pub fn exit_switch(&mut self) -> ExitControlFlow {
match self.stack.pop() {
None => ExitControlFlow::None,
Some(Nesting::Loop { .. }) => {
unreachable!("Unexpected loop state when exiting switch");
}
Some(Nesting::Switch {
variable,
continue_encountered: inner_continue,
}) => {
if !inner_continue {
ExitControlFlow::None
} else if let Some(&mut Nesting::Switch {
continue_encountered: ref mut outer_continue,
..
}) = self.stack.last_mut()
{
*outer_continue = true;
ExitControlFlow::Break { variable }
} else {
ExitControlFlow::Continue { variable }
}
}
}
}
pub fn continue_encountered(&mut self) -> Option<&str> {
if let Some(&mut Nesting::Switch {
ref variable,
ref mut continue_encountered,
}) = self.stack.last_mut()
{
*continue_encountered = true;
Some(variable)
} else {
None
}
}
}