Skip to main content

sim_lib_control/
condition.rs

1use sim_kernel::{
2    Cx, Error, Ref, Result, Symbol,
3    control::{ControlCapture, capture, default_control_result_shape},
4};
5
6use crate::ContinuationValue;
7
8/// A signalled condition: a kind symbol plus a payload reference.
9///
10/// The condition system's analogue of a raised exception, but resumable: the
11/// payload travels to the nearest matching [`ConditionHandler`].
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct Condition {
14    kind: Symbol,
15    payload: Ref,
16}
17
18impl Condition {
19    /// Builds a condition of the given `kind` carrying `payload`.
20    pub fn new(kind: Symbol, payload: Ref) -> Self {
21        Self { kind, payload }
22    }
23
24    /// Returns the condition kind used to select a handler.
25    pub fn kind(&self) -> &Symbol {
26        &self.kind
27    }
28
29    /// Returns the payload delivered to the handler.
30    pub fn payload(&self) -> &Ref {
31        &self.payload
32    }
33}
34
35/// A handler bound to a condition kind, prompt, and capture continuation.
36///
37/// Installed on a [`ConditionStack`]; when a matching [`Condition`] is
38/// signalled, its prompt and continuation drive the underlying control capture.
39#[derive(Clone, Debug, PartialEq, Eq)]
40pub struct ConditionHandler {
41    kind: Symbol,
42    prompt: Ref,
43    continuation: Ref,
44    multishot: bool,
45}
46
47impl ConditionHandler {
48    /// Builds a one-shot handler for `kind` bound to `prompt` and
49    /// `continuation`.
50    pub fn new(kind: Symbol, prompt: Ref, continuation: Ref) -> Self {
51        Self {
52            kind,
53            prompt,
54            continuation,
55            multishot: false,
56        }
57    }
58
59    /// Returns a copy of this handler marked multishot (resumable more than
60    /// once).
61    pub fn multishot(mut self) -> Self {
62        self.multishot = true;
63        self
64    }
65
66    /// Returns the condition kind this handler matches.
67    pub fn kind(&self) -> &Symbol {
68        &self.kind
69    }
70
71    /// Returns the prompt the handler captures against.
72    pub fn prompt(&self) -> &Ref {
73        &self.prompt
74    }
75
76    /// Returns the continuation resumed after the handler runs.
77    pub fn continuation(&self) -> &Ref {
78        &self.continuation
79    }
80}
81
82/// A stack of [`ConditionHandler`]s, searched innermost-first on signal.
83///
84/// Models the dynamic-extent handler chain of the condition system; signalling
85/// dispatches to the nearest handler whose kind matches.
86#[derive(Clone, Debug, Default, PartialEq, Eq)]
87pub struct ConditionStack {
88    handlers: Vec<ConditionHandler>,
89}
90
91impl ConditionStack {
92    /// Builds an empty condition stack.
93    pub fn new() -> Self {
94        Self::default()
95    }
96
97    /// Installs `handler` as the new innermost handler.
98    pub fn push(&mut self, handler: ConditionHandler) {
99        self.handlers.push(handler);
100    }
101
102    /// Removes and returns the innermost handler, if any.
103    pub fn pop(&mut self) -> Option<ConditionHandler> {
104        self.handlers.pop()
105    }
106
107    /// Signals `condition`, capturing against the nearest matching handler and
108    /// returning the resulting [`ContinuationValue`].
109    ///
110    /// Fails with [`Error::Eval`](sim_kernel::Error::Eval) when no handler
111    /// matches the condition kind.
112    pub fn signal(&self, cx: &mut Cx, condition: Condition) -> Result<ContinuationValue> {
113        let handler = self.nearest_handler(condition.kind())?;
114        let mut request = ControlCapture::new(
115            handler.prompt().clone(),
116            handler.continuation().clone(),
117            condition.payload().clone(),
118            default_control_result_shape(),
119        );
120        if handler.multishot {
121            request = request.multishot();
122        }
123        let capture_result = capture(cx, request)?;
124        Ok(ContinuationValue::new(
125            handler.continuation().clone(),
126            capture_result,
127            handler.multishot,
128        ))
129    }
130
131    /// Returns the innermost handler matching `kind`, or
132    /// [`Error::Eval`](sim_kernel::Error::Eval) when none is installed.
133    pub fn nearest_handler(&self, kind: &Symbol) -> Result<&ConditionHandler> {
134        self.handlers
135            .iter()
136            .rev()
137            .find(|handler| handler.kind() == kind)
138            .ok_or_else(|| Error::Eval(format!("no condition handler for {kind}")))
139    }
140}
141
142/// Signals `condition` against `stack`; free-function form of
143/// [`ConditionStack::signal`].
144pub fn signal_condition(
145    cx: &mut Cx,
146    stack: &ConditionStack,
147    condition: Condition,
148) -> Result<ContinuationValue> {
149    stack.signal(cx, condition)
150}