forked from Orchid/orchid
September-october commit
- manual parser - stl refinements - all language constructs are now Send
This commit is contained in:
@@ -1,13 +1,16 @@
|
||||
use std::any::Any;
|
||||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
|
||||
use dyn_clone::DynClone;
|
||||
|
||||
use crate::interpreted::ExprInst;
|
||||
use super::ExternError;
|
||||
use crate::ddispatch::request;
|
||||
use crate::error::AssertionError;
|
||||
use crate::interpreted::{ExprInst, TryFromExprInst};
|
||||
use crate::interpreter::{Context, RuntimeError};
|
||||
use crate::representations::interpreted::Clause;
|
||||
use crate::utils::ddispatch::Responder;
|
||||
use crate::Primitive;
|
||||
|
||||
/// Information returned by [Atomic::run]. This mirrors
|
||||
/// [crate::interpreter::Return] but with a clause instead of an Expr.
|
||||
@@ -24,8 +27,18 @@ pub struct AtomicReturn {
|
||||
/// Returned by [Atomic::run]
|
||||
pub type AtomicResult = Result<AtomicReturn, RuntimeError>;
|
||||
|
||||
/// Trait for things that are _definitely_ equal.
|
||||
pub trait StrictEq {
|
||||
/// must return true if the objects were produced via the exact same sequence
|
||||
/// of transformations, including any relevant context data. Must return false
|
||||
/// if the objects are of different type, or if their type is [PartialEq]
|
||||
/// and [PartialEq::eq] returns false.
|
||||
fn strict_eq(&self, other: &dyn Any) -> bool;
|
||||
}
|
||||
|
||||
/// Functionality the interpreter needs to handle a value
|
||||
pub trait Atomic: Any + Debug + DynClone + Responder
|
||||
pub trait Atomic:
|
||||
Any + Debug + DynClone + StrictEq + Responder + Send
|
||||
where
|
||||
Self: 'static,
|
||||
{
|
||||
@@ -54,7 +67,7 @@ where
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Clause::P(Primitive::Atom(Atom(Box::new(self))))
|
||||
Clause::Atom(Atom(Box::new(self)))
|
||||
}
|
||||
|
||||
/// Wrap the atom in a new expression instance to be placed in a tree
|
||||
@@ -73,7 +86,7 @@ where
|
||||
/// inert at which point the [Atom] will validate and process the argument,
|
||||
/// returning a different [Atom] intended for processing by external code, a new
|
||||
/// [ExternFn] to capture an additional argument, or an Orchid expression
|
||||
/// to pass control back to the interpreter.btop
|
||||
/// to pass control back to the interpreter.
|
||||
pub struct Atom(pub Box<dyn Atomic>);
|
||||
impl Atom {
|
||||
/// Wrap an [Atomic] in a type-erased box
|
||||
@@ -84,23 +97,26 @@ impl Atom {
|
||||
/// Get the contained data
|
||||
#[must_use]
|
||||
pub fn data(&self) -> &dyn Atomic { self.0.as_ref() as &dyn Atomic }
|
||||
/// Attempt to downcast contained data to a specific type
|
||||
pub fn try_cast<T: Atomic>(self) -> Result<T, Self> {
|
||||
/// Test the type of the contained data without downcasting
|
||||
#[must_use]
|
||||
pub fn is<T: Atomic>(&self) -> bool { self.data().as_any_ref().is::<T>() }
|
||||
/// Downcast contained data, panic if it isn't the specified type
|
||||
#[must_use]
|
||||
pub fn downcast<T: Atomic>(self) -> T {
|
||||
*self.0.as_any().downcast().expect("Type mismatch on Atom::cast")
|
||||
}
|
||||
/// Normalize the contained data
|
||||
pub fn run(self, ctx: Context) -> AtomicResult { self.0.run(ctx) }
|
||||
/// Request a delegate from the encapsulated data
|
||||
pub fn request<T: 'static>(&self) -> Option<T> { request(self.0.as_ref()) }
|
||||
/// Downcast the atom to a concrete atomic type, or return the original atom
|
||||
/// if it is not the specified type
|
||||
pub fn try_downcast<T: Atomic>(self) -> Result<T, Self> {
|
||||
match self.0.as_any_ref().is::<T>() {
|
||||
true => Ok(*self.0.as_any().downcast().expect("checked just above")),
|
||||
false => Err(self),
|
||||
}
|
||||
}
|
||||
/// Test the type of the contained data without downcasting
|
||||
#[must_use]
|
||||
pub fn is<T: 'static>(&self) -> bool { self.data().as_any_ref().is::<T>() }
|
||||
/// Downcast contained data, panic if it isn't the specified type
|
||||
#[must_use]
|
||||
pub fn cast<T: 'static>(self) -> T {
|
||||
*self.0.as_any().downcast().expect("Type mismatch on Atom::cast")
|
||||
}
|
||||
/// Normalize the contained data
|
||||
pub fn run(self, ctx: Context) -> AtomicResult { self.0.run(ctx) }
|
||||
}
|
||||
|
||||
impl Clone for Atom {
|
||||
@@ -109,6 +125,16 @@ impl Clone for Atom {
|
||||
|
||||
impl Debug for Atom {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "##ATOM[{:?}]##", self.data())
|
||||
write!(f, "{:?}", self.data())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromExprInst for Atom {
|
||||
fn from_exi(exi: ExprInst) -> Result<Self, Rc<dyn ExternError>> {
|
||||
let loc = exi.location();
|
||||
match exi.expr_val().clause {
|
||||
Clause::Atom(a) => Ok(a),
|
||||
_ => AssertionError::fail(loc, "atom"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ use trait_set::trait_set;
|
||||
use super::{Atomic, ExternFn, InertAtomic, XfnResult};
|
||||
use crate::interpreted::{Clause, ExprInst};
|
||||
use crate::interpreter::{Context, HandlerRes};
|
||||
use crate::utils::pure_push::pushed_ref;
|
||||
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 + 'static;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -3,13 +3,12 @@ use std::fmt::{Debug, Display};
|
||||
use std::hash::Hash;
|
||||
use std::rc::Rc;
|
||||
|
||||
use dyn_clone::DynClone;
|
||||
use dyn_clone::{DynClone, clone_box};
|
||||
|
||||
use super::XfnResult;
|
||||
use crate::interpreted::ExprInst;
|
||||
use crate::interpreter::Context;
|
||||
use crate::representations::interpreted::Clause;
|
||||
use crate::Primitive;
|
||||
|
||||
/// Errors produced by external code
|
||||
pub trait ExternError: Display {
|
||||
@@ -34,7 +33,7 @@ impl Error for dyn ExternError {}
|
||||
/// Represents an externally defined function from the perspective of
|
||||
/// the executor. Since Orchid lacks basic numerical operations,
|
||||
/// these are also external functions.
|
||||
pub trait ExternFn: DynClone {
|
||||
pub trait ExternFn: DynClone + Send {
|
||||
/// Display name of the function
|
||||
#[must_use]
|
||||
fn name(&self) -> &str;
|
||||
@@ -50,7 +49,7 @@ pub trait ExternFn: DynClone {
|
||||
where
|
||||
Self: Sized + 'static,
|
||||
{
|
||||
Clause::P(Primitive::ExternFn(Box::new(self)))
|
||||
Clause::ExternFn(ExFn(Box::new(self)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,3 +67,21 @@ impl Debug for dyn ExternFn {
|
||||
write!(f, "##EXTERN[{}]##", self.name())
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a black box function that can be applied to a [Clause] to produce
|
||||
/// a new [Clause], typically an [Atom] representing external work, a new [ExFn]
|
||||
/// to take additional arguments, or an Orchid tree to return control to the
|
||||
/// interpreter
|
||||
#[derive(Debug)]
|
||||
pub struct ExFn(pub Box<dyn ExternFn + 'static>);
|
||||
impl ExFn {
|
||||
/// Combine the function with an argument to produce a new clause
|
||||
pub fn apply(self, arg: ExprInst, ctx: Context) -> XfnResult<Clause> {
|
||||
self.0.apply(arg, ctx)
|
||||
}
|
||||
}
|
||||
impl Clone for ExFn {
|
||||
fn clone(&self) -> Self {
|
||||
Self(clone_box(self.0.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::fmt::Debug;
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::atom::StrictEq;
|
||||
use super::{
|
||||
Atomic, AtomicResult, AtomicReturn, ExternError, ExternFn, XfnResult,
|
||||
};
|
||||
@@ -9,7 +10,7 @@ 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};
|
||||
use crate::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
|
||||
@@ -17,6 +18,8 @@ use crate::{Literal, OrcString};
|
||||
pub trait ToClause: Clone {
|
||||
/// Convert the type to a [Clause].
|
||||
fn to_clause(self) -> Clause;
|
||||
/// Convert to an expression instance via [ToClause].
|
||||
fn to_exi(self) -> ExprInst { self.to_clause().wrap() }
|
||||
}
|
||||
|
||||
impl<T: Atomic + Clone> ToClause for T {
|
||||
@@ -28,14 +31,8 @@ impl ToClause for Clause {
|
||||
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() }
|
||||
fn to_clause(self) -> Clause { OrcString::from(self).atom_cls() }
|
||||
}
|
||||
impl<T: ToClause> ToClause for Option<T> {
|
||||
fn to_clause(self) -> Clause { opt(self.map(|t| t.to_clause().wrap())) }
|
||||
@@ -59,31 +56,28 @@ pub struct Param<T, U, F> {
|
||||
_t: PhantomData<T>,
|
||||
_u: PhantomData<U>,
|
||||
}
|
||||
unsafe impl<T, U, F: Send> Send for Param<T, U, F> {}
|
||||
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() }
|
||||
Self { data: f, _t: PhantomData, _u: PhantomData }
|
||||
}
|
||||
/// 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(),
|
||||
}
|
||||
Self { data: self.data.clone(), _t: PhantomData, _u: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
T: 'static + TryFromExprInst,
|
||||
U: 'static + ToClause,
|
||||
F: 'static + Clone + FnOnce(T) -> Result<U, Rc<dyn ExternError>>,
|
||||
F: 'static + Clone + Send + FnOnce(T) -> Result<U, Rc<dyn ExternError>>,
|
||||
> ToClause for Param<T, U, F>
|
||||
{
|
||||
fn to_clause(self) -> Clause { self.xfn_cls() }
|
||||
@@ -93,6 +87,11 @@ struct FnMiddleStage<T, U, F> {
|
||||
argument: ExprInst,
|
||||
f: Param<T, U, F>,
|
||||
}
|
||||
impl<T, U, F> StrictEq for FnMiddleStage<T, U, F> {
|
||||
fn strict_eq(&self, _other: &dyn std::any::Any) -> bool {
|
||||
unimplemented!("This should never be able to appear in a pattern")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U, F: Clone> Clone for FnMiddleStage<T, U, F> {
|
||||
fn clone(&self) -> Self {
|
||||
@@ -110,7 +109,7 @@ 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>>,
|
||||
F: 'static + Clone + FnOnce(T) -> Result<U, Rc<dyn ExternError>> + Send,
|
||||
> Atomic for FnMiddleStage<T, U, F>
|
||||
{
|
||||
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
|
||||
@@ -128,7 +127,7 @@ impl<
|
||||
impl<
|
||||
T: 'static + TryFromExprInst,
|
||||
U: 'static + ToClause,
|
||||
F: 'static + Clone + FnOnce(T) -> Result<U, Rc<dyn ExternError>>,
|
||||
F: 'static + Clone + Send + FnOnce(T) -> Result<U, Rc<dyn ExternError>>,
|
||||
> ExternFn for Param<T, U, F>
|
||||
{
|
||||
fn name(&self) -> &str { "anonymous Rust function" }
|
||||
@@ -155,16 +154,16 @@ pub mod constructors {
|
||||
" 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"
|
||||
"- all but the last argument must implement [Clone] and [Send]\n"
|
||||
"- the return type must implement [ToClause].\n\n"
|
||||
]
|
||||
#[doc = "Other arities: " $( "[xfn_" $alt "ary], " )+ ]
|
||||
pub fn [< xfn_ $number ary >] <
|
||||
$( $t : TryFromExprInst + Clone + 'static, )*
|
||||
$( $t : TryFromExprInst + Clone + Send + 'static, )*
|
||||
TLast: TryFromExprInst + 'static,
|
||||
TReturn: ToClause + 'static,
|
||||
TReturn: ToClause + Send + 'static,
|
||||
TFunction: FnOnce( $( $t , )* TLast )
|
||||
-> Result<TReturn, Rc<dyn ExternError>> + Clone + 'static
|
||||
-> Result<TReturn, Rc<dyn ExternError>> + Clone + Send + 'static
|
||||
>(function: TFunction) -> impl ExternFn {
|
||||
xfn_variant!(@BODY_LOOP function
|
||||
( $( ( $t [< $t:lower >] ) )* )
|
||||
|
||||
@@ -2,22 +2,25 @@ use std::any::Any;
|
||||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
|
||||
use ordered_float::NotNan;
|
||||
|
||||
use super::atom::StrictEq;
|
||||
use super::{AtomicResult, AtomicReturn, ExternError};
|
||||
use crate::error::AssertionError;
|
||||
#[allow(unused)] // for doc
|
||||
// use crate::define_fn;
|
||||
use crate::foreign::Atomic;
|
||||
use crate::interpreted::{Clause, Expr, ExprInst, TryFromExprInst};
|
||||
use crate::interpreter::Context;
|
||||
use crate::systems::AssertionError;
|
||||
use crate::systems::stl::Numeric;
|
||||
use crate::utils::ddispatch::{Request, Responder};
|
||||
use crate::Primitive;
|
||||
|
||||
/// A proxy trait that implements [Atomic] for blobs of data in Rust code that
|
||||
/// cannot be processed and always report inert. Since these are expected to be
|
||||
/// parameters of functions defined with [define_fn] it also automatically
|
||||
/// implements [TryFromExprInst] so that a conversion doesn't have to be
|
||||
/// provided in argument lists.
|
||||
pub trait InertAtomic: Debug + Clone + 'static {
|
||||
pub trait InertAtomic: Debug + Clone + Send + 'static {
|
||||
/// Typename to be shown in the error when a conversion from [ExprInst] fails
|
||||
#[must_use]
|
||||
fn type_str() -> &'static str;
|
||||
@@ -25,9 +28,29 @@ pub trait InertAtomic: Debug + Clone + 'static {
|
||||
/// you need it, but behaves exactly as the default implementation.
|
||||
#[allow(unused_mut, unused_variables)] // definition should show likely usage
|
||||
fn respond(&self, mut request: Request) {}
|
||||
/// Equality comparison used by the pattern matcher. Since the pattern matcher
|
||||
/// only works with parsed code, you only need to implement this if your type
|
||||
/// is directly parseable.
|
||||
///
|
||||
/// If your type implements [PartialEq], this can simply be implemented as
|
||||
/// ```ignore
|
||||
/// fn strict_eq(&self, other: &Self) -> bool { self == other }
|
||||
/// ```
|
||||
fn strict_eq(&self, _: &Self) -> bool { false }
|
||||
}
|
||||
impl<T: InertAtomic> StrictEq for T {
|
||||
fn strict_eq(&self, other: &dyn Any) -> bool {
|
||||
other.downcast_ref().map_or(false, |other| self.strict_eq(other))
|
||||
}
|
||||
}
|
||||
impl<T: InertAtomic> Responder for T {
|
||||
fn respond(&self, request: Request) { self.respond(request) }
|
||||
fn respond(&self, mut request: Request) {
|
||||
if request.can_serve::<T>() {
|
||||
request.serve(self.clone())
|
||||
} else {
|
||||
self.respond(request)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: InertAtomic> Atomic for T {
|
||||
fn as_any(self: Box<Self>) -> Box<dyn Any> { self }
|
||||
@@ -42,7 +65,7 @@ impl<T: InertAtomic> TryFromExprInst for T {
|
||||
fn from_exi(exi: ExprInst) -> Result<Self, Rc<dyn ExternError>> {
|
||||
let Expr { clause, location } = exi.expr_val();
|
||||
match clause {
|
||||
Clause::P(Primitive::Atom(a)) => match a.0.as_any().downcast() {
|
||||
Clause::Atom(a) => match a.0.as_any().downcast() {
|
||||
Ok(t) => Ok(*t),
|
||||
Err(_) => AssertionError::fail(location, Self::type_str()),
|
||||
},
|
||||
@@ -50,3 +73,24 @@ impl<T: InertAtomic> TryFromExprInst for T {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InertAtomic for bool {
|
||||
fn type_str() -> &'static str { "bool" }
|
||||
fn strict_eq(&self, other: &Self) -> bool { self == other }
|
||||
}
|
||||
|
||||
impl InertAtomic for usize {
|
||||
fn type_str() -> &'static str { "usize" }
|
||||
fn strict_eq(&self, other: &Self) -> bool { self == other }
|
||||
fn respond(&self, mut request: Request) {
|
||||
request.serve(Numeric::Uint(*self))
|
||||
}
|
||||
}
|
||||
|
||||
impl InertAtomic for NotNan<f64> {
|
||||
fn type_str() -> &'static str { "NotNan<f64>" }
|
||||
fn strict_eq(&self, other: &Self) -> bool { self == other }
|
||||
fn respond(&self, mut request: Request) {
|
||||
request.serve(Numeric::Float(*self))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ mod inert;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
pub use atom::{Atom, Atomic, AtomicResult, AtomicReturn};
|
||||
pub use extern_fn::{ExternError, ExternFn};
|
||||
pub use atom::{Atom, Atomic, AtomicResult, AtomicReturn, StrictEq};
|
||||
pub use extern_fn::{ExternError, ExternFn, ExFn};
|
||||
pub use fn_bridge::constructors::{
|
||||
xfn_1ary, xfn_2ary, xfn_3ary, xfn_4ary, xfn_5ary, xfn_6ary, xfn_7ary,
|
||||
xfn_8ary, xfn_9ary,
|
||||
|
||||
Reference in New Issue
Block a user