#[allow(unused)] // for the doc comments use std::any::Any; #[allow(unused)] // for the doc comments use std::fmt::Debug; #[allow(unused)] // for the doc comments use dyn_clone::DynClone; #[allow(unused)] // for the doc comments use crate::define_fn; #[allow(unused)] // for the doc comments use crate::foreign::{Atomic, ExternFn}; #[allow(unused)] // for the doc comments use crate::write_fn_step; #[allow(unused)] // for the doc comments use crate::Primitive; /// A macro that generates implementations of [Atomic] to simplify the /// development of external bindings for Orchid. /// /// Most use cases are fulfilled by [define_fn], pathological cases can combine /// [write_fn_step] with manual [Atomic] implementations. /// /// The macro depends on implementations of [`AsRef`] 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 [Clone]. /// /// 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]. /// /// 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. /// /// _definition of the `add` function in the STL_ /// ``` /// use orchidlang::{Literal}; /// use orchidlang::interpreted::ExprInst; /// use orchidlang::stl::litconv::with_lit; /// use orchidlang::{atomic_impl, atomic_redirect, externfn_impl}; /// /// /// Convert a literal to a string using Rust's conversions for floats, chars and /// /// uints respectively /// #[derive(Clone)] /// struct ToString; /// /// externfn_impl!{ /// ToString, |_: &Self, expr_inst: ExprInst|{ /// Ok(InternalToString { /// expr_inst /// }) /// } /// } /// #[derive(std::fmt::Debug,Clone)] /// struct InternalToString { /// expr_inst: ExprInst, /// } /// atomic_redirect!(InternalToString, expr_inst); /// atomic_impl!(InternalToString, |Self { expr_inst }: &Self, _|{ /// with_lit(expr_inst, |l| Ok(match l { /// Literal::Char(c) => c.to_string(), /// Literal::Uint(i) => i.to_string(), /// Literal::Num(n) => n.to_string(), /// Literal::Str(s) => s.clone(), /// })).map(|s| Literal::Str(s).into()) /// }); /// ``` #[macro_export] macro_rules! atomic_impl { ($typ:ident) => { $crate::atomic_impl! {$typ, |this: &Self, _: $crate::interpreter::Context| { use $crate::foreign::ExternFn; Ok(this.clone().to_xfn_cls()) }} }; ($typ:ident, $next_phase:expr) => { impl $crate::foreign::Atomic for $typ { $crate::atomic_defaults! {} fn run( &self, ctx: $crate::interpreter::Context, ) -> $crate::foreign::AtomicResult { // extract the expression let expr = >::as_ref(self).clone(); // run the expression let ret = $crate::interpreter::run(expr, ctx.clone())?; let $crate::interpreter::Return { gas, state, inert } = ret; // rebuild the atomic let next_self = >::from((self, state)); // branch off or wrap up let clause = if inert { let closure = $next_phase; let res: Result< $crate::interpreted::Clause, std::rc::Rc, > = closure(&next_self, ctx); match res { Ok(r) => r, Err(e) => return Err($crate::interpreter::RuntimeError::Extern(e)), } } else { next_self.to_atom_cls() }; // package and return Ok($crate::foreign::AtomicReturn { clause, gas, inert: false }) } } }; }