use sim_kernel::{
Cx, Error, Ref, Result, Symbol,
control::{ControlCapture, capture, default_control_result_shape},
};
use crate::ContinuationValue;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Condition {
kind: Symbol,
payload: Ref,
}
impl Condition {
pub fn new(kind: Symbol, payload: Ref) -> Self {
Self { kind, payload }
}
pub fn kind(&self) -> &Symbol {
&self.kind
}
pub fn payload(&self) -> &Ref {
&self.payload
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ConditionHandler {
kind: Symbol,
prompt: Ref,
continuation: Ref,
multishot: bool,
}
impl ConditionHandler {
pub fn new(kind: Symbol, prompt: Ref, continuation: Ref) -> Self {
Self {
kind,
prompt,
continuation,
multishot: false,
}
}
pub fn multishot(mut self) -> Self {
self.multishot = true;
self
}
pub fn kind(&self) -> &Symbol {
&self.kind
}
pub fn prompt(&self) -> &Ref {
&self.prompt
}
pub fn continuation(&self) -> &Ref {
&self.continuation
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ConditionStack {
handlers: Vec<ConditionHandler>,
}
impl ConditionStack {
pub fn new() -> Self {
Self::default()
}
pub fn push(&mut self, handler: ConditionHandler) {
self.handlers.push(handler);
}
pub fn pop(&mut self) -> Option<ConditionHandler> {
self.handlers.pop()
}
pub fn signal(&self, cx: &mut Cx, condition: Condition) -> Result<ContinuationValue> {
let handler = self.nearest_handler(condition.kind())?;
let mut request = ControlCapture::new(
handler.prompt().clone(),
handler.continuation().clone(),
condition.payload().clone(),
default_control_result_shape(),
);
if handler.multishot {
request = request.multishot();
}
let capture_result = capture(cx, request)?;
Ok(ContinuationValue::new(
handler.continuation().clone(),
capture_result,
handler.multishot,
))
}
pub fn nearest_handler(&self, kind: &Symbol) -> Result<&ConditionHandler> {
self.handlers
.iter()
.rev()
.find(|handler| handler.kind() == kind)
.ok_or_else(|| Error::Eval(format!("no condition handler for {kind}")))
}
}
pub fn signal_condition(
cx: &mut Cx,
stack: &ConditionStack,
condition: Condition,
) -> Result<ContinuationValue> {
stack.signal(cx, condition)
}