Files
orchid/src/foreign/cps_box.rs
Lawrence Bethlenfalvy c279301583 Massive feature drop
- pattern matching seems to be correct
- dynamic dispatch works with the to_string example
- template strings as a last-minute addition
- interpreter revamp, virtual stack for abort safety
2024-01-29 18:26:56 +00:00

101 lines
3.3 KiB
Rust

//! Automated wrappers to make working with CPS commands easier.
use std::fmt::Debug;
use trait_set::trait_set;
use super::atom::{
Atomic, AtomicResult, AtomicReturn, CallData, NotAFunction, RunData,
};
use super::error::{ExternError, ExternResult};
use crate::interpreter::nort::{Clause, Expr};
use crate::location::CodeLocation;
use crate::utils::ddispatch::{Request, Responder};
use crate::utils::pure_seq::pushed_ref;
trait_set! {
/// A "well behaved" type that can be used as payload in a CPS box
pub trait CPSPayload = Clone + Debug + Send + 'static;
/// A function to handle a CPS box with a specific payload
pub trait CPSHandler<T: CPSPayload> = FnMut(&T, &Expr) -> ExternResult<Expr>;
}
/// An Orchid Atom value encapsulating a payload and continuation points
#[derive(Debug, Clone)]
pub struct CPSBox<T: CPSPayload> {
/// Number of arguments not provided yet
pub argc: usize,
/// Details about the command
pub payload: T,
/// Possible continuations, in the order they were provided
pub continuations: Vec<Expr>,
}
impl<T: CPSPayload> CPSBox<T> {
/// Create a new command prepared to receive exacly N continuations
#[must_use]
pub fn new(argc: usize, payload: T) -> Self {
debug_assert!(argc > 0, "Null-ary CPS functions are invalid");
Self { argc, continuations: Vec::new(), payload }
}
/// Unpack the wrapped command and the continuation
#[must_use]
pub fn unpack1(&self) -> (&T, Expr) {
match &self.continuations[..] {
[cont] => (&self.payload, cont.clone()),
_ => panic!("size mismatch"),
}
}
/// Unpack the wrapped command and 2 continuations (usually an async and a
/// sync)
#[must_use]
pub fn unpack2(&self) -> (&T, Expr, Expr) {
match &self.continuations[..] {
[c1, c2] => (&self.payload, c1.clone(), c2.clone()),
_ => panic!("size mismatch"),
}
}
/// Unpack the wrapped command and 3 continuations (usually an async success,
/// an async fail and a sync)
#[must_use]
pub fn unpack3(&self) -> (&T, Expr, Expr, Expr) {
match &self.continuations[..] {
[c1, c2, c3] => (&self.payload, c1.clone(), c2.clone(), c3.clone()),
_ => panic!("size mismatch"),
}
}
fn assert_applicable(&self, err_loc: &CodeLocation) -> ExternResult<()> {
match self.argc {
0 => Err(NotAFunction(self.clone().atom_expr(err_loc.clone())).rc()),
_ => Ok(()),
}
}
}
impl<T: CPSPayload> Responder for CPSBox<T> {
fn respond(&self, _request: Request) {}
}
impl<T: CPSPayload> Atomic for CPSBox<T> {
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
fn as_any_ref(&self) -> &dyn std::any::Any { self }
fn parser_eq(&self, _: &dyn std::any::Any) -> bool { false }
fn redirect(&mut self) -> Option<&mut Expr> { None }
fn run(self: Box<Self>, _: RunData) -> AtomicResult {
AtomicReturn::inert(*self)
}
fn apply(mut self: Box<Self>, call: CallData) -> ExternResult<Clause> {
self.assert_applicable(&call.location)?;
self.argc -= 1;
self.continuations.push(call.arg);
Ok(self.atom_cls())
}
fn apply_ref(&self, call: CallData) -> ExternResult<Clause> {
self.assert_applicable(&call.location)?;
let new = Self {
argc: self.argc - 1,
continuations: pushed_ref(&self.continuations, call.arg),
payload: self.payload.clone(),
};
Ok(new.atom_cls())
}
}