forked from Orchid/orchid
Backup commit
My backspace key started ghosting. Nothing works atm.
This commit is contained in:
@@ -4,99 +4,97 @@ use std::fmt::Debug;
|
||||
|
||||
use trait_set::trait_set;
|
||||
|
||||
use super::{Atomic, ExternFn, InertAtomic, XfnResult};
|
||||
use crate::interpreted::{Clause, ExprInst};
|
||||
use crate::interpreter::{Context, HandlerRes};
|
||||
use super::atom::{Atomic, AtomicResult, AtomicReturn, NotAFunction};
|
||||
use super::error::{ExternError, ExternResult};
|
||||
use crate::interpreter::apply::CallData;
|
||||
use crate::interpreter::nort::{Clause, ClauseInst, Expr};
|
||||
use crate::interpreter::run::RunData;
|
||||
use crate::location::CodeLocation;
|
||||
use crate::utils::ddispatch::{Request, Responder};
|
||||
use crate::utils::pure_seq::pushed_ref;
|
||||
use crate::ConstTree;
|
||||
|
||||
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, &ExprInst) -> HandlerRes;
|
||||
pub trait CPSHandler<T: CPSPayload> = FnMut(&T, &Expr) -> ExternResult<Expr>;
|
||||
}
|
||||
|
||||
/// The pre-argument version of CPSBox
|
||||
#[derive(Debug, Clone)]
|
||||
struct CPSFn<T: CPSPayload> {
|
||||
pub argc: usize,
|
||||
pub continuations: Vec<ExprInst>,
|
||||
pub payload: T,
|
||||
}
|
||||
impl<T: CPSPayload> CPSFn<T> {
|
||||
#[must_use]
|
||||
fn new(argc: usize, payload: T) -> Self {
|
||||
debug_assert!(
|
||||
argc > 0,
|
||||
"Null-ary CPS functions are invalid, use an Atom instead"
|
||||
);
|
||||
Self { argc, continuations: Vec::new(), payload }
|
||||
}
|
||||
}
|
||||
impl<T: CPSPayload> ExternFn for CPSFn<T> {
|
||||
fn name(&self) -> &str { "CPS function without argument" }
|
||||
fn apply(self: Box<Self>, arg: ExprInst, _ctx: Context) -> XfnResult<Clause> {
|
||||
let payload = self.payload.clone();
|
||||
let continuations = pushed_ref(&self.continuations, arg);
|
||||
if self.argc == 1 {
|
||||
Ok(CPSBox { payload, continuations }.atom_cls())
|
||||
} else {
|
||||
Ok(CPSFn { argc: self.argc - 1, payload, continuations }.xfn_cls())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An inert Orchid Atom value encapsulating a payload and a continuation point
|
||||
/// 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<ExprInst>,
|
||||
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, ExprInst) {
|
||||
let [cont]: [ExprInst; 1] =
|
||||
self.continuations.try_into().expect("size checked");
|
||||
(self.payload, cont)
|
||||
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, ExprInst, ExprInst) {
|
||||
let [c1, c2]: [ExprInst; 2] =
|
||||
self.continuations.try_into().expect("size checked");
|
||||
(self.payload, c1, c2)
|
||||
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, ExprInst, ExprInst, ExprInst) {
|
||||
let [c1, c2, c3]: [ExprInst; 3] =
|
||||
self.continuations.try_into().expect("size checked");
|
||||
(self.payload, c1, c2, c3)
|
||||
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> InertAtomic for CPSBox<T> {
|
||||
fn type_str() -> &'static str { "a CPS box" }
|
||||
impl<T: CPSPayload> Responder for CPSBox<T> {
|
||||
fn respond(&self, _request: Request) {}
|
||||
}
|
||||
|
||||
/// Like [init_cps] but wrapped in a [ConstTree] for init-time usage
|
||||
#[must_use]
|
||||
pub fn const_cps<T: CPSPayload>(argc: usize, payload: T) -> ConstTree {
|
||||
ConstTree::xfn(CPSFn::new(argc, payload))
|
||||
}
|
||||
|
||||
/// Construct a CPS function which takes an argument and then acts inert
|
||||
/// so that command executors can receive it.
|
||||
///
|
||||
/// This function is meant to be used in an external function defined with
|
||||
/// [crate::define_fn]. For usage in a [ConstTree], see [mk_const]
|
||||
#[must_use]
|
||||
pub fn init_cps<T: CPSPayload>(argc: usize, payload: T) -> Clause {
|
||||
CPSFn::new(argc, payload).xfn_cls()
|
||||
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 ClauseInst> { None }
|
||||
fn run(self: Box<Self>, run: RunData) -> AtomicResult {
|
||||
AtomicReturn::inert(*self, run.ctx)
|
||||
}
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user