use sim_kernel::{
Cx, Error, Ref, Result, Symbol,
control::{ControlAbort, abort, default_control_result_shape},
};
use crate::ControlResultValue;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NonLocalExitKind {
Break,
Next,
Return,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LabeledPrompt {
label: Symbol,
prompt: Ref,
}
impl LabeledPrompt {
pub fn new(label: Symbol, prompt: Ref) -> Self {
Self { label, prompt }
}
pub fn label(&self) -> &Symbol {
&self.label
}
pub fn prompt(&self) -> &Ref {
&self.prompt
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NonLocalExit {
kind: NonLocalExitKind,
label: Symbol,
value: Ref,
}
impl NonLocalExit {
pub fn new(kind: NonLocalExitKind, label: Symbol, value: Ref) -> Self {
Self { kind, label, value }
}
pub fn break_to(label: Symbol, value: Ref) -> Self {
Self::new(NonLocalExitKind::Break, label, value)
}
pub fn next_to(label: Symbol, value: Ref) -> Self {
Self::new(NonLocalExitKind::Next, label, value)
}
pub fn return_to(label: Symbol, value: Ref) -> Self {
Self::new(NonLocalExitKind::Return, label, value)
}
pub fn kind(&self) -> NonLocalExitKind {
self.kind
}
pub fn label(&self) -> &Symbol {
&self.label
}
pub fn value(&self) -> &Ref {
&self.value
}
}
pub fn escape_to_label(
cx: &mut Cx,
prompts: &[LabeledPrompt],
exit: NonLocalExit,
) -> Result<ControlResultValue> {
let prompt = prompts
.iter()
.rev()
.find(|prompt| prompt.label() == exit.label())
.ok_or_else(|| Error::Eval(format!("no labeled prompt for {}", exit.label())))?;
let result = abort(
cx,
ControlAbort::new(
prompt.prompt().clone(),
exit.value().clone(),
default_control_result_shape(),
),
)?;
Ok(ControlResultValue::new(result))
}