in midst of refactor

This commit is contained in:
2024-04-29 21:46:42 +02:00
parent ed0d64d52e
commit aa3f7e99ab
221 changed files with 5431 additions and 685 deletions

View File

@@ -0,0 +1,97 @@
//! Automated wrappers to make working with CPS commands easier.
use std::fmt;
use trait_set::trait_set;
use super::atom::{Atomic, AtomicResult, AtomicReturn, CallData, NotAFunction, RunData};
use super::error::{RTError, RTResult};
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 + fmt::Debug + Send + 'static;
/// A function to handle a CPS box with a specific payload
pub trait CPSHandler<T: CPSPayload> = FnMut(&T, &Expr) -> RTResult<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) -> RTResult<()> {
match self.argc {
0 => Err(NotAFunction(self.clone().atom_expr(err_loc.clone())).pack()),
_ => 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 type_name(&self) -> &'static str { std::any::type_name::<Self>() }
fn parser_eq(&self, _: &dyn Atomic) -> 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) -> RTResult<Clause> {
self.assert_applicable(&call.location)?;
self.argc -= 1;
self.continuations.push(call.arg);
Ok(self.atom_cls())
}
fn apply_mut(&mut self, call: CallData) -> RTResult<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())
}
}

View File

@@ -0,0 +1,212 @@
//! Insert Rust functions in Orchid code almost seamlessly
use std::any::{Any, TypeId};
use std::fmt;
use std::marker::PhantomData;
use intern_all::{i, Tok};
use super::atom::{Atomic, AtomicResult, AtomicReturn, CallData, RunData};
use super::error::RTResult;
use super::to_clause::ToClause;
use super::try_from_expr::TryFromExpr;
use crate::interpreter::nort::{Clause, Expr};
use crate::utils::ddispatch::Responder;
/// Return a unary lambda wrapped in this struct to take an additional argument
/// in a function passed to Orchid through [super::fn_bridge::xfn].
///
/// 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.
///
/// If the function takes an instance of [Thunk], it will contain the expression
/// the function was applied to without any specific normalization. If it takes
/// any other type, the argument will be fully normalized and cast using the
/// type's [TryFromExpr] impl.
pub struct Param<T, U, F> {
data: F,
name: Tok<String>,
_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(name: Tok<String>, f: F) -> Self
where F: FnOnce(T) -> U {
Self { name, 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 { name: self.name.clone(), data: self.data.clone(), _t: PhantomData, _u: PhantomData }
}
}
impl<T, U, F> fmt::Display for Param<T, U, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.name) }
}
impl<T, U, F> fmt::Debug for Param<T, U, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Param").field(&*self.name).finish()
}
}
/// A marker struct that gets assigned an expression without normalizing it.
/// This behaviour cannot be replicated in usercode, it's implemented with an
/// explicit runtime [TypeId] check invoked by [Param].
#[derive(Debug, Clone)]
pub struct Thunk(pub Expr);
impl TryFromExpr for Thunk {
fn from_expr(expr: Expr) -> RTResult<Self> { Ok(Thunk(expr)) }
}
struct FnMiddleStage<T, U, F> {
arg: Expr,
f: Param<T, U, F>,
}
impl<T, U, F: Clone> Clone for FnMiddleStage<T, U, F> {
fn clone(&self) -> Self { Self { arg: self.arg.clone(), f: self.f.clone() } }
}
impl<T, U, F> fmt::Debug for FnMiddleStage<T, U, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "FnMiddleStage({} {})", self.f, self.arg)
}
}
impl<T, U, F> Responder for FnMiddleStage<T, U, F> {}
impl<
T: 'static + TryFromExpr,
U: 'static + ToClause,
F: 'static + Clone + FnOnce(T) -> U + Any + Send,
> 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 type_name(&self) -> &'static str { std::any::type_name::<Self>() }
fn redirect(&mut self) -> Option<&mut Expr> {
// this should be ctfe'd
(TypeId::of::<T>() != TypeId::of::<Thunk>()).then_some(&mut self.arg)
}
fn run(self: Box<Self>, r: RunData) -> AtomicResult {
let Self { arg, f: Param { data: f, .. } } = *self;
Ok(AtomicReturn::Change(0, f(arg.downcast()?).to_clause(r.location)))
}
fn apply_mut(&mut self, _: CallData) -> RTResult<Clause> { panic!("Atom should have decayed") }
}
impl<T, U, F> Responder for Param<T, U, F> {}
impl<
T: 'static + TryFromExpr + Clone,
U: 'static + ToClause,
F: 'static + Clone + Send + FnOnce(T) -> U,
> Atomic for Param<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 type_name(&self) -> &'static str { std::any::type_name::<Self>() }
fn redirect(&mut self) -> Option<&mut Expr> { None }
fn run(self: Box<Self>, _: RunData) -> AtomicResult { AtomicReturn::inert(*self) }
fn apply_mut(&mut self, call: CallData) -> RTResult<Clause> {
Ok(FnMiddleStage { arg: call.arg, f: self.clone() }.atom_cls())
}
fn apply(self: Box<Self>, call: CallData) -> RTResult<Clause> {
Ok(FnMiddleStage { arg: call.arg, f: *self }.atom_cls())
}
}
/// Convert a Rust function to Orchid. If you can, register your Rust functions
/// statically with functions in [crate::gen::tree].
pub fn xfn<const N: usize, Argv, Ret>(
name: &str,
x: impl Xfn<N, Argv, Ret>,
) -> impl Atomic + Clone {
x.to_atomic(i(name))
}
/// Trait for functions that can be directly passed to Orchid. Constraints in a
/// nutshell:
///
/// - the function must live as long as ['static]
/// - All arguments must implement [TryFromExpr]
/// - all but the last argument must implement [Clone] and [Send]
/// - the return type must implement [ToClause]
///
/// Take [Thunk] to consume the argument as-is, without normalization.
pub trait Xfn<const N: usize, Argv, Ret>: Clone + Send + 'static {
/// Convert Rust type to Orchid function, given a name for logging
fn to_atomic(self, name: Tok<String>) -> impl Atomic + Clone;
}
/// Conversion functions from [Fn] traits into [Atomic]. Since Rust's type
/// system allows overloaded [Fn] implementations, we must specify the arity and
/// argument types for this process. Arities are only defined up to 9, but the
/// function can always return another call to `xfn_`N`ary` to consume more
/// arguments.
pub mod xfn_impls {
use intern_all::{i, Tok};
use super::super::atom::Atomic;
use super::super::try_from_expr::TryFromExpr;
#[allow(unused)] // for doc
use super::Thunk;
use super::{Param, ToClause, Xfn};
macro_rules! xfn_variant {
(
$number:expr,
($($t:ident)*)
($($alt:expr)*)
) => {
paste::paste!{
impl<
$( $t : TryFromExpr + Clone + Send + 'static, )*
TLast: TryFromExpr + Clone + 'static,
TReturn: ToClause + Send + 'static,
TFunction: FnOnce( $( $t , )* TLast )
-> TReturn + Clone + Send + 'static
> Xfn<$number, ($($t,)* TLast,), TReturn> for TFunction {
fn to_atomic(self, name: Tok<String>) -> impl Atomic + Clone {
#[allow(unused_variables)]
let argc = 0;
let stage_n = name.clone();
xfn_variant!(@BODY_LOOP self name stage_n argc
( $( ( $t [< $t:lower >] ) )* )
( $( [< $t:lower >] )* )
)
}
}
}
};
(@BODY_LOOP $function:ident $name:ident $stage_n:ident $argc:ident (
( $Next:ident $next:ident )
$( ( $T:ident $t:ident ) )*
) $full:tt) => {{
Param::new($stage_n, move |$next : $Next| {
let $argc = $argc + 1;
let $stage_n = i(&format!("{}/{}", $name, $argc));
xfn_variant!(@BODY_LOOP $function $name $stage_n $argc ( $( ( $T $t ) )* ) $full)
})
}};
(@BODY_LOOP $function:ident $name:ident $stage_n:ident $argc:ident (
) ( $( $t:ident )* )) => {{
Param::new($stage_n, |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
}

View File

@@ -0,0 +1,129 @@
//! An [Atomic] that wraps inert data.
use std::any::Any;
use std::fmt;
use std::ops::{Deref, DerefMut};
use ordered_float::NotNan;
use super::atom::{Atom, Atomic, AtomicResult, AtomicReturn, CallData, NotAFunction, RunData};
use super::error::{RTError, RTResult};
use super::try_from_expr::TryFromExpr;
use crate::foreign::error::AssertionError;
use crate::interpreter::nort::{Clause, Expr};
use crate::libs::std::number::Numeric;
use crate::libs::std::string::OrcString;
use crate::utils::ddispatch::{Request, Responder};
/// 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 Rust functions it also automatically implements [TryFromExpr]
/// so that `Inert<MyType>` arguments can be parsed directly.
pub trait InertPayload: fmt::Debug + Clone + Send + 'static {
/// Typename to be shown in the error when a conversion from [Expr] fails
///
/// This will default to `type_name::<Self>()` when it becomes stable
const TYPE_STR: &'static str;
/// Proxies to [Responder] so that you don't have to implmeent it manually if
/// 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 }
/// ```
#[allow(unused_variables)]
fn strict_eq(&self, other: &Self) -> bool { false }
}
/// An atom that stores a value and rejects all interpreter interactions. It is
/// used to reference foreign data in Orchid.
#[derive(Debug, Clone)]
pub struct Inert<T: InertPayload>(pub T);
impl<T: InertPayload> Inert<T> {
/// Wrap the argument in a type-erased [Atom] for embedding in Orchid
/// structures.
pub fn atom(t: T) -> Atom { Atom::new(Inert(t)) }
}
impl<T: InertPayload> Deref for Inert<T> {
type Target = T;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl<T: InertPayload> DerefMut for Inert<T> {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}
impl<T: InertPayload> Responder for Inert<T> {
fn respond(&self, mut request: Request) {
if request.can_serve::<T>() { request.serve(self.0.clone()) } else { self.0.respond(request) }
}
}
impl<T: InertPayload> Atomic for Inert<T> {
fn as_any(self: Box<Self>) -> Box<dyn Any> { self }
fn as_any_ref(&self) -> &dyn Any { self }
fn type_name(&self) -> &'static str { std::any::type_name::<Self>() }
fn redirect(&mut self) -> Option<&mut Expr> { None }
fn run(self: Box<Self>, _: RunData) -> AtomicResult { AtomicReturn::inert(*self) }
fn apply_mut(&mut self, call: CallData) -> RTResult<Clause> {
Err(NotAFunction(self.clone().atom_expr(call.location)).pack())
}
fn parser_eq(&self, other: &dyn Atomic) -> bool {
other.as_any_ref().downcast_ref::<Self>().map_or(false, |other| self.0.strict_eq(&other.0))
}
}
impl<T: InertPayload> TryFromExpr for Inert<T> {
fn from_expr(expr: Expr) -> RTResult<Self> {
let Expr { clause, location } = expr;
match clause.try_unwrap() {
Ok(Clause::Atom(at)) => at
.try_downcast::<Self>()
.map_err(|a| AssertionError::ext(location, T::TYPE_STR, format!("{a:?}"))),
Err(inst) => match &*inst.cls_mut() {
Clause::Atom(at) => at
.downcast_ref::<Self>()
.cloned()
.ok_or_else(|| AssertionError::ext(location, T::TYPE_STR, format!("{inst}"))),
cls => AssertionError::fail(location, "atom", format!("{cls}")),
},
Ok(cls) => AssertionError::fail(location, "atom", format!("{cls}")),
}
}
}
impl<T: InertPayload + fmt::Display> fmt::Display for Inert<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) }
}
impl InertPayload for bool {
const TYPE_STR: &'static str = "bool";
fn strict_eq(&self, other: &Self) -> bool { self == other }
fn respond(&self, mut request: Request) {
request.serve_with(|| OrcString::from(self.to_string()))
}
}
impl InertPayload for usize {
const 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));
request.serve_with(|| OrcString::from(self.to_string()))
}
}
impl InertPayload for NotNan<f64> {
const 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));
request.serve_with(|| OrcString::from(self.to_string()))
}
}

View File

@@ -0,0 +1,12 @@
//! Interaction with foreign code
//!
//! Structures and traits used in the exposure of external functions and values
//! to Orchid code
pub mod atom;
pub mod cps_box;
pub mod error;
pub mod fn_bridge;
pub mod inert;
pub mod process;
pub mod to_clause;
pub mod try_from_expr;

View File

@@ -0,0 +1,40 @@
//! An [Atomic] implementor that runs a callback and turns into the return
//! value. Useful to generate expressions that depend on values in the
//! interpreter's context, and to defer the generation of expensive
//! subexpressions
use std::fmt;
use super::atom::{Atomic, AtomicResult, AtomicReturn, CallData, RunData};
use super::error::RTResult;
use super::to_clause::ToClause;
use crate::interpreter::nort::{Clause, Expr};
use crate::utils::ddispatch::Responder;
/// An atom that immediately decays to the result of the function when
/// normalized. Can be used to build infinite recursive datastructures from
/// Rust.
#[derive(Clone)]
pub struct Unstable<F>(F);
impl<F: FnOnce(RunData) -> R + Send + 'static, R: ToClause> Unstable<F> {
/// Wrap a function in an Unstable
pub const fn new(f: F) -> Self { Self(f) }
}
impl<F> Responder for Unstable<F> {}
impl<F> fmt::Debug for Unstable<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Unstable").finish_non_exhaustive()
}
}
impl<F: FnOnce(RunData) -> R + Send + 'static, R: ToClause> Atomic for Unstable<F> {
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
fn as_any_ref(&self) -> &dyn std::any::Any { self }
fn type_name(&self) -> &'static str { std::any::type_name::<Self>() }
fn apply_mut(&mut self, _: CallData) -> RTResult<Clause> { panic!("This atom decays instantly") }
fn run(self: Box<Self>, run: RunData) -> AtomicResult {
let loc = run.location.clone();
let clause = self.0(run).to_clause(loc);
Ok(AtomicReturn::Change(0, clause))
}
fn redirect(&mut self) -> Option<&mut Expr> { None }
}

View File

@@ -0,0 +1,202 @@
//! Conversions from Rust values to Orchid expressions. Many APIs and
//! [super::fn_bridge] in particular use this to automatically convert values on
//! the boundary. The opposite conversion is [super::try_from_expr::TryFromExpr]
use super::atom::{Atomic, RunData};
use super::process::Unstable;
use crate::gen::tpl;
use crate::gen::traits::Gen;
use crate::interpreter::gen_nort::nort_gen;
use crate::interpreter::nort::{Clause, ClauseInst, Expr};
use crate::location::CodeLocation;
use crate::utils::clonable_iter::Clonable;
/// A trait for things that are infallibly convertible to [ClauseInst]. These
/// types can be returned by callbacks passed to [super::fn_bridge::xfn].
pub trait ToClause: Sized {
/// Convert this value to a [Clause]. If your value can only be directly
/// converted to a [ClauseInst], you can call `ClauseInst::to_clause` to
/// unwrap it if possible or fall back to [Clause::Identity].
fn to_clause(self, location: CodeLocation) -> Clause;
/// Convert the type to a [Clause].
fn to_clsi(self, location: CodeLocation) -> ClauseInst {
ClauseInst::new(self.to_clause(location))
}
/// Convert to an expression via [ToClause].
fn to_expr(self, location: CodeLocation) -> Expr {
Expr { clause: self.to_clsi(location.clone()), location }
}
}
impl<T: Atomic + Clone> ToClause for T {
fn to_clause(self, _: CodeLocation) -> Clause { self.atom_cls() }
}
impl ToClause for Clause {
fn to_clause(self, _: CodeLocation) -> Clause { self }
}
impl ToClause for ClauseInst {
fn to_clause(self, _: CodeLocation) -> Clause { self.into_cls() }
fn to_clsi(self, _: CodeLocation) -> ClauseInst { self }
}
impl ToClause for Expr {
fn to_clause(self, location: CodeLocation) -> Clause { self.clause.to_clause(location) }
fn to_clsi(self, _: CodeLocation) -> ClauseInst { self.clause }
fn to_expr(self, _: CodeLocation) -> Expr { self }
}
struct ListGen<I>(Clonable<I>)
where
I: Iterator + Send,
I::Item: ToClause + Send;
impl<I> Clone for ListGen<I>
where
I: Iterator + Send,
I::Item: ToClause + Send,
{
fn clone(&self) -> Self { Self(self.0.clone()) }
}
impl<I> ToClause for ListGen<I>
where
I: Iterator + Send + 'static,
I::Item: ToClause + Clone + Send,
{
fn to_clause(mut self, location: CodeLocation) -> Clause {
let ctx = nort_gen(location.clone());
match self.0.next() {
None => tpl::C("std::list::end").template(ctx, []),
Some(val) => {
let atom = Unstable::new(|run| self.to_clause(run.location));
tpl::a2(tpl::C("std::list::cons"), tpl::Slot, tpl::V(atom))
.template(ctx, [val.to_clause(location)])
},
}
}
}
/// Convert an iterator into a lazy-evaluated Orchid list.
pub fn list<I>(items: I) -> impl ToClause
where
I: IntoIterator + Clone + Send + Sync + 'static,
I::IntoIter: Send,
I::Item: ToClause + Clone + Send,
{
Unstable::new(move |RunData { location, .. }| {
ListGen(Clonable::new(items.clone().into_iter().map(move |t| t.to_clsi(location.clone()))))
})
}
mod implementations {
use std::any::Any;
use std::fmt;
use std::sync::Arc;
use super::{list, ToClause};
use crate::foreign::atom::{Atom, Atomic, AtomicResult, CallData, RunData};
use crate::foreign::error::{AssertionError, RTErrorObj, RTResult};
use crate::foreign::inert::Inert;
use crate::foreign::try_from_expr::TryFromExpr;
use crate::gen::tpl;
use crate::gen::traits::Gen;
use crate::interpreter::gen_nort::nort_gen;
use crate::interpreter::nort::{Clause, Expr};
use crate::libs::std::tuple::Tuple;
use crate::location::CodeLocation;
use crate::utils::ddispatch::Responder;
impl<T: ToClause> ToClause for Option<T> {
fn to_clause(self, location: CodeLocation) -> Clause {
let ctx = nort_gen(location.clone());
match self {
None => tpl::C("std::option::none").template(ctx, []),
Some(t) =>
tpl::A(tpl::C("std::option::some"), tpl::Slot).template(ctx, [t.to_clause(location)]),
}
}
}
impl<T: ToClause, U: ToClause> ToClause for Result<T, U> {
fn to_clause(self, location: CodeLocation) -> Clause {
let ctx = nort_gen(location.clone());
match self {
Ok(t) =>
tpl::A(tpl::C("std::result::ok"), tpl::Slot).template(ctx, [t.to_clause(location)]),
Err(e) =>
tpl::A(tpl::C("std::result::err"), tpl::Slot).template(ctx, [e.to_clause(location)]),
}
}
}
struct PendingError(RTErrorObj);
impl Responder for PendingError {}
impl fmt::Debug for PendingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PendingError({})", self.0)
}
}
impl Atomic for PendingError {
fn as_any(self: Box<Self>) -> Box<dyn Any> { self }
fn as_any_ref(&self) -> &dyn Any { self }
fn type_name(&self) -> &'static str { std::any::type_name::<Self>() }
fn redirect(&mut self) -> Option<&mut Expr> { None }
fn run(self: Box<Self>, _: RunData) -> AtomicResult { Err(self.0) }
fn apply_mut(&mut self, _: CallData) -> RTResult<Clause> {
panic!("This atom decays instantly")
}
}
impl<T: ToClause> ToClause for RTResult<T> {
fn to_clause(self, location: CodeLocation) -> Clause {
match self {
Err(e) => PendingError(e).atom_cls(),
Ok(t) => t.to_clause(location),
}
}
}
impl<T: ToClause + Clone + Send + Sync + 'static> ToClause for Vec<T> {
fn to_clause(self, location: CodeLocation) -> Clause { list(self).to_clause(location) }
}
impl ToClause for Atom {
fn to_clause(self, _: CodeLocation) -> Clause { Clause::Atom(self) }
}
macro_rules! gen_tuple_impl {
( ($($T:ident)*) ($($t:ident)*)) => {
impl<$($T: ToClause),*> ToClause for ($($T,)*) {
fn to_clause(self, location: CodeLocation) -> Clause {
let ($($t,)*) = self;
Inert(Tuple(Arc::new(vec![
$($t.to_expr(location.clone()),)*
]))).atom_cls()
}
}
impl<$($T: TryFromExpr),*> TryFromExpr for ($($T,)*) {
fn from_expr(ex: Expr) -> RTResult<Self> {
let Inert(Tuple(slice)) = ex.clone().downcast()?;
match &slice[..] {
[$($t),*] => Ok(($($t.clone().downcast()?,)*)),
_ => AssertionError::fail(ex.location(), "Tuple length mismatch", format!("{ex}"))
}
}
}
};
}
gen_tuple_impl!((A)(a));
gen_tuple_impl!((A B) (a b));
gen_tuple_impl!((A B C) (a b c));
gen_tuple_impl!((A B C D) (a b c d));
gen_tuple_impl!((A B C D E) (a b c d e));
gen_tuple_impl!((A B C D E F) (a b c d e f));
gen_tuple_impl!((A B C D E F G) (a b c d e f g));
gen_tuple_impl!((A B C D E F G H) (a b c d e f g h));
gen_tuple_impl!((A B C D E F G H I) (a b c d e f g h i));
gen_tuple_impl!((A B C D E F G H I J) (a b c d e f g h i j));
gen_tuple_impl!((A B C D E F G H I J K) (a b c d e f g h i j k));
gen_tuple_impl!((A B C D E F G H I J K L) (a b c d e f g h i j k l));
}