use std::mem; use std::rc::Rc; use futures::FutureExt; use orchid_base::error::{OrcErr, OrcErrv}; use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants}; use orchid_base::location::Pos; use orchid_base::name::Sym; use orchid_base::{match_mapping, tl_cache}; use crate::api; use crate::atom::{AtomFactory, ToAtom}; use crate::entrypoint::request; use crate::expr::Expr; #[derive(Clone, Debug)] pub struct GExpr { pub kind: GExprKind, pub pos: Pos, } impl GExpr { /// Release notifications will not be sent for the slots. Use this with /// messages that imply ownership transfer pub async fn serialize(self) -> api::Expression { if let GExprKind::Slot(ex) = self.kind { let hand = ex.handle(); mem::drop(ex); api::Expression { location: api::Location::SlotTarget, // an instance is leaked here, we must take ownership of it when we receive this kind: api::ExpressionKind::Slot(hand.serialize().await), } } else { api::Expression { location: api::Location::Inherit, kind: self.kind.serialize().boxed_local().await, } } } pub fn at(self, pos: Pos) -> Self { GExpr { pos, kind: self.kind } } pub async fn create(self) -> Expr { Expr::deserialize(request(api::Create(self.serialize().await)).await).await } } impl Format for GExpr { async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { self.kind.print(c).boxed_local().await } } #[derive(Clone, Debug)] pub enum GExprKind { Call(Box, Box), Lambda(u64, Box), Arg(u64), Seq(Box, Box), Const(Sym), NewAtom(AtomFactory), Slot(Expr), Bottom(OrcErrv), } impl GExprKind { pub async fn serialize(self) -> api::ExpressionKind { match_mapping!(self, Self => api::ExpressionKind { Call( f => Box::new(f.serialize().await), x => Box::new(x.serialize().await) ), Seq( a => Box::new(a.serialize().await), b => Box::new(b.serialize().await) ), Lambda(arg, body => Box::new(body.serialize().await)), Arg(arg), Const(name.to_api()), Bottom(err.to_api()), NewAtom(fac.clone().build().await), } { Self::Slot(_) => panic!("processed elsewhere") }) } } impl Format for GExprKind { async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { match self { GExprKind::Call(f, x) => tl_cache!(Rc: Rc::new(Variants::default().bounded("{0} ({1})"))) .units([f.print(c).await, x.print(c).await]), GExprKind::Lambda(arg, body) => tl_cache!(Rc: Rc::new(Variants::default().bounded("\\{0}.{1}"))) .units([arg.to_string().into(), body.print(c).await]), GExprKind::Arg(arg) => arg.to_string().into(), GExprKind::Seq(a, b) => tl_cache!(Rc: Rc::new(Variants::default().bounded("[{0}] {1}"))) .units([a.print(c).await, b.print(c).await]), GExprKind::Const(sym) => sym.to_string().into(), GExprKind::NewAtom(atom_factory) => atom_factory.to_string().into(), GExprKind::Slot(expr) => tl_cache!(Rc: Rc::new(Variants::default().bounded("{{{0}}}"))) .units([expr.print(c).await]), GExprKind::Bottom(orc_errv) => orc_errv.to_string().into(), } } } fn inherit(kind: GExprKind) -> GExpr { GExpr { pos: Pos::Inherit, kind } } pub fn sym_ref(path: Sym) -> GExpr { inherit(GExprKind::Const(path)) } /// Creates an expression from a new atom that we own. pub fn new_atom(atom: A) -> GExpr { inherit(GExprKind::NewAtom(atom.to_atom_factory())) } pub fn seq(deps: impl IntoIterator, val: GExpr) -> GExpr { fn recur(mut ops: impl Iterator) -> Option { let op = ops.next()?; Some(match recur(ops) { None => op, Some(rec) => inherit(GExprKind::Seq(Box::new(op), Box::new(rec))), }) } recur(deps.into_iter().chain([val])).expect("Empty list provided to seq!") } pub fn arg(n: u64) -> GExpr { inherit(GExprKind::Arg(n)) } pub fn lambda(n: u64, [b]: [GExpr; 1]) -> GExpr { inherit(GExprKind::Lambda(n, Box::new(b))) } pub fn call(f: GExpr, argv: impl IntoIterator) -> GExpr { (argv.into_iter()).fold(f, |f, x| inherit(GExprKind::Call(Box::new(f), Box::new(x)))) } pub fn bot(ev: impl IntoIterator) -> GExpr { inherit(GExprKind::Bottom(OrcErrv::new(ev).unwrap())) }