use std::any::{Any, TypeId}; use std::future::Future; use std::num::NonZero; use std::pin::Pin; use futures::FutureExt; use futures::future::LocalBoxFuture; use orchid_api_traits::{Coding, Decode, Encode, Request}; use orchid_base::boxed_iter::BoxedIter; use orchid_base::name::Sym; use orchid_base::reqnot::{Receipt, Requester}; use crate::api; use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId, AtomicFeatures, ForeignAtom, TAtom, get_info}; use crate::context::ctx; use crate::coroutine_exec::Replier; use crate::entrypoint::ExtReq; use crate::func_atom::{Fun, Lambda}; use crate::lexer::LexerObj; use crate::parser::ParserObj; use crate::system_ctor::{CtedObj, SystemCtor}; use crate::tree::GenMember; /// System as consumed by foreign code pub trait SystemCard: Default + Send + Sync + 'static { type Ctor: SystemCtor; type Req: Coding; fn atoms() -> impl IntoIterator>>; } pub trait DynSystemCard: Send + Sync + Any + 'static { fn name(&self) -> &'static str; /// Atoms explicitly defined by the system card. Do not rely on this for /// querying atoms as it doesn't include the general atom types fn atoms(&'_ self) -> BoxedIter<'_, Option>>; } /// Atoms supported by this package which may appear in all extensions. /// The indices of these are bitwise negated, such that the MSB of an atom index /// marks whether it belongs to this package (0) or the importer (1) fn general_atoms() -> impl Iterator>> { [Some(Fun::dynfo()), Some(Lambda::dynfo()), Some(Replier::dynfo())].into_iter() } pub fn atom_info_for( sys: &(impl DynSystemCard + ?Sized), tid: TypeId, ) -> Option<(AtomTypeId, Box)> { (sys.atoms().enumerate().map(|(i, o)| (NonZero::new(i as u32 + 1).unwrap(), o))) .chain(general_atoms().enumerate().map(|(i, o)| (NonZero::new(!(i as u32)).unwrap(), o))) .filter_map(|(i, o)| o.map(|a| (AtomTypeId(i), a))) .find(|ent| ent.1.tid() == tid) } pub fn atom_by_idx( sys: &(impl DynSystemCard + ?Sized), tid: AtomTypeId, ) -> Option> { if (u32::from(tid.0) >> (u32::BITS - 1)) & 1 == 1 { general_atoms().nth(!u32::from(tid.0) as usize).unwrap() } else { sys.atoms().nth(u32::from(tid.0) as usize - 1).unwrap() } } pub async fn resolv_atom( sys: &(impl DynSystemCard + ?Sized), atom: &api::Atom, ) -> Box { let tid = AtomTypeId::decode(Pin::new(&mut &atom.data.0[..])).await; atom_by_idx(sys, tid).expect("Value of nonexistent type found") } impl DynSystemCard for T { fn name(&self) -> &'static str { T::Ctor::NAME } fn atoms(&'_ self) -> BoxedIter<'_, Option>> { Box::new(Self::atoms().into_iter()) } } /// System as defined by author pub trait System: Send + Sync + SystemCard + 'static { fn prelude() -> impl Future>; fn env() -> impl Future>; fn lexers() -> Vec; fn parsers() -> Vec; fn request(hand: ExtReq<'_>, req: Self::Req) -> impl Future>; } pub trait DynSystem: Send + Sync + DynSystemCard + 'static { fn dyn_prelude(&self) -> LocalBoxFuture<'_, Vec>; fn dyn_env(&self) -> LocalBoxFuture<'_, Vec>; fn dyn_lexers(&self) -> Vec; fn dyn_parsers(&self) -> Vec; fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec) -> LocalBoxFuture<'a, Receipt<'a>>; fn card(&self) -> &dyn DynSystemCard; } impl DynSystem for T { fn dyn_prelude(&self) -> LocalBoxFuture<'_, Vec> { Box::pin(Self::prelude()) } fn dyn_env(&self) -> LocalBoxFuture<'_, Vec> { Self::env().boxed_local() } fn dyn_lexers(&self) -> Vec { Self::lexers() } fn dyn_parsers(&self) -> Vec { Self::parsers() } fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec) -> LocalBoxFuture<'a, Receipt<'a>> { Box::pin(async move { Self::request(hand, ::Req::decode(Pin::new(&mut &req[..])).await).await }) } fn card(&self) -> &dyn DynSystemCard { self } } pub async fn downcast_atom(foreign: ForeignAtom) -> Result, ForeignAtom> where A: AtomicFeatures { let mut data = &foreign.atom.data.0[..]; let ctx = ctx(); let value = AtomTypeId::decode(Pin::new(&mut data)).await; let own_inst = ctx.get::().inst(); let owner = if *ctx.get::() == foreign.atom.owner { own_inst.card() } else { (ctx.get::().deps().find(|s| s.id() == foreign.atom.owner)) .ok_or_else(|| foreign.clone())? .get_card() }; if owner.atoms().flatten().all(|dynfo| dynfo.tid() != TypeId::of::()) { return Err(foreign); } let (typ_id, dynfo) = get_info::(owner); if value != typ_id { return Err(foreign); } let val = dynfo.decode(AtomCtx(data, foreign.atom.drop)).await; let value = *val.downcast::().expect("atom decode returned wrong type"); Ok(TAtom { value, untyped: foreign }) } pub async fn dep_req>(req: Req) -> Req::Response { let ctx = ctx(); let mut msg = Vec::new(); req.into().encode(std::pin::pin!(&mut msg)).await; let own_inst = ctx.get::().inst(); let owner = if own_inst.card().type_id() == TypeId::of::() { ctx.sys_id() } else { (ctx.get::().deps().find(|s| s.get_card().type_id() == TypeId::of::())) .expect("System not in dependency array") .id() }; let reply = ctx.reqnot().request(api::SysFwd(owner, msg)).await; Req::Response::decode(std::pin::pin!(&reply[..])).await }