use crate::ast;
use crate::ast::Spanned;
use crate::compile::v1::Needs;
use crate::compile::{CompileError, CompileErrorKind, CompileResult};
use crate::parse::ResolveContext;
use crate::runtime::Label;
use std::cell::RefCell;
use std::rc::Rc;
pub(crate) struct LoopGuard {
loops: Rc<RefCell<Vec<Loop>>>,
}
impl Drop for LoopGuard {
fn drop(&mut self) {
let empty = self.loops.borrow_mut().pop().is_some();
debug_assert!(empty);
}
}
#[derive(Clone, Copy)]
pub(crate) struct Loop {
pub(crate) label: Option<ast::Label>,
pub(crate) continue_label: Label,
pub(crate) continue_var_count: usize,
pub(crate) break_label: Label,
pub(crate) break_var_count: usize,
pub(crate) needs: Needs,
pub(crate) drop: Option<usize>,
}
pub(crate) struct Loops {
loops: Rc<RefCell<Vec<Loop>>>,
}
impl Loops {
pub(crate) fn new() -> Self {
Self {
loops: Rc::new(RefCell::new(vec![])),
}
}
pub(crate) fn last(&self) -> Option<Loop> {
self.loops.borrow().last().copied()
}
pub(crate) fn push(&mut self, l: Loop) -> LoopGuard {
self.loops.borrow_mut().push(l);
LoopGuard {
loops: self.loops.clone(),
}
}
pub(crate) fn walk_until_label(
&self,
ctx: ResolveContext<'_>,
expected: &ast::Label,
) -> CompileResult<(Loop, Vec<usize>)> {
use crate::parse::Resolve;
let span = expected.span();
let expected = expected.resolve(ctx)?;
let mut to_drop = Vec::new();
for l in self.loops.borrow().iter().rev() {
to_drop.extend(l.drop);
let label = match l.label {
Some(label) => label,
None => {
continue;
}
};
let label = label.resolve(ctx)?;
if expected == label {
return Ok((*l, to_drop));
}
}
Err(CompileError::new(
span,
CompileErrorKind::MissingLoopLabel {
label: expected.into(),
},
))
}
pub(crate) fn iter(&self) -> impl Iterator<Item = Loop> {
let loops = self.loops.borrow().clone();
loops.into_iter()
}
}