use core::fmt; use std::any::{TypeId, type_name}; use std::future::Future; use std::num::NonZero; use std::pin::Pin; use std::rc::Rc; use futures::future::LocalBoxFuture; use hashbrown::HashMap; use orchid_api_traits::{Coding, Decode}; use orchid_base::boxed_iter::BoxedIter; use orchid_base::builtin::Spawner; use orchid_base::interner::Interner; use orchid_base::logging::Logger; use orchid_base::reqnot::{Receipt, ReqNot}; use crate::api; use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId, AtomicFeatures, ForeignAtom, TypAtom, get_info}; use crate::atom_owned::ObjStore; use crate::entrypoint::ExtReq; use crate::fs::DeclFs; use crate::func_atom::Fun; use crate::lexer::LexerObj; use crate::parser::ParserObj; use crate::system_ctor::{CtedObj, SystemCtor}; use crate::tree::MemKind; /// 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 + '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>>; } /// 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())].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[..])).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>> { Box::new(Self::atoms().into_iter()) } } /// System as defined by author pub trait System: Send + Sync + SystemCard + 'static { fn env() -> Vec<(String, MemKind)>; fn vfs() -> DeclFs; fn lexers() -> Vec; fn parsers() -> Vec; fn request(hand: ExtReq<'_>, req: Self::Req) -> impl Future>; } pub trait DynSystem: Send + Sync + DynSystemCard + 'static { fn dyn_env(&self) -> HashMap; fn dyn_vfs(&self) -> DeclFs; 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_env(&self) -> HashMap { Self::env().into_iter().collect() } fn dyn_vfs(&self) -> DeclFs { Self::vfs() } 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[..]; let ctx = foreign.ctx.clone(); let value = AtomTypeId::decode(Pin::new(&mut data)).await; let own_inst = ctx.cted.inst(); let owner = if ctx.id == foreign.atom.owner { own_inst.card() } else { (ctx.cted.deps().find(|s| s.id() == foreign.atom.owner)) .ok_or_else(|| foreign.clone())? .get_card() }; let (typ_id, dynfo) = get_info::(owner); if value != typ_id { return Err(foreign); } let val = dynfo.decode(AtomCtx(data, foreign.atom.drop, ctx)).await; let value = *val.downcast::().expect("atom decode returned wrong type"); Ok(TypAtom { value, data: foreign }) } #[derive(Clone)] pub struct SysCtx { pub reqnot: ReqNot, pub spawner: Spawner, pub id: api::SysId, pub cted: CtedObj, pub logger: Logger, pub obj_store: ObjStore, pub i: Rc, } impl fmt::Debug for SysCtx { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "SysCtx({:?})", self.id) } }