From 32d6237dc51bc6e1d65190b7bdf395522a31c9fc Mon Sep 17 00:00:00 2001 From: Lawrence Bethlenfalvy Date: Thu, 1 Jan 2026 14:54:29 +0000 Subject: [PATCH] task_local context over context objects - interner impls logically separate from API in orchid-base (default host interner still in base for testing) - error reporting, logging, and a variety of other features passed down via context in extension, not yet in host to maintain library-ish profile, should consider options - no global spawn mechanic, the host has a spawn function but extensions only get a stash for enqueuing async work in sync callbacks which is then explicitly, manually, and with strict order popped and awaited - still deadlocks nondeterministically for some ungodly reason --- Cargo.lock | 6 +- orchid-api-derive/src/decode.rs | 10 +- orchid-api-derive/src/encode.rs | 9 +- orchid-api-traits/Cargo.toml | 1 - orchid-api-traits/src/coding.rs | 239 +++--- orchid-api-traits/src/helpers.rs | 70 +- orchid-api-traits/src/relations.rs | 2 +- orchid-api/Cargo.toml | 1 + orchid-api/src/binary.rs | 67 ++ orchid-api/src/lib.rs | 1 + orchid-api/src/proto.rs | 45 +- orchid-base/Cargo.toml | 2 +- orchid-base/src/builtin.rs | 34 - orchid-base/src/error.rs | 31 +- orchid-base/src/format.rs | 5 +- orchid-base/src/interner.rs | 295 +++++++- orchid-base/src/lib.rs | 2 +- orchid-base/src/localset.rs | 48 ++ orchid-base/src/logging.rs | 11 + orchid-base/src/msg.rs | 5 +- orchid-base/src/reqnot.rs | 388 +++++----- orchid-base/src/stash.rs | 22 +- orchid-extension/Cargo.toml | 2 +- orchid-extension/src/atom.rs | 54 +- orchid-extension/src/atom_owned.rs | 60 +- orchid-extension/src/atom_thin.rs | 32 +- orchid-extension/src/context.rs | 90 --- orchid-extension/src/conv.rs | 4 +- orchid-extension/src/entrypoint.rs | 771 ++++++++++---------- orchid-extension/src/expr.rs | 20 +- orchid-extension/src/ext_port.rs | 12 + orchid-extension/src/func_atom.rs | 45 +- orchid-extension/src/gen_expr.rs | 5 +- orchid-extension/src/interner.rs | 109 ++- orchid-extension/src/lexer.rs | 38 +- orchid-extension/src/lib.rs | 5 +- orchid-extension/src/other_system.rs | 1 + orchid-extension/src/parser.rs | 52 +- orchid-extension/src/reflection.rs | 44 +- orchid-extension/src/system.rs | 71 +- orchid-extension/src/system_ctor.rs | 20 +- orchid-extension/src/tokio.rs | 63 +- orchid-extension/src/tree.rs | 103 +-- orchid-host/src/atom.rs | 16 +- orchid-host/src/ctx.rs | 37 +- orchid-host/src/dealias.rs | 40 +- orchid-host/src/execute.rs | 14 +- orchid-host/src/expr.rs | 21 +- orchid-host/src/extension.rs | 406 ++++++----- orchid-host/src/lex.rs | 36 +- orchid-host/src/parse.rs | 75 +- orchid-host/src/parsed.rs | 31 +- orchid-host/src/subprocess.rs | 94 +-- orchid-host/src/sys_parser.rs | 28 +- orchid-host/src/system.rs | 50 +- orchid-host/src/tree.rs | 71 +- orchid-std/src/macros/instantiate_tpl.rs | 3 +- orchid-std/src/macros/let_line.rs | 63 +- orchid-std/src/macros/macro_lib.rs | 5 +- orchid-std/src/macros/macro_line.rs | 103 ++- orchid-std/src/macros/macro_system.rs | 30 +- orchid-std/src/macros/macro_value.rs | 6 +- orchid-std/src/macros/mactree.rs | 4 +- orchid-std/src/macros/mactree_lexer.rs | 14 +- orchid-std/src/macros/match_macros.rs | 26 +- orchid-std/src/macros/ph_lexer.rs | 12 +- orchid-std/src/macros/resolve.rs | 29 +- orchid-std/src/macros/rule/build.rs | 26 +- orchid-std/src/macros/rule/matcher.rs | 8 +- orchid-std/src/macros/rule/shared.rs | 8 +- orchid-std/src/macros/rule/state.rs | 18 +- orchid-std/src/macros/rule/vec_attrs.rs | 4 +- orchid-std/src/macros/std_macros.rs | 27 +- orchid-std/src/macros/utils.rs | 25 +- orchid-std/src/main.rs | 4 +- orchid-std/src/std/number/num_atom.rs | 3 +- orchid-std/src/std/number/num_lexer.rs | 5 +- orchid-std/src/std/option.rs | 17 +- orchid-std/src/std/protocol/parse_impls.rs | 37 +- orchid-std/src/std/protocol/proto_parser.rs | 22 +- orchid-std/src/std/protocol/type_parser.rs | 26 +- orchid-std/src/std/protocol/types.rs | 23 +- orchid-std/src/std/record/record_atom.rs | 10 +- orchid-std/src/std/reflection/sym_atom.rs | 6 +- orchid-std/src/std/std_system.rs | 20 +- orchid-std/src/std/string/str_atom.rs | 25 +- orchid-std/src/std/string/str_lexer.rs | 32 +- orchid-std/src/std/string/str_lib.rs | 7 +- orchid-std/src/std/string/to_string.rs | 3 +- orchid-std/src/std/tuple.rs | 10 +- orcx/src/main.rs | 216 +++--- orcx/src/parse_folder.rs | 39 +- 92 files changed, 2507 insertions(+), 2223 deletions(-) create mode 100644 orchid-api/src/binary.rs delete mode 100644 orchid-base/src/builtin.rs create mode 100644 orchid-base/src/localset.rs delete mode 100644 orchid-extension/src/context.rs create mode 100644 orchid-extension/src/ext_port.rs diff --git a/Cargo.lock b/Cargo.lock index 4c7efee..d4dfa6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1011,6 +1011,7 @@ dependencies = [ "orchid-api-traits", "ordered-float", "test_executors 0.3.5", + "unsync-pipe", ] [[package]] @@ -1028,7 +1029,6 @@ dependencies = [ name = "orchid-api-traits" version = "0.1.0" dependencies = [ - "async-fn-stream", "futures", "itertools", "never", @@ -1056,8 +1056,8 @@ dependencies = [ "ordered-float", "regex", "rust-embed", - "some_executor", "substack", + "task-local", "test_executors 0.4.0", "trait-set", "unsync-pipe", @@ -1069,6 +1069,7 @@ version = "0.1.0" dependencies = [ "async-fn-stream", "async-once-cell", + "bound", "derive_destructure", "dyn-clone", "futures", @@ -1087,7 +1088,6 @@ dependencies = [ "orchid-base", "ordered-float", "pastey", - "some_executor", "substack", "task-local", "tokio", diff --git a/orchid-api-derive/src/decode.rs b/orchid-api-derive/src/decode.rs index 63eb153..3771a4a 100644 --- a/orchid-api-derive/src/decode.rs +++ b/orchid-api-derive/src/decode.rs @@ -14,8 +14,8 @@ pub fn derive(input: TokenStream) -> TokenStream { impl #impl_generics orchid_api_traits::Decode for #name #ty_generics #where_clause { async fn decode( mut read: std::pin::Pin<&mut R> - ) -> Self { - #decode + ) -> std::io::Result { + Ok(#decode) } } }; @@ -30,7 +30,7 @@ fn decode_fields(fields: &syn::Fields) -> pm2::TokenStream { let syn::Field { ty, ident, .. } = &f; quote! { #ident : (Box::pin(< #ty as orchid_api_traits::Decode>::decode(read.as_mut())) - as std::pin::Pin>>).await + as std::pin::Pin>>>).await? } }); quote! { { #( #exprs, )* } } @@ -40,7 +40,7 @@ fn decode_fields(fields: &syn::Fields) -> pm2::TokenStream { let ty = &field.ty; quote! { (Box::pin(< #ty as orchid_api_traits::Decode>::decode(read.as_mut())) - as std::pin::Pin>>).await, + as std::pin::Pin>>>).await?, } }); quote! { ( #( #exprs )* ) } @@ -62,7 +62,7 @@ fn decode_body(data: &syn::Data) -> proc_macro2::TokenStream { quote! { #id => Self::#ident #fields, } }); quote! { - match ::decode(read.as_mut()).await { + match ::decode(read.as_mut()).await? { #(#opts)* x => panic!("Unrecognized enum kind {x}") } diff --git a/orchid-api-derive/src/encode.rs b/orchid-api-derive/src/encode.rs index e39c3f0..a0ca62d 100644 --- a/orchid-api-derive/src/encode.rs +++ b/orchid-api-derive/src/encode.rs @@ -17,8 +17,9 @@ pub fn derive(input: TokenStream) -> TokenStream { async fn encode( &self, mut write: std::pin::Pin<&mut W> - ) { - #encode + ) -> std::io::Result<()> { + #encode; + Ok(()) } } }; @@ -43,7 +44,7 @@ fn encode_body(data: &syn::Data) -> Option { quote! { Self::#ident #dest => { (Box::pin((#i as u8).encode(write.as_mut())) - as std::pin::Pin>>).await; + as std::pin::Pin>>>).await?; #body } } @@ -61,7 +62,7 @@ fn encode_body(data: &syn::Data) -> Option { fn encode_names(names: impl Iterator) -> pm2::TokenStream { quote! { #( (Box::pin(#names .encode(write.as_mut())) - as std::pin::Pin>>).await; + as std::pin::Pin>>>).await?; )* } } diff --git a/orchid-api-traits/Cargo.toml b/orchid-api-traits/Cargo.toml index f4947f6..2f6622b 100644 --- a/orchid-api-traits/Cargo.toml +++ b/orchid-api-traits/Cargo.toml @@ -6,7 +6,6 @@ edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" } futures = { version = "0.3.31", features = ["std"], default-features = false } itertools = "0.14.0" never = "0.1.0" diff --git a/orchid-api-traits/src/coding.rs b/orchid-api-traits/src/coding.rs index eb18877..0496f78 100644 --- a/orchid-api-traits/src/coding.rs +++ b/orchid-api-traits/src/coding.rs @@ -1,33 +1,44 @@ use std::collections::HashMap; use std::future::Future; use std::hash::Hash; +use std::io; use std::num::NonZero; use std::ops::{Range, RangeInclusive}; use std::pin::Pin; use std::rc::Rc; use std::sync::Arc; -use async_fn_stream::stream; -use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, StreamExt}; +use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use never::Never; use ordered_float::NotNan; -use crate::encode_enum; +use crate::{decode_err, decode_err_for, encode_enum, spin_on}; -pub trait Decode: 'static { +pub trait Decode: 'static + Sized { /// Decode an instance from the beginning of the buffer. Return the decoded /// data and the remaining buffer. - fn decode(read: Pin<&mut R>) -> impl Future + '_; + fn decode( + read: Pin<&mut R>, + ) -> impl Future> + '_; + fn decode_slice(slc: &mut &[u8]) -> Self { + spin_on(Self::decode(Pin::new(slc) as Pin<&mut _>)).expect("Decode from slice cannot fail") + } } pub trait Encode { /// Append an instance of the struct to the buffer - fn encode(&self, write: Pin<&mut W>) -> impl Future; + fn encode( + &self, + write: Pin<&mut W>, + ) -> impl Future>; + fn encode_vec(&self, vec: &mut Vec) { + spin_on(self.encode(Pin::new(vec) as Pin<&mut _>)).expect("Encode to vector cannot fail") + } } pub trait Coding: Encode + Decode + Clone { - fn get_decoder + 'static>( - map: impl Fn(Self) -> F + Clone + 'static, - ) -> impl AsyncFn(Pin<&mut dyn AsyncRead>) -> T { - async move |r| map(Self::decode(r).await).await + fn get_decoder( + map: impl AsyncFn(Self) -> T + Clone + 'static, + ) -> impl AsyncFn(Pin<&mut dyn AsyncRead>) -> io::Result { + async move |r| Ok(map(Self::decode(r).await?).await) } } impl Coding for T {} @@ -35,15 +46,15 @@ impl Coding for T {} macro_rules! num_impl { ($number:ty) => { impl Decode for $number { - async fn decode(mut read: Pin<&mut R>) -> Self { + async fn decode(mut read: Pin<&mut R>) -> io::Result { let mut bytes = [0u8; (<$number>::BITS / 8) as usize]; - read.read_exact(&mut bytes).await.unwrap(); - <$number>::from_be_bytes(bytes) + read.read_exact(&mut bytes).await?; + Ok(<$number>::from_be_bytes(bytes)) } } impl Encode for $number { - async fn encode(&self, mut write: Pin<&mut W>) { - write.write_all(&self.to_be_bytes()).await.expect("Could not write number") + async fn encode(&self, mut write: Pin<&mut W>) -> io::Result<()> { + write.write_all(&self.to_be_bytes()).await } } }; @@ -62,12 +73,12 @@ num_impl!(i8); macro_rules! nonzero_impl { ($name:ty) => { impl Decode for NonZero<$name> { - async fn decode(read: Pin<&mut R>) -> Self { - Self::new(<$name as Decode>::decode(read).await).unwrap() + async fn decode(read: Pin<&mut R>) -> io::Result { + Self::new(<$name as Decode>::decode(read).await?).ok_or_else(decode_err) } } impl Encode for NonZero<$name> { - async fn encode(&self, write: Pin<&mut W>) { + async fn encode(&self, write: Pin<&mut W>) -> io::Result<()> { self.get().encode(write).await } } @@ -86,22 +97,22 @@ nonzero_impl!(i64); nonzero_impl!(i128); impl Encode for &T { - async fn encode(&self, write: Pin<&mut W>) { + async fn encode(&self, write: Pin<&mut W>) -> io::Result<()> { (**self).encode(write).await } } macro_rules! float_impl { ($t:ty, $size:expr) => { impl Decode for NotNan<$t> { - async fn decode(mut read: Pin<&mut R>) -> Self { + async fn decode(mut read: Pin<&mut R>) -> io::Result { let mut bytes = [0u8; $size]; - read.read_exact(&mut bytes).await.unwrap(); - NotNan::new(<$t>::from_be_bytes(bytes)).expect("Float was NaN") + read.read_exact(&mut bytes).await?; + NotNan::new(<$t>::from_be_bytes(bytes)).map_err(|_| decode_err()) } } impl Encode for NotNan<$t> { - async fn encode(&self, mut write: Pin<&mut W>) { - write.write_all(&self.as_ref().to_be_bytes()).await.expect("Could not write number") + async fn encode(&self, mut write: Pin<&mut W>) -> io::Result<()> { + write.write_all(&self.as_ref().to_be_bytes()).await } } }; @@ -111,78 +122,77 @@ float_impl!(f64, 8); float_impl!(f32, 4); impl Decode for String { - async fn decode(mut read: Pin<&mut R>) -> Self { - let len: usize = u64::decode(read.as_mut()).await.try_into().unwrap(); + async fn decode(mut read: Pin<&mut R>) -> io::Result { + let len: usize = u64::decode(read.as_mut()).await?.try_into().map_err(decode_err_for)?; let mut data = vec![0u8; len]; - read.read_exact(&mut data).await.unwrap(); - std::str::from_utf8(&data).expect("String invalid UTF-8").to_owned() + read.read_exact(&mut data).await?; + Ok(std::str::from_utf8(&data).map_err(decode_err_for)?.to_owned()) } } impl Encode for String { - async fn encode(&self, mut write: Pin<&mut W>) { - u64::try_from(self.len()).unwrap().encode(write.as_mut()).await; - write.write_all(self.as_bytes()).await.unwrap() + async fn encode(&self, mut write: Pin<&mut W>) -> io::Result<()> { + u64::try_from(self.len()).map_err(decode_err_for)?.encode(write.as_mut()).await?; + write.write_all(self.as_bytes()).await } } impl Encode for str { - async fn encode(&self, mut write: Pin<&mut W>) { - u64::try_from(self.len()).unwrap().encode(write.as_mut()).await; - write.write_all(self.as_bytes()).await.unwrap() + async fn encode(&self, mut write: Pin<&mut W>) -> io::Result<()> { + u64::try_from(self.len()).map_err(decode_err_for)?.encode(write.as_mut()).await?; + write.write_all(self.as_bytes()).await } } impl Decode for Vec { - async fn decode(mut read: Pin<&mut R>) -> Self { - let len = u64::decode(read.as_mut()).await; - stream(async |mut cx| { - for _ in 0..len { - cx.emit(T::decode(read.as_mut()).await).await - } - }) - .collect() - .await + async fn decode(mut read: Pin<&mut R>) -> io::Result { + let len = u64::decode(read.as_mut()).await?; + let mut values = Vec::with_capacity(len.try_into().map_err(decode_err_for)?); + for _ in 0..len { + values.push(T::decode(read.as_mut()).await?); + } + Ok(values) } } impl Encode for Vec { - async fn encode(&self, write: Pin<&mut W>) { + async fn encode(&self, write: Pin<&mut W>) -> io::Result<()> { self.as_slice().encode(write).await } } impl Encode for [T] { - async fn encode(&self, mut write: Pin<&mut W>) { - u64::try_from(self.len()).unwrap().encode(write.as_mut()).await; + async fn encode(&self, mut write: Pin<&mut W>) -> io::Result<()> { + u64::try_from(self.len()).unwrap().encode(write.as_mut()).await?; for t in self.iter() { - t.encode(write.as_mut()).await + t.encode(write.as_mut()).await? } + Ok(()) } } impl Decode for Option { - async fn decode(mut read: Pin<&mut R>) -> Self { - match u8::decode(read.as_mut()).await { - 0 => None, - 1 => Some(T::decode(read).await), - x => panic!("{x} is not a valid option value"), - } + async fn decode(mut read: Pin<&mut R>) -> io::Result { + Ok(match bool::decode(read.as_mut()).await? { + false => None, + true => Some(T::decode(read).await?), + }) } } impl Encode for Option { - async fn encode(&self, mut write: Pin<&mut W>) { - let t = if let Some(t) = self { t } else { return 0u8.encode(write.as_mut()).await }; - 1u8.encode(write.as_mut()).await; - t.encode(write).await; + async fn encode(&self, mut write: Pin<&mut W>) -> io::Result<()> { + self.is_some().encode(write.as_mut()).await?; + if let Some(t) = self { + t.encode(write).await? + } + Ok(()) } } impl Decode for Result { - async fn decode(mut read: Pin<&mut R>) -> Self { - match u8::decode(read.as_mut()).await { - 0 => Self::Ok(T::decode(read).await), - 1 => Self::Err(E::decode(read).await), - x => panic!("Invalid Result tag {x}"), - } + async fn decode(mut read: Pin<&mut R>) -> io::Result { + Ok(match bool::decode(read.as_mut()).await? { + false => Self::Ok(T::decode(read).await?), + true => Self::Err(E::decode(read).await?), + }) } } impl Encode for Result { - async fn encode(&self, write: Pin<&mut W>) { + async fn encode(&self, write: Pin<&mut W>) -> io::Result<()> { match self { Ok(t) => encode_enum(write, 0, |w| t.encode(w)).await, Err(e) => encode_enum(write, 1, |w| e.encode(w)).await, @@ -190,36 +200,37 @@ impl Encode for Result { } } impl Decode for HashMap { - async fn decode(mut read: Pin<&mut R>) -> Self { - let len = u64::decode(read.as_mut()).await; - stream(async |mut cx| { - for _ in 0..len { - cx.emit(<(K, V)>::decode(read.as_mut()).await).await - } - }) - .collect() - .await + async fn decode(mut read: Pin<&mut R>) -> io::Result { + let len = u64::decode(read.as_mut()).await?; + let mut map = HashMap::with_capacity(len.try_into().map_err(decode_err_for)?); + for _ in 0..len { + map.insert(K::decode(read.as_mut()).await?, V::decode(read.as_mut()).await?); + } + Ok(map) } } impl Encode for HashMap { - async fn encode(&self, mut write: Pin<&mut W>) { - u64::try_from(self.len()).unwrap().encode(write.as_mut()).await; - for pair in self.iter() { - pair.encode(write.as_mut()).await + async fn encode(&self, mut write: Pin<&mut W>) -> io::Result<()> { + u64::try_from(self.len()).unwrap().encode(write.as_mut()).await?; + for (key, value) in self.iter() { + key.encode(write.as_mut()).await?; + value.encode(write.as_mut()).await?; } + Ok(()) } } macro_rules! tuple { (($($t:ident)*) ($($T:ident)*)) => { impl<$($T: Decode),*> Decode for ($($T,)*) { - async fn decode(mut read: Pin<&mut R>) -> Self { - ($($T::decode(read.as_mut()).await,)*) + async fn decode(mut read: Pin<&mut R>) -> io::Result { + Ok(($($T::decode(read.as_mut()).await?,)*)) } } impl<$($T: Encode),*> Encode for ($($T,)*) { - async fn encode(&self, mut write: Pin<&mut W>) { + async fn encode(&self, mut write: Pin<&mut W>) -> io::Result<()> { let ($($t,)*) = self; - $( $t.encode(write.as_mut()).await; )* + $( $t.encode(write.as_mut()).await?; )* + Ok(()) } } }; @@ -243,63 +254,67 @@ tuple!((t u v x y z a b c d e f g h i) (T U V X Y Z A B C D E F G H I)); tuple!((t u v x y z a b c d e f g h i j) (T U V X Y Z A B C D E F G H I J)); // 16 impl Decode for () { - async fn decode(_: Pin<&mut R>) -> Self {} + async fn decode(_: Pin<&mut R>) -> io::Result { Ok(()) } } impl Encode for () { - async fn encode(&self, _: Pin<&mut W>) {} + async fn encode(&self, _: Pin<&mut W>) -> io::Result<()> { Ok(()) } } impl Decode for Never { - async fn decode(_: Pin<&mut R>) -> Self { + async fn decode(_: Pin<&mut R>) -> io::Result { unreachable!("A value of Never cannot exist so it can't have been serialized"); } } impl Encode for Never { - async fn encode(&self, _: Pin<&mut W>) { match *self {} } + async fn encode(&self, _: Pin<&mut W>) -> io::Result<()> { + match *self {} + } } impl Decode for bool { - async fn decode(mut read: Pin<&mut R>) -> Self { + async fn decode(mut read: Pin<&mut R>) -> io::Result { let mut buf = [0]; - read.read_exact(&mut buf).await.unwrap(); - buf[0] != 0 + read.read_exact(&mut buf).await?; + Ok(buf[0] != 0) } } impl Encode for bool { - async fn encode(&self, mut write: Pin<&mut W>) { - write.write_all(&[if *self { 0xffu8 } else { 0u8 }]).await.unwrap() + async fn encode(&self, mut write: Pin<&mut W>) -> io::Result<()> { + write.write_all(&[if *self { 0xffu8 } else { 0u8 }]).await } } impl Decode for [T; N] { - async fn decode(mut read: Pin<&mut R>) -> Self { - let v = stream(async |mut cx| { - for _ in 0..N { - cx.emit(T::decode(read.as_mut()).await).await - } - }) - .collect::>() - .await; - v.try_into().unwrap_or_else(|_| unreachable!("The length of this stream is statically known")) + async fn decode(mut read: Pin<&mut R>) -> io::Result { + let mut v = Vec::with_capacity(N); + for _ in 0..N { + v.push(T::decode(read.as_mut()).await?); + } + match v.try_into() { + Err(_) => unreachable!("The length of this stream is statically known"), + Ok(arr) => Ok(arr), + } } } impl Encode for [T; N] { - async fn encode(&self, mut write: Pin<&mut W>) { + async fn encode(&self, mut write: Pin<&mut W>) -> io::Result<()> { for t in self.iter() { - t.encode(write.as_mut()).await + t.encode(write.as_mut()).await? } + Ok(()) } } macro_rules! two_end_range { ($this:ident, $name:tt, $op:tt, $start:expr, $end:expr) => { impl Decode for $name { - async fn decode(mut read: Pin<&mut R>) -> Self { - T::decode(read.as_mut()).await $op T::decode(read).await + async fn decode(mut read: Pin<&mut R>) -> io::Result { + Ok(T::decode(read.as_mut()).await? $op T::decode(read).await?) } } impl Encode for $name { - async fn encode(&self, mut write: Pin<&mut W>) { + async fn encode(&self, mut write: Pin<&mut W>) -> io::Result<()> { let $this = self; - ($start).encode(write.as_mut()).await; - ($end).encode(write).await; + ($start).encode(write.as_mut()).await?; + ($end).encode(write).await?; + Ok(()) } } } @@ -311,12 +326,12 @@ two_end_range!(x, RangeInclusive, ..=, x.start(), x.end()); macro_rules! smart_ptr { ($name:tt) => { impl Decode for $name { - async fn decode(read: Pin<&mut R>) -> Self { - $name::new(T::decode(read).await) + async fn decode(read: Pin<&mut R>) -> io::Result { + Ok($name::new(T::decode(read).await?)) } } impl Encode for $name { - async fn encode(&self, write: Pin<&mut W>) { + async fn encode(&self, write: Pin<&mut W>) -> io::Result<()> { (**self).encode(write).await } } @@ -328,12 +343,12 @@ smart_ptr!(Rc); smart_ptr!(Box); impl Decode for char { - async fn decode(read: Pin<&mut R>) -> Self { - char::from_u32(u32::decode(read).await).unwrap() + async fn decode(read: Pin<&mut R>) -> io::Result { + char::from_u32(u32::decode(read).await?).ok_or_else(decode_err) } } impl Encode for char { - async fn encode(&self, write: Pin<&mut W>) { + async fn encode(&self, write: Pin<&mut W>) -> io::Result<()> { (*self as u32).encode(write).await } } diff --git a/orchid-api-traits/src/helpers.rs b/orchid-api-traits/src/helpers.rs index bf48df0..e263b69 100644 --- a/orchid-api-traits/src/helpers.rs +++ b/orchid-api-traits/src/helpers.rs @@ -1,24 +1,24 @@ -use std::future::Future; -use std::pin::Pin; +use std::error::Error; +use std::io; +use std::pin::{Pin, pin}; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::task::{Context, Poll, Wake}; -use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use futures::{AsyncRead, AsyncReadExt, AsyncWrite}; use itertools::{Chunk, Itertools}; use crate::Encode; -pub async fn encode_enum<'a, W: AsyncWrite + ?Sized, F: Future>( +pub async fn encode_enum<'a, W: AsyncWrite + ?Sized>( mut write: Pin<&'a mut W>, id: u8, - f: impl FnOnce(Pin<&'a mut W>) -> F, -) { - id.encode(write.as_mut()).await; + f: impl AsyncFnOnce(Pin<&'a mut W>) -> io::Result<()>, +) -> io::Result<()> { + id.encode(write.as_mut()).await?; f(write).await } -pub async fn write_exact(mut write: Pin<&mut W>, bytes: &'static [u8]) { - write.write_all(bytes).await.expect("Failed to write exact bytes") -} - pub fn print_bytes(b: &[u8]) -> String { (b.iter().map(|b| format!("{b:02x}"))) .chunks(4) @@ -27,16 +27,52 @@ pub fn print_bytes(b: &[u8]) -> String { .join(" ") } -pub async fn read_exact(mut read: Pin<&mut R>, bytes: &'static [u8]) { +pub async fn read_exact( + mut read: Pin<&mut R>, + bytes: &'static [u8], +) -> io::Result<()> { let mut data = vec![0u8; bytes.len()]; - read.read_exact(&mut data).await.expect("Failed to read bytes"); - if data != bytes { - panic!("Wrong bytes!\nExpected: {}\nFound: {}", print_bytes(bytes), print_bytes(&data)); + read.read_exact(&mut data).await?; + if data == bytes { + Ok(()) + } else { + let msg = + format!("Wrong bytes!\nExpected: {}\nFound: {}", print_bytes(bytes), print_bytes(&data)); + Err(io::Error::new(io::ErrorKind::InvalidData, msg)) } } -pub async fn enc_vec(enc: &impl Encode) -> Vec { +pub fn enc_vec(enc: &impl Encode) -> Vec { let mut vec = Vec::new(); - enc.encode(Pin::new(&mut vec)).await; + enc.encode_vec(&mut vec); vec } + +/// Raises a bool flag when called +struct FlagWaker(AtomicBool); +impl Wake for FlagWaker { + fn wake(self: Arc) { self.0.store(true, Ordering::Relaxed) } +} + +pub fn spin_on(fut: F) -> F::Output { + let flag = AtomicBool::new(false); + let flag_waker = Arc::new(FlagWaker(flag)); + let mut future = pin!(fut); + loop { + let waker = flag_waker.clone().into(); + let mut ctx = Context::from_waker(&waker); + match future.as_mut().poll(&mut ctx) { + // ideally the future should return synchronously + Poll::Ready(res) => break res, + // poorly written futures may yield and immediately wake + Poll::Pending if flag_waker.0.load(Ordering::Relaxed) => (), + // there is no external event to wait for, this has to be a deadlock + Poll::Pending => panic!("Future inside spin_on cannot block"), + }; + } +} + +pub fn decode_err() -> io::Error { io::Error::new(io::ErrorKind::InvalidData, "Unexpected zero") } +pub fn decode_err_for(e: impl Error) -> io::Error { + io::Error::new(io::ErrorKind::InvalidData, e.to_string()) +} diff --git a/orchid-api-traits/src/relations.rs b/orchid-api-traits/src/relations.rs index 0c6e5e5..bc7cb8f 100644 --- a/orchid-api-traits/src/relations.rs +++ b/orchid-api-traits/src/relations.rs @@ -9,7 +9,7 @@ pub trait Request: fmt::Debug + Sized + 'static { type Response: fmt::Debug + Coding + 'static; } -pub async fn respond(_: &R, rep: R::Response) -> Vec { enc_vec(&rep).await } +pub fn respond(_: &R, rep: R::Response) -> Vec { enc_vec(&rep) } pub trait Channel: 'static { type Req: Coding + Sized + 'static; diff --git a/orchid-api/Cargo.toml b/orchid-api/Cargo.toml index f860d20..d2492f2 100644 --- a/orchid-api/Cargo.toml +++ b/orchid-api/Cargo.toml @@ -11,6 +11,7 @@ orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" } orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" } futures = { version = "0.3.31", features = ["std"], default-features = false } itertools = "0.14.0" +unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" } [dev-dependencies] test_executors = "0.3.5" diff --git a/orchid-api/src/binary.rs b/orchid-api/src/binary.rs new file mode 100644 index 0000000..b58eb1f --- /dev/null +++ b/orchid-api/src/binary.rs @@ -0,0 +1,67 @@ +//! # Binary extension definition +//! +//! A binary extension is a DLL / shared object / dylib with a symbol called +//! `orchid_extension_main` which accepts a single argument of type [ExtCtx]. +//! Once that is received, communication continuees through the channel with the +//! same protocol outlined in [crate::proto] + +use unsync_pipe::{Reader, Writer}; + +/// !Send !Sync owned waker +#[repr(C)] +pub struct OwnedWakerVT { + data: *const (), + /// `self` + drop: extern "C" fn(*const ()), + /// `&self` + wake: extern "C" fn(*const ()), +} + +/// !Send !Sync, equivalent to `&mut Context<'a>`, hence no `drop`. +/// When received in [FutureVT::poll], it must not outlive the call. +#[repr(C)] +pub struct FutureContextVT { + data: *const (), + /// `&self` + waker: extern "C" fn(*const ()) -> OwnedWakerVT, +} + +/// ABI-stable `Poll<()>` +#[repr(C)] +pub enum UnitPoll { + Pending, + Ready, +} + +/// ABI-stable `Pin>>` +#[repr(C)] +pub struct FutureVT { + data: *const (), + /// `self` + drop: extern "C" fn(*const ()), + /// `&mut self` Equivalent to [Future::poll] + poll: extern "C" fn(*const (), FutureContextVT) -> UnitPoll, +} + +/// Owned extension context. +/// +/// When an extension starts, this is passed to +#[repr(C)] +pub struct ExtensionContext { + data: *const (), + /// `self` + drop: extern "C" fn(*const ()), + /// `self` upgrade to a later version of this struct. May only be called if + /// none of the other elements have been used yet. If a newer version isn't + /// supported, the server must return null, otherwise the the return value is + /// a pointer to the immediate next version of this struct + next: extern "C" fn(*const ()) -> *const (), + /// `&self` Add a future to this extension's task + spawn: extern "C" fn(*const (), FutureVT), + /// serialized [crate::HostExtChannel] + input: Reader, + /// serialized [crate::ExtHostChannel] + output: Writer, + /// UTF-8 log stream directly to log service. + log: Writer, +} diff --git a/orchid-api/src/lib.rs b/orchid-api/src/lib.rs index fae9832..6a52b41 100644 --- a/orchid-api/src/lib.rs +++ b/orchid-api/src/lib.rs @@ -1,3 +1,4 @@ +pub mod binary; mod lexer; pub use lexer::*; mod format; diff --git a/orchid-api/src/proto.rs b/orchid-api/src/proto.rs index 4763b16..95f2d4e 100644 --- a/orchid-api/src/proto.rs +++ b/orchid-api/src/proto.rs @@ -22,51 +22,54 @@ //! be preserved. Toolkits must ensure that the client code is able to observe //! the ordering of messages. +use std::io; use std::pin::Pin; -use futures::{AsyncRead, AsyncWrite}; +use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; use orchid_api_derive::{Coding, Hierarchy}; -use orchid_api_traits::{Channel, Decode, Encode, MsgSet, Request, read_exact, write_exact}; +use orchid_api_traits::{Channel, Decode, Encode, MsgSet, Request, read_exact}; use crate::{Sweeped, atom, expr, interner, lexer, logging, parser, system, tree}; static HOST_INTRO: &[u8] = b"Orchid host, binary API v0\n"; +#[derive(Clone, Debug)] pub struct HostHeader { pub log_strategy: logging::LogStrategy, pub msg_logs: logging::LogStrategy, } impl Decode for HostHeader { - async fn decode(mut read: Pin<&mut R>) -> Self { - read_exact(read.as_mut(), HOST_INTRO).await; - Self { - log_strategy: logging::LogStrategy::decode(read.as_mut()).await, - msg_logs: logging::LogStrategy::decode(read.as_mut()).await, - } + async fn decode(mut read: Pin<&mut R>) -> io::Result { + read_exact(read.as_mut(), HOST_INTRO).await?; + Ok(Self { + log_strategy: logging::LogStrategy::decode(read.as_mut()).await?, + msg_logs: logging::LogStrategy::decode(read.as_mut()).await?, + }) } } impl Encode for HostHeader { - async fn encode(&self, mut write: Pin<&mut W>) { - write_exact(write.as_mut(), HOST_INTRO).await; - self.log_strategy.encode(write.as_mut()).await; + async fn encode(&self, mut write: Pin<&mut W>) -> io::Result<()> { + write.write_all(HOST_INTRO).await?; + self.log_strategy.encode(write.as_mut()).await?; self.msg_logs.encode(write.as_mut()).await } } static EXT_INTRO: &[u8] = b"Orchid extension, binary API v0\n"; +#[derive(Clone, Debug)] pub struct ExtensionHeader { pub name: String, pub systems: Vec, } impl Decode for ExtensionHeader { - async fn decode(mut read: Pin<&mut R>) -> Self { - read_exact(read.as_mut(), EXT_INTRO).await; - Self { name: String::decode(read.as_mut()).await, systems: Vec::decode(read).await } + async fn decode(mut read: Pin<&mut R>) -> io::Result { + read_exact(read.as_mut(), EXT_INTRO).await?; + Ok(Self { name: String::decode(read.as_mut()).await?, systems: Vec::decode(read).await? }) } } impl Encode for ExtensionHeader { - async fn encode(&self, mut write: Pin<&mut W>) { - write_exact(write.as_mut(), EXT_INTRO).await; - self.name.encode(write.as_mut()).await; + async fn encode(&self, mut write: Pin<&mut W>) -> io::Result<()> { + write.write_all(EXT_INTRO).await?; + self.name.encode(write.as_mut()).await?; self.systems.encode(write).await } } @@ -169,9 +172,9 @@ mod tests { log_strategy: logging::LogStrategy::File("SomeFile".to_string()), msg_logs: logging::LogStrategy::File("SomeFile".to_string()), }; - let mut enc = &enc_vec(&hh).await[..]; + let mut enc = &enc_vec(&hh)[..]; eprintln!("Encoded to {enc:?}"); - HostHeader::decode(Pin::new(&mut enc)).await; + HostHeader::decode(Pin::new(&mut enc)).await.unwrap(); assert_eq!(enc, []); }) } @@ -188,9 +191,9 @@ mod tests { priority: NotNan::new(1f64).unwrap(), }], }; - let mut enc = &enc_vec(&eh).await[..]; + let mut enc = &enc_vec(&eh)[..]; eprintln!("Encoded to {enc:?}"); - ExtensionHeader::decode(Pin::new(&mut enc)).await; + ExtensionHeader::decode(Pin::new(&mut enc)).await.unwrap(); assert_eq!(enc, []) }) } diff --git a/orchid-base/Cargo.toml b/orchid-base/Cargo.toml index ab3bea7..106406b 100644 --- a/orchid-base/Cargo.toml +++ b/orchid-base/Cargo.toml @@ -24,9 +24,9 @@ orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" } ordered-float = "5.0.0" regex = "1.11.2" rust-embed = "8.7.2" -some_executor = "0.6.1" substack = "1.1.1" trait-set = "0.3.0" +task-local = "0.1.0" [dev-dependencies] futures = "0.3.31" diff --git a/orchid-base/src/builtin.rs b/orchid-base/src/builtin.rs deleted file mode 100644 index 07f6e9d..0000000 --- a/orchid-base/src/builtin.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::ops::Deref; -use std::rc::Rc; - -use futures::future::LocalBoxFuture; - -use crate::api; - -pub type Spawner = Rc)>; - -/// The 3 primary contact points with an extension are -/// - send a message -/// - wait for a message to arrive -/// - wait for the extension to stop after exit (this is the implicit Drop) -/// -/// There are no ordering guarantees about these -pub trait ExtPort { - #[must_use] - fn send<'a>(&'a self, msg: &'a [u8]) -> LocalBoxFuture<'a, ()>; - #[must_use] - fn recv(&self) -> LocalBoxFuture<'_, Option>>; -} - -pub struct ExtInit { - pub header: api::ExtensionHeader, - pub port: Box, -} -impl ExtInit { - pub async fn send(&self, msg: &[u8]) { self.port.send(msg).await } - pub async fn recv(&self) -> Option> { self.port.recv().await } -} -impl Deref for ExtInit { - type Target = api::ExtensionHeader; - fn deref(&self) -> &Self::Target { &self.header } -} diff --git a/orchid-base/src/error.rs b/orchid-base/src/error.rs index deb2301..c097bc4 100644 --- a/orchid-base/src/error.rs +++ b/orchid-base/src/error.rs @@ -5,9 +5,10 @@ use std::ops::Add; use std::rc::Rc; use std::sync::Arc; +use futures::FutureExt; use futures::future::join_all; use itertools::Itertools; -use some_executor::task_local; +use task_local::task_local; use crate::api; use crate::interner::{IStr, es, is}; @@ -237,7 +238,19 @@ task_local! { static REPORTER: Reporter; } -pub async fn with_reporter(fut: impl Future>) -> OrcRes { +/// Run the future with a new reporter, and return all errors reported within. +/// +/// If your future returns [OrcRes], see [try_with_reporter] +pub async fn with_reporter(fut: impl Future) -> OrcRes { + try_with_reporter(fut.map(Ok)).await +} + +/// Run the future with a new reporter, and return all errors either returned or +/// reported by it +/// +/// If your future may report errors but always returns an approximate value, +/// see [with_reporter] +pub async fn try_with_reporter(fut: impl Future>) -> OrcRes { let rep = Reporter::default(); let res = REPORTER.scope(rep.clone(), fut).await; let errors = rep.errors.take(); @@ -249,9 +262,8 @@ pub async fn with_reporter(fut: impl Future>) -> OrcRes } pub async fn is_erroring() -> bool { - REPORTER.with(|r| { - !r.expect("Sidechannel errors must be caught by a reporter").errors.borrow().is_empty() - }) + (REPORTER.try_with(|r| !r.errors.borrow().is_empty())) + .expect("Sidechannel errors must be caught by a reporter") } /// Report an error that is fatal and prevents a correct output, but @@ -259,11 +271,10 @@ pub async fn is_erroring() -> bool { /// This can be used for pub fn report(e: impl Into) { let errv = e.into(); - REPORTER.with(|r| match r { - Some(r) => r.errors.borrow_mut().extend(errv), - None => panic!( + REPORTER.try_with(|r| r.errors.borrow_mut().extend(errv.clone())).unwrap_or_else(|_| { + panic!( "Unhandled error! Sidechannel errors must be caught by an enclosing call to with_reporter.\n\ - Error: {errv}", - ), + Error: {errv}" + ) }) } diff --git a/orchid-base/src/format.rs b/orchid-base/src/format.rs index 331737b..8c9bc8c 100644 --- a/orchid-base/src/format.rs +++ b/orchid-base/src/format.rs @@ -304,6 +304,7 @@ pub async fn take_first_fmt(v: &(impl Format + ?Sized)) -> String { take_first(&v.print(&FmtCtxImpl { _foo: PhantomData }).await, false) } +#[derive(Default)] pub struct FmtCtxImpl<'a> { _foo: PhantomData<&'a ()>, } @@ -331,8 +332,8 @@ impl Format for Never { /// Format with default strategy. Currently equal to [take_first_fmt] pub async fn fmt(v: &(impl Format + ?Sized)) -> String { take_first_fmt(v).await } /// Format a sequence with default strategy. Currently equal to [take_first_fmt] -pub async fn fmt_v>( - v: impl IntoIterator, +pub async fn fmt_v( + v: impl IntoIterator>, ) -> impl Iterator { join_all(v.into_iter().map(|f| async move { take_first_fmt(f.borrow()).await })).await.into_iter() } diff --git a/orchid-base/src/interner.rs b/orchid-base/src/interner.rs index fb8e5b1..da44ac0 100644 --- a/orchid-base/src/interner.rs +++ b/orchid-base/src/interner.rs @@ -6,12 +6,16 @@ use std::rc::Rc; use std::{fmt, hash}; use futures::future::LocalBoxFuture; -use some_executor::task_local; +use task_local::task_local; use crate::api; -pub trait IStrHandle: AsRef {} -pub trait IStrvHandle: AsRef<[IStr]> {} +pub trait IStrHandle: AsRef { + fn rc(&self) -> Rc; +} +pub trait IStrvHandle: AsRef<[IStr]> { + fn rc(&self) -> Rc>; +} #[derive(Clone)] pub struct IStr(pub api::TStr, pub Rc); @@ -22,6 +26,7 @@ impl IStr { /// the same value only as long as at least one instance exists. If a value is /// no longer interned, the interner is free to forget about it. pub fn to_api(&self) -> api::TStr { self.0 } + pub fn rc(&self) -> Rc { self.1.rc() } } impl Deref for IStr { type Target = str; @@ -49,6 +54,7 @@ impl IStrv { /// the same value only as long as at least one instance exists. If a value is /// no longer interned, the interner is free to forget about it. pub fn to_api(&self) -> api::TStrv { self.0 } + pub fn rc(&self) -> Rc> { self.1.rc() } } impl Deref for IStrv { type Target = [IStr]; @@ -79,10 +85,10 @@ impl Debug for IStrv { } pub trait InternerSrv { - fn is(&self, v: &str) -> LocalBoxFuture<'static, IStr>; - fn es(&self, t: api::TStr) -> LocalBoxFuture<'static, IStr>; - fn iv(&self, v: &[IStr]) -> LocalBoxFuture<'static, IStrv>; - fn ev(&self, t: api::TStrv) -> LocalBoxFuture<'static, IStrv>; + fn is<'a>(&'a self, v: &'a str) -> LocalBoxFuture<'a, IStr>; + fn es(&self, t: api::TStr) -> LocalBoxFuture<'_, IStr>; + fn iv<'a>(&'a self, v: &'a [IStr]) -> LocalBoxFuture<'a, IStrv>; + fn ev(&self, t: api::TStrv) -> LocalBoxFuture<'_, IStrv>; } task_local! { @@ -94,10 +100,283 @@ pub async fn with_interner(val: Rc, fut: F) -> F::Ou } fn get_interner() -> Rc { - INTERNER.with(|i| i.expect("Interner not initialized").clone()) + INTERNER.try_with(|i| i.clone()).expect("Interner not initialized") } pub async fn is(v: &str) -> IStr { get_interner().is(v).await } pub async fn iv(v: &[IStr]) -> IStrv { get_interner().iv(v).await } pub async fn es(v: api::TStr) -> IStr { get_interner().es(v).await } pub async fn ev(v: api::TStrv) -> IStrv { get_interner().ev(v).await } + +pub mod local_interner { + use std::borrow::Borrow; + use std::cell::RefCell; + use std::fmt::Debug; + use std::future; + use std::hash::{BuildHasher, Hash}; + use std::num::NonZeroU64; + use std::rc::{Rc, Weak}; + + use futures::future::LocalBoxFuture; + use hashbrown::hash_table::{Entry, OccupiedEntry, VacantEntry}; + use hashbrown::{DefaultHashBuilder, HashTable}; + use orchid_api_traits::Coding; + + use super::{IStr, IStrHandle, IStrv, IStrvHandle, InternerSrv}; + use crate::api; + + /// Associated types and methods for parallel concepts between scalar and + /// vector interning + pub trait InternableCard: 'static + Sized + Default + Debug { + /// API representation of an interner key + type Token: Clone + Copy + Debug + Hash + Eq + PartialOrd + Ord + Coding + 'static; + /// Owned version of interned value physically held by `'static` interner + /// and token + type Data: 'static + Borrow + Eq + Hash + Debug; + /// Borrowed version of interned value placed in intern queries to avoid a + /// copy + type Borrow: ToOwned + ?Sized + Eq + Hash + Debug; + /// Smart object handed out by the interner for storage and comparison in + /// third party code. [IStr] or [IStrv] + type Interned: Clone + Debug; + /// Create smart object from token for fast comparison and a handle for + /// everything else incl. virtual drop + fn new_interned(token: Self::Token, handle: Rc>) -> Self::Interned; + } + + #[derive(Default, Debug)] + pub struct StrBranch; + impl InternableCard for StrBranch { + type Data = String; + type Token = api::TStr; + type Borrow = str; + type Interned = IStr; + fn new_interned(t: Self::Token, h: Rc>) -> Self::Interned { IStr(t, h) } + } + + #[derive(Default, Debug)] + pub struct StrvBranch; + impl InternableCard for StrvBranch { + type Data = Vec; + type Token = api::TStrv; + type Borrow = [IStr]; + type Interned = IStrv; + fn new_interned(t: Self::Token, h: Rc>) -> Self::Interned { IStrv(t, h) } + } + + /// Pairs interned data with its internment key + #[derive(Debug)] + struct Data { + token: B::Token, + data: Rc, + } + impl Clone for Data { + fn clone(&self) -> Self { Self { token: self.token, data: self.data.clone() } } + } + + /// Implementor for the trait objects held by [IStr] and [IStrv] + pub struct Handle { + data: Data, + parent: Weak>>, + } + impl IStrHandle for Handle { + fn rc(&self) -> Rc { self.data.data.clone() } + } + impl AsRef for Handle { + fn as_ref(&self) -> &str { self.data.data.as_ref().as_ref() } + } + impl IStrvHandle for Handle { + fn rc(&self) -> Rc> { self.data.data.clone() } + } + impl AsRef<[IStr]> for Handle { + fn as_ref(&self) -> &[IStr] { self.data.data.as_ref().as_ref() } + } + impl Drop for Handle { + fn drop(&mut self) { + let Some(parent) = self.parent.upgrade() else { return }; + if let Entry::Occupied(ent) = + parent.borrow_mut().entry_by_data(self.data.data.as_ref().borrow()) + { + ent.remove(); + } + if let Entry::Occupied(ent) = parent.borrow_mut().entry_by_tok(self.data.token) { + ent.remove(); + } + } + } + + /// Information retained about an interned token indexed both by key and + /// value. + struct Rec { + /// This reference is weak, but the [Drop] handler of [Handle] removes all + /// [Rec]s from the interner so it is guaranteed to be live. + handle: Weak>, + /// Keys for indexing from either table + data: Data, + } + + /// Read data from an occupied entry in an interner. The equivalent insert + /// command is [insert] + fn read(entry: OccupiedEntry<'_, Rec>) -> B::Interned { + let hand = entry.get().handle.upgrade().expect("Found entry but handle already dropped"); + B::new_interned(entry.get().data.token, hand) + } + + /// Insert some data into an entry borrowed from this same interner. + /// The equivalent read command is [read] + fn insert(entry: VacantEntry<'_, Rec>, handle: Rc>) { + entry.insert(Rec { data: handle.data.clone(), handle: Rc::downgrade(&handle) }); + } + + #[derive(Default)] + struct IntData { + by_tok: HashTable>, + by_data: HashTable>, + hasher: DefaultHashBuilder, + } + impl IntData { + fn entry_by_data(&mut self, query: &B::Borrow) -> Entry<'_, Rec> { + self.by_data.entry( + self.hasher.hash_one(query), + |rec| rec.data.data.as_ref().borrow() == query, + |rec| self.hasher.hash_one(rec.data.data.as_ref().borrow()), + ) + } + fn entry_by_tok(&mut self, token: B::Token) -> Entry<'_, Rec> { + self.by_tok.entry( + self.hasher.hash_one(token), + |rec| rec.data.token == token, + |rec| self.hasher.hash_one(rec.data.token), + ) + } + } + + /// Failing intern command that can be recovered if the value is found + /// elsewhere + pub struct InternError<'a, B: InternableCard> { + int: &'a Int, + query: &'a B::Borrow, + } + impl InternError<'_, B> { + /// If a racing write populates the entry, the continuation returns that + /// value and discards its argument + pub fn set_if_empty(self, token: B::Token) -> B::Interned { + let mut int_data = self.int.0.borrow_mut(); + match int_data.entry_by_data(self.query) { + Entry::Occupied(ent) => read(ent), + Entry::Vacant(ent) => { + let hand = self.int.mk_handle(Data { token, data: Rc::new(self.query.to_owned()) }); + insert(ent, hand.clone()); + let Entry::Vacant(other_ent) = int_data.entry_by_tok(token) else { + panic!("Data and key tables out of sync") + }; + insert(other_ent, hand.clone()); + B::new_interned(token, hand) + }, + } + } + } + impl Debug for InternError<'_, B> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("InternEntry").field(&self.query).finish() + } + } + + /// Failing extern command that can be recovered if the value is found + /// elsewhere + pub struct ExternError<'a, B: InternableCard> { + int: &'a Int, + token: B::Token, + } + impl ExternError<'_, B> { + /// If a racing write populates the entry, the continuation returns that + /// value and discards its argument + pub fn set_if_empty(&self, data: Rc) -> B::Interned { + let mut int_data = self.int.0.borrow_mut(); + match int_data.entry_by_tok(self.token) { + Entry::Occupied(ent) => read(ent), + Entry::Vacant(ent) => { + let hand = self.int.mk_handle(Data { token: self.token, data: data.clone() }); + insert(ent, hand.clone()); + let Entry::Vacant(other_ent) = int_data.entry_by_data(data.as_ref().borrow()) else { + panic!("Data and key tables out of sync") + }; + insert(other_ent, hand.clone()); + B::new_interned(self.token, hand) + }, + } + } + } + impl Debug for ExternError<'_, B> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("ExternEntry").field(&self.token).finish() + } + } + + #[derive(Default)] + pub struct Int(Rc>>); + impl Int { + fn mk_handle(&self, data: Data) -> Rc> { + Rc::new(Handle { data: data.clone(), parent: Rc::downgrade(&self.0.clone()) }) + } + + /// Look up by value, or yield to figure out its ID from elsewhere + pub fn i<'a>(&'a self, query: &'a B::Borrow) -> Result> { + if let Entry::Occupied(val) = self.0.borrow_mut().entry_by_data(query) { + return Ok(read(val)); + } + Err(InternError { int: self, query }) + } + + /// Look up by key or yield to figure out its value from elsewhere + pub fn e(&self, token: B::Token) -> Result> { + if let Entry::Occupied(ent) = self.0.borrow_mut().entry_by_tok(token) { + return Ok(read(ent)); + } + Err(ExternError { int: self, token }) + } + } + + thread_local! { + static NEXT_ID: RefCell = 0.into(); + } + + fn with_new_id(fun: impl FnOnce(NonZeroU64) -> T) -> T { + fun( + NonZeroU64::new(NEXT_ID.with_borrow_mut(|id| { + *id += 1; + *id + })) + .unwrap(), + ) + } + + #[derive(Default)] + struct LocalInterner { + str: Int, + strv: Int, + } + impl InternerSrv for LocalInterner { + fn is<'a>(&'a self, v: &'a str) -> LocalBoxFuture<'a, IStr> { + match self.str.i(v) { + Ok(int) => Box::pin(future::ready(int)), + Err(e) => with_new_id(|id| Box::pin(future::ready(e.set_if_empty(api::TStr(id))))), + } + } + fn es(&self, t: api::TStr) -> LocalBoxFuture<'_, IStr> { + Box::pin(future::ready(self.str.e(t).expect("Unrecognized token cannot be externed"))) + } + fn iv<'a>(&'a self, v: &'a [IStr]) -> LocalBoxFuture<'a, IStrv> { + match self.strv.i(v) { + Ok(int) => Box::pin(future::ready(int)), + Err(e) => with_new_id(|id| Box::pin(future::ready(e.set_if_empty(api::TStrv(id))))), + } + } + fn ev(&self, t: orchid_api::TStrv) -> LocalBoxFuture<'_, IStrv> { + Box::pin(future::ready(self.strv.e(t).expect("Unrecognized token cannot be externed"))) + } + } + + /// Creates a basic thread-local interner for testing and root role. + pub fn local_interner() -> Rc { Rc::::default() } +} diff --git a/orchid-base/src/lib.rs b/orchid-base/src/lib.rs index b812005..654811d 100644 --- a/orchid-base/src/lib.rs +++ b/orchid-base/src/lib.rs @@ -3,7 +3,6 @@ use orchid_api as api; pub mod box_cow; pub mod boxed_iter; -pub mod builtin; pub mod char_filter; pub mod clone; pub mod combine; @@ -14,6 +13,7 @@ pub mod id_store; pub mod interner; pub mod iter_utils; pub mod join; +mod localset; pub mod location; pub mod logging; mod match_mapping; diff --git a/orchid-base/src/localset.rs b/orchid-base/src/localset.rs new file mode 100644 index 0000000..ca0f238 --- /dev/null +++ b/orchid-base/src/localset.rs @@ -0,0 +1,48 @@ +use std::collections::VecDeque; +use std::pin::Pin; +use std::task::Poll; + +use futures::StreamExt; +use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender, unbounded}; +use futures::future::LocalBoxFuture; + +pub struct LocalSet<'a, E> { + receiver: UnboundedReceiver>>, + pending: VecDeque>>, +} +impl<'a, E> LocalSet<'a, E> { + pub fn new() -> (UnboundedSender>>, Self) { + let (sender, receiver) = unbounded(); + (sender, Self { receiver, pending: VecDeque::new() }) + } +} +impl Future for LocalSet<'_, E> { + type Output = Result<(), E>; + fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { + let this = self.get_mut(); + let mut any_pending = false; + loop { + match this.receiver.poll_next_unpin(cx) { + Poll::Pending => { + any_pending = true; + break; + }, + Poll::Ready(None) => break, + Poll::Ready(Some(fut)) => this.pending.push_back(fut), + } + } + let count = this.pending.len(); + for _ in 0..count { + let mut req = this.pending.pop_front().unwrap(); + match req.as_mut().poll(cx) { + Poll::Ready(Ok(())) => (), + Poll::Ready(Err(e)) => return Poll::Ready(Err(e)), + Poll::Pending => { + any_pending = true; + this.pending.push_back(req) + }, + } + } + if any_pending { Poll::Pending } else { Poll::Ready(Ok(())) } + } +} diff --git a/orchid-base/src/logging.rs b/orchid-base/src/logging.rs index 95bdb32..0f35f62 100644 --- a/orchid-base/src/logging.rs +++ b/orchid-base/src/logging.rs @@ -4,6 +4,7 @@ use std::io::{Write, stderr}; pub use api::LogStrategy; use itertools::Itertools; +use task_local::task_local; use crate::api; @@ -34,3 +35,13 @@ impl Logger { } } } + +task_local! { + static LOGGER: Logger; +} + +pub async fn with_logger(logger: Logger, fut: F) -> F::Output { + LOGGER.scope(logger, fut).await +} + +pub fn logger() -> Logger { LOGGER.try_with(|l| l.clone()).expect("Logger not set!") } diff --git a/orchid-base/src/msg.rs b/orchid-base/src/msg.rs index c61a988..1ed0c09 100644 --- a/orchid-base/src/msg.rs +++ b/orchid-base/src/msg.rs @@ -6,7 +6,8 @@ use orchid_api_traits::{Decode, Encode}; pub async fn send_msg(mut write: Pin<&mut impl AsyncWrite>, msg: &[u8]) -> io::Result<()> { let mut len_buf = vec![]; - u32::try_from(msg.len()).unwrap().encode(Pin::new(&mut len_buf)).await; + let len_prefix = u32::try_from(msg.len()).expect("Message over 4GB not permitted on channel"); + len_prefix.encode_vec(&mut len_buf); write.write_all(&len_buf).await?; write.write_all(msg).await?; write.flush().await @@ -15,7 +16,7 @@ pub async fn send_msg(mut write: Pin<&mut impl AsyncWrite>, msg: &[u8]) -> io::R pub async fn recv_msg(mut read: Pin<&mut impl AsyncRead>) -> io::Result> { let mut len_buf = [0u8; (u32::BITS / 8) as usize]; read.read_exact(&mut len_buf).await?; - let len = u32::decode(Pin::new(&mut &len_buf[..])).await; + let len = u32::decode(Pin::new(&mut &len_buf[..])).await?; let mut msg = vec![0u8; len as usize]; read.read_exact(&mut msg).await?; Ok(msg) diff --git a/orchid-base/src/reqnot.rs b/orchid-base/src/reqnot.rs index 38a9b06..4af976b 100644 --- a/orchid-base/src/reqnot.rs +++ b/orchid-base/src/reqnot.rs @@ -1,12 +1,11 @@ use std::cell::RefCell; -use std::collections::VecDeque; use std::future::Future; use std::marker::PhantomData; use std::pin::{Pin, pin}; use std::rc::Rc; -use std::task::Poll; +use std::{io, mem}; -use async_fn_stream::stream; +use async_fn_stream::try_stream; use bound::Bound; use derive_destructure::destructure; use futures::channel::mpsc::{self, Receiver, Sender, channel}; @@ -14,23 +13,29 @@ use futures::channel::oneshot; use futures::future::LocalBoxFuture; use futures::lock::{Mutex, MutexGuard}; use futures::{ - AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, Stream, StreamExt, stream, stream_select, + AsyncRead, AsyncWrite, AsyncWriteExt, FutureExt, SinkExt, Stream, StreamExt, stream_select, }; use hashbrown::HashMap; -use orchid_api_traits::{Channel, Decode, Encode, Request, UnderRoot}; +use orchid_api_traits::{Decode, Encode, Request, UnderRoot}; + +use crate::localset::LocalSet; #[must_use = "Receipts indicate that a required action has been performed within a function. \ Most likely this should be returned somewhere."] pub struct Receipt<'a>(PhantomData<&'a mut ()>); +impl Receipt<'_> { + /// Only call this function from a custom implementation of [RepWriter] + pub fn _new() -> Self { Self(PhantomData) } +} /// Write guard to outbound for the purpose of serializing a request. Only one /// can exist at a time. Dropping this object should panic. -pub trait ReqWriter { +pub trait ReqWriter<'a> { /// Access to the underlying channel. This may be buffered. fn writer(&mut self) -> Pin<&mut dyn AsyncWrite>; /// Finalize the request, release the outbound channel, then queue for the /// reply on the inbound channel. - fn send(self: Box) -> LocalBoxFuture<'static, Box>; + fn send(self: Box) -> LocalBoxFuture<'a, io::Result + 'a>>>; } /// Write guard to inbound for the purpose of deserializing a reply. While held, @@ -40,49 +45,106 @@ pub trait ReqWriter { /// synchronously, because the API isn't cancellation safe in general so it is a /// programmer error in all cases to drop an object related to it without proper /// cleanup. -pub trait RepReader { +pub trait RepReader<'a> { /// Access to the underlying channel. The length of the message is inferred /// from the number of bytes read so this must not be buffered. fn reader(&mut self) -> Pin<&mut dyn AsyncRead>; /// Finish reading the request - fn finish(self: Box) -> LocalBoxFuture<'static, ()>; + fn finish(self: Box) -> LocalBoxFuture<'a, ()>; } /// Write guard to outbound for the purpose of serializing a notification. /// /// Dropping this object should panic for the same reason [RepReader] panics -pub trait MsgWriter { +pub trait MsgWriter<'a> { /// Access to the underlying channel. This may be buffered. fn writer(&mut self) -> Pin<&mut dyn AsyncWrite>; /// Send the notification - fn finish(self: Box) -> LocalBoxFuture<'static, ()>; + fn finish(self: Box) -> LocalBoxFuture<'a, io::Result<()>>; } /// For initiating outbound requests and notifications pub trait Client { - fn start_request(&self) -> LocalBoxFuture<'_, Box>; - fn start_notif(&self) -> LocalBoxFuture<'_, Box>; + fn start_request(&self) -> LocalBoxFuture<'_, io::Result + '_>>>; + fn start_notif(&self) -> LocalBoxFuture<'_, io::Result + '_>>>; } +impl ClientExt for T {} /// Extension trait with convenience methods that handle outbound request and /// notif lifecycle and typing #[allow(async_fn_in_trait)] -pub trait ClientExt: Client { - async fn request>(&self, t: T) -> T::Response { - let mut req = self.start_request().await; - t.into_root().encode(req.writer().as_mut()).await; - let mut rep = req.send().await; +pub trait ClientExt: Client { + async fn request>(&self, t: T) -> io::Result { + let mut req = self.start_request().await?; + t.into_root().encode(req.writer().as_mut()).await?; + let mut rep = req.send().await?; let response = T::Response::decode(rep.reader()).await; rep.finish().await; response } - async fn notify>(&self, t: T) { - let mut notif = self.start_notif().await; - t.into_root().encode(notif.writer().as_mut()).await; - notif.finish().await; + async fn notify>(&self, t: T) -> io::Result<()> { + let mut notif = self.start_notif().await?; + t.into_root().encode(notif.writer().as_mut()).await?; + notif.finish().await?; + Ok(()) + } +} + +pub trait ReqReader<'a> { + fn reader(&mut self) -> Pin<&mut dyn AsyncRead>; + fn finish(self: Box) -> LocalBoxFuture<'a, Box + 'a>>; +} +impl<'a, T: ReqReader<'a> + ?Sized> ReqReaderExt<'a> for T {} +#[allow(async_fn_in_trait)] +pub trait ReqReaderExt<'a>: ReqReader<'a> { + async fn read_req(&mut self) -> io::Result { R::decode(self.reader()).await } + async fn reply( + self: Box, + req: impl Evidence, + rep: &R::Response, + ) -> io::Result> { + self.finish().await.reply(req, rep).await + } + async fn start_reply(self: Box) -> io::Result + 'a>> { + self.finish().await.start_reply().await + } +} + +pub trait ReqHandle<'a> { + fn start_reply(self: Box) -> LocalBoxFuture<'a, io::Result + 'a>>>; +} +impl<'a, T: ReqHandle<'a> + ?Sized> ReqHandleExt<'a> for T {} +#[allow(async_fn_in_trait)] +pub trait ReqHandleExt<'a>: ReqHandle<'a> { + async fn reply( + self: Box, + _: impl Evidence, + rep: &Req::Response, + ) -> io::Result> { + let mut reply = self.start_reply().await?; + rep.encode(reply.writer()).await?; + reply.finish().await + } +} + +pub trait RepWriter<'a> { + fn writer(&mut self) -> Pin<&mut dyn AsyncWrite>; + fn finish(self: Box) -> LocalBoxFuture<'a, io::Result>>; +} + +pub trait MsgReader<'a> { + fn reader(&mut self) -> Pin<&mut dyn AsyncRead>; + fn finish(self: Box) -> LocalBoxFuture<'a, ()>; +} +impl<'a, T: ?Sized + MsgReader<'a>> MsgReaderExt<'a> for T {} +#[allow(async_fn_in_trait)] +pub trait MsgReaderExt<'a>: MsgReader<'a> { + async fn read(mut self: Box) -> io::Result { + let n = N::decode(self.reader()).await; + self.finish().await; + n } } -impl ClientExt for T {} /// A form of [Evidence] that doesn't require the value to be kept around pub struct Witness(PhantomData); @@ -105,64 +167,52 @@ type IoLock = Rc>>>; type IoGuard = Bound>>, IoLock>; /// An incoming request. This holds a lock on the ingress channel. -pub struct ReqReader<'a> { - id: u64, +pub struct IoReqReader<'a> { + prefix: &'a [u8], read: IoGuard, write: &'a Mutex>, } -impl<'a> ReqReader<'a> { - /// Access - pub fn reader(&mut self) -> Pin<&mut dyn AsyncRead> { self.read.as_mut() } - pub async fn read_req(&mut self) -> R { R::decode(self.reader()).await } - pub async fn start_reply(self) -> RepWriter<'a> { self.branch().await.start_reply().await } - pub async fn reply(self, req: impl Evidence, rep: &R::Response) -> Receipt<'a> { - self.branch().await.reply(req, rep).await +impl<'a> ReqReader<'a> for IoReqReader<'a> { + fn reader(&mut self) -> Pin<&mut dyn AsyncRead> { self.read.as_mut() } + fn finish(self: Box) -> LocalBoxFuture<'a, Box + 'a>> { + Box::pin(async { + Box::new(IoReqHandle { prefix: self.prefix, write: self.write }) as Box> + }) } - pub async fn branch(self) -> ReqHandle<'a> { ReqHandle { id: self.id, write: self.write } } } -pub struct ReqHandle<'a> { - id: u64, +pub struct IoReqHandle<'a> { + prefix: &'a [u8], write: &'a Mutex>, } -impl<'a> ReqHandle<'a> { - pub async fn reply( - self, - _: impl Evidence, - rep: &Req::Response, - ) -> Receipt<'a> { - let mut reply = self.start_reply().await; - rep.encode(reply.writer()).await; - reply.send().await - } - pub async fn start_reply(self) -> RepWriter<'a> { - let mut write = self.write.lock().await; - (!self.id).encode(write.as_mut()).await; - RepWriter { write } +impl<'a> ReqHandle<'a> for IoReqHandle<'a> { + fn start_reply(self: Box) -> LocalBoxFuture<'a, io::Result + 'a>>> { + Box::pin(async move { + let mut write = self.write.lock().await; + write.as_mut().write_all(self.prefix).await?; + Ok(Box::new(IoRepWriter { write }) as Box>) + }) } } -pub struct RepWriter<'a> { +pub struct IoRepWriter<'a> { write: MutexGuard<'a, IoRef>, } -impl<'a> RepWriter<'a> { - pub fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> { self.write.as_mut() } - pub async fn send(mut self) -> Receipt<'a> { - self.writer().flush().await.unwrap(); - Receipt(PhantomData) +impl<'a> RepWriter<'a> for IoRepWriter<'a> { + fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> { self.write.as_mut() } + fn finish(mut self: Box) -> LocalBoxFuture<'a, io::Result>> { + Box::pin(async move { + self.writer().flush().await?; + Ok(Receipt(PhantomData)) + }) } } -pub struct NotifReader<'a> { +pub struct IoMsgReader<'a> { _pd: PhantomData<&'a mut ()>, read: IoGuard, } -impl<'a> NotifReader<'a> { - pub fn reader(&mut self) -> Pin<&mut dyn AsyncRead> { self.read.as_mut() } - pub async fn read(mut self) -> N { - let n = N::decode(self.reader()).await; - self.release().await; - n - } - pub async fn release(self) {} +impl<'a> MsgReader<'a> for IoMsgReader<'a> { + fn reader(&mut self) -> Pin<&mut dyn AsyncRead> { self.read.as_mut() } + fn finish(self: Box) -> LocalBoxFuture<'static, ()> { Box::pin(async {}) } } #[derive(Debug)] @@ -187,14 +237,14 @@ impl IoClient { } } impl Client for IoClient { - fn start_notif(&self) -> LocalBoxFuture<'_, Box> { + fn start_notif(&self) -> LocalBoxFuture<'_, io::Result + '_>>> { Box::pin(async { let mut o = self.lock_out().await; - 0u64.encode(o.as_mut()).await; - Box::new(IoNotifWriter { o }) as Box + 0u64.encode(o.as_mut()).await?; + Ok(Box::new(IoNotifWriter { o }) as Box) }) } - fn start_request(&self) -> LocalBoxFuture<'_, Box> { + fn start_request(&self) -> LocalBoxFuture<'_, io::Result + '_>>> { Box::pin(async { let id = { let mut id_g = self.id.borrow_mut(); @@ -206,8 +256,8 @@ impl Client for IoClient { self.subscribe.as_ref().clone().send(ReplySub { id, ack, cb }).await.unwrap(); got_ack.await.unwrap(); let mut w = self.lock_out().await; - id.encode(w.as_mut()).await; - Box::new(IoReqWriter { reply, w }) as Box + id.encode(w.as_mut()).await?; + Ok(Box::new(IoReqWriter { reply, w }) as Box) }) } } @@ -216,13 +266,15 @@ struct IoReqWriter { reply: oneshot::Receiver>, w: IoGuard, } -impl ReqWriter for IoReqWriter { +impl<'a> ReqWriter<'a> for IoReqWriter { fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> { self.w.as_mut() } - fn send(self: Box) -> LocalBoxFuture<'static, Box> { + fn send(self: Box) -> LocalBoxFuture<'a, io::Result + 'a>>> { Box::pin(async { - let Self { reply, .. } = *self; + let Self { reply, mut w } = *self; + w.flush().await?; + mem::drop(w); let i = reply.await.expect("Client dropped before reply received"); - Box::new(IoRepReader { i }) as Box + Ok(Box::new(IoRepReader { i }) as Box) }) } } @@ -230,7 +282,7 @@ impl ReqWriter for IoReqWriter { struct IoRepReader { i: IoGuard, } -impl RepReader for IoRepReader { +impl<'a> RepReader<'a> for IoRepReader { fn reader(&mut self) -> Pin<&mut dyn AsyncRead> { self.i.as_mut() } fn finish(self: Box) -> LocalBoxFuture<'static, ()> { Box::pin(async {}) } } @@ -239,11 +291,10 @@ impl RepReader for IoRepReader { struct IoNotifWriter { o: IoGuard, } -impl MsgWriter for IoNotifWriter { +impl<'a> MsgWriter<'a> for IoNotifWriter { fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> { self.o.as_mut() } - fn finish(self: Box) -> LocalBoxFuture<'static, ()> { - self.destructure(); - Box::pin(async {}) + fn finish(mut self: Box) -> LocalBoxFuture<'static, io::Result<()>> { + Box::pin(async move { self.o.flush().await }) } } @@ -262,12 +313,12 @@ impl CommCtx { /// parameters are associated with the client and serve to ensure with a runtime /// check that the correct message families are sent in the correct directions /// across the channel. -pub fn io_comm( +pub fn io_comm( o: Rc>>>, i: Mutex>>, - notif: impl for<'a> AsyncFn(NotifReader<'a>), - req: impl for<'a> AsyncFn(ReqReader<'a>) -> Receipt<'a>, -) -> (impl ClientExt, CommCtx, impl Future) { + notif: impl for<'a> AsyncFn(Box + 'a>) -> io::Result<()>, + req: impl for<'a> AsyncFn(Box + 'a>) -> io::Result>, +) -> (impl Client + 'static, CommCtx, impl Future>) { let i = Rc::new(i); let (onsub, client) = IoClient::new(o.clone()); let (exit, onexit) = channel(1); @@ -278,65 +329,76 @@ pub fn io_comm( Exit, } let exiting = RefCell::new(false); - let input_stream = stream(async |mut h| { + let input_stream = try_stream(async |mut h| { loop { let mut g = Bound::async_new(i.clone(), async |i| i.lock().await).await; - let id = u64::decode(g.as_mut()).await; - h.emit(Event::Input(id, g)).await; + match u64::decode(g.as_mut()).await { + Ok(id) => h.emit(Event::Input(id, g)).await, + Err(e) + if matches!( + e.kind(), + io::ErrorKind::BrokenPipe + | io::ErrorKind::ConnectionAborted + | io::ErrorKind::UnexpectedEof + ) => + h.emit(Event::Exit).await, + Err(e) => return Err(e), + } } }); - let pending_reqs = RefCell::new(VecDeque::>::new()); - // this stream will never yield a value - let mut fork_stream = pin!( - stream::poll_fn(|cx| { - let mut reqs_g = pending_reqs.borrow_mut(); - reqs_g.retain_mut(|req| match req.as_mut().poll(cx) { - Poll::Pending => true, - Poll::Ready(()) => false, - }); - if *exiting.borrow() { Poll::Ready(None) } else { Poll::Pending } - }) - .fuse() - ); + let (mut add_pending_req, fork_future) = LocalSet::new(); + let mut fork_stream = pin!(fork_future.fuse().into_stream()); let mut pending_replies = HashMap::new(); - { + 'body: { let mut shared = pin!(stream_select!( - pin!(input_stream) as Pin<&mut dyn Stream>, - onsub.map(Event::Sub), - fork_stream.as_mut(), - onexit.map(|()| Event::Exit), + pin!(input_stream) as Pin<&mut dyn Stream>>, + onsub.map(|sub| Ok(Event::Sub(sub))), + fork_stream.as_mut().map(|res| { + res.map(|()| panic!("this substream cannot exit while the loop is running")) + }), + onexit.map(|()| Ok(Event::Exit)), )); while let Some(next) = shared.next().await { match next { - Event::Exit => { + Err(e) => break 'body Err(e), + Ok(Event::Exit) => { *exiting.borrow_mut() = true; break; }, - Event::Sub(ReplySub { id, ack, cb }) => { + Ok(Event::Sub(ReplySub { id, ack, cb })) => { pending_replies.insert(id, cb); ack.send(()).unwrap(); }, - Event::Input(0, read) => { + Ok(Event::Input(0, read)) => { let notif = ¬if; - pending_reqs.borrow_mut().push_back(Box::pin(async move { - notif(NotifReader { _pd: PhantomData, read }).await - })); + let notif_job = + async move { notif(Box::new(IoMsgReader { _pd: PhantomData, read })).await }; + add_pending_req.send(Box::pin(notif_job)).await.unwrap(); + }, + // MSB == 0 is a request, !id where MSB == 1 is the corresponding response + Ok(Event::Input(id, read)) if (id & (1 << (u64::BITS - 1))) == 0 => { + let (o, req) = (o.clone(), &req); + let req_job = async move { + let mut prefix = Vec::new(); + (!id).encode_vec(&mut prefix); + let _ = req(Box::new(IoReqReader { prefix: &pin!(prefix), read, write: &o })).await; + Ok(()) + }; + add_pending_req.send(Box::pin(req_job)).await.unwrap(); + }, + Ok(Event::Input(id, read)) => { + let cb = pending_replies.remove(&!id).expect("Reply to unrecognized request"); + cb.send(read).unwrap_or_else(|_| panic!("Failed to send reply")); }, - // id.msb == 0 is a request, !id where id.msb == 1 is the equivalent response - Event::Input(id, read) => - if (id & (1 << (u64::BITS - 1))) == 0 { - let (o, req) = (o.clone(), &req); - pending_reqs.borrow_mut().push_back(Box::pin(async move { - let _ = req(ReqReader { id, read, write: &o }).await; - }) as LocalBoxFuture<()>); - } else { - let cb = pending_replies.remove(&!id).expect("Reply to unrecognized request"); - cb.send(read).unwrap_or_else(|_| panic!("Failed to send reply")); - }, } } + Ok(()) + }?; + mem::drop(add_pending_req); + while let Some(next) = fork_stream.next().await { + next? } - fork_stream.as_mut().count().await; + Ok(()) }) } @@ -347,18 +409,48 @@ mod test { use futures::channel::mpsc; use futures::lock::Mutex; use futures::{SinkExt, StreamExt, join}; - use never::Never; use orchid_api_derive::{Coding, Hierarchy}; - use orchid_api_traits::{Channel, Request}; + use orchid_api_traits::Request; use test_executors::spin_on; use unsync_pipe::pipe; - use crate::reqnot::{ClientExt, NotifReader, io_comm}; + use crate::reqnot::{ClientExt, MsgReaderExt, ReqReaderExt, io_comm}; #[derive(Clone, Debug, PartialEq, Coding, Hierarchy)] #[extendable] struct TestNotif(u64); + #[test] + fn notification() { + spin_on(async { + let (in1, out2) = pipe(1024); + let (in2, out1) = pipe(1024); + let (received, mut on_receive) = mpsc::channel(2); + let (_, recv_ctx, run_recv) = io_comm( + Rc::new(Mutex::new(Box::pin(in2))), + Mutex::new(Box::pin(out2)), + async |notif| { + received.clone().send(notif.read::().await?).await.unwrap(); + Ok(()) + }, + async |_| panic!("Should receive notif, not request"), + ); + let (sender, ..) = io_comm( + Rc::new(Mutex::new(Box::pin(in1))), + Mutex::new(Box::pin(out1)), + async |_| panic!("Should not receive notif"), + async |_| panic!("Should not receive request"), + ); + join!(async { run_recv.await.unwrap() }, async { + sender.notify(TestNotif(3)).await.unwrap(); + assert_eq!(on_receive.next().await, Some(TestNotif(3))); + sender.notify(TestNotif(4)).await.unwrap(); + assert_eq!(on_receive.next().await, Some(TestNotif(4))); + recv_ctx.exit().await; + }); + }) + } + #[derive(Clone, Debug, Coding, Hierarchy)] #[extendable] struct DummyRequest(u64); @@ -366,64 +458,28 @@ mod test { type Response = u64; } - struct TestChannel; - impl Channel for TestChannel { - type Notif = TestNotif; - type Req = DummyRequest; - } - - #[test] - fn notification() { - spin_on(async { - let (in1, out2) = pipe(1024); - let (in2, out1) = pipe(1024); - let (received, mut on_receive) = mpsc::channel(2); - let (_, recv_ctx, run_recv) = io_comm::( - Rc::new(Mutex::new(Box::pin(in2))), - Mutex::new(Box::pin(out2)), - async |notif: NotifReader| { - received.clone().send(notif.read::().await).await.unwrap(); - }, - async |_| panic!("Should receive notif, not request"), - ); - let (sender, ..) = io_comm::( - Rc::new(Mutex::new(Box::pin(in1))), - Mutex::new(Box::pin(out1)), - async |_| panic!("Should not receive notif"), - async |_| panic!("Should not receive request"), - ); - join!(run_recv, async { - sender.notify(TestNotif(3)).await; - assert_eq!(on_receive.next().await, Some(TestNotif(3))); - sender.notify(TestNotif(4)).await; - assert_eq!(on_receive.next().await, Some(TestNotif(4))); - recv_ctx.exit().await; - }); - }) - } - #[test] fn request() { spin_on(async { let (in1, out2) = pipe(1024); let (in2, out1) = pipe(1024); - let (_, srv_ctx, run_srv) = io_comm::( + let (_, srv_ctx, run_srv) = io_comm( Rc::new(Mutex::new(Box::pin(in2))), Mutex::new(Box::pin(out2)), async |_| panic!("No notifs expected"), async |mut req| { - let val = req.read_req::().await; + let val = req.read_req::().await?; req.reply(&val, &(val.0 + 1)).await }, ); - let (client, client_ctx, run_client) = io_comm::( + let (client, client_ctx, run_client) = io_comm( Rc::new(Mutex::new(Box::pin(in1))), Mutex::new(Box::pin(out1)), async |_| panic!("Not expecting ingress notif"), async |_| panic!("Not expecting ingress req"), ); - join!(run_srv, run_client, async { - let response = client.request(DummyRequest(5)).await; + join!(async { run_srv.await.unwrap() }, async { run_client.await.unwrap() }, async { + let response = client.request(DummyRequest(5)).await.unwrap(); assert_eq!(response, 6); srv_ctx.exit().await; client_ctx.exit().await; diff --git a/orchid-base/src/stash.rs b/orchid-base/src/stash.rs index 83290ab..b525284 100644 --- a/orchid-base/src/stash.rs +++ b/orchid-base/src/stash.rs @@ -1,16 +1,21 @@ //! A pattern for running async code from sync destructors and other //! unfortunately sync callbacks //! -//! We create a task_local +//! We create a task_local vecdeque which is moved into a thread_local whenever +//! the task is being polled. A call to [stash] pushes the future onto this +//! deque. Before [with_stash] returns, it pops everything from the deque +//! individually and awaits each of them, pushing any additionally stashed +//! futures onto the back of the same deque. +use std::cell::RefCell; use std::collections::VecDeque; use std::pin::Pin; -use some_executor::task_local; +use task_local::task_local; #[derive(Default)] struct StashedFutures { - queue: VecDeque>>>, + queue: RefCell>>>>, } task_local! { @@ -23,7 +28,7 @@ pub async fn with_stash(fut: F) -> F::Output { STASHED_FUTURES .scope(StashedFutures::default(), async { let val = fut.await; - while let Some(fut) = STASHED_FUTURES.with_mut(|sf| sf.unwrap().queue.pop_front()) { + while let Some(fut) = STASHED_FUTURES.with(|sf| sf.queue.borrow_mut().pop_front()) { fut.await; } val @@ -33,10 +38,7 @@ pub async fn with_stash(fut: F) -> F::Output { /// Schedule a future to be run before the next [with_stash] guard ends. This is /// most useful for sending messages from destructors. -pub fn stash(fut: F) { - STASHED_FUTURES.with_mut(|sf| { - sf.expect("No stash! Timely completion cannot be guaranteed").queue.push_back(Box::pin(async { - fut.await; - })) - }) +pub fn stash + 'static>(fut: F) { + (STASHED_FUTURES.try_with(|sf| sf.queue.borrow_mut().push_back(Box::pin(fut)))) + .expect("No stash! Timely completion cannot be guaranteed") } diff --git a/orchid-extension/Cargo.toml b/orchid-extension/Cargo.toml index ad6c3e6..cfaddf7 100644 --- a/orchid-extension/Cargo.toml +++ b/orchid-extension/Cargo.toml @@ -8,6 +8,7 @@ edition = "2024" [dependencies] async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" } async-once-cell = "0.5.4" +bound = "0.6.0" derive_destructure = "1.0.0" dyn-clone = "1.0.20" futures = { version = "0.3.31", features = [ @@ -29,7 +30,6 @@ orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" } orchid-base = { version = "0.1.0", path = "../orchid-base" } ordered-float = "5.0.0" pastey = "0.1.1" -some_executor = "0.6.1" substack = "1.1.1" task-local = "0.1.0" tokio = { version = "1.47.1", optional = true, features = [] } diff --git a/orchid-extension/src/atom.rs b/orchid-extension/src/atom.rs index f815255..0225638 100644 --- a/orchid-extension/src/atom.rs +++ b/orchid-extension/src/atom.rs @@ -14,20 +14,20 @@ use orchid_api_derive::Coding; use orchid_api_traits::{Coding, Decode, Encode, Request, enc_vec}; use orchid_base::error::{OrcErrv, OrcRes, mk_errv, mk_errv_floating}; use orchid_base::format::{FmtCtx, FmtUnit, Format, fmt}; +use orchid_base::interner::is; use orchid_base::location::Pos; use orchid_base::name::Sym; -use orchid_base::reqnot::Requester; use trait_set::trait_set; use crate::api; -use crate::context::{ctx, i}; use crate::conv::ToExpr; +use crate::entrypoint::request; // use crate::error::{ProjectError, ProjectResult}; use crate::expr::{Expr, ExprData, ExprHandle, ExprKind}; use crate::gen_expr::GExpr; -use crate::system::{DynSystemCard, atom_info_for, downcast_atom}; +use crate::system::{DynSystemCard, atom_by_idx, atom_info_for, cted, downcast_atom}; -#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)] pub struct AtomTypeId(pub NonZeroU32); pub trait AtomCard: 'static + Sized { @@ -99,13 +99,13 @@ impl ForeignAtom { ForeignAtom { atom, expr: handle, pos } } pub async fn request(&self, m: M) -> Option { - let rep = (ctx().reqnot().request(api::Fwd( + let rep = (request(api::Fwd( self.atom.clone(), - Sym::parse(M::NAME, &i()).await.unwrap().tok().to_api(), - enc_vec(&m).await, + Sym::parse(M::NAME).await.unwrap().tok().to_api(), + enc_vec(&m), ))) .await?; - Some(M::Response::decode(Pin::new(&mut &rep[..])).await) + Some(M::Response::decode_slice(&mut &rep[..])) } pub async fn downcast(self) -> Result, NotTypAtom> { TAtom::downcast(self.ex().handle()).await @@ -119,7 +119,7 @@ impl fmt::Debug for ForeignAtom { } impl Format for ForeignAtom { async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { - FmtUnit::from_api(&ctx().reqnot().request(api::ExtAtomPrint(self.atom.clone())).await) + FmtUnit::from_api(&request(api::ExtAtomPrint(self.atom.clone())).await) } } impl ToExpr for ForeignAtom { @@ -138,8 +138,8 @@ pub struct NotTypAtom { impl NotTypAtom { pub async fn mk_err(&self) -> OrcErrv { mk_errv( - i().i("Not the expected type").await, - format!("The expression {} is not a {}", fmt(&self.expr, &i()).await, self.typ.name()), + is("Not the expected type").await, + format!("The expression {} is not a {}", fmt(&self.expr).await, self.typ.name()), [self.pos.clone()], ) } @@ -172,8 +172,10 @@ impl MethodSetBuilder { self.handlers.push(( M::NAME, Rc::new(move |a: &A, req: Pin<&mut dyn AsyncRead>, rep: Pin<&mut dyn AsyncWrite>| { - async { Supports::::handle(a, M::decode(req).await).await.encode(rep).await } - .boxed_local() + async { + Supports::::handle(a, M::decode(req).await.unwrap()).await.encode(rep).await.unwrap() + } + .boxed_local() }), )); self @@ -182,7 +184,7 @@ impl MethodSetBuilder { pub async fn pack(&self) -> MethodSet { MethodSet { handlers: stream::iter(self.handlers.iter()) - .then(async |(k, v)| (Sym::parse(k, &i()).await.unwrap(), v.clone())) + .then(async |(k, v)| (Sym::parse(k).await.unwrap(), v.clone())) .collect() .await, } @@ -234,16 +236,15 @@ impl TAtom { } pub async fn request(&self, req: M) -> M::Response where A: Supports { - M::Response::decode(Pin::new( - &mut &(ctx().reqnot().request(api::Fwd( + M::Response::decode_slice( + &mut &(request(api::Fwd( self.untyped.atom.clone(), - Sym::parse(M::NAME, &i()).await.unwrap().tok().to_api(), - enc_vec(&req).await, + Sym::parse(M::NAME).await.unwrap().tok().to_api(), + enc_vec(&req), ))) .await .unwrap()[..], - )) - .await + ) } } impl Deref for TAtom { @@ -311,9 +312,18 @@ impl Format for AtomFactory { } pub async fn err_not_callable() -> OrcErrv { - mk_errv_floating(i().i("This atom is not callable").await, "Attempted to apply value as function") + mk_errv_floating(is("This atom is not callable").await, "Attempted to apply value as function") } pub async fn err_not_command() -> OrcErrv { - mk_errv_floating(i().i("This atom is not a command").await, "Settled on an inactionable value") + mk_errv_floating(is("This atom is not a command").await, "Settled on an inactionable value") +} + +/// Read the type ID prefix from an atom, return type information and the rest +/// of the data +pub(crate) fn resolve_atom_type(atom: &api::Atom) -> (Box, AtomTypeId, &[u8]) { + let mut data = &atom.data.0[..]; + let tid = AtomTypeId::decode_slice(&mut data); + let atom_record = atom_by_idx(cted().inst().card(), tid).expect("Unrecognized atom type ID"); + (atom_record, tid, data) } diff --git a/orchid-extension/src/atom_owned.rs b/orchid-extension/src/atom_owned.rs index b0c02dc..09cd966 100644 --- a/orchid-extension/src/atom_owned.rs +++ b/orchid-extension/src/atom_owned.rs @@ -1,11 +1,12 @@ use std::any::{Any, TypeId, type_name}; use std::borrow::Cow; +use std::cell::RefCell; use std::future::Future; use std::marker::PhantomData; use std::num::NonZero; use std::ops::Deref; use std::pin::Pin; -use std::sync::atomic::AtomicU64; +use std::rc::Rc; use async_once_cell::OnceCell; use dyn_clone::{DynClone, clone_box}; @@ -19,32 +20,33 @@ use orchid_api_traits::{Decode, Encode, enc_vec}; use orchid_base::error::OrcRes; use orchid_base::format::{FmtCtx, FmtCtxImpl, FmtUnit, take_first}; use orchid_base::name::Sym; +use task_local::task_local; use crate::api; use crate::atom::{ AtomCard, AtomCtx, AtomDynfo, AtomFactory, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet, MethodSetBuilder, TAtom, err_not_callable, err_not_command, get_info, }; -use crate::context::{SysCtxEntry, ctx, i}; use crate::expr::Expr; use crate::gen_expr::{GExpr, bot}; -use crate::system_ctor::CtedObj; +use crate::system::{cted, sys_id}; pub struct OwnedVariant; impl AtomicVariant for OwnedVariant {} impl> AtomicFeaturesImpl for A { fn _factory(self) -> AtomFactory { AtomFactory::new(async move || { - let serial = ctx() - .get_or_default::() - .next_id - .fetch_add(1, std::sync::atomic::Ordering::Relaxed); - let atom_id = api::AtomId(NonZero::new(serial + 1).unwrap()); - let (typ_id, _) = get_info::(ctx().get::().inst().card()); - let mut data = enc_vec(&typ_id).await; + let obj_store = get_obj_store(); + let atom_id = { + let mut id = obj_store.next_id.borrow_mut(); + *id += 1; + api::AtomId(NonZero::new(*id + 1).unwrap()) + }; + let (typ_id, _) = get_info::(cted().inst().card()); + let mut data = enc_vec(&typ_id); self.encode(Pin::<&mut Vec>::new(&mut data)).await; - ctx().get_or_default::().objects.read().await.insert(atom_id, Box::new(self)); - api::Atom { drop: Some(atom_id), data: api::AtomData(data), owner: ctx().sys_id() } + obj_store.objects.read().await.insert(atom_id, Box::new(self)); + api::Atom { drop: Some(atom_id), data: api::AtomData(data), owner: sys_id() } }) } fn _info() -> Self::_Info { OwnedAtomDynfo { msbuild: A::reg_reqs(), ms: OnceCell::new() } } @@ -59,7 +61,7 @@ pub(crate) struct AtomReadGuard<'a> { } impl<'a> AtomReadGuard<'a> { async fn new(id: api::AtomId) -> Self { - let guard = ctx().get_or_default::().objects.read().await; + let guard = get_obj_store().objects.read().await; if guard.get(&id).is_none() { panic!("Received invalid atom ID: {id:?}"); } @@ -73,7 +75,7 @@ impl Deref for AtomReadGuard<'_> { /// Remove an atom from the store pub(crate) async fn take_atom(id: api::AtomId) -> Box { - let mut g = ctx().get_or_default::().objects.write().await; + let mut g = get_obj_store().objects.write().await; g.remove(&id).unwrap_or_else(|| panic!("Received invalid atom ID: {}", id.0)) } @@ -86,7 +88,7 @@ impl AtomDynfo for OwnedAtomDynfo { fn name(&self) -> &'static str { type_name::() } fn decode<'a>(&'a self, AtomCtx(data, ..): AtomCtx<'a>) -> LocalBoxFuture<'a, Box> { Box::pin(async { - Box::new(::Data::decode(Pin::new(&mut &data[..])).await) as Box + Box::new(::Data::decode_slice(&mut &data[..])) as Box }) } fn call(&self, AtomCtx(_, id): AtomCtx, arg: Expr) -> LocalBoxFuture<'_, GExpr> { @@ -127,7 +129,7 @@ impl AtomDynfo for OwnedAtomDynfo { ) -> LocalBoxFuture<'a, Option>> { Box::pin(async move { let id = id.unwrap(); - id.encode(write.as_mut()).await; + id.encode(write.as_mut()).await.unwrap(); AtomReadGuard::new(id).await.dyn_serialize(write).await }) } @@ -155,7 +157,7 @@ pub trait DeserializeCtx: Sized { struct DeserCtxImpl<'a>(&'a [u8]); impl DeserializeCtx for DeserCtxImpl<'_> { - async fn read(&mut self) -> T { T::decode(Pin::new(&mut self.0)).await } + async fn read(&mut self) -> T { T::decode(Pin::new(&mut self.0)).await.unwrap() } fn is_empty(&self) -> bool { self.0.is_empty() } } @@ -266,7 +268,7 @@ impl DynOwnedAtom for T { fn atom_tid(&self) -> TypeId { TypeId::of::() } fn as_any_ref(&self) -> &dyn Any { self } fn encode<'a>(&'a self, buffer: Pin<&'a mut dyn AsyncWrite>) -> LocalBoxFuture<'a, ()> { - async { self.val().await.as_ref().encode(buffer).await }.boxed_local() + async { self.val().await.as_ref().encode(buffer).await.unwrap() }.boxed_local() } fn dyn_call_ref(&self, arg: Expr) -> LocalBoxFuture<'_, GExpr> { self.call_ref(arg).boxed_local() @@ -279,7 +281,7 @@ impl DynOwnedAtom for T { } fn dyn_free(self: Box) -> LocalBoxFuture<'static, ()> { self.free().boxed_local() } fn dyn_print(&self) -> LocalBoxFuture<'_, FmtUnit> { - async move { self.print_atom(&FmtCtxImpl { i: &i() }).await }.boxed_local() + async move { self.print_atom(&FmtCtxImpl::default()).await }.boxed_local() } fn dyn_serialize<'a>( &'a self, @@ -294,13 +296,24 @@ impl DynOwnedAtom for T { #[derive(Default)] pub(crate) struct ObjStore { - pub(crate) next_id: AtomicU64, + pub(crate) next_id: RefCell, pub(crate) objects: RwLock>>, } -impl SysCtxEntry for ObjStore {} + +task_local! { + static OBJ_STORE: Rc; +} + +pub(crate) fn with_obj_store<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> { + Box::pin(OBJ_STORE.scope(Rc::new(ObjStore::default()), fut)) +} + +pub(crate) fn get_obj_store() -> Rc { + OBJ_STORE.try_with(|store| store.clone()).expect("Owned atom store not initialized") +} pub async fn own(typ: &TAtom) -> A { - let g = ctx().get_or_default::().objects.read().await; + let g = get_obj_store().objects.read().await; let atom_id = typ.untyped.atom.drop.expect("Owned atoms always have a drop ID"); let dyn_atom = g.get(&atom_id).expect("Atom ID invalid; atom type probably not owned by this crate"); @@ -308,8 +321,7 @@ pub async fn own(typ: &TAtom) -> A { } pub async fn debug_print_obj_store(show_atoms: bool) { - let ctx = ctx(); - let store = ctx.get_or_default::(); + let store = get_obj_store(); let keys = store.objects.read().await.keys().cloned().collect_vec(); let mut message = "Atoms in store:".to_string(); if !show_atoms { diff --git a/orchid-extension/src/atom_thin.rs b/orchid-extension/src/atom_thin.rs index e710169..5817b7d 100644 --- a/orchid-extension/src/atom_thin.rs +++ b/orchid-extension/src/atom_thin.rs @@ -8,6 +8,7 @@ use futures::{AsyncRead, AsyncWrite, FutureExt}; use orchid_api_traits::{Coding, enc_vec}; use orchid_base::error::OrcRes; use orchid_base::format::FmtUnit; +use orchid_base::logging::logger; use orchid_base::name::Sym; use crate::api; @@ -15,20 +16,19 @@ use crate::atom::{ AtomCard, AtomCtx, AtomDynfo, AtomFactory, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet, MethodSetBuilder, err_not_callable, err_not_command, get_info, }; -use crate::context::ctx; use crate::expr::Expr; use crate::gen_expr::{GExpr, bot}; -use crate::system_ctor::CtedObj; +use crate::system::{cted, sys_id}; pub struct ThinVariant; impl AtomicVariant for ThinVariant {} impl> AtomicFeaturesImpl for A { fn _factory(self) -> AtomFactory { AtomFactory::new(async move || { - let (id, _) = get_info::(ctx().get::().inst().card()); - let mut buf = enc_vec(&id).await; - self.encode(Pin::new(&mut buf)).await; - api::Atom { drop: None, data: api::AtomData(buf), owner: ctx().sys_id() } + let (id, _) = get_info::(cted().inst().card()); + let mut buf = enc_vec(&id); + self.encode_vec(&mut buf); + api::Atom { drop: None, data: api::AtomData(buf), owner: sys_id() } }) } fn _info() -> Self::_Info { ThinAtomDynfo { msbuild: Self::reg_reqs(), ms: OnceCell::new() } } @@ -41,18 +41,18 @@ pub struct ThinAtomDynfo { } impl AtomDynfo for ThinAtomDynfo { fn print<'a>(&self, AtomCtx(buf, _): AtomCtx<'a>) -> LocalBoxFuture<'a, FmtUnit> { - Box::pin(async move { T::decode(Pin::new(&mut &buf[..])).await.print().await }) + Box::pin(async move { T::decode_slice(&mut &buf[..]).print().await }) } fn tid(&self) -> TypeId { TypeId::of::() } fn name(&self) -> &'static str { type_name::() } fn decode<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>) -> LocalBoxFuture<'a, Box> { - Box::pin(async { Box::new(T::decode(Pin::new(&mut &buf[..])).await) as Box }) + Box::pin(async { Box::new(T::decode_slice(&mut &buf[..])) as Box }) } fn call<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr> { - Box::pin(async move { T::decode(Pin::new(&mut &buf[..])).await.call(arg).await }) + Box::pin(async move { T::decode_slice(&mut &buf[..]).call(arg).await }) } fn call_ref<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr> { - Box::pin(async move { T::decode(Pin::new(&mut &buf[..])).await.call(arg).await }) + Box::pin(async move { T::decode_slice(&mut &buf[..]).call(arg).await }) } fn handle_req<'a, 'm1: 'a, 'm2: 'a>( &'a self, @@ -63,14 +63,14 @@ impl AtomDynfo for ThinAtomDynfo { ) -> LocalBoxFuture<'a, bool> { Box::pin(async move { let ms = self.ms.get_or_init(self.msbuild.pack()).await; - ms.dispatch(&T::decode(Pin::new(&mut &buf[..])).await, key, req, rep).await + ms.dispatch(&T::decode_slice(&mut &buf[..]), key, req, rep).await }) } fn command<'a>( &'a self, AtomCtx(buf, _): AtomCtx<'a>, ) -> LocalBoxFuture<'a, OrcRes>> { - async move { T::decode(Pin::new(&mut &buf[..])).await.command().await }.boxed_local() + async move { T::decode_slice(&mut &buf[..]).command().await }.boxed_local() } fn serialize<'a, 'b: 'a>( &'a self, @@ -78,18 +78,18 @@ impl AtomDynfo for ThinAtomDynfo { write: Pin<&'b mut dyn AsyncWrite>, ) -> LocalBoxFuture<'a, Option>> { Box::pin(async { - T::decode(Pin::new(&mut &ctx.0[..])).await.encode(write).await; + T::decode_slice(&mut &ctx.0[..]).encode(write).await.unwrap(); Some(Vec::new()) }) } fn deserialize<'a>(&'a self, data: &'a [u8], refs: &'a [Expr]) -> LocalBoxFuture<'a, api::Atom> { assert!(refs.is_empty(), "Refs found when deserializing thin atom"); - Box::pin(async { T::decode(Pin::new(&mut &data[..])).await._factory().build().await }) + Box::pin(async { T::decode_slice(&mut &data[..])._factory().build().await }) } fn drop<'a>(&'a self, AtomCtx(buf, _): AtomCtx<'a>) -> LocalBoxFuture<'a, ()> { Box::pin(async move { - let string_self = T::decode(Pin::new(&mut &buf[..])).await.print().await; - writeln!(ctx().logger(), "Received drop signal for non-drop atom {string_self:?}"); + let string_self = T::decode_slice(&mut &buf[..]).print().await; + writeln!(logger(), "Received drop signal for non-drop atom {string_self:?}"); }) } } diff --git a/orchid-extension/src/context.rs b/orchid-extension/src/context.rs deleted file mode 100644 index 3d3f557..0000000 --- a/orchid-extension/src/context.rs +++ /dev/null @@ -1,90 +0,0 @@ -use std::any::{Any, TypeId, type_name}; -use std::fmt; -use std::num::NonZero; -use std::rc::Rc; - -use memo_map::MemoMap; -use orchid_base::builtin::Spawner; -use orchid_base::interner::Interner; -use orchid_base::logging::Logger; -use orchid_base::reqnot::ReqNot; -use task_local::task_local; - -use crate::api; -use crate::system_ctor::CtedObj; - -#[derive(Clone)] -pub struct SysCtx(Rc>>); -impl SysCtx { - pub fn new( - id: api::SysId, - i: Interner, - reqnot: ReqNot, - spawner: Spawner, - logger: Logger, - cted: CtedObj, - ) -> Self { - let this = Self(Rc::new(MemoMap::new())); - this.add(id).add(i).add(reqnot).add(spawner).add(logger).add(cted); - this - } - pub fn add(&self, t: T) -> &Self { - assert!(self.0.insert(TypeId::of::(), Box::new(t)), "Key already exists"); - self - } - pub fn get_or_insert(&self, f: impl FnOnce() -> T) -> &T { - (self.0.get_or_insert_owned(TypeId::of::(), || Box::new(f())).downcast_ref()) - .expect("Keyed by TypeId") - } - pub fn get_or_default(&self) -> &T { self.get_or_insert(T::default) } - pub fn try_get(&self) -> Option<&T> { - Some(self.0.get(&TypeId::of::())?.downcast_ref().expect("Keyed by TypeId")) - } - pub fn get(&self) -> &T { - self.try_get().unwrap_or_else(|| panic!("Context {} missing", type_name::())) - } - /// Shorthand to get the messaging link - pub fn reqnot(&self) -> &ReqNot { self.get::>() } - /// Shorthand to get the system ID - pub fn sys_id(&self) -> api::SysId { *self.get::() } - /// Spawn a task that will eventually be executed asynchronously - pub fn spawn(&self, f: impl Future + 'static) { - (self.get::())(Box::pin(CTX.scope(self.clone(), f))) - } - /// Shorthand to get the logger - pub fn logger(&self) -> &Logger { self.get::() } - /// Shorthand to get the constructed system object - pub fn cted(&self) -> &CtedObj { self.get::() } -} -impl fmt::Debug for SysCtx { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "SysCtx({:?})", self.sys_id()) - } -} -pub trait SysCtxEntry: 'static + Sized {} -impl SysCtxEntry for api::SysId {} -impl SysCtxEntry for ReqNot {} -impl SysCtxEntry for Spawner {} -impl SysCtxEntry for CtedObj {} -impl SysCtxEntry for Logger {} -impl SysCtxEntry for Interner {} - -task_local! { - static CTX: SysCtx; -} - -pub async fn with_ctx(ctx: SysCtx, f: F) -> F::Output { CTX.scope(ctx, f).await } -pub fn ctx() -> SysCtx { CTX.get() } - -/// Shorthand to get the [Interner] instance -pub fn i() -> Interner { ctx().get::().clone() } - -pub fn mock_ctx() -> SysCtx { - let ctx = SysCtx(Rc::default()); - ctx - .add(Logger::new(api::LogStrategy::StdErr)) - .add(Interner::new_master()) - .add::(Rc::new(|_| panic!("Cannot fork in test environment"))) - .add(api::SysId(NonZero::::MIN)); - ctx -} diff --git a/orchid-extension/src/conv.rs b/orchid-extension/src/conv.rs index d9e1c98..8aebdad 100644 --- a/orchid-extension/src/conv.rs +++ b/orchid-extension/src/conv.rs @@ -4,11 +4,11 @@ use std::pin::Pin; use dyn_clone::DynClone; use never::Never; use orchid_base::error::{OrcErrv, OrcRes, mk_errv}; +use orchid_base::interner::is; use orchid_base::location::Pos; use trait_set::trait_set; use crate::atom::{AtomicFeatures, ForeignAtom, TAtom, ToAtom}; -use crate::context::i; use crate::expr::Expr; use crate::gen_expr::{GExpr, atom, bot}; @@ -27,7 +27,7 @@ impl TryFromExpr for (T, U) { } async fn err_not_atom(pos: Pos) -> OrcErrv { - mk_errv(i().i("Expected an atom").await, "This expression is not an atom", [pos]) + mk_errv(is("Expected an atom").await, "This expression is not an atom", [pos]) } impl TryFromExpr for ForeignAtom { diff --git a/orchid-extension/src/entrypoint.rs b/orchid-extension/src/entrypoint.rs index 1eb3549..1e3a77f 100644 --- a/orchid-extension/src/entrypoint.rs +++ b/orchid-extension/src/entrypoint.rs @@ -1,453 +1,438 @@ use std::cell::RefCell; use std::future::Future; -use std::mem; use std::num::NonZero; use std::pin::Pin; -use std::rc::{Rc, Weak}; +use std::rc::Rc; +use std::{io, mem}; -use futures::channel::mpsc::{Receiver, Sender, channel}; use futures::future::{LocalBoxFuture, join_all}; use futures::lock::Mutex; -use futures::{FutureExt, SinkExt, StreamExt, stream, stream_select}; -use futures_locks::RwLock; +use futures::{AsyncRead, AsyncWrite, AsyncWriteExt, StreamExt, stream}; use hashbrown::HashMap; use itertools::Itertools; -use orchid_api_traits::{Decode, UnderRoot, enc_vec}; -use orchid_base::builtin::{ExtInit, ExtPort, Spawner}; +use orchid_api::{ExtHostNotif, ExtHostReq}; +use orchid_api_traits::{Decode, Encode, Request, UnderRoot, enc_vec}; use orchid_base::char_filter::{char_filter_match, char_filter_union, mk_char_filter}; -use orchid_base::clone; -use orchid_base::error::Reporter; -use orchid_base::interner::{Interner, Tok}; -use orchid_base::logging::Logger; +use orchid_base::error::try_with_reporter; +use orchid_base::interner::{es, is, with_interner}; +use orchid_base::logging::{Logger, with_logger}; use orchid_base::name::Sym; use orchid_base::parse::{Comment, Snippet}; -use orchid_base::reqnot::{ReqNot, ReqReader, Requester}; +use orchid_base::reqnot::{ + Client, ClientExt, CommCtx, MsgReader, MsgReaderExt, Receipt, RepWriter, ReqHandle, ReqHandleExt, + ReqReader, ReqReaderExt, Witness, io_comm, +}; +use orchid_base::stash::with_stash; use orchid_base::tree::{TokenVariant, ttv_from_api}; use substack::Substack; -use trait_set::trait_set; +use task_local::task_local; use crate::api; -use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId}; -use crate::atom_owned::take_atom; -use crate::context::{SysCtx, ctx, i, with_ctx}; +use crate::atom::{AtomCtx, AtomTypeId, resolve_atom_type}; +use crate::atom_owned::{take_atom, with_obj_store}; use crate::expr::{BorrowedExprStore, Expr, ExprHandle}; +use crate::ext_port::ExtPort; +use crate::func_atom::with_funs_ctx; +use crate::interner::new_interner; use crate::lexer::{LexContext, ekey_cascade, ekey_not_applicable}; -use crate::parser::{PTokTree, ParsCtx, get_const, linev_into_api}; -use crate::system::atom_by_idx; -use crate::system_ctor::{CtedObj, DynSystemCtor}; -use crate::tree::{LazyMemberFactory, TreeIntoApiCtxImpl}; +use crate::parser::{PTokTree, ParsCtx, get_const, linev_into_api, with_parsed_const_ctx}; +use crate::reflection::with_refl_roots; +use crate::system::{SysCtx, atom_by_idx, cted, with_sys}; +use crate::system_ctor::{CtedObj, DynSystemCtor, SystemCtor}; +use crate::tree::{TreeIntoApiCtxImpl, get_lazy, with_lazy_member_store}; task_local::task_local! { static CLIENT: Rc; + static CTX: Rc>>; } -fn get_client() -> Rc { - CLIENT.with(|c| c.expect("Client not set, not running inside a duplex reqnot channel!").clone()) +fn get_client() -> Rc { CLIENT.get() } +pub async fn exit() { + let cx = CTX.get().borrow_mut().take(); + cx.unwrap().exit().await } /// Sent the client used for global [request] and [notify] functions within the /// runtime of this future -pub async fn with_client(c: Rc, fut: F) -> F::Output { - CLIENT.scope(c, fut).await +pub async fn with_comm(c: Rc, ctx: CommCtx, fut: F) -> F::Output { + CLIENT.scope(c, CTX.scope(Rc::new(RefCell::new(Some(ctx))), fut)).await } /// Send a request through the global client's [ClientExt::request] -pub async fn request>(t: T) -> T::Response { - get_client().request(t).await +pub async fn request>(t: T) -> T::Response { + get_client().request(t).await.unwrap() } /// Send a notification through the global client's [ClientExt::notify] -pub async fn notify + 'static>(t: T) { get_client().notify(t).await } - -pub type ExtReq<'a> = ReqReader<'a, api::ExtMsgSet>; -pub type ExtReqNot = ReqNot; - -pub struct ExtensionData { - pub name: &'static str, - pub systems: &'static [&'static dyn DynSystemCtor], -} -impl ExtensionData { - pub fn new(name: &'static str, systems: &'static [&'static dyn DynSystemCtor]) -> Self { - Self { name, systems } - } -} - -pub enum MemberRecord { - Gen(Vec>, LazyMemberFactory), - Res, +pub async fn notify>(t: T) { + get_client().notify(t).await.unwrap() } pub struct SystemRecord { - lazy_members: Mutex>, - ctx: SysCtx, + cted: CtedObj, } -trait_set! { - pub trait WithAtomRecordCallback<'a, T> = AsyncFnOnce( - Box, - AtomTypeId, - &'a [u8] - ) -> T +type SystemTable = RefCell>>; + +task_local! { + static SYSTEM_TABLE: SystemTable; } -pub async fn with_atom_record<'a, F: Future, T>( - get_sys_ctx: &impl Fn(api::SysId) -> F, - atom: &'a api::Atom, - cb: impl WithAtomRecordCallback<'a, T>, -) -> T { - let mut data = &atom.data.0[..]; - let ctx = get_sys_ctx(atom.owner).await; - let inst = ctx.get::().inst(); - let id = AtomTypeId::decode(Pin::new(&mut data)).await; - let atom_record = atom_by_idx(inst.card(), id.clone()).expect("Atom ID reserved"); - with_ctx(ctx, async move { cb(atom_record, id, data).await }).await +async fn with_sys_record(id: api::SysId, fut: F) -> F::Output { + let cted = SYSTEM_TABLE.with(|tbl| { + eprintln!( + "Existing systems are {}", + tbl.borrow().iter().map(|(k, v)| format!("{k:?}={:?}", v.cted)).join(";") + ); + let sys = tbl.borrow().get(&id).expect("Invalid sys ID").cted.clone(); + eprintln!("Selected {:?}", sys); + sys + }); + with_sys(SysCtx(id, cted), fut).await } -pub struct ExtensionOwner { - _interner_cell: Rc>>, - _systems_lock: Rc>>, - out_recv: Mutex>>, - out_send: Sender>, +pub trait ContextModifier: 'static { + fn apply<'a>(self: Box, fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()>; } -impl ExtPort for ExtensionOwner { - fn send<'a>(&'a self, msg: &'a [u8]) -> LocalBoxFuture<'a, ()> { - Box::pin(async { self.out_send.clone().send(msg.to_vec()).boxed_local().await.unwrap() }) - } - fn recv(&self) -> LocalBoxFuture<'_, Option>> { - Box::pin(async { self.out_recv.lock().await.next().await }) +impl) + 'static> ContextModifier for F { + fn apply<'a>(self: Box, fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> { + Box::pin((self)(fut)) } } -pub fn extension_init( - data: ExtensionData, - host_header: api::HostHeader, - spawner: Spawner, -) -> ExtInit { - let api::HostHeader { log_strategy, msg_logs } = host_header; - 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_lock = Rc::new(RwLock::new(HashMap::::new())); - let ext_header = api::ExtensionHeader { name: data.name.to_string(), systems: decls.clone() }; - let (out_send, in_recv) = channel::>(1); - let (in_send, out_recv) = channel::>(1); - let (exit_send, exit_recv) = channel(1); - let logger = Logger::new(log_strategy); - let msg_logger = Logger::new(msg_logs); - let interner_cell = Rc::new(RefCell::new(None::)); - let interner_weak = Rc::downgrade(&interner_cell); - let systems_weak = Rc::downgrade(&systems_lock); - let get_ctx = clone!(systems_weak; move |id: api::SysId| clone!(systems_weak; async move { - let systems = - systems_weak.upgrade().expect("System table dropped before request processing done"); - systems.read().await.get(&id).expect("System not found").ctx.clone() - })); - let init_ctx = { - clone!(interner_weak, spawner, logger); - move |id: api::SysId, cted: CtedObj, reqnot: ReqNot| { - clone!(interner_weak, spawner, logger; async move { - let interner_rc = - interner_weak.upgrade().expect("System construction order while shutting down"); - let i = interner_rc.borrow().clone().expect("mk_ctx called very early, no interner!"); - SysCtx::new(id, i, reqnot, spawner, logger, cted) - }) - } - }; - let rn = ReqNot::::new( - msg_logger.clone(), - move |a, _| { - clone!(in_send mut); - Box::pin(async move { in_send.send(a.to_vec()).await.unwrap() }) - }, - { - clone!(exit_send); - move |n, _| { - clone!(exit_send mut); - async move { - match n { - api::HostExtNotif::Exit => exit_send.send(()).await.unwrap(), +pub struct ExtensionBuilder { + pub name: &'static str, + pub systems: Vec>, + pub context: Vec>, +} +impl ExtensionBuilder { + pub fn new(name: &'static str) -> Self { Self { name, systems: Vec::new(), context: Vec::new() } } + pub fn system(mut self, ctor: impl SystemCtor) -> Self { + self.systems.push(Box::new(ctor) as Box<_>); + self + } + pub fn add_context(&mut self, fun: impl ContextModifier) { + self.context.push(Box::new(fun) as Box<_>); + } + pub fn context(mut self, fun: impl ContextModifier) -> Self { + self.add_context(fun); + self + } + pub fn build(mut self, mut ctx: ExtPort) { + self.add_context(with_funs_ctx); + self.add_context(with_parsed_const_ctx); + self.add_context(with_obj_store); + self.add_context(with_lazy_member_store); + self.add_context(with_refl_roots); + (ctx.spawn)(Box::pin(async move { + let api::HostHeader { log_strategy, msg_logs } = + api::HostHeader::decode(ctx.input.as_mut()).await.unwrap(); + let decls = (self.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(); + api::ExtensionHeader { name: self.name.to_string(), systems: decls.clone() } + .encode(ctx.output.as_mut()) + .await + .unwrap(); + ctx.output.as_mut().flush().await.unwrap(); + let logger = Logger::new(log_strategy); + let logger2 = logger.clone(); + let msg_logger = Logger::new(msg_logs); + let (client, ctx, run_extension) = io_comm( + Rc::new(Mutex::new(ctx.output)), + Mutex::new(ctx.input), + async move |n: Box>| { + match n.read().await.unwrap() { + api::HostExtNotif::Exit => exit().await, } - } - .boxed_local() - } - }, - { - clone!(logger, get_ctx, init_ctx, systems_weak, interner_weak, decls, msg_logger); - move |hand, req| { - clone!(logger, get_ctx, init_ctx, systems_weak, interner_weak, decls, msg_logger); - async move { - let interner_cell = interner_weak.upgrade().expect("Interner dropped before request"); - let interner = - interner_cell.borrow().clone().expect("Request arrived before interner set"); - if !matches!(req, api::HostExtReq::AtomReq(api::AtomReq::AtomPrint(_))) { - writeln!(msg_logger, "{} extension received request {req:?}", data.name); - } - - match req { - api::HostExtReq::SystemDrop(sys_drop) => { - if let Some(rc) = systems_weak.upgrade() { - mem::drop(rc.write().await.remove(&sys_drop.0)) - } - hand.handle(&sys_drop, &()).await - }, - api::HostExtReq::AtomDrop(atom_drop @ api::AtomDrop(sys_id, atom)) => - with_ctx(get_ctx(sys_id).await, async move { - take_atom(atom).await.dyn_free().await; - hand.handle(&atom_drop, &()).await - }) - .await, - api::HostExtReq::Ping(ping @ api::Ping) => hand.handle(&ping, &()).await, - api::HostExtReq::Sweep(api::Sweep) => todo!(), - 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); - with_ctx(init_ctx(new_sys.id, cted.clone(), hand.reqnot()).await, async move { - 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_members = Mutex::new(HashMap::new()); - let const_root = stream::iter(cted.inst().dyn_env().await) - .then(|mem| { - let lazy_mems = &lazy_members; - async move { - let name = i().i(&mem.name).await; + Ok(()) + }, + async move |mut reader| { + with_stash(async { + let req = reader.read_req().await.unwrap(); + let handle = reader.finish().await; + if !matches!(req, api::HostExtReq::AtomReq(api::AtomReq::AtomPrint(_))) { + writeln!(msg_logger, "{} extension received request {req:?}", self.name); + } + match req { + api::HostExtReq::SystemDrop(sys_drop) => { + SYSTEM_TABLE.with(|l| l.borrow_mut().remove(&sys_drop.0)); + handle.reply(&sys_drop, &()).await + }, + api::HostExtReq::AtomDrop(atom_drop @ api::AtomDrop(sys_id, atom)) => + with_sys_record(sys_id, async { + take_atom(atom).await.dyn_free().await; + handle.reply(&atom_drop, &()).await + }) + .await, + api::HostExtReq::Ping(ping @ api::Ping) => handle.reply(&ping, &()).await, + api::HostExtReq::Sweep(api::Sweep) => todo!(), + api::HostExtReq::SysReq(api::SysReq::NewSystem(new_sys)) => { + let (ctor_idx, _) = + (decls.iter().enumerate().find(|(_, s)| s.id == new_sys.system)) + .expect("NewSystem call received for invalid system"); + let cted = self.systems[ctor_idx].new_system(&new_sys); + let record = Rc::new(SystemRecord { cted: cted.clone() }); + SYSTEM_TABLE.with(|tbl| { + let mut g = tbl.borrow_mut(); + g.insert(new_sys.id, record); + }); + with_sys_record(new_sys.id, async { + 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 const_root = stream::iter(cted.inst().dyn_env().await) + .then(async |mem| { + let name = is(&mem.name).await; let mut tia_ctx = TreeIntoApiCtxImpl { - lazy_members: &mut *lazy_mems.lock().await, basepath: &[], path: Substack::Bottom.push(name.clone()), }; (name.to_api(), mem.kind.into_api(&mut tia_ctx).await) - } - }) - .collect() + }) + .collect() + .await; + let prelude = + cted.inst().dyn_prelude().await.iter().map(|sym| sym.to_api()).collect(); + let line_types = join_all( + (cted.inst().dyn_parsers().iter()) + .map(async |p| is(p.line_head()).await.to_api()), + ) .await; - let prelude = - cted.inst().dyn_prelude().await.iter().map(|sym| sym.to_api()).collect(); - let record = SystemRecord { ctx: ctx(), lazy_members }; - let systems = systems_weak.upgrade().expect("System constructed during shutdown"); - systems.write().await.insert(new_sys.id, record); - let line_types = join_all( - (cted.inst().dyn_parsers().iter()) - .map(|p| async { interner.i(p.line_head()).await.to_api() }), - ) - .await; - let response = - api::NewSystemResponse { lex_filter, const_root, line_types, prelude }; - hand.handle(&new_sys, &response).await - }) - .await - }, - api::HostExtReq::GetMember(get_tree @ api::GetMember(sys_id, tree_id)) => - with_ctx(get_ctx(sys_id).await, async move { - let systems = systems_weak.upgrade().expect("Member queried during shutdown"); - let systems_g = systems.read().await; - let mut lazy_members = - systems_g.get(&sys_id).expect("System not found").lazy_members.lock().await; - 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(), &interner).await.unwrap()).await; - let mut tia_ctx = TreeIntoApiCtxImpl { - path: Substack::Bottom, - basepath: &path, - lazy_members: &mut lazy_members, - }; - hand.handle(&get_tree, &tree.into_api(&mut tia_ctx).await).await - }) - .await, - api::HostExtReq::SysReq(api::SysReq::SysFwded(fwd)) => { - let fwd_tok = hand.will_handle_as(&fwd); - let api::SysFwded(sys_id, payload) = fwd; - let ctx = get_ctx(sys_id).await; - with_ctx(ctx.clone(), async move { - let sys = ctx.cted().inst(); - let reply = Rc::new(RefCell::new(None)); - let reply2 = reply.clone(); - let sub_hand = ExtReq::new(hand.reqnot(), async move |v| { - reply2.borrow_mut().replace(v); - }); - sys.dyn_request(sub_hand, payload).await; - let reply_buf = - reply.borrow_mut().take().expect("Request discarded but did not throw"); - hand.handle_as(fwd_tok, &reply_buf).await - }) - .await - }, - api::HostExtReq::LexExpr(lex @ api::LexExpr { sys, src, text, pos, id }) => - with_ctx(get_ctx(sys).await, async move { - let text = Tok::from_api(text, &i()).await; - let src = Sym::from_api(src, &i()).await; - let rep = Reporter::new(); - let expr_store = BorrowedExprStore::new(); - let trigger_char = text.chars().nth(pos as usize).unwrap(); - let ekey_na = ekey_not_applicable().await; - let ekey_cascade = ekey_cascade().await; - let lexers = ctx().cted().inst().dyn_lexers(); - for lx in lexers.iter().filter(|l| char_filter_match(l.char_filter(), trigger_char)) - { - let ctx = LexContext::new(&expr_store, &text, id, pos, src.clone(), &rep); - match lx.lex(&text[pos as usize..], &ctx).await { - Err(e) if e.any(|e| *e == ekey_na) => continue, - Err(e) => { - let eopt = e.keep_only(|e| *e != ekey_cascade).map(|e| Err(e.to_api())); - expr_store.dispose().await; - return hand.handle(&lex, &eopt).await; + let response = + api::NewSystemResponse { lex_filter, const_root, line_types, prelude }; + handle.reply(&new_sys, &response).await + }) + .await + }, + api::HostExtReq::GetMember(get_tree @ api::GetMember(sys_id, tree_id)) => + with_sys_record(sys_id, async { + let (path, tree) = get_lazy(tree_id).await; + let mut tia_ctx = + TreeIntoApiCtxImpl { path: Substack::Bottom, basepath: &path[..] }; + handle.reply(&get_tree, &tree.into_api(&mut tia_ctx).await).await + }) + .await, + api::HostExtReq::SysReq(api::SysReq::SysFwded(fwd)) => { + let fwd_tok = Witness::of(&fwd); + let api::SysFwded(sys_id, payload) = fwd; + with_sys_record(sys_id, async { + struct TrivialReqCycle<'a> { + req: &'a [u8], + rep: &'a mut Vec, + } + impl<'a> ReqReader<'a> for TrivialReqCycle<'a> { + fn reader(&mut self) -> Pin<&mut dyn AsyncRead> { + Pin::new(&mut self.req) as Pin<&mut _> + } + fn finish(self: Box) -> LocalBoxFuture<'a, Box + 'a>> { + Box::pin(async { self as Box<_> }) + } + } + impl<'a> ReqHandle<'a> for TrivialReqCycle<'a> { + fn start_reply( + self: Box, + ) -> LocalBoxFuture<'a, io::Result + 'a>>> { + Box::pin(async { Ok(self as Box<_>) }) + } + } + impl<'a> RepWriter<'a> for TrivialReqCycle<'a> { + fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> { + Pin::new(&mut self.rep) as Pin<&mut _> + } + fn finish( + self: Box, + ) -> LocalBoxFuture<'a, io::Result>> + { + Box::pin(async { Ok(Receipt::_new()) }) + } + } + let mut reply = Vec::new(); + let req = TrivialReqCycle { req: &payload, rep: &mut reply }; + let _ = cted().inst().dyn_request(Box::new(req)).await; + handle.reply(fwd_tok, &reply).await + }) + .await + }, + api::HostExtReq::LexExpr(lex @ api::LexExpr { sys, src, text, pos, id }) => + with_sys_record(sys, async { + let text = es(text).await; + let src = Sym::from_api(src).await; + let expr_store = BorrowedExprStore::new(); + let trigger_char = text.chars().nth(pos as usize).unwrap(); + let ekey_na = ekey_not_applicable().await; + let ekey_cascade = ekey_cascade().await; + let lexers = cted().inst().dyn_lexers(); + writeln!( + logger, + "sys={sys:?}, tc={trigger_char}, lexers={}", + lexers.iter().map(|l| format!("{l:?}")).join(",") + ); + for lx in + lexers.iter().filter(|l| char_filter_match(l.char_filter(), trigger_char)) + { + let ctx = LexContext::new(&expr_store, &text, id, pos, src.clone()); + match try_with_reporter(lx.lex(&text[pos as usize..], &ctx)).await { + Err(e) if e.any(|e| *e == ekey_na) => continue, + Err(e) => { + let eopt = e.keep_only(|e| *e != ekey_cascade).map(|e| Err(e.to_api())); + expr_store.dispose().await; + return handle.reply(&lex, &eopt).await; + }, + Ok((s, expr)) => { + let expr = expr.into_api(&mut (), &mut ()).await; + let pos = (text.len() - s.len()) as u32; + expr_store.dispose().await; + return handle.reply(&lex, &Some(Ok(api::LexedExpr { pos, expr }))).await; + }, + } + } + writeln!(logger, "Got notified about n/a character '{trigger_char}'"); + expr_store.dispose().await; + handle.reply(&lex, &None).await + }) + .await, + api::HostExtReq::ParseLine(pline) => { + let api::ParseLine { module, src, exported, comments, sys, line, idx } = &pline; + with_sys_record(*sys, async { + let parsers = cted().inst().dyn_parsers(); + let src = Sym::from_api(*src).await; + let comments = + join_all(comments.iter().map(|c| Comment::from_api(c, src.clone()))).await; + let expr_store = BorrowedExprStore::new(); + let line: Vec = + ttv_from_api(line, &mut &expr_store, &mut (), &src).await; + let snip = Snippet::new(line.first().expect("Empty line"), &line); + let parser = parsers[*idx as usize]; + let module = Sym::from_api(*module).await; + let pctx = ParsCtx::new(module); + let o_line = + match try_with_reporter(parser.parse(pctx, *exported, comments, snip)).await { + Err(e) => Err(e.to_api()), + Ok(t) => Ok(linev_into_api(t).await), + }; + mem::drop(line); + expr_store.dispose().await; + handle.reply(&pline, &o_line).await + }) + .await + }, + api::HostExtReq::FetchParsedConst(ref fpc @ api::FetchParsedConst(sys, id)) => + with_sys_record(sys, async { + let cnst = get_const(id).await; + handle.reply(fpc, &cnst.serialize().await).await + }) + .await, + api::HostExtReq::AtomReq(atom_req) => { + let atom = atom_req.get_atom(); + with_sys_record(atom.owner, async { + let (nfo, id, buf) = resolve_atom_type(atom); + let actx = AtomCtx(buf, atom.drop); + match &atom_req { + api::AtomReq::SerializeAtom(ser) => { + let mut buf = enc_vec(&id); + match nfo.serialize(actx, Pin::<&mut Vec<_>>::new(&mut buf)).await { + None => handle.reply(ser, &None).await, + Some(refs) => { + let refs = + join_all(refs.into_iter().map(async |ex| ex.into_api(&mut ()).await)) + .await; + handle.reply(ser, &Some((buf, refs))).await + }, + } }, - Ok((s, expr)) => { - let expr = expr.into_api(&mut (), &mut ()).await; - let pos = (text.len() - s.len()) as u32; + api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) => + handle.reply(print, &nfo.print(actx).await.to_api()).await, + api::AtomReq::Fwded(fwded) => { + let api::Fwded(_, key, payload) = &fwded; + let mut reply = Vec::new(); + let key = Sym::from_api(*key).await; + let some = nfo + .handle_req( + actx, + key, + Pin::<&mut &[u8]>::new(&mut &payload[..]), + Pin::<&mut Vec<_>>::new(&mut reply), + ) + .await; + handle.reply(fwded, &some.then_some(reply)).await + }, + api::AtomReq::CallRef(call @ api::CallRef(_, arg)) => { + let expr_store = BorrowedExprStore::new(); + let expr_handle = ExprHandle::borrowed(*arg, &expr_store); + let ret = nfo.call_ref(actx, Expr::from_handle(expr_handle.clone())).await; + let api_expr = ret.serialize().await; + mem::drop(expr_handle); expr_store.dispose().await; - return hand.handle(&lex, &Some(Ok(api::LexedExpr { pos, expr }))).await; + handle.reply(call, &api_expr).await + }, + api::AtomReq::FinalCall(call @ api::FinalCall(_, arg)) => { + let expr_store = BorrowedExprStore::new(); + let expr_handle = ExprHandle::borrowed(*arg, &expr_store); + let ret = nfo.call(actx, Expr::from_handle(expr_handle.clone())).await; + let api_expr = ret.serialize().await; + mem::drop(expr_handle); + expr_store.dispose().await; + handle.reply(call, &api_expr).await + }, + api::AtomReq::Command(cmd @ api::Command(_)) => match nfo.command(actx).await { + Err(e) => handle.reply(cmd, &Err(e.to_api())).await, + Ok(opt) => match opt { + None => handle.reply(cmd, &Ok(api::NextStep::Halt)).await, + Some(cont) => { + let cont = cont.serialize().await; + handle.reply(cmd, &Ok(api::NextStep::Continue(cont))).await + }, + }, }, } - } - writeln!(logger, "Got notified about n/a character '{trigger_char}'"); - expr_store.dispose().await; - hand.handle(&lex, &None).await - }) - .await, - api::HostExtReq::ParseLine(pline) => { - let api::ParseLine { module, src, exported, comments, sys, line, idx } = &pline; - with_ctx(get_ctx(*sys).await, async { - let parsers = ctx().cted().inst().dyn_parsers(); - let src = Sym::from_api(*src, &i()).await; - let comments = - join_all(comments.iter().map(|c| Comment::from_api(c, src.clone(), &interner))) - .await; - let expr_store = BorrowedExprStore::new(); - let line: Vec = - ttv_from_api(line, &mut &expr_store, &mut (), &src, &i()).await; - let snip = Snippet::new(line.first().expect("Empty line"), &line); - let parser = parsers[*idx as usize]; - let module = Sym::from_api(*module, &i()).await; - let reporter = Reporter::new(); - let pctx = ParsCtx::new(module, &reporter); - let parse_res = parser.parse(pctx, *exported, comments, snip).await; - let o_line = match reporter.merge(parse_res) { - Err(e) => Err(e.to_api()), - Ok(t) => Ok(linev_into_api(t).await), - }; - mem::drop(line); - expr_store.dispose().await; - hand.handle(&pline, &o_line).await - }) - .await - }, - api::HostExtReq::FetchParsedConst(ref fpc @ api::FetchParsedConst(sys, id)) => - with_ctx(get_ctx(sys).await, async move { - let cnst = get_const(id).await; - hand.handle(fpc, &cnst.serialize().await).await - }) - .await, - api::HostExtReq::AtomReq(atom_req) => { - let atom = atom_req.get_atom(); - let atom_req = atom_req.clone(); - with_atom_record(&get_ctx, atom, async move |nfo, id, buf| { - let actx = AtomCtx(buf, atom.drop); - match &atom_req { - api::AtomReq::SerializeAtom(ser) => { - let mut buf = enc_vec(&id).await; - match nfo.serialize(actx, Pin::<&mut Vec<_>>::new(&mut buf)).await { - None => hand.handle(ser, &None).await, - Some(refs) => { - let refs = - join_all(refs.into_iter().map(|ex| async { ex.into_api(&mut ()).await })) - .await; - hand.handle(ser, &Some((buf, refs))).await - }, - } - }, - api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) => - hand.handle(print, &nfo.print(actx).await.to_api()).await, - api::AtomReq::Fwded(fwded) => { - let api::Fwded(_, key, payload) = &fwded; - let mut reply = Vec::new(); - let key = Sym::from_api(*key, &interner).await; - let some = nfo - .handle_req( - actx, - key, - Pin::<&mut &[u8]>::new(&mut &payload[..]), - Pin::<&mut Vec<_>>::new(&mut reply), - ) - .await; - hand.handle(fwded, &some.then_some(reply)).await - }, - api::AtomReq::CallRef(call @ api::CallRef(_, arg)) => { - let expr_store = BorrowedExprStore::new(); - let expr_handle = ExprHandle::borrowed(*arg, &expr_store); - let ret = nfo.call_ref(actx, Expr::from_handle(expr_handle.clone())).await; - let api_expr = ret.serialize().await; - mem::drop(expr_handle); - expr_store.dispose().await; - hand.handle(call, &api_expr).await - }, - api::AtomReq::FinalCall(call @ api::FinalCall(_, arg)) => { - let expr_store = BorrowedExprStore::new(); - let expr_handle = ExprHandle::borrowed(*arg, &expr_store); - let ret = nfo.call(actx, Expr::from_handle(expr_handle.clone())).await; - let api_expr = ret.serialize().await; - mem::drop(expr_handle); - expr_store.dispose().await; - hand.handle(call, &api_expr).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.serialize().await; - hand.handle(cmd, &Ok(api::NextStep::Continue(cont))).await - }, - }, - }, - } - }) - .await - }, - api::HostExtReq::DeserAtom(deser) => { - let api::DeserAtom(sys, buf, refs) = &deser; - let mut read = &mut &buf[..]; - let ctx = get_ctx(*sys).await; - // SAFETY: deserialization implicitly grants ownership to previously owned exprs - let refs = (refs.iter()) - .map(|tk| Expr::from_handle(ExprHandle::deserialize(*tk))) - .collect_vec(); - let id = AtomTypeId::decode(Pin::new(&mut read)).await; - let inst = ctx.cted().inst(); - let nfo = atom_by_idx(inst.card(), id).expect("Deserializing atom with invalid ID"); - hand.handle(&deser, &nfo.deserialize(read, &refs).await).await - }, - } - } - .boxed_local() - } - }, - ); - *interner_cell.borrow_mut() = - Some(Interner::new_replica(rn.clone().map(|ir: api::IntReq| ir.into_root()))); - spawner(Box::pin(clone!(spawner; async move { - let mut streams = stream_select! { in_recv.map(Some), exit_recv.map(|_| None) }; - while let Some(item) = streams.next().await { - match item { - Some(rcvd) => spawner(Box::pin(clone!(rn; async move { rn.receive(&rcvd[..]).await }))), - None => break, - } - } - }))); - ExtInit { - header: ext_header, - port: Box::new(ExtensionOwner { - out_recv: Mutex::new(out_recv), - out_send, - _interner_cell: interner_cell, - _systems_lock: systems_lock, - }), + }) + .await + }, + api::HostExtReq::DeserAtom(deser) => { + let api::DeserAtom(sys, buf, refs) = &deser; + let read = &mut &buf[..]; + with_sys_record(*sys, async { + // SAFETY: deserialization implicitly grants ownership to previously owned exprs + let refs = (refs.iter()) + .map(|tk| Expr::from_handle(ExprHandle::deserialize(*tk))) + .collect_vec(); + let id = AtomTypeId::decode_slice(read); + let nfo = atom_by_idx(cted().inst().card(), id) + .expect("Deserializing atom with invalid ID"); + handle.reply(&deser, &nfo.deserialize(read, &refs).await).await + }) + .await + }, + } + }) + .await + }, + ); + // add essential services to the very tail, then fold all context into the run + // future + SYSTEM_TABLE + .scope( + RefCell::default(), + with_interner( + new_interner(), + with_logger( + logger2, + with_comm( + Rc::new(client), + ctx, + (self.context.into_iter()).fold( + Box::pin(async move { run_extension.await.unwrap() }) as LocalBoxFuture<()>, + |fut, cx| cx.apply(fut), + ), + ), + ), + ), + ) + .await + }) as Pin>); } } diff --git a/orchid-extension/src/expr.rs b/orchid-extension/src/expr.rs index 074cd98..4d497e6 100644 --- a/orchid-extension/src/expr.rs +++ b/orchid-extension/src/expr.rs @@ -10,12 +10,13 @@ use hashbrown::HashSet; use orchid_base::error::OrcErrv; use orchid_base::format::{FmtCtx, FmtUnit, Format}; use orchid_base::location::Pos; -use orchid_base::reqnot::Requester; +use orchid_base::stash::stash; use crate::api; use crate::atom::ForeignAtom; -use crate::context::{ctx, i}; +use crate::entrypoint::{notify, request}; use crate::gen_expr::{GExpr, GExprKind}; +use crate::system::sys_id; pub struct BorrowedExprStore(RefCell>>>); impl BorrowedExprStore { @@ -73,7 +74,7 @@ impl ExprHandle { /// to lend the expr, and you expect the receiver to use /// [ExprHandle::borrowed] or [ExprHandle::from_ticket] pub fn ticket(&self) -> api::ExprTicket { self.0 } - async fn send_acq(&self) { ctx().reqnot().notify(api::Acquire(ctx().sys_id(), self.0)).await } + async fn send_acq(&self) { notify(api::Acquire(sys_id(), self.0)).await } /// If this is the last one reference, do nothing, otherwise send an Acquire pub async fn on_borrow_expire(self: Rc) { self.serialize().await; } /// Drop the handle and get the ticket without a release notification. @@ -94,8 +95,8 @@ impl fmt::Debug for ExprHandle { } impl Drop for ExprHandle { fn drop(&mut self) { - let notif = api::Release(ctx().sys_id(), self.0); - ctx().spawn(async move { ctx().reqnot().clone().notify(notif).await }) + let notif = api::Release(sys_id(), self.0); + stash(async move { notify(notif).await }) } } @@ -117,12 +118,12 @@ impl Expr { } pub async fn data(&self) -> &ExprData { (self.data.get_or_init(async { - let details = ctx().reqnot().request(api::Inspect { target: self.handle.ticket() }).await; - let pos = Pos::from_api(&details.location, &i()).await; + let details = request(api::Inspect { target: self.handle.ticket() }).await; + let pos = Pos::from_api(&details.location).await; let kind = match details.kind { api::InspectedKind::Atom(a) => ExprKind::Atom(ForeignAtom::new(self.handle.clone(), a, pos.clone())), - api::InspectedKind::Bottom(b) => ExprKind::Bottom(OrcErrv::from_api(&b, &i()).await), + api::InspectedKind::Bottom(b) => ExprKind::Bottom(OrcErrv::from_api(&b).await), api::InspectedKind::Opaque => ExprKind::Opaque, }; ExprData { pos, kind } @@ -150,8 +151,7 @@ impl Format for Expr { match &self.data().await.kind { ExprKind::Opaque => "OPAQUE".to_string().into(), ExprKind::Bottom(b) => format!("Bottom({b})").into(), - ExprKind::Atom(a) => - FmtUnit::from_api(&ctx().reqnot().request(api::ExtAtomPrint(a.atom.clone())).await), + ExprKind::Atom(a) => FmtUnit::from_api(&request(api::ExtAtomPrint(a.atom.clone())).await), } } } diff --git a/orchid-extension/src/ext_port.rs b/orchid-extension/src/ext_port.rs new file mode 100644 index 0000000..e83fbb4 --- /dev/null +++ b/orchid-extension/src/ext_port.rs @@ -0,0 +1,12 @@ +use std::pin::Pin; +use std::rc::Rc; + +use futures::future::LocalBoxFuture; +use futures::{AsyncRead, AsyncWrite}; + +pub struct ExtPort { + pub input: Pin>, + pub output: Pin>, + pub log: Pin>, + pub spawn: Rc)>, +} diff --git a/orchid-extension/src/func_atom.rs b/orchid-extension/src/func_atom.rs index 47f8c5e..ba0e639 100644 --- a/orchid-extension/src/func_atom.rs +++ b/orchid-extension/src/func_atom.rs @@ -1,12 +1,12 @@ use std::any::TypeId; use std::borrow::Cow; +use std::cell::RefCell; use std::collections::HashMap; use std::future::Future; use std::pin::Pin; use std::rc::Rc; use futures::future::LocalBoxFuture; -use futures::lock::Mutex; use futures::{AsyncWrite, FutureExt}; use itertools::Itertools; use never::Never; @@ -15,15 +15,17 @@ use orchid_base::clone; use orchid_base::error::OrcRes; use orchid_base::format::{FmtCtx, FmtUnit}; use orchid_base::name::Sym; +use task_local::task_local; use trait_set::trait_set; +use crate::api; use crate::atom::Atomic; use crate::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant}; -use crate::context::{SysCtxEntry, ctx, i}; use crate::conv::ToExpr; use crate::coroutine_exec::{ExecHandle, exec}; use crate::expr::Expr; use crate::gen_expr::GExpr; +use crate::system::sys_id; trait_set! { trait FunCB = Fn(Vec) -> LocalBoxFuture<'static, OrcRes> + 'static; @@ -34,9 +36,14 @@ pub trait ExprFunc: Clone + 'static { fn apply<'a>(&self, hand: ExecHandle<'a>, v: Vec) -> impl Future>; } -#[derive(Default)] -struct FunsCtx(Mutex>); -impl SysCtxEntry for FunsCtx {} +task_local! { + static FUNS_CTX: RefCell>; +} + +pub(crate) fn with_funs_ctx<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> { + Box::pin(FUNS_CTX.scope(RefCell::default(), fut)) +} + #[derive(Clone)] struct FunRecord { argtyps: &'static [TypeId], @@ -77,17 +84,17 @@ pub(crate) struct Fun { } impl Fun { pub async fn new>(path: Sym, f: F) -> Self { - let ctx = ctx(); - let funs: &FunsCtx = ctx.get_or_default(); - let mut fung = funs.0.lock().await; - let record = if let Some(record) = fung.get(&path) { - record.clone() - } else { - let record = process_args(f); - fung.insert(path.clone(), record.clone()); - record - }; - Self { args: vec![], path, record } + FUNS_CTX.with(|cx| { + let mut fung = cx.borrow_mut(); + let record = if let Some(record) = fung.get(&(sys_id(), path.clone())) { + record.clone() + } else { + let record = process_args(f); + fung.insert((sys_id(), path.clone()), record.clone()); + record + }; + Self { args: vec![], path, record } + }) } pub fn arity(&self) -> u8 { self.record.argtyps.len() as u8 } } @@ -108,12 +115,12 @@ impl OwnedAtom for Fun { } async fn call(self, arg: Expr) -> GExpr { self.call_ref(arg).await } async fn serialize(&self, write: Pin<&mut (impl AsyncWrite + ?Sized)>) -> Self::Refs { - self.path.to_api().encode(write).await; + self.path.to_api().encode(write).await.unwrap(); self.args.clone() } async fn deserialize(mut ds_cx: impl DeserializeCtx, args: Self::Refs) -> Self { - let path = Sym::from_api(ds_cx.decode().await, &i()).await; - let record = (ctx().get::().0.lock().await.get(&path)) + let path = Sym::from_api(ds_cx.decode().await).await; + let record = (FUNS_CTX.with(|funs| funs.borrow().get(&(sys_id(), path.clone())).cloned())) .expect("Function missing during deserialization") .clone(); Self { args, path, record } diff --git a/orchid-extension/src/gen_expr.rs b/orchid-extension/src/gen_expr.rs index a04e57a..afed46d 100644 --- a/orchid-extension/src/gen_expr.rs +++ b/orchid-extension/src/gen_expr.rs @@ -6,12 +6,11 @@ use orchid_base::error::{OrcErr, OrcErrv}; use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants}; use orchid_base::location::Pos; use orchid_base::name::Sym; -use orchid_base::reqnot::Requester; use orchid_base::{match_mapping, tl_cache}; use crate::api; use crate::atom::{AtomFactory, ToAtom}; -use crate::context::ctx; +use crate::entrypoint::request; use crate::expr::Expr; #[derive(Clone, Debug)] @@ -40,7 +39,7 @@ impl GExpr { } pub fn at(self, pos: Pos) -> Self { GExpr { pos, kind: self.kind } } pub async fn create(self) -> Expr { - Expr::deserialize(ctx().reqnot().request(api::Create(self.serialize().await)).await).await + Expr::deserialize(request(api::Create(self.serialize().await)).await).await } } impl Format for GExpr { diff --git a/orchid-extension/src/interner.rs b/orchid-extension/src/interner.rs index 49700e3..cd90006 100644 --- a/orchid-extension/src/interner.rs +++ b/orchid-extension/src/interner.rs @@ -1,73 +1,48 @@ -use std::borrow::Borrow; -use std::cell::RefCell; -use std::fmt::Debug; -use std::hash::Hash; -use std::rc::{Rc, Weak}; +use std::rc::Rc; -use hashbrown::HashMap; -use orchid_api_traits::Coding; -use orchid_base::interner::{IStr, IStrHandle, IStrv, IStrvHandle}; +use futures::future::{LocalBoxFuture, join_all, ready}; +use itertools::Itertools; +use orchid_base::interner::local_interner::{Int, StrBranch, StrvBranch}; +use orchid_base::interner::{IStr, IStrv, InternerSrv}; use crate::api; +use crate::entrypoint::request; -trait Branch: 'static { - type Token: Clone + Copy + Debug + Hash + PartialEq + Eq + PartialOrd + Ord + Coding + 'static; - type Data: 'static + Borrow; - type Borrow: ToOwned + ?Sized; - type Handle: AsRef; - type Interned: Clone; - fn mk_interned(t: Self::Token, h: Rc) -> Self::Interned; +#[derive(Default)] +struct ExtInterner { + str: Int, + strv: Int, +} +impl InternerSrv for ExtInterner { + fn is<'a>(&'a self, v: &'a str) -> LocalBoxFuture<'a, IStr> { + match self.str.i(v) { + Ok(i) => Box::pin(ready(i)), + Err(e) => Box::pin(async { e.set_if_empty(request(api::InternStr(v.to_owned())).await) }), + } + } + fn es(&self, t: api::TStr) -> LocalBoxFuture<'_, IStr> { + match self.str.e(t) { + Ok(i) => Box::pin(ready(i)), + Err(e) => Box::pin(async move { e.set_if_empty(Rc::new(request(api::ExternStr(t)).await)) }), + } + } + fn iv<'a>(&'a self, v: &'a [IStr]) -> LocalBoxFuture<'a, IStrv> { + match self.strv.i(v) { + Ok(i) => Box::pin(ready(i)), + Err(e) => Box::pin(async { + e.set_if_empty(request(api::InternStrv(v.iter().map(|is| is.to_api()).collect_vec())).await) + }), + } + } + fn ev(&self, t: orchid_api::TStrv) -> LocalBoxFuture<'_, IStrv> { + match self.strv.e(t) { + Ok(i) => Box::pin(ready(i)), + Err(e) => Box::pin(async move { + let tstr_v = request(api::ExternStrv(t)).await; + e.set_if_empty(Rc::new(join_all(tstr_v.into_iter().map(|t| self.es(t))).await)) + }), + } + } } -struct StrBranch; -impl Branch for StrBranch { - type Data = String; - type Token = api::TStr; - type Borrow = str; - type Handle = Handle; - type Interned = IStr; - fn mk_interned(t: Self::Token, h: Rc) -> Self::Interned { IStr(t, h) } -} -struct StrvBranch; -impl Branch for StrvBranch { - type Data = Vec; - type Token = api::TStrv; - type Borrow = [IStr]; - type Handle = Handle; - type Interned = IStrv; - fn mk_interned(t: Self::Token, h: Rc) -> Self::Interned { IStrv(t, h) } -} - -struct Data { - token: B::Token, - data: Rc, -} - -struct Handle { - data: Rc>, - parent: Weak>>, -} -impl IStrHandle for Handle {} -impl AsRef for Handle { - fn as_ref(&self) -> &str { self.data.data.as_ref().as_ref() } -} -impl IStrvHandle for Handle {} -impl AsRef<[IStr]> for Handle { - fn as_ref(&self) -> &[IStr] { self.data.data.as_ref().as_ref() } -} - -struct Rec { - handle: Weak, - data: Rc>, -} - -struct IntData { - by_tok: HashMap>, - by_data: HashMap, Rec>, -} -impl IntData { - async fn i(&mut self, q: &B::Borrow) -> B::Interned { todo!() } - async fn e(&mut self, q: &B::Token) -> B::Interned { todo!() } -} - -struct Int(Rc>>); +pub fn new_interner() -> Rc { Rc::::default() } diff --git a/orchid-extension/src/lexer.rs b/orchid-extension/src/lexer.rs index e62af31..7634b93 100644 --- a/orchid-extension/src/lexer.rs +++ b/orchid-extension/src/lexer.rs @@ -1,27 +1,24 @@ use std::fmt; +use std::fmt::Debug; use std::future::Future; use std::ops::RangeInclusive; use futures::FutureExt; use futures::future::LocalBoxFuture; -use orchid_base::error::{OrcErrv, OrcRes, Reporter, mk_errv}; -use orchid_base::interner::{Interner, Tok}; +use orchid_base::error::{OrcErrv, OrcRes, mk_errv}; +use orchid_base::interner::{IStr, is}; use orchid_base::location::{Pos, SrcRange}; use orchid_base::name::Sym; -use orchid_base::parse::ParseCtx; -use orchid_base::reqnot::Requester; use crate::api; -use crate::context::{ctx, i}; +use crate::entrypoint::request; use crate::expr::BorrowedExprStore; use crate::parser::PTokTree; use crate::tree::GenTokTree; -pub async fn ekey_cascade() -> Tok { - i().i("An error cascading from a recursive call").await -} -pub async fn ekey_not_applicable() -> Tok { - i().i("Pseudo-error to communicate that the current branch in a dispatch doesn't apply").await +pub async fn ekey_cascade() -> IStr { is("An error cascading from a recursive call").await } +pub async fn ekey_not_applicable() -> IStr { + is("Pseudo-error to communicate that the current branch in a dispatch doesn't apply").await } const MSG_INTERNAL_ERROR: &str = "This error is a sentinel for the extension library.\ it should not be emitted by the extension."; @@ -36,23 +33,20 @@ pub async fn err_not_applicable() -> OrcErrv { pub struct LexContext<'a> { pub(crate) exprs: &'a BorrowedExprStore, - pub text: &'a Tok, + pub text: &'a IStr, pub id: api::ParsId, pub pos: u32, - i: Interner, pub(crate) src: Sym, - pub(crate) rep: &'a Reporter, } impl<'a> LexContext<'a> { pub fn new( exprs: &'a BorrowedExprStore, - text: &'a Tok, + text: &'a IStr, id: api::ParsId, pos: u32, src: Sym, - rep: &'a Reporter, ) -> Self { - Self { exprs, i: i(), id, pos, rep, src, text } + Self { exprs, id, pos, src, text } } pub fn src(&self) -> &Sym { &self.src } /// This function returns [PTokTree] because it can never return @@ -61,10 +55,10 @@ impl<'a> LexContext<'a> { /// for embedding in the return value. pub async fn recurse(&self, tail: &'a str) -> OrcRes<(&'a str, PTokTree)> { let start = self.pos(tail); - let Some(lx) = ctx().reqnot().request(api::SubLex { pos: start, id: self.id }).await else { + let Some(lx) = request(api::SubLex { pos: start, id: self.id }).await else { return Err(err_cascade().await); }; - let tree = PTokTree::from_api(&lx.tree, &mut { self.exprs }, &mut (), &self.src, &i()).await; + let tree = PTokTree::from_api(&lx.tree, &mut { self.exprs }, &mut (), &self.src).await; Ok((&self.text[lx.pos as usize..], tree)) } @@ -77,12 +71,8 @@ impl<'a> LexContext<'a> { SrcRange::new(self.pos(tail) - len.try_into().unwrap()..self.pos(tail), &self.src) } } -impl ParseCtx for LexContext<'_> { - fn i(&self) -> &Interner { &self.i } - fn rep(&self) -> &Reporter { self.rep } -} -pub trait Lexer: Send + Sync + Sized + Default + 'static { +pub trait Lexer: Debug + Send + Sync + Sized + Default + 'static { const CHAR_FILTER: &'static [RangeInclusive]; fn lex<'a>( tail: &'a str, @@ -90,7 +80,7 @@ pub trait Lexer: Send + Sync + Sized + Default + 'static { ) -> impl Future>; } -pub trait DynLexer: Send + Sync + 'static { +pub trait DynLexer: Debug + Send + Sync + 'static { fn char_filter(&self) -> &'static [RangeInclusive]; fn lex<'a>( &self, diff --git a/orchid-extension/src/lib.rs b/orchid-extension/src/lib.rs index fd71ca2..86bf5b2 100644 --- a/orchid-extension/src/lib.rs +++ b/orchid-extension/src/lib.rs @@ -7,12 +7,11 @@ pub mod conv; pub mod coroutine_exec; pub mod entrypoint; pub mod expr; +pub mod ext_port; pub mod func_atom; pub mod gen_expr; -pub mod lexer; -// pub mod msg; -pub mod context; pub mod interner; +pub mod lexer; pub mod other_system; pub mod parser; pub mod reflection; diff --git a/orchid-extension/src/other_system.rs b/orchid-extension/src/other_system.rs index c7043df..a50f1cf 100644 --- a/orchid-extension/src/other_system.rs +++ b/orchid-extension/src/other_system.rs @@ -1,6 +1,7 @@ use crate::api; use crate::system::{DynSystemCard, SystemCard}; +#[derive(Debug)] pub struct SystemHandle { pub(crate) card: C, pub(crate) id: api::SysId, diff --git a/orchid-extension/src/parser.rs b/orchid-extension/src/parser.rs index a1ad42c..39a9a60 100644 --- a/orchid-extension/src/parser.rs +++ b/orchid-extension/src/parser.rs @@ -5,21 +5,22 @@ use futures::future::{LocalBoxFuture, join_all}; use futures::{FutureExt, Stream, StreamExt}; use itertools::Itertools; use never::Never; -use orchid_base::error::{OrcErrv, OrcRes, Reporter}; +use orchid_base::error::{OrcErrv, OrcRes}; use orchid_base::id_store::IdStore; -use orchid_base::interner::{Interner, Tok}; +use orchid_base::interner::IStr; use orchid_base::location::SrcRange; use orchid_base::match_mapping; use orchid_base::name::Sym; -use orchid_base::parse::{Comment, ParseCtx, Snippet}; -use orchid_base::reqnot::Requester; +use orchid_base::parse::{Comment, Snippet}; use orchid_base::tree::{TokTree, Token, ttv_into_api}; +use task_local::task_local; use crate::api; -use crate::context::{SysCtxEntry, ctx, i}; use crate::conv::ToExpr; +use crate::entrypoint::request; use crate::expr::Expr; use crate::gen_expr::GExpr; +use crate::system::sys_id; use crate::tree::{GenTok, GenTokTree}; pub type PTok = Token; @@ -82,27 +83,21 @@ pub type ParserObj = &'static dyn DynParser; pub struct ParsCtx<'a> { _parse: PhantomData<&'a mut ()>, module: Sym, - reporter: &'a Reporter, - i: Interner, } impl<'a> ParsCtx<'a> { - pub(crate) fn new(module: Sym, reporter: &'a Reporter) -> Self { - Self { _parse: PhantomData, module, reporter, i: i() } - } + pub(crate) fn new(module: Sym) -> Self { Self { _parse: PhantomData, module } } pub fn module(&self) -> Sym { self.module.clone() } } -impl ParseCtx for ParsCtx<'_> { - fn i(&self) -> &Interner { &self.i } - fn rep(&self) -> &Reporter { self.reporter } -} type BoxConstCallback = Box LocalBoxFuture<'static, GExpr>>; -#[derive(Default)] -pub(crate) struct ParsedConstCtxEntry { - pub(crate) consts: IdStore, +task_local! { + static PARSED_CONST_CTX: IdStore +} + +pub(crate) fn with_parsed_const_ctx<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> { + Box::pin(PARSED_CONST_CTX.scope(IdStore::default(), fut)) } -impl SysCtxEntry for ParsedConstCtxEntry {} pub struct ParsedLine { pub sr: SrcRange, @@ -114,7 +109,7 @@ impl ParsedLine { sr: &SrcRange, comments: impl IntoIterator, exported: bool, - name: Tok, + name: IStr, f: F, ) -> Self { let cb = Box::new(|ctx| async move { f(ctx).await.to_gen().await }.boxed_local()); @@ -126,7 +121,7 @@ impl ParsedLine { sr: &SrcRange, comments: impl IntoIterator, exported: bool, - name: &Tok, + name: &IStr, use_prelude: bool, lines: impl IntoIterator, ) -> Self { @@ -145,7 +140,7 @@ impl ParsedLine { exported: mem.exported, kind: match mem.kind { ParsedMemKind::Const(cb) => api::ParsedMemberKind::Constant(api::ParsedConstId( - ctx().get_or_default::().consts.add(cb).id(), + PARSED_CONST_CTX.with(|consts| consts.add(cb).id()), )), ParsedMemKind::Mod { lines, use_prelude } => api::ParsedMemberKind::Module { lines: linev_into_api(lines).boxed_local().await, @@ -170,7 +165,7 @@ pub enum ParsedLineKind { } pub struct ParsedMem { - pub name: Tok, + pub name: IStr, pub exported: bool, pub kind: ParsedMemKind, } @@ -191,14 +186,14 @@ impl ConstCtx { ) -> impl Stream> + 'b { let resolve_names = api::ResolveNames { constid: self.constid, - sys: ctx().sys_id(), + sys: sys_id(), names: names.into_iter().map(|n| n.to_api()).collect_vec(), }; stream(async |mut cx| { - for name_opt in ctx().reqnot().request(resolve_names).await { + for name_opt in request(resolve_names).await { cx.emit(match name_opt { - Err(e) => Err(OrcErrv::from_api(&e, &i()).await), - Ok(name) => Ok(Sym::from_api(name, &i()).await), + Err(e) => Err(OrcErrv::from_api(&e).await), + Ok(name) => Ok(Sym::from_api(name).await), }) .await } @@ -210,8 +205,7 @@ impl ConstCtx { } pub(crate) async fn get_const(id: api::ParsedConstId) -> GExpr { - let cb = (ctx().get_or_default::().consts.get(id.0)) - .expect("Bad ID or double read of parsed const") - .remove(); + let cb = PARSED_CONST_CTX + .with(|ent| ent.get(id.0).expect("Bad ID or double read of parsed const").remove()); cb(ConstCtx { constid: id }).await } diff --git a/orchid-extension/src/reflection.rs b/orchid-extension/src/reflection.rs index c623b10..cf28a4d 100644 --- a/orchid-extension/src/reflection.rs +++ b/orchid-extension/src/reflection.rs @@ -1,15 +1,18 @@ -use std::cell::OnceCell; +use std::cell::{OnceCell, RefCell}; use std::rc::Rc; use futures::FutureExt; +use futures::future::LocalBoxFuture; use futures::lock::Mutex; +use hashbrown::HashMap; use memo_map::MemoMap; -use orchid_base::interner::Tok; +use orchid_base::interner::{IStr, es, iv}; use orchid_base::name::{NameLike, VPath}; -use orchid_base::reqnot::Requester; +use task_local::task_local; use crate::api; -use crate::context::{SysCtxEntry, ctx, i}; +use crate::entrypoint::request; +use crate::system::sys_id; #[derive(Debug)] pub struct ReflMemData { @@ -33,29 +36,28 @@ pub enum ReflMemKind { pub struct ReflModData { inferred: Mutex, path: VPath, - members: MemoMap, ReflMem>, + members: MemoMap, } #[derive(Clone, Debug)] pub struct ReflMod(Rc); impl ReflMod { - pub fn path(&self) -> &[Tok] { &self.0.path[..] } + pub fn path(&self) -> &[IStr] { &self.0.path[..] } pub fn is_root(&self) -> bool { self.0.path.is_empty() } async fn try_populate(&self) -> Result<(), api::LsModuleError> { - let path_tok = i().i(&self.0.path[..]).await; - let reply = match ctx().reqnot().request(api::LsModule(ctx().sys_id(), path_tok.to_api())).await - { + let path_tok = iv(&self.0.path[..]).await; + let reply = match request(api::LsModule(sys_id(), path_tok.to_api())).await { Err(api::LsModuleError::TreeUnavailable) => panic!("Reflected tree accessed outside an interpreter call. This extension is faulty."), Err(err) => return Err(err), Ok(details) => details, }; for (k, v) in reply.members { - let k = i().ex(k).await; + let k = es(k).await; let mem = match self.0.members.get(&k) { Some(mem) => mem, None => { - let path = self.0.path.clone().name_with_suffix(k.clone()).to_sym(&i()).await; + let path = self.0.path.clone().name_with_suffix(k.clone()).to_sym().await; let kind = match v.kind { api::MemberInfoKind::Constant => ReflMemKind::Const, api::MemberInfoKind::Module => @@ -68,7 +70,7 @@ impl ReflMod { } Ok(()) } - pub async fn get_child(&self, key: &Tok) -> Option { + pub async fn get_child(&self, key: &IStr) -> Option { let inferred_g = self.0.inferred.lock().await; if let Some(mem) = self.0.members.get(key) { return Some(mem.clone()); @@ -86,7 +88,7 @@ impl ReflMod { } self.0.members.get(key).cloned() } - pub async fn get_by_path(&self, path: &[Tok]) -> Result { + pub async fn get_by_path(&self, path: &[IStr]) -> Result { let (next, tail) = path.split_first().expect("Attempted to walk by empty path"); let inferred_g = self.0.inferred.lock().await; if let Some(next) = self.0.members.get(next) { @@ -130,9 +132,9 @@ impl ReflMod { } } -#[derive(Clone)] -struct ReflRoot(ReflMod); -impl SysCtxEntry for ReflRoot {} +task_local! { + static REFL_ROOTS: RefCell> +} #[derive(Clone, Debug)] pub struct InvalidPathError { @@ -150,8 +152,12 @@ fn default_member(is_root: bool, kind: ReflMemKind) -> ReflMem { })) } -fn get_root() -> ReflRoot { - ctx().get_or_insert(|| ReflRoot(default_module(VPath::new([])))).clone() +pub fn refl() -> ReflMod { + REFL_ROOTS.with(|tbl| { + tbl.borrow_mut().entry(sys_id()).or_insert_with(|| default_module(VPath::new([]))).clone() + }) } -pub fn refl() -> ReflMod { get_root().0.clone() } +pub fn with_refl_roots<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> { + Box::pin(REFL_ROOTS.scope(RefCell::default(), fut)) +} diff --git a/orchid-extension/src/system.rs b/orchid-extension/src/system.rs index 9a6fd6d..1ae6e73 100644 --- a/orchid-extension/src/system.rs +++ b/orchid-extension/src/system.rs @@ -1,4 +1,5 @@ use std::any::{Any, TypeId}; +use std::fmt::Debug; use std::future::Future; use std::num::NonZero; use std::pin::Pin; @@ -8,13 +9,13 @@ use futures::future::LocalBoxFuture; use orchid_api_traits::{Coding, Decode, Encode, Request}; use orchid_base::boxed_iter::BoxedIter; use orchid_base::name::Sym; -use orchid_base::reqnot::{Receipt, Requester}; +use orchid_base::reqnot::{Receipt, ReqHandle, ReqReader, ReqReaderExt}; +use task_local::task_local; use crate::api; use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId, AtomicFeatures, ForeignAtom, TAtom, get_info}; -use crate::context::ctx; use crate::coroutine_exec::Replier; -use crate::entrypoint::ExtReq; +use crate::entrypoint::request; use crate::func_atom::{Fun, Lambda}; use crate::lexer::LexerObj; use crate::parser::ParserObj; @@ -22,7 +23,7 @@ use crate::system_ctor::{CtedObj, SystemCtor}; use crate::tree::GenMember; /// System as consumed by foreign code -pub trait SystemCard: Default + Send + Sync + 'static { +pub trait SystemCard: Debug + Default + Send + Sync + 'static { type Ctor: SystemCtor; type Req: Coding; fn atoms() -> impl IntoIterator>>; @@ -67,7 +68,7 @@ pub async fn resolv_atom( sys: &(impl DynSystemCard + ?Sized), atom: &api::Atom, ) -> Box { - let tid = AtomTypeId::decode(Pin::new(&mut &atom.data.0[..])).await; + let tid = AtomTypeId::decode(Pin::new(&mut &atom.data.0[..])).await.unwrap(); atom_by_idx(sys, tid).expect("Value of nonexistent type found") } @@ -84,7 +85,10 @@ pub trait System: Send + Sync + SystemCard + 'static { fn env() -> impl Future>; fn lexers() -> Vec; fn parsers() -> Vec; - fn request(hand: ExtReq<'_>, req: Self::Req) -> impl Future>; + fn request<'a>( + hand: Box + 'a>, + req: Self::Req, + ) -> impl Future>; } pub trait DynSystem: Send + Sync + DynSystemCard + 'static { @@ -92,7 +96,7 @@ pub trait DynSystem: Send + Sync + DynSystemCard + 'static { fn dyn_env(&self) -> LocalBoxFuture<'_, Vec>; fn dyn_lexers(&self) -> Vec; fn dyn_parsers(&self) -> Vec; - fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec) -> LocalBoxFuture<'a, Receipt<'a>>; + fn dyn_request<'a>(&self, hand: Box + 'a>) -> LocalBoxFuture<'a, Receipt<'a>>; fn card(&self) -> &dyn DynSystemCard; } @@ -101,26 +105,41 @@ impl DynSystem for T { fn dyn_env(&self) -> LocalBoxFuture<'_, Vec> { Self::env().boxed_local() } fn dyn_lexers(&self) -> Vec { Self::lexers() } fn dyn_parsers(&self) -> Vec { Self::parsers() } - fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec) -> LocalBoxFuture<'a, Receipt<'a>> { + fn dyn_request<'a>( + &self, + mut hand: Box + 'a>, + ) -> LocalBoxFuture<'a, Receipt<'a>> { Box::pin(async move { - Self::request(hand, ::Req::decode(Pin::new(&mut &req[..])).await).await + let value = hand.read_req::<::Req>().await.unwrap(); + Self::request(hand.finish().await, value).await }) } fn card(&self) -> &dyn DynSystemCard { self } } +#[derive(Clone)] +pub(crate) struct SysCtx(pub api::SysId, pub CtedObj); + +task_local! { + static SYS_CTX: SysCtx; +} + +pub(crate) async fn with_sys(sys: SysCtx, fut: F) -> F::Output { + SYS_CTX.scope(sys, fut).await +} + +pub fn sys_id() -> api::SysId { SYS_CTX.with(|cx| cx.0) } +pub fn cted() -> CtedObj { SYS_CTX.with(|cx| cx.1.clone()) } pub async fn downcast_atom(foreign: ForeignAtom) -> Result, ForeignAtom> where A: AtomicFeatures { let mut data = &foreign.atom.data.0[..]; - let ctx = ctx(); - let value = AtomTypeId::decode(Pin::new(&mut data)).await; - let own_inst = ctx.get::().inst(); - let owner = if *ctx.get::() == foreign.atom.owner { + let value = AtomTypeId::decode_slice(&mut data); + let cted = cted(); + let own_inst = cted.inst(); + let owner = if sys_id() == foreign.atom.owner { own_inst.card() } else { - (ctx.get::().deps().find(|s| s.id() == foreign.atom.owner)) - .ok_or_else(|| foreign.clone())? - .get_card() + cted.deps().find(|s| s.id() == foreign.atom.owner).ok_or_else(|| foreign.clone())?.get_card() }; if owner.atoms().flatten().all(|dynfo| dynfo.tid() != TypeId::of::()) { return Err(foreign); @@ -130,22 +149,24 @@ where A: AtomicFeatures { return Err(foreign); } let val = dynfo.decode(AtomCtx(data, foreign.atom.drop)).await; - let value = *val.downcast::().expect("atom decode returned wrong type"); - Ok(TAtom { value, untyped: foreign }) + let Ok(value) = val.downcast::() else { + panic!("decode of {} returned wrong type.", dynfo.name()); + }; + Ok(TAtom { value: *value, untyped: foreign }) } pub async fn dep_req>(req: Req) -> Req::Response { - let ctx = ctx(); let mut msg = Vec::new(); - req.into().encode(std::pin::pin!(&mut msg)).await; - let own_inst = ctx.get::().inst(); + req.into().encode_vec(&mut msg); + let cted = cted(); + let own_inst = cted.inst(); let owner = if own_inst.card().type_id() == TypeId::of::() { - ctx.sys_id() + sys_id() } else { - (ctx.get::().deps().find(|s| s.get_card().type_id() == TypeId::of::())) + (cted.deps().find(|s| s.get_card().type_id() == TypeId::of::())) .expect("System not in dependency array") .id() }; - let reply = ctx.reqnot().request(api::SysFwd(owner, msg)).await; - Req::Response::decode(std::pin::pin!(&reply[..])).await + let reply = request(api::SysFwd(owner, msg)).await; + Req::Response::decode(std::pin::pin!(&reply[..])).await.unwrap() } diff --git a/orchid-extension/src/system_ctor.rs b/orchid-extension/src/system_ctor.rs index 933db61..c5bcd61 100644 --- a/orchid-extension/src/system_ctor.rs +++ b/orchid-extension/src/system_ctor.rs @@ -1,4 +1,5 @@ use std::any::Any; +use std::fmt::Debug; use std::sync::Arc; use orchid_base::boxed_iter::{BoxedIter, box_empty, box_once}; @@ -8,6 +9,7 @@ use crate::api; use crate::other_system::{DynSystemHandle, SystemHandle}; use crate::system::{DynSystem, System, SystemCard}; +#[derive(Debug)] pub struct Cted { pub deps: ::Sat, pub inst: Arc, @@ -15,7 +17,7 @@ pub struct Cted { impl Clone for Cted { fn clone(&self) -> Self { Self { deps: self.deps.clone(), inst: self.inst.clone() } } } -pub trait DynCted: Send + Sync + 'static { +pub trait DynCted: Debug + Send + Sync + 'static { fn as_any(&self) -> &dyn Any; fn deps<'a>(&'a self) -> BoxedIter<'a, &'a (dyn DynSystemHandle + 'a)>; fn inst(&self) -> Arc; @@ -27,11 +29,11 @@ impl DynCted for Cted { } pub type CtedObj = Arc; -pub trait DepSat: Clone + Send + Sync + 'static { +pub trait DepSat: Debug + Clone + Send + Sync + 'static { fn iter<'a>(&'a self) -> BoxedIter<'a, &'a (dyn DynSystemHandle + 'a)>; } -pub trait DepDef { +pub trait DepDef: Debug { type Sat: DepSat; fn report(names: &mut impl FnMut(&'static str)); fn create(take: &mut impl FnMut() -> api::SysId) -> Self::Sat; @@ -57,17 +59,17 @@ impl DepDef for () { fn report(_: &mut impl FnMut(&'static str)) {} } -pub trait SystemCtor: Send + Sync + 'static { +pub trait SystemCtor: Debug + Send + Sync + 'static { type Deps: DepDef; type Instance: System; const NAME: &'static str; const VERSION: f64; /// Create a system instance. When this function is called, a context object /// isn't yet available - fn inst(deps: ::Sat) -> Self::Instance; + fn inst(&self, deps: ::Sat) -> Self::Instance; } -pub trait DynSystemCtor: Send + Sync + 'static { +pub trait DynSystemCtor: Debug + Send + Sync + 'static { fn decl(&self, id: api::SysDeclId) -> api::SystemDecl; fn new_system(&self, new: &api::NewSystem) -> CtedObj; } @@ -84,7 +86,7 @@ impl DynSystemCtor for T { fn new_system(&self, api::NewSystem { system: _, id: _, depends }: &api::NewSystem) -> CtedObj { let mut ids = depends.iter().copied(); let deps = T::Deps::create(&mut || ids.next().unwrap()); - let inst = Arc::new(T::inst(deps.clone())); + let inst = Arc::new(self.inst(deps.clone())); Arc::new(Cted:: { deps, inst }) } } @@ -151,8 +153,4 @@ mod dep_set_tuple_impls { dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J); dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J, K); dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L); // 12 - dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M); - dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N); - dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); - dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); // 16 } diff --git a/orchid-extension/src/tokio.rs b/orchid-extension/src/tokio.rs index 199b3d2..0c9859b 100644 --- a/orchid-extension/src/tokio.rs +++ b/orchid-extension/src/tokio.rs @@ -1,57 +1,24 @@ -use crate::entrypoint::ExtensionData; +use std::rc::Rc; + +use crate::entrypoint::ExtensionBuilder; +use crate::ext_port::ExtPort; #[cfg(feature = "tokio")] -pub async fn tokio_main(data: ExtensionData) { - use std::io::{ErrorKind, Write}; - use std::mem; - use std::pin::{Pin, pin}; - use std::rc::Rc; - - use async_once_cell::OnceCell; - use futures::StreamExt; - use futures::future::LocalBoxFuture; - use futures::lock::Mutex; - use futures::stream::FuturesUnordered; - use orchid_api_traits::{Decode, Encode}; - use orchid_base::msg::{recv_msg, send_msg}; - use tokio::io::{Stdout, stdin, stdout}; +pub async fn tokio_main(builder: ExtensionBuilder) { + use tokio::io::{stderr, stdin, stdout}; use tokio::task::{LocalSet, spawn_local}; - use tokio_util::compat::{Compat, TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt}; - - use crate::api; - use crate::entrypoint::extension_init; + use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt}; let local_set = LocalSet::new(); local_set.spawn_local(async { - let host_header = api::HostHeader::decode(Pin::new(&mut stdin().compat())).await; - let init = - Rc::new(extension_init(data, host_header, Rc::new(|fut| mem::drop(spawn_local(fut))))); - let mut buf = Vec::new(); - init.header.encode(Pin::new(&mut buf)).await; - std::io::stdout().write_all(&buf).unwrap(); - std::io::stdout().flush().unwrap(); - // These are concurrent processes that never exit, so if the FuturesUnordered - // produces any result the extension should exit - let mut io = FuturesUnordered::>::new(); - io.push(Box::pin(async { - loop { - match recv_msg(pin!(stdin().compat())).await { - Ok(msg) => init.send(&msg[..]).await, - Err(e) if e.kind() == ErrorKind::BrokenPipe => break, - Err(e) if e.kind() == ErrorKind::UnexpectedEof => break, - Err(e) => panic!("{e}"), - } - } - })); - io.push(Box::pin(async { - while let Some(msg) = init.recv().await { - static STDOUT: OnceCell>> = OnceCell::new(); - let stdout_lk = STDOUT.get_or_init(async { Mutex::new(stdout().compat_write()) }).await; - let mut stdout_g = stdout_lk.lock().await; - send_msg(pin!(&mut *stdout_g), &msg[..]).await.expect("Parent pipe broken"); - } - })); - io.next().await; + builder.build(ExtPort { + input: Box::pin(stdin().compat()), + output: Box::pin(stdout().compat_write()), + log: Box::pin(stderr().compat_write()), + spawn: Rc::new(|fut| { + spawn_local(fut); + }), + }); }); local_set.await; } diff --git a/orchid-extension/src/tree.rs b/orchid-extension/src/tree.rs index fcbab87..c9147e0 100644 --- a/orchid-extension/src/tree.rs +++ b/orchid-extension/src/tree.rs @@ -1,4 +1,6 @@ +use std::cell::RefCell; use std::num::NonZero; +use std::rc::Rc; use async_fn_stream::stream; use dyn_clone::{DynClone, clone_box}; @@ -6,17 +8,16 @@ use futures::future::{LocalBoxFuture, join_all}; use futures::{FutureExt, StreamExt}; use hashbrown::HashMap; use itertools::Itertools; -use orchid_base::interner::{Interner, Tok}; +use orchid_base::interner::{IStr, is}; use orchid_base::location::SrcRange; use orchid_base::name::Sym; use orchid_base::tree::{TokTree, Token, TokenVariant}; use substack::Substack; +use task_local::task_local; use trait_set::trait_set; use crate::api; -use crate::context::i; use crate::conv::ToExpr; -use crate::entrypoint::MemberRecord; use crate::expr::{BorrowedExprStore, Expr, ExprHandle}; use crate::func_atom::{ExprFunc, Fun}; use crate::gen_expr::{GExpr, sym_ref}; @@ -27,12 +28,7 @@ pub type GenTok = Token; impl TokenVariant for GExpr { type FromApiCtx<'a> = (); type ToApiCtx<'a> = (); - async fn from_api( - _: &api::Expression, - _: &mut Self::FromApiCtx<'_>, - _: SrcRange, - _: &Interner, - ) -> Self { + async fn from_api(_: &api::Expression, _: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self { panic!("Received new expression from host") } async fn into_api(self, _: &mut Self::ToApiCtx<'_>) -> api::Expression { self.serialize().await } @@ -40,12 +36,7 @@ impl TokenVariant for GExpr { impl TokenVariant for Expr { type FromApiCtx<'a> = &'a BorrowedExprStore; - async fn from_api( - api: &api::ExprTicket, - exprs: &mut Self::FromApiCtx<'_>, - _: SrcRange, - _: &Interner, - ) -> Self { + async fn from_api(api: &api::ExprTicket, exprs: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self { // SAFETY: receiving trees from sublexers implies borrowing Expr::from_handle(ExprHandle::borrowed(*api, exprs)) } @@ -84,9 +75,8 @@ pub fn root_mod(name: &str, mems: impl IntoIterator>) -> ( (name.to_string(), kind) } pub fn fun(public: bool, name: &str, xf: impl ExprFunc) -> Vec { - let fac = LazyMemberFactory::new(async move |sym| { - MemKind::Const(Fun::new(sym, xf).await.to_gen().await) - }); + let fac = + LazyMemberFactory::new(async move |sym| MemKind::Const(Fun::new(sym, xf).await.to_gen().await)); vec![GenMember { name: name.to_string(), kind: MemKind::Lazy(fac), public, comments: vec![] }] } pub fn prefix(path: &str, items: impl IntoIterator>) -> Vec { @@ -167,10 +157,10 @@ pub struct GenMember { pub comments: Vec, } impl GenMember { - pub async fn into_api(self, tia_cx: &mut impl TreeIntoApiCtx) -> api::Member { - let name = i().i::(&self.name).await; + pub(crate) async fn into_api(self, tia_cx: &mut impl TreeIntoApiCtx) -> api::Member { + let name = is(&self.name).await; let kind = self.kind.into_api(&mut tia_cx.push_path(name.clone())).await; - let comments = join_all(self.comments.iter().map(async |cmt| i().i(cmt).await.to_api())).await; + let comments = join_all(self.comments.iter().map(async |cmt| is(cmt).await.to_api())).await; api::Member { kind, name: name.to_api(), comments, exported: self.public } } } @@ -181,9 +171,9 @@ pub enum MemKind { Lazy(LazyMemberFactory), } impl MemKind { - pub async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MemberKind { + pub(crate) async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MemberKind { match self { - Self::Lazy(lazy) => api::MemberKind::Lazy(ctx.with_lazy(lazy)), + Self::Lazy(lazy) => api::MemberKind::Lazy(add_lazy(ctx, lazy)), Self::Const(c) => api::MemberKind::Const(c.serialize().await), Self::Mod { members } => api::MemberKind::Module(api::Module { members: stream(async |mut cx| { @@ -199,29 +189,58 @@ impl MemKind { } } -pub trait TreeIntoApiCtx { - fn with_lazy(&mut self, fac: LazyMemberFactory) -> api::TreeId; - fn push_path(&mut self, seg: Tok) -> impl TreeIntoApiCtx; +pub enum MemberRecord { + Gen(Vec, LazyMemberFactory), + Res, } -pub struct TreeIntoApiCtxImpl<'a, 'b> { - pub basepath: &'a [Tok], - pub path: Substack<'a, Tok>, - pub lazy_members: &'b mut HashMap, +#[derive(Clone, Default)] +pub(crate) struct LazyMemberStore(Rc>>); + +task_local! { + static LAZY_MEMBERS: LazyMemberStore; } -impl TreeIntoApiCtx for TreeIntoApiCtxImpl<'_, '_> { - fn push_path(&mut self, seg: Tok) -> impl TreeIntoApiCtx { - TreeIntoApiCtxImpl { - lazy_members: self.lazy_members, - 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_members.len() + 2) as u64).unwrap()); - let path = self.basepath.iter().cloned().chain(self.path.unreverse()).collect_vec(); - self.lazy_members.insert(id, MemberRecord::Gen(path, fac)); +pub fn with_lazy_member_store<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> { + Box::pin(LAZY_MEMBERS.scope(LazyMemberStore::default(), fut)) +} + +fn add_lazy(cx: &impl TreeIntoApiCtx, fac: LazyMemberFactory) -> api::TreeId { + LAZY_MEMBERS.with(|lazy_members| { + let mut g = lazy_members.0.borrow_mut(); + let id = api::TreeId(NonZero::new((g.len() + 2) as u64).unwrap()); + let path = cx.path().collect_vec(); + g.insert(id, MemberRecord::Gen(path, fac)); id + }) +} + +pub async fn get_lazy(id: api::TreeId) -> (Sym, MemKind) { + let (path, cb) = + LAZY_MEMBERS.with(|tbl| match tbl.0.borrow_mut().insert(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 path = Sym::new(path).await.unwrap(); + (path.clone(), cb.build(path).await) +} + +pub(crate) trait TreeIntoApiCtx { + fn push_path(&mut self, seg: IStr) -> impl TreeIntoApiCtx; + fn path(&self) -> impl Iterator; +} + +pub struct TreeIntoApiCtxImpl<'a> { + pub basepath: &'a [IStr], + pub path: Substack<'a, IStr>, +} + +impl TreeIntoApiCtx for TreeIntoApiCtxImpl<'_> { + fn push_path(&mut self, seg: IStr) -> impl TreeIntoApiCtx { + TreeIntoApiCtxImpl { basepath: self.basepath, path: self.path.push(seg) } + } + fn path(&self) -> impl Iterator { + self.basepath.iter().cloned().chain(self.path.unreverse()) } } diff --git a/orchid-host/src/atom.rs b/orchid-host/src/atom.rs index d6bf439..dc7434f 100644 --- a/orchid-host/src/atom.rs +++ b/orchid-host/src/atom.rs @@ -5,7 +5,7 @@ use async_once_cell::OnceCell; use derive_destructure::destructure; use orchid_base::format::{FmtCtx, FmtUnit, Format, take_first_fmt}; use orchid_base::location::Pos; -use orchid_base::reqnot::Requester; +use orchid_base::reqnot::ClientExt; use orchid_base::tree::AtomRepr; use crate::api; @@ -59,11 +59,11 @@ impl AtomHand { pub async fn call(self, arg: Expr) -> Expr { let owner_sys = self.0.owner.clone(); let ctx = owner_sys.ctx(); - let reqnot = owner_sys.reqnot(); + let client = owner_sys.client(); ctx.exprs.give_expr(arg.clone()); let ret = match Rc::try_unwrap(self.0) { - Ok(data) => reqnot.request(api::FinalCall(data.api(), arg.id())).await, - Err(hand) => reqnot.request(api::CallRef(hand.api_ref(), arg.id())).await, + Ok(data) => client.request(api::FinalCall(data.api(), arg.id())).await.unwrap(), + Err(hand) => client.request(api::CallRef(hand.api_ref(), arg.id())).await.unwrap(), }; let val = Expr::from_api(&ret, PathSetBuilder::new(), ctx.clone()).await; ctx.exprs.take_expr(arg.id()); @@ -74,19 +74,21 @@ impl AtomHand { #[must_use] pub fn ext(&self) -> &Extension { self.sys().ext() } pub async fn req(&self, key: api::TStrv, req: Vec) -> Option> { - self.0.owner.reqnot().request(api::Fwded(self.0.api_ref(), key, req)).await + self.0.owner.client().request(api::Fwded(self.0.api_ref(), key, req)).await.unwrap() } #[must_use] pub fn api_ref(&self) -> api::Atom { self.0.api_ref() } #[must_use] - pub async fn to_string(&self) -> String { take_first_fmt(self, &self.0.owner.ctx().i).await } + pub async fn to_string(&self) -> String { take_first_fmt(self).await } #[must_use] pub fn downgrade(&self) -> WeakAtomHand { WeakAtomHand(Rc::downgrade(&self.0)) } } impl Format for AtomHand { async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { (self.0.display.get_or_init(async { - FmtUnit::from_api(&self.0.owner.reqnot().request(api::AtomPrint(self.0.api_ref())).await) + FmtUnit::from_api( + &self.0.owner.client().request(api::AtomPrint(self.0.api_ref())).await.unwrap(), + ) })) .await .clone() diff --git a/orchid-host/src/ctx.rs b/orchid-host/src/ctx.rs index 50a13f4..6e7779c 100644 --- a/orchid-host/src/ctx.rs +++ b/orchid-host/src/ctx.rs @@ -3,19 +3,28 @@ use std::num::{NonZero, NonZeroU16}; use std::rc::{Rc, Weak}; use std::{fmt, ops}; +use futures::future::LocalBoxFuture; use futures_locks::RwLock; use hashbrown::HashMap; -use orchid_base::builtin::Spawner; -use orchid_base::interner::Interner; +use orchid_base::logging::Logger; use crate::api; use crate::expr_store::ExprStore; use crate::system::{System, WeakSystem}; use crate::tree::WeakRoot; +pub trait JoinHandle { + fn abort(&self); + fn join(self: Box) -> LocalBoxFuture<'static, ()>; +} + +pub trait Spawner { + fn spawn_obj(&self, fut: LocalBoxFuture<'static, ()>) -> Box; +} + pub struct CtxData { - pub i: Interner, - pub spawn: Spawner, + spawner: Rc, + pub msg_logs: Logger, pub systems: RwLock>, pub system_id: RefCell, pub exprs: ExprStore, @@ -37,16 +46,25 @@ impl WeakCtx { } impl Ctx { #[must_use] - pub fn new(spawn: Spawner) -> Self { + pub fn new(msg_logs: Logger, spawner: impl Spawner + 'static) -> Self { Self(Rc::new(CtxData { - spawn, - i: Interner::default(), + msg_logs, + spawner: Rc::new(spawner), systems: RwLock::default(), system_id: RefCell::new(NonZero::new(1).unwrap()), exprs: ExprStore::default(), root: RwLock::default(), })) } + /// Spawn a parallel future that you can join at any later time. + /// + /// Don't use this for async Drop, use [orchid_base::stash::stash] instead. + /// If you use this for an actor object, make sure to actually join the + /// handle. + #[must_use] + pub fn spawn(&self, fut: impl Future + 'static) -> Box { + self.spawner.spawn_obj(Box::pin(fut)) + } #[must_use] pub(crate) async fn system_inst(&self, id: api::SysId) -> Option { self.systems.read().await.get(&id).and_then(WeakSystem::upgrade) @@ -62,9 +80,6 @@ impl Ctx { } impl fmt::Debug for Ctx { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Ctx") - .field("i", &self.i) - .field("system_id", &self.system_id) - .finish_non_exhaustive() + f.debug_struct("Ctx").field("system_id", &self.system_id).finish_non_exhaustive() } } diff --git a/orchid-host/src/dealias.rs b/orchid-host/src/dealias.rs index a158041..fcd3368 100644 --- a/orchid-host/src/dealias.rs +++ b/orchid-host/src/dealias.rs @@ -1,7 +1,7 @@ use hashbrown::HashSet; use itertools::Itertools; -use orchid_base::error::{OrcErrv, OrcRes, Reporter, mk_errv}; -use orchid_base::interner::{Interner, Tok}; +use orchid_base::error::{OrcErrv, OrcRes, mk_errv}; +use orchid_base::interner::{IStr, is}; use orchid_base::location::Pos; use orchid_base::name::VName; @@ -16,17 +16,17 @@ pub enum AbsPathError { RootPath, } impl AbsPathError { - pub async fn err_obj(self, i: &Interner, pos: Pos, path: &str) -> OrcErrv { + pub async fn err_obj(self, pos: Pos, path: &str) -> OrcErrv { let (descr, msg) = match self { AbsPathError::RootPath => ( - i.i("Path ends on root module").await, + is("Path ends on root module").await, format!( "{path} is equal to the empty path. You cannot directly reference the root. \ Use one fewer 'super::' or add more segments to make it valid." ), ), AbsPathError::TooManySupers => ( - i.i("Too many 'super::' steps in path").await, + is("Too many 'super::' steps in path").await, format!("{path} is leading outside the root."), ), }; @@ -41,13 +41,9 @@ impl AbsPathError { /// /// if the relative path contains as many or more `super` segments than the /// length of the absolute path. -pub async fn absolute_path( - mut cwd: &[Tok], - mut rel: &[Tok], - i: &Interner, -) -> Result { - let i_self = i.i("self").await; - let i_super = i.i("super").await; +pub async fn absolute_path(mut cwd: &[IStr], mut rel: &[IStr]) -> Result { + let i_self = is("self").await; + let i_super = is("super").await; let mut relative = false; if let Some((_, tail)) = rel.split_first().filter(|(h, _)| **h == i_self) { rel = tail; @@ -63,19 +59,13 @@ pub async fn absolute_path( .map_err(|_| AbsPathError::RootPath) } -pub struct DealiasCtx<'a> { - pub i: &'a Interner, - pub rep: &'a Reporter, -} - pub async fn resolv_glob( - cwd: &[Tok], + cwd: &[IStr], root: &Mod, - abs_path: &[Tok], + abs_path: &[IStr], pos: Pos, - i: &Interner, ctx: &mut Mod::Ctx<'_>, -) -> OrcRes>> { +) -> OrcRes> { let coprefix_len = cwd.iter().zip(abs_path).take_while(|(a, b)| a == b).count(); let (co_prefix, diff_path) = abs_path.split_at(abs_path.len().min(coprefix_len + 1)); let fst_diff = @@ -89,7 +79,7 @@ pub async fn resolv_glob( ChildErrorKind::Missing => ("Invalid import path", format!("{path} not found")), ChildErrorKind::Private => ("Import inaccessible", format!("{path} is private")), }; - return Err(mk_errv(i.i(tk).await, msg, [pos])); + return Err(mk_errv(is(tk).await, msg, [pos])); }, }; Ok(target_module.children(coprefix_len < abs_path.len())) @@ -100,11 +90,11 @@ pub type ChildResult<'a, T> = Result<&'a T, ChildErrorKind>; pub trait Tree { type Ctx<'a>; #[must_use] - fn children(&self, public_only: bool) -> HashSet>; + fn children(&self, public_only: bool) -> HashSet; #[must_use] fn child( &self, - key: Tok, + key: IStr, public_only: bool, ctx: &mut Self::Ctx<'_>, ) -> impl Future>; @@ -135,7 +125,7 @@ pub struct ChildError { pub async fn walk<'a, T: Tree>( root: &'a T, public_only: bool, - path: impl IntoIterator>, + path: impl IntoIterator, ctx: &mut T::Ctx<'_>, ) -> Result<&'a T, ChildError> { let mut cur = root; diff --git a/orchid-host/src/execute.rs b/orchid-host/src/execute.rs index c965fe8..10974a2 100644 --- a/orchid-host/src/execute.rs +++ b/orchid-host/src/execute.rs @@ -4,11 +4,10 @@ use bound::Bound; use futures::FutureExt; use futures_locks::{RwLockWriteGuard, TryLockError}; use orchid_base::error::OrcErrv; -use orchid_base::format::{FmtCtxImpl, Format, take_first}; +use orchid_base::format::fmt; use orchid_base::location::Pos; -use orchid_base::logging::Logger; +use orchid_base::logging::logger; -use crate::ctx::Ctx; use crate::expr::{Expr, ExprKind, PathSet, Step}; use crate::tree::Root; @@ -30,21 +29,19 @@ pub enum ExecResult { } pub struct ExecCtx { - ctx: Ctx, gas: Option, stack: Vec, cur: ExprGuard, cur_pos: Pos, did_pop: bool, - logger: Logger, root: Root, } impl ExecCtx { #[must_use] - pub async fn new(ctx: Ctx, logger: Logger, root: Root, init: Expr) -> Self { + pub async fn new(root: Root, init: Expr) -> Self { let cur_pos = init.pos(); let cur = Bound::async_new(init, |init| init.kind().write()).await; - Self { ctx, gas: None, stack: vec![], cur, cur_pos, did_pop: false, logger, root } + Self { gas: None, stack: vec![], cur, cur_pos, did_pop: false, root } } #[must_use] pub fn remaining_gas(&self) -> u64 { self.gas.expect("queried remaining_gas but no gas was set") } @@ -89,8 +86,7 @@ impl ExecCtx { while self.use_gas(1) { let mut kind_swap = ExprKind::Missing; mem::swap(&mut kind_swap, &mut self.cur); - let unit = kind_swap.print(&FmtCtxImpl { i: &self.ctx.i }).await; - writeln!(self.logger, "Exxecute lvl{} {}", self.stack.len(), take_first(&unit, true)); + writeln!(logger(), "Exxecute lvl{} {}", self.stack.len(), fmt(&kind_swap).await); let (kind, op) = match kind_swap { ExprKind::Identity(target) => { let inner = self.unpack_ident(&target).await; diff --git a/orchid-host/src/expr.rs b/orchid-host/src/expr.rs index cfb1544..ce32d39 100644 --- a/orchid-host/src/expr.rs +++ b/orchid-host/src/expr.rs @@ -9,7 +9,6 @@ use futures_locks::RwLock; use itertools::Itertools; use orchid_base::error::OrcErrv; use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants}; -use orchid_base::interner::Interner; use orchid_base::location::{Pos, SrcRange}; use orchid_base::name::Sym; use orchid_base::tl_cache; @@ -56,13 +55,13 @@ impl Expr { } #[must_use] pub async fn from_api(api: &api::Expression, psb: PathSetBuilder<'_, u64>, ctx: Ctx) -> Self { - let pos = Pos::from_api(&api.location, &ctx.i).await; + let pos = Pos::from_api(&api.location).await; let kind = match &api.kind { api::ExpressionKind::Arg(n) => { assert!(psb.register_arg(n), "Arguments must be enclosed in a matching lambda"); ExprKind::Arg }, - api::ExpressionKind::Bottom(bot) => ExprKind::Bottom(OrcErrv::from_api(bot, &ctx.i).await), + api::ExpressionKind::Bottom(bot) => ExprKind::Bottom(OrcErrv::from_api(bot).await), api::ExpressionKind::Call(f, x) => { let (lpsb, rpsb) = psb.split(); ExprKind::Call( @@ -70,7 +69,7 @@ impl Expr { Expr::from_api(x, rpsb, ctx).boxed_local().await, ) }, - api::ExpressionKind::Const(name) => ExprKind::Const(Sym::from_api(*name, &ctx.i).await), + api::ExpressionKind::Const(name) => ExprKind::Const(Sym::from_api(*name).await), api::ExpressionKind::Lambda(x, body) => { let lbuilder = psb.lambda(x); let body = Expr::from_api(body, lbuilder.stack(), ctx).boxed_local().await; @@ -326,12 +325,7 @@ impl WeakExpr { impl TokenVariant for Expr { type FromApiCtx<'a> = ExprStore; - async fn from_api( - api: &api::ExprTicket, - ctx: &mut Self::FromApiCtx<'_>, - _: SrcRange, - _: &Interner, - ) -> Self { + async fn from_api(api: &api::ExprTicket, ctx: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self { ctx.get_expr(*api).expect("Invalid ticket") } type ToApiCtx<'a> = ExprStore; @@ -348,12 +342,7 @@ pub struct ExprWillPanic; impl TokenVariant for Expr { type FromApiCtx<'a> = Ctx; - async fn from_api( - api: &api::Expression, - ctx: &mut Self::FromApiCtx<'_>, - _: SrcRange, - _: &Interner, - ) -> Self { + async fn from_api(api: &api::Expression, ctx: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self { Self::from_api(api, PathSetBuilder::new(), ctx.clone()).await } type ToApiCtx<'a> = ExprWillPanic; diff --git a/orchid-host/src/extension.rs b/orchid-host/src/extension.rs index 24f9efa..2a4924d 100644 --- a/orchid-host/src/extension.rs +++ b/orchid-host/src/extension.rs @@ -2,7 +2,7 @@ use std::cell::RefCell; use std::future::Future; use std::io; use std::num::NonZeroU64; -use std::pin::pin; +use std::pin::Pin; use std::rc::{Rc, Weak}; use async_fn_stream::stream; @@ -10,28 +10,33 @@ use derive_destructure::destructure; use futures::channel::mpsc::{Sender, channel}; use futures::future::{join, join_all}; use futures::lock::Mutex; -use futures::{SinkExt, StreamExt, stream}; -use hashbrown::HashMap; +use futures::{AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt}; +use hashbrown::{HashMap, HashSet}; use itertools::Itertools; -use orchid_api_traits::Request; -use orchid_base::builtin::ExtInit; +use orchid_api_traits::{Decode, Encode, Request}; use orchid_base::clone; use orchid_base::format::{FmtCtxImpl, Format}; -use orchid_base::interner::Tok; +use orchid_base::interner::{IStr, IStrv, es, ev, is, iv}; use orchid_base::location::Pos; -use orchid_base::logging::Logger; +use orchid_base::logging::logger; use orchid_base::name::Sym; -use orchid_base::reqnot::{DynRequester, ReqNot, Requester as _}; +use orchid_base::reqnot::{Client, ClientExt, MsgReaderExt, ReqHandleExt, ReqReaderExt, io_comm}; +use orchid_base::stash::{stash, with_stash}; use orchid_base::tree::AtomRepr; use crate::api; use crate::atom::AtomHand; -use crate::ctx::Ctx; +use crate::ctx::{Ctx, JoinHandle}; use crate::dealias::{ChildError, ChildErrorKind, walk}; use crate::expr::{Expr, PathSetBuilder}; use crate::system::SystemCtor; use crate::tree::MemberKind; +pub struct ExtPort { + pub input: Pin>, + pub output: Pin>, +} + pub struct ReqPair(R, Sender); /// Data held about an Extension. This is refcounted within [Extension]. It's @@ -42,69 +47,47 @@ pub struct ReqPair(R, Sender); pub struct ExtensionData { name: String, ctx: Ctx, - reqnot: ReqNot, + join_ext: Option>, + client: Rc, systems: Vec, - logger: Logger, next_pars: RefCell, - exiting_snd: Sender<()>, lex_recur: Mutex>>>, + strings: RefCell>, + string_vecs: RefCell>, } impl Drop for ExtensionData { fn drop(&mut self) { - let reqnot = self.reqnot.clone(); - let mut exiting_snd = self.exiting_snd.clone(); - (self.ctx.spawn)(Box::pin(async move { - reqnot.notify(api::HostExtNotif::Exit).await; - - exiting_snd.send(()).await.unwrap() - })) + let client = self.client.clone(); + let join_ext = self.join_ext.take().expect("Only called once in Drop"); + stash(async move { + client.notify(api::HostExtNotif::Exit).await.unwrap(); + join_ext.join().await; + }) } } #[derive(Clone)] pub struct Extension(Rc); impl Extension { - pub fn new(init: ExtInit, logger: Logger, msg_logger: Logger, ctx: Ctx) -> io::Result { + pub async fn new(mut init: ExtPort, ctx: Ctx) -> io::Result { + api::HostHeader { log_strategy: logger().strat(), msg_logs: ctx.msg_logs.strat() } + .encode(init.input.as_mut()) + .await + .unwrap(); + init.input.flush().await.unwrap(); + + let header = api::ExtensionHeader::decode(init.output.as_mut()).await.unwrap(); Ok(Self(Rc::new_cyclic(|weak: &Weak| { - let init = Rc::new(init); - let (exiting_snd, exiting_rcv) = channel::<()>(0); - (ctx.spawn)({ - clone!(init, weak, ctx); - Box::pin(async move { - let rcv_stream = stream(async |mut cx| { - loop { - cx.emit(init.recv().await).await - } - }); - let mut event_stream = pin!(stream::select(exiting_rcv.map(|()| None), rcv_stream)); - while let Some(Some(msg)) = event_stream.next().await { - if let Some(reqnot) = weak.upgrade().map(|rc| rc.reqnot.clone()) { - let reqnot = reqnot.clone(); - (ctx.spawn)(Box::pin(async move { - reqnot.receive(&msg).await; - })) - } - } - }) - }); - ExtensionData { - name: init.name.clone(), - exiting_snd, - ctx: ctx.clone(), - systems: (init.systems.iter().cloned()) - .map(|decl| SystemCtor { decl, ext: WeakExtension(weak.clone()) }) - .collect(), - logger: logger.clone(), - next_pars: RefCell::new(NonZeroU64::new(1).unwrap()), - lex_recur: Mutex::default(), - reqnot: ReqNot::new( - msg_logger, - move |sfn, _| clone!(init; Box::pin(async move { init.send(sfn).await })), - clone!(weak; move |notif, _| { - clone!(weak; Box::pin(async move { + // context not needed because exit is extension-initiated + let (client, _, future) = io_comm( + Rc::new(Mutex::new(init.input)), + Mutex::new(init.output), + clone!(weak; async move |reader| { + with_stash(async { let this = Extension(weak.upgrade().unwrap()); + let notif = reader.read::().await.unwrap(); if !matches!(notif, api::ExtHostNotif::Log(_)) { - writeln!(this.reqnot().logger(), "Host received notif {notif:?}"); + writeln!(logger(), "Host received notif {notif:?}"); } match notif { api::ExtHostNotif::ExprNotif(api::ExprNotif::Acquire(acq)) => { @@ -115,153 +98,191 @@ impl Extension { if this.is_own_sys(rel.0).await { this.0.ctx.exprs.take_expr(rel.1); } else { - writeln!(this.reqnot().logger(), "Not our system {:?}", rel.0) + writeln!(this.0.ctx.msg_logs, "Not our system {:?}", rel.0) } }, - api::ExtHostNotif::Log(api::Log(str)) => this.logger().log(str), - } - }))}), - { - clone!(weak, ctx); - move |hand, req| { - clone!(weak, ctx); - Box::pin(async move { - let this = Self(weak.upgrade().unwrap()); - if !matches!(req, api::ExtHostReq::ExtAtomPrint(_)) { - writeln!(this.reqnot().logger(), "Host received request {req:?}"); + api::ExtHostNotif::Log(api::Log(str)) => logger().log(str), + api::ExtHostNotif::Sweeped(data) => { + for i in join_all(data.strings.into_iter().map(es)).await { + this.0.strings.borrow_mut().remove(&i); } - let i = this.ctx().i.clone(); - match req { - api::ExtHostReq::Ping(ping) => hand.handle(&ping, &()).await, - api::ExtHostReq::IntReq(intreq) => match intreq { - api::IntReq::InternStr(s) => hand.handle(&s, &i.i(&*s.0).await.to_api()).await, - api::IntReq::InternStrv(v) => { - let tokens = join_all(v.0.iter().map(|m| i.ex(*m))).await; - hand.handle(&v, &i.i(&tokens).await.to_api()).await - }, - api::IntReq::ExternStr(si) => - hand.handle(&si, &Tok::::from_api(si.0, &i).await.rc()).await, - api::IntReq::ExternStrv(vi) => { - let markerv = (i.ex(vi.0).await.iter()).map(|t| t.to_api()).collect_vec(); - hand.handle(&vi, &markerv).await - }, + for i in join_all(data.vecs.into_iter().map(ev)).await { + this.0.string_vecs.borrow_mut().remove(&i); + } + }, + } + Ok(()) + }).await + }), + { + clone!(weak, ctx); + async move |mut reader| { + with_stash(async { + let this = Self(weak.upgrade().unwrap()); + let req = reader.read_req::().await.unwrap(); + let handle = reader.finish().await; + if !matches!(req, api::ExtHostReq::ExtAtomPrint(_)) { + writeln!(logger(), "Host received request {req:?}"); + } + match req { + api::ExtHostReq::Ping(ping) => handle.reply(&ping, &()).await, + api::ExtHostReq::IntReq(intreq) => match intreq { + api::IntReq::InternStr(s) => { + let i = is(&s.0).await; + this.0.strings.borrow_mut().insert(i.clone()); + handle.reply(&s, &i.to_api()).await }, - api::ExtHostReq::Fwd(ref fw @ api::Fwd(ref atom, ref key, ref body)) => { - let sys = - ctx.system_inst(atom.owner).await.expect("owner of live atom dropped"); - let reply = - sys.reqnot().request(api::Fwded(fw.0.clone(), *key, body.clone())).await; - hand.handle(fw, &reply).await + api::IntReq::InternStrv(v) => { + let tokens = join_all(v.0.iter().map(|m| es(*m))).await; + this.0.strings.borrow_mut().extend(tokens.iter().cloned()); + let i = iv(&tokens).await; + this.0.string_vecs.borrow_mut().insert(i.clone()); + handle.reply(&v, &i.to_api()).await }, - api::ExtHostReq::SysFwd(ref fw @ api::SysFwd(id, ref body)) => { - let sys = ctx.system_inst(id).await.unwrap(); - hand.handle(fw, &sys.request(body.clone()).await).await + api::IntReq::ExternStr(si) => { + let i = es(si.0).await; + this.0.strings.borrow_mut().insert(i.clone()); + handle.reply(&si, &i.to_string()).await }, - api::ExtHostReq::SubLex(sl) => { - let (rep_in, mut rep_out) = channel(0); - { - let lex_g = this.0.lex_recur.lock().await; - let mut req_in = - lex_g.get(&sl.id).cloned().expect("Sublex for nonexistent lexid"); - req_in.send(ReqPair(sl.clone(), rep_in)).await.unwrap(); + api::IntReq::ExternStrv(vi) => { + let i = ev(vi.0).await; + this.0.strings.borrow_mut().extend(i.iter().cloned()); + this.0.string_vecs.borrow_mut().insert(i.clone()); + let markerv = i.iter().map(|t| t.to_api()).collect_vec(); + handle.reply(&vi, &markerv).await + }, + }, + api::ExtHostReq::Fwd(ref fw @ api::Fwd(ref atom, ref key, ref body)) => { + let sys = ctx.system_inst(atom.owner).await.expect("owner of live atom dropped"); + let client = sys.client(); + let reply = + client.request(api::Fwded(fw.0.clone(), *key, body.clone())).await.unwrap(); + handle.reply(fw, &reply).await + }, + api::ExtHostReq::SysFwd(ref fw @ api::SysFwd(id, ref body)) => { + let sys = ctx.system_inst(id).await.unwrap(); + handle.reply(fw, &sys.request(body.clone()).await).await + }, + api::ExtHostReq::SubLex(sl) => { + let (rep_in, mut rep_out) = channel(0); + { + let lex_g = this.0.lex_recur.lock().await; + let mut req_in = + lex_g.get(&sl.id).cloned().expect("Sublex for nonexistent lexid"); + req_in.send(ReqPair(sl.clone(), rep_in)).await.unwrap(); + } + handle.reply(&sl, &rep_out.next().await.unwrap()).await + }, + api::ExtHostReq::ExprReq(expr_req) => match expr_req { + api::ExprReq::Inspect(ins @ api::Inspect { target }) => { + let expr = ctx.exprs.get_expr(target).expect("Invalid ticket"); + handle + .reply(&ins, &api::Inspected { + refcount: expr.strong_count() as u32, + location: expr.pos().to_api(), + kind: expr.to_api().await, + }) + .await + }, + api::ExprReq::Create(ref cre @ api::Create(ref expr)) => { + let expr = Expr::from_api(expr, PathSetBuilder::new(), ctx.clone()).await; + let expr_id = expr.id(); + ctx.exprs.give_expr(expr); + handle.reply(cre, &expr_id).await + }, + }, + api::ExtHostReq::LsModule(ref ls @ api::LsModule(_sys, path)) => { + let reply: ::Response = 'reply: { + let path = ev(path).await; + let root = (ctx.root.read().await.upgrade()) + .expect("LSModule called when root isn't in context"); + let root_data = &*root.0.read().await; + let mut walk_ctx = (ctx.clone(), &root_data.consts); + let module = + match walk(&root_data.root, false, path.iter().cloned(), &mut walk_ctx).await + { + Ok(module) => module, + Err(ChildError { kind, .. }) => + break 'reply Err(match kind { + ChildErrorKind::Private => panic!("Access checking was disabled"), + ChildErrorKind::Constant => api::LsModuleError::IsConstant, + ChildErrorKind::Missing => api::LsModuleError::InvalidPath, + }), + }; + let mut members = std::collections::HashMap::new(); + for (k, v) in &module.members { + let kind = match v.kind(ctx.clone(), &root_data.consts).await { + MemberKind::Const => api::MemberInfoKind::Constant, + MemberKind::Module(_) => api::MemberInfoKind::Module, + }; + members.insert(k.to_api(), api::MemberInfo { public: v.public, kind }); } - hand.handle(&sl, &rep_out.next().await.unwrap()).await - }, - api::ExtHostReq::ExprReq(expr_req) => match expr_req { - api::ExprReq::Inspect(ins @ api::Inspect { target }) => { - let expr = ctx.exprs.get_expr(target).expect("Invalid ticket"); - hand - .handle(&ins, &api::Inspected { - refcount: expr.strong_count() as u32, - location: expr.pos().to_api(), - kind: expr.to_api().await, - }) - .await - }, - api::ExprReq::Create(ref cre @ api::Create(ref expr)) => { - let expr = Expr::from_api(expr, PathSetBuilder::new(), ctx.clone()).await; - let expr_id = expr.id(); - ctx.exprs.give_expr(expr); - hand.handle(cre, &expr_id).await - }, - }, - api::ExtHostReq::LsModule(ref ls @ api::LsModule(_sys, path)) => { - let reply: ::Response = 'reply: { - let path = i.ex(path).await; - let root = (ctx.root.read().await.upgrade()) - .expect("LSModule called when root isn't in context"); - let root_data = &*root.0.read().await; - let mut walk_ctx = (ctx.clone(), &root_data.consts); - let module = - match walk(&root_data.root, false, path.iter().cloned(), &mut walk_ctx) - .await - { - Ok(module) => module, - Err(ChildError { kind, .. }) => - break 'reply Err(match kind { - ChildErrorKind::Private => panic!("Access checking was disabled"), - ChildErrorKind::Constant => api::LsModuleError::IsConstant, - ChildErrorKind::Missing => api::LsModuleError::InvalidPath, - }), - }; - let mut members = std::collections::HashMap::new(); - for (k, v) in &module.members { - let kind = match v.kind(ctx.clone(), &root_data.consts).await { - MemberKind::Const => api::MemberInfoKind::Constant, - MemberKind::Module(_) => api::MemberInfoKind::Module, - }; - members.insert(k.to_api(), api::MemberInfo { public: v.public, kind }); - } - Ok(api::ModuleInfo { members }) - }; - hand.handle(ls, &reply).await - }, - api::ExtHostReq::ResolveNames(ref rn) => { - let api::ResolveNames { constid, names, sys } = rn; - let mut resolver = { - let systems = ctx.systems.read().await; - let weak_sys = systems.get(sys).expect("ResolveNames for invalid sys"); - let sys = weak_sys.upgrade().expect("ResolveNames after sys drop"); - sys.name_resolver(*constid).await - }; - let responses = stream(async |mut cx| { - for name in names { - cx.emit(match resolver(&ctx.i.ex(*name).await[..]).await { - Ok(abs) => Ok(abs.to_sym(&ctx.i).await.to_api()), - Err(e) => Err(e.to_api()), - }) - .await - } - }) - .collect() - .await; - hand.handle(rn, &responses).await - }, - api::ExtHostReq::ExtAtomPrint(ref eap @ api::ExtAtomPrint(ref atom)) => { - let atom = AtomHand::from_api(atom, Pos::None, &mut ctx.clone()).await; - let unit = atom.print(&FmtCtxImpl { i: &this.ctx().i }).await; - hand.handle(eap, &unit.to_api()).await - }, - } - }) - } - }, - ), + Ok(api::ModuleInfo { members }) + }; + handle.reply(ls, &reply).await + }, + api::ExtHostReq::ResolveNames(ref rn) => { + let api::ResolveNames { constid, names, sys } = rn; + let mut resolver = { + let systems = ctx.systems.read().await; + let weak_sys = systems.get(sys).expect("ResolveNames for invalid sys"); + let sys = weak_sys.upgrade().expect("ResolveNames after sys drop"); + sys.name_resolver(*constid).await + }; + let responses = stream(async |mut cx| { + for name in names { + cx.emit(match resolver(&ev(*name).await[..]).await { + Ok(abs) => Ok(abs.to_sym().await.to_api()), + Err(e) => Err(e.to_api()), + }) + .await + } + }) + .collect() + .await; + handle.reply(rn, &responses).await + }, + api::ExtHostReq::ExtAtomPrint(ref eap @ api::ExtAtomPrint(ref atom)) => { + let atom = AtomHand::from_api(atom, Pos::None, &mut ctx.clone()).await; + let unit = atom.print(&FmtCtxImpl::default()).await; + handle.reply(eap, &unit.to_api()).await + }, + } + }) + .await + } + }, + ); + + let join_ext = ctx.spawn(async { + future.await.unwrap(); + // extension exited successfully + }); + ExtensionData { + name: header.name.clone(), + ctx: ctx.clone(), + systems: (header.systems.iter().cloned()) + .map(|decl| SystemCtor { decl, ext: WeakExtension(weak.clone()) }) + .collect(), + join_ext: Some(join_ext), + next_pars: RefCell::new(NonZeroU64::new(1).unwrap()), + lex_recur: Mutex::default(), + client: Rc::new(client), + strings: RefCell::default(), + string_vecs: RefCell::default(), } }))) } pub fn name(&self) -> &String { &self.0.name } #[must_use] - pub fn reqnot(&self) -> &ReqNot { &self.0.reqnot } + pub fn client(&self) -> &dyn Client { &*self.0.client } #[must_use] pub fn ctx(&self) -> &Ctx { &self.0.ctx } - #[must_use] - pub fn logger(&self) -> &Logger { &self.0.logger } pub fn system_ctors(&self) -> impl Iterator { self.0.systems.iter() } #[must_use] pub async fn is_own_sys(&self, id: api::SysId) -> bool { let Some(sys) = self.ctx().system_inst(id).await else { - writeln!(self.logger(), "Invalid system ID {id:?}"); + writeln!(logger(), "Invalid system ID {id:?}"); return false; }; Rc::ptr_eq(&self.0, &sys.ext().0) @@ -274,7 +295,7 @@ impl Extension { } pub(crate) async fn lex_req>>( &self, - source: Tok, + source: IStr, src: Sym, pos: u32, sys: api::SysId, @@ -287,9 +308,10 @@ impl Extension { self.0.lex_recur.lock().await.insert(id, req_in); // lex_recur released let (ret, ()) = join( async { - let res = (self.reqnot()) + let res = (self.client()) .request(api::LexExpr { id, pos, sys, src: src.to_api(), text: source.to_api() }) - .await; + .await + .unwrap(); // collect sender to unblock recursion handler branch before returning self.0.lex_recur.lock().await.remove(&id); res @@ -306,10 +328,10 @@ impl Extension { } pub fn system_drop(&self, id: api::SysId) { let rc = self.clone(); - (self.ctx().spawn)(Box::pin(async move { - rc.reqnot().request(api::SystemDrop(id)).await; + let _ = self.ctx().spawn(with_stash(async move { + rc.client().request(api::SystemDrop(id)).await.unwrap(); rc.ctx().systems.write().await.remove(&id); - })) + })); } #[must_use] pub fn downgrade(&self) -> WeakExtension { WeakExtension(Rc::downgrade(&self.0)) } diff --git a/orchid-host/src/lex.rs b/orchid-host/src/lex.rs index 27b741e..15f7fca 100644 --- a/orchid-host/src/lex.rs +++ b/orchid-host/src/lex.rs @@ -1,10 +1,8 @@ -use std::rc::Rc; - use futures::FutureExt; use futures::lock::Mutex; use orchid_base::clone; use orchid_base::error::{OrcErrv, OrcRes, mk_errv}; -use orchid_base::interner::Tok; +use orchid_base::interner::{IStr, is}; use orchid_base::location::SrcRange; use orchid_base::name::Sym; use orchid_base::parse::{name_char, name_start, op_char, unrep_space}; @@ -20,7 +18,7 @@ use crate::system::System; pub struct LexCtx<'a> { pub systems: &'a [System], - pub source: &'a Tok, + pub source: &'a IStr, pub path: &'a Sym, pub tail: &'a str, pub sub_trees: &'a mut Vec, @@ -60,7 +58,7 @@ impl<'a> LexCtx<'a> { } #[must_use] pub async fn des_subtree(&mut self, tree: &api::TokenTree, exprs: ExprStore) -> ParsTokTree { - ParsTokTree::from_api(tree, &mut { exprs }, &mut self.ctx.clone(), self.path, &self.ctx.i).await + ParsTokTree::from_api(tree, &mut { exprs }, &mut self.ctx.clone(), self.path).await } #[must_use] pub fn strip_char(&mut self, tgt: char) -> bool { @@ -98,21 +96,21 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes { let name = &ctx.tail[..ctx.tail.len() - tail.len() - "::".len()]; ctx.set_tail(tail); let body = lex_once(ctx).boxed_local().await?; - ParsTok::NS(ctx.ctx.i.i(name).await, Box::new(body)) + ParsTok::NS(is(name).await, Box::new(body)) } else if ctx.strip_prefix("--[") { let Some((cmt, tail)) = ctx.tail.split_once("]--") else { return Err(mk_errv( - ctx.ctx.i.i("Unterminated block comment").await, + is("Unterminated block comment").await, "This block comment has no ending ]--", [SrcRange::new(start..start + 3, ctx.path)], )); }; ctx.set_tail(tail); - ParsTok::Comment(Rc::new(cmt.to_string())) + ParsTok::Comment(is(cmt).await) } else if let Some(tail) = ctx.tail.strip_prefix("--").filter(|t| !t.starts_with(op_char)) { let end = tail.find(['\n', '\r']).map_or(tail.len(), |n| n - 1); ctx.push_pos(end as u32); - ParsTok::Comment(Rc::new(tail[2..end].to_string())) + ParsTok::Comment(is(&tail[2..end]).await) } else if let Some(tail) = ctx.tail.strip_prefix('\\').filter(|t| t.starts_with(name_start)) { // fanciness like \$placeh in templates is resolved in the macro engine. ctx.set_tail(tail); @@ -125,7 +123,7 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes { while !ctx.strip_char(*rp) { if ctx.tail.is_empty() { return Err(mk_errv( - ctx.ctx.i.i("unclosed paren").await, + is("unclosed paren").await, format!("this {lp} has no matching {rp}"), [SrcRange::new(start..start + 1, ctx.path)], )); @@ -162,10 +160,7 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes { }) .await; match lx { - Err(e) => - return Err( - errors.into_iter().fold(OrcErrv::from_api(&e, &ctx.ctx.i).await, |a, b| a + b), - ), + Err(e) => return Err(errors.into_iter().fold(OrcErrv::from_api(&e).await, |a, b| a + b)), Ok(Some(lexed)) => { ctx.set_pos(lexed.pos); let lexed_tree = ctx.des_subtree(&lexed.expr, temp_store).await; @@ -185,12 +180,12 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes { } } if ctx.tail.starts_with(name_start) { - ParsTok::Name(ctx.ctx.i.i(ctx.get_start_matches(name_char)).await) + ParsTok::Name(is(ctx.get_start_matches(name_char)).await) } else if ctx.tail.starts_with(op_char) { - ParsTok::Name(ctx.ctx.i.i(ctx.get_start_matches(op_char)).await) + ParsTok::Name(is(ctx.get_start_matches(op_char)).await) } else { return Err(mk_errv( - ctx.ctx.i.i("Unrecognized character").await, + is("Unrecognized character").await, "The following syntax is meaningless.", [SrcRange::new(start..start + 1, ctx.path)], )); @@ -199,12 +194,7 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes { Ok(ParsTokTree { tok, sr: SrcRange::new(start..ctx.get_pos(), ctx.path) }) } -pub async fn lex( - text: Tok, - path: Sym, - systems: &[System], - ctx: &Ctx, -) -> OrcRes> { +pub async fn lex(text: IStr, path: Sym, systems: &[System], ctx: &Ctx) -> OrcRes> { let mut sub_trees = Vec::new(); let mut ctx = LexCtx { source: &text, sub_trees: &mut sub_trees, tail: &text[..], systems, path: &path, ctx }; diff --git a/orchid-host/src/parse.rs b/orchid-host/src/parse.rs index 87a8bdb..b90908c 100644 --- a/orchid-host/src/parse.rs +++ b/orchid-host/src/parse.rs @@ -1,12 +1,11 @@ use futures::FutureExt; use itertools::Itertools; -use orchid_base::error::{OrcRes, Reporter, mk_errv}; +use orchid_base::error::{OrcRes, mk_errv, report}; use orchid_base::format::fmt; -use orchid_base::interner::{Interner, Tok}; +use orchid_base::interner::{IStr, is}; use orchid_base::name::Sym; use orchid_base::parse::{ - Comment, Import, ParseCtx, Parsed, Snippet, expect_end, line_items, parse_multiname, - try_pop_no_fluff, + Comment, Import, Parsed, Snippet, expect_end, line_items, parse_multiname, try_pop_no_fluff, }; use orchid_base::tree::{Paren, TokTree, Token}; use substack::Substack; @@ -22,12 +21,6 @@ pub struct HostParseCtxImpl<'a> { pub ctx: Ctx, pub src: Sym, pub systems: &'a [System], - pub rep: &'a Reporter, -} - -impl ParseCtx for HostParseCtxImpl<'_> { - fn rep(&self) -> &Reporter { self.rep } - fn i(&self) -> &Interner { &self.ctx.i } } impl HostParseCtx for HostParseCtxImpl<'_> { @@ -36,7 +29,7 @@ impl HostParseCtx for HostParseCtxImpl<'_> { fn src_path(&self) -> Sym { self.src.clone() } } -pub trait HostParseCtx: ParseCtx { +pub trait HostParseCtx { #[must_use] fn ctx(&self) -> &Ctx; #[must_use] @@ -47,14 +40,14 @@ pub trait HostParseCtx: ParseCtx { pub async fn parse_items( ctx: &impl HostParseCtx, - path: Substack<'_, Tok>, + path: Substack<'_, IStr>, items: ParsSnippet<'_>, ) -> OrcRes> { - let lines = line_items(ctx, items).await; + let lines = line_items(items).await; let mut line_ok = Vec::new(); for Parsed { output: comments, tail } in lines { match parse_item(ctx, path.clone(), comments, tail).boxed_local().await { - Err(e) => ctx.rep().report(e), + Err(e) => report(e), Ok(l) => line_ok.extend(l), } } @@ -63,23 +56,23 @@ pub async fn parse_items( pub async fn parse_item( ctx: &impl HostParseCtx, - path: Substack<'_, Tok>, + path: Substack<'_, IStr>, comments: Vec, item: ParsSnippet<'_>, ) -> OrcRes> { match item.pop_front() { Some((TokTree { tok: Token::Name(n), .. }, postdisc)) => match n { - n if *n == ctx.i().i("export").await => match try_pop_no_fluff(ctx, postdisc).await? { + n if *n == is("export").await => match try_pop_no_fluff(postdisc).await? { Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } => parse_exportable_item(ctx, path, comments, true, n.clone(), tail).await, Parsed { output, tail: _ } => Err(mk_errv( - ctx.i().i("Malformed export").await, + is("Malformed export").await, "`export` can either prefix other lines or list names inside ( )", [output.sr()], )), }, - n if *n == ctx.i().i("import").await => { - let imports = parse_import(ctx, postdisc).await?; + n if *n == is("import").await => { + let imports = parse_import(postdisc).await?; Ok(Vec::from_iter(imports.into_iter().map(|t| Item { comments: comments.clone(), sr: t.sr.clone(), @@ -88,33 +81,29 @@ pub async fn parse_item( }, n => parse_exportable_item(ctx, path, comments, false, n.clone(), postdisc).await, }, - Some(_) => Err(mk_errv( - ctx.i().i("Expected a line type").await, - "All lines must begin with a keyword", - [item.sr()], - )), + Some(_) => + Err(mk_errv(is("Expected a line type").await, "All lines must begin with a keyword", [ + item.sr() + ])), None => unreachable!("These lines are filtered and aggregated in earlier stages"), } } -pub async fn parse_import<'a>( - ctx: &impl HostParseCtx, - tail: ParsSnippet<'a>, -) -> OrcRes> { - let Parsed { output: imports, tail } = parse_multiname(ctx, tail).await?; - expect_end(ctx, tail).await?; +pub async fn parse_import<'a>(tail: ParsSnippet<'a>) -> OrcRes> { + let Parsed { output: imports, tail } = parse_multiname(tail).await?; + expect_end(tail).await?; Ok(imports) } pub async fn parse_exportable_item<'a>( ctx: &impl HostParseCtx, - path: Substack<'_, Tok>, + path: Substack<'_, IStr>, comments: Vec, exported: bool, - discr: Tok, + discr: IStr, tail: ParsSnippet<'a>, ) -> OrcRes> { - let kind = if discr == ctx.i().i("mod").await { + let kind = if discr == is("mod").await { let (name, body) = parse_module(ctx, path, tail).await?; ItemKind::Member(ParsedMember { name, exported, kind: ParsedMemberKind::Mod(body) }) } else if let Some(parser) = ctx.systems().find_map(|s| s.get_parser(discr.clone())) { @@ -127,7 +116,7 @@ pub async fn parse_exportable_item<'a>( } else { let ext_lines = ctx.systems().flat_map(System::line_types).join(", "); return Err(mk_errv( - ctx.i().i("Unrecognized line type").await, + is("Unrecognized line type").await, format!("Line types are: mod, {ext_lines}"), [tail.prev().sr()], )); @@ -137,25 +126,25 @@ pub async fn parse_exportable_item<'a>( pub async fn parse_module<'a>( ctx: &impl HostParseCtx, - path: Substack<'_, Tok>, + path: Substack<'_, IStr>, tail: ParsSnippet<'a>, -) -> OrcRes<(Tok, ParsedModule)> { - let (name, tail) = match try_pop_no_fluff(ctx, tail).await? { +) -> OrcRes<(IStr, ParsedModule)> { + let (name, tail) = match try_pop_no_fluff(tail).await? { Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } => (n.clone(), tail), Parsed { output, .. } => { return Err(mk_errv( - ctx.i().i("Missing module name").await, - format!("A name was expected, {} was found", fmt(output, ctx.i()).await), + is("Missing module name").await, + format!("A name was expected, {} was found", fmt(output).await), [output.sr()], )); }, }; - let Parsed { output, tail: surplus } = try_pop_no_fluff(ctx, tail).await?; - expect_end(ctx, surplus).await?; + let Parsed { output, tail: surplus } = try_pop_no_fluff(tail).await?; + expect_end(surplus).await?; let Some(body) = output.as_s(Paren::Round) else { return Err(mk_errv( - ctx.i().i("Expected module body").await, - format!("A ( block ) was expected, {} was found", fmt(output, ctx.i()).await), + is("Expected module body").await, + format!("A ( block ) was expected, {} was found", fmt(output).await), [output.sr()], )); }; diff --git a/orchid-host/src/parsed.rs b/orchid-host/src/parsed.rs index 62bc9c4..c66a1ee 100644 --- a/orchid-host/src/parsed.rs +++ b/orchid-host/src/parsed.rs @@ -6,7 +6,7 @@ use futures::future::{LocalBoxFuture, join_all}; use hashbrown::HashSet; use itertools::Itertools; use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants}; -use orchid_base::interner::Tok; +use orchid_base::interner::{IStr, IStrv}; use orchid_base::location::SrcRange; use orchid_base::parse::{Comment, Import}; use orchid_base::tl_cache; @@ -57,10 +57,10 @@ impl Format for Item { ItemKind::Member(mem) => match &mem.kind { ParsedMemberKind::Const(_, sys) => tl_cache!(Rc: Rc::new(Variants::default().bounded("const {0} via {1}"))) - .units([mem.name.rc().into(), sys.print(c).await]), + .units([mem.name.to_string().into(), sys.print(c).await]), ParsedMemberKind::Mod(module) => tl_cache!(Rc: Rc::new(Variants::default().bounded("module {0} {{\n\t{1}\n}}"))) - .units([mem.name.rc().into(), module.print(c).boxed_local().await]), + .units([mem.name.to_string().into(), module.print(c).boxed_local().await]), }, }; tl_cache!(Rc: Rc::new(Variants::default().bounded("{0}\n{1}"))) @@ -69,14 +69,14 @@ impl Format for Item { } pub struct ParsedMember { - pub name: Tok, + pub name: IStr, pub exported: bool, pub kind: ParsedMemberKind, } impl ParsedMember { #[must_use] - pub fn name(&self) -> Tok { self.name.clone() } - pub fn new(exported: bool, name: Tok, kind: impl Into) -> Self { + pub fn name(&self) -> IStr { self.name.clone() } + pub fn new(exported: bool, name: IStr, kind: impl Into) -> Self { Self { exported, name, kind: kind.into() } } } @@ -89,17 +89,14 @@ impl Debug for ParsedMember { } } -pub(crate) type ParsedExprCallback = - Rc Fn(&'a [Tok]) -> LocalBoxFuture<'a, Expr>>; +pub(crate) type ParsedExprCallback = Rc Fn(&'a [IStr]) -> LocalBoxFuture<'a, Expr>>; pub struct ParsedExpr { pub(crate) debug: String, pub(crate) callback: ParsedExprCallback, } impl ParsedExpr { - pub async fn run(self, imported_names: &[Tok]) -> Expr { - (self.callback)(imported_names).await - } + pub async fn run(self, imported_names: &[IStr]) -> Expr { (self.callback)(imported_names).await } } impl fmt::Debug for ParsedExpr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.debug) } @@ -115,7 +112,7 @@ impl From for ParsedMemberKind { } #[derive(Debug, Default)] pub struct ParsedModule { - pub exports: Vec>, + pub exports: Vec, pub items: Vec, pub use_prelude: bool, } @@ -141,7 +138,7 @@ impl ParsedModule { (self.items.iter()) .filter_map(|it| if let ItemKind::Import(i) = &it.kind { Some(i) } else { None }) } - pub fn default_item(self, name: Tok, sr: SrcRange) -> Item { + pub fn default_item(self, name: IStr, sr: SrcRange) -> Item { let mem = ParsedMember { exported: true, name, kind: ParsedMemberKind::Mod(self) }; Item { comments: vec![], sr, kind: ItemKind::Member(mem) } } @@ -150,7 +147,7 @@ impl Tree for ParsedModule { type Ctx<'a> = (); async fn child( &self, - key: Tok, + key: IStr, public_only: bool, (): &mut Self::Ctx<'_>, ) -> ChildResult<'_, Self> { @@ -168,7 +165,7 @@ impl Tree for ParsedModule { } ChildResult::Err(ChildErrorKind::Missing) } - fn children(&self, public_only: bool) -> HashSet> { + fn children(&self, public_only: bool) -> HashSet { let mut public: HashSet<_> = self.exports.iter().cloned().collect(); if !public_only { public.extend( @@ -197,11 +194,11 @@ impl Format for ParsedModule { /// point to a module and rule_loc selects a macro rule within that module #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct ConstPath { - steps: Tok>>, + steps: IStrv, } impl ConstPath { #[must_use] - pub fn to_const(steps: Tok>>) -> Self { Self { steps } } + pub fn to_const(steps: IStrv) -> Self { Self { steps } } } pub async fn tt_to_api(exprs: &mut ExprStore, subtree: ParsTokTree) -> api::TokenTree { diff --git a/orchid-host/src/subprocess.rs b/orchid-host/src/subprocess.rs index 109c56d..298550f 100644 --- a/orchid-host/src/subprocess.rs +++ b/orchid-host/src/subprocess.rs @@ -1,102 +1,32 @@ -use std::cell::RefCell; -use std::io::{self, Write}; -use std::pin::Pin; +use std::io; -use async_process::{self, Child, ChildStdin, ChildStdout}; -use futures::future::LocalBoxFuture; +use async_process; use futures::io::BufReader; -use futures::lock::Mutex; -use futures::{self, AsyncBufReadExt, AsyncWriteExt}; -use orchid_api_traits::{Decode, Encode}; -use orchid_base::builtin::{ExtInit, ExtPort}; -use orchid_base::logging::Logger; -use orchid_base::msg::{recv_msg, send_msg}; +use futures::{self, AsyncBufReadExt}; +use orchid_base::logging::logger; -use crate::api; use crate::ctx::Ctx; +use crate::extension::ExtPort; -pub async fn ext_command( - cmd: std::process::Command, - logger: Logger, - msg_logs: Logger, - ctx: Ctx, -) -> io::Result { +pub async fn ext_command(cmd: std::process::Command, ctx: Ctx) -> io::Result { let mut child = async_process::Command::from(cmd) .stdin(async_process::Stdio::piped()) .stdout(async_process::Stdio::piped()) .stderr(async_process::Stdio::piped()) .spawn()?; - let mut stdin = child.stdin.take().unwrap(); - api::HostHeader { log_strategy: logger.strat(), msg_logs: msg_logs.strat() } - .encode(Pin::new(&mut stdin)) - .await; - let mut stdout = child.stdout.take().unwrap(); - let header = api::ExtensionHeader::decode(Pin::new(&mut stdout)).await; + let stdin = child.stdin.take().unwrap(); + let stdout = child.stdout.take().unwrap(); let mut child_stderr = child.stderr.take().unwrap(); - (ctx.spawn)(Box::pin(async move { + let _ = ctx.spawn(Box::pin(async move { + let _ = child; let mut reader = BufReader::new(&mut child_stderr); loop { let mut buf = String::new(); if 0 == reader.read_line(&mut buf).await.unwrap() { break; } - logger.log(buf.strip_suffix('\n').expect("Readline implies this")); + logger().log(buf.strip_suffix('\n').expect("Readline implies this")); } })); - Ok(ExtInit { - port: Box::new(Subprocess { - name: header.name.clone(), - child: RefCell::new(Some(child)), - stdin: Some(Mutex::new(Box::pin(stdin))), - stdout: Mutex::new(Box::pin(stdout)), - ctx, - }), - header, - }) -} - -pub struct Subprocess { - name: String, - child: RefCell>, - stdin: Option>>>, - stdout: Mutex>>, - ctx: Ctx, -} -impl Drop for Subprocess { - fn drop(&mut self) { - let mut child = self.child.borrow_mut().take().unwrap(); - let name = self.name.clone(); - if std::thread::panicking() { - eprintln!("Killing extension {name}"); - // we don't really care to handle errors here - let _: Result<_, _> = std::io::stderr().flush(); - let _: Result<_, _> = child.kill(); - return; - } - let stdin = self.stdin.take().unwrap(); - (self.ctx.spawn)(Box::pin(async move { - stdin.lock().await.close().await.unwrap(); - let status = (child.status().await) - .unwrap_or_else(|e| panic!("{e}, extension {name} exited with error")); - assert!(status.success(), "Extension {name} exited with error {status}"); - })) - } -} -impl ExtPort for Subprocess { - fn send<'a>(&'a self, msg: &'a [u8]) -> LocalBoxFuture<'a, ()> { - Box::pin(async { - send_msg(Pin::new(&mut *self.stdin.as_ref().unwrap().lock().await), msg).await.unwrap() - }) - } - fn recv(&self) -> LocalBoxFuture<'_, Option>> { - Box::pin(async { - std::io::Write::flush(&mut std::io::stderr()).unwrap(); - match recv_msg(self.stdout.lock().await.as_mut()).await { - Ok(msg) => Some(msg), - Err(e) if e.kind() == io::ErrorKind::BrokenPipe => None, - Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => None, - Err(e) => panic!("Failed to read from stdout: {}, {e}", e.kind()), - } - }) - } + Ok(ExtPort { input: Box::pin(stdin), output: Box::pin(stdout) }) } diff --git a/orchid-host/src/sys_parser.rs b/orchid-host/src/sys_parser.rs index 804fc29..84e52bb 100644 --- a/orchid-host/src/sys_parser.rs +++ b/orchid-host/src/sys_parser.rs @@ -2,11 +2,11 @@ use futures::FutureExt; use futures::future::join_all; use itertools::Itertools; use orchid_base::error::{OrcErrv, OrcRes}; -use orchid_base::interner::{Interner, Tok}; +use orchid_base::interner::{IStr, es}; use orchid_base::location::SrcRange; use orchid_base::name::Sym; use orchid_base::parse::Comment; -use orchid_base::reqnot::Requester; +use orchid_base::reqnot::ClientExt; use orchid_base::tree::ttv_from_api; use substack::Substack; @@ -22,7 +22,7 @@ pub struct Parser { pub(crate) system: System, pub(crate) idx: u16, } -type ModPath<'a> = Substack<'a, Tok>; +type ModPath<'a> = Substack<'a, IStr>; impl Parser { pub async fn parse( @@ -39,7 +39,7 @@ impl Parser { let line = join_all((line.into_iter()).map(|t| async { tt_to_api(&mut temp_store.clone(), t).await })) .await; - let mod_path = ctx.src_path().suffix(path.unreverse(), self.system.i()).await; + let mod_path = ctx.src_path().suffix(path.unreverse()).await; let comments = comments.iter().map(Comment::to_api).collect_vec(); let req = api::ParseLine { idx: self.idx, @@ -50,17 +50,16 @@ impl Parser { comments, line, }; - match self.system.reqnot().request(req).await { + match self.system.client().request(req).await.unwrap() { Ok(parsed_v) => conv(parsed_v, path, callback, &mut ConvCtx { - i: self.system.i(), mod_path: &mod_path, ext_exprs: &mut temp_store, src_path: &src_path, sys: &self.system, }) .await, - Err(e) => Err(OrcErrv::from_api(&e, &self.system.ctx().i).await), + Err(e) => Err(OrcErrv::from_api(&e).await), } } } @@ -69,13 +68,12 @@ struct ConvCtx<'a> { sys: &'a System, mod_path: &'a Sym, src_path: &'a Sym, - i: &'a Interner, ext_exprs: &'a mut ExprStore, } async fn conv( parsed_v: Vec, - module: Substack<'_, Tok>, - callback: &'_ mut impl AsyncFnMut(Substack<'_, Tok>, Vec) -> OrcRes>, + module: Substack<'_, IStr>, + callback: &'_ mut impl AsyncFnMut(Substack<'_, IStr>, Vec) -> OrcRes>, ctx: &mut ConvCtx<'_>, ) -> OrcRes> { let mut items = Vec::new(); @@ -85,12 +83,12 @@ async fn conv( (name, exported, kind), api::ParsedLineKind::Recursive(rec) => { let tokens = - ttv_from_api(rec, ctx.ext_exprs, &mut ctx.sys.ctx().clone(), ctx.src_path, ctx.i).await; + ttv_from_api(rec, ctx.ext_exprs, &mut ctx.sys.ctx().clone(), ctx.src_path).await; items.extend(callback(module.clone(), tokens).await?); continue; }, }; - let name = ctx.i.ex(name).await; + let name = es(name).await; let mem_path = module.push(name.clone()); let mkind = match kind { api::ParsedMemberKind::Module { lines, use_prelude } => { @@ -98,16 +96,16 @@ async fn conv( ParsedMemberKind::Mod(ParsedModule::new(use_prelude, items)) }, api::ParsedMemberKind::Constant(cid) => { - ctx.sys.0.const_paths.insert(cid, ctx.mod_path.suffix(mem_path.unreverse(), ctx.i).await); + ctx.sys.0.const_paths.insert(cid, ctx.mod_path.suffix(mem_path.unreverse()).await); ParsedMemberKind::Const(cid, ctx.sys.clone()) }, }; items.push(Item { comments: join_all( - parsed.comments.iter().map(|c| Comment::from_api(c, ctx.src_path.clone(), ctx.i)), + parsed.comments.iter().map(|c| Comment::from_api(c, ctx.src_path.clone())), ) .await, - sr: SrcRange::from_api(&parsed.source_range, ctx.i).await, + sr: SrcRange::from_api(&parsed.source_range).await, kind: ItemKind::Member(ParsedMember { name, exported, kind: mkind }), }) } diff --git a/orchid-host/src/system.rs b/orchid-host/src/system.rs index 6dad18f..2d3fab6 100644 --- a/orchid-host/src/system.rs +++ b/orchid-host/src/system.rs @@ -12,10 +12,11 @@ use memo_map::MemoMap; use orchid_base::char_filter::char_filter_match; use orchid_base::error::{OrcRes, mk_errv_floating}; use orchid_base::format::{FmtCtx, FmtUnit, Format}; -use orchid_base::interner::{Interner, Tok}; +use orchid_base::interner::{IStr, es, is}; use orchid_base::iter_utils::IteratorPrint; +use orchid_base::logging::logger; use orchid_base::name::{NameLike, Sym, VName, VPath}; -use orchid_base::reqnot::{ReqNot, Requester}; +use orchid_base::reqnot::{Client, ClientExt}; use ordered_float::NotNan; use substack::{Stackframe, Substack}; @@ -35,7 +36,7 @@ pub(crate) struct SystemInstData { decl_id: api::SysDeclId, lex_filter: api::CharFilter, id: api::SysId, - line_types: Vec>, + line_types: Vec, prelude: Vec, owned_atoms: RwLock>, pub(crate) const_paths: MemoMap, @@ -68,8 +69,6 @@ impl System { #[must_use] pub fn ctx(&self) -> &Ctx { &self.0.ctx } #[must_use] - pub fn i(&self) -> &Interner { &self.0.ctx.i } - #[must_use] pub fn deps(&self) -> &[System] { &self.0.deps } #[must_use] pub fn ctor(&self) -> SystemCtor { @@ -77,22 +76,26 @@ impl System { .expect("Ctor was used to create ext") } #[must_use] - pub(crate) fn reqnot(&self) -> &ReqNot { self.0.ext.reqnot() } + pub(crate) fn client(&self) -> &dyn Client { self.0.ext.client() } #[must_use] pub async fn get_tree(&self, id: api::TreeId) -> api::MemberKind { - self.reqnot().request(api::GetMember(self.0.id, id)).await + self.client().request(api::GetMember(self.0.id, id)).await.unwrap() } #[must_use] pub fn has_lexer(&self) -> bool { !self.0.lex_filter.0.is_empty() } #[must_use] - pub fn can_lex(&self, c: char) -> bool { char_filter_match(&self.0.lex_filter, c) } + pub fn can_lex(&self, c: char) -> bool { + let ret = char_filter_match(&self.0.lex_filter, c); + writeln!(logger(), "{} can lex {c}: {}", self.ctor().name(), ret); + ret + } #[must_use] pub fn prelude(&self) -> Vec { self.0.prelude.clone() } /// Have this system lex a part of the source. It is assumed that /// [Self::can_lex] was called and returned true. pub async fn lex>>( &self, - source: Tok, + source: IStr, src: Sym, pos: u32, r: impl FnMut(u32) -> F, @@ -100,16 +103,16 @@ impl System { self.0.ext.lex_req(source, src, pos, self.id(), r).await } #[must_use] - pub fn get_parser(&self, ltyp: Tok) -> Option { + pub fn get_parser(&self, ltyp: IStr) -> Option { (self.0.line_types.iter().enumerate()) .find(|(_, txt)| *txt == <yp) .map(|(idx, _)| Parser { idx: idx as u16, system: self.clone() }) } - pub fn line_types(&self) -> impl Iterator> + '_ { self.0.line_types.iter() } + pub fn line_types(&self) -> impl Iterator + '_ { self.0.line_types.iter() } #[must_use] pub async fn request(&self, req: Vec) -> Vec { - self.reqnot().request(api::SysFwded(self.id(), req)).await + self.client().request(api::SysFwded(self.id(), req)).await.unwrap() } pub(crate) async fn new_atom(&self, data: Vec, id: api::AtomId) -> AtomHand { let mut owned_g = self.0.owned_atoms.write().await; @@ -124,10 +127,10 @@ impl System { } pub(crate) fn drop_atom(&self, dropped_atom_id: api::AtomId) { let this = self.0.clone(); - (self.0.ctx.spawn)(Box::pin(async move { - this.ext.reqnot().request(api::AtomDrop(this.id, dropped_atom_id)).await; + let _ = self.0.ctx.spawn(Box::pin(async move { + this.ext.client().request(api::AtomDrop(this.id, dropped_atom_id)).await.unwrap(); this.owned_atoms.write().await.remove(&dropped_atom_id); - })) + })); } #[must_use] pub fn downgrade(&self) -> WeakSystem { @@ -137,7 +140,7 @@ impl System { pub(crate) async fn name_resolver( &self, orig: api::ParsedConstId, - ) -> impl AsyncFnMut(&[Tok]) -> OrcRes + use<> { + ) -> impl AsyncFnMut(&[IStr]) -> OrcRes + use<> { let root = self.0.ctx.root.read().await.upgrade().expect("find_names when root not in context"); let orig = self.0.const_paths.get(&orig).expect("origin for find_names invalid").clone(); let ctx = self.0.ctx.clone(); @@ -155,7 +158,7 @@ impl System { Some(Ok(dest)) => return Ok(dest.target.to_vname().suffix(tail.iter().cloned())), Some(Err(dests)) => return Err(mk_errv_floating( - ctx.i.i("Ambiguous name").await, + is("Ambiguous name").await, format!( "{selector} could refer to {}", dests.iter().map(|ri| &ri.target).display("or") @@ -170,7 +173,7 @@ impl System { return Ok(VPath::new(cwd.iter().cloned()).name_with_suffix(selector.clone())); } Err(mk_errv_floating( - ctx.i.i("Invalid name").await, + is("Invalid name").await, format!("{selector} doesn't refer to a module"), )) } @@ -203,8 +206,7 @@ impl SystemCtor { #[must_use] pub fn name(&self) -> &str { &self.decl.name } pub async fn name_tok(&self) -> Sym { - (Sym::parse(&self.decl.name, &self.ext.upgrade().expect("ext dropped early").ctx().i).await) - .expect("System cannot have empty name") + (Sym::parse(&self.decl.name).await).expect("System cannot have empty name") } #[must_use] pub fn priority(&self) -> NotNan { self.decl.priority } @@ -220,17 +222,17 @@ impl SystemCtor { debug_assert_eq!(depends.len(), self.decl.depends.len(), "Wrong number of deps provided"); let ext = self.ext.upgrade().expect("SystemCtor should be freed before Extension"); let id = ext.ctx().next_sys_id(); - let sys_inst = ext.reqnot().request(api::NewSystem { depends, id, system: self.decl.id }).await; + let sys_inst = + ext.client().request(api::NewSystem { depends, id, system: self.decl.id }).await.unwrap(); let data = System(Rc::new(SystemInstData { deps, decl_id: self.decl.id, ext: ext.clone(), ctx: ext.ctx().clone(), lex_filter: sys_inst.lex_filter, - line_types: join_all(sys_inst.line_types.iter().map(|m| Tok::from_api(*m, &ext.ctx().i))) - .await, + line_types: join_all(sys_inst.line_types.iter().map(|m| es(*m))).await, id, - prelude: join_all(sys_inst.prelude.iter().map(|tok| Sym::from_api(*tok, &ext.ctx().i))).await, + prelude: join_all(sys_inst.prelude.iter().map(|tok| Sym::from_api(*tok))).await, owned_atoms: RwLock::new(HashMap::new()), const_paths: MemoMap::new(), })); diff --git a/orchid-host/src/tree.rs b/orchid-host/src/tree.rs index 4f098cc..e9e77b6 100644 --- a/orchid-host/src/tree.rs +++ b/orchid-host/src/tree.rs @@ -13,11 +13,11 @@ use hashbrown::hash_map::Entry; use itertools::Itertools; use memo_map::MemoMap; use orchid_base::clone; -use orchid_base::error::{OrcRes, Reporter, mk_errv}; -use orchid_base::interner::Tok; +use orchid_base::error::{OrcRes, mk_errv, report}; +use orchid_base::interner::{IStr, IStrv, es, is, iv}; use orchid_base::location::{CodeGenInfo, Pos}; use orchid_base::name::{NameLike, Sym, VPath}; -use orchid_base::reqnot::Requester; +use orchid_base::reqnot::ClientExt; use crate::api; use crate::ctx::Ctx; @@ -45,7 +45,7 @@ impl Root { #[must_use] pub async fn from_api(api: api::Module, sys: &System) -> Self { let consts = MemoMap::new(); - let mut tfac = TreeFromApiCtx { consts: &consts, path: sys.i().i(&[][..]).await, sys }; + let mut tfac = TreeFromApiCtx { consts: &consts, path: iv(&[][..]).await, sys }; let root = Module::from_api(api, &mut tfac).await; Root(Rc::new(RwLock::new(RootData { root, consts, ctx: sys.ctx().clone() }))) } @@ -60,7 +60,7 @@ impl Root { Ok(Self(Rc::new(RwLock::new(RootData { root, consts, ctx: this.ctx.clone() })))) } #[must_use] - pub async fn add_parsed(&self, parsed: &ParsedModule, pars_prefix: Sym, rep: &Reporter) -> Self { + pub async fn add_parsed(&self, parsed: &ParsedModule, pars_prefix: Sym) -> Self { let mut ref_this = self.0.write().await; let this = &mut *ref_this; let mut deferred_consts = HashMap::new(); @@ -72,7 +72,6 @@ impl Root { pars_prefix: pars_prefix.clone(), root: &this.root, ctx: &this.ctx, - rep, }; let mut module = Module::from_parsed(parsed, pars_prefix.clone(), &mut tfpctx).await; for step in pars_prefix.iter().rev() { @@ -89,7 +88,7 @@ impl Root { *this.ctx.root.write().await = new.downgrade(); for (path, (sys_id, pc_id)) in deferred_consts { let sys = this.ctx.system_inst(sys_id).await.expect("System dropped since parsing"); - let api_expr = sys.reqnot().request(api::FetchParsedConst(sys.id(), pc_id)).await; + let api_expr = sys.client().request(api::FetchParsedConst(sys.id(), pc_id)).await.unwrap(); let expr = Expr::from_api(&api_expr, PathSetBuilder::new(), this.ctx.clone()).await; new.0.write().await.consts.insert(path, expr); } @@ -110,7 +109,7 @@ impl Root { } match module { Ok(_) => Err(mk_errv( - ctx.i.i("module used as constant").await, + is("module used as constant").await, format!("{name} is a module, not a constant"), [pos], )), @@ -118,7 +117,7 @@ impl Root { ChildErrorKind::Private => panic!("public_only is false"), ChildErrorKind::Constant => panic!("Tree refers to constant not in table"), ChildErrorKind::Missing => Err(mk_errv( - ctx.i.i("Constant does not exist").await, + is("Constant does not exist").await, format!("{name} does not refer to a constant"), [pos], )), @@ -144,12 +143,12 @@ impl Default for WeakRoot { pub struct TreeFromApiCtx<'a> { pub sys: &'a System, pub consts: &'a MemoMap, - pub path: Tok>>, + pub path: IStrv, } impl<'a> TreeFromApiCtx<'a> { #[must_use] - pub async fn push<'c>(&'c self, name: Tok) -> TreeFromApiCtx<'c> { - let path = self.sys.ctx().i.i(&self.path.iter().cloned().chain([name]).collect_vec()).await; + pub async fn push<'c>(&'c self, name: IStr) -> TreeFromApiCtx<'c> { + let path = iv(&self.path.iter().cloned().chain([name]).collect_vec()).await; TreeFromApiCtx { path, consts: self.consts, sys: self.sys } } } @@ -162,17 +161,17 @@ pub struct ResolvedImport { #[derive(Clone, Default)] pub struct Module { - pub imports: HashMap, Result>>, - pub members: HashMap, Rc>, + pub imports: HashMap>>, + pub members: HashMap>, } impl Module { #[must_use] pub async fn from_api(api: api::Module, ctx: &mut TreeFromApiCtx<'_>) -> Self { let mut members = HashMap::new(); for mem in api.members { - let mem_name = ctx.sys.i().ex(mem.name).await; + let mem_name = es(mem.name).await; let vname = VPath::new(ctx.path.iter().cloned()).name_with_suffix(mem_name.clone()); - let name = vname.to_sym(ctx.sys.i()).await; + let name = vname.to_sym().await; let (lazy, kind) = match mem.kind { api::MemberKind::Lazy(id) => (Some(LazyMemberHandle { id, sys: ctx.sys.id(), path: name.clone() }), None), @@ -205,23 +204,23 @@ impl Module { let mut glob_imports_by_name = HashMap::<_, Vec<_>>::new(); for import in parsed.get_imports().into_iter().filter(|i| i.name.is_none()) { let pos = import.sr.pos(); - match absolute_path(&path, &import.path, &ctx.ctx.i).await { - Err(e) => ctx.rep.report(e.err_obj(&ctx.ctx.i, pos, &import.path.to_string()).await), + match absolute_path(&path, &import.path).await { + Err(e) => report(e.err_obj(pos, &import.path.to_string()).await), Ok(abs_path) => { let names_res = match abs_path.strip_prefix(&ctx.pars_prefix[..]) { None => { let mut tree_ctx = (ctx.ctx.clone(), ctx.consts); - resolv_glob(&path, ctx.root, &abs_path, pos, &ctx.ctx.i, &mut tree_ctx).await + resolv_glob(&path, ctx.root, &abs_path, pos, &mut tree_ctx).await }, Some(sub_tgt) => { let sub_path = (path.strip_prefix(&ctx.pars_prefix[..])) .expect("from_parsed called with path outside pars_prefix"); - resolv_glob(sub_path, ctx.pars_root, sub_tgt, pos, &ctx.ctx.i, &mut ()).await + resolv_glob(sub_path, ctx.pars_root, sub_tgt, pos, &mut ()).await }, }; - let abs_path = abs_path.to_sym(&ctx.ctx.i).await; + let abs_path = abs_path.to_sym().await; match names_res { - Err(e) => ctx.rep.report(e), + Err(e) => report(e), Ok(names) => for name in names { match glob_imports_by_name.entry(name) { @@ -244,30 +243,28 @@ impl Module { prelude_item.last_seg(), Ok(ResolvedImport { target: prelude_item, - pos: CodeGenInfo::new_details(sys.ctor().name_tok().await, "In prelude", &ctx.ctx.i) - .await - .pos(), + pos: CodeGenInfo::new_details(sys.ctor().name_tok().await, "In prelude").await.pos(), }), ); } } } - let conflicting_imports_msg = ctx.ctx.i.i("Conflicting imports").await; + let conflicting_imports_msg = is("Conflicting imports").await; for (key, values) in imports_by_name { if values.len() == 1 { let import = values.into_iter().next().unwrap(); let sr = import.sr.clone(); - let abs_path_res = absolute_path(&path, &import.clone().mspath(), &ctx.ctx.i).await; + let abs_path_res = absolute_path(&path, &import.clone().mspath()).await; match abs_path_res { - Err(e) => ctx.rep.report(e.err_obj(&ctx.ctx.i, sr.pos(), &import.to_string()).await), + Err(e) => report(e.err_obj(sr.pos(), &import.to_string()).await), Ok(abs_path) => { - let target = abs_path.to_sym(&ctx.ctx.i).await; + let target = abs_path.to_sym().await; imports.insert(key, Ok(ResolvedImport { target, pos: sr.pos() })); }, } } else { for item in values { - ctx.rep.report(mk_errv( + report(mk_errv( conflicting_imports_msg.clone(), format!("{key} is imported multiple times from different modules"), [item.sr.pos()], @@ -277,12 +274,11 @@ impl Module { } for (key, values) in glob_imports_by_name { if !imports.contains_key(&key) { - let i = &ctx.ctx.i; let values = stream::iter(values) .then(|(n, sr)| { clone!(key; async move { ResolvedImport { - target: n.to_vname().suffix([key.clone()]).to_sym(i).await, + target: n.to_vname().suffix([key.clone()]).to_sym().await, pos: sr.pos(), } }) @@ -292,12 +288,12 @@ impl Module { imports.insert(key, if values.len() == 1 { Ok(values[0].clone()) } else { Err(values) }); } } - let self_referential_msg = ctx.ctx.i.i("Self-referential import").await; + let self_referential_msg = is("Self-referential import").await; for (key, value) in imports.iter() { let Ok(import) = value else { continue }; if import.target.strip_prefix(&path[..]).is_some_and(|t| t.starts_with(slice::from_ref(key))) { - ctx.rep.report(mk_errv( + report(mk_errv( self_referential_msg.clone(), format!("import {} points to itself or a path within itself", &import.target), [import.pos.clone()], @@ -308,7 +304,7 @@ impl Module { for item in &parsed.items { match &item.kind { ItemKind::Member(mem) => { - let path = path.to_vname().suffix([mem.name.clone()]).to_sym(&ctx.ctx.i).await; + let path = path.to_vname().suffix([mem.name.clone()]).to_sym().await; let kind = OnceCell::from(MemberKind::from_parsed(&mem.kind, path.clone(), ctx).await); members.insert( mem.name.clone(), @@ -385,7 +381,6 @@ pub struct FromParsedCtx<'a> { pars_prefix: Sym, pars_root: &'a ParsedModule, root: &'a Module, - rep: &'a Reporter, ctx: &'a Ctx, consts: &'a MemoMap, deferred_consts: &'a mut HashMap, @@ -395,7 +390,7 @@ impl Tree for Module { type Ctx<'a> = (Ctx, &'a MemoMap); async fn child( &self, - key: Tok, + key: IStr, public_only: bool, (ctx, consts): &mut Self::Ctx<'_>, ) -> crate::dealias::ChildResult<'_, Self> { @@ -410,7 +405,7 @@ impl Tree for Module { MemberKind::Const => Err(ChildErrorKind::Constant), } } - fn children(&self, public_only: bool) -> hashbrown::HashSet> { + fn children(&self, public_only: bool) -> hashbrown::HashSet { self.members.iter().filter(|(_, v)| !public_only || v.public).map(|(k, _)| k.clone()).collect() } } diff --git a/orchid-std/src/macros/instantiate_tpl.rs b/orchid-std/src/macros/instantiate_tpl.rs index ae45981..5554c27 100644 --- a/orchid-std/src/macros/instantiate_tpl.rs +++ b/orchid-std/src/macros/instantiate_tpl.rs @@ -4,7 +4,6 @@ use never::Never; use orchid_base::format::fmt; use orchid_extension::atom::{Atomic, TAtom}; use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own}; -use orchid_extension::context::i; use orchid_extension::conv::ToExpr; use orchid_extension::coroutine_exec::exec; use orchid_extension::expr::Expr; @@ -38,7 +37,7 @@ impl OwnedAtom for InstantiateTplCall { async fn call(mut self, arg: Expr) -> GExpr { exec(async move |mut h| { match h.exec::>(arg.clone()).await { - Err(_) => panic!("Expected a macro param, found {}", fmt(&arg, &i()).await), + Err(_) => panic!("Expected a macro param, found {}", fmt(&arg).await), Ok(t) => self.argv.push(own(&t).await), }; if self.argv.len() < self.argc { diff --git a/orchid-std/src/macros/let_line.rs b/orchid-std/src/macros/let_line.rs index ce323c6..1894aa9 100644 --- a/orchid-std/src/macros/let_line.rs +++ b/orchid-std/src/macros/let_line.rs @@ -3,15 +3,13 @@ use std::pin::pin; use futures::{FutureExt, StreamExt, stream}; use hashbrown::HashMap; use itertools::Itertools; -use orchid_base::error::{OrcRes, Reporter}; +use orchid_base::error::{OrcRes, report, with_reporter}; +use orchid_base::interner::is; use orchid_base::name::Sym; -use orchid_base::parse::{ - Comment, ParseCtx, Parsed, Snippet, expect_tok, token_errv, try_pop_no_fluff, -}; +use orchid_base::parse::{Comment, Parsed, Snippet, expect_tok, token_errv, try_pop_no_fluff}; use orchid_base::sym; use orchid_base::tree::Paren; use orchid_extension::atom::TAtom; -use orchid_extension::context::i; use orchid_extension::conv::TryFromExpr; use orchid_extension::gen_expr::{atom, call, sym_ref}; use orchid_extension::parser::{ConstCtx, PSnippet, PTok, PTokTree, ParsCtx, ParsedLine, Parser}; @@ -24,40 +22,36 @@ pub struct LetLine; impl Parser for LetLine { const LINE_HEAD: &'static str = "let"; async fn parse<'a>( - ctx: ParsCtx<'a>, + _: ParsCtx<'a>, exported: bool, comments: Vec, line: PSnippet<'a>, ) -> OrcRes> { let sr = line.sr(); - let Parsed { output: name_tok, tail } = try_pop_no_fluff(&ctx, line).await?; + let Parsed { output: name_tok, tail } = try_pop_no_fluff(line).await?; let Some(name) = name_tok.as_name() else { - let err = token_errv(&ctx, name_tok, "Constant must have a name", |t| { + let err = token_errv(name_tok, "Constant must have a name", |t| { format!("Expected a name but found {t}") }); return Err(err.await); }; - let Parsed { tail, .. } = expect_tok(&ctx, tail, ctx.i().i("=").await).await?; - let aliased = parse_tokv(tail, &ctx).await; + let Parsed { tail, .. } = expect_tok(tail, is("=").await).await?; + let aliased = parse_tokv(tail).await; Ok(vec![ParsedLine::cnst(&line.sr(), &comments, exported, name, async move |ctx| { - let rep = Reporter::new(); let macro_input = - MacTok::S(Paren::Round, dealias_mac_v(&aliased, &ctx, &rep).await).at(sr.pos()); - if let Some(e) = rep.errv() { - return Err(e); - } - Ok(call(sym_ref(sym!(macros::resolve; i())), [atom(macro_input)])) + MacTok::S(Paren::Round, with_reporter(dealias_mac_v(&aliased, &ctx)).await?).at(sr.pos()); + Ok(call(sym_ref(sym!(macros::resolve)), [atom(macro_input)])) })]) } } -pub async fn dealias_mac_v(aliased: &MacTreeSeq, ctx: &ConstCtx, rep: &Reporter) -> MacTreeSeq { +pub async fn dealias_mac_v(aliased: &MacTreeSeq, ctx: &ConstCtx) -> MacTreeSeq { let keys = aliased.glossary().iter().cloned().collect_vec(); let mut names: HashMap<_, _> = HashMap::new(); let mut stream = pin!(ctx.names(&keys).zip(stream::iter(&keys))); while let Some((canonical, local)) = stream.next().await { match canonical { - Err(e) => rep.report(e), + Err(e) => report(e), Ok(name) => { names.insert(local.clone(), name); }, @@ -69,16 +63,16 @@ pub async fn dealias_mac_v(aliased: &MacTreeSeq, ctx: &ConstCtx, rep: &Reporter) }) } -pub async fn parse_tokv(line: PSnippet<'_>, ctx: &impl ParseCtx) -> MacTreeSeq { +pub async fn parse_tokv(line: PSnippet<'_>) -> MacTreeSeq { if let Some((idx, arg)) = line.iter().enumerate().find_map(|(i, x)| Some((i, x.as_lambda()?))) { let (head, lambda) = line.split_at(idx as u32); let (_, body) = lambda.pop_front().unwrap(); - let body = parse_tokv(body, ctx).boxed_local().await; - let mut all = parse_tokv_no_lambdas(&head, ctx).await; - match parse_tok(arg, ctx).await { + let body = parse_tokv(body).boxed_local().await; + let mut all = parse_tokv_no_lambdas(&head).await; + match parse_tok(arg).await { Some(arg) => all.push(MacTok::Lambda(arg, body).at(lambda.sr().pos())), - None => ctx.rep().report( - token_errv(ctx, arg, "Lambda argument fluff", |arg| { + None => report( + token_errv(arg, "Lambda argument fluff", |arg| { format!("Lambda arguments must be a valid token, found meaningless fragment {arg}") }) .await, @@ -86,29 +80,29 @@ pub async fn parse_tokv(line: PSnippet<'_>, ctx: &impl ParseCtx) -> MacTreeSeq { }; MacTreeSeq::new(all) } else { - MacTreeSeq::new(parse_tokv_no_lambdas(&line, ctx).await) + MacTreeSeq::new(parse_tokv_no_lambdas(&line).await) } } -async fn parse_tokv_no_lambdas(line: &[PTokTree], ctx: &impl ParseCtx) -> Vec { - stream::iter(line).filter_map(|tt| parse_tok(tt, ctx)).collect::>().await +async fn parse_tokv_no_lambdas(line: &[PTokTree]) -> Vec { + stream::iter(line).filter_map(parse_tok).collect::>().await } -pub async fn parse_tok(tree: &PTokTree, ctx: &impl ParseCtx) -> Option { +pub async fn parse_tok(tree: &PTokTree) -> Option { let tok = match &tree.tok { PTok::Bottom(errv) => MacTok::Bottom(errv.clone()), PTok::BR | PTok::Comment(_) => return None, - PTok::Name(n) => MacTok::Name(Sym::new([n.clone()], ctx.i()).await.unwrap()), + PTok::Name(n) => MacTok::Name(Sym::new([n.clone()]).await.unwrap()), PTok::NS(..) => match tree.as_multiname() { - Ok(mn) => MacTok::Name(mn.to_sym(ctx.i()).await), + Ok(mn) => MacTok::Name(mn.to_sym().await), Err(nested) => { - ctx.rep().report( - token_errv(ctx, tree, ":: can only be followed by a name in an expression", |tok| { + report( + token_errv(tree, ":: can only be followed by a name in an expression", |tok| { format!("Expected name, found {tok}") }) .await, ); - return parse_tok(nested, ctx).boxed_local().await; + return parse_tok(nested).boxed_local().await; }, }, PTok::Handle(expr) => match TAtom::::try_from_expr(expr.clone()).await { @@ -117,8 +111,7 @@ pub async fn parse_tok(tree: &PTokTree, ctx: &impl ParseCtx) -> Option }, PTok::NewExpr(never) => match *never {}, PTok::LambdaHead(_) => panic!("Lambda-head handled in the sequence parser"), - PTok::S(p, body) => - MacTok::S(*p, parse_tokv(Snippet::new(tree, body), ctx).boxed_local().await), + PTok::S(p, body) => MacTok::S(*p, parse_tokv(Snippet::new(tree, body)).boxed_local().await), }; Some(tok.at(tree.sr().pos())) } diff --git a/orchid-std/src/macros/macro_lib.rs b/orchid-std/src/macros/macro_lib.rs index ba4e59f..3c7a984 100644 --- a/orchid-std/src/macros/macro_lib.rs +++ b/orchid-std/src/macros/macro_lib.rs @@ -1,7 +1,6 @@ use orchid_base::sym; use orchid_extension::atom::TAtom; use orchid_extension::atom_owned::own; -use orchid_extension::context::i; use orchid_extension::conv::ToExpr; use orchid_extension::coroutine_exec::exec; use orchid_extension::gen_expr::{call, sym_ref}; @@ -20,12 +19,12 @@ pub async fn gen_macro_lib() -> Vec { build_macro(None, ["..", "_"]).finish(), build_macro(Some(1), ["+"]) .rule(mactreev!("...$" lhs 0 macros::common::+ "...$" rhs 1), [async |[lhs, rhs]| { - call(sym_ref(sym!(std::number::add; i())), [resolve(lhs).await, resolve(rhs).await]) + call(sym_ref(sym!(std::number::add)), [resolve(lhs).await, resolve(rhs).await]) }]) .finish(), build_macro(Some(2), ["*"]) .rule(mactreev!("...$" lhs 0 macros::common::* "...$" rhs 1), [async |[lhs, rhs]| { - call(sym_ref(sym!(std::number::mul; i())), [resolve(lhs).await, resolve(rhs).await]) + call(sym_ref(sym!(std::number::mul)), [resolve(lhs).await, resolve(rhs).await]) }]) .finish(), build_macro(None, ["comma_list", ","]) diff --git a/orchid-std/src/macros/macro_line.rs b/orchid-std/src/macros/macro_line.rs index a38842b..bda4eff 100644 --- a/orchid-std/src/macros/macro_line.rs +++ b/orchid-std/src/macros/macro_line.rs @@ -4,15 +4,14 @@ use async_fn_stream::stream; use async_once_cell::OnceCell; use futures::StreamExt; use itertools::Itertools; -use orchid_base::error::{OrcRes, Reporter, mk_errv}; +use orchid_base::error::{OrcRes, mk_errv, report, with_reporter}; +use orchid_base::interner::is; use orchid_base::parse::{ - Comment, ParseCtx, Parsed, Snippet, expect_end, expect_tok, line_items, token_errv, - try_pop_no_fluff, + Comment, Parsed, Snippet, expect_end, expect_tok, line_items, token_errv, try_pop_no_fluff, }; use orchid_base::tree::{Paren, Token}; use orchid_base::{clone, sym}; use orchid_extension::atom::TAtom; -use orchid_extension::context::i; use orchid_extension::conv::{ToExpr, TryFromExpr}; use orchid_extension::gen_expr::{call, sym_ref}; use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser}; @@ -35,22 +34,22 @@ impl Parser for MacroLine { ) -> OrcRes> { if exported { return Err(mk_errv( - ctx.i().i("macros are always exported").await, + is("macros are always exported").await, "The export keyword is forbidden here to avoid confusion\n\ because macros are exported by default", [line.sr()], )); } let module = ctx.module(); - let Parsed { output: prio_or_body, tail } = try_pop_no_fluff(&ctx, line).await?; + let Parsed { output: prio_or_body, tail } = try_pop_no_fluff(line).await?; let bad_first_item_err = || { - token_errv(&ctx, prio_or_body, "Expected priority or block", |s| { + token_errv(prio_or_body, "Expected priority or block", |s| { format!("Expected a priority number or a () block, found {s}") }) }; let (prio, body) = match &prio_or_body.tok { Token::S(Paren::Round, body) => { - expect_end(&ctx, tail).await?; + expect_end(tail).await?; (None, body) }, Token::Handle(expr) => match TAtom::::try_from_expr(expr.clone()).await { @@ -58,33 +57,32 @@ impl Parser for MacroLine { return Err(e + bad_first_item_err().await); }, Ok(prio) => { - let Parsed { output: body, tail } = try_pop_no_fluff(&ctx, tail).await?; + let Parsed { output: body, tail } = try_pop_no_fluff(tail).await?; let Token::S(Paren::Round, block) = &body.tok else { return Err( - token_errv(&ctx, prio_or_body, "Expected () block", |s| { + token_errv(prio_or_body, "Expected () block", |s| { format!("Expected a () block, found {s}") }) .await, ); }; - expect_end(&ctx, tail).await?; + expect_end(tail).await?; (Some(prio), block) }, }, _ => return Err(bad_first_item_err().await), }; - let lines = line_items(&ctx, Snippet::new(prio_or_body, body)).await; + let lines = line_items(Snippet::new(prio_or_body, body)).await; let Some((kw_line, rule_lines)) = lines.split_first() else { return Ok(Vec::new()) }; let mut keywords = Vec::new(); - let Parsed { tail: kw_tail, .. } = - expect_tok(&ctx, kw_line.tail, ctx.i().i("keywords").await).await?; + let Parsed { tail: kw_tail, .. } = expect_tok(kw_line.tail, is("keywords").await).await?; for kw_tok in kw_tail.iter().filter(|kw| !kw.is_fluff()) { match kw_tok.as_name() { Some(kw) => { keywords.push((kw, kw_tok.sr())); }, - None => ctx.rep().report( - token_errv(&ctx, kw_tok, "invalid macro keywords list", |tok| { + None => report( + token_errv(kw_tok, "invalid macro keywords list", |tok| { format!("The keywords list must be a sequence of names; received {tok}") }) .await, @@ -93,7 +91,7 @@ impl Parser for MacroLine { } let Some((macro_name, _)) = keywords.first().cloned() else { return Err(mk_errv( - ctx.i().i("macro with no keywords").await, + is("macro with no keywords").await, "Macros must define at least one macro of their own.", [kw_line.tail.sr()], )); @@ -102,18 +100,18 @@ impl Parser for MacroLine { let mut lines = Vec::new(); for (idx, line) in rule_lines.iter().enumerate().map(|(n, v)| (n as u32, v)) { let sr = line.tail.sr(); - let name = ctx.i().i(&format!("rule::{}::{}", macro_name, idx)).await; - let Parsed { tail, .. } = expect_tok(&ctx, line.tail, ctx.i().i("rule").await).await?; - let arrow_token = ctx.i().i("=>").await; + let name = is(&format!("rule::{}::{}", macro_name, idx)).await; + let Parsed { tail, .. } = expect_tok(line.tail, is("rule").await).await?; + let arrow_token = is("=>").await; let Some((pattern, body)) = tail.split_once(|tok| tok.is_kw(arrow_token.clone())) else { - ctx.rep().report(mk_errv( - ctx.i().i("Missing => in rule").await, + report(mk_errv( + is("Missing => in rule").await, "Rule lines are of the form `rule ...pattern => ...body`", [line.tail.sr()], )); continue; }; - let pattern = parse_tokv(pattern, &ctx).await; + let pattern = parse_tokv(pattern).await; let mut placeholders = Vec::new(); pattern.map(&mut false, &mut |tok| { if let MacTok::Ph(ph) = tok.tok() { @@ -121,9 +119,9 @@ impl Parser for MacroLine { } None }); - let mut body_mactree = parse_tokv(body, &ctx).await; + let mut body_mactree = parse_tokv(body).await; for (ph, ph_pos) in placeholders.iter().rev() { - let name = ctx.module().suffix([ph.name.clone()], ctx.i()).await; + let name = ctx.module().suffix([ph.name.clone()]).await; body_mactree = MacTreeSeq::new([ MacTok::Lambda(MacTok::Name(name).at(ph_pos.clone()), body_mactree).at(ph_pos.clone()) @@ -132,45 +130,40 @@ impl Parser for MacroLine { let body_sr = body.sr(); rules.push((name.clone(), placeholders, pattern)); lines.push(ParsedLine::cnst(&sr, &line.output, true, name, async move |ctx| { - let rep = Reporter::new(); - let body = dealias_mac_v(&body_mactree, &ctx, &rep).await; - let macro_input = MacTok::S(Paren::Round, body).at(body_sr.pos()); - if let Some(e) = rep.errv() { - return Err(e); - } - Ok(call(sym_ref(sym!(macros::resolve; i())), [macro_input.to_gen().await])) + let macro_input = + MacTok::S(Paren::Round, with_reporter(dealias_mac_v(&body_mactree, &ctx)).await?) + .at(body_sr.pos()); + Ok(call(sym_ref(sym!(macros::resolve)), [macro_input.to_gen().await])) })) } let mac_cell = Rc::new(OnceCell::new()); let rules = Rc::new(rules); for (kw, sr) in &*keywords { clone!(mac_cell, rules, module, macro_name, prio); - let kw_key = i().i(&format!("__macro__{kw}")).await; + let kw_key = is(&format!("__macro__{kw}")).await; lines.push(ParsedLine::cnst(&sr.clone(), &comments, true, kw_key, async move |cctx| { let mac_future = async { - let rep = Reporter::new(); - let rules = stream(async |mut h| { - for (body, ph_names, pattern_rel) in rules.iter() { - let pattern = dealias_mac_v(pattern_rel, &cctx, &rep).await; - let ph_names = ph_names.iter().map(|(ph, _)| ph.name.clone()).collect_vec(); - match Matcher::new(pattern.clone()).await { - Ok(matcher) => - h.emit(Rule { body: body.clone(), matcher, pattern, ph_names }).await, - Err(e) => rep.report(e), + let rules = with_reporter( + stream(async |mut h| { + for (body, ph_names, pattern_rel) in rules.iter() { + let pattern = dealias_mac_v(pattern_rel, &cctx).await; + let ph_names = ph_names.iter().map(|(ph, _)| ph.name.clone()).collect_vec(); + match Matcher::new(pattern.clone()).await { + Ok(matcher) => + h.emit(Rule { body: body.clone(), matcher, pattern, ph_names }).await, + Err(e) => report(e), + } } - } - }) - .collect::>() - .await; - match rep.errv() { - Some(e) => Err(e), - None => Ok(Macro(Rc::new(MacroData { - canonical_name: module.suffix([macro_name], &i()).await, - module, - prio: prio.map(|i| i.0 as u64), - rules, - }))), - } + }) + .collect::>(), + ) + .await?; + Ok(Macro(Rc::new(MacroData { + canonical_name: module.suffix([macro_name]).await, + module, + prio: prio.map(|i| i.0 as u64), + rules, + }))) }; mac_cell.get_or_init(mac_future).await.clone().to_gen().await })) diff --git a/orchid-std/src/macros/macro_system.rs b/orchid-std/src/macros/macro_system.rs index df95b56..34e4c89 100644 --- a/orchid-std/src/macros/macro_system.rs +++ b/orchid-std/src/macros/macro_system.rs @@ -1,10 +1,8 @@ use never::Never; use orchid_base::name::Sym; -use orchid_base::reqnot::Receipt; +use orchid_base::reqnot::{Receipt, ReqHandle}; use orchid_base::sym; use orchid_extension::atom::{AtomDynfo, AtomicFeatures}; -use orchid_extension::context::i; -use orchid_extension::entrypoint::ExtReq; use orchid_extension::lexer::LexerObj; use orchid_extension::other_system::SystemHandle; use orchid_extension::parser::ParserObj; @@ -24,14 +22,14 @@ use crate::macros::std_macros::gen_std_macro_lib; use crate::macros::utils::MacroBodyArgCollector; use crate::{MacTree, StdSystem}; -#[derive(Default)] +#[derive(Debug, Default)] pub struct MacroSystem; impl SystemCtor for MacroSystem { type Deps = StdSystem; type Instance = Self; const NAME: &'static str = "orchid::macros"; const VERSION: f64 = 0.00_01; - fn inst(_: SystemHandle) -> Self::Instance { Self } + fn inst(&self, _: SystemHandle) -> Self::Instance { Self } } impl SystemCard for MacroSystem { type Ctor = Self; @@ -48,19 +46,19 @@ impl SystemCard for MacroSystem { } } impl System for MacroSystem { - async fn request(_: ExtReq<'_>, req: Never) -> Receipt<'_> { match req {} } + async fn request<'a>(_: Box + 'a>, req: Never) -> Receipt<'a> { match req {} } async fn prelude() -> Vec { vec![ - sym!(macros::common::+; i()), - sym!(macros::common::*; i()), - sym!(macros::common::,; i()), - sym!(macros::common::;; i()), - sym!(macros::common::..; i()), - sym!(macros::common::_; i()), - sym!(std::tuple::t; i()), - sym!(pattern::match; i()), - sym!(pattern::ref; i()), - sym!(pattern::=>; i()), + sym!(macros::common::+), + sym!(macros::common::*), + sym!(macros::common::,), + sym!(macros::common::;), + sym!(macros::common::..), + sym!(macros::common::_), + sym!(std::tuple::t), + sym!(pattern::match), + sym!(pattern::ref), + sym!(pattern::=>), ] } fn lexers() -> Vec { vec![&MacTreeLexer, &PhLexer] } diff --git a/orchid-std/src/macros/macro_value.rs b/orchid-std/src/macros/macro_value.rs index ee532d5..a55a837 100644 --- a/orchid-std/src/macros/macro_value.rs +++ b/orchid-std/src/macros/macro_value.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::rc::Rc; use never::Never; -use orchid_base::interner::Tok; +use orchid_base::interner::IStr; use orchid_base::name::Sym; use orchid_extension::atom::Atomic; use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant}; @@ -25,8 +25,8 @@ pub struct Macro(pub Rc); pub struct Rule { pub pattern: MacTreeSeq, pub matcher: Matcher, - pub ph_names: Vec>, - pub body: Tok, + pub ph_names: Vec, + pub body: IStr, } impl Atomic for Macro { type Data = (); diff --git a/orchid-std/src/macros/mactree.rs b/orchid-std/src/macros/mactree.rs index ad2b63f..f916779 100644 --- a/orchid-std/src/macros/mactree.rs +++ b/orchid-std/src/macros/mactree.rs @@ -8,7 +8,7 @@ use hashbrown::HashSet; use orchid_api_derive::Coding; use orchid_base::error::OrcErrv; use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants}; -use orchid_base::interner::Tok; +use orchid_base::interner::IStr; use orchid_base::location::Pos; use orchid_base::name::Sym; use orchid_base::tl_cache; @@ -205,7 +205,7 @@ pub async fn mtreev_fmt<'b>( #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct Ph { - pub name: Tok, + pub name: IStr, pub kind: PhKind, } impl Display for Ph { diff --git a/orchid-std/src/macros/mactree_lexer.rs b/orchid-std/src/macros/mactree_lexer.rs index 4c252fe..06fb000 100644 --- a/orchid-std/src/macros/mactree_lexer.rs +++ b/orchid-std/src/macros/mactree_lexer.rs @@ -3,7 +3,7 @@ use std::ops::RangeInclusive; use futures::FutureExt; use itertools::chain; use orchid_base::error::{OrcRes, mk_errv}; -use orchid_base::parse::ParseCtx; +use orchid_base::interner::is; use orchid_base::tokens::PARENS; use orchid_base::tree::Paren; use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable}; @@ -14,7 +14,7 @@ use crate::macros::instantiate_tpl::InstantiateTplCall; use crate::macros::let_line::parse_tok; use crate::macros::mactree::{MacTok, MacTree, MacTreeSeq}; -#[derive(Default)] +#[derive(Debug, Default)] pub struct MacTreeLexer; impl Lexer for MacTreeLexer { const CHAR_FILTER: &'static [RangeInclusive] = &['\''..='\'']; @@ -54,11 +54,9 @@ impl Lexer for MacTreeLexer { let tok = MacTok::S(*paren, MacTreeSeq::new(items)); break Ok((tail3, tok.at(ctx.pos_tt(tail, tail3).pos()))); } else if tail2.is_empty() { - return Err(mk_errv( - ctx.i().i("Unclosed block").await, - format!("Expected closing {rp}"), - [ctx.pos_lt(1, tail)], - )); + return Err(mk_errv(is("Unclosed block").await, format!("Expected closing {rp}"), [ + ctx.pos_lt(1, tail), + ])); } let (new_tail, new_item) = mac_tree(tail2, args, ctx).boxed_local().await?; body_tail = new_tail; @@ -87,7 +85,7 @@ impl Lexer for MacTreeLexer { Ok((tail3, MacTok::Lambda(param, MacTreeSeq::new(body)).at(ctx.pos_tt(tail, tail3).pos()))) } else { let (tail2, sub) = ctx.recurse(tail).await?; - let parsed = parse_tok(&sub, ctx).await.expect("Unexpected invalid token"); + let parsed = parse_tok(&sub).await.expect("Unexpected invalid token"); Ok((tail2, parsed)) } } diff --git a/orchid-std/src/macros/match_macros.rs b/orchid-std/src/macros/match_macros.rs index 2b87227..73105d4 100644 --- a/orchid-std/src/macros/match_macros.rs +++ b/orchid-std/src/macros/match_macros.rs @@ -8,11 +8,11 @@ use orchid_api::ExprTicket; use orchid_api_derive::Coding; use orchid_base::error::{OrcRes, mk_errv}; use orchid_base::format::fmt; +use orchid_base::interner::is; use orchid_base::name::Sym; use orchid_base::sym; use orchid_extension::atom::{Atomic, TAtom}; use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own}; -use orchid_extension::context::i; use orchid_extension::conv::ToExpr; use orchid_extension::coroutine_exec::{ExecHandle, exec}; use orchid_extension::expr::{Expr, ExprHandle}; @@ -43,7 +43,7 @@ impl MatcherData { pub fn keys(&self) -> impl Stream { stream(async |mut h| { for tk in &self.keys { - h.emit(Sym::from_api(*tk, &i()).await).await + h.emit(Sym::from_api(*tk).await).await } }) } @@ -85,7 +85,7 @@ pub async fn gen_match_macro_lib() -> Vec { }, ), fun(true, "matcher", async |names: HomoTpl>, matcher: Expr| MatcherAtom { - keys: join_all(names.0.iter().map(async |atm| Sym::from_api(atm.0, &i()).await)).await, + keys: join_all(names.0.iter().map(async |atm| Sym::from_api(atm.0).await)).await, matcher, }), build_macro(None, ["match", "match_rule", "_row", "=>"]) @@ -93,7 +93,7 @@ pub async fn gen_match_macro_lib() -> Vec { async |[value, rules]| { exec(async move |mut h| { let rule_lines = h - .exec::>(call(sym_ref(sym!(macros::resolve; i())), [ + .exec::>(call(sym_ref(sym!(macros::resolve)), [ mactree!(macros::common::semi_list "push" rules.clone();).to_gen().await, ])) .await?; @@ -105,20 +105,20 @@ pub async fn gen_match_macro_lib() -> Vec { )) .await?; let Tpl((matcher, body)) = h - .exec(call(sym_ref(sym!(macros::resolve; i())), [ + .exec(call(sym_ref(sym!(macros::resolve)), [ mactree!(pattern::_row "push" own(&line_mac).await ;).to_gen().await, ])) .await?; rule_atoms.push((matcher, body)); } let base_case = lambda(0, [bot(mk_errv( - i().i("No branches match").await, + is("No branches match").await, "None of the patterns matches this value", [rules.pos()], ))]); let match_expr = stream::iter(rule_atoms.into_iter().rev()) .fold(base_case, async |tail, (mat, body)| { - lambda(0, [call(sym_ref(sym!(pattern::match_one; i())), [ + lambda(0, [call(sym_ref(sym!(pattern::match_one)), [ mat.to_gen().await, arg(0), body.to_gen().await, @@ -144,14 +144,14 @@ pub async fn gen_match_macro_lib() -> Vec { async |[pattern, mut value]| { exec(async move |mut h| -> OrcRes, GExpr)>> { let Ok(pat) = h - .exec::>(call(sym_ref(sym!(macros::resolve; i())), [ + .exec::>(call(sym_ref(sym!(macros::resolve)), [ mactree!(pattern::match_rule "push" pattern.clone();).to_gen().await, ])) .await else { return Err(mk_errv( - i().i("Invalid pattern").await, - format!("Could not parse {} as a match pattern", fmt(&pattern, &i()).await), + is("Invalid pattern").await, + format!("Could not parse {} as a match pattern", fmt(&pattern).await), [pattern.pos()], )); }; @@ -169,18 +169,18 @@ pub async fn gen_match_macro_lib() -> Vec { .rule(mactreev!(pattern::match_rule(pattern::ref "$" name)), [async |[name]| { let MacTok::Name(name) = name.tok() else { return Err(mk_errv( - i().i("pattern 'ref' requires a name to bind to").await, + is("pattern 'ref' requires a name to bind to").await, format!( "'ref' was interpreted as a binding matcher, \ but it was followed by {} instead of a name", - fmt(&name, &i()).await + fmt(&name).await ), [name.pos()], )); }; Ok(MatcherAtom { keys: vec![name.clone()], - matcher: sym_ref(sym!(pattern::ref_body; i())).to_expr().await, + matcher: sym_ref(sym!(pattern::ref_body)).to_expr().await, }) }]) .finish(), diff --git a/orchid-std/src/macros/ph_lexer.rs b/orchid-std/src/macros/ph_lexer.rs index cd6e019..d48b49c 100644 --- a/orchid-std/src/macros/ph_lexer.rs +++ b/orchid-std/src/macros/ph_lexer.rs @@ -1,10 +1,10 @@ use orchid_api_derive::Coding; use orchid_base::error::{OrcRes, mk_errv}; use orchid_base::format::FmtUnit; +use orchid_base::interner::{es, is}; use orchid_base::parse::{name_char, name_start}; use orchid_extension::atom::Atomic; use orchid_extension::atom_thin::{ThinAtom, ThinVariant}; -use orchid_extension::context::i; use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable}; use orchid_extension::tree::{GenTokTree, x_tok}; @@ -13,7 +13,7 @@ use crate::macros::mactree::{Ph, PhKind}; #[derive(Clone, Coding)] pub struct PhAtom(orchid_api::TStr, PhKind); impl PhAtom { - pub async fn to_full(&self) -> Ph { Ph { kind: self.1, name: i().ex(self.0).await } } + pub async fn to_full(&self) -> Ph { Ph { kind: self.1, name: es(self.0).await } } } impl Atomic for PhAtom { type Data = Self; @@ -21,11 +21,11 @@ impl Atomic for PhAtom { } impl ThinAtom for PhAtom { async fn print(&self) -> FmtUnit { - Ph { name: i().ex(self.0).await, kind: self.1 }.to_string().into() + Ph { name: es(self.0).await, kind: self.1 }.to_string().into() } } -#[derive(Default)] +#[derive(Debug, Default)] pub struct PhLexer; impl Lexer for PhLexer { const CHAR_FILTER: &'static [std::ops::RangeInclusive] = &['$'..='$', '.'..='.']; @@ -52,7 +52,7 @@ impl Lexer for PhLexer { (prio_num, tail) } else { return Err(mk_errv( - i().i("Invalid priority, must be 0-255").await, + is("Invalid priority, must be 0-255").await, format!("{prio} is not a valid placeholder priority"), [ctx.pos_lt(prio.len(), tail)], )); @@ -71,7 +71,7 @@ impl Lexer for PhLexer { return Err(err_not_applicable().await); } }; - let ph_atom = PhAtom(i().i::(name).await.to_api(), phkind); + let ph_atom = PhAtom(is(name).await.to_api(), phkind); Ok((tail, x_tok(ph_atom).await.at(ctx.pos_tt(line, tail)))) } } diff --git a/orchid-std/src/macros/resolve.rs b/orchid-std/src/macros/resolve.rs index bde6cea..cf11b51 100644 --- a/orchid-std/src/macros/resolve.rs +++ b/orchid-std/src/macros/resolve.rs @@ -6,12 +6,13 @@ use hashbrown::{HashMap, HashSet}; use itertools::Itertools; use orchid_base::error::mk_errv; use orchid_base::format::fmt; +use orchid_base::interner::is; use orchid_base::location::Pos; +use orchid_base::logging::logger; use orchid_base::name::{NameLike, Sym, VPath}; use orchid_base::tree::Paren; use orchid_extension::atom::TAtom; use orchid_extension::atom_owned::own; -use orchid_extension::context::{ctx, i}; use orchid_extension::conv::ToExpr; use orchid_extension::coroutine_exec::{ExecHandle, exec}; use orchid_extension::gen_expr::{GExpr, arg, bot, call, lambda, sym_ref}; @@ -26,17 +27,16 @@ use crate::{MacTok, MacTree}; pub async fn resolve(val: MacTree) -> GExpr { exec(async move |mut h| { - let ctx = ctx(); // if ctx.logger().is_active() { - writeln!(ctx.logger(), "Macro-resolving {}", fmt(&val, &i()).await); + writeln!(logger(), "Macro-resolving {}", fmt(&val).await); // } let root = refl(); let mut macros = HashMap::new(); for n in val.glossary() { let (foot, body) = n.split_last_seg(); let new_name = VPath::new(body.iter().cloned()) - .name_with_suffix(i().i(&format!("__macro__{foot}")).await) - .to_sym(&i()) + .name_with_suffix(is(&format!("__macro__{foot}")).await) + .to_sym() .await; if let Ok(ReflMemKind::Const) = root.get_by_path(&new_name).await.map(|m| m.kind()) { let Ok(mac) = h.exec::>(sym_ref(new_name)).await else { continue }; @@ -67,12 +67,7 @@ pub async fn resolve(val: MacTree) -> GExpr { } let mut rctx = ResolveCtx { h, exclusive, priod }; let gex = resolve_one(&mut rctx, Substack::Bottom, &val).await; - writeln!( - ctx.logger(), - "Macro-resolution over {}\nreturned {}", - fmt(&val, &i()).await, - fmt(&gex, &i()).await - ); + writeln!(logger(), "Macro-resolution over {}\nreturned {}", fmt(&val).await, fmt(&gex).await); gex }) .await @@ -110,7 +105,7 @@ async fn resolve_one( MacTok::Lambda(arg, body) => { let MacTok::Name(name) = &*arg.tok else { return bot(mk_errv( - i().i("Syntax error after macros").await, + is("Syntax error after macros").await, "This token ends up as a binding, consider replacing it with a name", [arg.pos()], )); @@ -121,8 +116,8 @@ async fn resolve_one( }, MacTok::S(Paren::Round, body) => resolve_seq(ctx, arg_stk, body.clone(), value.pos()).await, MacTok::S(..) => bot(mk_errv( - i().i("Leftover [] or {} not matched by macro").await, - format!("{} was not matched by any macro", fmt(value, &i()).await), + is("Leftover [] or {} not matched by macro").await, + format!("{} was not matched by any macro", fmt(value).await), [value.pos()], )), } @@ -150,7 +145,7 @@ async fn resolve_seq( ) -> GExpr { if val.items.is_empty() { return bot(mk_errv( - i().i("Empty sequence").await, + is("Empty sequence").await, "() or (\\arg ) left after macro execution. \ This is usually caused by an incomplete call to a macro with bad error detection", [fallback_pos], @@ -224,7 +219,7 @@ async fn resolve_seq( Err((lran, rran)) } }); - let mac_conflict_tk = i().i("Macro conflict").await; + let mac_conflict_tk = is("Macro conflict").await; let error = conflict_sets .filter(|r| 1 < r.len()) .map(|set| { @@ -289,5 +284,5 @@ async fn mk_body_call(mac: &Macro, rule: &Rule, state: &MatchState<'_>, pos: Pos MacTok::S(Paren::Round, MacTreeSeq::new(vec.iter().cloned())).at(Pos::None).to_gen().await, }); } - call(sym_ref(mac.0.module.suffix([rule.body.clone()], &i()).await), call_args).at(pos.clone()) + call(sym_ref(mac.0.module.suffix([rule.body.clone()]).await), call_args).at(pos.clone()) } diff --git a/orchid-std/src/macros/rule/build.rs b/orchid-std/src/macros/rule/build.rs index 9110b2e..dd74e34 100644 --- a/orchid-std/src/macros/rule/build.rs +++ b/orchid-std/src/macros/rule/build.rs @@ -2,17 +2,16 @@ use futures::FutureExt; use futures::future::join_all; use itertools::Itertools; use orchid_base::error::{OrcRes, mk_errv}; -use orchid_base::interner::Tok; +use orchid_base::interner::{IStr, is}; use orchid_base::join_ok; use orchid_base::side::Side; -use orchid_extension::context::i; use super::shared::{AnyMatcher, ScalMatcher, VecMatcher}; use super::vec_attrs::vec_attrs; use crate::macros::mactree::{Ph, PhKind}; use crate::macros::{MacTok, MacTree}; -pub type MaxVecSplit<'a> = (&'a [MacTree], (Tok, u8, bool), &'a [MacTree]); +pub type MaxVecSplit<'a> = (&'a [MacTree], (IStr, u8, bool), &'a [MacTree]); /// Derive the details of the central vectorial and the two sides from a /// slice of Expr's @@ -126,7 +125,7 @@ async fn mk_scalar(pattern: &MacTree) -> OrcRes { MacTok::S(c, body) => ScalMatcher::S(*c, Box::new(mk_any(&body.items).boxed_local().await?)), MacTok::Lambda(..) => return Err(mk_errv( - i().i("Lambda in matcher").await, + is("Lambda in matcher").await, "Lambdas can't be matched for, only generated in templates", [pattern.pos()], )), @@ -137,10 +136,11 @@ async fn mk_scalar(pattern: &MacTree) -> OrcRes { #[cfg(test)] mod test { + use orchid_base::interner::local_interner::local_interner; + use orchid_base::interner::{is, with_interner}; use orchid_base::location::SrcRange; use orchid_base::sym; use orchid_base::tokens::Paren; - use orchid_extension::context::{i, mock_ctx, with_ctx}; use test_executors::spin_on; use super::mk_any; @@ -149,27 +149,27 @@ mod test { #[test] fn test_scan() { - spin_on(with_ctx(mock_ctx(), async { - let ex = |tok: MacTok| async { tok.at(SrcRange::mock(&i()).await.pos()) }; + spin_on(with_interner(local_interner(), async { + let ex = |tok: MacTok| async { tok.at(SrcRange::mock().await.pos()) }; let pattern = vec![ ex(MacTok::Ph(Ph { kind: PhKind::Vector { priority: 0, at_least_one: false }, - name: i().i("::prefix").await, + name: is("::prefix").await, })) .await, - ex(MacTok::Name(sym!(prelude::do; i()))).await, + ex(MacTok::Name(sym!(prelude::do))).await, ex(MacTok::S( Paren::Round, MacTreeSeq::new([ ex(MacTok::Ph(Ph { kind: PhKind::Vector { priority: 0, at_least_one: false }, - name: i().i("expr").await, + name: is("expr").await, })) .await, - ex(MacTok::Name(sym!(prelude::; ; i()))).await, + ex(MacTok::Name(sym!(prelude::;))).await, ex(MacTok::Ph(Ph { kind: PhKind::Vector { priority: 1, at_least_one: false }, - name: i().i("rest").await, + name: is("rest").await, })) .await, ]), @@ -177,7 +177,7 @@ mod test { .await, ex(MacTok::Ph(Ph { kind: PhKind::Vector { priority: 0, at_least_one: false }, - name: i().i("::suffix").await, + name: is("::suffix").await, })) .await, ]; diff --git a/orchid-std/src/macros/rule/matcher.rs b/orchid-std/src/macros/rule/matcher.rs index c72b7f7..cf7a829 100644 --- a/orchid-std/src/macros/rule/matcher.rs +++ b/orchid-std/src/macros/rule/matcher.rs @@ -2,8 +2,8 @@ use std::fmt; use std::rc::Rc; use orchid_base::error::OrcRes; +use orchid_base::interner::is; use orchid_base::name::Sym; -use orchid_extension::context::i; use super::any_match::any_match; use super::build::mk_any; @@ -24,12 +24,12 @@ impl Matcher { let first = pattern.first().expect("Empty pattern is not allowed"); if vec_attrs(first).is_none() { let pos = first.pos(); - pattern.insert(0, MacTok::Ph(Ph { name: i().i("::before").await, kind }).at(pos)); + pattern.insert(0, MacTok::Ph(Ph { name: is("::before").await, kind }).at(pos)); } let last = pattern.last().expect("first returned Some above"); if vec_attrs(last).is_none() { let pos = last.pos(); - pattern.insert(0, MacTok::Ph(Ph { name: i().i("::after").await, kind }).at(pos)); + pattern.insert(0, MacTok::Ph(Ph { name: is("::after").await, kind }).at(pos)); } Ok(Matcher { inner: mk_any(&pattern).await? }) } @@ -42,7 +42,7 @@ impl Matcher { ) -> Option<(&'a [MacTree], MatchState<'a>, &'a [MacTree])> { let mut result = any_match(&self.inner, seq, &save_loc)?; async fn remove_frame<'a>(result: &mut MatchState<'a>, key: &str) -> &'a [MacTree] { - match result.remove(i().i(key).await) { + match result.remove(is(key).await) { Some(StateEntry::Scalar(_)) => panic!("{key} is defined in the constructor as a Vec"), Some(StateEntry::Vec(v)) => v, None => &[], diff --git a/orchid-std/src/macros/rule/shared.rs b/orchid-std/src/macros/rule/shared.rs index 32bee70..b942162 100644 --- a/orchid-std/src/macros/rule/shared.rs +++ b/orchid-std/src/macros/rule/shared.rs @@ -3,7 +3,7 @@ use std::fmt; use itertools::Itertools; -use orchid_base::interner::Tok; +use orchid_base::interner::IStr; use orchid_base::name::Sym; use orchid_base::side::Side; use orchid_base::tokens::{PARENS, Paren}; @@ -11,12 +11,12 @@ use orchid_base::tokens::{PARENS, Paren}; pub enum ScalMatcher { Name(Sym), S(Paren, Box), - Placeh { key: Tok }, + Placeh { key: IStr }, } pub enum VecMatcher { Placeh { - key: Tok, + key: IStr, nonzero: bool, }, Scan { @@ -41,7 +41,7 @@ pub enum VecMatcher { /// the length of matches on either side. /// /// Vectorial keys that appear on either side, in priority order - key_order: Vec>, + key_order: Vec, }, } diff --git a/orchid-std/src/macros/rule/state.rs b/orchid-std/src/macros/rule/state.rs index 5ef4c07..1d79e96 100644 --- a/orchid-std/src/macros/rule/state.rs +++ b/orchid-std/src/macros/rule/state.rs @@ -2,7 +2,7 @@ use std::any::Any; use hashbrown::HashMap; -use orchid_base::interner::Tok; +use orchid_base::interner::IStr; use orchid_base::join::join_maps; use orchid_base::location::Pos; use orchid_base::match_mapping; @@ -30,11 +30,11 @@ pub enum StateEntry<'a> { } #[derive(Clone, Debug)] pub struct MatchState<'a> { - placeholders: HashMap, StateEntry<'a>>, + placeholders: HashMap>, name_posv: HashMap>, } impl<'a> MatchState<'a> { - pub fn from_ph(key: Tok, entry: StateEntry<'a>) -> Self { + pub fn from_ph(key: IStr, entry: StateEntry<'a>) -> Self { Self { placeholders: HashMap::from([(key, entry)]), name_posv: HashMap::new() } } pub fn combine(self, s: Self) -> Self { @@ -45,7 +45,7 @@ impl<'a> MatchState<'a> { }), } } - pub fn ph_len(&self, key: &Tok) -> Option { + pub fn ph_len(&self, key: &IStr) -> Option { match self.placeholders.get(key)? { StateEntry::Vec(slc) => Some(slc.len()), _ => None, @@ -57,10 +57,8 @@ impl<'a> MatchState<'a> { pub fn names(&self) -> impl Iterator { self.name_posv.iter().map(|(sym, vec)| (sym.clone(), &vec[..])) } - pub fn get(&self, key: &Tok) -> Option<&StateEntry<'a>> { self.placeholders.get(key) } - pub fn remove(&mut self, name: Tok) -> Option> { - self.placeholders.remove(&name) - } + pub fn get(&self, key: &IStr) -> Option<&StateEntry<'a>> { self.placeholders.get(key) } + pub fn remove(&mut self, name: IStr) -> Option> { self.placeholders.remove(&name) } pub fn mk_owned(self) -> OwnedState { OwnedState { placeholders: (self.placeholders.into_iter()) @@ -88,10 +86,10 @@ pub enum OwnedEntry { Scalar(MacTree), } pub struct OwnedState { - placeholders: HashMap, OwnedEntry>, + placeholders: HashMap, name_posv: HashMap>, } impl OwnedState { - pub fn get(&self, key: &Tok) -> Option<&OwnedEntry> { self.placeholders.get(key) } + pub fn get(&self, key: &IStr) -> Option<&OwnedEntry> { self.placeholders.get(key) } pub fn positions(&self, name: &Sym) -> &[Pos] { self.name_posv.get(name).map_or(&[], |v| &v[..]) } } diff --git a/orchid-std/src/macros/rule/vec_attrs.rs b/orchid-std/src/macros/rule/vec_attrs.rs index ddb9900..a440f8d 100644 --- a/orchid-std/src/macros/rule/vec_attrs.rs +++ b/orchid-std/src/macros/rule/vec_attrs.rs @@ -1,4 +1,4 @@ -use orchid_base::interner::Tok; +use orchid_base::interner::IStr; use crate::macros::mactree::{Ph, PhKind}; use crate::macros::{MacTok, MacTree}; @@ -6,7 +6,7 @@ use crate::macros::{MacTok, MacTree}; /// Returns the name, priority and at_least_one of the expression if it is /// a vectorial placeholder #[must_use] -pub fn vec_attrs(expr: &MacTree) -> Option<(Tok, u8, bool)> { +pub fn vec_attrs(expr: &MacTree) -> Option<(IStr, u8, bool)> { match (*expr.tok).clone() { MacTok::Ph(Ph { kind: PhKind::Vector { priority, at_least_one }, name }) => Some((name, priority, at_least_one)), diff --git a/orchid-std/src/macros/std_macros.rs b/orchid-std/src/macros/std_macros.rs index 7b6846a..b505c39 100644 --- a/orchid-std/src/macros/std_macros.rs +++ b/orchid-std/src/macros/std_macros.rs @@ -3,7 +3,6 @@ use orchid_base::error::OrcRes; use orchid_base::sym; use orchid_extension::atom::TAtom; use orchid_extension::atom_owned::own; -use orchid_extension::context::i; use orchid_extension::conv::ToExpr; use orchid_extension::coroutine_exec::exec; use orchid_extension::expr::Expr; @@ -39,7 +38,7 @@ pub async fn gen_std_macro_lib() -> Vec { Ok(MatcherAtom { keys: sub.keys().collect().await, matcher: h - .register(call(sym_ref(sym!(std::option::is_some_body; i())), [sub + .register(call(sym_ref(sym!(std::option::is_some_body)), [sub .to_gen() .await])) .await, @@ -51,7 +50,7 @@ pub async fn gen_std_macro_lib() -> Vec { exec(async |mut h| { Ok(MatcherAtom { keys: vec![], - matcher: h.register(sym_ref(sym!(std::option::is_none_body; i()))).await, + matcher: h.register(sym_ref(sym!(std::option::is_none_body))).await, }) }) }]) @@ -62,16 +61,16 @@ pub async fn gen_std_macro_lib() -> Vec { .rule(mactreev!(std::tuple::t [ "...$" elements 0 ]), [|[elements]: [_; _]| { exec(async move |mut h| { let tup = h - .exec::>>(call(sym_ref(sym!(macros::resolve; i())), [ + .exec::>>(call(sym_ref(sym!(macros::resolve)), [ mactree!((macros::common::comma_list "push" elements ;)).to_gen().await, ])) .await?; let val = stream::iter(&tup.0[..]) - .fold(sym_ref(sym!(std::tuple::empty; i())), async |head, new| { - call(sym_ref(sym!(std::tuple::cat; i())), [ + .fold(sym_ref(sym!(std::tuple::empty)), async |head, new| { + call(sym_ref(sym!(std::tuple::cat)), [ head, - call(sym_ref(sym!(std::tuple::one; i())), [call( - sym_ref(sym!(macros::resolve; i())), + call(sym_ref(sym!(std::tuple::one)), [call( + sym_ref(sym!(macros::resolve)), [new.clone().to_gen().await], )]), ]) @@ -102,7 +101,7 @@ pub async fn gen_std_macro_lib() -> Vec { fn parse_tpl(elements: MacTree, tail_matcher: Option) -> impl Future { exec(async move |mut h| -> OrcRes { let tup = h - .exec::>>(call(sym_ref(sym!(macros::resolve; i())), [ + .exec::>>(call(sym_ref(sym!(macros::resolve)), [ mactree!((macros::common::comma_list "push" elements ;)).to_gen().await, ])) .await?; @@ -110,7 +109,7 @@ fn parse_tpl(elements: MacTree, tail_matcher: Option) -> impl Future>(call(sym_ref(sym!(macros::resolve; i())), [ + .exec::>(call(sym_ref(sym!(macros::resolve)), [ mactree!(pattern::match_rule ("push" mac ;)).to_gen().await, ])) .await?; @@ -118,7 +117,7 @@ fn parse_tpl(elements: MacTree, tail_matcher: Option) -> impl Future Some( - h.exec::>(call(sym_ref(sym!(macros::resolve; i())), [ + h.exec::>(call(sym_ref(sym!(macros::resolve)), [ mactree!(pattern::match_rule "push" mac ;).to_gen().await, ])) .await?, @@ -131,7 +130,7 @@ fn parse_tpl(elements: MacTree, tail_matcher: Option) -> impl Future (), Some(tail_mat) => { let tail_tpl = stream::iter(&value.0[children.0.len()..]) - .fold(sym_ref(sym!(std::tuple::empty; i())), async |prefix, new| { - call(sym_ref(sym!(std::tuple::cat; i())), [prefix, new.clone().to_gen().await]) + .fold(sym_ref(sym!(std::tuple::empty)), async |prefix, new| { + call(sym_ref(sym!(std::tuple::cat)), [prefix, new.clone().to_gen().await]) }) .await; match tail_mat.run_matcher(&mut h, tail_tpl).await? { diff --git a/orchid-std/src/macros/utils.rs b/orchid-std/src/macros/utils.rs index a24eb1c..47c23b6 100644 --- a/orchid-std/src/macros/utils.rs +++ b/orchid-std/src/macros/utils.rs @@ -6,10 +6,10 @@ use futures::StreamExt; use futures::future::LocalBoxFuture; use itertools::{Itertools, chain}; use never::Never; +use orchid_base::interner::is; use orchid_base::name::{NameLike, Sym, VPath}; use orchid_extension::atom::{Atomic, TAtom}; use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own}; -use orchid_extension::context::i; use orchid_extension::conv::ToExpr; use orchid_extension::gen_expr::{GExpr, sym_ref}; use orchid_extension::tree::{GenMember, MemKind, cnst, lazy}; @@ -101,11 +101,11 @@ impl MacroBuilder { let Self { own_kws, prio, patterns, body_consts } = self; let name = own_kws[0]; let main_const = lazy(true, &format!("__macro__{name}"), async move |path| { - let module = (Sym::new(path.split_last_seg().1.iter().cloned(), &i()).await) + let module = (Sym::new(path.split_last_seg().1.iter().cloned()).await) .expect("Default macro in global root"); MemKind::Const( Macro(Rc::new(MacroData { - canonical_name: module.suffix([i().i(name).await], &i()).await, + canonical_name: module.suffix([is(name).await]).await, module, prio, rules: stream(async |mut h| { @@ -121,7 +121,7 @@ impl MacroBuilder { matcher: Matcher::new(pattern.clone()).await.unwrap(), pattern, ph_names: placeholders, - body: i().i(&format!("({name})::{counter}")).await, + body: is(&format!("({name})::{counter}")).await, }) .await; } @@ -136,8 +136,8 @@ impl MacroBuilder { let kw_consts = own_kws[1..].iter().flat_map(|kw| { lazy(true, &format!("__macro__{kw}"), async move |path| { let main_const_name = VPath::new(path.split_last_seg().1.iter().cloned()) - .name_with_suffix(i().i(&format!("__macro__{name}")).await) - .to_sym(&i()) + .name_with_suffix(is(&format!("__macro__{name}")).await) + .to_sym() .await; MemKind::Const(sym_ref(main_const_name)) }) @@ -156,21 +156,21 @@ macro_rules! mactreev_impl { (@RECUR $ret:ident) => {}; (@RECUR $ret:ident "..$" $name:ident $prio:literal $($tail:tt)*) => { $ret.push($crate::macros::mactree::MacTok::Ph($crate::macros::mactree::Ph{ - name: orchid_extension::context::i().i(stringify!($name)).await, + name: orchid_base::interner::is(stringify!($name)).await, kind: $crate::macros::mactree::PhKind::Vector{ at_least_one: false, priority: $prio } }).at(orchid_base::location::Pos::Inherit)); $crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*); }; (@RECUR $ret:ident "...$" $name:ident $prio:literal $($tail:tt)*) => { $ret.push($crate::macros::mactree::MacTok::Ph($crate::macros::mactree::Ph{ - name: orchid_extension::context::i().i(stringify!($name)).await, + name: orchid_base::interner::is(stringify!($name)).await, kind: $crate::macros::mactree::PhKind::Vector{ at_least_one: true, priority: $prio } }).at(orchid_base::location::Pos::Inherit)); $crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*); }; (@RECUR $ret:ident "$" $name:ident $($tail:tt)*) => { $ret.push($crate::macros::mactree::MacTok::Ph($crate::macros::mactree::Ph{ - name: orchid_extension::context::i().i(stringify!(name)).await, + name: orchid_base::interner::is(stringify!(name)).await, kind: $crate::macros::mactree::PhKind::Scalar }).at(orchid_base::location::Pos::Inherit)); $crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*); @@ -195,7 +195,7 @@ macro_rules! mactreev_impl { }; (@RECUR $ret:ident "l" $argh:tt $(:: $arg:tt)+ ($($body:tt)*) $($tail:tt)*) => { $ret.push(MacTok::Lambda( - MacTok::Name(sym!($argh $(:: $arg)+; orchid_extension::context::i()).await).at(orchid_base::location::Pos::Inherit), + MacTok::Name(sym!($argh $(:: $arg)+).await).at(orchid_base::location::Pos::Inherit), $crate::macros::utils::mactreev!($($body)*) ).at(orchid_base::location::Pos::Inherit)); $crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*); @@ -207,8 +207,7 @@ macro_rules! mactreev_impl { $name ); let sym = orchid_base::name::Sym::parse( - $name, - &orchid_extension::context::i() + $name ).await.expect("Empty string in sym literal in Rust"); $ret.push( $crate::macros::mactree::MacTok::Name(sym) @@ -253,7 +252,7 @@ macro_rules! mactreev_impl { $crate::macros::utils::mactreev_impl!(@NAME_MUNCHER $ret ($($munched)* :: $name) $($tail)*) }; (@NAME_MUNCHER $ret:ident ($($munched:tt)*) $($tail:tt)*) => { - let sym = orchid_base::sym!($($munched)* ; orchid_extension::context::i()); + let sym = orchid_base::sym!($($munched)*); $ret.push( $crate::macros::mactree::MacTok::Name(sym) .at(orchid_base::location::Pos::Inherit) diff --git a/orchid-std/src/main.rs b/orchid-std/src/main.rs index 5576b2c..215ac37 100644 --- a/orchid-std/src/main.rs +++ b/orchid-std/src/main.rs @@ -1,8 +1,8 @@ -use orchid_extension::entrypoint::ExtensionData; +use orchid_extension::entrypoint::ExtensionBuilder; use orchid_extension::tokio::tokio_main; use orchid_std::{MacroSystem, StdSystem}; #[tokio::main(flavor = "current_thread")] pub async fn main() { - tokio_main(ExtensionData::new("orchid-std::main", &[&StdSystem, &MacroSystem])).await + tokio_main(ExtensionBuilder::new("orchid-std::main").system(StdSystem).system(MacroSystem)).await } diff --git a/orchid-std/src/std/number/num_atom.rs b/orchid-std/src/std/number/num_atom.rs index 674a027..c7e1808 100644 --- a/orchid-std/src/std/number/num_atom.rs +++ b/orchid-std/src/std/number/num_atom.rs @@ -6,7 +6,6 @@ use orchid_base::name::Sym; use orchid_base::number::Numeric; use orchid_extension::atom::{AtomFactory, Atomic, AtomicFeatures, Supports, TAtom, ToAtom}; use orchid_extension::atom_thin::{ThinAtom, ThinVariant}; -use orchid_extension::context::i; use orchid_extension::conv::TryFromExpr; use orchid_extension::expr::Expr; use ordered_float::NotNan; @@ -31,7 +30,7 @@ impl TryFromExpr for Int { } impl Supports for Int { async fn handle(&self, _: GetTagIdMethod) -> ::Response { - Sym::parse("std::number::Int", &i()).await.unwrap().to_api() + Sym::parse("std::number::Int").await.unwrap().to_api() } } impl Supports for Int { diff --git a/orchid-std/src/std/number/num_lexer.rs b/orchid-std/src/std/number/num_lexer.rs index a2a9240..f41ffa4 100644 --- a/orchid-std/src/std/number/num_lexer.rs +++ b/orchid-std/src/std/number/num_lexer.rs @@ -3,13 +3,12 @@ use std::ops::RangeInclusive; use orchid_base::error::OrcRes; use orchid_base::number::{num_to_errv, parse_num}; use orchid_extension::atom::ToAtom; -use orchid_extension::context::i; use orchid_extension::lexer::{LexContext, Lexer}; use orchid_extension::tree::{GenTokTree, x_tok}; use super::num_atom::Num; -#[derive(Default)] +#[derive(Debug, Default)] pub struct NumLexer; impl Lexer for NumLexer { const CHAR_FILTER: &'static [RangeInclusive] = &['0'..='9']; @@ -18,7 +17,7 @@ impl Lexer for NumLexer { let (chars, tail) = all.split_at(ends_at.unwrap_or(all.len())); let fac = match parse_num(chars) { Ok(numeric) => Num(numeric).to_atom_factory(), - Err(e) => return Err(num_to_errv(e, lxcx.pos(all), lxcx.src(), &i()).await), + Err(e) => return Err(num_to_errv(e, lxcx.pos(all), lxcx.src()).await), }; Ok((tail, x_tok(fac).await.at(lxcx.pos_lt(chars.len(), tail)))) } diff --git a/orchid-std/src/std/option.rs b/orchid-std/src/std/option.rs index 8443cb6..8893fb4 100644 --- a/orchid-std/src/std/option.rs +++ b/orchid-std/src/std/option.rs @@ -4,10 +4,10 @@ use std::pin::Pin; use futures::AsyncWrite; use orchid_api_traits::Encode; use orchid_base::error::mk_errv; +use orchid_base::interner::is; use orchid_base::sym; use orchid_extension::atom::{Atomic, ForeignAtom, TAtom}; use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant}; -use orchid_extension::context::i; use orchid_extension::conv::{ToExpr, TryFromExpr}; use orchid_extension::expr::{Expr, ExprHandle}; use orchid_extension::gen_expr::{call, sym_ref}; @@ -30,7 +30,7 @@ impl OwnedAtom for OptAtom { Self(ctx.read::().await.then(|| refs.into_iter().next().unwrap())) } async fn serialize(&self, write: Pin<&mut (impl AsyncWrite + ?Sized)>) -> Self::Refs { - self.0.is_some().encode(write).await; + self.0.is_some().encode(write).await.unwrap(); self.0.iter().cloned().collect() } } @@ -50,9 +50,9 @@ impl TryFromExpr for OrcOpt { impl ToExpr for OrcOpt { async fn to_gen(self) -> orchid_extension::gen_expr::GExpr { if let Some(val) = self.0 { - call(sym_ref(sym!(std::option::some; i())), [val.to_gen().await]) + call(sym_ref(sym!(std::option::some)), [val.to_gen().await]) } else { - sym_ref(sym!(std::option::none; i())) + sym_ref(sym!(std::option::none)) } } } @@ -64,11 +64,10 @@ pub fn gen_option_lib() -> Vec { fun(true, "expect", async |opt: ForeignAtom, msg: OrcString| { match OrcOpt::try_from_expr(opt.clone().ex()).await? { OrcOpt(Some(ex)) => Ok::(ex), - OrcOpt(None) => Err(mk_errv( - i().i("Unwrapped std::option::none").await, - msg.get_string().await.as_str(), - [opt.pos()], - )), + OrcOpt(None) => + Err(mk_errv(is("Unwrapped std::option::none").await, msg.get_string().await.as_str(), [ + opt.pos(), + ])), } }), ]) diff --git a/orchid-std/src/std/protocol/parse_impls.rs b/orchid-std/src/std/protocol/parse_impls.rs index 4557ab2..9dbaa55 100644 --- a/orchid-std/src/std/protocol/parse_impls.rs +++ b/orchid-std/src/std/protocol/parse_impls.rs @@ -1,9 +1,9 @@ use itertools::{Itertools, chain}; use orchid_base::error::{OrcRes, mk_errv}; -use orchid_base::interner::Tok; +use orchid_base::interner::{IStr, is}; use orchid_base::name::Sym; use orchid_base::parse::{ - Import, ParseCtx, Parsed, Snippet, expect_tok, line_items, parse_multiname, token_errv, + Import, Parsed, Snippet, expect_tok, line_items, parse_multiname, token_errv, }; use orchid_base::tree::{Paren, Token}; use orchid_extension::parser::{ @@ -11,57 +11,54 @@ use orchid_extension::parser::{ }; pub async fn parse_impls( - ctx: &ParsCtx<'_>, + _: &ParsCtx<'_>, lines: &mut Vec, - impls: &mut Vec<(Sym, Tok)>, + impls: &mut Vec<(Sym, IStr)>, body_tt: &PTokTree, ) -> OrcRes<()> { - let i = ctx.i().clone(); let body = match &body_tt.tok { - Token::S(Paren::Round, body) => line_items(ctx, Snippet::new(body_tt, body)).await, + Token::S(Paren::Round, body) => line_items(Snippet::new(body_tt, body)).await, Token::S(ptyp, _) => return Err(mk_errv( - i.i("Incorrect paren type").await, + is("Incorrect paren type").await, format!("Expected () block, found {ptyp}"), [body_tt.sr().pos()], )), _ => return Err( - token_errv(ctx, body_tt, "Expected body", |s| { - format!("Expected (impl ...) block, found {s}") - }) - .await, + token_errv(body_tt, "Expected body", |s| format!("Expected (impl ...) block, found {s}")) + .await, ), }; for Parsed { tail: line, output: comments } in body { - if let Ok(Parsed { tail, .. }) = expect_tok(ctx, line, i.i("impl").await).await { - let Parsed { tail, output: name_tt } = parse_multiname(ctx, tail).await?; + if let Ok(Parsed { tail, .. }) = expect_tok(line, is("impl").await).await { + let Parsed { tail, output: name_tt } = parse_multiname(tail).await?; let (name, name_sr) = match name_tt.into_iter().at_most_one() { Ok(None) => panic!("multiname is always at least one name"), Ok(Some(ref n @ Import { name: Some(_), ref sr, .. })) => - (n.clone().mspath().to_sym(&i).await, sr.clone()), + (n.clone().mspath().to_sym().await, sr.clone()), Ok(Some(Import { name: None, sr, .. })) => return Err(mk_errv( - i.i("impl line with globstar").await, + is("impl line with globstar").await, "::* is not permitted in a protocol impl", [sr.pos()], )), Err(e) => return Err(mk_errv( - i.i("Impl line with multiple protocol names").await, + is("Impl line with multiple protocol names").await, "::() is not permitted in a protocol impl", e.map(|i| i.sr.pos()), )), }; - let Parsed { tail, .. } = expect_tok(ctx, tail, i.i("as").await).await?; - let cnst_name = i.i(&format!("{}{}", lines.len(), name.iter().join("__"))).await; + let Parsed { tail, .. } = expect_tok(tail, is("as").await).await?; + let cnst_name = is(&format!("{}{}", lines.len(), name.iter().join("__"))).await; lines.push(ParsedLine { comments, sr: line.sr(), kind: ParsedLineKind::Rec(Vec::from_iter(chain![ - [Token::Name(i.i("let").await).at(line.sr())], + [Token::Name(is("let").await).at(line.sr())], [Token::Name(cnst_name.clone()).at(name_sr)], - [Token::Name(i.i("=").await).at(line.sr())], + [Token::Name(is("=").await).at(line.sr())], tail.iter().cloned().map(p_tree2gen), ])), }); diff --git a/orchid-std/src/std/protocol/proto_parser.rs b/orchid-std/src/std/protocol/proto_parser.rs index e350ef4..0340f6d 100644 --- a/orchid-std/src/std/protocol/proto_parser.rs +++ b/orchid-std/src/std/protocol/proto_parser.rs @@ -2,10 +2,10 @@ use std::rc::Rc; use hashbrown::HashMap; use orchid_base::error::{OrcRes, mk_errv}; +use orchid_base::interner::is; use orchid_base::parse::{Comment, Parsed, expect_end, try_pop_no_fluff}; use orchid_base::sym; use orchid_base::tree::Token; -use orchid_extension::context::i; use orchid_extension::coroutine_exec::exec; use orchid_extension::gen_expr::{call, sym_ref}; use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser}; @@ -23,11 +23,11 @@ impl Parser for AsProtoParser { cmts: Vec, line: PSnippet<'a>, ) -> OrcRes> { - let Parsed { output: body_tt, tail } = try_pop_no_fluff(&pcx, line).await?; - expect_end(&pcx, tail).await?; + let Parsed { output: body_tt, tail } = try_pop_no_fluff(line).await?; + expect_end(tail).await?; if exported { return Err(mk_errv( - i().i("Exported internal line").await, + is("Exported internal line").await, "as_proto cannot be exported, the type shares the enclosing module's visibility", [line.sr().pos()], )); @@ -36,20 +36,20 @@ impl Parser for AsProtoParser { let mut impls = Vec::new(); parse_impls(&pcx, &mut lines, &mut impls, body_tt).await?; let id = pcx.module(); - let proto_tag_name = i().i("__protocol_tag__").await; - let proto_tag_path = id.suffix([proto_tag_name.clone()], &i()).await; + let proto_tag_name = is("__protocol_tag__").await; + let proto_tag_path = id.suffix([proto_tag_name.clone()]).await; lines.push(ParsedLine::cnst(&line.sr(), &cmts, true, proto_tag_name, async |_ccx| { exec(async move |mut h| { let mut new_impls = HashMap::new(); for (k, v) in impls { - new_impls.insert(k.clone(), h.register(sym_ref(id.suffix([v], &i()).await)).await); + new_impls.insert(k.clone(), h.register(sym_ref(id.suffix([v]).await)).await); } Tag { id, impls: Rc::new(new_impls) } }) .await })); - lines.push(ParsedLine::cnst(&line.sr(), [], false, i().i("resolve").await, async move |_| { - call(sym_ref(sym!(std::protocol::resolve; i())), [sym_ref(proto_tag_path)]) + lines.push(ParsedLine::cnst(&line.sr(), [], false, is("resolve").await, async move |_| { + call(sym_ref(sym!(std::protocol::resolve)), [sym_ref(proto_tag_path)]) })); Ok(lines) } @@ -65,9 +65,9 @@ impl Parser for ProtoParser { cmts: Vec, line: PSnippet<'a>, ) -> OrcRes> { - let Parsed { output: name_tt, tail } = try_pop_no_fluff(&ctx, line).await?; + let Parsed { output: name_tt, tail } = try_pop_no_fluff(line).await?; let Token::Name(name) = &name_tt.tok else { - return Err(mk_errv(i().i("missing name for type").await, "A type needs a name", [name_tt + return Err(mk_errv(is("missing name for type").await, "A type needs a name", [name_tt .sr() .pos()])); }; diff --git a/orchid-std/src/std/protocol/type_parser.rs b/orchid-std/src/std/protocol/type_parser.rs index ce10ba3..7a96de4 100644 --- a/orchid-std/src/std/protocol/type_parser.rs +++ b/orchid-std/src/std/protocol/type_parser.rs @@ -2,10 +2,10 @@ use std::rc::Rc; use hashbrown::HashMap; use orchid_base::error::{OrcRes, mk_errv}; +use orchid_base::interner::is; use orchid_base::parse::{Comment, Parsed, expect_end, try_pop_no_fluff}; use orchid_base::sym; use orchid_base::tree::Token; -use orchid_extension::context::i; use orchid_extension::coroutine_exec::exec; use orchid_extension::gen_expr::{call, sym_ref}; use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser}; @@ -23,11 +23,11 @@ impl Parser for AsTypeParser { cmts: Vec, line: PSnippet<'a>, ) -> OrcRes> { - let Parsed { output: body_tt, tail } = try_pop_no_fluff(&ctx, line).await?; - expect_end(&ctx, tail).await?; + let Parsed { output: body_tt, tail } = try_pop_no_fluff(line).await?; + expect_end(tail).await?; if exported { return Err(mk_errv( - i().i("Exported internal line").await, + is("Exported internal line").await, "as_type cannot be exported, the type shares the enclosing module's visibility", [line.sr().pos()], )); @@ -36,25 +36,25 @@ impl Parser for AsTypeParser { let mut impls = Vec::new(); parse_impls(&ctx, &mut lines, &mut impls, body_tt).await?; let id = ctx.module(); - let type_tag_name = i().i("__type_tag__").await; - let type_tag_path = id.suffix([type_tag_name.clone()], &i()).await; + let type_tag_name = is("__type_tag__").await; + let type_tag_path = id.suffix([type_tag_name.clone()]).await; lines.push(ParsedLine::cnst(&line.sr(), &cmts, true, type_tag_name, async |_ccx| { exec(async move |mut h| { let mut new_impls = HashMap::new(); for (k, v) in impls { - new_impls.insert(k.clone(), h.register(sym_ref(id.suffix([v], &i()).await)).await); + new_impls.insert(k.clone(), h.register(sym_ref(id.suffix([v]).await)).await); } Tag { id, impls: Rc::new(new_impls) } }) .await })); let type_tag_path_1 = type_tag_path.clone(); - lines.push(ParsedLine::cnst(&line.sr(), [], false, i().i("wrap").await, async move |_ccx| { - call(sym_ref(sym!(std::protocol::wrap; i())), [sym_ref(type_tag_path_1)]) + lines.push(ParsedLine::cnst(&line.sr(), [], false, is("wrap").await, async move |_ccx| { + call(sym_ref(sym!(std::protocol::wrap)), [sym_ref(type_tag_path_1)]) })); let type_tag_path_1 = type_tag_path.clone(); - lines.push(ParsedLine::cnst(&line.sr(), [], false, i().i("unwrap").await, async move |_ccx| { - call(sym_ref(sym!(std::protocol::unwrap; i())), [sym_ref(type_tag_path_1)]) + lines.push(ParsedLine::cnst(&line.sr(), [], false, is("unwrap").await, async move |_ccx| { + call(sym_ref(sym!(std::protocol::unwrap)), [sym_ref(type_tag_path_1)]) })); Ok(lines) } @@ -70,9 +70,9 @@ impl Parser for TypeParser { cmts: Vec, line: PSnippet<'a>, ) -> OrcRes> { - let Parsed { output: name_tt, tail } = try_pop_no_fluff(&ctx, line).await?; + let Parsed { output: name_tt, tail } = try_pop_no_fluff(line).await?; let Token::Name(name) = &name_tt.tok else { - return Err(mk_errv(i().i("missing name for type").await, "A type needs a name", [name_tt + return Err(mk_errv(is("missing name for type").await, "A type needs a name", [name_tt .sr() .pos()])); }; diff --git a/orchid-std/src/std/protocol/types.rs b/orchid-std/src/std/protocol/types.rs index bf20858..138d271 100644 --- a/orchid-std/src/std/protocol/types.rs +++ b/orchid-std/src/std/protocol/types.rs @@ -7,10 +7,10 @@ use orchid_api_derive::Coding; use orchid_api_traits::Request; use orchid_base::error::{OrcRes, mk_errv}; use orchid_base::format::fmt; +use orchid_base::interner::is; use orchid_base::name::Sym; use orchid_extension::atom::{AtomMethod, Atomic, ForeignAtom, MethodSetBuilder, Supports, TAtom}; use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own}; -use orchid_extension::context::i; use orchid_extension::conv::ToExpr; use orchid_extension::expr::Expr; use orchid_extension::gen_expr::call; @@ -34,7 +34,7 @@ impl OwnedAtom for Tag { } impl Supports for Tag { async fn handle(&self, req: GetImplMethod) -> ::Response { - self.impls.get(&Sym::from_api(req.0, &i()).await).map(|expr| expr.handle().ticket()) + self.impls.get(&Sym::from_api(req.0).await).map(|expr| expr.handle().ticket()) } } #[derive(Clone, Debug, Coding)] @@ -75,13 +75,13 @@ impl Supports for Tagged { pub async fn get_impl(receiver: ForeignAtom, proto: ForeignAtom) -> OrcRes { let Some(proto_id) = proto.request(GetTagIdMethod).await else { - return Err(mk_errv(i().i("Not a protocol").await, "Protocol does not have a tag ID", [ + return Err(mk_errv(is("Not a protocol").await, "Protocol does not have a tag ID", [ proto.pos() ])); }; let Some(impl_val_opt) = receiver.request(GetImplMethod(proto_id)).await else { return Err(mk_errv( - i().i("Receiver not tagged").await, + is("Receiver not tagged").await, "The receiver does not have a type tag", [receiver.pos()], )); @@ -91,14 +91,14 @@ pub async fn get_impl(receiver: ForeignAtom, proto: ForeignAtom) -> OrcRes } let Some(type_id) = receiver.request(GetTagIdMethod).await else { return Err(mk_errv( - i().i("Incorrect protocols implementation in extension").await, + is("Incorrect protocols implementation in extension").await, "Atom provides an impl table but no tag ID", [receiver.pos()], )); }; let Some(impl_val_opt) = proto.request(GetImplMethod(type_id)).await else { return Err(mk_errv( - i().i("Incorrect protocols implementation in extension").await, + is("Incorrect protocols implementation in extension").await, "Proto table atom provides a tag ID but no impl table", [receiver.pos()], )); @@ -107,7 +107,7 @@ pub async fn get_impl(receiver: ForeignAtom, proto: ForeignAtom) -> OrcRes return Ok(Expr::deserialize(impl_val).await); } return Err(mk_errv( - i().i("Implementation not found").await, + is("Implementation not found").await, "This protocol is not implemented for this receiver", [receiver.pos(), proto.pos()], )); @@ -126,13 +126,8 @@ pub fn gen_protocol_lib() -> Vec { Ok(own_val.value.to_gen().await) } else { Err(mk_errv( - i().i("Type mismatch").await, - format!( - "{} has type {}, expected {}", - fmt(&value, &i()).await, - own_val.tag.id, - own_tag.id - ), + is("Type mismatch").await, + format!("{} has type {}, expected {}", fmt(&value).await, own_val.tag.id, own_tag.id), [value.pos()], )) } diff --git a/orchid-std/src/std/record/record_atom.rs b/orchid-std/src/std/record/record_atom.rs index db09645..16109ef 100644 --- a/orchid-std/src/std/record/record_atom.rs +++ b/orchid-std/src/std/record/record_atom.rs @@ -6,16 +6,15 @@ use futures::AsyncWrite; use futures::future::join_all; use hashbrown::HashMap; use orchid_api_traits::Encode; -use orchid_base::interner::Tok; +use orchid_base::interner::{IStr, es}; use orchid_extension::atom::Atomic; use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant}; -use orchid_extension::context::i; use orchid_extension::expr::Expr; use crate::api; #[derive(Clone)] -pub struct Record(pub Rc, Expr>>); +pub struct Record(pub Rc>); impl Atomic for Record { type Data = (); type Variant = OwnedVariant; @@ -25,13 +24,12 @@ impl OwnedAtom for Record { async fn serialize(&self, write: Pin<&mut (impl AsyncWrite + ?Sized)>) -> Self::Refs { let (keys, values) = self.0.iter().map(|(k, v)| (k.to_api(), v.clone())).unzip::<_, _, Vec<_>, Vec<_>>(); - keys.encode(write).await; + keys.encode(write).await.unwrap(); values } async fn deserialize(mut dctx: impl DeserializeCtx, refs: Self::Refs) -> Self { let keys = - join_all(dctx.decode::>().await.iter().map(|t| async { i().ex(*t).await })) - .await; + join_all(dctx.decode::>().await.iter().map(|t| async { es(*t).await })).await; Record(Rc::new(keys.into_iter().zip(refs).collect())) } diff --git a/orchid-std/src/std/reflection/sym_atom.rs b/orchid-std/src/std/reflection/sym_atom.rs index ebc78f0..9bb8c4e 100644 --- a/orchid-std/src/std/reflection/sym_atom.rs +++ b/orchid-std/src/std/reflection/sym_atom.rs @@ -4,10 +4,10 @@ use orchid_api::TStrv; use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_traits::Request; use orchid_base::error::mk_errv; +use orchid_base::interner::{es, is}; use orchid_base::name::{NameLike, Sym}; use orchid_extension::atom::{Atomic, Supports, TAtom}; use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own}; -use orchid_extension::context::i; use orchid_extension::expr::{Expr, ExprHandle}; use orchid_extension::system::dep_req; use orchid_extension::tree::{GenMember, fun, prefix}; @@ -51,10 +51,10 @@ pub async fn sym_expr(sym: Sym) -> Expr { pub async fn gen_sym_lib() -> Vec { prefix("std::refl::sym", [ fun(true, "from_str", async move |str: TAtom| { - match Sym::parse(&i().ex(*str).await, &i()).await { + match Sym::parse(&es(*str).await).await { Ok(sym) => Ok(SymAtom(sym)), Err(_) => Err(mk_errv( - i().i("Cannot parse sym from empty string").await, + is("Cannot parse sym from empty string").await, "Empty string passed to std::refl::sym::from_str", [str.pos()], )), diff --git a/orchid-std/src/std/std_system.rs b/orchid-std/src/std/std_system.rs index 6d692cf..5cbe2a8 100644 --- a/orchid-std/src/std/std_system.rs +++ b/orchid-std/src/std/std_system.rs @@ -3,12 +3,10 @@ use std::rc::Rc; use futures::future::join_all; use orchid_api_derive::{Coding, Hierarchy}; use orchid_base::name::Sym; -use orchid_base::reqnot::Receipt; +use orchid_base::reqnot::{Receipt, ReqHandle, ReqHandleExt}; use orchid_base::sym; use orchid_extension::atom::{AtomDynfo, AtomicFeatures}; -use orchid_extension::context::i; use orchid_extension::conv::ToExpr; -use orchid_extension::entrypoint::ExtReq; use orchid_extension::expr::Expr; use orchid_extension::lexer::LexerObj; use orchid_extension::parser::ParserObj; @@ -39,14 +37,14 @@ pub enum StdReq { CreateSymAtom(CreateSymAtom), } -#[derive(Default)] +#[derive(Debug, Default)] pub struct StdSystem; impl SystemCtor for StdSystem { type Deps = (); type Instance = Self; const NAME: &'static str = "orchid::std"; const VERSION: f64 = 0.00_01; - fn inst(_: ()) -> Self::Instance { Self } + fn inst(&self, _: ()) -> Self::Instance { Self } } impl SystemCard for StdSystem { type Ctor = Self; @@ -68,16 +66,16 @@ impl SystemCard for StdSystem { } } impl System for StdSystem { - async fn request(xreq: ExtReq<'_>, req: Self::Req) -> Receipt<'_> { + async fn request<'a>(xreq: Box + 'a>, req: Self::Req) -> Receipt<'a> { match req { StdReq::CreateTuple(ref req @ CreateTuple(ref items)) => { let tpl = Tuple(Rc::new(join_all(items.iter().copied().map(Expr::deserialize)).await)); let tk = tpl.to_expr().await.serialize().await; - xreq.handle(req, &tk).await + xreq.reply(req, &tk).await.unwrap() }, StdReq::CreateSymAtom(ref req @ CreateSymAtom(sym_tok)) => { - let sym_atom = SymAtom(Sym::from_api(sym_tok, &i()).await); - xreq.handle(req, &sym_atom.to_expr().await.serialize().await).await + let sym_atom = SymAtom(Sym::from_api(sym_tok).await); + xreq.reply(req, &sym_atom.to_expr().await.serialize().await).await.unwrap() }, } } @@ -94,7 +92,5 @@ impl System for StdSystem { gen_sym_lib().await, ]) } - async fn prelude() -> Vec { - vec![sym!(std; i()), sym!(std::tuple; i()), sym!(std::option; i())] - } + async fn prelude() -> Vec { vec![sym!(std), sym!(std::tuple), sym!(std::option)] } } diff --git a/orchid-std/src/std/string/str_atom.rs b/orchid-std/src/std/string/str_atom.rs index c74c85c..8fdd3a0 100644 --- a/orchid-std/src/std/string/str_atom.rs +++ b/orchid-std/src/std/string/str_atom.rs @@ -8,10 +8,9 @@ use orchid_api_derive::Coding; use orchid_api_traits::{Encode, Request}; use orchid_base::error::{OrcRes, mk_errv}; use orchid_base::format::{FmtCtx, FmtUnit}; -use orchid_base::interner::Tok; +use orchid_base::interner::{IStr, es, is}; use orchid_extension::atom::{AtomMethod, Atomic, MethodSetBuilder, Supports, TAtom}; use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant}; -use orchid_extension::context::i; use orchid_extension::conv::TryFromExpr; use orchid_extension::expr::Expr; @@ -52,7 +51,7 @@ impl OwnedAtom for StrAtom { type Refs = (); async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) } async fn serialize(&self, sink: Pin<&mut (impl AsyncWrite + ?Sized)>) -> Self::Refs { - self.deref().encode(sink).await + self.deref().encode(sink).await.unwrap() } async fn print_atom<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { format!("{:?}", &*self.0).into() @@ -63,36 +62,36 @@ impl OwnedAtom for StrAtom { } #[derive(Debug, Clone)] -pub struct IntStrAtom(pub(crate) Tok); +pub struct IntStrAtom(pub(crate) IStr); impl Atomic for IntStrAtom { type Variant = OwnedVariant; type Data = orchid_api::TStr; } -impl From> for IntStrAtom { - fn from(value: Tok) -> Self { Self(value) } +impl From for IntStrAtom { + fn from(value: IStr) -> Self { Self(value) } } impl OwnedAtom for IntStrAtom { type Refs = (); async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.0.to_api()) } async fn print_atom<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { - format!("{:?}i", *self.0).into() + format!("{:?}i", &*self.0).into() } async fn serialize(&self, write: Pin<&mut (impl AsyncWrite + ?Sized)>) { - self.0.encode(write).await + self.0.encode(write).await.unwrap() } async fn deserialize(mut dctx: impl DeserializeCtx, _: ()) -> Self { let s = dctx.decode::().await; - Self(i().i(&s).await) + Self(is(&s).await) } } impl TryFromExpr for IntStrAtom { async fn try_from_expr(expr: Expr) -> OrcRes { - Ok(IntStrAtom(i().ex(TAtom::::try_from_expr(expr).await?.value).await)) + Ok(IntStrAtom(es(TAtom::::try_from_expr(expr).await?.value).await)) } } impl Supports for IntStrAtom { async fn handle(&self, _: ToStringMethod) -> ::Response { - self.0.as_str().to_string() + self.0.to_string() } } @@ -109,7 +108,7 @@ pub enum OrcStringKind { impl OrcString { pub async fn get_string(&self) -> Rc { match &self.kind { - OrcStringKind::Int(tok) => i().ex(**tok).await.rc(), + OrcStringKind::Int(tok) => es(**tok).await.rc(), OrcStringKind::Val(atom) => atom.request(StringGetVal).await, } } @@ -122,7 +121,7 @@ impl TryFromExpr for OrcString { } match TAtom::::try_from_expr(expr).await { Ok(t) => Ok(OrcString { kind: OrcStringKind::Int(t) }), - Err(e) => Err(mk_errv(i().i("A string was expected").await, "", e.pos_iter())), + Err(e) => Err(mk_errv(is("A string was expected").await, "", e.pos_iter())), } } } diff --git a/orchid-std/src/std/string/str_lexer.rs b/orchid-std/src/std/string/str_lexer.rs index e96ad87..b229a0c 100644 --- a/orchid-std/src/std/string/str_lexer.rs +++ b/orchid-std/src/std/string/str_lexer.rs @@ -1,12 +1,10 @@ use itertools::Itertools; use orchid_base::error::{OrcErr, OrcErrv, OrcRes, mk_errv}; -use orchid_base::interner::Interner; +use orchid_base::interner::is; use orchid_base::location::SrcRange; use orchid_base::name::Sym; -use orchid_base::parse::ParseCtx; use orchid_base::sym; use orchid_base::tree::{Paren, wrap_tokv}; -use orchid_extension::context::i; use orchid_extension::gen_expr::sym_ref; use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable}; use orchid_extension::parser::p_tree2gen; @@ -36,10 +34,10 @@ struct StringError { impl StringError { /// Convert into project error for reporting - pub async fn into_proj(self, path: &Sym, pos: u32, i: &Interner) -> OrcErrv { + pub async fn into_proj(self, path: &Sym, pos: u32) -> OrcErrv { let start = pos + self.pos; mk_errv( - i.i("Failed to parse string").await, + is("Failed to parse string").await, match self.kind { StringErrorKind::NotHex => "Expected a hex digit", StringErrorKind::BadCodePoint => "The specified number is not a Unicode code point", @@ -95,7 +93,7 @@ fn parse_string(str: &str) -> Result { Ok(target) } -#[derive(Default)] +#[derive(Debug, Default)] pub struct StringLexer; impl Lexer for StringLexer { const CHAR_FILTER: &'static [std::ops::RangeInclusive] = &['"'..='"', '`'..='`']; @@ -114,18 +112,16 @@ impl Lexer for StringLexer { ) -> GenTokTree { let str_val_res = parse_string(&str.split_off(0)); if let Err(e) = &str_val_res { - err.extend(e.clone().into_proj(ctx.src(), ctx.pos(tail) - str.len() as u32, ctx.i()).await); + err.extend(e.clone().into_proj(ctx.src(), ctx.pos(tail) - str.len() as u32).await); } let str_val = str_val_res.unwrap_or_default(); - x_tok(IntStrAtom::from(ctx.i().i(&*str_val).await)) - .await - .at(ctx.pos_lt(str.len() as u32, tail)) as GenTokTree + x_tok(IntStrAtom::from(is(&str_val).await)).await.at(ctx.pos_lt(str.len() as u32, tail)) + as GenTokTree } let add_frag = |prev: Option, new: GenTokTree| async { let Some(prev) = prev else { return new }; - let concat_fn = ref_tok(sym!(std::string::concat; lctx.i())) - .await - .at(SrcRange::zw(prev.sr.path(), prev.sr.start())); + let concat_fn = + ref_tok(sym!(std::string::concat)).await.at(SrcRange::zw(prev.sr.path(), prev.sr.start())); wrap_tokv([concat_fn, prev, new]) }; loop { @@ -139,7 +135,7 @@ impl Lexer for StringLexer { let (new_tail, tree) = lctx.recurse(rest).await?; tail = new_tail; // wrap the received token in a call to to_str - let to_str = sym_ref(sym!(std::string::to_str; i())); + let to_str = sym_ref(sym!(std::string::to_str)); let sr = tree.sr(); let inj_to_str_tok = GenTok::NewExpr(to_str).at(sr.map_range(|_| sr.start()..sr.start())); let to_str_call = GenTok::S(Paren::Round, vec![inj_to_str_tok, p_tree2gen(tree)]).at(sr); @@ -154,11 +150,9 @@ impl Lexer for StringLexer { tail = ch.as_str(); } else { let range = lctx.pos(all)..lctx.pos(""); - return Err(mk_errv( - lctx.i().i("No string end").await, - "String never terminated with \"", - [SrcRange::new(range.clone(), lctx.src())], - )); + return Err(mk_errv(is("No string end").await, "String never terminated with \"", [ + SrcRange::new(range.clone(), lctx.src()), + ])); } } } diff --git a/orchid-std/src/std/string/str_lib.rs b/orchid-std/src/std/string/str_lib.rs index 26f21a2..8c7b321 100644 --- a/orchid-std/src/std/string/str_lib.rs +++ b/orchid-std/src/std/string/str_lib.rs @@ -3,7 +3,6 @@ use std::rc::Rc; use orchid_base::format::fmt; use orchid_base::sym; use orchid_extension::atom::ForeignAtom; -use orchid_extension::context::i; use orchid_extension::conv::ToExpr; use orchid_extension::coroutine_exec::exec; use orchid_extension::expr::Expr; @@ -35,13 +34,13 @@ pub fn gen_str_lib() -> Vec { if let Some(str) = atom.request(ToStringMethod).await { return StrAtom::new(Rc::new(str)).to_gen().await; } - let proto_ref = sym_ref(sym!(std::string::to_string::__protocol_tag__; i())); + let proto_ref = sym_ref(sym!(std::string::to_string::__protocol_tag__)); let proto = h.exec(proto_ref).await.expect("This protocol is defined in this system"); if let Ok(cb) = get_impl(atom.clone(), proto).await { return call(cb.to_gen().await, [atom.to_gen().await]).to_gen().await; } } - return StrAtom::new(Rc::new(fmt(&input, &i()).await)).to_gen().await; + return StrAtom::new(Rc::new(fmt(&input).await)).to_gen().await; }) .await }), @@ -50,7 +49,7 @@ pub fn gen_str_lib() -> Vec { cnst(true, "__type_tag__", AsStrTag), fun(true, "resolve", async |atom: ForeignAtom| { exec(async |mut h| { - let proto = h.exec(sym_ref(sym!(std::string::to_string; i()))).await?; + let proto = h.exec(sym_ref(sym!(std::string::to_string))).await?; Ok(call(get_impl(atom.clone(), proto).await?.to_gen().await, [atom.to_gen().await])) }) .await diff --git a/orchid-std/src/std/string/to_string.rs b/orchid-std/src/std/string/to_string.rs index 1ea8fff..023cc35 100644 --- a/orchid-std/src/std/string/to_string.rs +++ b/orchid-std/src/std/string/to_string.rs @@ -3,7 +3,6 @@ use orchid_api_traits::Request; use orchid_base::name::Sym; use orchid_extension::atom::{AtomMethod, Atomic, MethodSetBuilder, Supports}; use orchid_extension::atom_thin::{ThinAtom, ThinVariant}; -use orchid_extension::context::i; use crate::std::protocol::types::{GetImplMethod, GetTagIdMethod}; @@ -19,7 +18,7 @@ impl Atomic for AsStrTag { impl ThinAtom for AsStrTag {} impl Supports for AsStrTag { async fn handle(&self, _: GetTagIdMethod) -> ::Response { - Sym::parse("std::string::to_string", &i()).await.unwrap().to_api() + Sym::parse("std::string::to_string").await.unwrap().to_api() } } impl Supports for AsStrTag { diff --git a/orchid-std/src/std/tuple.rs b/orchid-std/src/std/tuple.rs index 5490b9c..1a9a90d 100644 --- a/orchid-std/src/std/tuple.rs +++ b/orchid-std/src/std/tuple.rs @@ -11,9 +11,9 @@ use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_traits::Request; use orchid_base::error::{OrcRes, mk_errv}; use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants}; +use orchid_base::interner::is; use orchid_extension::atom::{Atomic, TAtom}; use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant, own}; -use orchid_extension::context::i; use orchid_extension::conv::{ToExpr, TryFromExpr}; use orchid_extension::expr::{Expr, ExprHandle}; use orchid_extension::gen_expr::GExpr; @@ -95,7 +95,7 @@ pub fn gen_tuple_lib() -> Vec { return Ok(val.clone()); } return Err(mk_errv( - i().i("Tuple index out of bounds").await, + is("Tuple index out of bounds").await, format!("{} is out of bounds for Tuple{}", idx.0, tup.len()), [idx.pos()], )); @@ -109,7 +109,7 @@ pub fn gen_tuple_lib() -> Vec { } } return Err(mk_errv( - i().i("Tuple index out of bounds").await, + is("Tuple index out of bounds").await, format!("{} is out of bounds for Tuple{}", idx.0, tup.len()), [idx.pos()], )); @@ -145,7 +145,7 @@ pub struct Tpl(pub T); mod tpl_impls { use itertools::Itertools; use orchid_base::error::{OrcRes, mk_errv}; - use orchid_extension::context::i; + use orchid_base::interner::is; use orchid_extension::conv::{ToExpr, TryFromExpr}; use orchid_extension::expr::Expr; use orchid_extension::gen_expr::GExpr; @@ -160,7 +160,7 @@ mod tpl_impls { let tpl = UntypedTuple::try_from_expr(expr.clone()).await?; let Some([$( [< $t:lower >], )*]) = tpl.0.iter().cloned().collect_array() else { return Err(mk_errv( - i().i("Tuple arity mismatch").await, + is("Tuple arity mismatch").await, format!("Expected a {}-ary tuple, found {}-ary", $len, tpl.0.len()), [expr.data().await.pos.clone()] )); diff --git a/orcx/src/main.rs b/orcx/src/main.rs index c2f086e..8172612 100644 --- a/orcx/src/main.rs +++ b/orcx/src/main.rs @@ -3,24 +3,26 @@ pub mod parse_folder; use std::cell::RefCell; use std::fs::File; use std::io::{Read, Write}; -use std::mem; use std::process::{Command, ExitCode}; use std::rc::Rc; use async_fn_stream::try_stream; use camino::Utf8PathBuf; use clap::{Parser, Subcommand}; +use futures::future::LocalBoxFuture; use futures::{FutureExt, Stream, TryStreamExt, io}; use itertools::Itertools; -use orchid_base::error::Reporter; -use orchid_base::format::{FmtCtxImpl, Format, fmt, take_first}; +use orchid_base::error::{try_with_reporter, with_reporter}; +use orchid_base::format::{FmtCtxImpl, Format, fmt, fmt_v, take_first}; +use orchid_base::interner::local_interner::local_interner; +use orchid_base::interner::{is, with_interner}; use orchid_base::location::SrcRange; -use orchid_base::logging::{LogStrategy, Logger}; +use orchid_base::logging::{LogStrategy, Logger, with_logger}; use orchid_base::name::{NameLike, VPath}; use orchid_base::parse::{Import, Snippet}; use orchid_base::sym; use orchid_base::tree::{Token, ttv_fmt}; -use orchid_host::ctx::Ctx; +use orchid_host::ctx::{Ctx, JoinHandle, Spawner}; use orchid_host::execute::{ExecCtx, ExecResult}; use orchid_host::expr::ExprKind; use orchid_host::extension::Extension; @@ -78,84 +80,87 @@ pub enum Commands { fn get_all_extensions<'a>( args: &'a Args, - logger: &'a Logger, - msg_logger: &'a Logger, ctx: &'a Ctx, ) -> impl Stream> + 'a { try_stream(async |mut cx| { for ext_path in args.extension.iter() { let exe = if cfg!(windows) { ext_path.with_extension("exe") } else { ext_path.clone() }; - let init = - ext_command(Command::new(exe.as_os_str()), logger.clone(), msg_logger.clone(), ctx.clone()) - .await?; - cx.emit(Extension::new(init, logger.clone(), msg_logger.clone(), ctx.clone())?).await; + let init = ext_command(Command::new(exe.as_os_str()), ctx.clone()).await?; + cx.emit(Extension::new(init, ctx.clone()).await?).await; } Ok(cx) }) } +struct JoinHandleImpl(tokio::task::JoinHandle<()>); +impl JoinHandle for JoinHandleImpl { + fn abort(&self) { self.0.abort() } + fn join(self: Box) -> LocalBoxFuture<'static, ()> { + Box::pin(async { self.0.await.unwrap() }) + } +} + +struct SpawnerImpl; +impl Spawner for SpawnerImpl { + fn spawn_obj(&self, fut: LocalBoxFuture<'static, ()>) -> Box { + Box::new(JoinHandleImpl(spawn_local(fut))) + } +} + #[tokio::main] async fn main() -> io::Result { + eprintln!("orcx launched"); let exit_code = Rc::new(RefCell::new(ExitCode::SUCCESS)); let local_set = LocalSet::new(); let exit_code1 = exit_code.clone(); + let args = Args::parse(); + let logger = Logger::new(if args.logs { LogStrategy::StdErr } else { LogStrategy::Discard }); + let cx_logger = logger.clone(); + let msg_logger = + Logger::new(if args.msg_logs { LogStrategy::StdErr } else { LogStrategy::Discard }); local_set.spawn_local(async move { - let args = Args::parse(); - let ctx = &Ctx::new(Rc::new(|fut| mem::drop(spawn_local(fut)))); - let i = &ctx.i; - let logger = Logger::new(if args.logs { LogStrategy::StdErr } else { LogStrategy::Discard }); - let msg_logger = - Logger::new(if args.msg_logs { LogStrategy::StdErr } else { LogStrategy::Discard }); - let extensions = get_all_extensions(&args, &logger, &msg_logger, ctx) - .try_collect::>() - .await - .unwrap(); + let ctx = &Ctx::new(msg_logger.clone(), SpawnerImpl); + let extensions = get_all_extensions(&args, ctx).try_collect::>().await.unwrap(); match args.command { Commands::Lex { file } => { let (_, systems) = init_systems(&args.system, &extensions).await.unwrap(); let mut file = File::open(file.as_std_path()).unwrap(); let mut buf = String::new(); file.read_to_string(&mut buf).unwrap(); - let lexemes = lex(i.i(&buf).await, sym!(usercode; i), &systems, ctx).await.unwrap(); - println!("{}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true)) + let lexemes = lex(is(&buf).await, sym!(usercode), &systems, ctx).await.unwrap(); + println!("{}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl::default()).await, true)) }, Commands::Parse { file } => { let (_, systems) = init_systems(&args.system, &extensions).await.unwrap(); let mut file = File::open(file.as_std_path()).unwrap(); let mut buf = String::new(); file.read_to_string(&mut buf).unwrap(); - let lexemes = lex(i.i(&buf).await, sym!(usercode; i), &systems, ctx).await.unwrap(); + let lexemes = lex(is(&buf).await, sym!(usercode), &systems, ctx).await.unwrap(); let Some(first) = lexemes.first() else { println!("File empty!"); return; }; - let reporter = Reporter::new(); - let pctx = HostParseCtxImpl { - rep: &reporter, - systems: &systems, - ctx: ctx.clone(), - src: sym!(usercode; i), - }; + let pctx = HostParseCtxImpl { systems: &systems, ctx: ctx.clone(), src: sym!(usercode) }; let snip = Snippet::new(first, &lexemes); - let ptree = parse_items(&pctx, Substack::Bottom, snip).await.unwrap(); - if let Some(errv) = reporter.errv() { - eprintln!("{errv}"); - *exit_code1.borrow_mut() = ExitCode::FAILURE; - return; - } - if ptree.is_empty() { - eprintln!("File empty only after parsing, but no errors were reported"); - *exit_code1.borrow_mut() = ExitCode::FAILURE; - return; - } - for item in ptree { - println!("{}", take_first(&item.print(&FmtCtxImpl { i }).await, true)) - } + match with_reporter(parse_items(&pctx, Substack::Bottom, snip)).await.unwrap() { + Err(errv) => { + eprintln!("{errv}"); + *exit_code1.borrow_mut() = ExitCode::FAILURE; + }, + Ok(ptree) if ptree.is_empty() => { + eprintln!("File empty only after parsing, but no errors were reported"); + *exit_code1.borrow_mut() = ExitCode::FAILURE; + }, + Ok(ptree) => + for item in ptree { + println!("{}", take_first(&item.print(&FmtCtxImpl::default()).await, true)) + }, + }; }, Commands::Repl => { let mut counter = 0; let mut imports = Vec::new(); - let usercode_path = sym!(usercode; i); + let usercode_path = sym!(usercode); let mut stdin = BufReader::new(stdin()); loop { counter += 1; @@ -164,26 +169,24 @@ async fn main() -> io::Result { std::io::stdout().flush().unwrap(); let mut prompt = String::new(); stdin.read_line(&mut prompt).await.unwrap(); - let name = i.i(&format!("_{counter}")).await; - let path = usercode_path.suffix([name.clone()], i).await; + let name = is(&format!("_{counter}")).await; + let path = usercode_path.suffix([name.clone()]).await; let mut lexemes = - lex(i.i(prompt.trim()).await, path.clone(), &systems, ctx).await.unwrap(); + lex(is(prompt.trim()).await, path.clone(), &systems, ctx).await.unwrap(); let Some(discr) = lexemes.first() else { continue }; if args.logs { - println!("lexed: {}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true)); + println!( + "lexed: {}", + take_first(&ttv_fmt(&lexemes, &FmtCtxImpl::default()).await, true) + ); } let prefix_sr = SrcRange::zw(path.clone(), 0); let process_lexemes = async |lexemes: &[ParsTokTree]| { let snippet = Snippet::new(&lexemes[0], lexemes); - let reporter = Reporter::new(); - let parse_ctx = HostParseCtxImpl { - ctx: ctx.clone(), - rep: &reporter, - src: path.clone(), - systems: &systems[..], - }; - let parse_result = parse_item(&parse_ctx, Substack::Bottom, vec![], snippet).await; - match reporter.merge(parse_result) { + let parse_ctx = + HostParseCtxImpl { ctx: ctx.clone(), src: path.clone(), systems: &systems[..] }; + match try_with_reporter(parse_item(&parse_ctx, Substack::Bottom, vec![], snippet)).await + { Ok(items) => Some(items), Err(e) => { eprintln!("{e}"); @@ -194,7 +197,7 @@ async fn main() -> io::Result { let add_imports = |items: &mut Vec, imports: &[Import]| { items.extend(imports.iter().map(|import| Item::new(import.sr.clone(), import.clone()))); }; - if discr.is_kw(i.i("import").await) { + if discr.is_kw(is("import").await) { let Some(import_lines) = process_lexemes(&lexemes).await else { continue }; imports.extend(import_lines.into_iter().map(|it| match it.kind { ItemKind::Import(imp) => imp, @@ -202,8 +205,8 @@ async fn main() -> io::Result { })); continue; } - if !discr.is_kw(i.i("let").await) { - let prefix = [i.i("export").await, i.i("let").await, name.clone(), i.i("=").await]; + if !discr.is_kw(is("let").await) { + let prefix = [is("export").await, is("let").await, name.clone(), is("=").await]; lexemes.splice(0..0, prefix.map(|n| Token::Name(n).at(prefix_sr.clone()))); } let Some(mut new_lines) = process_lexemes(&lexemes).await else { continue }; @@ -216,29 +219,36 @@ async fn main() -> io::Result { add_imports(&mut new_lines, &imports); imports.push(Import::new(input_sr.clone(), VPath::new(path.segs()), const_name.clone())); let new_module = ParsedModule::new(true, new_lines); - let reporter = Reporter::new(); - root = root.add_parsed(&new_module, path.clone(), &reporter).await; + match with_reporter(root.add_parsed(&new_module, path.clone())).await { + Ok(new) => root = new, + Err(errv) => { + eprintln!("{errv}"); + *exit_code1.borrow_mut() = ExitCode::FAILURE; + return; + }, + } eprintln!("parsed"); let entrypoint = - ExprKind::Const(path.suffix([const_name.clone()], i).await).at(input_sr.pos()); - let mut xctx = ExecCtx::new(ctx.clone(), logger.clone(), root.clone(), entrypoint).await; + ExprKind::Const(path.suffix([const_name.clone()]).await).at(input_sr.pos()); + let mut xctx = ExecCtx::new(root.clone(), entrypoint).await; eprintln!("executed"); xctx.set_gas(Some(1000)); xctx.execute().await; match xctx.result() { - ExecResult::Value(val) => - println!("{const_name} = {}", take_first(&val.print(&FmtCtxImpl { i }).await, false)), + ExecResult::Value(val) => println!( + "{const_name} = {}", + take_first(&val.print(&FmtCtxImpl::default()).await, false) + ), ExecResult::Err(e) => println!("error: {e}"), ExecResult::Gas(_) => println!("Ran out of gas!"), } } }, Commands::ModTree { proj, prefix } => { - let reporter = Reporter::new(); let (mut root, _systems) = init_systems(&args.system, &extensions).await.unwrap(); if let Some(proj_path) = proj { let path = proj_path.into_std_path_buf(); - match parse_folder(&root, path, sym!(src; i), &reporter, ctx.clone()).await { + match try_with_reporter(parse_folder(&root, path, sym!(src), ctx.clone())).await { Ok(r) => root = r, Err(e) => { eprintln!("{e}"); @@ -248,7 +258,7 @@ async fn main() -> io::Result { } } let prefix = match prefix { - Some(pref) => VPath::parse(&pref, i).await, + Some(pref) => VPath::parse(&pref).await, None => VPath::new([]), }; let root_data = root.0.read().await; @@ -265,7 +275,7 @@ async fn main() -> io::Result { } } for (key, mem) in &module.members { - let new_path = path.clone().name_with_suffix(key.clone()).to_sym(&root.ctx.i).await; + let new_path = path.clone().name_with_suffix(key.clone()).to_sym().await; match mem.kind(root.ctx.clone(), &root.consts).await { MemberKind::Module(module) => { println!("{indent}module {key} {{"); @@ -274,20 +284,20 @@ async fn main() -> io::Result { }, MemberKind::Const => { let value = root.consts.get(&new_path).expect("Missing const!"); - println!("{indent}const {key} = {}", fmt(value, &root.ctx.i).await) + println!("{indent}const {key} = {}", fmt(value).await) }, } } } }, Commands::Exec { proj, code } => { - let reporter = Reporter::new(); - let path = sym!(usercode; i); + eprintln!("exec branch"); + let path = sym!(usercode); let prefix_sr = SrcRange::zw(path.clone(), 0); let (mut root, systems) = init_systems(&args.system, &extensions).await.unwrap(); if let Some(proj_path) = proj { let path = proj_path.into_std_path_buf(); - match parse_folder(&root, path, sym!(src; i), &reporter, ctx.clone()).await { + match try_with_reporter(parse_folder(&root, path, sym!(src), ctx.clone())).await { Ok(r) => root = r, Err(e) => { eprintln!("{e}"); @@ -296,22 +306,32 @@ async fn main() -> io::Result { }, } } - let mut lexemes = lex(i.i(code.trim()).await, path.clone(), &systems, ctx).await.unwrap(); - if args.logs { - println!("lexed: {}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true)); - } - let parse_ctx = HostParseCtxImpl { - ctx: ctx.clone(), - rep: &reporter, - src: path.clone(), - systems: &systems[..], + let mut lexemes = match lex(is(code.trim()).await, path.clone(), &systems, ctx).await { + Ok(lexemes) => { + if args.logs { + println!("lexed: {}", fmt_v::(lexemes.iter()).await.join(" ")); + } + lexemes + }, + Err(e) => { + eprintln!("{e}"); + *exit_code1.borrow_mut() = ExitCode::FAILURE; + return; + }, }; - let prefix = - [i.i("export").await, i.i("let").await, i.i("entrypoint").await, i.i("=").await]; + let parse_ctx = + HostParseCtxImpl { ctx: ctx.clone(), src: path.clone(), systems: &systems[..] }; + let prefix = [is("export").await, is("let").await, is("entrypoint").await, is("=").await]; lexemes.splice(0..0, prefix.map(|n| Token::Name(n).at(prefix_sr.clone()))); let snippet = Snippet::new(&lexemes[0], &lexemes); - let parse_res = parse_item(&parse_ctx, Substack::Bottom, vec![], snippet).await; - let entrypoint = match reporter.merge(parse_res) { + let entrypoint = match try_with_reporter(parse_item( + &parse_ctx, + Substack::Bottom, + vec![], + snippet, + )) + .await + { Ok(items) => ParsedModule::new(true, items), Err(e) => { eprintln!("{e}"); @@ -319,22 +339,28 @@ async fn main() -> io::Result { return; }, }; - let reporter = Reporter::new(); - let root = root.add_parsed(&entrypoint, path.clone(), &reporter).await; - let expr = ExprKind::Const(sym!(usercode::entrypoint; i)).at(prefix_sr.pos()); - let mut xctx = ExecCtx::new(ctx.clone(), logger.clone(), root, expr).await; + let root = match with_reporter(root.add_parsed(&entrypoint, path.clone())).await { + Err(e) => { + eprintln!("{e}"); + *exit_code1.borrow_mut() = ExitCode::FAILURE; + return; + }, + Ok(new_root) => new_root, + }; + let expr = ExprKind::Const(sym!(usercode::entrypoint)).at(prefix_sr.pos()); + let mut xctx = ExecCtx::new(root, expr).await; xctx.set_gas(Some(10_000)); xctx.execute().await; match xctx.result() { ExecResult::Value(val) => - println!("{}", take_first(&val.print(&FmtCtxImpl { i }).await, false)), + println!("{}", take_first(&val.print(&FmtCtxImpl::default()).await, false)), ExecResult::Err(e) => println!("error: {e}"), ExecResult::Gas(_) => println!("Ran out of gas!"), } }, } }); - local_set.await; + with_interner(local_interner(), with_logger(cx_logger, local_set)).await; let x = *exit_code.borrow(); Ok(x) } diff --git a/orcx/src/parse_folder.rs b/orcx/src/parse_folder.rs index 658f0a2..6c57491 100644 --- a/orcx/src/parse_folder.rs +++ b/orcx/src/parse_folder.rs @@ -3,7 +3,8 @@ use std::path::{Path, PathBuf}; use futures::FutureExt; use itertools::Itertools; -use orchid_base::error::{OrcRes, Reporter, async_io_err, os_str_to_string}; +use orchid_base::error::{OrcRes, async_io_err, os_str_to_string, report}; +use orchid_base::interner::is; use orchid_base::location::SrcRange; use orchid_base::name::Sym; use orchid_base::parse::Snippet; @@ -16,22 +17,16 @@ use substack::Substack; use tokio::fs::{self, File}; use tokio::io::AsyncReadExt; -pub async fn parse_folder( - root: &Root, - path: PathBuf, - ns: Sym, - rep: &Reporter, - ctx: Ctx, -) -> OrcRes { - let parsed_module = (recur(&path, ns.clone(), rep, ctx).await?) - .expect("Project folder is a single non-orchid file"); - return Ok(root.add_parsed(&parsed_module, ns, rep).await); - async fn recur(path: &Path, ns: Sym, rep: &Reporter, ctx: Ctx) -> OrcRes> { +pub async fn parse_folder(root: &Root, path: PathBuf, ns: Sym, ctx: Ctx) -> OrcRes { + let parsed_module = + (recur(&path, ns.clone(), ctx).await?).expect("Project folder is a single non-orchid file"); + return Ok(root.add_parsed(&parsed_module, ns).await); + async fn recur(path: &Path, ns: Sym, ctx: Ctx) -> OrcRes> { let sr = SrcRange::new(0..0, &ns); if path.is_dir() { let mut items = Vec::new(); let mut stream = match fs::read_dir(path).await { - Err(err) => return Err(async_io_err(err, &ctx.i, [sr]).await), + Err(err) => return Err(async_io_err(err, [sr]).await), Ok(s) => s, }; loop { @@ -39,17 +34,17 @@ pub async fn parse_folder( Ok(Some(ent)) => ent, Ok(None) => break, Err(err) => { - rep.report(async_io_err(err, &ctx.i, [sr.clone()]).await); + report(async_io_err(err, [sr.clone()]).await); continue; }, }; let os_name = entry.path().file_stem().expect("File name could not be read").to_owned(); - let name = ctx.i.i(os_str_to_string(&os_name, &ctx.i, [sr.clone()]).await?).await; - let ns = ns.suffix([name.clone()], &ctx.i).await; + let name = is(os_str_to_string(&os_name, [sr.clone()]).await?).await; + let ns = ns.suffix([name.clone()]).await; let sr = SrcRange::new(0..0, &ns); - match recur(&entry.path(), ns.clone(), rep, ctx.clone()).boxed_local().await { + match recur(&entry.path(), ns.clone(), ctx.clone()).boxed_local().await { Err(e) => { - rep.report(e); + report(e); continue; }, Ok(None) => continue, @@ -59,17 +54,17 @@ pub async fn parse_folder( Ok(Some(ParsedModule::new(false, items))) } else if path.extension() == Some(OsStr::new("orc")) { let mut file = match File::open(path).await { - Err(e) => return Err(async_io_err(e, &ctx.i, [sr]).await), + Err(e) => return Err(async_io_err(e, [sr]).await), Ok(file) => file, }; let mut text = String::new(); if let Err(e) = file.read_to_string(&mut text).await { - return Err(async_io_err(e, &ctx.i, [sr]).await); + return Err(async_io_err(e, [sr]).await); } let systems = ctx.systems.read().await.iter().filter_map(|(_, sys)| sys.upgrade()).collect_vec(); - let lexemes = lex(ctx.i.i(&text).await, ns.clone(), &systems, &ctx).await?; - let hpctx = HostParseCtxImpl { ctx: ctx.clone(), rep, src: ns.clone(), systems: &systems }; + let lexemes = lex(is(&text).await, ns.clone(), &systems, &ctx).await?; + let hpctx = HostParseCtxImpl { ctx: ctx.clone(), src: ns.clone(), systems: &systems }; let Some(fst) = lexemes.first() else { return Ok(Some(ParsedModule::new(false, []))) }; let items = parse_items(&hpctx, Substack::Bottom, Snippet::new(fst, &lexemes)).await?; Ok(Some(ParsedModule::new(true, items)))