rune/compile/v1/
breaks.rsuse crate as rune;
use crate::alloc::prelude::*;
use crate::alloc::{self, Vec};
use crate::ast::Spanned;
use crate::compile::{self, ErrorKind, WithSpan};
use crate::runtime::{InstAddress, Label, Output};
#[derive(TryClone)]
pub(crate) struct Break<'hir> {
pub(crate) label: Option<&'hir str>,
pub(crate) output: Option<Output>,
pub(crate) continue_label: Option<Label>,
pub(crate) break_label: Label,
pub(crate) drop: Option<InstAddress>,
}
pub(crate) struct Breaks<'hir> {
loops: Vec<Break<'hir>>,
}
impl<'hir> Breaks<'hir> {
pub(crate) fn new() -> Self {
Self { loops: Vec::new() }
}
pub(crate) fn last(&self) -> Option<&Break<'hir>> {
self.loops.last()
}
pub(crate) fn push(&mut self, l: Break<'hir>) -> alloc::Result<()> {
self.loops.try_push(l)?;
Ok(())
}
pub(crate) fn pop(&mut self) {
let empty = self.loops.pop().is_some();
debug_assert!(empty);
}
pub(crate) fn walk_until_label(
&self,
span: &dyn Spanned,
expected: &str,
drop: &mut Vec<InstAddress>,
) -> compile::Result<&Break<'hir>> {
drop.clear();
self.find_label_inner(span, expected, &mut |l| drop.try_extend(l.drop))
}
pub(crate) fn find_label(
&self,
span: &dyn Spanned,
expected: &str,
) -> compile::Result<&Break<'hir>> {
self.find_label_inner(span, expected, &mut |_| Ok(()))
}
fn find_label_inner(
&self,
span: &dyn Spanned,
expected: &str,
visitor: &mut dyn FnMut(&Break<'hir>) -> alloc::Result<()>,
) -> compile::Result<&Break<'hir>> {
for l in self.loops.iter().rev() {
visitor(l).with_span(span)?;
let Some(label) = l.label else {
continue;
};
if expected == label {
return Ok(l);
}
}
Err(compile::Error::new(
span,
ErrorKind::MissingLabel {
label: expected.try_into()?,
},
))
}
}