diff --git a/SWAP.md b/SWAP.md index 6574e98..55ff426 100644 --- a/SWAP.md +++ b/SWAP.md @@ -1,11 +1,8 @@ -Must figure out how preprocessor can both be a System and referenced in the interpreter +## Async conversion -Must actually write macro system as recorded in note - -At this point swappable preprocessors aren't a target because interaction with module system sounds complicated - -Check if any of this needs interpreter, if so, start with that +convert host to async non-send +convert extension's SysCtx to a typed context bag ## alternate extension mechanism @@ -15,3 +12,12 @@ Ideally, it should reuse `orchid-extension` for message routing and decoding. `orchid-host` accepts extensions as `impl ExtensionPort` +## Preprocessor extension + +Must figure out how preprocessor can both be a System and referenced in the interpreter + +Must actually write macro system as recorded in note + +At this point swappable preprocessors aren't a target because interaction with module system sounds complicated + +Check if any of this needs interpreter, if so, start with that \ No newline at end of file diff --git a/orchid-api/src/interner.rs b/orchid-api/src/interner.rs index 1d5d765..da67472 100644 --- a/orchid-api/src/interner.rs +++ b/orchid-api/src/interner.rs @@ -23,7 +23,7 @@ pub enum IntReq { /// See [IntReq] #[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)] #[extends(IntReq, ExtHostReq)] -pub struct InternStr(pub Arc); +pub struct InternStr(pub String); impl Request for InternStr { type Response = TStr; } @@ -37,7 +37,7 @@ impl Request for InternStr { #[extends(IntReq, ExtHostReq)] pub struct ExternStr(pub TStr); impl Request for ExternStr { - type Response = Arc; + type Response = String; } /// replica -> master to intern a vector of interned strings /// @@ -46,7 +46,7 @@ impl Request for ExternStr { /// See [IntReq] #[derive(Clone, Debug, Coding, Hierarchy)] #[extends(IntReq, ExtHostReq)] -pub struct InternStrv(pub Arc>); +pub struct InternStrv(pub Vec); impl Request for InternStrv { type Response = TStrv; } @@ -60,7 +60,7 @@ impl Request for InternStrv { #[extends(IntReq, ExtHostReq)] pub struct ExternStrv(pub TStrv); impl Request for ExternStrv { - type Response = Arc>; + type Response = Vec; } /// A substitute for an interned string in serialized datastructures. diff --git a/orchid-base/src/clone.rs b/orchid-base/src/clone.rs index 0eb593d..7aa9202 100644 --- a/orchid-base/src/clone.rs +++ b/orchid-base/src/clone.rs @@ -6,4 +6,7 @@ macro_rules! clone { $body } ); + ($($n:ident),+) => { + $( let $n = $n.clone(); )+ + } } diff --git a/orchid-base/src/error.rs b/orchid-base/src/error.rs index 5ad9660..d067e39 100644 --- a/orchid-base/src/error.rs +++ b/orchid-base/src/error.rs @@ -6,7 +6,7 @@ use futures::future::join_all; use itertools::Itertools; use crate::api; -use crate::interner::Tok; +use crate::interner::{Interner, Tok}; use crate::location::Pos; /// A point of interest in resolving the error, such as the point where @@ -22,10 +22,10 @@ impl ErrPos { pub fn new(msg: &str, position: Pos) -> Self { Self { message: Some(Arc::new(msg.to_string())), position } } - async fn from_api(api: &api::ErrLocation) -> Self { + async fn from_api(api: &api::ErrLocation, i: &Interner) -> Self { Self { message: Some(api.message.clone()).filter(|s| !s.is_empty()), - position: Pos::from_api(&api.location).await, + position: Pos::from_api(&api.location, i).await, } } fn to_api(&self) -> api::ErrLocation { @@ -53,11 +53,11 @@ impl OrcErr { locations: self.positions.iter().map(ErrPos::to_api).collect(), } } - async fn from_api(api: &api::OrcError) -> Self { + async fn from_api(api: &api::OrcError, i: &Interner) -> Self { Self { - description: Tok::from_api(api.description).await, + description: Tok::from_api(api.description, i).await, message: api.message.clone(), - positions: join_all(api.locations.iter().map(ErrPos::from_api)).await, + positions: join_all(api.locations.iter().map(|e| ErrPos::from_api(e, i))).await, } } } @@ -113,8 +113,11 @@ impl OrcErrv { self.0.iter().flat_map(|e| e.positions.iter().cloned()) } pub fn to_api(&self) -> Vec { self.0.iter().map(OrcErr::to_api).collect() } - pub async fn from_api<'a>(api: impl IntoIterator) -> Self { - Self(join_all(api.into_iter().map(OrcErr::from_api)).await) + pub async fn from_api<'a>( + api: impl IntoIterator, + i: &Interner, + ) -> Self { + Self(join_all(api.into_iter().map(|e| OrcErr::from_api(e, i))).await) } } impl From for OrcErrv { diff --git a/orchid-base/src/interner.rs b/orchid-base/src/interner.rs index 02b83a2..4ba6ee0 100644 --- a/orchid-base/src/interner.rs +++ b/orchid-base/src/interner.rs @@ -1,14 +1,13 @@ use std::borrow::Borrow; use std::future::Future; use std::hash::BuildHasher as _; -use std::marker::PhantomData; use std::num::NonZeroU64; -use std::ops::{Deref, DerefMut}; +use std::ops::Deref; use std::rc::Rc; -use std::sync::{Arc, Mutex, MutexGuard, atomic}; -use std::{fmt, hash, mem}; +use std::sync::atomic; +use std::{fmt, hash}; -use async_once_cell::Lazy; +use async_std::sync::Mutex; use hashbrown::{HashMap, HashSet}; use itertools::Itertools as _; use orchid_api_traits::Request; @@ -29,9 +28,9 @@ pub struct Tok { impl Tok { pub fn new(data: Rc, marker: T::Marker) -> Self { Self { data, marker: ForceSized(marker) } } pub fn to_api(&self) -> T::Marker { self.marker.0 } - pub async fn from_api(marker: M, i: &mut Interner) -> Self + pub async fn from_api(marker: M, i: &Interner) -> Self where M: InternMarker { - i.deintern(marker).await + i.ex(marker).await } pub fn rc(&self) -> Rc { self.data.clone() } } @@ -67,7 +66,7 @@ impl fmt::Debug for Tok { pub trait Interned: Eq + hash::Hash + Clone + fmt::Debug + Internable { type Marker: InternMarker + Sized; fn intern( - self: Arc, + self: Rc, req: &(impl DynRequester + ?Sized), ) -> impl Future; fn bimap(interner: &mut TypedInterners) -> &mut Bimap; @@ -75,15 +74,13 @@ pub trait Interned: Eq + hash::Hash + Clone + fmt::Debug + Internable Arc; + fn get_owned(&self) -> Rc; } pub trait InternMarker: Copy + PartialEq + Eq + PartialOrd + Ord + hash::Hash + Sized { type Interned: Interned; - fn resolve( - self, - req: &(impl DynRequester + ?Sized), - ) -> impl Future>; + /// Only called on replicas + fn resolve(self, i: &Interner) -> impl Future>; fn get_id(self) -> NonZeroU64; fn from_id(id: NonZeroU64) -> Self; } @@ -91,63 +88,61 @@ pub trait InternMarker: Copy + PartialEq + Eq + PartialOrd + Ord + hash::Hash + impl Interned for String { type Marker = api::TStr; async fn intern( - self: Arc, + self: Rc, req: &(impl DynRequester + ?Sized), ) -> Self::Marker { - req.request(api::InternStr(self)).await + req.request(api::InternStr(self.to_string())).await } fn bimap(interners: &mut TypedInterners) -> &mut Bimap { &mut interners.strings } } impl InternMarker for api::TStr { type Interned = String; - async fn resolve( - self, - req: &(impl DynRequester + ?Sized), - ) -> Tok { - Tok::new(req.request(api::ExternStr(self)).await, self) + async fn resolve(self, i: &Interner) -> Tok { + Tok::new(Rc::new(i.master.as_ref().unwrap().request(api::ExternStr(self)).await), self) } fn get_id(self) -> NonZeroU64 { self.0 } fn from_id(id: NonZeroU64) -> Self { Self(id) } } impl Internable for str { type Interned = String; - fn get_owned(&self) -> Arc { Arc::new(self.to_string()) } + fn get_owned(&self) -> Rc { Rc::new(self.to_string()) } } impl Internable for String { type Interned = String; - fn get_owned(&self) -> Arc { Arc::new(self.to_string()) } + fn get_owned(&self) -> Rc { Rc::new(self.to_string()) } } impl Interned for Vec> { type Marker = api::TStrv; async fn intern( - self: Arc, + self: Rc, req: &(impl DynRequester + ?Sized), ) -> Self::Marker { - req.request(api::InternStrv(Arc::new(self.iter().map(|t| t.to_api()).collect()))).await + req.request(api::InternStrv(self.iter().map(|t| t.to_api()).collect())).await } fn bimap(interners: &mut TypedInterners) -> &mut Bimap { &mut interners.vecs } } impl InternMarker for api::TStrv { type Interned = Vec>; - async fn resolve( - self, - req: &(impl DynRequester + ?Sized), - ) -> Tok { - let rep = req.request(api::ExternStrv(self)).await; - let data = futures::future::join_all(rep.iter().map(|m| deintern(*m))).await; - Tok::new(Arc::new(data), self) + async fn resolve(self, i: &Interner) -> Tok { + let rep = i.master.as_ref().unwrap().request(api::ExternStrv(self)).await; + let data = futures::future::join_all(rep.into_iter().map(|m| i.ex(m))).await; + Tok::new(Rc::new(data), self) } fn get_id(self) -> NonZeroU64 { self.0 } fn from_id(id: NonZeroU64) -> Self { Self(id) } } impl Internable for [Tok] { type Interned = Vec>; - fn get_owned(&self) -> Arc { Arc::new(self.to_vec()) } + fn get_owned(&self) -> Rc { Rc::new(self.to_vec()) } +} +impl Internable for [Tok; N] { + type Interned = Vec>; + fn get_owned(&self) -> Rc { Rc::new(self.to_vec()) } } impl Internable for Vec> { type Interned = Vec>; - fn get_owned(&self) -> Arc { Arc::new(self.to_vec()) } + fn get_owned(&self) -> Rc { Rc::new(self.to_vec()) } } // impl Internable for Vec { // type Interned = Vec>; @@ -167,14 +162,14 @@ const BASE_RC: usize = 3; #[test] fn base_rc_correct() { - let tok = Tok::new(Arc::new("foo".to_string()), api::TStr(1.try_into().unwrap())); + let tok = Tok::new(Rc::new("foo".to_string()), api::TStr(1.try_into().unwrap())); let mut bimap = Bimap::default(); bimap.insert(tok.clone()); - assert_eq!(Arc::strong_count(&tok.data), BASE_RC + 1, "the bimap plus the current instance"); + assert_eq!(Rc::strong_count(&tok.data), BASE_RC + 1, "the bimap plus the current instance"); } pub struct Bimap { - intern: HashMap, Tok>, + intern: HashMap, Tok>, by_id: HashMap>, } impl Bimap { @@ -194,7 +189,7 @@ impl Bimap { pub fn sweep_replica(&mut self) -> Vec { (self.intern) - .extract_if(|k, _| Arc::strong_count(k) == BASE_RC) + .extract_if(|k, _| Rc::strong_count(k) == BASE_RC) .map(|(_, v)| { self.by_id.remove(&v.to_api()); v.to_api() @@ -203,7 +198,7 @@ impl Bimap { } pub fn sweep_master(&mut self, retained: HashSet) { - self.intern.retain(|k, v| BASE_RC < Arc::strong_count(k) || retained.contains(&v.to_api())) + self.intern.retain(|k, v| BASE_RC < Rc::strong_count(k) || retained.contains(&v.to_api())) } } @@ -223,58 +218,59 @@ pub struct TypedInterners { #[derive(Default)] pub struct Interner { - interners: TypedInterners, + interners: Mutex, master: Option>>, } impl Interner { pub fn new_master() -> Self { Self::default() } pub fn new_replica(req: impl DynRequester + 'static) -> Self { - Self { - master: Some(Box::new(req)), - interners: TypedInterners { strings: Bimap::default(), vecs: Bimap::default() }, - } + Self { master: Some(Box::new(req)), interners: Mutex::default() } } - pub async fn intern( - &mut self, - t: &(impl Internable + ?Sized), - ) -> Tok { + /// Intern some data; query its identifier if not known locally + pub async fn i(&self, t: &(impl Internable + ?Sized)) -> Tok { let data = t.get_owned(); let job = format!("{t:?} in {}", if self.master.is_some() { "replica" } else { "master" }); eprintln!("Interning {job}"); - let typed = T::bimap(&mut self.interners); + let mut g = self.interners.lock().await; + let typed = T::bimap(&mut g); if let Some(tok) = typed.by_value(&data) { return tok; } - let marker = match &mut self.master { + let marker = match &self.master { Some(c) => data.clone().intern(&**c).await, None => T::Marker::from_id(NonZeroU64::new(ID.fetch_add(1, atomic::Ordering::Relaxed)).unwrap()), }; let tok = Tok::new(data, marker); - T::bimap(&mut self.interners).insert(tok.clone()); + T::bimap(&mut g).insert(tok.clone()); eprintln!("Interned {job}"); tok } - async fn deintern(&mut self, marker: M) -> Tok { - if let Some(tok) = M::Interned::bimap(&mut self.interners).by_marker(marker) { + /// Extern an identifier; query the data it represents if not known locally + async fn ex(&self, marker: M) -> Tok { + if let Some(tok) = M::Interned::bimap(&mut *self.interners.lock().await).by_marker(marker) { return tok; } - let master = self.master.as_mut().expect("ID not in local interner and this is master"); - let token = marker.resolve(&**master).await; - M::Interned::bimap(&mut self.interners).insert(token.clone()); + assert!(self.master.is_some(), "ID not in local interner and this is master"); + let token = marker.resolve(self).await; + M::Interned::bimap(&mut *self.interners.lock().await).insert(token.clone()); token } - pub fn sweep_replica(&mut self) -> api::Retained { + pub async fn sweep_replica(&self) -> api::Retained { assert!(self.master.is_some(), "Not a replica"); - api::Retained { - strings: self.interners.strings.sweep_replica(), - vecs: self.interners.vecs.sweep_replica(), - } + let mut g = self.interners.lock().await; + api::Retained { strings: g.strings.sweep_replica(), vecs: g.vecs.sweep_replica() } } - pub fn sweep_master(&mut self, retained: api::Retained) { + pub async fn sweep_master(&self, retained: api::Retained) { assert!(self.master.is_none(), "Not master"); - self.interners.strings.sweep_master(retained.strings.into_iter().collect()); - self.interners.vecs.sweep_master(retained.vecs.into_iter().collect()); + let mut g = self.interners.lock().await; + g.strings.sweep_master(retained.strings.into_iter().collect()); + g.vecs.sweep_master(retained.vecs.into_iter().collect()); + } +} +impl fmt::Debug for Interner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Interner{{ replica: {} }}", self.master.is_none()) } } @@ -297,11 +293,9 @@ mod test { #[test] fn test_i() { - let _: Tok = spin_on(intern!(str: "foo")); - let _: Tok>> = spin_on(intern!([Tok]: &[ - spin_on(intern!(str: "bar")), - spin_on(intern!(str: "baz")) - ])); + let i = Interner::new_master(); + let _: Tok = spin_on(i.i("foo")); + let _: Tok>> = spin_on(i.i(&[spin_on(i.i("bar")), spin_on(i.i("baz"))])); } #[test] diff --git a/orchid-base/src/location.rs b/orchid-base/src/location.rs index 7d8afb9..16b002e 100644 --- a/orchid-base/src/location.rs +++ b/orchid-base/src/location.rs @@ -6,9 +6,9 @@ use std::ops::Range; use trait_set::trait_set; -use crate::interner::{Tok, intern}; +use crate::interner::{Interner, Tok}; use crate::name::Sym; -use crate::{api, intern, match_mapping, sym}; +use crate::{api, match_mapping, sym}; trait_set! { pub trait GetSrc = FnMut(&Sym) -> Tok; @@ -36,12 +36,12 @@ impl Pos { other => format!("{other:?}"), } } - pub async fn from_api(api: &api::Location) -> Self { + pub async fn from_api(api: &api::Location, i: &Interner) -> Self { match_mapping!(api, api::Location => Pos { None, Inherit, SlotTarget, Range(r.clone()), - Gen(cgi => CodeGenInfo::from_api(cgi).await), - SourceRange(sr => SourceRange::from_api(sr).await) + Gen(cgi => CodeGenInfo::from_api(cgi, i).await), + SourceRange(sr => SourceRange::from_api(sr, i).await) }) } pub fn to_api(&self) -> api::Location { @@ -67,7 +67,7 @@ impl SourceRange { } /// Create a dud [SourceRange] for testing. Its value is unspecified and /// volatile. - pub async fn mock() -> Self { Self { range: 0..1, path: sym!(test).await } } + pub async fn mock(i: &Interner) -> Self { Self { range: 0..1, path: sym!(test; i).await } } /// Path the source text was loaded from pub fn path(&self) -> Sym { self.path.clone() } /// Byte range @@ -92,8 +92,8 @@ impl SourceRange { } } pub fn zw(path: Sym, pos: u32) -> Self { Self { path, range: pos..pos } } - async fn from_api(api: &api::SourceRange) -> Self { - Self { path: Sym::from_api(api.path).await, range: api.range.clone() } + async fn from_api(api: &api::SourceRange, i: &Interner) -> Self { + Self { path: Sym::from_api(api.path, i).await, range: api.range.clone() } } fn to_api(&self) -> api::SourceRange { api::SourceRange { path: self.path.to_api(), range: self.range.clone() } @@ -110,19 +110,19 @@ pub struct CodeGenInfo { } impl CodeGenInfo { /// A codegen marker with no user message and parameters - pub async fn new_short(generator: Sym) -> Self { - Self { generator, details: intern!(str: "").await } + pub async fn new_short(generator: Sym, i: &Interner) -> Self { + Self { generator, details: i.i("").await } } /// A codegen marker with a user message or parameters - pub async fn new_details(generator: Sym, details: impl AsRef) -> Self { - Self { generator, details: intern(details.as_ref()).await } + pub async fn new_details(generator: Sym, details: impl AsRef, i: &Interner) -> Self { + Self { generator, details: i.i(details.as_ref()).await } } /// Syntactic location pub fn pos(&self) -> Pos { Pos::Gen(self.clone()) } - pub async fn from_api(api: &api::CodeGenInfo) -> Self { + pub async fn from_api(api: &api::CodeGenInfo, i: &Interner) -> Self { Self { - generator: Sym::from_api(api.generator).await, - details: Tok::from_api(api.details).await, + generator: Sym::from_api(api.generator, i).await, + details: Tok::from_api(api.details, i).await, } } pub fn to_api(&self) -> api::CodeGenInfo { diff --git a/orchid-base/src/macros.rs b/orchid-base/src/macros.rs index e73005b..ad8b576 100644 --- a/orchid-base/src/macros.rs +++ b/orchid-base/src/macros.rs @@ -3,11 +3,12 @@ use std::sync::Arc; use async_std::stream; use async_std::sync::Mutex; +use futures::future::LocalBoxFuture; use futures::{FutureExt, StreamExt}; -use itertools::Itertools; use never::Never; use trait_set::trait_set; +use crate::interner::Interner; use crate::location::Pos; use crate::name::Sym; use crate::tree::{Paren, Ph}; @@ -20,7 +21,7 @@ impl MacroSlot<'_> { } trait_set! { - pub trait MacroAtomToApi = FnMut(&A) -> api::MacroToken; + pub trait MacroAtomToApi = for<'a> FnMut(&'a A) -> LocalBoxFuture<'a, api::MacroToken>; pub trait MacroAtomFromApi<'a, A> = FnMut(&api::Atom) -> MTok<'a, A>; } @@ -33,14 +34,18 @@ impl<'a, A> MTree<'a, A> { pub(crate) async fn from_api( api: &api::MacroTree, do_atom: &mut impl MacroAtomFromApi<'a, A>, + i: &Interner, ) -> Self { Self { - pos: Pos::from_api(&api.location).await, - tok: Arc::new(MTok::from_api(&api.token, do_atom).await), + pos: Pos::from_api(&api.location, i).await, + tok: Arc::new(MTok::from_api(&api.token, do_atom, i).await), } } - pub(crate) fn to_api(&self, do_atom: &mut impl MacroAtomToApi) -> api::MacroTree { - api::MacroTree { location: self.pos.to_api(), token: self.tok.to_api(do_atom) } + pub(crate) async fn to_api(&self, do_atom: &mut impl MacroAtomToApi) -> api::MacroTree { + api::MacroTree { + location: self.pos.to_api(), + token: self.tok.to_api(do_atom).boxed_local().await, + } } } @@ -62,29 +67,30 @@ impl<'a, A> MTok<'a, A> { pub(crate) async fn from_api( api: &api::MacroToken, do_atom: &mut impl MacroAtomFromApi<'a, A>, + i: &Interner, ) -> Self { match_mapping!(&api, api::MacroToken => MTok::<'a, A> { - Lambda(x => mtreev_from_api(x, do_atom).await, b => mtreev_from_api(b, do_atom).await), - Name(t => Sym::from_api(*t).await), + Lambda(x => mtreev_from_api(x, do_atom, i).await, b => mtreev_from_api(b, do_atom, i).await), + Name(t => Sym::from_api(*t, i).await), Slot(tk => MacroSlot(*tk, PhantomData)), - S(p.clone(), b => mtreev_from_api(b, do_atom).await), - Ph(ph => Ph::from_api(ph).await), + S(p.clone(), b => mtreev_from_api(b, do_atom, i).await), + Ph(ph => Ph::from_api(ph, i).await), } { api::MacroToken::Atom(a) => do_atom(a) }) } - pub(crate) fn to_api(&self, do_atom: &mut impl MacroAtomToApi) -> api::MacroToken { - fn sink(n: &Never) -> api::MacroToken { match *n {} } + pub(crate) async fn to_api(&self, do_atom: &mut impl MacroAtomToApi) -> api::MacroToken { + fn sink(n: &Never) -> LocalBoxFuture<'_, T> { match *n {} } match_mapping!(&self, MTok => api::MacroToken { - Lambda(x => mtreev_to_api(x, do_atom), b => mtreev_to_api(b, do_atom)), + Lambda(x => mtreev_to_api(x, do_atom).await, b => mtreev_to_api(b, do_atom).await), Name(t.tok().to_api()), Ph(ph.to_api()), - S(p.clone(), b => mtreev_to_api(b, do_atom)), + S(p.clone(), b => mtreev_to_api(b, do_atom).await), Slot(tk.0.clone()), } { - MTok::Ref(r) => r.to_api(&mut sink), - MTok::Done(t) => t.to_api(do_atom), - MTok::Atom(a) => do_atom(a), + MTok::Ref(r) => r.to_api(&mut sink).boxed_local().await, + MTok::Done(t) => t.to_api(do_atom).boxed_local().await, + MTok::Atom(a) => do_atom(a).await, }) } pub fn at(self, pos: Pos) -> MTree<'a, A> { MTree { pos, tok: Arc::new(self) } } @@ -93,17 +99,24 @@ impl<'a, A> MTok<'a, A> { pub async fn mtreev_from_api<'a, 'b, A>( api: impl IntoIterator, do_atom: &mut impl MacroAtomFromApi<'a, A>, + i: &Interner, ) -> Vec> { let do_atom_lk = Mutex::new(do_atom); stream::from_iter(api) - .then(|api| async { MTree::from_api(api, &mut *do_atom_lk.lock().await).boxed_local().await }) + .then(|api| async { + MTree::from_api(api, &mut *do_atom_lk.lock().await, i).boxed_local().await + }) .collect() .await } -pub fn mtreev_to_api<'a: 'b, 'b, A: 'b>( +pub async fn mtreev_to_api<'a: 'b, 'b, A: 'b>( v: impl IntoIterator>, do_atom: &mut impl MacroAtomToApi, ) -> Vec { - v.into_iter().map(|t| t.to_api(do_atom)).collect_vec() + let mut out = Vec::new(); + for t in v { + out.push(t.to_api(do_atom).await); + } + out } diff --git a/orchid-base/src/match_mapping.rs b/orchid-base/src/match_mapping.rs index f5ddfc0..799ab46 100644 --- a/orchid-base/src/match_mapping.rs +++ b/orchid-base/src/match_mapping.rs @@ -7,7 +7,7 @@ /// match_mapping!(self, ThisType => OtherType { /// EmptyVariant, /// TupleVariant(foo => intern(foo), bar.clone()), -/// StructVariant{ a.to_api(), b => b.} +/// StructVariant{ a.to_api(), b } /// }) /// ``` #[macro_export] @@ -69,6 +69,9 @@ macro_rules! match_mapping { $($prefix)::* :: $variant { $($names),* } }; (@PAT_MUNCH $ctx:tt $names:tt $(,)? ) => { match_mapping!($ctx $names) }; + (@PAT_MUNCH $ctx:tt ($($names:ident)*) $name:ident , $($tail:tt)*) => { + match_mapping!(@PAT_MUNCH $ctx ($($names)* $name) $($tail)*) + }; (@PAT_MUNCH $ctx:tt ($($names:ident)*) * $name:ident , $($tail:tt)*) => { match_mapping!(@PAT_MUNCH $ctx ($($names)* $name) $($tail)*) }; @@ -94,6 +97,9 @@ macro_rules! match_mapping { (@VAL $prefix:tt { $($fields:tt)* }) => { match_mapping!(@VAL_MUNCH ({} $prefix) () $($fields)* , ) }; + (@VAL_MUNCH $ctx:tt ($($prefix:tt)*) $name:ident , $($tail:tt)*) => { + match_mapping!(@VAL_MUNCH $ctx ($($prefix)* ($name ($name)) ) $($tail)*) + }; (@VAL_MUNCH $ctx:tt ($($prefix:tt)*) * $name:ident , $($tail:tt)*) => { match_mapping!(@VAL_MUNCH $ctx ($($prefix)* ($name (* $name)) ) $($tail)*) }; diff --git a/orchid-base/src/name.rs b/orchid-base/src/name.rs index 14c290e..7bc2810 100644 --- a/orchid-base/src/name.rs +++ b/orchid-base/src/name.rs @@ -13,7 +13,7 @@ use itertools::Itertools; use trait_set::trait_set; use crate::api; -use crate::interner::{InternMarker, Tok, intern}; +use crate::interner::{InternMarker, Interner, Tok}; trait_set! { /// Traits that all name iterators should implement @@ -174,8 +174,8 @@ impl VPath { Self(self.0.into_iter().chain(items).collect()) } /// Partition the string by `::` namespace separators - pub async fn parse(s: &str) -> Self { - Self(if s.is_empty() { vec![] } else { join_all(s.split("::").map(intern)).await }) + pub async fn parse(s: &str, i: &Interner) -> Self { + Self(if s.is_empty() { vec![] } else { join_all(s.split("::").map(|s| i.i(s))).await }) } /// Walk over the segments pub fn str_iter(&self) -> impl Iterator { @@ -195,14 +195,15 @@ impl VPath { } /// Convert a fs path to a vpath - pub async fn from_path(path: &Path, ext: &str) -> Option<(Self, bool)> { - async fn to_vpath(p: &Path) -> Option { - let tok_opt_v = join_all(p.iter().map(|c| OptionFuture::from(c.to_str().map(intern)))).await; + pub async fn from_path(path: &Path, ext: &str, i: &Interner) -> Option<(Self, bool)> { + async fn to_vpath(p: &Path, i: &Interner) -> Option { + let tok_opt_v = + join_all(p.iter().map(|c| OptionFuture::from(c.to_str().map(|s| i.i(s))))).await; tok_opt_v.into_iter().collect::>().map(VPath) } match path.extension().map(|s| s.to_str()) { - Some(Some(s)) if s == ext => Some((to_vpath(&path.with_extension("")).await?, true)), - None => Some((to_vpath(path).await?, false)), + Some(Some(s)) if s == ext => Some((to_vpath(&path.with_extension(""), i).await?, true)), + None => Some((to_vpath(path, i).await?, false)), Some(_) => None, } } @@ -259,8 +260,11 @@ impl VName { let data: Vec<_> = items.into_iter().collect(); if data.is_empty() { Err(EmptyNameError) } else { Ok(Self(data)) } } - pub async fn deintern(name: impl IntoIterator) -> Result { - Self::new(join_all(name.into_iter().map(Tok::from_api)).await) + pub async fn deintern( + name: impl IntoIterator, + i: &Interner, + ) -> Result { + Self::new(join_all(name.into_iter().map(|m| Tok::from_api(m, i))).await) } /// Unwrap the enclosed vector pub fn into_vec(self) -> Vec> { self.0 } @@ -270,7 +274,7 @@ impl VName { /// must never be empty. pub fn vec_mut(&mut self) -> &mut Vec> { &mut self.0 } /// Intern the name and return a [Sym] - pub async fn to_sym(&self) -> Sym { Sym(intern(&self.0[..]).await) } + pub async fn to_sym(&self, i: &Interner) -> Sym { Sym(i.i(&self.0[..]).await) } /// If this name has only one segment, return it pub fn as_root(&self) -> Option> { self.0.iter().exactly_one().ok().cloned() } /// Prepend the segments to this name @@ -284,8 +288,12 @@ impl VName { Self(self.0.into_iter().chain(items).collect()) } /// Read a `::` separated namespaced name - pub async fn parse(s: &str) -> Result { Self::new(VPath::parse(s).await) } - pub async fn literal(s: &'static str) -> Self { Self::parse(s).await.expect("empty literal !?") } + pub async fn parse(s: &str, i: &Interner) -> Result { + Self::new(VPath::parse(s, i).await) + } + pub async fn literal(s: &'static str, i: &Interner) -> Self { + Self::parse(s, i).await.expect("empty literal !?") + } /// Obtain an iterator over the segments of the name pub fn iter(&self) -> impl Iterator> + '_ { self.0.iter().cloned() } } @@ -341,13 +349,16 @@ pub struct Sym(Tok>>); impl Sym { /// Assert that the sequence isn't empty, intern it and wrap it in a [Sym] to /// represent this invariant - pub async fn new(v: impl IntoIterator>) -> Result { + pub async fn new( + v: impl IntoIterator>, + i: &Interner, + ) -> Result { let items = v.into_iter().collect_vec(); - Self::from_tok(intern(&items[..]).await) + Self::from_tok(i.i(&items).await) } /// Read a `::` separated namespaced name. - pub async fn parse(s: &str) -> Result { - Ok(Sym(intern(&VName::parse(s).await?.into_vec()[..]).await)) + pub async fn parse(s: &str, i: &Interner) -> Result { + Ok(Sym(i.i(&VName::parse(s, i).await?.into_vec()).await)) } /// Assert that a token isn't empty, and wrap it in a [Sym] pub fn from_tok(t: Tok>>) -> Result { @@ -359,8 +370,8 @@ impl Sym { pub fn id(&self) -> NonZeroU64 { self.0.to_api().get_id() } /// Extern the sym for editing pub fn to_vname(&self) -> VName { VName(self[..].to_vec()) } - pub async fn from_api(marker: api::TStrv) -> Sym { - Self::from_tok(Tok::from_api(marker).await).expect("Empty sequence found for serialized Sym") + pub async fn from_api(marker: api::TStrv, i: &Interner) -> Sym { + Self::from_tok(Tok::from_api(marker, i).await).expect("Empty sequence found for serialized Sym") } pub fn to_api(&self) -> api::TStrv { self.tok().to_api() } } @@ -439,11 +450,11 @@ impl NameLike for VName {} /// cloning the token. #[macro_export] macro_rules! sym { - ($seg1:tt $( :: $seg:tt)*) => { async { + ($seg1:tt $( :: $seg:tt)* ; $i:expr) => { async { $crate::name::Sym::from_tok( - $crate::intern!([$crate::interner::Tok]: &[ - $crate::intern!(str: stringify!($seg1)).await - $( , $crate::intern!(str: stringify!($seg)).await )* + $i.i(&[ + $i.i(stringify!($seg1)).await + $( , $i.i(stringify!($seg)).await )* ]) .await ).unwrap() @@ -457,10 +468,10 @@ macro_rules! sym { /// The components are interned much like in [sym]. #[macro_export] macro_rules! vname { - ($seg1:tt $( :: $seg:tt)*) => { async { + ($seg1:tt $( :: $seg:tt)* ; $i:expr) => { async { $crate::name::VName::new([ - $crate::intern!(str: stringify!($seg1)).await - $( , $crate::intern!(str: stringify!($seg)).await )* + $i.i(stringify!($seg1)).await + $( , $i.i(stringify!($seg)).await )* ]).unwrap() } }; } @@ -470,10 +481,10 @@ macro_rules! vname { /// The components are interned much like in [sym]. #[macro_export] macro_rules! vpath { - ($seg1:tt $( :: $seg:tt)+) => { async { + ($seg1:tt $( :: $seg:tt)+ ; $i:expr) => { async { $crate::name::VPath(vec![ - $crate::intern!(str: stringify!($seg1)).await - $( , $crate::intern!(str: stringify!($seg)).await )+ + $i.i(stringify!($seg1)).await + $( , $i.i(stringify!($seg)).await )+ ]) } }; () => { @@ -481,46 +492,6 @@ macro_rules! vpath { } } -/// Create a &[PathSlice] literal. -/// -/// The components are interned much like in [sym] -#[macro_export] -macro_rules! with_path_slice { - (@UNIT $tt:tt) => { () }; - ($seg1:tt $( :: $seg:tt)* in $expr:expr) => { { - use std::future::Future; - use std::ops::Deref as _; - use std::pin::Pin; - - const fn count_helper(_: [(); N]) -> usize { N } - - type Output = [Tok; const { - count_helper([() $(, $crate::with_path_slice!(@UNIT $seg))*]) - }]; - type InternFuture = Pin>>; - thread_local! { - static VALUE: Pin>> = - std::rc::Rc::pin(async_once_cell::Lazy::new(Box::pin(async { - [ - $crate::intern!(str: stringify!($seg1)).await - $( , $crate::intern!(str: stringify!($seg)).await )+ - ] - }))); - } - VALUE.with(|v| $crate::clone!(v; async move { - let expr = $expr; - let result = v.as_ref().await; - let ps: &PathSlice = $crate::name::PathSlice::new(&result.deref()[..]); - (expr)(ps).await - })) - - } }; - ($expr:expr) => { - let expr = $expr; - (expr)($crate::name::PathSlice::new(&[])) - } -} - #[cfg(test)] mod test { use std::borrow::Borrow; @@ -528,13 +499,14 @@ mod test { use test_executors::spin_on; use super::{PathSlice, Sym, VName}; - use crate::interner::{Tok, intern}; + use crate::interner::{Interner, Tok}; use crate::name::VPath; #[test] fn recur() { spin_on(async { - let myname = vname!(foo::bar).await; + let i = Interner::new_master(); + let myname = vname!(foo::bar; i).await; let _borrowed_slice: &[Tok] = myname.borrow(); let _borrowed_pathslice: &PathSlice = myname.borrow(); let _deref_pathslice: &PathSlice = &myname; @@ -545,25 +517,19 @@ mod test { #[test] fn literals() { spin_on(async { + let i = Interner::new_master(); assert_eq!( - sym!(foo::bar::baz).await, - Sym::new([intern("foo").await, intern("bar").await, intern("baz").await]).await.unwrap() + sym!(foo::bar::baz; i).await, + Sym::new([i.i("foo").await, i.i("bar").await, i.i("baz").await], &i).await.unwrap() ); assert_eq!( - vname!(foo::bar::baz).await, - VName::new([intern("foo").await, intern("bar").await, intern("baz").await]).unwrap() + vname!(foo::bar::baz; i).await, + VName::new([i.i("foo").await, i.i("bar").await, i.i("baz").await]).unwrap() ); assert_eq!( - vpath!(foo::bar::baz).await, - VPath::new([intern("foo").await, intern("bar").await, intern("baz").await]) + vpath!(foo::bar::baz; i).await, + VPath::new([i.i("foo").await, i.i("bar").await, i.i("baz").await]) ); - with_path_slice!(foo::bar::baz in |val| async move { - assert_eq!( - val, - PathSlice::new(&[intern("foo").await, intern("bar").await, intern("baz").await]) - ); - }) - .await }) } } diff --git a/orchid-base/src/number.rs b/orchid-base/src/number.rs index 9c19be8..527da4a 100644 --- a/orchid-base/src/number.rs +++ b/orchid-base/src/number.rs @@ -6,7 +6,7 @@ use ordered_float::NotNan; use rust_decimal::Decimal; use crate::error::{OrcErr, mk_err}; -use crate::intern; +use crate::interner::Interner; use crate::location::Pos; /// A number, either floating point or unsigned int, parsed by Orchid. @@ -63,9 +63,9 @@ pub struct NumError { pub kind: NumErrorKind, } -pub async fn num_to_err(NumError { kind, range }: NumError, offset: u32) -> OrcErr { +pub async fn num_to_err(NumError { kind, range }: NumError, offset: u32, i: &Interner) -> OrcErr { mk_err( - intern!(str: "Failed to parse number").await, + i.i("Failed to parse number").await, match kind { NumErrorKind::NaN => "NaN emerged during parsing", NumErrorKind::InvalidDigit => "non-digit character encountered", diff --git a/orchid-base/src/parse.rs b/orchid-base/src/parse.rs index e4558c5..8104424 100644 --- a/orchid-base/src/parse.rs +++ b/orchid-base/src/parse.rs @@ -4,12 +4,12 @@ use std::ops::{Deref, Range}; use futures::future::join_all; use itertools::Itertools; +use crate::api; use crate::error::{OrcRes, Reporter, mk_err, mk_errv}; -use crate::interner::{Tok, intern}; +use crate::interner::{Internable, Interned, Interner, Tok}; use crate::location::Pos; use crate::name::VPath; use crate::tree::{AtomRepr, ExtraTok, Paren, TokTree, Token}; -use crate::{api, intern}; pub fn name_start(c: char) -> bool { c.is_alphabetic() || c == '_' } pub fn name_char(c: char) -> bool { name_start(c) || c.is_numeric() } @@ -20,15 +20,24 @@ pub fn unrep_space(c: char) -> bool { c.is_whitespace() && !"\r\n".contains(c) } pub struct Snippet<'a, 'b, A: AtomRepr, X: ExtraTok> { prev: &'a TokTree<'b, A, X>, cur: &'a [TokTree<'b, A, X>], + interner: &'a Interner, } impl<'a, 'b, A: AtomRepr, X: ExtraTok> Snippet<'a, 'b, A, X> { - pub fn new(prev: &'a TokTree<'b, A, X>, cur: &'a [TokTree<'b, A, X>]) -> Self { - Self { prev, cur } + pub fn new( + prev: &'a TokTree<'b, A, X>, + cur: &'a [TokTree<'b, A, X>], + interner: &'a Interner, + ) -> Self { + Self { prev, cur, interner } + } + pub async fn i(&self, arg: &(impl Internable + ?Sized)) -> Tok { + self.interner.i(arg).await } pub fn split_at(self, pos: u32) -> (Self, Self) { - let fst = Self { prev: self.prev, cur: &self.cur[..pos as usize] }; + let Self { prev, cur, interner } = self; + let fst = Self { prev, cur: &cur[..pos as usize], interner }; let new_prev = if pos == 0 { self.prev } else { &self.cur[pos as usize - 1] }; - let snd = Self { prev: new_prev, cur: &self.cur[pos as usize..] }; + let snd = Self { prev: new_prev, cur: &self.cur[pos as usize..], interner }; (fst, snd) } pub fn find_idx(self, mut f: impl FnMut(&Token<'b, A, X>) -> bool) -> Option { @@ -101,8 +110,8 @@ impl Comment { pub fn to_api(&self) -> api::Comment { api::Comment { location: self.pos.to_api(), text: self.text.to_api() } } - pub async fn from_api(api: &api::Comment) -> Self { - Self { pos: Pos::from_api(&api.location).await, text: Tok::from_api(api.text).await } + pub async fn from_api(api: &api::Comment, i: &Interner) -> Self { + Self { pos: Pos::from_api(&api.location, i).await, text: Tok::from_api(api.text, i).await } } } @@ -124,7 +133,7 @@ pub async fn line_items<'a, 'b, A: AtomRepr, X: ExtraTok>( let comments = join_all(comments.drain(..).chain(cmts.cur).map(|t| async { match &t.tok { Token::Comment(c) => - Comment { text: intern(&**c).await, pos: Pos::Range(t.range.clone()) }, + Comment { text: tail.i(&**c).await, pos: Pos::Range(t.range.clone()) }, _ => unreachable!("All are comments checked above"), } })) @@ -141,18 +150,17 @@ pub async fn try_pop_no_fluff<'a, 'b, A: AtomRepr, X: ExtraTok>( ) -> ParseRes<'a, 'b, &'a TokTree<'b, A, X>, A, X> { match snip.skip_fluff().pop_front() { Some((output, tail)) => Ok(Parsed { output, tail }), - None => - Err(mk_errv(intern!(str: "Unexpected end").await, "Pattern ends abruptly", [Pos::Range( - snip.pos(), - ) - .into()])), + None => Err(mk_errv(snip.i("Unexpected end").await, "Pattern ends abruptly", [Pos::Range( + snip.pos(), + ) + .into()])), } } pub async fn expect_end(snip: Snippet<'_, '_, impl AtomRepr, impl ExtraTok>) -> OrcRes<()> { match snip.skip_fluff().get(0) { Some(surplus) => Err(mk_errv( - intern!(str: "Extra code after end of line").await, + snip.i("Extra code after end of line").await, "Code found after the end of the line", [Pos::Range(surplus.range.clone()).into()], )), @@ -168,7 +176,7 @@ pub async fn expect_tok<'a, 'b, A: AtomRepr, X: ExtraTok>( match &head.tok { Token::Name(n) if *n == tok => Ok(Parsed { output: (), tail }), t => Err(mk_errv( - intern!(str: "Expected specific keyword").await, + snip.i("Expected specific keyword").await, format!("Expected {tok} but found {t}"), [Pos::Range(head.range.clone()).into()], )), @@ -192,11 +200,11 @@ pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>( ctx: &impl Reporter, tail: Snippet<'a, 'b, A, X>, ) -> ParseRes<'a, 'b, Vec<(Vec>, Option>, Pos)>, A, X> { - let comma = intern!(str: ",").await; - let globstar = intern!(str: "*").await; + let comma = tail.i(",").await; + let globstar = tail.i("*").await; let Some((name, tail)) = tail.skip_fluff().pop_front() else { return Err(mk_errv( - intern!(str: "Expected name").await, + tail.i("Expected name").await, "Expected a name, a list of names, or a globstar.", [Pos::Range(tail.pos()).into()], )); @@ -204,10 +212,9 @@ pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>( if let Some((Token::NS, tail)) = tail.skip_fluff().pop_front().map(|(tt, s)| (&tt.tok, s)) { let n = match &name.tok { Token::Name(n) if n.starts_with(name_start) => Ok(n), - _ => - Err(mk_err(intern!(str: "Unexpected name prefix").await, "Only names can precede ::", [ - Pos::Range(name.range.clone()).into(), - ])), + _ => Err(mk_err(tail.i("Unexpected name prefix").await, "Only names can precede ::", [ + Pos::Range(name.range.clone()).into(), + ])), }; match (Box::pin(rec(ctx, tail)).await, n) { (Err(ev), n) => Err(ev.extended(n.err())), @@ -227,7 +234,7 @@ pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>( n if *n == globstar => None, n if n.starts_with(op_char) => { return Err(mk_errv( - intern!(str: "Unescaped operator in multiname").await, + tail.i("Unescaped operator in multiname").await, "Operators in multinames should be enclosed in []", [Pos::Range(name.range.clone()).into()], )); @@ -244,7 +251,7 @@ pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>( ok.push((vec![], Some(n.clone()), Pos::Range(tt.range.clone()))), Token::BR | Token::Comment(_) => (), _ => ctx.report(mk_err( - intern!(str: "Non-operator in escapement in multiname").await, + tail.i("Non-operator in escapement in multiname").await, "In multinames, [] functions as a literal name list reserved for operators", [Pos::Range(name.range.clone()).into()], )), @@ -254,14 +261,14 @@ pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>( }, Token::S(Paren::Round, b) => { let mut ok = Vec::new(); - let body = Snippet::new(name, b); + let body = Snippet::new(name, b, tail.interner); for csent in body.split(|n| matches!(n, Token::Name(n) if *n == comma)) { match Box::pin(rec(ctx, csent)).await { Err(e) => ctx.report(e), Ok(Parsed { output, tail }) => match tail.get(0) { None => ok.extend(output), Some(t) => ctx.report(mk_err( - intern!(str: "Unexpected token in multiname group").await, + tail.i("Unexpected token in multiname group").await, "Unexpected token. Likely missing a :: or , or wanted [] instead of ()", [Pos::Range(t.range.clone()).into()], )), @@ -272,7 +279,7 @@ pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>( }, t => { return Err(mk_errv( - intern!(str: "Unrecognized name end").await, + tail.i("Unrecognized name end").await, format!("Names cannot end with {t} tokens"), [Pos::Range(name.range.clone()).into()], )); diff --git a/orchid-base/src/reqnot.rs b/orchid-base/src/reqnot.rs index 3fd4913..03309ca 100644 --- a/orchid-base/src/reqnot.rs +++ b/orchid-base/src/reqnot.rs @@ -38,7 +38,11 @@ fn get_id(message: &[u8]) -> (u64, &[u8]) { } pub trait ReqHandlish { - fn defer_drop(&self, val: impl Any + 'static); + fn defer_drop(&self, val: impl Any + 'static) + where Self: Sized { + self.defer_drop_objsafe(Box::new(val)); + } + fn defer_drop_objsafe(&self, val: Box); } #[derive(destructure)] @@ -77,7 +81,7 @@ impl<'a, MS: MsgSet + 'static> RequestHandle<'a, MS> { } } impl ReqHandlish for RequestHandle<'_, MS> { - fn defer_drop(&self, val: impl Any) { self.defer_drop.borrow_mut().push(Box::new(val)) } + fn defer_drop_objsafe(&self, val: Box) { self.defer_drop.borrow_mut().push(val); } } impl Drop for RequestHandle<'_, MS> { fn drop(&mut self) { @@ -212,6 +216,7 @@ impl Clone for ReqNot { #[cfg(test)] mod test { + use std::rc::Rc; use std::sync::Arc; use async_std::sync::Mutex; @@ -270,8 +275,8 @@ mod test { #[test] fn request() { spin_on(async { - let receiver = Arc::new(Mutex::>>::new(None)); - let sender = Arc::new(ReqNot::::new( + let receiver = Rc::new(Mutex::>>::new(None)); + let sender = Rc::new(ReqNot::::new( clone!(receiver; move |d, _| clone!(receiver; Box::pin(async move { receiver.lock().await.as_ref().unwrap().receive(d).await }))), diff --git a/orchid-base/src/tree.rs b/orchid-base/src/tree.rs index e38e5e3..e293cb4 100644 --- a/orchid-base/src/tree.rs +++ b/orchid-base/src/tree.rs @@ -9,14 +9,15 @@ use std::sync::Arc; pub use api::PhKind; use async_std::stream; use async_std::sync::Mutex; -use futures::StreamExt; +use futures::future::LocalBoxFuture; +use futures::{FutureExt, StreamExt}; use itertools::Itertools; use never::Never; use ordered_float::NotNan; use trait_set::trait_set; use crate::error::OrcErrv; -use crate::interner::Tok; +use crate::interner::{Interner, Tok}; use crate::location::Pos; use crate::name::PathSlice; use crate::parse::Snippet; @@ -26,6 +27,8 @@ use crate::{api, match_mapping}; trait_set! { pub trait RecurCB<'a, A: AtomRepr, X: ExtraTok> = Fn(TokTree<'a, A, X>) -> TokTree<'a, A, X>; pub trait ExtraTok = Display + Clone + fmt::Debug; + pub trait RefDoExtra = + for<'b> FnMut(&'b X, Range) -> LocalBoxFuture<'b, api::TokenTree>; } pub fn recur<'a, A: AtomRepr, X: ExtraTok>( @@ -72,41 +75,38 @@ pub struct TokTree<'a, A: AtomRepr, X: ExtraTok> { pub tok: Token<'a, A, X>, pub range: Range, } -impl<'a, A: AtomRepr, X: ExtraTok> TokTree<'a, A, X> { - pub async fn from_api(tt: &api::TokenTree, ctx: &mut A::Ctx) -> Self { - let tok = match_mapping!(&tt.token, api::Token => Token::<'a, A, X> { +impl<'b, A: AtomRepr, X: ExtraTok> TokTree<'b, A, X> { + pub async fn from_api(tt: &api::TokenTree, ctx: &mut A::Ctx, i: &Interner) -> Self { + let tok = match_mapping!(&tt.token, api::Token => Token::<'b, A, X> { BR, NS, Atom(a => A::from_api(a, Pos::Range(tt.range.clone()), ctx)), - Bottom(e => OrcErrv::from_api(e).await), - LambdaHead(arg => ttv_from_api(arg, ctx).await), - Name(n => Tok::from_api(*n).await), - S(*par, b => ttv_from_api(b, ctx).await), + Bottom(e => OrcErrv::from_api(e, i).await), + LambdaHead(arg => ttv_from_api(arg, ctx, i).await), + Name(n => Tok::from_api(*n, i).await), + S(*par, b => ttv_from_api(b, ctx, i).await), Comment(c.clone()), Slot(id => TokHandle::new(*id)), - Ph(ph => Ph::from_api(ph).await), + Ph(ph => Ph::from_api(ph, i).await), Macro(*prio) }); Self { range: tt.range.clone(), tok } } - pub fn to_api( - &self, - do_extra: &mut impl FnMut(&X, Range) -> api::TokenTree, - ) -> api::TokenTree { + pub async fn to_api(&self, do_extra: &mut impl RefDoExtra) -> api::TokenTree { let token = match_mapping!(&self.tok, Token => api::Token { Atom(a.to_api()), BR, NS, Bottom(e.to_api()), Comment(c.clone()), - LambdaHead(arg => ttv_to_api(arg, do_extra)), + LambdaHead(arg => ttv_to_api(arg, do_extra).boxed_local().await), Name(n.to_api()), Slot(tt.ticket()), - S(*p, b => ttv_to_api(b, do_extra)), + S(*p, b => ttv_to_api(b, do_extra).boxed_local().await), Ph(ph.to_api()), Macro(*prio), } { - Token::X(x) => return do_extra(x, self.range.clone()) + Token::X(x) => return do_extra(x, self.range.clone()).await }); api::TokenTree { range: self.range.clone(), token } } @@ -137,8 +137,8 @@ impl<'a, A: AtomRepr, X: ExtraTok> TokTree<'a, A, X> { pub fn as_name(&self) -> Option> { if let Token::Name(n) = &self.tok { Some(n.clone()) } else { None } } - pub fn as_s(&self, par: Paren) -> Option> { - self.tok.as_s(par).map(|slc| Snippet::new(self, slc)) + pub fn as_s<'a>(&'a self, par: Paren, i: &'a Interner) -> Option> { + self.tok.as_s(par).map(|slc| Snippet::new(self, slc, i)) } pub fn lambda(arg: Vec, mut body: Vec) -> Self { let arg_range = ttv_range(&arg); @@ -155,22 +155,27 @@ impl Display for TokTree<'_, A, X> { pub async fn ttv_from_api( tokv: impl IntoIterator>, ctx: &mut A::Ctx, + i: &Interner, ) -> Vec> { let ctx_lk = Mutex::new(ctx); stream::from_iter(tokv.into_iter()) .then(|t| async { let t = t; - TokTree::::from_api(t.borrow(), *ctx_lk.lock().await).await + TokTree::::from_api(t.borrow(), *ctx_lk.lock().await, i).await }) .collect() .await } -pub fn ttv_to_api<'a, A: AtomRepr, X: ExtraTok>( +pub async fn ttv_to_api<'a, A: AtomRepr, X: ExtraTok>( tokv: impl IntoIterator>>, - do_extra: &mut impl FnMut(&X, Range) -> api::TokenTree, + do_extra: &mut impl RefDoExtra, ) -> Vec { - tokv.into_iter().map(|tok| Borrow::>::borrow(&tok).to_api(do_extra)).collect_vec() + let mut output = Vec::new(); + for tok in tokv { + output.push(Borrow::>::borrow(&tok).to_api(do_extra).await) + } + output } pub fn ttv_into_api<'a, A: AtomRepr, X: ExtraTok>( @@ -307,8 +312,8 @@ pub struct Ph { pub kind: PhKind, } impl Ph { - pub async fn from_api(api: &api::Placeholder) -> Self { - Self { name: Tok::from_api(api.name).await, kind: api.kind } + pub async fn from_api(api: &api::Placeholder, i: &Interner) -> Self { + Self { name: Tok::from_api(api.name, i).await, kind: api.kind } } pub fn to_api(&self) -> api::Placeholder { api::Placeholder { name: self.name.to_api(), kind: self.kind } diff --git a/orchid-extension/src/atom.rs b/orchid-extension/src/atom.rs index 86d3abb..1db82f8 100644 --- a/orchid-extension/src/atom.rs +++ b/orchid-extension/src/atom.rs @@ -4,14 +4,17 @@ use std::future::Future; use std::io::{Read, Write}; use std::marker::PhantomData; use std::ops::Deref; -use std::sync::Arc; +use std::rc::Rc; +use ahash::HashMap; +use async_std::stream; use dyn_clone::{DynClone, clone_box}; -use futures::FutureExt; use futures::future::LocalBoxFuture; +use futures::{FutureExt, StreamExt}; use orchid_api_traits::{Coding, Decode, Encode, Request, enc_vec}; +use orchid_base::clone; use orchid_base::error::{OrcErr, OrcRes, mk_err}; -use orchid_base::intern; +use orchid_base::interner::Interner; use orchid_base::location::Pos; use orchid_base::name::Sym; use orchid_base::reqnot::Requester; @@ -21,6 +24,7 @@ use trait_set::trait_set; use crate::api; // use crate::error::{ProjectError, ProjectResult}; use crate::expr::{Expr, ExprData, ExprHandle, ExprKind}; +use crate::gen_expr::GExpr; use crate::system::{DynSystemCard, SysCtx, atom_info_for, downcast_atom}; pub trait AtomCard: 'static + Sized { @@ -31,7 +35,7 @@ pub trait AtomicVariant {} pub trait Atomic: 'static + Sized { type Variant: AtomicVariant; type Data: Clone + Coding + Sized; - fn reg_reqs() -> MethodSet; + fn reg_reqs() -> MethodSetBuilder; } impl AtomCard for A { type Data = ::Data; @@ -74,11 +78,11 @@ pub fn get_info( #[derive(Clone)] pub struct ForeignAtom<'a> { - pub expr: Option>, - pub _life: PhantomData<&'a ()>, - pub ctx: SysCtx, - pub atom: api::Atom, - pub pos: Pos, + pub(crate) expr: Option>, + pub(crate) _life: PhantomData<&'a ()>, + pub(crate) ctx: SysCtx, + pub(crate) atom: api::Atom, + pub(crate) pos: Pos, } impl ForeignAtom<'_> { pub fn ex_opt(self) -> Option { @@ -86,16 +90,18 @@ impl ForeignAtom<'_> { let data = ExprData { pos, kind: ExprKind::Atom(ForeignAtom { _life: PhantomData, ..self }) }; Some(Expr::new(handle, data)) } + pub fn pos(&self) -> Pos { self.pos.clone() } + pub fn ctx(&self) -> SysCtx { self.ctx.clone() } } impl ForeignAtom<'static> { pub fn ex(self) -> Expr { self.ex_opt().unwrap() } - pub(crate) fn new(handle: Arc, atom: api::Atom, pos: Pos) -> Self { + pub(crate) fn new(handle: Rc, atom: api::Atom, pos: Pos) -> Self { ForeignAtom { _life: PhantomData, atom, ctx: handle.ctx.clone(), expr: Some(handle), pos } } pub async fn request(&self, m: M) -> Option { let rep = (self.ctx.reqnot.request(api::Fwd( self.atom.clone(), - Sym::parse(M::NAME).await.unwrap().tok().to_api(), + Sym::parse(M::NAME, &self.ctx.i).await.unwrap().tok().to_api(), enc_vec(&m), ))) .await?; @@ -118,13 +124,18 @@ impl AtomRepr for ForeignAtom<'_> { fn to_api(&self) -> orchid_api::Atom { self.atom.clone() } } -pub struct NotTypAtom(pub Pos, pub Expr, pub Box); +pub struct NotTypAtom { + pub pos: Pos, + pub expr: Expr, + pub typ: Box, + pub ctx: SysCtx, +} impl NotTypAtom { pub async fn mk_err(&self) -> OrcErr { mk_err( - intern!(str: "Not the expected type").await, - format!("This expression is not a {}", self.2.name()), - [self.0.clone().into()], + self.ctx.i.i("Not the expected type").await, + format!("This expression is not a {}", self.typ.name()), + [self.pos.clone().into()], ) } } @@ -142,52 +153,64 @@ trait_set! { SysCtx, &'a mut dyn Read, &'a mut dyn Write - ) -> LocalBoxFuture<'a, ()> + Send + Sync + ) -> LocalBoxFuture<'a, ()> } -pub struct AtomReqHandler { - key: Sym, - cb: Box>, +pub struct MethodSetBuilder { + handlers: Vec<(&'static str, Rc>)>, } - -pub struct MethodSet { - handlers: Vec>, -} -impl MethodSet { +impl MethodSetBuilder { pub fn new() -> Self { Self { handlers: vec![] } } - pub async fn handle(mut self) -> Self + pub fn handle(mut self) -> Self where A: Supports { - self.handlers.push(AtomReqHandler { - key: Sym::parse(M::NAME).await.expect("AtomMethod::NAME cannoot be empty"), - cb: Box::new(move |a: &A, ctx: SysCtx, req: &mut dyn Read, rep: &mut dyn Write| { + assert!(!M::NAME.is_empty(), "AtomMethod::NAME cannoot be empty"); + self.handlers.push(( + M::NAME, + Rc::new(move |a: &A, ctx: SysCtx, req: &mut dyn Read, rep: &mut dyn Write| { async { Supports::::handle(a, ctx, M::decode(req)).await.encode(rep) }.boxed_local() }), - }); + )); self } - pub(crate) fn dispatch<'a>( + pub async fn pack(&self, ctx: SysCtx) -> MethodSet { + MethodSet { + handlers: stream::from_iter(self.handlers.iter()) + .then(|(k, v)| { + clone!(ctx; async move { + (Sym::parse(k, &ctx.i).await.unwrap(), v.clone()) + }) + }) + .collect() + .await, + } + } +} + +pub struct MethodSet { + handlers: HashMap>>, +} +impl MethodSet { + pub(crate) async fn dispatch<'a>( &'a self, atom: &'a A, ctx: SysCtx, key: Sym, req: &'a mut dyn Read, rep: &'a mut dyn Write, - ) -> impl Future + 'a { - async move { - match self.handlers.iter().find(|h| h.key == key) { - None => false, - Some(handler) => { - (handler.cb)(atom, ctx, req, rep).await; - true - }, - } + ) -> bool { + match self.handlers.get(&key) { + None => false, + Some(handler) => { + handler(atom, ctx, req, rep).await; + true + }, } } } -impl Default for MethodSet { +impl Default for MethodSetBuilder { fn default() -> Self { Self::new() } } @@ -197,11 +220,21 @@ pub struct TypAtom<'a, A: AtomicFeatures> { pub value: A::Data, } impl TypAtom<'static, A> { - pub async fn downcast(expr: Arc) -> Result { + pub async fn downcast(expr: Rc) -> Result { match Expr::from_handle(expr).atom().await { - Err(oe) => Err(NotTypAtom(oe.data().await.pos.clone(), oe, Box::new(A::info()))), + Err(expr) => Err(NotTypAtom { + ctx: expr.handle().get_ctx(), + pos: expr.data().await.pos.clone(), + expr, + typ: Box::new(A::info()), + }), Ok(atm) => match downcast_atom::(atm) { - Err(fa) => Err(NotTypAtom(fa.pos.clone(), fa.ex(), Box::new(A::info()))), + Err(fa) => Err(NotTypAtom { + pos: fa.pos.clone(), + ctx: fa.ctx.clone(), + expr: fa.ex(), + typ: Box::new(A::info()), + }), Ok(tatom) => Ok(tatom), }, } @@ -213,7 +246,7 @@ impl TypAtom<'_, A> { M::Response::decode( &mut &(self.data.ctx.reqnot.request(api::Fwd( self.data.atom.clone(), - Sym::parse(M::NAME).await.unwrap().tok().to_api(), + Sym::parse(M::NAME, &self.data.ctx.i).await.unwrap().tok().to_api(), enc_vec(&req), ))) .await @@ -228,12 +261,12 @@ impl Deref for TypAtom<'_, A> { pub struct AtomCtx<'a>(pub &'a [u8], pub Option, pub SysCtx); -pub trait AtomDynfo: Send + Sync + 'static { +pub trait AtomDynfo: 'static { fn tid(&self) -> TypeId; fn name(&self) -> &'static str; fn decode(&self, ctx: AtomCtx<'_>) -> Box; - fn call<'a>(&'a self, ctx: AtomCtx<'a>, arg: api::ExprTicket) -> LocalBoxFuture<'a, Expr>; - fn call_ref<'a>(&'a self, ctx: AtomCtx<'a>, arg: api::ExprTicket) -> LocalBoxFuture<'a, Expr>; + fn call<'a>(&'a self, ctx: AtomCtx<'a>, arg: api::ExprTicket) -> LocalBoxFuture<'a, GExpr>; + fn call_ref<'a>(&'a self, ctx: AtomCtx<'a>, arg: api::ExprTicket) -> LocalBoxFuture<'a, GExpr>; fn print<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, String>; fn handle_req<'a, 'b: 'a, 'c: 'a>( &'a self, @@ -242,7 +275,7 @@ pub trait AtomDynfo: Send + Sync + 'static { req: &'b mut dyn Read, rep: &'c mut dyn Write, ) -> LocalBoxFuture<'a, bool>; - fn command<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, OrcRes>>; + fn command<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, OrcRes>>; fn serialize<'a, 'b: 'a>( &'a self, ctx: AtomCtx<'a>, @@ -258,12 +291,16 @@ pub trait AtomDynfo: Send + Sync + 'static { } trait_set! { - pub trait AtomFactoryFn = FnOnce(SysCtx) -> api::Atom + DynClone; + pub trait AtomFactoryFn = FnOnce(SysCtx) -> LocalBoxFuture<'static, api::Atom> + DynClone; } pub struct AtomFactory(Box); impl AtomFactory { - pub fn new(f: impl FnOnce(SysCtx) -> api::Atom + Clone + 'static) -> Self { Self(Box::new(f)) } - pub fn build(self, ctx: SysCtx) -> api::Atom { (self.0)(ctx) } + pub fn new + 'static>( + f: impl FnOnce(SysCtx) -> F + Clone + 'static, + ) -> Self { + Self(Box::new(|ctx| f(ctx).boxed_local())) + } + pub async fn build(self, ctx: SysCtx) -> api::Atom { (self.0)(ctx).await } } impl Clone for AtomFactory { fn clone(&self) -> Self { AtomFactory(clone_box(&*self.0)) } @@ -275,11 +312,10 @@ impl fmt::Display for AtomFactory { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AtomFactory") } } -pub async fn err_not_callable() -> OrcErr { - mk_err(intern!(str: "This atom is not callable").await, "Attempted to apply value as function", [ - ]) +pub async fn err_not_callable(i: &Interner) -> OrcErr { + mk_err(i.i("This atom is not callable").await, "Attempted to apply value as function", []) } -pub async fn err_not_command() -> OrcErr { - mk_err(intern!(str: "This atom is not a command").await, "Settled on an inactionable value", []) +pub async fn err_not_command(i: &Interner) -> OrcErr { + mk_err(i.i("This atom is not a command").await, "Settled on an inactionable value", []) } diff --git a/orchid-extension/src/atom_owned.rs b/orchid-extension/src/atom_owned.rs index 274cfca..1143343 100644 --- a/orchid-extension/src/atom_owned.rs +++ b/orchid-extension/src/atom_owned.rs @@ -2,8 +2,9 @@ use std::any::{Any, TypeId, type_name}; use std::borrow::Cow; use std::future::Future; use std::io::{Read, Write}; -use std::sync::Arc; +use std::rc::Rc; +use async_once_cell::OnceCell; use futures::FutureExt; use futures::future::{LocalBoxFuture, ready}; use itertools::Itertools; @@ -17,24 +18,25 @@ use orchid_base::name::Sym; use crate::api; use crate::atom::{ AtomCard, AtomCtx, AtomDynfo, AtomFactory, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet, - err_not_callable, err_not_command, get_info, + MethodSetBuilder, err_not_callable, err_not_command, get_info, }; -use crate::expr::{Expr, ExprHandle, bot}; +use crate::expr::{Expr, ExprHandle}; +use crate::gen_expr::{GExpr, bot}; use crate::system::SysCtx; pub struct OwnedVariant; impl AtomicVariant for OwnedVariant {} impl> AtomicFeaturesImpl for A { fn _factory(self) -> AtomFactory { - AtomFactory::new(move |ctx| { + AtomFactory::new(move |ctx| async move { let rec = ctx.obj_store.add(Box::new(self)); let (id, _) = get_info::(ctx.cted.inst().card()); let mut data = enc_vec(&id); - rec.encode(&mut data); + rec.encode(&mut data).await; api::Atom { drop: Some(api::AtomId(rec.id())), data, owner: ctx.id } }) } - fn _info() -> Self::_Info { OwnedAtomDynfo(A::reg_reqs()) } + fn _info() -> Self::_Info { OwnedAtomDynfo { msbuild: A::reg_reqs(), ms: OnceCell::new() } } type _Info = OwnedAtomDynfo; } @@ -46,21 +48,24 @@ fn with_atom<'a, U>( f(ctx.obj_store.get(id.0).unwrap_or_else(|| panic!("Received invalid atom ID: {}", id.0))) } -pub struct OwnedAtomDynfo(MethodSet); +pub struct OwnedAtomDynfo { + msbuild: MethodSetBuilder, + ms: OnceCell>, +} impl AtomDynfo for OwnedAtomDynfo { fn tid(&self) -> TypeId { TypeId::of::() } fn name(&self) -> &'static str { type_name::() } fn decode(&self, AtomCtx(data, ..): AtomCtx) -> Box { Box::new(::Data::decode(&mut &data[..])) } - fn call(&self, AtomCtx(_, id, ctx): AtomCtx, arg: api::ExprTicket) -> LocalBoxFuture<'_, Expr> { + fn call(&self, AtomCtx(_, id, ctx): AtomCtx, arg: api::ExprTicket) -> LocalBoxFuture<'_, GExpr> { with_atom(id.unwrap(), &ctx, |a| a.remove()).dyn_call(ctx.clone(), arg) } fn call_ref<'a>( &'a self, AtomCtx(_, id, ctx): AtomCtx<'a>, arg: api::ExprTicket, - ) -> LocalBoxFuture<'a, Expr> { + ) -> LocalBoxFuture<'a, GExpr> { async move { with_atom(id.unwrap(), &ctx, |a| clone!(ctx; async move { a.dyn_call_ref(ctx, arg).await })) .await @@ -83,7 +88,8 @@ impl AtomDynfo for OwnedAtomDynfo { async move { with_atom(id.unwrap(), &ctx, |a| { clone!(ctx; async move { - self.0.dispatch(a.as_any_ref().downcast_ref().unwrap(), ctx, key, req, rep).await + let ms = self.ms.get_or_init(self.msbuild.pack(ctx.clone())).await; + ms.dispatch(a.as_any_ref().downcast_ref().unwrap(), ctx, key, req, rep).await }) }) .await @@ -93,7 +99,7 @@ impl AtomDynfo for OwnedAtomDynfo { fn command<'a>( &'a self, AtomCtx(_, id, ctx): AtomCtx<'a>, - ) -> LocalBoxFuture<'a, OrcRes>> { + ) -> LocalBoxFuture<'a, OrcRes>> { async move { with_atom(id.unwrap(), &ctx, |a| a.remove().dyn_command(ctx.clone())).await } .boxed_local() } @@ -111,7 +117,7 @@ impl AtomDynfo for OwnedAtomDynfo { id.encode(write); with_atom(id, &ctx, |a| clone!(ctx; async move { a.dyn_serialize(ctx, write).await })) .await - .map(|v| v.into_iter().map(|t| t.handle().unwrap().tk).collect_vec()) + .map(|v| v.into_iter().map(|t| t.handle().tk).collect_vec()) } .boxed_local() } @@ -123,9 +129,9 @@ impl AtomDynfo for OwnedAtomDynfo { ) -> LocalBoxFuture<'a, api::Atom> { async move { let refs = - refs.iter().map(|tk| Expr::from_handle(Arc::new(ExprHandle::from_args(ctx.clone(), *tk)))); + refs.iter().map(|tk| Expr::from_handle(Rc::new(ExprHandle::from_args(ctx.clone(), *tk)))); let obj = T::deserialize(DeserCtxImpl(data, &ctx), T::Refs::from_iter(refs)).await; - obj._factory().build(ctx) + obj._factory().build(ctx).await } .boxed_local() } @@ -197,20 +203,20 @@ pub trait OwnedAtom: Atomic + Any + Clone + 'static { type Refs: RefSet; fn val(&self) -> impl Future>; #[allow(unused_variables)] - fn call_ref(&self, arg: ExprHandle) -> impl Future { - async { bot([err_not_callable().await]) } + fn call_ref(&self, arg: ExprHandle) -> impl Future { + async move { bot([err_not_callable(&arg.ctx.i).await]) } } - fn call(self, arg: ExprHandle) -> impl Future { + fn call(self, arg: ExprHandle) -> impl Future { async { let ctx = arg.get_ctx(); let gcl = self.call_ref(arg).await; - self.free(ctx); + self.free(ctx).await; gcl } } #[allow(unused_variables)] - fn command(self, ctx: SysCtx) -> impl Future>> { - async { Err(err_not_command().await.into()) } + fn command(self, ctx: SysCtx) -> impl Future>> { + async move { Err(err_not_command(&ctx.i).await.into()) } } #[allow(unused_variables)] fn free(self, ctx: SysCtx) -> impl Future { async {} } @@ -245,9 +251,10 @@ pub trait DynOwnedAtom: 'static { fn atom_tid(&self) -> TypeId; fn as_any_ref(&self) -> &dyn Any; fn encode<'a>(&'a self, buffer: &'a mut dyn Write) -> LocalBoxFuture<'a, ()>; - fn dyn_call_ref(&self, ctx: SysCtx, arg: api::ExprTicket) -> LocalBoxFuture<'_, Expr>; - fn dyn_call(self: Box, ctx: SysCtx, arg: api::ExprTicket) -> LocalBoxFuture<'static, Expr>; - fn dyn_command(self: Box, ctx: SysCtx) -> LocalBoxFuture<'static, OrcRes>>; + fn dyn_call_ref(&self, ctx: SysCtx, arg: api::ExprTicket) -> LocalBoxFuture<'_, GExpr>; + fn dyn_call(self: Box, ctx: SysCtx, arg: api::ExprTicket) + -> LocalBoxFuture<'static, GExpr>; + fn dyn_command(self: Box, ctx: SysCtx) -> LocalBoxFuture<'static, OrcRes>>; fn dyn_free(self: Box, ctx: SysCtx) -> LocalBoxFuture<'static, ()>; fn dyn_print(&self, ctx: SysCtx) -> LocalBoxFuture<'_, String>; fn dyn_serialize<'a>( @@ -262,13 +269,17 @@ impl DynOwnedAtom for T { fn encode<'a>(&'a self, buffer: &'a mut dyn Write) -> LocalBoxFuture<'a, ()> { async { self.val().await.as_ref().encode(buffer) }.boxed_local() } - fn dyn_call_ref(&self, ctx: SysCtx, arg: api::ExprTicket) -> LocalBoxFuture<'_, Expr> { + fn dyn_call_ref(&self, ctx: SysCtx, arg: api::ExprTicket) -> LocalBoxFuture<'_, GExpr> { self.call_ref(ExprHandle::from_args(ctx, arg)).boxed_local() } - fn dyn_call(self: Box, ctx: SysCtx, arg: api::ExprTicket) -> LocalBoxFuture<'static, Expr> { + fn dyn_call( + self: Box, + ctx: SysCtx, + arg: api::ExprTicket, + ) -> LocalBoxFuture<'static, GExpr> { self.call(ExprHandle::from_args(ctx, arg)).boxed_local() } - fn dyn_command(self: Box, ctx: SysCtx) -> LocalBoxFuture<'static, OrcRes>> { + fn dyn_command(self: Box, ctx: SysCtx) -> LocalBoxFuture<'static, OrcRes>> { self.command(ctx).boxed_local() } fn dyn_free(self: Box, ctx: SysCtx) -> LocalBoxFuture<'static, ()> { @@ -287,4 +298,4 @@ impl DynOwnedAtom for T { } } -pub type ObjStore = Arc>>; +pub type ObjStore = Rc>>; diff --git a/orchid-extension/src/atom_thin.rs b/orchid-extension/src/atom_thin.rs index c79b245..1e07fa0 100644 --- a/orchid-extension/src/atom_thin.rs +++ b/orchid-extension/src/atom_thin.rs @@ -2,6 +2,7 @@ use std::any::{Any, TypeId, type_name}; use std::future::Future; use std::io::Write; +use async_once_cell::OnceCell; use futures::FutureExt; use futures::future::LocalBoxFuture; use orchid_api_traits::{Coding, enc_vec}; @@ -11,27 +12,31 @@ use orchid_base::name::Sym; use crate::api; use crate::atom::{ AtomCard, AtomCtx, AtomDynfo, AtomFactory, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet, - err_not_callable, err_not_command, get_info, + MethodSetBuilder, err_not_callable, err_not_command, get_info, }; -use crate::expr::{Expr, ExprHandle, bot}; +use crate::expr::ExprHandle; +use crate::gen_expr::{GExpr, bot}; use crate::system::SysCtx; pub struct ThinVariant; impl AtomicVariant for ThinVariant {} impl> AtomicFeaturesImpl for A { fn _factory(self) -> AtomFactory { - AtomFactory::new(move |ctx| { + AtomFactory::new(move |ctx| async move { let (id, _) = get_info::(ctx.cted.inst().card()); let mut buf = enc_vec(&id); self.encode(&mut buf); api::Atom { drop: None, data: buf, owner: ctx.id } }) } - fn _info() -> Self::_Info { ThinAtomDynfo(Self::reg_reqs()) } + fn _info() -> Self::_Info { ThinAtomDynfo { msbuild: Self::reg_reqs(), ms: OnceCell::new() } } type _Info = ThinAtomDynfo; } -pub struct ThinAtomDynfo(MethodSet); +pub struct ThinAtomDynfo { + msbuild: MethodSetBuilder, + ms: OnceCell>, +} impl AtomDynfo for ThinAtomDynfo { fn print<'a>(&self, AtomCtx(buf, _, ctx): AtomCtx<'a>) -> LocalBoxFuture<'a, String> { async move { T::decode(&mut &buf[..]).print(ctx).await }.boxed_local() @@ -43,7 +48,7 @@ impl AtomDynfo for ThinAtomDynfo { &'a self, AtomCtx(buf, _, ctx): AtomCtx<'a>, arg: api::ExprTicket, - ) -> LocalBoxFuture<'a, Expr> { + ) -> LocalBoxFuture<'a, GExpr> { async move { T::decode(&mut &buf[..]).call(ExprHandle::from_args(ctx, arg)).await } .boxed_local() } @@ -51,7 +56,7 @@ impl AtomDynfo for ThinAtomDynfo { &'a self, AtomCtx(buf, _, ctx): AtomCtx<'a>, arg: api::ExprTicket, - ) -> LocalBoxFuture<'a, Expr> { + ) -> LocalBoxFuture<'a, GExpr> { async move { T::decode(&mut &buf[..]).call(ExprHandle::from_args(ctx, arg)).await } .boxed_local() } @@ -62,13 +67,16 @@ impl AtomDynfo for ThinAtomDynfo { req: &'m1 mut dyn std::io::Read, rep: &'m2 mut dyn Write, ) -> LocalBoxFuture<'a, bool> { - async move { self.0.dispatch(&T::decode(&mut &buf[..]), sys, key, req, rep).await } - .boxed_local() + async move { + let ms = self.ms.get_or_init(self.msbuild.pack(sys.clone())).await; + ms.dispatch(&T::decode(&mut &buf[..]), sys, key, req, rep).await + } + .boxed_local() } fn command<'a>( &'a self, AtomCtx(buf, _, ctx): AtomCtx<'a>, - ) -> LocalBoxFuture<'a, OrcRes>> { + ) -> LocalBoxFuture<'a, OrcRes>> { async move { T::decode(&mut &buf[..]).command(ctx).await }.boxed_local() } fn serialize<'a, 'b: 'a>( @@ -86,7 +94,7 @@ impl AtomDynfo for ThinAtomDynfo { refs: &'a [api::ExprTicket], ) -> LocalBoxFuture<'a, api::Atom> { assert!(refs.is_empty(), "Refs found when deserializing thin atom"); - async { T::decode(&mut &data[..])._factory().build(ctx) }.boxed_local() + async { T::decode(&mut &data[..])._factory().build(ctx).await }.boxed_local() } fn drop<'a>(&'a self, AtomCtx(buf, _, ctx): AtomCtx<'a>) -> LocalBoxFuture<'a, ()> { async move { @@ -101,12 +109,12 @@ pub trait ThinAtom: AtomCard + Atomic + Coding + Send + Sync + 'static { #[allow(unused_variables)] - fn call(&self, arg: ExprHandle) -> impl Future { - async { bot([err_not_callable().await]) } + fn call(&self, arg: ExprHandle) -> impl Future { + async move { bot([err_not_callable(&arg.ctx.i).await]) } } #[allow(unused_variables)] - fn command(&self, ctx: SysCtx) -> impl Future>> { - async { Err(err_not_command().await.into()) } + fn command(&self, ctx: SysCtx) -> impl Future>> { + async move { Err(err_not_command(&ctx.i).await.into()) } } #[allow(unused_variables)] fn print(&self, ctx: SysCtx) -> impl Future { diff --git a/orchid-extension/src/conv.rs b/orchid-extension/src/conv.rs index 096f42d..ff77de0 100644 --- a/orchid-extension/src/conv.rs +++ b/orchid-extension/src/conv.rs @@ -1,11 +1,12 @@ use std::future::Future; use orchid_base::error::{OrcErr, OrcRes, mk_err}; -use orchid_base::intern; +use orchid_base::interner::Interner; use orchid_base::location::Pos; use crate::atom::{AtomicFeatures, ToAtom, TypAtom}; -use crate::expr::{Expr, atom, bot}; +use crate::expr::Expr; +use crate::gen_expr::{GExpr, atom, bot}; use crate::system::downcast_atom; pub trait TryFromExpr: Sized { @@ -22,38 +23,39 @@ impl TryFromExpr for (T, U) { } } -async fn err_not_atom(pos: Pos) -> OrcErr { - mk_err(intern!(str: "Expected an atom").await, "This expression is not an atom", [pos.into()]) +async fn err_not_atom(pos: Pos, i: &Interner) -> OrcErr { + mk_err(i.i("Expected an atom").await, "This expression is not an atom", [pos.into()]) } -async fn err_type(pos: Pos) -> OrcErr { - mk_err(intern!(str: "Type error").await, "The atom is a different type than expected", [ - pos.into() - ]) +async fn err_type(pos: Pos, i: &Interner) -> OrcErr { + mk_err(i.i("Type error").await, "The atom is a different type than expected", [pos.into()]) } impl TryFromExpr for TypAtom<'_, A> { async fn try_from_expr(expr: Expr) -> OrcRes { match expr.atom().await { - Err(ex) => Err(err_not_atom(ex.data().await.pos.clone()).await.into()), + Err(ex) => Err(err_not_atom(ex.data().await.pos.clone(), &ex.ctx().i).await.into()), Ok(f) => match downcast_atom(f) { Ok(a) => Ok(a), - Err(f) => Err(err_type(f.pos).await.into()), + Err(f) => Err(err_type(f.pos(), &f.ctx().i).await.into()), }, } } } pub trait ToExpr { - fn to_expr(self) -> Expr; + fn to_expr(self) -> GExpr; } +impl ToExpr for GExpr { + fn to_expr(self) -> GExpr { self } +} impl ToExpr for Expr { - fn to_expr(self) -> Expr { self } + fn to_expr(self) -> GExpr { self.gen() } } impl ToExpr for OrcRes { - fn to_expr(self) -> Expr { + fn to_expr(self) -> GExpr { match self { Err(e) => bot(e), Ok(t) => t.to_expr(), @@ -62,5 +64,5 @@ impl ToExpr for OrcRes { } impl ToExpr for A { - fn to_expr(self) -> Expr { atom(self) } + fn to_expr(self) -> GExpr { atom(self) } } diff --git a/orchid-extension/src/entrypoint.rs b/orchid-extension/src/entrypoint.rs index 15f7bbf..b0d58ce 100644 --- a/orchid-extension/src/entrypoint.rs +++ b/orchid-extension/src/entrypoint.rs @@ -1,35 +1,41 @@ +use std::cell::RefCell; +use std::future::Future; use std::io::Write; +use std::mem; use std::num::NonZero; +use std::rc::Rc; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; -use std::{mem, process, thread}; use async_std::channel::{Receiver, Sender}; +use async_std::stream; use async_std::sync::Mutex; -use futures::FutureExt; -use futures::future::LocalBoxFuture; +use futures::future::{LocalBoxFuture, join_all}; use futures::task::LocalSpawn; +use futures::{FutureExt, StreamExt}; use hashbrown::HashMap; use itertools::Itertools; +use orchid_api::ApplyMacro; use orchid_api_traits::{Decode, Encode, enc_vec}; use orchid_base::builtin::ExtPort; use orchid_base::char_filter::{char_filter_match, char_filter_union, mk_char_filter}; use orchid_base::clone; -use orchid_base::interner::{Tok, init_replica, sweep_replica}; +use orchid_base::interner::{Interner, Tok}; use orchid_base::logging::Logger; use orchid_base::macros::{mtreev_from_api, mtreev_to_api}; use orchid_base::name::{PathSlice, Sym}; use orchid_base::parse::{Comment, Snippet}; -use orchid_base::reqnot::{Receipt, ReqHandlish, ReqNot, RequestHandle, Requester}; +use orchid_base::reqnot::{ReqNot, RequestHandle, Requester}; use orchid_base::tree::{ttv_from_api, ttv_to_api}; use substack::Substack; +use trait_set::trait_set; use crate::api; use crate::atom::{AtomCtx, AtomDynfo}; use crate::atom_owned::ObjStore; use crate::fs::VirtFS; use crate::lexer::{LexContext, err_cascade, err_not_applicable}; -use crate::macros::{RuleCtx, apply_rule}; +use crate::macros::{Rule, RuleCtx}; use crate::msg::{recv_parent_msg, send_parent_msg}; use crate::system::{SysCtx, atom_by_idx}; use crate::system_ctor::{CtedObj, DynSystemCtor}; @@ -59,16 +65,26 @@ pub struct SystemRecord { vfses: HashMap, declfs: api::EagerVfs, lazy_members: HashMap, + rules: HashMap>, } -pub async fn with_atom_record( - get_sys_ctx: &impl Fn(api::SysId, ReqNot) -> SysCtx, +trait_set! { + pub trait WARCallback<'a, T> = FnOnce( + Box, + SysCtx, + api::AtomId, + &'a [u8] + ) -> LocalBoxFuture<'a, T> +} + +pub async fn with_atom_record<'a, F: Future, T>( + get_sys_ctx: &impl Fn(api::SysId, ReqNot) -> F, reqnot: ReqNot, - atom: &api::Atom, - cb: impl for<'c> FnOnce(Box, SysCtx, api::AtomId, &'c [u8]) -> LocalBoxFuture<'c, T>, + atom: &'a api::Atom, + cb: impl WARCallback<'a, T>, ) -> T { let mut data = &atom.data[..]; - let ctx = get_sys_ctx(atom.owner, reqnot); + let ctx = get_sys_ctx(atom.owner, reqnot).await; let inst = ctx.cted.inst(); let id = api::AtomId::decode(&mut data); let atom_record = atom_by_idx(inst.card(), id).expect("Atom ID reserved"); @@ -106,239 +122,301 @@ impl ExtPort for ExtensionOwner { } } -async fn extension_main_logic(data: ExtensionData, spawner: Arc) { +async fn extension_main_logic(data: ExtensionData, spawner: Rc) { let api::HostHeader { log_strategy } = api::HostHeader::decode(&mut std::io::stdin().lock()); let mut buf = Vec::new(); let decls = (data.systems.iter().enumerate()) .map(|(id, sys)| (u16::try_from(id).expect("more than u16max system ctors"), sys)) .map(|(id, sys)| sys.decl(api::SysDeclId(NonZero::new(id + 1).unwrap()))) .collect_vec(); - let systems = Arc::new(Mutex::new(HashMap::::new())); + let systems = Rc::new(Mutex::new(HashMap::::new())); api::ExtensionHeader { name: data.name.to_string(), systems: decls.clone() }.encode(&mut buf); std::io::stdout().write_all(&buf).unwrap(); std::io::stdout().flush().unwrap(); let exiting = Arc::new(AtomicBool::new(false)); let logger = Arc::new(Logger::new(log_strategy)); + let interner_cell = Rc::new(RefCell::new(None::>)); + let interner_weak = Rc::downgrade(&interner_cell); let obj_store = ObjStore::default(); let mk_ctx = clone!( - logger, systems; - move |id: api::SysId, reqnot: ReqNot| async { - let cted = systems.lock().await[&id].cted.clone(); - SysCtx { id, cted, logger, reqnot, spawner, obj_store } - }.boxed_local() - ); + logger, systems, spawner, obj_store, interner_weak; + move |id: api::SysId, reqnot: ReqNot| { + clone!(logger, systems, spawner, obj_store, interner_weak; async move { + let cted = systems.lock().await[&id].cted.clone(); + let interner_cell = (interner_weak.upgrade()) + .expect("mk_ctx called after Interner rc dropped"); + let i = (interner_cell.borrow().clone()) + .expect("mk_ctx called before interner initialized"); + SysCtx { id, cted, logger, reqnot, spawner, obj_store, i: i.clone() } + }.boxed_local()) + }); let rn = ReqNot::::new( - clone!(logger; move |a, _| async { + clone!(logger; move |a, _| clone!(logger; async move { logger.log_buf("Upsending", a); send_parent_msg(a).await.unwrap() - }.boxed_local()), - clone!(systems, exiting, mk_ctx; move |n, reqnot| async { - match n { - api::HostExtNotif::Exit => exiting.store(true, Ordering::Relaxed), - api::HostExtNotif::SystemDrop(api::SystemDrop(sys_id)) => - mem::drop(systems.lock().await.remove(&sys_id)), - api::HostExtNotif::AtomDrop(api::AtomDrop(sys_id, atom)) => - obj_store.get(atom.0).unwrap().remove().dyn_free(mk_ctx(sys_id, reqnot).await).await, - } - }.boxed_local()), + }.boxed_local())), + clone!(systems, exiting, mk_ctx, obj_store; move |n, reqnot| { + clone!(systems, exiting, mk_ctx, obj_store; async move { + match n { + api::HostExtNotif::Exit => exiting.store(true, Ordering::Relaxed), + api::HostExtNotif::SystemDrop(api::SystemDrop(sys_id)) => + mem::drop(systems.lock().await.remove(&sys_id)), + api::HostExtNotif::AtomDrop(api::AtomDrop(sys_id, atom)) => + obj_store.get(atom.0).unwrap().remove().dyn_free(mk_ctx(sys_id, reqnot).await).await, + } + }.boxed_local()) + }), { - let systems = systems.clone(); - let logger = logger.clone(); - (move |hand, req| { - async { - let receipt: Receipt = match req { + clone!(systems, logger, mk_ctx, interner_weak, obj_store, spawner, decls); + move |hand, req| { + clone!(systems, logger, mk_ctx, interner_weak, obj_store, spawner, decls); + async move { + let interner_cell = interner_weak.upgrade().expect("Interner dropped before request"); + let i = interner_cell.borrow().clone().expect("Request arrived before interner set"); + match req { api::HostExtReq::Ping(ping @ api::Ping) => hand.handle(&ping, &()).await, - _ => panic!(), - // api::HostExtReq::Sweep(sweep@api::Sweep) => hand.handle(&sweep, - // &sweep_replica()).await, - // api::HostExtReq::SysReq(api::SysReq::NewSystem(new_sys)) => { - // let i = decls.iter().enumerate().find(|(_,s)|s.id==new_sys.system).unwrap().0; - // let cted = data.systems[i].new_system(&new_sys); - // let mut vfses = HashMap::new(); - // let lex_filter = cted.inst().dyn_lexers().iter().fold(api::CharFilter(vec![]), - // |cf,lx|{ let lxcf = mk_char_filter(lx.char_filter().iter().cloned()); - // char_filter_union(&cf, &lxcf) - // }); - // let mut lazy_mems = HashMap::new(); - // let ctx = SysCtx { - // cted:cted.clone(),id:new_sys.id,logger:logger.clone(),reqnot:hand.reqnot() - // }; - // let mut tia_ctx = TIACtxImpl { - // lazy: &mut lazy_mems,sys:ctx.clone(),basepath: &[],path:Substack::Bottom, - // }; - // let const_root = - // (cted.inst().dyn_env().into_iter()).map(|(k,v)|(k.to_api(),v.into_api(&mut - // tia_ctx))).collect(); systems.lock().unwrap().insert(new_sys.id, - // SystemRecord { declfs:cted.inst().dyn_vfs().to_api_rec(&mut - // vfses),vfses,cted,lazy_members:lazy_mems }); - // hand.handle(&new_sys, &api::SystemInst { - // lex_filter,const_root,line_types:vec![] - // }).await - // } - // api::HostExtReq::GetMember(get_tree@api::GetMember(sys_id,tree_id)) => { - // let mut systems_g = systems.lock().unwrap(); - // let sys = systems_g.get_mut(&sys_id).expect("System not found"); - // let lazy = &mut sys.lazy_members; - // let(path,cb) = match lazy.insert(tree_id,MemberRecord::Res){ - // None => panic!("Tree for ID not found"), - // Some(MemberRecord::Res) => panic!("This tree has already been transmitted"), - // Some(MemberRecord::Gen(path,cb)) => (path,cb), - - // }; - // let tree = cb.build(path.clone()); - // hand.handle(&get_tree, &tree.into_api(&mut TIACtxImpl { - // sys:SysCtx::new(sys_id, &sys.cted, - // &logger,hand.reqnot()),path:Substack::Bottom,basepath: &path,lazy, - // })).await - // } - // api::HostExtReq::VfsReq(api::VfsReq::GetVfs(get_vfs@api::GetVfs(sys_id))) => { - // let systems_g = systems.lock().unwrap(); - // hand.handle(&get_vfs, &systems_g[&sys_id].declfs).await - // } - // api::HostExtReq::SysReq(api::SysReq::SysFwded(fwd)) => { - // let api::SysFwded(sys_id,payload) = fwd; - // let ctx = mk_ctx(sys_id,hand.reqnot()); - // let sys = ctx.cted.inst(); - // sys.dyn_request(hand,payload) - // } - // api::HostExtReq::VfsReq(api::VfsReq::VfsRead(vfs_read)) => { - // let api::VfsRead(sys_id,vfs_id,path) = &vfs_read; - // let systems_g = systems.lock().unwrap(); - // let path = path.iter().map(|t|Tok::from_api(*t)).collect_vec(); - // hand.handle(&vfs_read, - // &systems_g[sys_id].vfses[vfs_id].load(PathSlice::new(&path))).await } - // api::HostExtReq::LexExpr(lex@api::LexExpr { - // sys,text,pos,id - // }) => { - // let systems_g = systems.lock().unwrap(); - // let lexers = systems_g[&sys].cted.inst().dyn_lexers(); - // mem::drop(systems_g); - // let text = Tok::from_api(text); - // let ctx = LexContext { - // sys,id,pos,reqnot:hand.reqnot(),text: &text - // }; - // let trigger_char = text.await.chars().nth(pos as usize).unwrap(); - // for lx in - // lexers.iter().filter(|l|char_filter_match(l.char_filter(),trigger_char)){ - // match lx.lex(&text[pos as usize..], &ctx){ - // Err(e)if e.any(|e| *e==err_not_applicable()) => continue, - // Err(e) => { - // let eopt = e.keep_only(|e| - // *e!=err_cascade()).map(|e|Err(e.to_api())); return - // hand.handle(&lex, &eopt) }, - // Ok((s,expr)) => { - // let ctx = mk_ctx(sys,hand.reqnot()); - // let expr = expr.to_api(&mut |f,r|do_extra(f,r,ctx.clone())); - // let pos = (text.len()-s.len())as u32; - // return hand.handle(&lex, &Some(Ok(api::LexedExpr { - // pos,expr - // }))) - // } - - // } - // }writeln!(logger,"Got notified about n/a character '{trigger_char}'"); - // hand.handle(&lex, &None).await - // }, - // api::HostExtReq::ParseLine(pline) => { - // let api::ParseLine { - // exported,comments,sys,line - // } = &pline; - // let mut ctx = mk_ctx(*sys,hand.reqnot()); - // let parsers = ctx.cted.inst().dyn_parsers(); - // let comments = comments.iter().map(Comment::from_api).collect(); - // let line:Vec = ttv_from_api(line, &mut ctx); - // let snip = Snippet::new(line.first().expect("Empty line"), &line); - // let(head,tail) = snip.pop_front().unwrap(); - // let name = if let GenTok::Name(n) = &head.tok { - // n - // }else { - // panic!("No line head") - // }; - // let parser = parsers.iter().find(|p|p.line_head()== **name).expect("No parser - // candidate"); let o_line = match parser.parse(*exported,comments,tail){ - // Err(e) => Err(e.to_api()), - // Ok(t) => Ok(ttv_to_api(t, &mut |f,range|{ - // api::TokenTree { - // range,token:api::Token::Atom(f.clone().build(ctx.clone())) - // } - // })), - - // }; - // hand.handle(&pline, &o_line).await - // } - // api::HostExtReq::AtomReq(atom_req) => { - // let atom = atom_req.get_atom(); - // with_atom_record(&mk_ctx,hand.reqnot(),atom, |nfo,ctx,id,buf|async { - // let actx = AtomCtx(buf,atom.drop,ctx.clone()); - // match&atom_req { - // api::AtomReq::SerializeAtom(ser) => { - // let mut buf = enc_vec(&id); - // let refs_opt = nfo.serialize(actx, &mut buf); - // hand.handle(ser, &refs_opt.map(|refs|(buf,refs))),await - // } - // api::AtomReq::AtomPrint(print@api::AtomPrint(_)) => hand.handle(print, - // &nfo.print(actx)).await, api::AtomReq::Fwded(fwded) => { - // let api::Fwded(_,key,payload) = &fwded; - // let mut reply = Vec::new(); - // let some = nfo.handle_req(actx,Sym::from_api(*key), &mut - // &payload[..], &mut reply); hand.handle(fwded, - // &some.then_some(reply)).await } - // api::AtomReq::CallRef(call@api::CallRef(_,arg)) => { - // let ret = nfo.call_ref(actx, *arg); - // hand.handle(call, &ret.api_return(ctx.clone(), &mut - // |h|hand.defer_drop(h))).await }, - // api::AtomReq::FinalCall(call@api::FinalCall(_,arg)) => { - // let ret = nfo.call(actx, *arg); - // hand.handle(call, &ret.api_return(ctx.clone(), &mut - // |h|hand.defer_drop(h))).await } - // api::AtomReq::Command(cmd@api::Command(_)) => { - // hand.handle(cmd, &match nfo.command(actx){ - // Err(e) => Err(e.to_api()), - // Ok(opt) => Ok(match opt { - // None => api::NextStep::Halt, - // Some(cont) => - // api::NextStep::Continue(cont.api_return(ctx.clone(), &mut |h|hand.defer_drop(h))), - - // }) - - // }).await - // } - - // } - // }).await - // }, - // api::HostExtReq::DeserAtom(deser) => { - // let api::DeserAtom(sys,buf,refs) = &deser; - // let mut read = &mut &buf[..]; - // let ctx = mk_ctx(*sys,hand.reqnot()); - // let id = api::AtomId::decode(&mut read); - // let inst = ctx.cted.inst(); - // let nfo = atom_by_idx(inst.card(),id).expect("Deserializing atom with invalid - // ID"); hand.handle(&deser, &nfo.deserialize(ctx.clone(),read,refs)).await - // }, - // orchid_api::HostExtReq::ApplyMacro(am) => { - // let tok = hand.will_handle_as(&am); - // let sys_ctx = mk_ctx(am.sys,hand.reqnot()); - // let ctx = RuleCtx { - // args:(am.params.into_iter()).map(|(k,v)|(Tok::from_api(k),mtreev_from_api(&v, - // &mut |_|panic!("No atom in macro - // prompt!")))).collect(),run_id:am.run_id,sys:sys_ctx.clone(), }; - // hand.handle_as(tok, &match apply_rule(am.id,ctx){ - // Err(e) => e.keep_only(|e| *e!=err_cascade()).map(|e|Err(e.to_api())), - // Ok(t) => Some(Ok(mtreev_to_api(&t, &mut |a|{ - // api::MacroToken::Atom(a.clone().build(sys_ctx.clone())) - // }))), - - // }).await - // } - }; - receipt + api::HostExtReq::Sweep(sweep @ api::Sweep) => + hand.handle(&sweep, &i.sweep_replica().await).await, + api::HostExtReq::SysReq(api::SysReq::NewSystem(new_sys)) => { + let (sys_id, _) = (decls.iter().enumerate().find(|(_, s)| s.id == new_sys.system)) + .expect("NewSystem call received for invalid system"); + let cted = data.systems[sys_id].new_system(&new_sys); + let mut vfses = HashMap::new(); + let lex_filter = + cted.inst().dyn_lexers().iter().fold(api::CharFilter(vec![]), |cf, lx| { + char_filter_union(&cf, &mk_char_filter(lx.char_filter().iter().cloned())) + }); + let lazy_mems = Mutex::new(HashMap::new()); + let rules = Mutex::new(HashMap::new()); + let ctx = SysCtx { + cted: cted.clone(), + id: new_sys.id, + logger: logger.clone(), + reqnot: hand.reqnot(), + i: i.clone(), + obj_store: obj_store.clone(), + spawner: spawner.clone(), + }; + let const_root = stream::from_iter(cted.inst().dyn_env()) + .then(|(k, v)| { + let (req, lazy_mems, rules) = (&hand, &lazy_mems, &rules); + clone!(i, ctx; async move { + let name = i.i::(&k).await.to_api(); + let value = v.into_api(&mut TIACtxImpl { + lazy_members: &mut *lazy_mems.lock().await, + rules: &mut *rules.lock().await, + sys: ctx, + basepath: &[], + path: Substack::Bottom, + req + }) + .await; + (name, value) + }) + }) + .collect() + .await; + let declfs = cted.inst().dyn_vfs().to_api_rec(&mut vfses, &i).await; + let record = SystemRecord { + declfs, + vfses, + cted, + lazy_members: lazy_mems.into_inner(), + rules: rules.into_inner(), + }; + systems.lock().await.insert(new_sys.id, record); + hand + .handle(&new_sys, &api::SystemInst { lex_filter, const_root, line_types: vec![] }) + .await + }, + api::HostExtReq::GetMember(get_tree @ api::GetMember(sys_id, tree_id)) => { + let sys_ctx = mk_ctx(sys_id, hand.reqnot()).await; + let mut systems_g = systems.lock().await; + let SystemRecord { lazy_members, rules, .. } = + systems_g.get_mut(&sys_id).expect("System not found"); + let (path, cb) = match lazy_members.insert(tree_id, MemberRecord::Res) { + None => panic!("Tree for ID not found"), + Some(MemberRecord::Res) => panic!("This tree has already been transmitted"), + Some(MemberRecord::Gen(path, cb)) => (path, cb), + }; + let tree = cb.build(Sym::new(path.clone(), &i).await.unwrap()).await; + let mut tia_ctx = TIACtxImpl { + sys: sys_ctx, + path: Substack::Bottom, + basepath: &path, + lazy_members, + rules, + req: &hand, + }; + hand.handle(&get_tree, &tree.into_api(&mut tia_ctx).await).await + }, + api::HostExtReq::VfsReq(api::VfsReq::GetVfs(get_vfs @ api::GetVfs(sys_id))) => { + let systems_g = systems.lock().await; + hand.handle(&get_vfs, &systems_g[&sys_id].declfs).await + }, + api::HostExtReq::SysReq(api::SysReq::SysFwded(fwd)) => { + let api::SysFwded(sys_id, payload) = fwd; + let ctx = mk_ctx(sys_id, hand.reqnot()).await; + let sys = ctx.cted.inst(); + sys.dyn_request(hand, payload).await + }, + api::HostExtReq::VfsReq(api::VfsReq::VfsRead(vfs_read)) => { + let api::VfsRead(sys_id, vfs_id, path) = &vfs_read; + let ctx = mk_ctx(*sys_id, hand.reqnot()).await; + let systems_g = systems.lock().await; + let path = join_all(path.iter().map(|t| Tok::from_api(*t, &i))).await; + let vfs = systems_g[sys_id].vfses[vfs_id].load(PathSlice::new(&path), ctx).await; + hand.handle(&vfs_read, &vfs).await + }, + api::HostExtReq::LexExpr(lex @ api::LexExpr { sys, text, pos, id }) => { + let systems_g = systems.lock().await; + let lexers = systems_g[&sys].cted.inst().dyn_lexers(); + mem::drop(systems_g); + let text = Tok::from_api(text, &i).await; + let ctx = LexContext { sys, id, pos, reqnot: hand.reqnot(), text: &text, i: &i }; + let trigger_char = text.chars().nth(pos as usize).unwrap(); + let err_na = err_not_applicable(&i).await; + let err_cascade = err_cascade(&i).await; + for lx in lexers.iter().filter(|l| char_filter_match(l.char_filter(), trigger_char)) { + match lx.lex(&text[pos as usize..], &ctx).await { + Err(e) if e.any(|e| *e == err_na) => continue, + Err(e) => { + let eopt = e.keep_only(|e| *e != err_cascade).map(|e| Err(e.to_api())); + return hand.handle(&lex, &eopt).await; + }, + Ok((s, expr)) => { + let ctx = mk_ctx(sys, hand.reqnot()).await; + let expr = expr + .to_api(&mut |f, r| { + clone!(ctx; async move { do_extra(f, r, ctx).await }).boxed_local() + }) + .await; + let pos = (text.len() - s.len()) as u32; + return hand.handle(&lex, &Some(Ok(api::LexedExpr { pos, expr }))).await; + }, + } + } + writeln!(logger, "Got notified about n/a character '{trigger_char}'"); + hand.handle(&lex, &None).await + }, + api::HostExtReq::ParseLine(pline) => { + let api::ParseLine { exported, comments, sys, line } = &pline; + let mut ctx = mk_ctx(*sys, hand.reqnot()).await; + let parsers = ctx.cted.inst().dyn_parsers(); + let comments = join_all(comments.iter().map(|c| Comment::from_api(c, &i))).await; + let line: Vec = ttv_from_api(line, &mut ctx, &i).await; + let snip = Snippet::new(line.first().expect("Empty line"), &line, &i); + let (head, tail) = snip.pop_front().unwrap(); + let name = if let GenTok::Name(n) = &head.tok { n } else { panic!("No line head") }; + let parser = + parsers.iter().find(|p| p.line_head() == **name).expect("No parser candidate"); + let o_line = match parser.parse(*exported, comments, tail) { + Err(e) => Err(e.to_api()), + Ok(t) => Ok( + ttv_to_api(t, &mut |f, range| { + clone!(ctx); + async move { + api::TokenTree { range, token: api::Token::Atom(f.clone().build(ctx).await) } + } + .boxed_local() + }) + .await, + ), + }; + hand.handle(&pline, &o_line).await + }, + api::HostExtReq::AtomReq(atom_req) => { + let atom = atom_req.get_atom(); + let atom_req = atom_req.clone(); + with_atom_record(&mk_ctx, hand.reqnot(), atom, move |nfo, ctx, id, buf| { + async move { + let actx = AtomCtx(buf, atom.drop, ctx.clone()); + match &atom_req { + api::AtomReq::SerializeAtom(ser) => { + let mut buf = enc_vec(&id); + let refs_opt = nfo.serialize(actx, &mut buf).await; + hand.handle(ser, &refs_opt.map(|refs| (buf, refs))).await + }, + api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) => + hand.handle(print, &nfo.print(actx).await).await, + api::AtomReq::Fwded(fwded) => { + let api::Fwded(_, key, payload) = &fwded; + let mut reply = Vec::new(); + let key = Sym::from_api(*key, &i).await; + let some = nfo.handle_req(actx, key, &mut &payload[..], &mut reply).await; + hand.handle(fwded, &some.then_some(reply)).await + }, + api::AtomReq::CallRef(call @ api::CallRef(_, arg)) => { + let ret = nfo.call_ref(actx, *arg).await; + hand.handle(call, &ret.api_return(ctx.clone(), &hand).await).await + }, + api::AtomReq::FinalCall(call @ api::FinalCall(_, arg)) => { + let ret = nfo.call(actx, *arg).await; + hand.handle(call, &ret.api_return(ctx.clone(), &hand).await).await + }, + api::AtomReq::Command(cmd @ api::Command(_)) => match nfo.command(actx).await { + Err(e) => hand.handle(cmd, &Err(e.to_api())).await, + Ok(opt) => match opt { + None => hand.handle(cmd, &Ok(api::NextStep::Halt)).await, + Some(cont) => { + let cont = cont.api_return(ctx.clone(), &hand).await; + hand.handle(cmd, &Ok(api::NextStep::Continue(cont))).await + }, + }, + }, + } + } + .boxed_local() + }) + .await + }, + api::HostExtReq::DeserAtom(deser) => { + let api::DeserAtom(sys, buf, refs) = &deser; + let mut read = &mut &buf[..]; + let ctx = mk_ctx(*sys, hand.reqnot()).await; + let id = api::AtomId::decode(&mut read); + let inst = ctx.cted.inst(); + let nfo = atom_by_idx(inst.card(), id).expect("Deserializing atom with invalid ID"); + hand.handle(&deser, &nfo.deserialize(ctx.clone(), read, refs).await).await + }, + orchid_api::HostExtReq::ApplyMacro(am) => { + let tok = hand.will_handle_as(&am); + let ApplyMacro { id, params, run_id, sys } = am; + let sys_ctx = mk_ctx(sys, hand.reqnot()).await; + let mut ctx = + RuleCtx { args: ahash::HashMap::default(), run_id, sys: sys_ctx.clone() }; + for (k, v) in params { + ctx.args.insert( + Tok::from_api(k, &i).await, + mtreev_from_api(&v, &mut |_| panic!("No atom in macro prompt!"), &i).await, + ); + } + let err_cascade = err_cascade(&i).await; + let systems_g = systems.lock().await; + let rule = &systems_g[&sys].rules[&id]; + match (rule.apply)(ctx).await { + Err(e) => { + let new_errors = e.keep_only(|e| *e != err_cascade); + hand.handle_as(tok, &new_errors.map(|e| Err(e.to_api()))).await + }, + Ok(t) => { + let result = mtreev_to_api(&t, &mut |a| { + clone!(sys_ctx; async move { + api::MacroToken::Atom(a.clone().build(sys_ctx.clone()).await) + }.boxed_local()) + }) + .await; + hand.handle_as(tok, &Some(Ok(result))).await + }, + } + }, + } } .boxed_local() - }) + } }, ); - init_replica(rn.clone().map()); + *interner_cell.borrow_mut() = Some(Rc::new(Interner::new_replica(rn.clone().map()))); while !exiting.load(Ordering::Relaxed) { let rcvd = recv_parent_msg().await.unwrap(); rn.receive(&rcvd).await diff --git a/orchid-extension/src/expr.rs b/orchid-extension/src/expr.rs index ab2d15b..b249adb 100644 --- a/orchid-extension/src/expr.rs +++ b/orchid-extension/src/expr.rs @@ -1,18 +1,16 @@ use std::fmt; -use std::sync::Arc; +use std::rc::Rc; use async_once_cell::OnceCell; use derive_destructure::destructure; use futures::task::LocalSpawnExt; -use orchid_base::error::{OrcErr, OrcErrv}; -use orchid_base::interner::Tok; +use orchid_base::error::OrcErrv; use orchid_base::location::Pos; use orchid_base::reqnot::Requester; use crate::api; -use crate::atom::{AtomFactory, ForeignAtom, ToAtom}; -use crate::conv::{ToExpr, TryFromExpr}; -use crate::func_atom::Lambda; +use crate::atom::ForeignAtom; +use crate::gen_expr::{GExpr, GExprKind}; use crate::system::SysCtx; #[derive(destructure)] @@ -31,7 +29,11 @@ impl fmt::Debug for ExprHandle { } impl Clone for ExprHandle { fn clone(&self) -> Self { - self.ctx.reqnot.notify(api::Acquire(self.ctx.id, self.tk)); + let SysCtx { reqnot, spawner, .. } = self.ctx.clone(); + let notif = api::Acquire(self.ctx.id, self.tk); + if let Err(e) = spawner.spawn_local(async move { reqnot.notify(notif).await }) { + panic!("Failed to schedule cloning notification, resource may not exist: {e}"); + } Self { ctx: self.ctx.clone(), tk: self.tk } } } @@ -47,25 +49,24 @@ impl Drop for ExprHandle { #[derive(Clone, Debug, destructure)] pub struct Expr { - handle: Option>, - data: Arc>, + handle: Rc, + data: Rc>, } impl Expr { - pub fn new(h: Arc, d: ExprData) -> Self { - Self { handle: Some(h), data: Arc::new(OnceCell::from(d)) } + pub fn from_handle(handle: Rc) -> Self { Self { handle, data: Rc::default() } } + pub fn new(handle: Rc, d: ExprData) -> Self { + Self { handle, data: Rc::new(OnceCell::from(d)) } } - pub fn from_handle(h: Arc) -> Self { Self { handle: Some(h), data: Arc::default() } } - pub fn from_data(d: ExprData) -> Self { Self { handle: None, data: Arc::new(OnceCell::from(d)) } } pub async fn data(&self) -> &ExprData { (self.data.get_or_init(async { - let handle = self.handle.as_ref().expect("Either the value or the handle must be set"); - let details = handle.ctx.reqnot.request(api::Inspect { target: handle.tk }).await; - let pos = Pos::from_api(&details.location).await; + let details = self.handle.ctx.reqnot.request(api::Inspect { target: self.handle.tk }).await; + let pos = Pos::from_api(&details.location, &self.handle.ctx.i).await; let kind = match details.kind { api::InspectedKind::Atom(a) => - ExprKind::Atom(ForeignAtom::new(handle.clone(), a, pos.clone())), - api::InspectedKind::Bottom(b) => ExprKind::Bottom(OrcErrv::from_api(&b).await), + ExprKind::Atom(ForeignAtom::new(self.handle.clone(), a, pos.clone())), + api::InspectedKind::Bottom(b) => + ExprKind::Bottom(OrcErrv::from_api(&b, &self.handle.ctx.i).await), api::InspectedKind::Opaque => ExprKind::Opaque, }; ExprData { pos, kind } @@ -73,25 +74,15 @@ impl Expr { .await } pub async fn atom(self) -> Result, Self> { - match (self.data().await, &self.handle) { - (ExprData { kind: ExprKind::Atom(atom), .. }, Some(_)) => Ok(atom.clone()), + match self.data().await { + ExprData { kind: ExprKind::Atom(atom), .. } => Ok(atom.clone()), _ => Err(self), } } - pub fn api_return( - self, - ctx: SysCtx, - do_slot: &mut impl FnMut(Arc), - ) -> api::Expression { - if let Some(h) = self.handle { - do_slot(h.clone()); - api::Expression { location: api::Location::SlotTarget, kind: api::ExpressionKind::Slot(h.tk) } - } else { - let data = self.data.get().expect("Either value or handle must be set"); - data.clone().api_return(ctx, do_slot) - } - } - pub fn handle(&self) -> Option> { self.handle.clone() } + pub fn handle(&self) -> Rc { self.handle.clone() } + pub fn ctx(&self) -> SysCtx { self.handle.ctx.clone() } + + pub fn gen(&self) -> GExpr { GExpr { pos: Pos::SlotTarget, kind: GExprKind::Slot(self.clone()) } } } #[derive(Clone, Debug)] @@ -99,84 +90,10 @@ pub struct ExprData { pub pos: Pos, pub kind: ExprKind, } -impl ExprData { - pub fn api_return( - self, - ctx: SysCtx, - do_slot: &mut impl FnMut(Arc), - ) -> api::Expression { - api::Expression { location: self.pos.to_api(), kind: self.kind.api_return(ctx, do_slot) } - } -} #[derive(Clone, Debug)] pub enum ExprKind { - Call(Box, Box), - Lambda(u64, Box), - Arg(u64), - Seq(Box, Box), - Const(Tok>>), - NewAtom(AtomFactory), Atom(ForeignAtom<'static>), Bottom(OrcErrv), Opaque, } -impl ExprKind { - pub fn api_return( - self, - ctx: SysCtx, - do_slot: &mut impl FnMut(Arc), - ) -> api::ExpressionKind { - use api::ExpressionKind as K; - match self { - Self::Call(f, x) => - K::Call(Box::new(f.api_return(ctx.clone(), do_slot)), Box::new(x.api_return(ctx, do_slot))), - Self::Seq(a, b) => - K::Seq(Box::new(a.api_return(ctx.clone(), do_slot)), Box::new(b.api_return(ctx, do_slot))), - Self::Lambda(arg, body) => K::Lambda(arg, Box::new(body.api_return(ctx, do_slot))), - Self::Arg(arg) => K::Arg(arg), - Self::Const(name) => K::Const(name.to_api()), - Self::Bottom(err) => K::Bottom(err.to_api()), - Self::NewAtom(fac) => K::NewAtom(fac.clone().build(ctx)), - kind @ (Self::Atom(_) | Self::Opaque) => panic!("{kind:?} should have a token"), - } - } -} -fn inherit(kind: ExprKind) -> Expr { Expr::from_data(ExprData { pos: Pos::Inherit, kind }) } - -pub fn sym_ref(path: Tok>>) -> Expr { inherit(ExprKind::Const(path)) } -pub fn atom(atom: A) -> Expr { inherit(ExprKind::NewAtom(atom.to_atom_factory())) } - -pub fn seq(ops: impl IntoIterator) -> Expr { - fn recur(mut ops: impl Iterator) -> Option { - let op = ops.next()?; - Some(match recur(ops) { - None => op, - Some(rec) => inherit(ExprKind::Seq(Box::new(op), Box::new(rec))), - }) - } - recur(ops.into_iter()).expect("Empty list provided to seq!") -} - -pub fn arg(n: u64) -> Expr { inherit(ExprKind::Arg(n)) } - -pub fn lambda(n: u64, b: impl IntoIterator) -> Expr { - inherit(ExprKind::Lambda(n, Box::new(call(b)))) -} - -pub fn call(v: impl IntoIterator) -> Expr { - v.into_iter() - .reduce(|f, x| inherit(ExprKind::Call(Box::new(f), Box::new(x)))) - .expect("Empty call expression") -} - -pub fn bot(ev: impl IntoIterator) -> Expr { - inherit(ExprKind::Bottom(OrcErrv::new(ev).unwrap())) -} - -pub fn with( - expr: Expr, - cont: impl Fn(I) -> O + Clone + Send + Sync + 'static, -) -> Expr { - call([lambda(0, [seq([arg(0), call([Lambda::new(cont).to_expr(), arg(0)])])]), expr]) -} diff --git a/orchid-extension/src/fs.rs b/orchid-extension/src/fs.rs index e30472b..f26c6cc 100644 --- a/orchid-extension/src/fs.rs +++ b/orchid-extension/src/fs.rs @@ -1,16 +1,20 @@ -use std::future::{Future, ready}; use std::num::NonZero; use futures::FutureExt; -use futures::future::{join, join_all}; +use futures::future::LocalBoxFuture; use hashbrown::HashMap; -use orchid_base::interner::intern; +use orchid_base::interner::Interner; use orchid_base::name::PathSlice; use crate::api; +use crate::system::SysCtx; pub trait VirtFS: Send + Sync + 'static { - fn load(&self, path: &PathSlice) -> api::OrcResult; + fn load<'a>( + &'a self, + path: &'a PathSlice, + ctx: SysCtx, + ) -> LocalBoxFuture<'a, api::OrcResult>; } pub enum DeclFs { @@ -18,26 +22,25 @@ pub enum DeclFs { Mod(&'static [(&'static str, DeclFs)]), } impl DeclFs { - pub fn to_api_rec( + pub async fn to_api_rec( &self, vfses: &mut HashMap, - ) -> impl Future + '_ { + i: &Interner, + ) -> api::EagerVfs { match self { DeclFs::Lazy(fs) => { let vfsc: u16 = vfses.len().try_into().expect("too many vfses (more than u16::MAX)"); let id = api::VfsId(NonZero::new(vfsc + 1).unwrap()); vfses.insert(id, *fs); - ready(api::EagerVfs::Lazy(id)).boxed_local() + api::EagerVfs::Lazy(id) }, DeclFs::Mod(children) => { - let promises: Vec<_> = - children.iter().map(|(k, v)| join(intern(*k), v.to_api_rec(vfses))).collect(); - async { - api::EagerVfs::Eager( - join_all(promises).await.into_iter().map(|(k, v)| (k.to_api(), v)).collect(), - ) + let mut output = std::collections::HashMap::new(); + for (k, v) in children.iter() { + output + .insert(i.i::(*k).await.to_api(), v.to_api_rec(vfses, i).boxed_local().await); } - .boxed_local() + api::EagerVfs::Eager(output) }, } } diff --git a/orchid-extension/src/func_atom.rs b/orchid-extension/src/func_atom.rs index ac1f415..fcf9049 100644 --- a/orchid-extension/src/func_atom.rs +++ b/orchid-extension/src/func_atom.rs @@ -2,12 +2,12 @@ use std::borrow::Cow; use std::collections::HashMap; use std::future::Future; use std::io; -use std::sync::{Arc, Mutex}; +use std::rc::Rc; +use async_std::sync::Mutex; use futures::FutureExt; use futures::future::LocalBoxFuture; use itertools::Itertools; -use lazy_static::lazy_static; use never::Never; use orchid_api_traits::Encode; use orchid_base::clone; @@ -15,23 +15,24 @@ use orchid_base::error::OrcRes; use orchid_base::name::Sym; use trait_set::trait_set; -use crate::atom::{Atomic, MethodSet}; +use crate::atom::{Atomic, MethodSetBuilder}; use crate::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant}; use crate::conv::ToExpr; use crate::expr::{Expr, ExprHandle}; +use crate::gen_expr::GExpr; use crate::system::SysCtx; trait_set! { - trait FunCB = Fn(Vec) -> LocalBoxFuture<'static, OrcRes> + Send + Sync + 'static; + trait FunCB = Fn(Vec) -> LocalBoxFuture<'static, OrcRes> + 'static; } -pub trait ExprFunc: Clone + Send + Sync + 'static { +pub trait ExprFunc: Clone + 'static { const ARITY: u8; - fn apply(&self, v: Vec) -> impl Future>; + fn apply(&self, v: Vec) -> impl Future>; } -lazy_static! { - static ref FUNS: Mutex)>> = Mutex::default(); +thread_local! { + static FUNS: Rc)>>> = Rc::default(); } /// An Atom representing a partially applied named native function. These @@ -44,15 +45,16 @@ pub(crate) struct Fun { path: Sym, args: Vec, arity: u8, - fun: Arc, + fun: Rc, } impl Fun { - pub fn new>(path: Sym, f: F) -> Self { - let mut fung = FUNS.lock().unwrap(); + pub async fn new>(path: Sym, f: F) -> Self { + let funs = FUNS.with(|funs| funs.clone()); + let mut fung = funs.lock().await; let fun = if let Some(x) = fung.get(&path) { x.1.clone() } else { - let fun = Arc::new(move |v| clone!(f; async move { f.apply(v).await }.boxed_local())); + let fun = Rc::new(move |v| clone!(f; async move { f.apply(v).await }.boxed_local())); fung.insert(path.clone(), (F::ARITY, fun.clone())); fun }; @@ -62,14 +64,13 @@ impl Fun { impl Atomic for Fun { type Data = (); type Variant = OwnedVariant; - fn reg_reqs() -> MethodSet { MethodSet::new() } + fn reg_reqs() -> MethodSetBuilder { MethodSetBuilder::new() } } impl OwnedAtom for Fun { type Refs = Vec; async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) } - async fn call_ref(&self, arg: ExprHandle) -> Expr { - let new_args = - self.args.iter().cloned().chain([Expr::from_handle(Arc::new(arg))]).collect_vec(); + async fn call_ref(&self, arg: ExprHandle) -> GExpr { + let new_args = self.args.iter().cloned().chain([Expr::from_handle(Rc::new(arg))]).collect_vec(); if new_args.len() == self.arity.into() { (self.fun)(new_args).await.to_expr() } else { @@ -77,14 +78,15 @@ impl OwnedAtom for Fun { .to_expr() } } - async fn call(self, arg: ExprHandle) -> Expr { self.call_ref(arg).await } + async fn call(self, arg: ExprHandle) -> GExpr { self.call_ref(arg).await } async fn serialize(&self, _: SysCtx, sink: &mut (impl io::Write + ?Sized)) -> Self::Refs { self.path.to_api().encode(sink); self.args.clone() } async fn deserialize(ctx: impl DeserializeCtx, args: Self::Refs) -> Self { - let path = Sym::from_api(ctx.decode()).await; - let (arity, fun) = FUNS.lock().unwrap().get(&path).unwrap().clone(); + let sys = ctx.sys(); + let path = Sym::from_api(ctx.decode(), &sys.i).await; + let (arity, fun) = FUNS.with(|f| f.clone()).lock().await.get(&path).unwrap().clone(); Self { args, arity, path, fun } } } @@ -97,32 +99,31 @@ impl OwnedAtom for Fun { pub struct Lambda { args: Vec, arity: u8, - fun: Arc, + fun: Rc, } impl Lambda { pub fn new>(f: F) -> Self { - let fun = Arc::new(move |v| clone!(f; async move { f.apply(v).await }.boxed_local())); + let fun = Rc::new(move |v| clone!(f; async move { f.apply(v).await }.boxed_local())); Self { args: vec![], arity: F::ARITY, fun } } } impl Atomic for Lambda { type Data = (); type Variant = OwnedVariant; - fn reg_reqs() -> MethodSet { MethodSet::new() } + fn reg_reqs() -> MethodSetBuilder { MethodSetBuilder::new() } } impl OwnedAtom for Lambda { type Refs = Never; async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) } - async fn call_ref(&self, arg: ExprHandle) -> Expr { - let new_args = - self.args.iter().cloned().chain([Expr::from_handle(Arc::new(arg))]).collect_vec(); + async fn call_ref(&self, arg: ExprHandle) -> GExpr { + let new_args = self.args.iter().cloned().chain([Expr::from_handle(Rc::new(arg))]).collect_vec(); if new_args.len() == self.arity.into() { (self.fun)(new_args).await.to_expr() } else { Self { args: new_args, arity: self.arity, fun: self.fun.clone() }.to_expr() } } - async fn call(self, arg: ExprHandle) -> Expr { self.call_ref(arg).await } + async fn call(self, arg: ExprHandle) -> GExpr { self.call_ref(arg).await } } mod expr_func_derives { @@ -131,6 +132,7 @@ mod expr_func_derives { use super::ExprFunc; use crate::conv::{ToExpr, TryFromExpr}; use crate::func_atom::Expr; + use crate::gen_expr::GExpr; macro_rules! expr_func_derive { ($arity: tt, $($t:ident),*) => { @@ -141,7 +143,7 @@ mod expr_func_derives { Func: Fn($($t,)*) -> Out + Clone + Send + Sync + 'static > ExprFunc<($($t,)*), Out> for Func { const ARITY: u8 = $arity; - async fn apply(&self, v: Vec) -> OrcRes { + async fn apply(&self, v: Vec) -> OrcRes { assert_eq!(v.len(), Self::ARITY.into(), "Arity mismatch"); let [$([< $t:lower >],)*] = v.try_into().unwrap_or_else(|_| panic!("Checked above")); Ok(self($($t::try_from_expr([< $t:lower >]).await?,)*).to_expr()) diff --git a/orchid-extension/src/gen_expr.rs b/orchid-extension/src/gen_expr.rs new file mode 100644 index 0000000..ab3d8f4 --- /dev/null +++ b/orchid-extension/src/gen_expr.rs @@ -0,0 +1,106 @@ +use futures::FutureExt; +use orchid_base::error::{OrcErr, OrcErrv}; +use orchid_base::location::Pos; +use orchid_base::match_mapping; +use orchid_base::name::Sym; +use orchid_base::reqnot::ReqHandlish; + +use crate::api; +use crate::atom::{AtomFactory, ToAtom}; +use crate::conv::{ToExpr, TryFromExpr}; +use crate::expr::Expr; +use crate::func_atom::Lambda; +use crate::system::SysCtx; + +pub struct GExpr { + pub kind: GExprKind, + pub pos: Pos, +} +impl GExpr { + pub async fn api_return(self, ctx: SysCtx, hand: &impl ReqHandlish) -> api::Expression { + if let GExprKind::Slot(ex) = self.kind { + hand.defer_drop(ex.handle()); + api::Expression { + location: api::Location::SlotTarget, + kind: api::ExpressionKind::Slot(ex.handle().tk), + } + } else { + api::Expression { + location: api::Location::Inherit, + kind: self.kind.api_return(ctx, hand).boxed_local().await, + } + } + } +} + +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 api_return(self, ctx: SysCtx, hand: &impl ReqHandlish) -> api::ExpressionKind { + match_mapping!(self, Self => api::ExpressionKind { + Call( + f => Box::new(f.api_return(ctx.clone(), hand).await), + x => Box::new(x.api_return(ctx, hand).await) + ), + Seq( + a => Box::new(a.api_return(ctx.clone(), hand).await), + b => Box::new(b.api_return(ctx, hand).await) + ), + Lambda(arg, body => Box::new(body.api_return(ctx, hand).await)), + Arg(arg), + Const(name.to_api()), + Const(name.to_api()), + Bottom(err.to_api()), + NewAtom(fac.clone().build(ctx).await), + } { + Self::Slot(_) => panic!("processed elsewhere") + }) + } +} + +fn inherit(kind: GExprKind) -> GExpr { GExpr { pos: Pos::Inherit, kind } } + +pub fn sym_ref(path: Sym) -> GExpr { inherit(GExprKind::Const(path)) } +pub fn atom(atom: A) -> GExpr { inherit(GExprKind::NewAtom(atom.to_atom_factory())) } + +pub fn seq(ops: impl IntoIterator) -> 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(ops.into_iter()).expect("Empty list provided to seq!") +} + +pub fn arg(n: u64) -> GExpr { inherit(GExprKind::Arg(n)) } + +pub fn lambda(n: u64, b: impl IntoIterator) -> GExpr { + inherit(GExprKind::Lambda(n, Box::new(call(b)))) +} + +pub fn call(v: impl IntoIterator) -> GExpr { + v.into_iter() + .reduce(|f, x| inherit(GExprKind::Call(Box::new(f), Box::new(x)))) + .expect("Empty call expression") +} + +pub fn bot(ev: impl IntoIterator) -> GExpr { + inherit(GExprKind::Bottom(OrcErrv::new(ev).unwrap())) +} + +pub fn with( + expr: GExpr, + cont: impl Fn(I) -> O + Clone + Send + Sync + 'static, +) -> GExpr { + call([lambda(0, [seq([arg(0), call([Lambda::new(cont).to_expr(), arg(0)])])]), expr]) +} diff --git a/orchid-extension/src/lexer.rs b/orchid-extension/src/lexer.rs index 1ca2f50..82a327d 100644 --- a/orchid-extension/src/lexer.rs +++ b/orchid-extension/src/lexer.rs @@ -1,8 +1,10 @@ +use std::future::Future; use std::ops::{Range, RangeInclusive}; +use futures::FutureExt; +use futures::future::LocalBoxFuture; use orchid_base::error::{OrcErr, OrcRes, mk_err}; -use orchid_base::intern; -use orchid_base::interner::Tok; +use orchid_base::interner::{Interner, Tok}; use orchid_base::location::Pos; use orchid_base::reqnot::{ReqNot, Requester}; use orchid_base::tree::TokHandle; @@ -10,20 +12,19 @@ use orchid_base::tree::TokHandle; use crate::api; use crate::tree::{GenTok, GenTokTree}; -pub async fn err_cascade() -> OrcErr { +pub async fn err_cascade(i: &Interner) -> OrcErr { mk_err( - intern!(str: "An error cascading from a recursive call").await, + i.i("An error cascading from a recursive call").await, "This error is a sentinel for the extension library.\ it should not be emitted by the extension.", [Pos::None.into()], ) } -pub async fn err_not_applicable() -> OrcErr { +pub async fn err_not_applicable(i: &Interner) -> OrcErr { mk_err( - intern!(str: "Pseudo-error to communicate that the current branch in a dispatch doesn't apply") - .await, - &*err_cascade().await.message, + i.i("Pseudo-error to communicate that the current branch in a dispatch doesn't apply").await, + &*err_cascade(i).await.message, [Pos::None.into()], ) } @@ -34,12 +35,13 @@ pub struct LexContext<'a> { pub id: api::ParsId, pub pos: u32, pub reqnot: ReqNot, + pub i: &'a Interner, } impl<'a> LexContext<'a> { pub async fn recurse(&self, tail: &'a str) -> OrcRes<(&'a str, GenTokTree<'a>)> { let start = self.pos(tail); let Some(lx) = self.reqnot.request(api::SubLex { pos: start, id: self.id }).await else { - return Err(err_cascade().await.into()); + return Err(err_cascade(self.i).await.into()); }; Ok((&self.text[lx.pos as usize..], GenTok::Slot(TokHandle::new(lx.ticket)).at(start..lx.pos))) } @@ -53,18 +55,29 @@ impl<'a> LexContext<'a> { pub trait Lexer: Send + Sync + Sized + Default + 'static { const CHAR_FILTER: &'static [RangeInclusive]; - fn lex<'a>(tail: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree<'a>)>; + fn lex<'a>( + tail: &'a str, + ctx: &'a LexContext<'a>, + ) -> impl Future)>>; } pub trait DynLexer: Send + Sync + 'static { fn char_filter(&self) -> &'static [RangeInclusive]; - fn lex<'a>(&self, tail: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree<'a>)>; + fn lex<'a>( + &self, + tail: &'a str, + ctx: &'a LexContext<'a>, + ) -> LocalBoxFuture<'a, OrcRes<(&'a str, GenTokTree<'a>)>>; } impl DynLexer for T { fn char_filter(&self) -> &'static [RangeInclusive] { T::CHAR_FILTER } - fn lex<'a>(&self, tail: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree<'a>)> { - T::lex(tail, ctx) + fn lex<'a>( + &self, + tail: &'a str, + ctx: &'a LexContext<'a>, + ) -> LocalBoxFuture<'a, OrcRes<(&'a str, GenTokTree<'a>)>> { + T::lex(tail, ctx).boxed_local() } } diff --git a/orchid-extension/src/lib.rs b/orchid-extension/src/lib.rs index 2b9f4d7..416bca5 100644 --- a/orchid-extension/src/lib.rs +++ b/orchid-extension/src/lib.rs @@ -8,6 +8,7 @@ pub mod entrypoint; pub mod expr; pub mod fs; pub mod func_atom; +pub mod gen_expr; pub mod lexer; pub mod macros; pub mod msg; diff --git a/orchid-extension/src/macros.rs b/orchid-extension/src/macros.rs index c8d3d11..a791830 100644 --- a/orchid-extension/src/macros.rs +++ b/orchid-extension/src/macros.rs @@ -1,15 +1,12 @@ -use std::num::NonZero; -use std::sync::RwLock; +use std::rc::Rc; use ahash::HashMap; -use futures::future::join_all; -use lazy_static::lazy_static; +use futures::future::{LocalBoxFuture, join_all}; +use itertools::Itertools; use never::Never; use orchid_base::error::OrcRes; -use orchid_base::interner::{Tok, intern}; -use orchid_base::location::Pos; +use orchid_base::interner::Tok; use orchid_base::macros::{MTree, mtreev_from_api, mtreev_to_api}; -use orchid_base::parse::Comment; use orchid_base::reqnot::Requester; use trait_set::trait_set; @@ -17,6 +14,7 @@ use crate::api; use crate::atom::AtomFactory; use crate::lexer::err_cascade; use crate::system::SysCtx; +use crate::tree::TreeIntoApiCtx; pub trait Macro { fn pattern() -> MTree<'static, Never>; @@ -42,12 +40,15 @@ pub struct RuleCtx<'a> { } impl<'a> RuleCtx<'a> { pub async fn recurse(&mut self, tree: &[MTree<'a, Never>]) -> OrcRes>> { - let req = - api::RunMacros { run_id: self.run_id, query: mtreev_to_api(tree, &mut |b| match *b {}) }; - let Some(treev) = self.sys.reqnot.request(req).await else { - return Err(err_cascade().await.into()); + let req = api::RunMacros { + run_id: self.run_id, + query: mtreev_to_api(tree, &mut |b| match *b {}).await, }; - Ok(mtreev_from_api(&treev, &mut |_| panic!("Returned atom from Rule recursion")).await) + let Some(treev) = self.sys.reqnot.request(req).await else { + return Err(err_cascade(&self.sys.i).await.into()); + }; + static ATOM_MSG: &str = "Returned atom from Rule recursion"; + Ok(mtreev_from_api(&treev, &mut |_| panic!("{ATOM_MSG}"), &self.sys.i).await) } pub fn getv(&mut self, key: &Tok) -> Vec> { self.args.remove(key).expect("Key not found") @@ -65,52 +66,37 @@ impl<'a> RuleCtx<'a> { } trait_set! { - pub trait RuleCB = for<'a> Fn(RuleCtx<'a>) -> OrcRes>> + Send + Sync; -} - -lazy_static! { - static ref RULES: RwLock>> = RwLock::default(); + pub trait RuleCB = for<'a> Fn(RuleCtx<'a>) -> LocalBoxFuture<'a, OrcRes>>>; } pub struct Rule { - pub(crate) comments: Vec, + pub(crate) comments: Vec, pub(crate) pattern: Vec>, - pub(crate) id: api::MacroId, + pub(crate) apply: Rc, } impl Rule { - pub(crate) fn to_api(&self) -> api::MacroRule { + pub(crate) async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MacroRule { api::MacroRule { - comments: self.comments.iter().map(|c| c.to_api()).collect(), + comments: join_all(self.comments.iter().map(|c| async { + api::Comment { text: ctx.sys().i.i(c).await.to_api(), location: api::Location::Inherit } + })) + .await, location: api::Location::Inherit, - pattern: mtreev_to_api(&self.pattern, &mut |b| match *b {}), - id: self.id, + pattern: mtreev_to_api(&self.pattern, &mut |b| match *b {}).await, + id: ctx.with_rule(Rc::new(self)), } } } -pub async fn rule_cmt<'a>( +pub fn rule_cmt<'a>( cmt: impl IntoIterator, pattern: Vec>, apply: impl RuleCB + 'static, ) -> Rule { - let mut rules = RULES.write().unwrap(); - let id = api::MacroId(NonZero::new(rules.len() as u64 + 1).unwrap()); - rules.insert(id, Box::new(apply)); - let comments = join_all( - cmt.into_iter().map(|s| async { Comment { pos: Pos::Inherit, text: intern(s).await } }), - ) - .await; - Rule { comments, pattern, id } + let comments = cmt.into_iter().map(|s| s.to_string()).collect_vec(); + Rule { comments, pattern, apply: Rc::new(apply) } } -pub async fn rule(pattern: Vec>, apply: impl RuleCB + 'static) -> Rule { - rule_cmt([], pattern, apply).await -} - -pub(crate) fn apply_rule( - id: api::MacroId, - ctx: RuleCtx<'static>, -) -> OrcRes>> { - let rules = RULES.read().unwrap(); - rules[&id](ctx) +pub fn rule(pattern: Vec>, apply: impl RuleCB + 'static) -> Rule { + rule_cmt([], pattern, apply) } diff --git a/orchid-extension/src/system.rs b/orchid-extension/src/system.rs index a08c0ff..2c5b514 100644 --- a/orchid-extension/src/system.rs +++ b/orchid-extension/src/system.rs @@ -1,6 +1,8 @@ +use core::fmt; use std::any::TypeId; use std::future::Future; use std::num::NonZero; +use std::rc::Rc; use std::sync::Arc; use futures::FutureExt; @@ -9,14 +11,13 @@ use futures::task::LocalSpawn; use hashbrown::HashMap; use orchid_api_traits::{Coding, Decode}; use orchid_base::boxed_iter::BoxedIter; -use orchid_base::id_store::IdStore; -use orchid_base::interner::Tok; +use orchid_base::interner::Interner; use orchid_base::logging::Logger; use orchid_base::reqnot::{Receipt, ReqNot}; use crate::api; use crate::atom::{AtomCtx, AtomDynfo, AtomicFeatures, ForeignAtom, TypAtom, get_info}; -use crate::atom_owned::{DynOwnedAtom, ObjStore}; +use crate::atom_owned::ObjStore; use crate::entrypoint::ExtReq; use crate::fs::DeclFs; use crate::func_atom::Fun; @@ -79,28 +80,28 @@ impl DynSystemCard for T { /// System as defined by author pub trait System: Send + Sync + SystemCard + 'static { - fn env() -> Vec<(Tok, MemKind)>; + fn env() -> Vec<(String, MemKind)>; fn vfs() -> DeclFs; fn lexers() -> Vec; fn parsers() -> Vec; - fn request<'a>(hand: ExtReq<'a>, req: Self::Req) -> impl Future>; + fn request(hand: ExtReq<'_>, req: Self::Req) -> impl Future>; } pub trait DynSystem: Send + Sync + DynSystemCard + 'static { - fn dyn_env(&self) -> HashMap, MemKind>; + fn dyn_env(&self) -> HashMap; fn dyn_vfs(&self) -> DeclFs; fn dyn_lexers(&self) -> Vec; fn dyn_parsers(&self) -> Vec; - fn dyn_request<'a>(&'a self, hand: ExtReq<'a>, req: Vec) -> LocalBoxFuture<'a, Receipt<'a>>; + 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, MemKind> { Self::env().into_iter().collect() } + 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>(&'a self, hand: ExtReq<'a>, req: Vec) -> LocalBoxFuture<'a, Receipt<'a>> { + fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec) -> LocalBoxFuture<'a, Receipt<'a>> { Self::request(hand, ::Req::decode(&mut &req[..])).boxed_local() } fn card(&self) -> &dyn DynSystemCard { self } @@ -125,9 +126,15 @@ pub fn downcast_atom(foreign: ForeignAtom) -> Result, - pub spawner: Arc, + pub spawner: Rc, pub id: api::SysId, pub cted: CtedObj, pub logger: Arc, 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) + } } diff --git a/orchid-extension/src/tree.rs b/orchid-extension/src/tree.rs index eb7cca3..8ef7b53 100644 --- a/orchid-extension/src/tree.rs +++ b/orchid-extension/src/tree.rs @@ -1,16 +1,16 @@ use std::future::Future; use std::num::NonZero; use std::ops::Range; +use std::rc::Rc; use dyn_clone::{DynClone, clone_box}; use futures::FutureExt; use futures::future::{LocalBoxFuture, join_all}; use hashbrown::HashMap; use itertools::Itertools; -use orchid_base::interner::{Tok, intern}; -use orchid_base::location::Pos; +use orchid_base::interner::Tok; use orchid_base::name::Sym; -use orchid_base::parse::Comment; +use orchid_base::reqnot::ReqHandlish; use orchid_base::tree::{TokTree, Token}; use ordered_float::NotNan; use substack::Substack; @@ -20,47 +20,56 @@ use crate::api; use crate::atom::{AtomFactory, ForeignAtom}; use crate::conv::ToExpr; use crate::entrypoint::MemberRecord; -use crate::expr::Expr; use crate::func_atom::{ExprFunc, Fun}; +use crate::gen_expr::GExpr; use crate::macros::Rule; use crate::system::SysCtx; pub type GenTokTree<'a> = TokTree<'a, ForeignAtom<'a>, AtomFactory>; pub type GenTok<'a> = Token<'a, ForeignAtom<'a>, AtomFactory>; -pub fn do_extra(f: &AtomFactory, r: Range, ctx: SysCtx) -> api::TokenTree { - api::TokenTree { range: r, token: api::Token::Atom(f.clone().build(ctx)) } +pub async fn do_extra(f: &AtomFactory, r: Range, ctx: SysCtx) -> api::TokenTree { + api::TokenTree { range: r, token: api::Token::Atom(f.clone().build(ctx).await) } } fn with_export(mem: GenMember, public: bool) -> Vec { - (public.then(|| GenItemKind::Export(mem.name.clone()).at(Pos::Inherit)).into_iter()) - .chain([GenItemKind::Member(mem).at(Pos::Inherit)]) + (public.then(|| GenItemKind::Export(mem.name.clone())).into_iter()) + .chain([GenItemKind::Member(mem)]) + .map(|kind| GenItem { comments: vec![], kind }) .collect() } pub struct GenItem { pub kind: GenItemKind, - pub comments: Vec, - pub pos: Pos, + pub comments: Vec, } impl GenItem { - pub fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Item { + pub async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Item { let kind = match self.kind { - GenItemKind::Export(n) => api::ItemKind::Export(n.to_api()), - GenItemKind::Member(mem) => api::ItemKind::Member(mem.into_api(ctx)), + GenItemKind::Export(n) => api::ItemKind::Export(ctx.sys().i.i::(&n).await.to_api()), + GenItemKind::Member(mem) => api::ItemKind::Member(mem.into_api(ctx).await), GenItemKind::Import(cn) => api::ItemKind::Import(cn.tok().to_api()), - GenItemKind::Macro(prio, rules) => api::ItemKind::Macro(api::MacroBlock { - priority: prio, - rules: rules.into_iter().map(|r| r.to_api()).collect_vec(), - }), + GenItemKind::Macro(priority, gen_rules) => { + let mut rules = Vec::with_capacity(gen_rules.len()); + for rule in gen_rules { + rules.push(rule.into_api(ctx).await) + } + api::ItemKind::Macro(api::MacroBlock { priority, rules }) + }, }; - let comments = self.comments.into_iter().map(|c| c.to_api()).collect_vec(); - api::Item { location: self.pos.to_api(), comments, kind } + let comments = join_all(self.comments.iter().map(|c| async { + api::Comment { + location: api::Location::Inherit, + text: ctx.sys().i.i::(c).await.to_api(), + } + })) + .await; + api::Item { location: api::Location::Inherit, comments, kind } } } -pub async fn cnst(public: bool, name: &str, value: impl ToExpr) -> Vec { - with_export(GenMember { name: intern(name).await, kind: MemKind::Const(value.to_expr()) }, public) +pub fn cnst(public: bool, name: &str, value: impl ToExpr) -> Vec { + with_export(GenMember { name: name.to_string(), kind: MemKind::Const(value.to_expr()) }, public) } pub async fn module( public: bool, @@ -75,31 +84,31 @@ pub async fn root_mod( name: &str, imports: impl IntoIterator, items: impl IntoIterator>, -) -> (Tok, MemKind) { +) -> (String, MemKind) { let kind = MemKind::Mod { imports: imports.into_iter().collect(), items: items.into_iter().flatten().collect(), }; - (intern(name).await, kind) + (name.to_string(), kind) } pub async fn fun(exported: bool, name: &str, xf: impl ExprFunc) -> Vec { let fac = - LazyMemberFactory::new(move |sym| async { MemKind::Const(Fun::new(sym, xf).to_expr()) }); - with_export(GenMember { name: intern(name).await, kind: MemKind::Lazy(fac) }, exported) + LazyMemberFactory::new(move |sym| async { MemKind::Const(Fun::new(sym, xf).await.to_expr()) }); + with_export(GenMember { name: name.to_string(), kind: MemKind::Lazy(fac) }, exported) } pub fn macro_block(prio: Option, rules: impl IntoIterator) -> Vec { let prio = prio.map(|p| NotNan::new(p).unwrap()); - vec![GenItemKind::Macro(prio, rules.into_iter().collect_vec()).gen()] + vec![GenItem { + kind: GenItemKind::Macro(prio, rules.into_iter().collect_vec()), + comments: vec![], + }] } -pub async fn comments<'a>( +pub fn comments<'a>( cmts: impl IntoIterator, mut val: Vec, ) -> Vec { - let cmts = join_all( - cmts.into_iter().map(|c| async { Comment { text: intern(c).await, pos: Pos::Inherit } }), - ) - .await; + let cmts = cmts.into_iter().map(|c| c.to_string()).collect_vec(); for v in val.iter_mut() { v.comments.extend(cmts.iter().cloned()); } @@ -108,12 +117,12 @@ pub async fn comments<'a>( trait_set! { trait LazyMemberCallback = - FnOnce(Sym) -> LocalBoxFuture<'static, MemKind> + Send + Sync + DynClone + FnOnce(Sym) -> LocalBoxFuture<'static, MemKind> + DynClone } pub struct LazyMemberFactory(Box); impl LazyMemberFactory { pub fn new + 'static>( - cb: impl FnOnce(Sym) -> F + Send + Sync + Clone + 'static, + cb: impl FnOnce(Sym) -> F + Clone + 'static, ) -> Self { Self(Box::new(|s| cb(s).boxed_local())) } @@ -125,49 +134,45 @@ impl Clone for LazyMemberFactory { pub enum GenItemKind { Member(GenMember), - Export(Tok), + Export(String), Import(Sym), Macro(Option>, Vec), } -impl GenItemKind { - pub fn at(self, pos: Pos) -> GenItem { GenItem { kind: self, comments: vec![], pos } } - pub fn gen(self) -> GenItem { GenItem { kind: self, comments: vec![], pos: Pos::Inherit } } - pub fn gen_equiv(self, comments: Vec) -> GenItem { - GenItem { kind: self, comments, pos: Pos::Inherit } - } -} pub struct GenMember { - name: Tok, + name: String, kind: MemKind, } impl GenMember { - pub fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Member { + pub async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Member { + let name = ctx.sys().i.i::(&self.name).await; api::Member { - name: self.name.to_api(), - kind: self.kind.into_api(&mut ctx.push_path(self.name)), + kind: self.kind.into_api(&mut ctx.push_path(name.clone())).await, + name: name.to_api(), } } } pub enum MemKind { - Const(Expr), + Const(GExpr), Mod { imports: Vec, items: Vec }, Lazy(LazyMemberFactory), } impl MemKind { - pub fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MemberKind { + pub async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MemberKind { match self { Self::Lazy(lazy) => api::MemberKind::Lazy(ctx.with_lazy(lazy)), - Self::Const(c) => - api::MemberKind::Const(c.api_return(ctx.sys(), &mut |_| panic!("Slot in const tree"))), - Self::Mod { imports, items } => api::MemberKind::Module(api::Module { - items: (imports.into_iter()) - .map(|t| GenItemKind::Import(t).gen()) - .chain(items) - .map(|i| i.into_api(ctx)) - .collect_vec(), - }), + Self::Const(c) => api::MemberKind::Const(c.api_return(ctx.sys(), ctx.req()).await), + Self::Mod { imports, items } => { + let all_items = (imports.into_iter()) + .map(|t| GenItem { comments: vec![], kind: GenItemKind::Import(t) }) + .chain(items); + let mut items = Vec::new(); + for i in all_items { + items.push(i.into_api(ctx).boxed_local().await) + } + api::MemberKind::Module(api::Module { items }) + }, } } } @@ -175,30 +180,42 @@ impl MemKind { pub trait TreeIntoApiCtx { fn sys(&self) -> SysCtx; fn with_lazy(&mut self, fac: LazyMemberFactory) -> api::TreeId; + fn with_rule(&mut self, rule: Rc) -> api::MacroId; fn push_path(&mut self, seg: Tok) -> impl TreeIntoApiCtx; + fn req(&self) -> &impl ReqHandlish; } -pub struct TIACtxImpl<'a, 'b> { +pub struct TIACtxImpl<'a, 'b, RH: ReqHandlish> { pub sys: SysCtx, pub basepath: &'a [Tok], pub path: Substack<'a, Tok>, - pub lazy: &'b mut HashMap, + pub lazy_members: &'b mut HashMap, + pub rules: &'b mut HashMap>, + pub req: &'a RH, } -impl<'a, 'b> TreeIntoApiCtx for TIACtxImpl<'a, 'b> { +impl TreeIntoApiCtx for TIACtxImpl<'_, '_, RH> { fn sys(&self) -> SysCtx { self.sys.clone() } fn push_path(&mut self, seg: Tok) -> impl TreeIntoApiCtx { TIACtxImpl { + req: self.req, + lazy_members: self.lazy_members, + rules: self.rules, sys: self.sys.clone(), - lazy: self.lazy, basepath: self.basepath, path: self.path.push(seg), } } fn with_lazy(&mut self, fac: LazyMemberFactory) -> api::TreeId { - let id = api::TreeId(NonZero::new((self.lazy.len() + 2) as u64).unwrap()); + let id = api::TreeId(NonZero::new((self.lazy_members.len() + 2) as u64).unwrap()); let path = self.basepath.iter().cloned().chain(self.path.unreverse()).collect_vec(); - self.lazy.insert(id, MemberRecord::Gen(path, fac)); + self.lazy_members.insert(id, MemberRecord::Gen(path, fac)); id } + fn with_rule(&mut self, rule: Rc) -> orchid_api::MacroId { + let id = api::MacroId(NonZero::new((self.lazy_members.len() + 1) as u64).unwrap()); + self.rules.insert(id, rule); + id + } + fn req(&self) -> &impl ReqHandlish { self.req } } diff --git a/orchid-std/src/number/num_atom.rs b/orchid-std/src/number/num_atom.rs index 9410415..9c3458c 100644 --- a/orchid-std/src/number/num_atom.rs +++ b/orchid-std/src/number/num_atom.rs @@ -1,6 +1,6 @@ use orchid_api_derive::Coding; use orchid_base::error::OrcRes; -use orchid_extension::atom::{AtomFactory, Atomic, AtomicFeatures, MethodSet, ToAtom, TypAtom}; +use orchid_extension::atom::{AtomFactory, Atomic, AtomicFeatures, MethodSetBuilder, ToAtom, TypAtom}; use orchid_extension::atom_thin::{ThinAtom, ThinVariant}; use orchid_extension::conv::TryFromExpr; use orchid_extension::expr::Expr; @@ -11,7 +11,7 @@ pub struct Int(pub i64); impl Atomic for Int { type Variant = ThinVariant; type Data = Self; - fn reg_reqs() -> MethodSet { MethodSet::new() } + fn reg_reqs() -> MethodSetBuilder { MethodSetBuilder::new() } } impl ThinAtom for Int {} impl TryFromExpr for Int { @@ -25,7 +25,7 @@ pub struct Float(pub NotNan); impl Atomic for Float { type Variant = ThinVariant; type Data = Self; - fn reg_reqs() -> MethodSet { MethodSet::new() } + fn reg_reqs() -> MethodSetBuilder { MethodSetBuilder::new() } } impl ThinAtom for Float {} impl TryFromExpr for Float { diff --git a/orchid-std/src/string/str_atom.rs b/orchid-std/src/string/str_atom.rs index f4c2f76..f18b598 100644 --- a/orchid-std/src/string/str_atom.rs +++ b/orchid-std/src/string/str_atom.rs @@ -8,7 +8,7 @@ use orchid_api_traits::{Encode, Request}; use orchid_base::error::{OrcRes, mk_errv}; use orchid_base::intern; use orchid_base::interner::{Tok, intern}; -use orchid_extension::atom::{AtomMethod, Atomic, MethodSet, Supports, TypAtom}; +use orchid_extension::atom::{AtomMethod, Atomic, MethodSetBuilder, Supports, TypAtom}; use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant}; use orchid_extension::conv::TryFromExpr; use orchid_extension::expr::Expr; @@ -33,7 +33,7 @@ pub struct StrAtom(Arc); impl Atomic for StrAtom { type Variant = OwnedVariant; type Data = (); - fn reg_reqs() -> MethodSet { MethodSet::new().handle::() } + fn reg_reqs() -> MethodSetBuilder { MethodSetBuilder::new().handle::() } } impl StrAtom { pub fn new(str: Arc) -> Self { Self(str) } @@ -59,7 +59,7 @@ pub struct IntStrAtom(Tok); impl Atomic for IntStrAtom { type Variant = OwnedVariant; type Data = orchid_api::TStr; - fn reg_reqs() -> MethodSet { MethodSet::new() } + fn reg_reqs() -> MethodSetBuilder { MethodSetBuilder::new() } } impl From> for IntStrAtom { fn from(value: Tok) -> Self { Self(value) }