Backup commit

My backspace key started ghosting. Nothing works atm.
This commit is contained in:
2024-01-27 14:50:33 +00:00
parent f77e4fd90a
commit a8887227e5
236 changed files with 10946 additions and 8977 deletions

View File

@@ -1,45 +1,79 @@
use std::any::Any;
use std::fmt::Debug;
use std::fmt::{Debug, Display};
use std::sync::{Arc, Mutex};
use dyn_clone::DynClone;
use never::Never;
use super::XfnResult;
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::{ast, NameLike};
use super::error::{ExternError, ExternResult};
use crate::interpreter::apply::CallData;
use crate::interpreter::context::RunContext;
use crate::interpreter::error::RunError;
use crate::interpreter::nort;
use crate::interpreter::run::RunData;
use crate::location::{CodeLocation, SourceRange};
use crate::name::NameLike;
use crate::parse::parsed;
use crate::utils::ddispatch::{request, Request, Responder};
/// Information returned by [Atomic::run]. This mirrors
/// [crate::interpreter::Return] but with a clause instead of an Expr.
pub struct AtomicReturn {
/// The next form of the expression
pub clause: Clause,
pub clause: nort::Clause,
/// Remaining gas
pub gas: Option<usize>,
/// Whether further normalization is possible by repeated calls to
/// [Atomic::run]
pub inert: bool,
}
impl AtomicReturn {
/// Report indicating that the value is inert
pub fn inert<T: Atomic, E>(this: T, ctx: RunContext) -> Result<Self, E> {
Ok(Self { clause: this.atom_cls(), gas: ctx.gas, inert: true })
}
/// Report indicating that the value has been processed
pub fn run<E>(clause: nort::Clause, run: RunData) -> Result<Self, E> {
Ok(Self { clause, gas: run.ctx.gas, inert: false })
}
}
/// Returned by [Atomic::run]
pub type AtomicResult = Result<AtomicReturn, RuntimeError>;
pub type AtomicResult = Result<AtomicReturn, RunError>;
/// 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;
/// General error produced when a non-function [Atom] is applied to something as
/// a function.
#[derive(Clone)]
pub struct NotAFunction(pub nort::Expr);
impl ExternError for NotAFunction {}
impl Display for NotAFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?} is not a function", self.0)
}
}
/// Functionality the interpreter needs to handle a value
pub trait Atomic: Any + Debug + DynClone + StrictEq + Responder + Send
where
Self: 'static,
///
/// # Lifecycle methods
///
/// Atomics expose the methods [Atomic::redirect], [Atomic::run],
/// [Atomic::apply] and [Atomic::apply_ref] to interact with the interpreter.
/// The interpreter first tries to call `redirect` to find a subexpression to
/// normalize. If it returns `None` or the subexpression is inert, `run` is
/// called. `run` takes ownership of the value and returns a new one.
///
/// If `run` indicated in its return value that the result is inert and the atom
/// is in the position of a function, `apply` or `apply_ref` is called depending
/// upon whether the atom is referenced elsewhere. `apply` falls back to
/// `apply_ref` so implementing it is considered an optimization to avoid
/// excessive copying.
///
/// Atoms don't generally have to be copyable because clauses are refcounted in
/// the interpreter, but Orchid code is always free to duplicate the references
/// and apply them as functions to multiple different arguments so atoms that
/// represent functions have to support application by-ref without consuming the
/// function itself.
pub trait Atomic: Any + Debug + Responder + Send
where Self: 'static
{
/// Casts this value to [Any] so that its original value can be salvaged
/// during introspection by other external code.
@@ -55,45 +89,90 @@ where
#[must_use]
fn as_any_ref(&self) -> &dyn Any;
/// Returns a reference to a possible expression held inside the atom which
/// can be reduced. For an overview of the lifecycle see [Atomic]
fn redirect(&mut self) -> Option<&mut nort::ClauseInst>;
/// Attempt to normalize this value. If it wraps a value, this should report
/// inert. If it wraps a computation, it should execute one logical step of
/// the computation and return a structure representing the ntext.
fn run(self: Box<Self>, ctx: Context) -> AtomicResult;
/// the computation and return a structure representing the next.
///
/// For an overview of the lifecycle see [Atomic]
fn run(self: Box<Self>, run: RunData) -> AtomicResult;
/// Combine the function with an argument to produce a new clause. Falls back
/// to [Atomic::apply_ref] by default.
///
/// For an overview of the lifecycle see [Atomic]
fn apply(self: Box<Self>, call: CallData) -> ExternResult<nort::Clause> {
self.apply_ref(call)
}
/// Combine the function with an argument to produce a new clause
///
/// For an overview of the lifecycle see [Atomic]
fn apply_ref(&self, call: CallData) -> ExternResult<nort::Clause>;
/// Must return true for atoms parsed from identical source.
/// If the atom cannot be parsed from source, it can safely be ignored
#[allow(unused_variables)]
fn parser_eq(&self, other: &dyn Any) -> bool { false }
/// Wrap the atom in a clause to be placed in an [AtomicResult].
#[must_use]
fn atom_cls(self) -> Clause
where
Self: Sized,
{
Clause::Atom(Atom(Box::new(self)))
fn atom_cls(self) -> nort::Clause
where Self: Sized {
nort::Clause::Atom(Atom(Box::new(self)))
}
/// Shorthand for `self.atom_cls().to_inst()`
fn atom_clsi(self) -> nort::ClauseInst
where Self: Sized {
self.atom_cls().to_inst()
}
/// Wrap the atom in a new expression instance to be placed in a tree
#[must_use]
fn atom_exi(self) -> ExprInst
where
Self: Sized,
{
self.atom_cls().wrap()
fn atom_expr(self, location: CodeLocation) -> nort::Expr
where Self: Sized {
self.atom_clsi().to_expr(location)
}
/// Wrap the atom in a clause to be placed in a [sourcefile::FileEntry].
#[must_use]
fn ast_cls<N: NameLike>(self) -> ast::Clause<N>
where
Self: Sized,
{
ast::Clause::Atom(Atom::new(self))
fn ast_cls(self) -> parsed::Clause
where Self: Sized + Clone {
parsed::Clause::Atom(AtomGenerator::cloner(self))
}
/// Wrap the atom in an expression to be placed in a [sourcefile::FileEntry].
#[must_use]
fn ast_exp<N: NameLike>(self) -> ast::Expr<N>
where
Self: Sized,
{
self.ast_cls().into_expr()
fn ast_exp<N: NameLike>(self, range: SourceRange) -> parsed::Expr
where Self: Sized + Clone {
self.ast_cls().into_expr(range)
}
}
/// A struct for generating any number of [Atom]s. Since atoms aren't Clone,
/// this represents the ability to create any number of instances of an atom
#[derive(Clone)]
pub struct AtomGenerator(Arc<dyn Fn() -> Atom + Send + Sync>);
impl AtomGenerator {
/// Use a factory function to create any number of atoms
pub fn new(f: impl Fn() -> Atom + Send + Sync + 'static) -> Self {
Self(Arc::new(f))
}
/// Clone a representative atom when called
pub fn cloner(atom: impl Atomic + Clone) -> Self {
let lock = Mutex::new(atom);
Self::new(move || Atom::new(lock.lock().unwrap().clone()))
}
/// Generate an atom
pub fn run(&self) -> Atom { self.0() }
}
impl Debug for AtomGenerator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AtomGenerator").finish_non_exhaustive()
}
}
@@ -123,7 +202,7 @@ impl Atom {
*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) }
pub fn run(self, run: RunData) -> AtomicResult { self.0.run(run) }
/// 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
@@ -134,10 +213,18 @@ impl Atom {
false => Err(self),
}
}
}
impl Clone for Atom {
fn clone(&self) -> Self { Self(dyn_clone::clone_box(self.data())) }
/// Downcast an atom by reference
pub fn downcast_ref<T: Atomic>(&self) -> Option<&T> {
self.0.as_any_ref().downcast_ref()
}
/// Combine the function with an argument to produce a new clause
pub fn apply(self, call: CallData) -> ExternResult<nort::Clause> {
self.0.apply(call)
}
/// Combine the function with an argument to produce a new clause
pub fn apply_ref(&self, call: CallData) -> ExternResult<nort::Clause> {
self.0.apply_ref(call)
}
}
impl Debug for Atom {
@@ -146,12 +233,15 @@ impl Debug for Atom {
}
}
impl TryFromExprInst for Atom {
fn from_exi(exi: ExprInst) -> XfnResult<Self> {
let loc = exi.location();
match exi.expr_val().clause {
Clause::Atom(a) => Ok(a),
_ => AssertionError::fail(loc, "atom"),
}
impl Responder for Never {
fn respond(&self, _request: Request) { match *self {} }
}
impl Atomic for Never {
fn as_any(self: Box<Self>) -> Box<dyn Any> { match *self {} }
fn as_any_ref(&self) -> &dyn Any { match *self {} }
fn redirect(&mut self) -> Option<&mut nort::ClauseInst> { match *self {} }
fn run(self: Box<Self>, _: RunData) -> AtomicResult { match *self {} }
fn apply_ref(&self, _: CallData) -> ExternResult<nort::Clause> {
match *self {}
}
}

View File

@@ -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())
}
}

68
src/foreign/error.rs Normal file
View File

@@ -0,0 +1,68 @@
use std::error::Error;
use std::fmt::{Debug, Display};
use std::sync::Arc;
use dyn_clone::DynClone;
use crate::location::CodeLocation;
/// Errors produced by external code
pub trait ExternError: Display + Send + Sync + DynClone {
/// Convert into trait object
#[must_use]
fn rc(self) -> Arc<dyn ExternError>
where Self: 'static + Sized {
Arc::new(self)
}
}
impl Debug for dyn ExternError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ExternError({self})")
}
}
impl Error for dyn ExternError {}
/// An error produced by Rust code called form Orchid. The error is type-erased.
pub type ExternResult<T> = Result<T, Arc<dyn ExternError>>;
/// Some expectation (usually about the argument types of a function) did not
/// hold.
#[derive(Clone)]
pub struct AssertionError {
location: CodeLocation,
message: &'static str,
details: String,
}
impl AssertionError {
/// Construct, upcast and wrap in a Result that never succeeds for easy
/// short-circuiting
pub fn fail<T>(
location: CodeLocation,
message: &'static str,
details: String,
) -> ExternResult<T> {
Err(Self::ext(location, message, details))
}
/// Construct and upcast to [ExternError]
pub fn ext(
location: CodeLocation,
message: &'static str,
details: String,
) -> Arc<dyn ExternError> {
Self { location, message, details }.rc()
}
}
impl Display for AssertionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Error: expected {}", self.message)?;
write!(f, " at {}", self.location)?;
write!(f, " details: {}", self.details)
}
}
impl ExternError for AssertionError {}

View File

@@ -1,94 +0,0 @@
use std::error::Error;
use std::fmt::{Debug, Display};
use std::hash::Hash;
use std::sync::Arc;
use dyn_clone::{clone_box, DynClone};
use super::XfnResult;
use crate::interpreted::ExprInst;
use crate::interpreter::Context;
use crate::representations::interpreted::Clause;
use crate::{ast, NameLike};
/// Errors produced by external code
pub trait ExternError: Display + Send + Sync + DynClone {
/// Convert into trait object
#[must_use]
fn into_extern(self) -> Arc<dyn ExternError>
where
Self: 'static + Sized,
{
Arc::new(self)
}
}
impl Debug for dyn ExternError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self}")
}
}
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 + Send {
/// Display name of the function
#[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<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)
}
/// Wrap this function in a clause to be placed in an [AtomicResult].
#[must_use]
fn xfn_cls(self) -> Clause
where
Self: Sized + 'static,
{
Clause::ExternFn(ExFn(Box::new(self)))
}
/// Wrap this function in a clause to be placed in a [FileEntry].
#[must_use]
fn xfn_ast_cls<N: NameLike>(self) -> ast::Clause<N>
where
Self: Sized + 'static,
{
ast::Clause::ExternFn(ExFn(Box::new(self)))
}
}
impl Eq for dyn ExternFn {}
impl PartialEq for dyn ExternFn {
fn eq(&self, other: &Self) -> bool { self.name() == other.name() }
}
impl Hash for dyn ExternFn {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name().hash(state)
}
}
impl Debug for dyn ExternFn {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
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())) }
}

View File

@@ -1,47 +1,16 @@
use std::any::{Any, TypeId};
use std::fmt::Debug;
use std::marker::PhantomData;
use std::sync::Arc;
use super::atom::StrictEq;
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::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;
/// Convert to an expression instance via [ToClause].
fn to_exi(self) -> ExprInst { self.to_clause().wrap() }
}
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 String {
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())) }
}
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()))
}
}
use super::atom::{Atomic, AtomicResult, AtomicReturn};
use super::error::ExternResult;
use super::to_clause::ToClause;
use super::try_from_expr::TryFromExpr;
use crate::interpreter::apply::CallData;
use crate::interpreter::context::Halt;
use crate::interpreter::nort::{Clause, ClauseInst, Expr};
use crate::interpreter::run::{run, RunData};
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 a member of the [super::xfn_1ary]
@@ -51,6 +20,11 @@ impl<T: ToClause, U: ToClause> ToClause for Result<T, U> {
/// 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 [Lazy], 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,
_t: PhantomData<T>,
@@ -60,9 +34,7 @@ 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, Arc<dyn ExternError>>,
{
where F: FnOnce(T) -> U {
Self { data: f, _t: PhantomData, _u: PhantomData }
}
/// Take out the function
@@ -74,75 +46,91 @@ impl<T, U, F: Clone> Clone for Param<T, U, F> {
}
}
impl<
T: 'static + TryFromExprInst,
U: 'static + ToClause,
F: 'static + Clone + Send + FnOnce(T) -> Result<U, Arc<dyn ExternError>>,
> ToClause for Param<T, U, F>
{
fn to_clause(self) -> Clause { self.xfn_cls() }
/// 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) -> ExternResult<Self> { Ok(Thunk(expr)) }
}
struct FnMiddleStage<T, U, F> {
argument: ExprInst,
arg: Expr,
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 {
Self { argument: self.argument.clone(), f: self.f.clone() }
}
fn clone(&self) -> Self { Self { arg: self.arg.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)
.field("argument", &self.arg)
.finish_non_exhaustive()
}
}
impl<T, U, F> Responder for FnMiddleStage<T, U, F> {}
impl<
T: 'static + TryFromExprInst,
T: 'static + TryFromExpr,
U: 'static + ToClause,
F: 'static + Clone + FnOnce(T) -> Result<U, Arc<dyn ExternError>> + Send,
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 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 })
fn redirect(&mut self) -> Option<&mut ClauseInst> {
// this should be ctfe'd
(TypeId::of::<T>() != TypeId::of::<Thunk>()).then(|| &mut self.arg.clause)
}
fn run(self: Box<Self>, r: RunData) -> AtomicResult {
let Self { arg, f: Param { data: f, .. } } = *self;
let clause = f(arg.downcast()?).to_clause(r.location);
Ok(AtomicReturn { gas: r.ctx.gas, inert: false, clause })
}
fn apply_ref(&self, _: CallData) -> ExternResult<Clause> {
panic!("Atom should have decayed")
}
}
impl<T, U, F> Responder for Param<T, U, F> {}
impl<T, U, F> Debug for Param<T, U, F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Param")
}
}
impl<
T: 'static + TryFromExprInst,
T: 'static + TryFromExpr + Clone,
U: 'static + ToClause,
F: 'static + Clone + Send + FnOnce(T) -> Result<U, Arc<dyn ExternError>>,
> ExternFn for Param<T, U, F>
F: 'static + Clone + Send + FnOnce(T) -> U,
> Atomic 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())
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
fn as_any_ref(&self) -> &dyn std::any::Any { self }
fn redirect(&mut self) -> Option<&mut ClauseInst> { None }
fn run(self: Box<Self>, r: RunData) -> AtomicResult {
AtomicReturn::inert(*self, r.ctx)
}
fn apply_ref(&self, call: CallData) -> ExternResult<Clause> {
Ok(FnMiddleStage { arg: call.arg, f: self.clone() }.atom_cls())
}
fn apply(self: Box<Self>, call: CallData) -> ExternResult<Clause> {
Ok(FnMiddleStage { arg: call.arg, f: *self }.atom_cls())
}
}
/// 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 constructors {
use std::sync::Arc;
use super::super::atom::Atomic;
use super::super::try_from_expr::TryFromExpr;
#[allow(unused)] // for doc
use super::Thunk;
use super::{Param, ToClause};
use crate::foreign::{ExternError, ExternFn};
use crate::interpreted::TryFromExprInst;
macro_rules! xfn_variant {
(
@@ -154,18 +142,21 @@ pub mod constructors {
#[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 arguments must implement [TryFromExpr]\n"
"- all but the last argument must implement [Clone] and [Send]\n"
"- the return type must implement [ToClause].\n\n"
]
#[doc = "Take [Lazy] to take the argument as-is,\n"
"without normalization\n\n"
]
#[doc = "Other arities: " $( "[xfn_" $alt "ary], " )+ ]
pub fn [< xfn_ $number ary >] <
$( $t : TryFromExprInst + Clone + Send + 'static, )*
TLast: TryFromExprInst + 'static,
$( $t : TryFromExpr + Clone + Send + 'static, )*
TLast: TryFromExpr + Clone + 'static,
TReturn: ToClause + Send + 'static,
TFunction: FnOnce( $( $t , )* TLast )
-> Result<TReturn, Arc<dyn ExternError>> + Clone + Send + 'static
>(function: TFunction) -> impl ExternFn {
-> TReturn + Clone + Send + 'static
>(function: TFunction) -> impl Atomic + Clone {
xfn_variant!(@BODY_LOOP function
( $( ( $t [< $t:lower >] ) )* )
( $( [< $t:lower >] )* )
@@ -178,7 +169,7 @@ pub mod constructors {
$( ( $T:ident $t:ident ) )*
) $full:tt) => {
Param::new(|$next : $Next| {
Ok(xfn_variant!(@BODY_LOOP $function ( $( ( $T $t ) )* ) $full))
xfn_variant!(@BODY_LOOP $function ( $( ( $T $t ) )* ) $full)
})
};
(@BODY_LOOP $function:ident () ( $( $t:ident )* )) => {

View File

@@ -0,0 +1,172 @@
use std::any::Any;
use std::fmt::Debug;
use std::sync::Arc;
use super::atom::{Atom, Atomic, AtomicResult};
use super::error::{ExternError, ExternResult};
use super::process::Unstable;
use super::to_clause::ToClause;
use crate::gen::tpl;
use crate::gen::traits::Gen;
use crate::interpreter::apply::CallData;
use crate::interpreter::error::RunError;
use crate::interpreter::gen_nort::nort_gen;
use crate::interpreter::nort::{Clause, ClauseInst};
use crate::interpreter::run::RunData;
use crate::location::CodeLocation;
use crate::utils::clonable_iter::Clonable;
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(Arc<dyn ExternError>);
impl Responder for PendingError {}
impl Debug for PendingError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::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 redirect(&mut self) -> Option<&mut ClauseInst> { None }
fn run(self: Box<Self>, _: RunData) -> AtomicResult {
Err(RunError::Extern(self.0))
}
fn apply_ref(&self, _: CallData) -> ExternResult<Clause> {
panic!("This atom decays instantly")
}
}
impl<T: ToClause> ToClause for ExternResult<T> {
fn to_clause(self, location: CodeLocation) -> Clause {
match self {
Err(e) => PendingError(e).atom_cls(),
Ok(t) => t.to_clause(location),
}
}
}
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::lit::end").template(ctx, []),
Some(val) => {
let atom = Unstable::new(|run| self.to_clause(run.location));
tpl::a2(tpl::C("std::lit::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())),
))
})
}
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) }
}
mod tuple_impls {
use std::sync::Arc;
use super::ToClause;
use crate::foreign::atom::Atomic;
use crate::foreign::error::AssertionError;
use crate::foreign::implementations::ExternResult;
use crate::foreign::inert::Inert;
use crate::foreign::try_from_expr::TryFromExpr;
use crate::interpreter::nort::{Clause, Expr};
use crate::libs::std::tuple::Tuple;
use crate::location::CodeLocation;
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) -> ExternResult<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));
}

View File

@@ -1,28 +1,30 @@
use std::any::Any;
use std::fmt::Debug;
use std::fmt::{Debug, Display};
use std::ops::{Deref, DerefMut};
use ordered_float::NotNan;
use super::atom::StrictEq;
use super::{AtomicResult, AtomicReturn, XfnResult};
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::stl::Numeric;
use super::atom::{Atom, Atomic, AtomicResult, AtomicReturn, NotAFunction};
use super::error::{ExternError, ExternResult};
use super::try_from_expr::TryFromExpr;
use crate::foreign::error::AssertionError;
use crate::interpreter::apply::CallData;
use crate::interpreter::nort::{Clause, ClauseInst, Expr};
use crate::interpreter::run::RunData;
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 functions defined with [define_fn] it also automatically
/// implements [TryFromExprInst] so that a conversion doesn't have to be
/// implements [TryFromExpr] so that a conversion doesn't have to be
/// provided in argument lists.
pub trait InertAtomic: Debug + Clone + Send + 'static {
pub trait InertPayload: Debug + Clone + Send + 'static {
/// Typename to be shown in the error when a conversion from [ExprInst] fails
#[must_use]
fn type_str() -> &'static str;
///
/// 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
@@ -37,59 +39,99 @@ pub trait InertAtomic: Debug + Clone + Send + 'static {
/// ```
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))
}
/// 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: InertAtomic> Responder for 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.clone())
request.serve(self.0.clone())
} else {
self.respond(request)
self.0.respond(request)
}
}
}
impl<T: InertAtomic> Atomic for T {
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 run(self: Box<Self>, ctx: Context) -> AtomicResult {
Ok(AtomicReturn { gas: ctx.gas, inert: true, clause: self.atom_cls() })
fn redirect(&mut self) -> Option<&mut ClauseInst> { None }
fn run(self: Box<Self>, run: RunData) -> AtomicResult {
AtomicReturn::inert(*self, run.ctx)
}
fn apply_ref(&self, call: CallData) -> ExternResult<Clause> {
Err(NotAFunction(self.clone().atom_expr(call.location)).rc())
}
fn parser_eq(&self, other: &dyn Any) -> bool {
(other.downcast_ref::<Self>())
.map_or(false, |other| self.0.strict_eq(&other.0))
}
}
impl<T: InertAtomic> TryFromExprInst for T {
fn from_exi(exi: ExprInst) -> XfnResult<Self> {
let Expr { clause, location } = exi.expr_val();
match clause {
Clause::Atom(a) => match a.0.as_any().downcast() {
Ok(t) => Ok(*t),
Err(_) => AssertionError::fail(location, Self::type_str()),
impl<T: InertPayload> TryFromExpr for Inert<T> {
fn from_expr(expr: Expr) -> ExternResult<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() {
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}")),
},
_ => AssertionError::fail(location, "atom"),
Ok(cls) => AssertionError::fail(location, "atom", format!("{cls}")),
}
}
}
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<T: InertPayload + Display> Display for Inert<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl InertAtomic for NotNan<f64> {
fn type_str() -> &'static str { "NotNan<f64>" }
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(Numeric::Float(*self))
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

@@ -2,24 +2,12 @@
//!
//! Structures and traits used in the exposure of external functions and values
//! to Orchid code
mod atom;
pub mod atom;
pub mod cps_box;
mod extern_fn;
mod fn_bridge;
mod inert;
use std::sync::Arc;
pub use atom::{Atom, Atomic, AtomicResult, AtomicReturn, StrictEq};
pub use extern_fn::{ExFn, 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;
/// Return type of the argument to the [xfn_1ary] family of functions
pub type XfnResult<T> = Result<T, Arc<dyn ExternError>>;
pub mod error;
pub mod fn_bridge;
pub mod implementations;
pub mod inert;
pub mod process;
pub mod to_clause;
pub mod try_from_expr;

39
src/foreign/process.rs Normal file
View File

@@ -0,0 +1,39 @@
use std::fmt::Debug;
use super::atom::{Atomic, AtomicReturn};
use super::error::ExternResult;
use super::to_clause::ToClause;
use crate::interpreter::apply::CallData;
use crate::interpreter::nort::{Clause, ClauseInst};
use crate::interpreter::run::RunData;
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> Debug for Unstable<F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::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 apply_ref(&self, _: CallData) -> ExternResult<Clause> {
panic!("This atom decays instantly")
}
fn run(self: Box<Self>, run: RunData) -> super::atom::AtomicResult {
let clause = self.0(run.clone()).to_clause(run.location.clone());
AtomicReturn::run(clause, run)
}
fn redirect(&mut self) -> Option<&mut ClauseInst> { None }
}

43
src/foreign/to_clause.rs Normal file
View File

@@ -0,0 +1,43 @@
use super::atom::Atomic;
use crate::interpreter::nort::{Clause, ClauseInst, Expr};
use crate::location::CodeLocation;
/// A trait for things that are infallibly convertible to [ClauseInst]. These
/// types can be returned by callbacks passed to the [super::xfn_1ary] family of
/// functions.
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 }
}

View File

@@ -0,0 +1,28 @@
use super::error::ExternResult;
use crate::interpreter::nort::{ClauseInst, Expr};
use crate::location::CodeLocation;
/// Types automatically convertible from an [Expr]. Most notably, this is how
/// foreign functions request automatic argument downcasting.
pub trait TryFromExpr: Sized {
/// Match and clone the value out of an [Expr]
fn from_expr(expr: Expr) -> ExternResult<Self>;
}
impl TryFromExpr for Expr {
fn from_expr(expr: Expr) -> ExternResult<Self> { Ok(expr) }
}
impl TryFromExpr for ClauseInst {
fn from_expr(expr: Expr) -> ExternResult<Self> { Ok(expr.clause.clone()) }
}
/// Request a value of a particular type and also return its location for
/// further error reporting
#[derive(Debug, Clone)]
pub struct WithLoc<T>(pub CodeLocation, pub T);
impl<T: TryFromExpr> TryFromExpr for WithLoc<T> {
fn from_expr(expr: Expr) -> ExternResult<Self> {
Ok(Self(expr.location(), T::from_expr(expr)?))
}
}