use std::any::{Any, TypeId, type_name}; use std::fmt; use std::future::Future; use std::num::NonZero; use std::pin::Pin; use std::rc::{Rc, Weak}; use futures::future::LocalBoxFuture; use memo_map::MemoMap; use orchid_api::ExtMsgSet; 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::name::Sym; use orchid_base::reqnot::{Receipt, ReqNot}; use crate::api; use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId, AtomicFeatures, ForeignAtom, TypAtom, get_info}; use crate::entrypoint::ExtReq; use crate::func_atom::Fun; 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 + '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())].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<'_, Option>> { Box::new(Self::atoms().into_iter()) } } /// System as defined by author pub trait System: Send + Sync + SystemCard + 'static { fn prelude(i: &Interner) -> impl Future>; fn env() -> Vec; 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<'a>(&'a self, i: &'a Interner) -> LocalBoxFuture<'a, Vec>; fn dyn_env(&'_ self) -> 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<'a>(&'a self, i: &'a Interner) -> LocalBoxFuture<'a, Vec> { Box::pin(Self::prelude(i)) } fn dyn_env(&'_ self) -> Vec { Self::env() } 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.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() }; 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, untyped: foreign }) } #[derive(Clone)] pub struct WeakSysCtx(Weak>>); impl WeakSysCtx { pub fn upgrade(&self) -> Option { Some(SysCtx(self.0.upgrade()?)) } } #[derive(Clone)] pub struct SysCtx(Rc>>); impl SysCtx { pub fn new( id: api::SysId, i: Interner, reqnot: ReqNot, spawner: Spawner, logger: Logger, cted: CtedObj, ) -> Self { let this = Self(Rc::new(MemoMap::new())); this.add(id).add(i).add(reqnot).add(spawner).add(logger).add(cted); this } pub fn downgrade(&self) -> WeakSysCtx { WeakSysCtx(Rc::downgrade(&self.0)) } pub fn add(&self, t: T) -> &Self { assert!(self.0.insert(TypeId::of::(), Box::new(t)), "Key already exists"); self } pub fn get_or_insert(&self, f: impl FnOnce() -> T) -> &T { (self.0.get_or_insert_owned(TypeId::of::(), || Box::new(f())).downcast_ref()) .expect("Keyed by TypeId") } pub fn get_or_default(&self) -> &T { self.get_or_insert(|| { let rc_id = self.0.as_ref() as *const _ as *const () as usize; eprintln!("Default-initializing {} in {}", type_name::(), rc_id); T::default() }) } pub fn try_get(&self) -> Option<&T> { Some(self.0.get(&TypeId::of::())?.downcast_ref().expect("Keyed by TypeId")) } pub fn get(&self) -> &T { self.try_get().unwrap_or_else(|| panic!("Context {} missing", type_name::())) } /// Shorthand to get the [Interner] instance pub fn i(&self) -> &Interner { self.get::() } /// Shorthand to get the messaging link pub fn reqnot(&self) -> &ReqNot { self.get::>() } /// Shorthand to get the system ID pub fn sys_id(&self) -> api::SysId { *self.get::() } /// Shorthand to get the task spawner callback pub fn spawner(&self) -> &Spawner { self.get::() } /// Shorthand to get the logger pub fn logger(&self) -> &Logger { self.get::() } /// Shorthand to get the constructed system object pub fn cted(&self) -> &CtedObj { self.get::() } } impl fmt::Debug for SysCtx { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "SysCtx({:?})", self.sys_id()) } } pub trait SysCtxEntry: 'static + Sized {} impl SysCtxEntry for api::SysId {} impl SysCtxEntry for ReqNot {} impl SysCtxEntry for Spawner {} impl SysCtxEntry for CtedObj {} impl SysCtxEntry for Logger {} impl SysCtxEntry for Interner {}