forked from Orchid/orchid
Removed foreign macros
Converted the function integration to use template metaprogramming instead of macros.
This commit is contained in:
@@ -36,7 +36,7 @@ impl<T: CPSPayload> CPSFn<T> {
|
||||
}
|
||||
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 {
|
||||
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 {
|
||||
|
||||
@@ -5,14 +5,12 @@ use std::rc::Rc;
|
||||
|
||||
use dyn_clone::DynClone;
|
||||
|
||||
use super::XfnResult;
|
||||
use crate::interpreted::ExprInst;
|
||||
use crate::interpreter::Context;
|
||||
use crate::representations::interpreted::Clause;
|
||||
use crate::Primitive;
|
||||
|
||||
/// Returned by [ExternFn::apply]
|
||||
pub type XfnResult = Result<Clause, Rc<dyn ExternError>>;
|
||||
|
||||
/// Errors produced by external code
|
||||
pub trait ExternError: Display {
|
||||
/// Convert into trait object
|
||||
@@ -41,7 +39,7 @@ pub trait ExternFn: DynClone {
|
||||
#[must_use]
|
||||
fn name(&self) -> &str;
|
||||
/// Combine the function with an argument to produce a new clause
|
||||
fn apply(self: Box<Self>, arg: ExprInst, ctx: Context) -> XfnResult;
|
||||
fn apply(self: Box<Self>, arg: ExprInst, ctx: Context) -> XfnResult<Clause>;
|
||||
/// Hash the name to get a somewhat unique hash.
|
||||
fn hash(&self, mut state: &mut dyn std::hash::Hasher) {
|
||||
self.name().hash(&mut state)
|
||||
|
||||
199
src/foreign/fn_bridge.rs
Normal file
199
src/foreign/fn_bridge.rs
Normal file
@@ -0,0 +1,199 @@
|
||||
use std::fmt::Debug;
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::{
|
||||
Atomic, AtomicResult, AtomicReturn, ExternError, ExternFn, XfnResult,
|
||||
};
|
||||
use crate::ddispatch::Responder;
|
||||
use crate::interpreted::{Clause, ExprInst, TryFromExprInst};
|
||||
use crate::interpreter::{run, Context, Return};
|
||||
use crate::systems::codegen::{opt, res};
|
||||
use crate::{Literal, OrcString};
|
||||
|
||||
/// A trait for things that are infallibly convertible to [Clause]. These types
|
||||
/// can be returned by callbacks passed to the [super::xfn_1ary] family of
|
||||
/// functions.
|
||||
pub trait ToClause: Clone {
|
||||
/// Convert the type to a [Clause].
|
||||
fn to_clause(self) -> Clause;
|
||||
}
|
||||
|
||||
impl<T: Atomic + Clone> ToClause for T {
|
||||
fn to_clause(self) -> Clause { self.atom_cls() }
|
||||
}
|
||||
impl ToClause for Clause {
|
||||
fn to_clause(self) -> Clause { self }
|
||||
}
|
||||
impl ToClause for ExprInst {
|
||||
fn to_clause(self) -> Clause { self.expr_val().clause }
|
||||
}
|
||||
impl ToClause for Literal {
|
||||
fn to_clause(self) -> Clause { self.into() }
|
||||
}
|
||||
impl ToClause for u64 {
|
||||
fn to_clause(self) -> Clause { Literal::Uint(self).into() }
|
||||
}
|
||||
impl ToClause for String {
|
||||
fn to_clause(self) -> Clause { OrcString::from(self).cls() }
|
||||
}
|
||||
impl<T: ToClause> ToClause for Option<T> {
|
||||
fn to_clause(self) -> Clause { opt(self.map(|t| t.to_clause().wrap())) }
|
||||
}
|
||||
impl<T: ToClause, U: ToClause> ToClause for Result<T, U> {
|
||||
fn to_clause(self) -> Clause {
|
||||
res(self.map(|t| t.to_clause().wrap()).map_err(|u| u.to_clause().wrap()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a unary lambda wrapped in this struct to take an additional argument
|
||||
/// in a function passed to Orchid through a member of the [super::xfn_1ary]
|
||||
/// family.
|
||||
///
|
||||
/// Container for a unary [FnOnce] that uniquely states the argument and return
|
||||
/// type. Rust functions are never overloaded, but inexplicably the [Fn] traits
|
||||
/// take the argument tuple as a generic parameter which means that it cannot
|
||||
/// be a unique dispatch target.
|
||||
pub struct Param<T, U, F> {
|
||||
data: F,
|
||||
_t: PhantomData<T>,
|
||||
_u: PhantomData<U>,
|
||||
}
|
||||
impl<T, U, F> Param<T, U, F> {
|
||||
/// Wrap a new function in a parametric struct
|
||||
pub fn new(f: F) -> Self
|
||||
where
|
||||
F: FnOnce(T) -> Result<U, Rc<dyn ExternError>>,
|
||||
{
|
||||
Self { data: f, _t: PhantomData::default(), _u: PhantomData::default() }
|
||||
}
|
||||
/// Take out the function
|
||||
pub fn get(self) -> F { self.data }
|
||||
}
|
||||
impl<T, U, F: Clone> Clone for Param<T, U, F> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
data: self.data.clone(),
|
||||
_t: PhantomData::default(),
|
||||
_u: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
T: 'static + TryFromExprInst,
|
||||
U: 'static + ToClause,
|
||||
F: 'static + Clone + FnOnce(T) -> Result<U, Rc<dyn ExternError>>,
|
||||
> ToClause for Param<T, U, F>
|
||||
{
|
||||
fn to_clause(self) -> Clause { self.xfn_cls() }
|
||||
}
|
||||
|
||||
struct FnMiddleStage<T, U, F> {
|
||||
argument: ExprInst,
|
||||
f: Param<T, U, F>,
|
||||
}
|
||||
|
||||
impl<T, U, F: Clone> Clone for FnMiddleStage<T, U, F> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { argument: self.argument.clone(), f: self.f.clone() }
|
||||
}
|
||||
}
|
||||
impl<T, U, F> Debug for FnMiddleStage<T, U, F> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("FnMiddleStage")
|
||||
.field("argument", &self.argument)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
impl<T, U, F> Responder for FnMiddleStage<T, U, F> {}
|
||||
impl<
|
||||
T: 'static + TryFromExprInst,
|
||||
U: 'static + ToClause,
|
||||
F: 'static + Clone + FnOnce(T) -> Result<U, Rc<dyn ExternError>>,
|
||||
> Atomic for FnMiddleStage<T, U, F>
|
||||
{
|
||||
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
|
||||
fn as_any_ref(&self) -> &dyn std::any::Any { self }
|
||||
fn run(self: Box<Self>, ctx: Context) -> AtomicResult {
|
||||
let Return { gas, inert, state } = run(self.argument, ctx)?;
|
||||
let clause = match inert {
|
||||
false => state.expr_val().clause,
|
||||
true => (self.f.data)(state.downcast()?)?.to_clause(),
|
||||
};
|
||||
Ok(AtomicReturn { gas, inert: false, clause })
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
T: 'static + TryFromExprInst,
|
||||
U: 'static + ToClause,
|
||||
F: 'static + Clone + FnOnce(T) -> Result<U, Rc<dyn ExternError>>,
|
||||
> ExternFn for Param<T, U, F>
|
||||
{
|
||||
fn name(&self) -> &str { "anonymous Rust function" }
|
||||
fn apply(self: Box<Self>, arg: ExprInst, _: Context) -> XfnResult<Clause> {
|
||||
Ok(FnMiddleStage { argument: arg, f: *self }.atom_cls())
|
||||
}
|
||||
}
|
||||
|
||||
pub mod constructors {
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::{Param, ToClause};
|
||||
use crate::foreign::{ExternError, ExternFn};
|
||||
use crate::interpreted::TryFromExprInst;
|
||||
|
||||
macro_rules! xfn_variant {
|
||||
(
|
||||
$number:expr,
|
||||
($($t:ident)*)
|
||||
($($alt:expr)*)
|
||||
) => {
|
||||
paste::paste!{
|
||||
#[doc = "Convert a function of " $number " argument(s) into a curried"
|
||||
" Orchid function. See also Constraints summarized:\n\n"
|
||||
"- the callback must live as long as `'static`\n"
|
||||
"- All arguments must implement [TryFromExprInst]\n"
|
||||
"- all but the last argument must implement [Clone]\n"
|
||||
"- the return type must implement [ToClause].\n\n"
|
||||
]
|
||||
#[doc = "Other arities: " $( "[xfn_" $alt "ary], " )+ ]
|
||||
pub fn [< xfn_ $number ary >] <
|
||||
$( $t : TryFromExprInst + Clone + 'static, )*
|
||||
TLast: TryFromExprInst + 'static,
|
||||
TReturn: ToClause + 'static,
|
||||
TFunction: FnOnce( $( $t , )* TLast )
|
||||
-> Result<TReturn, Rc<dyn ExternError>> + Clone + 'static
|
||||
>(function: TFunction) -> impl ExternFn {
|
||||
xfn_variant!(@BODY_LOOP function
|
||||
( $( ( $t [< $t:lower >] ) )* )
|
||||
( $( [< $t:lower >] )* )
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
(@BODY_LOOP $function:ident (
|
||||
( $Next:ident $next:ident )
|
||||
$( ( $T:ident $t:ident ) )*
|
||||
) $full:tt) => {
|
||||
Param::new(|$next : $Next| {
|
||||
Ok(xfn_variant!(@BODY_LOOP $function ( $( ( $T $t ) )* ) $full))
|
||||
})
|
||||
};
|
||||
(@BODY_LOOP $function:ident () ( $( $t:ident )* )) => {
|
||||
Param::new(|last: TLast| $function ( $( $t , )* last ))
|
||||
};
|
||||
}
|
||||
|
||||
xfn_variant!(1, () (2 3 4 5 6 7 8 9 10 11 12 13 14 15 16));
|
||||
xfn_variant!(2, (A) (1 3 4 5 6 7 8 9 10 11 12 13 14 15 16));
|
||||
xfn_variant!(3, (A B) (1 2 4 5 6 7 8 9 10 11 12 13 14 15 16));
|
||||
xfn_variant!(4, (A B C) (1 2 3 5 6 7 8 9 10 11 12 13 14 15 16));
|
||||
xfn_variant!(5, (A B C D) (1 2 3 4 6 7 8 9 10 11 12 13 14 15 16));
|
||||
xfn_variant!(6, (A B C D E) (1 2 3 4 5 7 8 9 10 11 12 13 14 15 16));
|
||||
xfn_variant!(7, (A B C D E F) (1 2 3 4 5 6 8 9 10 11 12 13 14 15 16));
|
||||
xfn_variant!(8, (A B C D E F G) (1 2 3 4 5 6 7 9 10 11 12 13 14 15 16));
|
||||
xfn_variant!(9, (A B C D E F G H) (1 2 3 4 5 6 7 8 10 11 12 13 14 15 16));
|
||||
// at higher arities rust-analyzer fails to load the project
|
||||
}
|
||||
@@ -4,7 +4,7 @@ use std::rc::Rc;
|
||||
|
||||
use super::{AtomicResult, AtomicReturn, ExternError};
|
||||
#[allow(unused)] // for doc
|
||||
use crate::define_fn;
|
||||
// use crate::define_fn;
|
||||
use crate::foreign::Atomic;
|
||||
use crate::interpreted::{Clause, Expr, ExprInst, TryFromExprInst};
|
||||
use crate::interpreter::Context;
|
||||
|
||||
@@ -5,15 +5,21 @@
|
||||
mod atom;
|
||||
pub mod cps_box;
|
||||
mod extern_fn;
|
||||
mod fn_bridge;
|
||||
mod inert;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
pub use atom::{Atom, Atomic, AtomicResult, AtomicReturn};
|
||||
pub use extern_fn::{ExternError, ExternFn, XfnResult};
|
||||
pub use extern_fn::{ExternError, ExternFn};
|
||||
pub use fn_bridge::constructors::{
|
||||
xfn_1ary, xfn_2ary, xfn_3ary, xfn_4ary, xfn_5ary, xfn_6ary, xfn_7ary,
|
||||
xfn_8ary, xfn_9ary,
|
||||
};
|
||||
pub use fn_bridge::{Param, ToClause};
|
||||
pub use inert::InertAtomic;
|
||||
|
||||
pub use crate::representations::interpreted::Clause;
|
||||
|
||||
/// A type-erased error in external code
|
||||
pub type RcError = Rc<dyn ExternError>;
|
||||
/// Return type of the argument to the [xfn_1ary] family of functions
|
||||
pub type XfnResult<T> = Result<T, Rc<dyn ExternError>>;
|
||||
|
||||
Reference in New Issue
Block a user