forked from Orchid/orchid
Working example
This commit is contained in:
19
src/foreign_macros/atomic_defaults.rs
Normal file
19
src/foreign_macros/atomic_defaults.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
#[allow(unused)] // for the doc comments
|
||||
use crate::foreign::Atomic;
|
||||
|
||||
/// A macro that generates the straightforward, syntactically invariant part of implementing
|
||||
/// [Atomic]. Implemented fns are [Atomic::as_any], [Atomic::definitely_eq] and [Atomic::hash].
|
||||
///
|
||||
/// It depends on [Eq] and [Hash]
|
||||
#[macro_export]
|
||||
macro_rules! atomic_defaults {
|
||||
() => {
|
||||
fn as_any(&self) -> &dyn std::any::Any { self }
|
||||
fn definitely_eq(&self, _other: &dyn std::any::Any) -> bool {
|
||||
_other.downcast_ref().map(|o| self == o).unwrap_or(false)
|
||||
}
|
||||
fn hash(&self, mut hasher: &mut dyn std::hash::Hasher) {
|
||||
<Self as std::hash::Hash>::hash(self, &mut hasher)
|
||||
}
|
||||
};
|
||||
}
|
||||
105
src/foreign_macros/atomic_impl.rs
Normal file
105
src/foreign_macros/atomic_impl.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
#[allow(unused)] // for the doc comments
|
||||
use crate::representations::Primitive;
|
||||
#[allow(unused)] // for the doc comments
|
||||
use crate::foreign::{Atomic, ExternFn};
|
||||
#[allow(unused)] // for the doc comments
|
||||
use std::any::Any;
|
||||
#[allow(unused)] // for the doc comments
|
||||
use dyn_clone::DynClone;
|
||||
#[allow(unused)] // for the doc comments
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// A macro that generates implementations of [Atomic] to simplify the development of
|
||||
/// external bindings for Orchid.
|
||||
///
|
||||
/// The macro depends on implementations of [AsRef<Clause>] and [From<(&Self, Clause)>] for
|
||||
/// extracting the clause to be processed and then reconstructing the [Atomic]. Naturally,
|
||||
/// supertraits of [Atomic] are also dependencies. These are [Any], [Debug] and [DynClone].
|
||||
///
|
||||
/// The simplest form just requires the typename to be specified. This additionally depends on an
|
||||
/// implementation of [ExternFn] because after the clause is fully normalized it returns `Self`
|
||||
/// wrapped in a [Primitive::ExternFn]. It is intended for intermediary
|
||||
/// stages of the function where validation and the next state are defined in [ExternFn::apply].
|
||||
///
|
||||
/// ```
|
||||
/// atomic_impl!(Multiply1)
|
||||
/// ```
|
||||
///
|
||||
/// The last stage of the function should use the extended form of the macro which takes an
|
||||
/// additional closure to explicitly describe what happens when the argument is fully processed.
|
||||
///
|
||||
/// ```
|
||||
/// // excerpt from the exact implementation of Multiply
|
||||
/// atomic_impl!(Multiply0, |Self(a, cls): &Self| {
|
||||
/// let b: Numeric = cls.clone().try_into().map_err(AssertionError::into_extern)?;
|
||||
/// Ok(*a * b).into())
|
||||
/// })
|
||||
/// ```
|
||||
///
|
||||
#[macro_export]
|
||||
macro_rules! atomic_impl {
|
||||
($typ:ident) => {
|
||||
atomic_impl!{$typ, |this: &Self| Ok(Clause::P(
|
||||
$crate::representations::Primitive::ExternFn(Box::new(this.clone()))
|
||||
))}
|
||||
};
|
||||
($typ:ident, $next_phase:expr) => {
|
||||
impl $crate::foreign::Atomic for $typ {
|
||||
$crate::atomic_defaults!{}
|
||||
fn run_once(&self) -> Result<
|
||||
$crate::representations::interpreted::Clause,
|
||||
$crate::representations::interpreted::InternalError
|
||||
> {
|
||||
match <Self as AsRef<$crate::representations::interpreted::Clause>>::as_ref(self).run_once() {
|
||||
Err($crate::representations::interpreted::InternalError::NonReducible) => {
|
||||
($next_phase)(self)
|
||||
.map_err($crate::representations::interpreted::RuntimeError::Extern)
|
||||
.map_err($crate::representations::interpreted::InternalError::Runtime)
|
||||
}
|
||||
Ok(arg) => Ok($crate::representations::interpreted::Clause::P(
|
||||
$crate::representations::Primitive::Atom(
|
||||
$crate::foreign::Atom::new(
|
||||
<Self as From<(&Self, Clause)>>::from((self, arg))
|
||||
)
|
||||
)
|
||||
)),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
fn run_n_times(&self, n: usize) -> Result<
|
||||
(
|
||||
$crate::representations::interpreted::Clause,
|
||||
usize
|
||||
),
|
||||
$crate::representations::interpreted::RuntimeError
|
||||
> {
|
||||
match <Self as AsRef<Clause>>::as_ref(self).run_n_times(n) {
|
||||
Ok((arg, k)) if k == n => Ok((Clause::P(
|
||||
$crate::representations::Primitive::Atom(
|
||||
$crate::foreign::Atom::new(
|
||||
<Self as From<(&Self, Clause)>>::from((self, arg))
|
||||
)
|
||||
)
|
||||
), k)),
|
||||
Ok((arg, k)) => {
|
||||
let intermediate = <Self as From<(&Self, Clause)>>::from((self, arg));
|
||||
($next_phase)(&intermediate)
|
||||
.map(|cls| (cls, k))
|
||||
.map_err($crate::representations::interpreted::RuntimeError::Extern)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
fn run_to_completion(&self) -> Result<Clause, $crate::representations::interpreted::RuntimeError> {
|
||||
match <Self as AsRef<Clause>>::as_ref(self).run_to_completion() {
|
||||
Ok(arg) => {
|
||||
let intermediate = <Self as From<(&Self, Clause)>>::from((self, arg));
|
||||
($next_phase)(&intermediate)
|
||||
.map_err($crate::representations::interpreted::RuntimeError::Extern)
|
||||
},
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
48
src/foreign_macros/atomic_inert.rs
Normal file
48
src/foreign_macros/atomic_inert.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
#[allow(unused)] // for the doc comments
|
||||
use crate::foreign::Atomic;
|
||||
#[allow(unused)] // for the doc comments
|
||||
use std::any::Any;
|
||||
#[allow(unused)] // for the doc comments
|
||||
use dyn_clone::DynClone;
|
||||
#[allow(unused)] // for the doc comments
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// Implement [Atomic] for a structure that cannot be transformed any further. This would be optimal
|
||||
/// for atomics encapsulating raw data. [Atomic] depends on [Any], [Debug] and [DynClone].
|
||||
#[macro_export]
|
||||
macro_rules! atomic_inert {
|
||||
($typ:ident) => {
|
||||
impl $crate::foreign::Atomic for $typ {
|
||||
$crate::atomic_defaults!{}
|
||||
fn run_once(&self) -> Result<
|
||||
$crate::representations::interpreted::Clause,
|
||||
$crate::representations::interpreted::InternalError
|
||||
> {
|
||||
Err($crate::representations::interpreted::InternalError::NonReducible)
|
||||
}
|
||||
fn run_n_times(&self, _: usize) -> Result<
|
||||
(
|
||||
$crate::representations::interpreted::Clause,
|
||||
usize
|
||||
),
|
||||
$crate::representations::interpreted::RuntimeError
|
||||
> {
|
||||
Ok(($crate::representations::interpreted::Clause::P(
|
||||
$crate::representations::Primitive::Atom(
|
||||
$crate::foreign::Atom::new(self.clone())
|
||||
)
|
||||
), 0))
|
||||
}
|
||||
fn run_to_completion(&self) -> Result<
|
||||
$crate::representations::interpreted::Clause,
|
||||
$crate::representations::interpreted::RuntimeError
|
||||
> {
|
||||
Ok($crate::representations::interpreted::Clause::P(
|
||||
$crate::representations::Primitive::Atom(
|
||||
$crate::foreign::Atom::new(self.clone())
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
28
src/foreign_macros/atomic_redirect.rs
Normal file
28
src/foreign_macros/atomic_redirect.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
#[allow(unused)]
|
||||
use super::atomic_impl;
|
||||
|
||||
/// Implement the traits required by [atomic_impl] to redirect run_* functions to a field
|
||||
/// with a particular name.
|
||||
#[macro_export]
|
||||
macro_rules! atomic_redirect {
|
||||
($typ:ident) => {
|
||||
impl AsRef<Clause> for $typ {
|
||||
fn as_ref(&self) -> &Clause { &self.0 }
|
||||
}
|
||||
impl From<(&Self, Clause)> for $typ {
|
||||
fn from((old, clause): (&Self, Clause)) -> Self {
|
||||
Self{ 0: clause, ..old.clone() }
|
||||
}
|
||||
}
|
||||
};
|
||||
($typ:ident, $field:ident) => {
|
||||
impl AsRef<Clause> for $typ {
|
||||
fn as_ref(&self) -> &Clause { &self.$field }
|
||||
}
|
||||
impl From<(&Self, Clause)> for $typ {
|
||||
fn from((old, $field): (&Self, Clause)) -> Self {
|
||||
Self{ $field, ..old.clone() }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
43
src/foreign_macros/externfn_impl.rs
Normal file
43
src/foreign_macros/externfn_impl.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
#[allow(unused)] // for the doc comments
|
||||
use crate::{atomic_impl, atomic_redirect};
|
||||
#[allow(unused)] // for the doc comments
|
||||
use crate::representations::Primitive;
|
||||
#[allow(unused)] // for the doc comments
|
||||
use crate::foreign::{Atomic, ExternFn};
|
||||
#[allow(unused)] // for the doc comments
|
||||
use std::any::Any;
|
||||
#[allow(unused)] // for the doc comments
|
||||
use std::hash::Hash;
|
||||
#[allow(unused)] // for the doc comments
|
||||
use dyn_clone::DynClone;
|
||||
#[allow(unused)] // for the doc comments
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// Implement [ExternFn] with a closure that produces an [Atomic] from a reference to self
|
||||
/// and a closure. This can be used in conjunction with [atomic_impl] and [atomic_redirect]
|
||||
/// to normalize the argument automatically before using it.
|
||||
#[macro_export]
|
||||
macro_rules! externfn_impl {
|
||||
($typ:ident, $next_atomic:expr) => {
|
||||
impl $crate::foreign::ExternFn for $typ {
|
||||
fn name(&self) -> &str {stringify!($typ)}
|
||||
fn apply(&self,
|
||||
c: $crate::representations::interpreted::Clause
|
||||
) -> Result<
|
||||
$crate::representations::interpreted::Clause,
|
||||
std::rc::Rc<dyn $crate::foreign::ExternError>
|
||||
> {
|
||||
match ($next_atomic)(self, c) { // ? casts the result but we want to strictly forward it
|
||||
Ok(r) => Ok(
|
||||
$crate::representations::interpreted::Clause::P(
|
||||
$crate::representations::Primitive::Atom(
|
||||
$crate::foreign::Atom::new(r)
|
||||
)
|
||||
)
|
||||
),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
5
src/foreign_macros/mod.rs
Normal file
5
src/foreign_macros/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod atomic_defaults;
|
||||
mod atomic_impl;
|
||||
mod atomic_inert;
|
||||
mod atomic_redirect;
|
||||
mod externfn_impl;
|
||||
Reference in New Issue
Block a user