use sim_kernel::{
Cx, Error, Ref, Result, Symbol,
control::{ControlResume, default_control_result_shape, resume},
};
use crate::{ContinuationValue, ControlResultValue};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Restart {
name: Symbol,
continuation: ContinuationValue,
}
impl Restart {
pub fn new(name: Symbol, continuation: ContinuationValue) -> Self {
Self { name, continuation }
}
pub fn name(&self) -> &Symbol {
&self.name
}
pub fn continuation(&self) -> &ContinuationValue {
&self.continuation
}
pub fn invoke(&self, cx: &mut Cx, value: Ref) -> Result<ControlResultValue> {
let result = resume(
cx,
ControlResume::new(
self.continuation.continuation().clone(),
value,
default_control_result_shape(),
),
)?;
Ok(ControlResultValue::new(result))
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RestartStack {
restarts: Vec<Restart>,
}
impl RestartStack {
pub fn new() -> Self {
Self::default()
}
pub fn push(&mut self, restart: Restart) {
self.restarts.push(restart);
}
pub fn pop(&mut self) -> Option<Restart> {
self.restarts.pop()
}
pub fn invoke(&self, cx: &mut Cx, name: &Symbol, value: Ref) -> Result<ControlResultValue> {
self.nearest_restart(name)?.invoke(cx, value)
}
pub fn nearest_restart(&self, name: &Symbol) -> Result<&Restart> {
self.restarts
.iter()
.rev()
.find(|restart| restart.name() == name)
.ok_or_else(|| Error::Eval(format!("no restart named {name}")))
}
}
pub fn invoke_restart(
cx: &mut Cx,
stack: &RestartStack,
name: &Symbol,
value: Ref,
) -> Result<ControlResultValue> {
stack.invoke(cx, name, value)
}