use std::collections::VecDeque; use std::fmt; use std::future::Future; use std::rc::{Rc, Weak}; use derive_destructure::destructure; use futures::future::join_all; use futures_locks::RwLock; use hashbrown::HashMap; use itertools::Itertools; use memo_map::MemoMap; use orchid_base::char_filter::char_filter_match; use orchid_base::error::{OrcRes, mk_errv_floating}; use orchid_base::format::{FmtCtx, FmtUnit, Format}; use orchid_base::interner::{Interner, Tok}; use orchid_base::iter_utils::IteratorPrint; use orchid_base::name::{NameLike, Sym, VName, VPath}; use orchid_base::reqnot::{ReqNot, Requester}; use ordered_float::NotNan; use substack::{Stackframe, Substack}; use crate::api; use crate::atom::{AtomHand, WeakAtomHand}; use crate::ctx::Ctx; use crate::dealias::walk; use crate::extension::{Extension, WeakExtension}; use crate::sys_parser::Parser; use crate::tree::Root; #[derive(destructure)] pub(crate) struct SystemInstData { deps: Vec, ctx: Ctx, ext: Extension, decl_id: api::SysDeclId, lex_filter: api::CharFilter, id: api::SysId, line_types: Vec>, prelude: Vec, owned_atoms: RwLock>, pub(crate) const_paths: MemoMap, } impl Drop for SystemInstData { fn drop(&mut self) { self.ext.system_drop(self.id); } } impl fmt::Debug for SystemInstData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SystemInstData") .field("decl_id", &self.decl_id) .field("lex_filter", &self.lex_filter) .field("id", &self.id) .field("line_types", &self.line_types) .finish_non_exhaustive() } } #[derive(Clone, Debug)] pub struct System(pub(crate) Rc); impl System { #[must_use] pub async fn atoms(&self) -> impl std::ops::Deref> { self.0.owned_atoms.read().await } #[must_use] pub fn id(&self) -> api::SysId { self.0.id } #[must_use] pub fn ext(&self) -> &Extension { &self.0.ext } #[must_use] pub fn ctx(&self) -> &Ctx { &self.0.ctx } #[must_use] pub fn i(&self) -> &Interner { &self.0.ctx.i } #[must_use] pub fn deps(&self) -> &[System] { &self.0.deps } #[must_use] pub fn ctor(&self) -> SystemCtor { (self.0.ext.system_ctors().find(|c| c.decl.id == self.0.decl_id).cloned()) .expect("Ctor was used to create ext") } #[must_use] pub(crate) fn reqnot(&self) -> &ReqNot { self.0.ext.reqnot() } #[must_use] pub async fn get_tree(&self, id: api::TreeId) -> api::MemberKind { self.reqnot().request(api::GetMember(self.0.id, id)).await } #[must_use] pub fn has_lexer(&self) -> bool { !self.0.lex_filter.0.is_empty() } #[must_use] pub fn can_lex(&self, c: char) -> bool { char_filter_match(&self.0.lex_filter, c) } #[must_use] pub fn prelude(&self) -> Vec { self.0.prelude.clone() } /// Have this system lex a part of the source. It is assumed that /// [Self::can_lex] was called and returned true. pub async fn lex>>( &self, source: Tok, src: Sym, pos: u32, r: impl FnMut(u32) -> F, ) -> api::OrcResult> { self.0.ext.lex_req(source, src, pos, self.id(), r).await } #[must_use] pub fn get_parser(&self, ltyp: Tok) -> Option { (self.0.line_types.iter().enumerate()) .find(|(_, txt)| *txt == <yp) .map(|(idx, _)| Parser { idx: idx as u16, system: self.clone() }) } pub fn line_types(&self) -> impl Iterator> + '_ { self.0.line_types.iter() } #[must_use] pub async fn request(&self, req: Vec) -> Vec { self.reqnot().request(api::SysFwded(self.id(), req)).await } pub(crate) async fn new_atom(&self, data: Vec, id: api::AtomId) -> AtomHand { let mut owned_g = self.0.owned_atoms.write().await; if let Some(data) = owned_g.get(&id) && let Some(atom) = data.upgrade() { return atom; } let new = AtomHand::new(data, self.clone(), Some(id)); owned_g.insert(id, new.downgrade()); new } pub(crate) fn drop_atom(&self, dropped_atom_id: api::AtomId) { let this = self.0.clone(); (self.0.ctx.spawn)(Box::pin(async move { this.ext.reqnot().request(api::AtomDrop(this.id, dropped_atom_id)).await; this.owned_atoms.write().await.remove(&dropped_atom_id); })) } #[must_use] pub fn downgrade(&self) -> WeakSystem { WeakSystem(Rc::downgrade(&self.0), self.0.decl_id, self.ext().downgrade()) } /// Implementation of [api::ResolveNames] pub(crate) async fn name_resolver( &self, orig: api::ParsedConstId, ) -> impl AsyncFnMut(&[Tok]) -> OrcRes + use<> { let root = self.0.ctx.root.read().await.upgrade().expect("find_names when root not in context"); let orig = self.0.const_paths.get(&orig).expect("origin for find_names invalid").clone(); let ctx = self.0.ctx.clone(); async move |rel| { let cwd = orig.split_last_seg().1; let root_data = &mut *root.0.write().await; let walk_ctx = &mut (ctx.clone(), &root_data.consts); let cmod = (walk(&root_data.root, false, cwd.iter().cloned(), walk_ctx).await) .expect("the parent module of a constant should exist"); let (selector, tail) = rel.split_first().expect("Names cannot be empty"); if cmod.members.get(selector).is_some() { return Ok(VName::new(cwd.iter().chain(rel).cloned()).unwrap()); } match cmod.imports.get(selector) { Some(Ok(dest)) => return Ok(dest.target.to_vname().suffix(tail.iter().cloned())), Some(Err(dests)) => return Err(mk_errv_floating( ctx.i.i("Ambiguous name").await, format!( "{selector} could refer to {}", dests.iter().map(|ri| &ri.target).display("or") ), )), None => (), } if root_data.root.members.get(selector).is_some() { return Ok(VName::new(rel.iter().cloned()).expect("split_first was called above")); } if tail.is_empty() { return Ok(VPath::new(cwd.iter().cloned()).name_with_suffix(selector.clone())); } Err(mk_errv_floating( ctx.i.i("Invalid name").await, format!("{selector} doesn't refer to a module"), )) } } } impl Format for System { async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { let ctor = (self.0.ext.system_ctors().find(|c| c.id() == self.0.decl_id)) .expect("System instance with no associated constructor"); format!("System({} @ {} #{})", ctor.name(), ctor.priority(), self.0.id.0).into() } } pub struct WeakSystem(Weak, api::SysDeclId, WeakExtension); impl WeakSystem { #[must_use] pub fn upgrade(&self) -> Option { self.0.upgrade().map(System) } pub fn ext(&self) -> Option { self.2.upgrade() } pub fn ctor(&self) -> Option { self.ext()?.system_ctors().find(|ctor| ctor.decl.id == self.1).cloned() } } #[derive(Clone)] pub struct SystemCtor { pub(crate) decl: api::SystemDecl, pub(crate) ext: WeakExtension, } impl SystemCtor { #[must_use] pub fn name(&self) -> &str { &self.decl.name } pub async fn name_tok(&self) -> Sym { (Sym::parse(&self.decl.name, &self.ext.upgrade().expect("ext dropped early").ctx().i).await) .expect("System cannot have empty name") } #[must_use] pub fn priority(&self) -> NotNan { self.decl.priority } #[must_use] pub fn depends(&self) -> impl ExactSizeIterator { self.decl.depends.iter().map(|s| &**s) } #[must_use] pub fn id(&self) -> api::SysDeclId { self.decl.id } #[must_use] pub async fn run(&self, deps: Vec) -> (Root, System) { let depends = deps.iter().map(|si| si.id()).collect_vec(); debug_assert_eq!(depends.len(), self.decl.depends.len(), "Wrong number of deps provided"); let ext = self.ext.upgrade().expect("SystemCtor should be freed before Extension"); let id = ext.ctx().next_sys_id(); let sys_inst = ext.reqnot().request(api::NewSystem { depends, id, system: self.decl.id }).await; let data = System(Rc::new(SystemInstData { deps, decl_id: self.decl.id, ext: ext.clone(), ctx: ext.ctx().clone(), lex_filter: sys_inst.lex_filter, line_types: join_all(sys_inst.line_types.iter().map(|m| Tok::from_api(*m, &ext.ctx().i))) .await, id, prelude: join_all(sys_inst.prelude.iter().map(|tok| Sym::from_api(*tok, &ext.ctx().i))).await, owned_atoms: RwLock::new(HashMap::new()), const_paths: MemoMap::new(), })); let api_module_root = api::Module { members: (sys_inst.const_root.into_iter()) .map(|(k, v)| api::Member { name: k, kind: v, comments: vec![], exported: true }) .collect_vec(), }; let root = Root::from_api(api_module_root, &data).await; ext.ctx().systems.write().await.insert(id, data.downgrade()); (root, data) } } #[derive(Debug, Clone)] pub enum SysResolvErr { Loop(Vec), Missing(String), } pub async fn init_systems( tgts: &[String], exts: &[Extension], ) -> Result<(Root, Vec), SysResolvErr> { let mut to_load = HashMap::<&str, &SystemCtor>::new(); let mut to_find = tgts.iter().map(|s| s.as_str()).collect::>(); while let Some(target) = to_find.pop_front() { if to_load.contains_key(target) { continue; } let ctor = (exts.iter()) .flat_map(|e| e.system_ctors().filter(|c| c.name() == target)) .max_by_key(|c| c.priority()) .ok_or_else(|| SysResolvErr::Missing(target.to_string()))?; to_load.insert(target, ctor); to_find.extend(ctor.depends()); } let mut to_load_ordered = Vec::new(); fn walk_deps<'a>( graph: &mut HashMap<&str, &'a SystemCtor>, list: &mut Vec<&'a SystemCtor>, chain: Stackframe<&str>, ) -> Result<(), SysResolvErr> { if let Some(ctor) = graph.remove(chain.item) { // if the above is none, the system is already queued. Missing systems are // detected above for dep in ctor.depends() { if Substack::Frame(chain).iter().any(|c| *c == dep) { let mut circle = vec![dep.to_string()]; circle.extend(Substack::Frame(chain).iter().map(|s| s.to_string())); return Err(SysResolvErr::Loop(circle)); } walk_deps(graph, list, Substack::Frame(chain).new_frame(dep))? } list.push(ctor); } Ok(()) } for tgt in tgts { walk_deps(&mut to_load, &mut to_load_ordered, Substack::Bottom.new_frame(tgt))?; } let mut systems = HashMap::<&str, System>::new(); let mut root = Root::new(exts.first().unwrap().ctx().clone()); for ctor in to_load_ordered.iter() { let (sys_root, sys) = ctor.run(ctor.depends().map(|n| systems[n].clone()).collect()).await; systems.insert(ctor.name(), sys); root = root.merge(&sys_root).await.expect("Conflicting roots"); } Ok((root, systems.into_values().collect_vec())) }