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
This commit is contained in:
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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<R: orchid_api_traits::AsyncRead + ?Sized>(
|
||||
mut read: std::pin::Pin<&mut R>
|
||||
) -> Self {
|
||||
#decode
|
||||
) -> std::io::Result<Self> {
|
||||
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<Box<dyn std::future::Future<Output = _>>>).await
|
||||
as std::pin::Pin<Box<dyn std::future::Future<Output = std::io::Result<_>>>>).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<Box<dyn std::future::Future<Output = _>>>).await,
|
||||
as std::pin::Pin<Box<dyn std::future::Future<Output = std::io::Result<_>>>>).await?,
|
||||
}
|
||||
});
|
||||
quote! { ( #( #exprs )* ) }
|
||||
@@ -62,7 +62,7 @@ fn decode_body(data: &syn::Data) -> proc_macro2::TokenStream {
|
||||
quote! { #id => Self::#ident #fields, }
|
||||
});
|
||||
quote! {
|
||||
match <u8 as orchid_api_traits::Decode>::decode(read.as_mut()).await {
|
||||
match <u8 as orchid_api_traits::Decode>::decode(read.as_mut()).await? {
|
||||
#(#opts)*
|
||||
x => panic!("Unrecognized enum kind {x}")
|
||||
}
|
||||
|
||||
@@ -17,8 +17,9 @@ pub fn derive(input: TokenStream) -> TokenStream {
|
||||
async fn encode<W: orchid_api_traits::AsyncWrite + ?Sized>(
|
||||
&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<pm2::TokenStream> {
|
||||
quote! {
|
||||
Self::#ident #dest => {
|
||||
(Box::pin((#i as u8).encode(write.as_mut()))
|
||||
as std::pin::Pin<Box<dyn std::future::Future<Output = _>>>).await;
|
||||
as std::pin::Pin<Box<dyn std::future::Future<Output = std::io::Result<()>>>>).await?;
|
||||
#body
|
||||
}
|
||||
}
|
||||
@@ -61,7 +62,7 @@ fn encode_body(data: &syn::Data) -> Option<pm2::TokenStream> {
|
||||
fn encode_names<T: ToTokens>(names: impl Iterator<Item = T>) -> pm2::TokenStream {
|
||||
quote! { #(
|
||||
(Box::pin(#names .encode(write.as_mut()))
|
||||
as std::pin::Pin<Box<dyn std::future::Future<Output = _>>>).await;
|
||||
as std::pin::Pin<Box<dyn std::future::Future<Output = std::io::Result<()>>>>).await?;
|
||||
)* }
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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<R: AsyncRead + ?Sized>(read: Pin<&mut R>) -> impl Future<Output = Self> + '_;
|
||||
fn decode<R: AsyncRead + ?Sized>(
|
||||
read: Pin<&mut R>,
|
||||
) -> impl Future<Output = io::Result<Self>> + '_;
|
||||
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<W: AsyncWrite + ?Sized>(&self, write: Pin<&mut W>) -> impl Future<Output = ()>;
|
||||
fn encode<W: AsyncWrite + ?Sized>(
|
||||
&self,
|
||||
write: Pin<&mut W>,
|
||||
) -> impl Future<Output = io::Result<()>>;
|
||||
fn encode_vec(&self, vec: &mut Vec<u8>) {
|
||||
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<T: 'static, F: Future<Output = T> + '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<T: 'static>(
|
||||
map: impl AsyncFn(Self) -> T + Clone + 'static,
|
||||
) -> impl AsyncFn(Pin<&mut dyn AsyncRead>) -> io::Result<T> {
|
||||
async move |r| Ok(map(Self::decode(r).await?).await)
|
||||
}
|
||||
}
|
||||
impl<T: Encode + Decode + Clone> Coding for T {}
|
||||
@@ -35,15 +46,15 @@ impl<T: Encode + Decode + Clone> Coding for T {}
|
||||
macro_rules! num_impl {
|
||||
($number:ty) => {
|
||||
impl Decode for $number {
|
||||
async fn decode<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> Self {
|
||||
async fn decode<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> io::Result<Self> {
|
||||
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<W: AsyncWrite + ?Sized>(&self, mut write: Pin<&mut W>) {
|
||||
write.write_all(&self.to_be_bytes()).await.expect("Could not write number")
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&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<R: AsyncRead + ?Sized>(read: Pin<&mut R>) -> Self {
|
||||
Self::new(<$name as Decode>::decode(read).await).unwrap()
|
||||
async fn decode<R: AsyncRead + ?Sized>(read: Pin<&mut R>) -> io::Result<Self> {
|
||||
Self::new(<$name as Decode>::decode(read).await?).ok_or_else(decode_err)
|
||||
}
|
||||
}
|
||||
impl Encode for NonZero<$name> {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, write: Pin<&mut W>) {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, write: Pin<&mut W>) -> io::Result<()> {
|
||||
self.get().encode(write).await
|
||||
}
|
||||
}
|
||||
@@ -86,22 +97,22 @@ nonzero_impl!(i64);
|
||||
nonzero_impl!(i128);
|
||||
|
||||
impl<T: Encode + ?Sized> Encode for &T {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, write: Pin<&mut W>) {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&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<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> Self {
|
||||
async fn decode<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> io::Result<Self> {
|
||||
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<W: AsyncWrite + ?Sized>(&self, mut write: Pin<&mut W>) {
|
||||
write.write_all(&self.as_ref().to_be_bytes()).await.expect("Could not write number")
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&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<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> Self {
|
||||
let len: usize = u64::decode(read.as_mut()).await.try_into().unwrap();
|
||||
async fn decode<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> io::Result<Self> {
|
||||
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<W: AsyncWrite + ?Sized>(&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<W: AsyncWrite + ?Sized>(&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<W: AsyncWrite + ?Sized>(&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<W: AsyncWrite + ?Sized>(&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<T: Decode> Decode for Vec<T> {
|
||||
async fn decode<R: AsyncRead + ?Sized>(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<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> io::Result<Self> {
|
||||
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<T: Encode> Encode for Vec<T> {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, write: Pin<&mut W>) {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, write: Pin<&mut W>) -> io::Result<()> {
|
||||
self.as_slice().encode(write).await
|
||||
}
|
||||
}
|
||||
impl<T: Encode> Encode for [T] {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, mut write: Pin<&mut W>) {
|
||||
u64::try_from(self.len()).unwrap().encode(write.as_mut()).await;
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&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<T: Decode> Decode for Option<T> {
|
||||
async fn decode<R: AsyncRead + ?Sized>(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<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> io::Result<Self> {
|
||||
Ok(match bool::decode(read.as_mut()).await? {
|
||||
false => None,
|
||||
true => Some(T::decode(read).await?),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl<T: Encode> Encode for Option<T> {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&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<W: AsyncWrite + ?Sized>(&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<T: Decode, E: Decode> Decode for Result<T, E> {
|
||||
async fn decode<R: AsyncRead + ?Sized>(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<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> io::Result<Self> {
|
||||
Ok(match bool::decode(read.as_mut()).await? {
|
||||
false => Self::Ok(T::decode(read).await?),
|
||||
true => Self::Err(E::decode(read).await?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode, E: Encode> Encode for Result<T, E> {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, write: Pin<&mut W>) {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&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<T: Encode, E: Encode> Encode for Result<T, E> {
|
||||
}
|
||||
}
|
||||
impl<K: Decode + Eq + Hash, V: Decode> Decode for HashMap<K, V> {
|
||||
async fn decode<R: AsyncRead + ?Sized>(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<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> io::Result<Self> {
|
||||
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<K: Encode + Eq + Hash, V: Encode> Encode for HashMap<K, V> {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&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<W: AsyncWrite + ?Sized>(&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<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> Self {
|
||||
($($T::decode(read.as_mut()).await,)*)
|
||||
async fn decode<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> io::Result<Self> {
|
||||
Ok(($($T::decode(read.as_mut()).await?,)*))
|
||||
}
|
||||
}
|
||||
impl<$($T: Encode),*> Encode for ($($T,)*) {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, mut write: Pin<&mut W>) {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&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<R: AsyncRead + ?Sized>(_: Pin<&mut R>) -> Self {}
|
||||
async fn decode<R: AsyncRead + ?Sized>(_: Pin<&mut R>) -> io::Result<Self> { Ok(()) }
|
||||
}
|
||||
impl Encode for () {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, _: Pin<&mut W>) {}
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, _: Pin<&mut W>) -> io::Result<()> { Ok(()) }
|
||||
}
|
||||
impl Decode for Never {
|
||||
async fn decode<R: AsyncRead + ?Sized>(_: Pin<&mut R>) -> Self {
|
||||
async fn decode<R: AsyncRead + ?Sized>(_: Pin<&mut R>) -> io::Result<Self> {
|
||||
unreachable!("A value of Never cannot exist so it can't have been serialized");
|
||||
}
|
||||
}
|
||||
impl Encode for Never {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, _: Pin<&mut W>) { match *self {} }
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, _: Pin<&mut W>) -> io::Result<()> {
|
||||
match *self {}
|
||||
}
|
||||
}
|
||||
impl Decode for bool {
|
||||
async fn decode<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> Self {
|
||||
async fn decode<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> io::Result<Self> {
|
||||
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<W: AsyncWrite + ?Sized>(&self, mut write: Pin<&mut W>) {
|
||||
write.write_all(&[if *self { 0xffu8 } else { 0u8 }]).await.unwrap()
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, mut write: Pin<&mut W>) -> io::Result<()> {
|
||||
write.write_all(&[if *self { 0xffu8 } else { 0u8 }]).await
|
||||
}
|
||||
}
|
||||
impl<T: Decode, const N: usize> Decode for [T; N] {
|
||||
async fn decode<R: AsyncRead + ?Sized>(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::<Vec<_>>()
|
||||
.await;
|
||||
v.try_into().unwrap_or_else(|_| unreachable!("The length of this stream is statically known"))
|
||||
async fn decode<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> io::Result<Self> {
|
||||
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<T: Encode, const N: usize> Encode for [T; N] {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, mut write: Pin<&mut W>) {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&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<T: Decode> Decode for $name<T> {
|
||||
async fn decode<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> Self {
|
||||
T::decode(read.as_mut()).await $op T::decode(read).await
|
||||
async fn decode<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> io::Result<Self> {
|
||||
Ok(T::decode(read.as_mut()).await? $op T::decode(read).await?)
|
||||
}
|
||||
}
|
||||
impl<T: Encode> Encode for $name<T> {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, mut write: Pin<&mut W>) {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&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<T: Decode> Decode for $name<T> {
|
||||
async fn decode<R: AsyncRead + ?Sized>(read: Pin<&mut R>) -> Self {
|
||||
$name::new(T::decode(read).await)
|
||||
async fn decode<R: AsyncRead + ?Sized>(read: Pin<&mut R>) -> io::Result<Self> {
|
||||
Ok($name::new(T::decode(read).await?))
|
||||
}
|
||||
}
|
||||
impl<T: Encode> Encode for $name<T> {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, write: Pin<&mut W>) {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&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<R: AsyncRead + ?Sized>(read: Pin<&mut R>) -> Self {
|
||||
char::from_u32(u32::decode(read).await).unwrap()
|
||||
async fn decode<R: AsyncRead + ?Sized>(read: Pin<&mut R>) -> io::Result<Self> {
|
||||
char::from_u32(u32::decode(read).await?).ok_or_else(decode_err)
|
||||
}
|
||||
}
|
||||
impl Encode for char {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, write: Pin<&mut W>) {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, write: Pin<&mut W>) -> io::Result<()> {
|
||||
(*self as u32).encode(write).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Output = ()>>(
|
||||
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<W: AsyncWrite + ?Sized>(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<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>, bytes: &'static [u8]) {
|
||||
pub async fn read_exact<R: AsyncRead + ?Sized>(
|
||||
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<u8> {
|
||||
pub fn enc_vec(enc: &impl Encode) -> Vec<u8> {
|
||||
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>) { self.0.store(true, Ordering::Relaxed) }
|
||||
}
|
||||
|
||||
pub fn spin_on<F: Future>(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())
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ pub trait Request: fmt::Debug + Sized + 'static {
|
||||
type Response: fmt::Debug + Coding + 'static;
|
||||
}
|
||||
|
||||
pub async fn respond<R: Request>(_: &R, rep: R::Response) -> Vec<u8> { enc_vec(&rep).await }
|
||||
pub fn respond<R: Request>(_: &R, rep: R::Response) -> Vec<u8> { enc_vec(&rep) }
|
||||
|
||||
pub trait Channel: 'static {
|
||||
type Req: Coding + Sized + 'static;
|
||||
|
||||
@@ -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"
|
||||
|
||||
67
orchid-api/src/binary.rs
Normal file
67
orchid-api/src/binary.rs
Normal file
@@ -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<Box<dyn Future<Output = ()>>>`
|
||||
#[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,
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod binary;
|
||||
mod lexer;
|
||||
pub use lexer::*;
|
||||
mod format;
|
||||
|
||||
@@ -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<R: AsyncRead + ?Sized>(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<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> io::Result<Self> {
|
||||
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<W: AsyncWrite + ?Sized>(&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<W: AsyncWrite + ?Sized>(&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<system::SystemDecl>,
|
||||
}
|
||||
impl Decode for ExtensionHeader {
|
||||
async fn decode<R: AsyncRead + ?Sized>(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<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> io::Result<Self> {
|
||||
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<W: AsyncWrite + ?Sized>(&self, mut write: Pin<&mut W>) {
|
||||
write_exact(write.as_mut(), EXT_INTRO).await;
|
||||
self.name.encode(write.as_mut()).await;
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&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, [])
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::future::LocalBoxFuture;
|
||||
|
||||
use crate::api;
|
||||
|
||||
pub type Spawner = Rc<dyn Fn(LocalBoxFuture<'static, ()>)>;
|
||||
|
||||
/// 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<Vec<u8>>>;
|
||||
}
|
||||
|
||||
pub struct ExtInit {
|
||||
pub header: api::ExtensionHeader,
|
||||
pub port: Box<dyn ExtPort>,
|
||||
}
|
||||
impl ExtInit {
|
||||
pub async fn send(&self, msg: &[u8]) { self.port.send(msg).await }
|
||||
pub async fn recv(&self) -> Option<Vec<u8>> { self.port.recv().await }
|
||||
}
|
||||
impl Deref for ExtInit {
|
||||
type Target = api::ExtensionHeader;
|
||||
fn deref(&self) -> &Self::Target { &self.header }
|
||||
}
|
||||
@@ -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<T>(fut: impl Future<Output = OrcRes<T>>) -> OrcRes<T> {
|
||||
/// 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<T>(fut: impl Future<Output = T>) -> OrcRes<T> {
|
||||
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<T>(fut: impl Future<Output = OrcRes<T>>) -> OrcRes<T> {
|
||||
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<T>(fut: impl Future<Output = OrcRes<T>>) -> OrcRes<T>
|
||||
}
|
||||
|
||||
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<OrcErrv>) {
|
||||
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}"
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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<F: Format + ?Sized, R: Borrow<F>>(
|
||||
v: impl IntoIterator<Item = R>,
|
||||
pub async fn fmt_v<F: Format + ?Sized>(
|
||||
v: impl IntoIterator<Item: Borrow<F>>,
|
||||
) -> impl Iterator<Item = String> {
|
||||
join_all(v.into_iter().map(|f| async move { take_first_fmt(f.borrow()).await })).await.into_iter()
|
||||
}
|
||||
|
||||
@@ -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<str> {}
|
||||
pub trait IStrvHandle: AsRef<[IStr]> {}
|
||||
pub trait IStrHandle: AsRef<str> {
|
||||
fn rc(&self) -> Rc<String>;
|
||||
}
|
||||
pub trait IStrvHandle: AsRef<[IStr]> {
|
||||
fn rc(&self) -> Rc<Vec<IStr>>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct IStr(pub api::TStr, pub Rc<dyn IStrHandle>);
|
||||
@@ -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<String> { 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<Vec<IStr>> { 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<F: Future>(val: Rc<dyn InternerSrv>, fut: F) -> F::Ou
|
||||
}
|
||||
|
||||
fn get_interner() -> Rc<dyn InternerSrv> {
|
||||
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<Self::Borrow> + Eq + Hash + Debug;
|
||||
/// Borrowed version of interned value placed in intern queries to avoid a
|
||||
/// copy
|
||||
type Borrow: ToOwned<Owned = Self::Data> + ?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<Handle<Self>>) -> 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<Handle<Self>>) -> Self::Interned { IStr(t, h) }
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct StrvBranch;
|
||||
impl InternableCard for StrvBranch {
|
||||
type Data = Vec<IStr>;
|
||||
type Token = api::TStrv;
|
||||
type Borrow = [IStr];
|
||||
type Interned = IStrv;
|
||||
fn new_interned(t: Self::Token, h: Rc<Handle<Self>>) -> Self::Interned { IStrv(t, h) }
|
||||
}
|
||||
|
||||
/// Pairs interned data with its internment key
|
||||
#[derive(Debug)]
|
||||
struct Data<B: InternableCard> {
|
||||
token: B::Token,
|
||||
data: Rc<B::Data>,
|
||||
}
|
||||
impl<B: InternableCard> Clone for Data<B> {
|
||||
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<B: InternableCard> {
|
||||
data: Data<B>,
|
||||
parent: Weak<RefCell<IntData<B>>>,
|
||||
}
|
||||
impl IStrHandle for Handle<StrBranch> {
|
||||
fn rc(&self) -> Rc<String> { self.data.data.clone() }
|
||||
}
|
||||
impl AsRef<str> for Handle<StrBranch> {
|
||||
fn as_ref(&self) -> &str { self.data.data.as_ref().as_ref() }
|
||||
}
|
||||
impl IStrvHandle for Handle<StrvBranch> {
|
||||
fn rc(&self) -> Rc<Vec<IStr>> { self.data.data.clone() }
|
||||
}
|
||||
impl AsRef<[IStr]> for Handle<StrvBranch> {
|
||||
fn as_ref(&self) -> &[IStr] { self.data.data.as_ref().as_ref() }
|
||||
}
|
||||
impl<B: InternableCard> Drop for Handle<B> {
|
||||
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<B: InternableCard> {
|
||||
/// 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<Handle<B>>,
|
||||
/// Keys for indexing from either table
|
||||
data: Data<B>,
|
||||
}
|
||||
|
||||
/// Read data from an occupied entry in an interner. The equivalent insert
|
||||
/// command is [insert]
|
||||
fn read<B: InternableCard>(entry: OccupiedEntry<'_, Rec<B>>) -> 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<B: InternableCard>(entry: VacantEntry<'_, Rec<B>>, handle: Rc<Handle<B>>) {
|
||||
entry.insert(Rec { data: handle.data.clone(), handle: Rc::downgrade(&handle) });
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct IntData<B: InternableCard> {
|
||||
by_tok: HashTable<Rec<B>>,
|
||||
by_data: HashTable<Rec<B>>,
|
||||
hasher: DefaultHashBuilder,
|
||||
}
|
||||
impl<B: InternableCard> IntData<B> {
|
||||
fn entry_by_data(&mut self, query: &B::Borrow) -> Entry<'_, Rec<B>> {
|
||||
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<B>> {
|
||||
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<B>,
|
||||
query: &'a B::Borrow,
|
||||
}
|
||||
impl<B: InternableCard> 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<B: InternableCard> 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<B>,
|
||||
token: B::Token,
|
||||
}
|
||||
impl<B: InternableCard> 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::Data>) -> 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<B: InternableCard> 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<B: InternableCard>(Rc<RefCell<IntData<B>>>);
|
||||
impl<B: InternableCard> Int<B> {
|
||||
fn mk_handle(&self, data: Data<B>) -> Rc<Handle<B>> {
|
||||
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<B::Interned, InternError<'a, B>> {
|
||||
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<B::Interned, ExternError<'_, B>> {
|
||||
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<u64> = 0.into();
|
||||
}
|
||||
|
||||
fn with_new_id<T>(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<StrBranch>,
|
||||
strv: Int<StrvBranch>,
|
||||
}
|
||||
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<dyn InternerSrv> { Rc::<LocalInterner>::default() }
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
48
orchid-base/src/localset.rs
Normal file
48
orchid-base/src/localset.rs
Normal file
@@ -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<LocalBoxFuture<'a, Result<(), E>>>,
|
||||
pending: VecDeque<LocalBoxFuture<'a, Result<(), E>>>,
|
||||
}
|
||||
impl<'a, E> LocalSet<'a, E> {
|
||||
pub fn new() -> (UnboundedSender<LocalBoxFuture<'a, Result<(), E>>>, Self) {
|
||||
let (sender, receiver) = unbounded();
|
||||
(sender, Self { receiver, pending: VecDeque::new() })
|
||||
}
|
||||
}
|
||||
impl<E> Future for LocalSet<'_, E> {
|
||||
type Output = Result<(), E>;
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
|
||||
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(())) }
|
||||
}
|
||||
}
|
||||
@@ -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<F: Future>(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!") }
|
||||
|
||||
@@ -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<Vec<u8>> {
|
||||
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)
|
||||
|
||||
@@ -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<Self>) -> LocalBoxFuture<'static, Box<dyn RepReader>>;
|
||||
fn send(self: Box<Self>) -> LocalBoxFuture<'a, io::Result<Box<dyn RepReader<'a> + '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<Self>) -> LocalBoxFuture<'static, ()>;
|
||||
fn finish(self: Box<Self>) -> 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<Self>) -> LocalBoxFuture<'static, ()>;
|
||||
fn finish(self: Box<Self>) -> LocalBoxFuture<'a, io::Result<()>>;
|
||||
}
|
||||
|
||||
/// For initiating outbound requests and notifications
|
||||
pub trait Client {
|
||||
fn start_request(&self) -> LocalBoxFuture<'_, Box<dyn ReqWriter>>;
|
||||
fn start_notif(&self) -> LocalBoxFuture<'_, Box<dyn MsgWriter>>;
|
||||
fn start_request(&self) -> LocalBoxFuture<'_, io::Result<Box<dyn ReqWriter<'_> + '_>>>;
|
||||
fn start_notif(&self) -> LocalBoxFuture<'_, io::Result<Box<dyn MsgWriter<'_> + '_>>>;
|
||||
}
|
||||
|
||||
impl<T: Client + ?Sized> 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<CH: Channel>: Client {
|
||||
async fn request<T: Request + UnderRoot<Root = CH::Req>>(&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<T: Request + UnderRoot<Root: Encode>>(&self, t: T) -> io::Result<T::Response> {
|
||||
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<T: UnderRoot<Root = CH::Notif>>(&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<T: UnderRoot<Root: Encode>>(&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<Self>) -> LocalBoxFuture<'a, Box<dyn ReqHandle<'a> + '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<R: Decode>(&mut self) -> io::Result<R> { R::decode(self.reader()).await }
|
||||
async fn reply<R: Request>(
|
||||
self: Box<Self>,
|
||||
req: impl Evidence<R>,
|
||||
rep: &R::Response,
|
||||
) -> io::Result<Receipt<'a>> {
|
||||
self.finish().await.reply(req, rep).await
|
||||
}
|
||||
async fn start_reply(self: Box<Self>) -> io::Result<Box<dyn RepWriter<'a> + 'a>> {
|
||||
self.finish().await.start_reply().await
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ReqHandle<'a> {
|
||||
fn start_reply(self: Box<Self>) -> LocalBoxFuture<'a, io::Result<Box<dyn RepWriter<'a> + '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<Req: Request>(
|
||||
self: Box<Self>,
|
||||
_: impl Evidence<Req>,
|
||||
rep: &Req::Response,
|
||||
) -> io::Result<Receipt<'a>> {
|
||||
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<Self>) -> LocalBoxFuture<'a, io::Result<Receipt<'a>>>;
|
||||
}
|
||||
|
||||
pub trait MsgReader<'a> {
|
||||
fn reader(&mut self) -> Pin<&mut dyn AsyncRead>;
|
||||
fn finish(self: Box<Self>) -> 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<N: Decode>(mut self: Box<Self>) -> io::Result<N> {
|
||||
let n = N::decode(self.reader()).await;
|
||||
self.finish().await;
|
||||
n
|
||||
}
|
||||
}
|
||||
impl<CH: Channel, T: Client + ?Sized> ClientExt<CH> for T {}
|
||||
|
||||
/// A form of [Evidence] that doesn't require the value to be kept around
|
||||
pub struct Witness<T>(PhantomData<T>);
|
||||
@@ -105,64 +167,52 @@ type IoLock<T> = Rc<Mutex<Pin<Box<T>>>>;
|
||||
type IoGuard<T> = Bound<MutexGuard<'static, Pin<Box<T>>>, IoLock<T>>;
|
||||
|
||||
/// 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<dyn AsyncRead>,
|
||||
write: &'a Mutex<IoRef<dyn AsyncWrite>>,
|
||||
}
|
||||
impl<'a> ReqReader<'a> {
|
||||
/// Access
|
||||
pub fn reader(&mut self) -> Pin<&mut dyn AsyncRead> { self.read.as_mut() }
|
||||
pub async fn read_req<R: Decode>(&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<R: Request>(self, req: impl Evidence<R>, 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<Self>) -> LocalBoxFuture<'a, Box<dyn ReqHandle<'a> + 'a>> {
|
||||
Box::pin(async {
|
||||
Box::new(IoReqHandle { prefix: self.prefix, write: self.write }) as Box<dyn ReqHandle<'a>>
|
||||
})
|
||||
}
|
||||
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<IoRef<dyn AsyncWrite>>,
|
||||
}
|
||||
impl<'a> ReqHandle<'a> {
|
||||
pub async fn reply<Req: Request>(
|
||||
self,
|
||||
_: impl Evidence<Req>,
|
||||
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<Self>) -> LocalBoxFuture<'a, io::Result<Box<dyn RepWriter<'a> + '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<dyn RepWriter<'a>>)
|
||||
})
|
||||
}
|
||||
}
|
||||
pub struct RepWriter<'a> {
|
||||
pub struct IoRepWriter<'a> {
|
||||
write: MutexGuard<'a, IoRef<dyn AsyncWrite>>,
|
||||
}
|
||||
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<Self>) -> LocalBoxFuture<'a, io::Result<Receipt<'a>>> {
|
||||
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<dyn AsyncRead>,
|
||||
}
|
||||
impl<'a> NotifReader<'a> {
|
||||
pub fn reader(&mut self) -> Pin<&mut dyn AsyncRead> { self.read.as_mut() }
|
||||
pub async fn read<N: Decode>(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<Self>) -> LocalBoxFuture<'static, ()> { Box::pin(async {}) }
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -187,14 +237,14 @@ impl IoClient {
|
||||
}
|
||||
}
|
||||
impl Client for IoClient {
|
||||
fn start_notif(&self) -> LocalBoxFuture<'_, Box<dyn MsgWriter>> {
|
||||
fn start_notif(&self) -> LocalBoxFuture<'_, io::Result<Box<dyn MsgWriter<'_> + '_>>> {
|
||||
Box::pin(async {
|
||||
let mut o = self.lock_out().await;
|
||||
0u64.encode(o.as_mut()).await;
|
||||
Box::new(IoNotifWriter { o }) as Box<dyn MsgWriter>
|
||||
0u64.encode(o.as_mut()).await?;
|
||||
Ok(Box::new(IoNotifWriter { o }) as Box<dyn MsgWriter>)
|
||||
})
|
||||
}
|
||||
fn start_request(&self) -> LocalBoxFuture<'_, Box<dyn ReqWriter>> {
|
||||
fn start_request(&self) -> LocalBoxFuture<'_, io::Result<Box<dyn ReqWriter<'_> + '_>>> {
|
||||
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<dyn ReqWriter>
|
||||
id.encode(w.as_mut()).await?;
|
||||
Ok(Box::new(IoReqWriter { reply, w }) as Box<dyn ReqWriter>)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -216,13 +266,15 @@ struct IoReqWriter {
|
||||
reply: oneshot::Receiver<IoGuard<dyn AsyncRead>>,
|
||||
w: IoGuard<dyn AsyncWrite>,
|
||||
}
|
||||
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<Self>) -> LocalBoxFuture<'static, Box<dyn RepReader>> {
|
||||
fn send(self: Box<Self>) -> LocalBoxFuture<'a, io::Result<Box<dyn RepReader<'a> + '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<dyn RepReader>
|
||||
Ok(Box::new(IoRepReader { i }) as Box<dyn RepReader>)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -230,7 +282,7 @@ impl ReqWriter for IoReqWriter {
|
||||
struct IoRepReader {
|
||||
i: IoGuard<dyn AsyncRead>,
|
||||
}
|
||||
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<Self>) -> LocalBoxFuture<'static, ()> { Box::pin(async {}) }
|
||||
}
|
||||
@@ -239,11 +291,10 @@ impl RepReader for IoRepReader {
|
||||
struct IoNotifWriter {
|
||||
o: IoGuard<dyn AsyncWrite>,
|
||||
}
|
||||
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<Self>) -> LocalBoxFuture<'static, ()> {
|
||||
self.destructure();
|
||||
Box::pin(async {})
|
||||
fn finish(mut self: Box<Self>) -> 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<CH: Channel>(
|
||||
pub fn io_comm(
|
||||
o: Rc<Mutex<Pin<Box<dyn AsyncWrite>>>>,
|
||||
i: Mutex<Pin<Box<dyn AsyncRead>>>,
|
||||
notif: impl for<'a> AsyncFn(NotifReader<'a>),
|
||||
req: impl for<'a> AsyncFn(ReqReader<'a>) -> Receipt<'a>,
|
||||
) -> (impl ClientExt<CH>, CommCtx, impl Future<Output = ()>) {
|
||||
notif: impl for<'a> AsyncFn(Box<dyn MsgReader<'a> + 'a>) -> io::Result<()>,
|
||||
req: impl for<'a> AsyncFn(Box<dyn ReqReader<'a> + 'a>) -> io::Result<Receipt<'a>>,
|
||||
) -> (impl Client + 'static, CommCtx, impl Future<Output = io::Result<()>>) {
|
||||
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<CH: Channel>(
|
||||
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::<LocalBoxFuture<()>>::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<Item = Event>>,
|
||||
onsub.map(Event::Sub),
|
||||
fork_stream.as_mut(),
|
||||
onexit.map(|()| Event::Exit),
|
||||
pin!(input_stream) as Pin<&mut dyn Stream<Item = io::Result<Event>>>,
|
||||
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::<TestNotif>().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::<Never>(
|
||||
Rc::new(Mutex::new(Box::pin(in2))),
|
||||
Mutex::new(Box::pin(out2)),
|
||||
async |notif: NotifReader| {
|
||||
received.clone().send(notif.read::<TestNotif>().await).await.unwrap();
|
||||
},
|
||||
async |_| panic!("Should receive notif, not request"),
|
||||
);
|
||||
let (sender, ..) = io_comm::<TestChannel>(
|
||||
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::<Never>(
|
||||
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::<DummyRequest>().await;
|
||||
let val = req.read_req::<DummyRequest>().await?;
|
||||
req.reply(&val, &(val.0 + 1)).await
|
||||
},
|
||||
);
|
||||
let (client, client_ctx, run_client) = io_comm::<TestChannel>(
|
||||
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;
|
||||
|
||||
@@ -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<Pin<Box<dyn Future<Output = ()>>>>,
|
||||
queue: RefCell<VecDeque<Pin<Box<dyn Future<Output = ()>>>>>,
|
||||
}
|
||||
|
||||
task_local! {
|
||||
@@ -23,7 +28,7 @@ pub async fn with_stash<F: Future>(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<F: Future>(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<F: Future + 'static>(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<F: Future<Output = ()> + '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")
|
||||
}
|
||||
|
||||
@@ -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 = [] }
|
||||
|
||||
@@ -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<M: AtomMethod>(&self, m: M) -> Option<M::Response> {
|
||||
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<T: AtomicFeatures>(self) -> Result<TAtom<T>, 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<A: AtomCard> MethodSetBuilder<A> {
|
||||
self.handlers.push((
|
||||
M::NAME,
|
||||
Rc::new(move |a: &A, req: Pin<&mut dyn AsyncRead>, rep: Pin<&mut dyn AsyncWrite>| {
|
||||
async { Supports::<M>::handle(a, M::decode(req).await).await.encode(rep).await }
|
||||
.boxed_local()
|
||||
async {
|
||||
Supports::<M>::handle(a, M::decode(req).await.unwrap()).await.encode(rep).await.unwrap()
|
||||
}
|
||||
.boxed_local()
|
||||
}),
|
||||
));
|
||||
self
|
||||
@@ -182,7 +184,7 @@ impl<A: AtomCard> MethodSetBuilder<A> {
|
||||
pub async fn pack(&self) -> MethodSet<A> {
|
||||
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<A: AtomicFeatures> TAtom<A> {
|
||||
}
|
||||
pub async fn request<M: AtomMethod>(&self, req: M) -> M::Response
|
||||
where A: Supports<M> {
|
||||
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<A: AtomicFeatures> Deref for TAtom<A> {
|
||||
@@ -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<dyn AtomDynfo>, 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)
|
||||
}
|
||||
|
||||
@@ -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<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVariant> for A {
|
||||
fn _factory(self) -> AtomFactory {
|
||||
AtomFactory::new(async move || {
|
||||
let serial = ctx()
|
||||
.get_or_default::<ObjStore>()
|
||||
.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::<A>(ctx().get::<CtedObj>().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::<A>(cted().inst().card());
|
||||
let mut data = enc_vec(&typ_id);
|
||||
self.encode(Pin::<&mut Vec<u8>>::new(&mut data)).await;
|
||||
ctx().get_or_default::<ObjStore>().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::<ObjStore>().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<dyn DynOwnedAtom> {
|
||||
let mut g = ctx().get_or_default::<ObjStore>().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<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
|
||||
fn name(&self) -> &'static str { type_name::<T>() }
|
||||
fn decode<'a>(&'a self, AtomCtx(data, ..): AtomCtx<'a>) -> LocalBoxFuture<'a, Box<dyn Any>> {
|
||||
Box::pin(async {
|
||||
Box::new(<T as AtomCard>::Data::decode(Pin::new(&mut &data[..])).await) as Box<dyn Any>
|
||||
Box::new(<T as AtomCard>::Data::decode_slice(&mut &data[..])) as Box<dyn Any>
|
||||
})
|
||||
}
|
||||
fn call(&self, AtomCtx(_, id): AtomCtx, arg: Expr) -> LocalBoxFuture<'_, GExpr> {
|
||||
@@ -127,7 +129,7 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
|
||||
) -> LocalBoxFuture<'a, Option<Vec<Expr>>> {
|
||||
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<T: Decode>(&mut self) -> T { T::decode(Pin::new(&mut self.0)).await }
|
||||
async fn read<T: Decode>(&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<T: OwnedAtom> DynOwnedAtom for T {
|
||||
fn atom_tid(&self) -> TypeId { TypeId::of::<T>() }
|
||||
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<T: OwnedAtom> DynOwnedAtom for T {
|
||||
}
|
||||
fn dyn_free(self: Box<Self>) -> 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<T: OwnedAtom> DynOwnedAtom for T {
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct ObjStore {
|
||||
pub(crate) next_id: AtomicU64,
|
||||
pub(crate) next_id: RefCell<u64>,
|
||||
pub(crate) objects: RwLock<MemoMap<api::AtomId, Box<dyn DynOwnedAtom>>>,
|
||||
}
|
||||
impl SysCtxEntry for ObjStore {}
|
||||
|
||||
task_local! {
|
||||
static OBJ_STORE: Rc<ObjStore>;
|
||||
}
|
||||
|
||||
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<ObjStore> {
|
||||
OBJ_STORE.try_with(|store| store.clone()).expect("Owned atom store not initialized")
|
||||
}
|
||||
|
||||
pub async fn own<A: OwnedAtom>(typ: &TAtom<A>) -> A {
|
||||
let g = ctx().get_or_default::<ObjStore>().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<A: OwnedAtom>(typ: &TAtom<A>) -> A {
|
||||
}
|
||||
|
||||
pub async fn debug_print_obj_store(show_atoms: bool) {
|
||||
let ctx = ctx();
|
||||
let store = ctx.get_or_default::<ObjStore>();
|
||||
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 {
|
||||
|
||||
@@ -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<A: ThinAtom + Atomic<Variant = ThinVariant>> AtomicFeaturesImpl<ThinVariant> for A {
|
||||
fn _factory(self) -> AtomFactory {
|
||||
AtomFactory::new(async move || {
|
||||
let (id, _) = get_info::<A>(ctx().get::<CtedObj>().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::<A>(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<T: ThinAtom> {
|
||||
}
|
||||
impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
|
||||
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::<T>() }
|
||||
fn name(&self) -> &'static str { type_name::<T>() }
|
||||
fn decode<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>) -> LocalBoxFuture<'a, Box<dyn Any>> {
|
||||
Box::pin(async { Box::new(T::decode(Pin::new(&mut &buf[..])).await) as Box<dyn Any> })
|
||||
Box::pin(async { Box::new(T::decode_slice(&mut &buf[..])) as Box<dyn Any> })
|
||||
}
|
||||
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<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
|
||||
) -> 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<Option<GExpr>>> {
|
||||
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<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
|
||||
write: Pin<&'b mut dyn AsyncWrite>,
|
||||
) -> LocalBoxFuture<'a, Option<Vec<Expr>>> {
|
||||
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:?}");
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<MemoMap<TypeId, Box<dyn Any>>>);
|
||||
impl SysCtx {
|
||||
pub fn new(
|
||||
id: api::SysId,
|
||||
i: Interner,
|
||||
reqnot: ReqNot<api::ExtMsgSet>,
|
||||
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<T: SysCtxEntry>(&self, t: T) -> &Self {
|
||||
assert!(self.0.insert(TypeId::of::<T>(), Box::new(t)), "Key already exists");
|
||||
self
|
||||
}
|
||||
pub fn get_or_insert<T: SysCtxEntry>(&self, f: impl FnOnce() -> T) -> &T {
|
||||
(self.0.get_or_insert_owned(TypeId::of::<T>(), || Box::new(f())).downcast_ref())
|
||||
.expect("Keyed by TypeId")
|
||||
}
|
||||
pub fn get_or_default<T: SysCtxEntry + Default>(&self) -> &T { self.get_or_insert(T::default) }
|
||||
pub fn try_get<T: SysCtxEntry>(&self) -> Option<&T> {
|
||||
Some(self.0.get(&TypeId::of::<T>())?.downcast_ref().expect("Keyed by TypeId"))
|
||||
}
|
||||
pub fn get<T: SysCtxEntry>(&self) -> &T {
|
||||
self.try_get().unwrap_or_else(|| panic!("Context {} missing", type_name::<T>()))
|
||||
}
|
||||
/// Shorthand to get the messaging link
|
||||
pub fn reqnot(&self) -> &ReqNot<api::ExtMsgSet> { self.get::<ReqNot<api::ExtMsgSet>>() }
|
||||
/// Shorthand to get the system ID
|
||||
pub fn sys_id(&self) -> api::SysId { *self.get::<api::SysId>() }
|
||||
/// Spawn a task that will eventually be executed asynchronously
|
||||
pub fn spawn(&self, f: impl Future<Output = ()> + 'static) {
|
||||
(self.get::<Spawner>())(Box::pin(CTX.scope(self.clone(), f)))
|
||||
}
|
||||
/// Shorthand to get the logger
|
||||
pub fn logger(&self) -> &Logger { self.get::<Logger>() }
|
||||
/// Shorthand to get the constructed system object
|
||||
pub fn cted(&self) -> &CtedObj { self.get::<CtedObj>() }
|
||||
}
|
||||
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<api::ExtMsgSet> {}
|
||||
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<F: Future>(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::<Interner>().clone() }
|
||||
|
||||
pub fn mock_ctx() -> SysCtx {
|
||||
let ctx = SysCtx(Rc::default());
|
||||
ctx
|
||||
.add(Logger::new(api::LogStrategy::StdErr))
|
||||
.add(Interner::new_master())
|
||||
.add::<Spawner>(Rc::new(|_| panic!("Cannot fork in test environment")))
|
||||
.add(api::SysId(NonZero::<u16>::MIN));
|
||||
ctx
|
||||
}
|
||||
@@ -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<T: TryFromExpr, U: TryFromExpr> 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 {
|
||||
|
||||
@@ -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<dyn Client>;
|
||||
static CTX: Rc<RefCell<Option<CommCtx>>>;
|
||||
}
|
||||
|
||||
fn get_client() -> Rc<dyn Client> {
|
||||
CLIENT.with(|c| c.expect("Client not set, not running inside a duplex reqnot channel!").clone())
|
||||
fn get_client() -> Rc<dyn Client> { 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<F: Future>(c: Rc<dyn Client>, fut: F) -> F::Output {
|
||||
CLIENT.scope(c, fut).await
|
||||
pub async fn with_comm<F: Future>(c: Rc<dyn Client>, 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: Request + UnderRoot<Root: Encode>>(t: T) -> T::Response {
|
||||
get_client().request(t).await
|
||||
pub async fn request<T: Request + UnderRoot<Root = ExtHostReq>>(t: T) -> T::Response {
|
||||
get_client().request(t).await.unwrap()
|
||||
}
|
||||
|
||||
/// Send a notification through the global client's [ClientExt::notify]
|
||||
pub async fn notify<T: UnderRoot<Root: Encode> + 'static>(t: T) { get_client().notify(t).await }
|
||||
|
||||
pub type ExtReq<'a> = ReqReader<'a, api::ExtMsgSet>;
|
||||
pub type ExtReqNot = ReqNot<api::ExtMsgSet>;
|
||||
|
||||
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<Tok<String>>, LazyMemberFactory),
|
||||
Res,
|
||||
pub async fn notify<T: UnderRoot<Root = ExtHostNotif>>(t: T) {
|
||||
get_client().notify(t).await.unwrap()
|
||||
}
|
||||
|
||||
pub struct SystemRecord {
|
||||
lazy_members: Mutex<HashMap<api::TreeId, MemberRecord>>,
|
||||
ctx: SysCtx,
|
||||
cted: CtedObj,
|
||||
}
|
||||
|
||||
trait_set! {
|
||||
pub trait WithAtomRecordCallback<'a, T> = AsyncFnOnce(
|
||||
Box<dyn AtomDynfo>,
|
||||
AtomTypeId,
|
||||
&'a [u8]
|
||||
) -> T
|
||||
type SystemTable = RefCell<HashMap<api::SysId, Rc<SystemRecord>>>;
|
||||
|
||||
task_local! {
|
||||
static SYSTEM_TABLE: SystemTable;
|
||||
}
|
||||
|
||||
pub async fn with_atom_record<'a, F: Future<Output = SysCtx>, 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::<CtedObj>().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<F: Future>(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<RefCell<Option<Interner>>>,
|
||||
_systems_lock: Rc<RwLock<HashMap<api::SysId, SystemRecord>>>,
|
||||
out_recv: Mutex<Receiver<Vec<u8>>>,
|
||||
out_send: Sender<Vec<u8>>,
|
||||
pub trait ContextModifier: 'static {
|
||||
fn apply<'a>(self: Box<Self>, 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<Vec<u8>>> {
|
||||
Box::pin(async { self.out_recv.lock().await.next().await })
|
||||
impl<F: AsyncFnOnce(LocalBoxFuture<'_, ()>) + 'static> ContextModifier for F {
|
||||
fn apply<'a>(self: Box<Self>, 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::<api::SysId, SystemRecord>::new()));
|
||||
let ext_header = api::ExtensionHeader { name: data.name.to_string(), systems: decls.clone() };
|
||||
let (out_send, in_recv) = channel::<Vec<u8>>(1);
|
||||
let (in_send, out_recv) = channel::<Vec<u8>>(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::<Interner>));
|
||||
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<api::ExtMsgSet>| {
|
||||
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::<api::ExtMsgSet>::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<Box<dyn DynSystemCtor>>,
|
||||
pub context: Vec<Box<dyn ContextModifier>>,
|
||||
}
|
||||
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<dyn MsgReader<'_>>| {
|
||||
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<u8>,
|
||||
}
|
||||
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<Self>) -> LocalBoxFuture<'a, Box<dyn ReqHandle<'a> + 'a>> {
|
||||
Box::pin(async { self as Box<_> })
|
||||
}
|
||||
}
|
||||
impl<'a> ReqHandle<'a> for TrivialReqCycle<'a> {
|
||||
fn start_reply(
|
||||
self: Box<Self>,
|
||||
) -> LocalBoxFuture<'a, io::Result<Box<dyn RepWriter<'a> + '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<Self>,
|
||||
) -> LocalBoxFuture<'a, io::Result<orchid_base::reqnot::Receipt<'a>>>
|
||||
{
|
||||
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<PTokTree> =
|
||||
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<PTokTree> =
|
||||
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<Box<_>>);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Option<HashSet<Rc<ExprHandle>>>>);
|
||||
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>) { 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
orchid-extension/src/ext_port.rs
Normal file
12
orchid-extension/src/ext_port.rs
Normal file
@@ -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<Box<dyn AsyncRead>>,
|
||||
pub output: Pin<Box<dyn AsyncWrite>>,
|
||||
pub log: Pin<Box<dyn AsyncWrite>>,
|
||||
pub spawn: Rc<dyn Fn(LocalBoxFuture<'static, ()>)>,
|
||||
}
|
||||
@@ -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<Expr>) -> LocalBoxFuture<'static, OrcRes<GExpr>> + 'static;
|
||||
@@ -34,9 +36,14 @@ pub trait ExprFunc<I, O>: Clone + 'static {
|
||||
fn apply<'a>(&self, hand: ExecHandle<'a>, v: Vec<Expr>) -> impl Future<Output = OrcRes<GExpr>>;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct FunsCtx(Mutex<HashMap<Sym, FunRecord>>);
|
||||
impl SysCtxEntry for FunsCtx {}
|
||||
task_local! {
|
||||
static FUNS_CTX: RefCell<HashMap<(api::SysId, Sym), FunRecord>>;
|
||||
}
|
||||
|
||||
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<I, O, F: ExprFunc<I, O>>(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::<FunsCtx>().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 }
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<Self::Borrow>;
|
||||
type Borrow: ToOwned<Owned = Self::Data> + ?Sized;
|
||||
type Handle: AsRef<Self::Borrow>;
|
||||
type Interned: Clone;
|
||||
fn mk_interned(t: Self::Token, h: Rc<Self::Handle>) -> Self::Interned;
|
||||
#[derive(Default)]
|
||||
struct ExtInterner {
|
||||
str: Int<StrBranch>,
|
||||
strv: Int<StrvBranch>,
|
||||
}
|
||||
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<Self>;
|
||||
type Interned = IStr;
|
||||
fn mk_interned(t: Self::Token, h: Rc<Self::Handle>) -> Self::Interned { IStr(t, h) }
|
||||
}
|
||||
struct StrvBranch;
|
||||
impl Branch for StrvBranch {
|
||||
type Data = Vec<IStr>;
|
||||
type Token = api::TStrv;
|
||||
type Borrow = [IStr];
|
||||
type Handle = Handle<Self>;
|
||||
type Interned = IStrv;
|
||||
fn mk_interned(t: Self::Token, h: Rc<Self::Handle>) -> Self::Interned { IStrv(t, h) }
|
||||
}
|
||||
|
||||
struct Data<B: Branch> {
|
||||
token: B::Token,
|
||||
data: Rc<B::Data>,
|
||||
}
|
||||
|
||||
struct Handle<B: Branch> {
|
||||
data: Rc<Data<B>>,
|
||||
parent: Weak<RefCell<IntData<B>>>,
|
||||
}
|
||||
impl IStrHandle for Handle<StrBranch> {}
|
||||
impl AsRef<str> for Handle<StrBranch> {
|
||||
fn as_ref(&self) -> &str { self.data.data.as_ref().as_ref() }
|
||||
}
|
||||
impl IStrvHandle for Handle<StrvBranch> {}
|
||||
impl AsRef<[IStr]> for Handle<StrvBranch> {
|
||||
fn as_ref(&self) -> &[IStr] { self.data.data.as_ref().as_ref() }
|
||||
}
|
||||
|
||||
struct Rec<B: Branch> {
|
||||
handle: Weak<B::Handle>,
|
||||
data: Rc<Data<B>>,
|
||||
}
|
||||
|
||||
struct IntData<B: Branch> {
|
||||
by_tok: HashMap<B::Token, Rec<B>>,
|
||||
by_data: HashMap<Rc<B::Data>, Rec<B>>,
|
||||
}
|
||||
impl<B: Branch> IntData<B> {
|
||||
async fn i(&mut self, q: &B::Borrow) -> B::Interned { todo!() }
|
||||
async fn e(&mut self, q: &B::Token) -> B::Interned { todo!() }
|
||||
}
|
||||
|
||||
struct Int<B: Branch>(Rc<RefCell<IntData<B>>>);
|
||||
pub fn new_interner() -> Rc<dyn InternerSrv> { Rc::<ExtInterner>::default() }
|
||||
|
||||
@@ -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<String> {
|
||||
i().i("An error cascading from a recursive call").await
|
||||
}
|
||||
pub async fn ekey_not_applicable() -> Tok<String> {
|
||||
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<String>,
|
||||
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<String>,
|
||||
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<char>];
|
||||
fn lex<'a>(
|
||||
tail: &'a str,
|
||||
@@ -90,7 +80,7 @@ pub trait Lexer: Send + Sync + Sized + Default + 'static {
|
||||
) -> impl Future<Output = OrcRes<(&'a str, GenTokTree)>>;
|
||||
}
|
||||
|
||||
pub trait DynLexer: Send + Sync + 'static {
|
||||
pub trait DynLexer: Debug + Send + Sync + 'static {
|
||||
fn char_filter(&self) -> &'static [RangeInclusive<char>];
|
||||
fn lex<'a>(
|
||||
&self,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::api;
|
||||
use crate::system::{DynSystemCard, SystemCard};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SystemHandle<C: SystemCard> {
|
||||
pub(crate) card: C,
|
||||
pub(crate) id: api::SysId,
|
||||
|
||||
@@ -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<Expr, Never>;
|
||||
@@ -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<dyn FnOnce(ConstCtx) -> LocalBoxFuture<'static, GExpr>>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct ParsedConstCtxEntry {
|
||||
pub(crate) consts: IdStore<BoxConstCallback>,
|
||||
task_local! {
|
||||
static PARSED_CONST_CTX: IdStore<BoxConstCallback>
|
||||
}
|
||||
|
||||
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<Item = &'a Comment>,
|
||||
exported: bool,
|
||||
name: Tok<String>,
|
||||
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<Item = &'a Comment>,
|
||||
exported: bool,
|
||||
name: &Tok<String>,
|
||||
name: &IStr,
|
||||
use_prelude: bool,
|
||||
lines: impl IntoIterator<Item = ParsedLine>,
|
||||
) -> 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::<ParsedConstCtxEntry>().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<String>,
|
||||
pub name: IStr,
|
||||
pub exported: bool,
|
||||
pub kind: ParsedMemKind,
|
||||
}
|
||||
@@ -191,14 +186,14 @@ impl ConstCtx {
|
||||
) -> impl Stream<Item = OrcRes<Sym>> + '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::<ParsedConstCtxEntry>().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
|
||||
}
|
||||
|
||||
@@ -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<bool>,
|
||||
path: VPath,
|
||||
members: MemoMap<Tok<String>, ReflMem>,
|
||||
members: MemoMap<IStr, ReflMem>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ReflMod(Rc<ReflModData>);
|
||||
impl ReflMod {
|
||||
pub fn path(&self) -> &[Tok<String>] { &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<String>) -> Option<ReflMem> {
|
||||
pub async fn get_child(&self, key: &IStr) -> Option<ReflMem> {
|
||||
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<String>]) -> Result<ReflMem, InvalidPathError> {
|
||||
pub async fn get_by_path(&self, path: &[IStr]) -> Result<ReflMem, InvalidPathError> {
|
||||
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<HashMap<api::SysId, ReflMod>>
|
||||
}
|
||||
|
||||
#[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))
|
||||
}
|
||||
|
||||
@@ -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<Item = Option<Box<dyn AtomDynfo>>>;
|
||||
@@ -67,7 +68,7 @@ pub async fn resolv_atom(
|
||||
sys: &(impl DynSystemCard + ?Sized),
|
||||
atom: &api::Atom,
|
||||
) -> Box<dyn AtomDynfo> {
|
||||
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<Output = Vec<GenMember>>;
|
||||
fn lexers() -> Vec<LexerObj>;
|
||||
fn parsers() -> Vec<ParserObj>;
|
||||
fn request(hand: ExtReq<'_>, req: Self::Req) -> impl Future<Output = Receipt<'_>>;
|
||||
fn request<'a>(
|
||||
hand: Box<dyn ReqHandle<'a> + 'a>,
|
||||
req: Self::Req,
|
||||
) -> impl Future<Output = Receipt<'a>>;
|
||||
}
|
||||
|
||||
pub trait DynSystem: Send + Sync + DynSystemCard + 'static {
|
||||
@@ -92,7 +96,7 @@ pub trait DynSystem: Send + Sync + DynSystemCard + 'static {
|
||||
fn dyn_env(&self) -> LocalBoxFuture<'_, Vec<GenMember>>;
|
||||
fn dyn_lexers(&self) -> Vec<LexerObj>;
|
||||
fn dyn_parsers(&self) -> Vec<ParserObj>;
|
||||
fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec<u8>) -> LocalBoxFuture<'a, Receipt<'a>>;
|
||||
fn dyn_request<'a>(&self, hand: Box<dyn ReqReader<'a> + 'a>) -> LocalBoxFuture<'a, Receipt<'a>>;
|
||||
fn card(&self) -> &dyn DynSystemCard;
|
||||
}
|
||||
|
||||
@@ -101,26 +105,41 @@ impl<T: System> DynSystem for T {
|
||||
fn dyn_env(&self) -> LocalBoxFuture<'_, Vec<GenMember>> { Self::env().boxed_local() }
|
||||
fn dyn_lexers(&self) -> Vec<LexerObj> { Self::lexers() }
|
||||
fn dyn_parsers(&self) -> Vec<ParserObj> { Self::parsers() }
|
||||
fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec<u8>) -> LocalBoxFuture<'a, Receipt<'a>> {
|
||||
fn dyn_request<'a>(
|
||||
&self,
|
||||
mut hand: Box<dyn ReqReader<'a> + 'a>,
|
||||
) -> LocalBoxFuture<'a, Receipt<'a>> {
|
||||
Box::pin(async move {
|
||||
Self::request(hand, <Self as SystemCard>::Req::decode(Pin::new(&mut &req[..])).await).await
|
||||
let value = hand.read_req::<<Self as SystemCard>::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<F: Future>(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<A>(foreign: ForeignAtom) -> Result<TAtom<A>, 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::<CtedObj>().inst();
|
||||
let owner = if *ctx.get::<api::SysId>() == 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::<CtedObj>().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::<A>()) {
|
||||
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::<A::Data>().expect("atom decode returned wrong type");
|
||||
Ok(TAtom { value, untyped: foreign })
|
||||
let Ok(value) = val.downcast::<A::Data>() else {
|
||||
panic!("decode of {} returned wrong type.", dynfo.name());
|
||||
};
|
||||
Ok(TAtom { value: *value, untyped: foreign })
|
||||
}
|
||||
|
||||
pub async fn dep_req<Sys: SystemCard, Req: Request + Into<Sys::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::<CtedObj>().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::<Sys>() {
|
||||
ctx.sys_id()
|
||||
sys_id()
|
||||
} else {
|
||||
(ctx.get::<CtedObj>().deps().find(|s| s.get_card().type_id() == TypeId::of::<Sys>()))
|
||||
(cted.deps().find(|s| s.get_card().type_id() == TypeId::of::<Sys>()))
|
||||
.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()
|
||||
}
|
||||
|
||||
@@ -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<Ctor: SystemCtor + ?Sized> {
|
||||
pub deps: <Ctor::Deps as DepDef>::Sat,
|
||||
pub inst: Arc<Ctor::Instance>,
|
||||
@@ -15,7 +17,7 @@ pub struct Cted<Ctor: SystemCtor + ?Sized> {
|
||||
impl<C: SystemCtor + ?Sized> Clone for Cted<C> {
|
||||
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<dyn DynSystem>;
|
||||
@@ -27,11 +29,11 @@ impl<C: SystemCtor + ?Sized> DynCted for Cted<C> {
|
||||
}
|
||||
pub type CtedObj = Arc<dyn DynCted>;
|
||||
|
||||
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: <Self::Deps as DepDef>::Sat) -> Self::Instance;
|
||||
fn inst(&self, deps: <Self::Deps as DepDef>::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<T: SystemCtor> 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::<T> { 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
|
||||
}
|
||||
|
||||
@@ -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::<LocalBoxFuture<()>>::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<Mutex<Compat<Stdout>>> = 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;
|
||||
}
|
||||
|
||||
@@ -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<Expr, GExpr>;
|
||||
impl TokenVariant<api::Expression> 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<api::Expression> for GExpr {
|
||||
|
||||
impl TokenVariant<api::ExprTicket> 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<Item = Vec<GenMember>>) -> (
|
||||
(name.to_string(), kind)
|
||||
}
|
||||
pub fn fun<I, O>(public: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenMember> {
|
||||
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<Item = Vec<GenMember>>) -> Vec<GenMember> {
|
||||
@@ -167,10 +157,10 @@ pub struct GenMember {
|
||||
pub comments: Vec<String>,
|
||||
}
|
||||
impl GenMember {
|
||||
pub async fn into_api(self, tia_cx: &mut impl TreeIntoApiCtx) -> api::Member {
|
||||
let name = i().i::<String>(&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<String>) -> impl TreeIntoApiCtx;
|
||||
pub enum MemberRecord {
|
||||
Gen(Vec<IStr>, LazyMemberFactory),
|
||||
Res,
|
||||
}
|
||||
|
||||
pub struct TreeIntoApiCtxImpl<'a, 'b> {
|
||||
pub basepath: &'a [Tok<String>],
|
||||
pub path: Substack<'a, Tok<String>>,
|
||||
pub lazy_members: &'b mut HashMap<api::TreeId, MemberRecord>,
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct LazyMemberStore(Rc<RefCell<HashMap<api::TreeId, MemberRecord>>>);
|
||||
|
||||
task_local! {
|
||||
static LAZY_MEMBERS: LazyMemberStore;
|
||||
}
|
||||
|
||||
impl TreeIntoApiCtx for TreeIntoApiCtxImpl<'_, '_> {
|
||||
fn push_path(&mut self, seg: Tok<String>) -> 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<Item = IStr>;
|
||||
}
|
||||
|
||||
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<Item = IStr> {
|
||||
self.basepath.iter().cloned().chain(self.path.unreverse())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<u8>) -> Option<Vec<u8>> {
|
||||
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()
|
||||
|
||||
@@ -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<Self>) -> LocalBoxFuture<'static, ()>;
|
||||
}
|
||||
|
||||
pub trait Spawner {
|
||||
fn spawn_obj(&self, fut: LocalBoxFuture<'static, ()>) -> Box<dyn JoinHandle>;
|
||||
}
|
||||
|
||||
pub struct CtxData {
|
||||
pub i: Interner,
|
||||
pub spawn: Spawner,
|
||||
spawner: Rc<dyn Spawner>,
|
||||
pub msg_logs: Logger,
|
||||
pub systems: RwLock<HashMap<api::SysId, WeakSystem>>,
|
||||
pub system_id: RefCell<NonZeroU16>,
|
||||
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<Output = ()> + 'static) -> Box<dyn JoinHandle> {
|
||||
self.spawner.spawn_obj(Box::pin(fut))
|
||||
}
|
||||
#[must_use]
|
||||
pub(crate) async fn system_inst(&self, id: api::SysId) -> Option<System> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String>],
|
||||
mut rel: &[Tok<String>],
|
||||
i: &Interner,
|
||||
) -> Result<VName, AbsPathError> {
|
||||
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<VName, AbsPathError> {
|
||||
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<Mod: Tree>(
|
||||
cwd: &[Tok<String>],
|
||||
cwd: &[IStr],
|
||||
root: &Mod,
|
||||
abs_path: &[Tok<String>],
|
||||
abs_path: &[IStr],
|
||||
pos: Pos,
|
||||
i: &Interner,
|
||||
ctx: &mut Mod::Ctx<'_>,
|
||||
) -> OrcRes<HashSet<Tok<String>>> {
|
||||
) -> OrcRes<HashSet<IStr>> {
|
||||
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<Mod: Tree>(
|
||||
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<Tok<String>>;
|
||||
fn children(&self, public_only: bool) -> HashSet<IStr>;
|
||||
#[must_use]
|
||||
fn child(
|
||||
&self,
|
||||
key: Tok<String>,
|
||||
key: IStr,
|
||||
public_only: bool,
|
||||
ctx: &mut Self::Ctx<'_>,
|
||||
) -> impl Future<Output = ChildResult<'_, Self>>;
|
||||
@@ -135,7 +125,7 @@ pub struct ChildError {
|
||||
pub async fn walk<'a, T: Tree>(
|
||||
root: &'a T,
|
||||
public_only: bool,
|
||||
path: impl IntoIterator<Item = Tok<String>>,
|
||||
path: impl IntoIterator<Item = IStr>,
|
||||
ctx: &mut T::Ctx<'_>,
|
||||
) -> Result<&'a T, ChildError> {
|
||||
let mut cur = root;
|
||||
|
||||
@@ -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<u64>,
|
||||
stack: Vec<ExprGuard>,
|
||||
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;
|
||||
|
||||
@@ -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<api::ExprTicket> 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<api::Expression> 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;
|
||||
|
||||
@@ -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<Box<dyn AsyncWrite>>,
|
||||
pub output: Pin<Box<dyn AsyncRead>>,
|
||||
}
|
||||
|
||||
pub struct ReqPair<R: Request>(R, Sender<R::Response>);
|
||||
|
||||
/// Data held about an Extension. This is refcounted within [Extension]. It's
|
||||
@@ -42,69 +47,47 @@ pub struct ReqPair<R: Request>(R, Sender<R::Response>);
|
||||
pub struct ExtensionData {
|
||||
name: String,
|
||||
ctx: Ctx,
|
||||
reqnot: ReqNot<api::HostMsgSet>,
|
||||
join_ext: Option<Box<dyn JoinHandle>>,
|
||||
client: Rc<dyn Client>,
|
||||
systems: Vec<SystemCtor>,
|
||||
logger: Logger,
|
||||
next_pars: RefCell<NonZeroU64>,
|
||||
exiting_snd: Sender<()>,
|
||||
lex_recur: Mutex<HashMap<api::ParsId, Sender<ReqPair<api::SubLex>>>>,
|
||||
strings: RefCell<HashSet<IStr>>,
|
||||
string_vecs: RefCell<HashSet<IStrv>>,
|
||||
}
|
||||
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<ExtensionData>);
|
||||
impl Extension {
|
||||
pub fn new(init: ExtInit, logger: Logger, msg_logger: Logger, ctx: Ctx) -> io::Result<Self> {
|
||||
pub async fn new(mut init: ExtPort, ctx: Ctx) -> io::Result<Self> {
|
||||
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<ExtensionData>| {
|
||||
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::<api::ExtHostNotif>().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::<String>::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::<api::ExtHostReq>().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: <api::LsModule as Request>::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: <api::LsModule as Request>::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<api::HostMsgSet> { &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<Item = &SystemCtor> { 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<F: Future<Output = Option<api::SubLexed>>>(
|
||||
&self,
|
||||
source: Tok<String>,
|
||||
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)) }
|
||||
|
||||
@@ -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<String>,
|
||||
pub source: &'a IStr,
|
||||
pub path: &'a Sym,
|
||||
pub tail: &'a str,
|
||||
pub sub_trees: &'a mut Vec<Expr>,
|
||||
@@ -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<ParsTokTree> {
|
||||
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<ParsTokTree> {
|
||||
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<ParsTokTree> {
|
||||
})
|
||||
.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<ParsTokTree> {
|
||||
}
|
||||
}
|
||||
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<ParsTokTree> {
|
||||
Ok(ParsTokTree { tok, sr: SrcRange::new(start..ctx.get_pos(), ctx.path) })
|
||||
}
|
||||
|
||||
pub async fn lex(
|
||||
text: Tok<String>,
|
||||
path: Sym,
|
||||
systems: &[System],
|
||||
ctx: &Ctx,
|
||||
) -> OrcRes<Vec<ParsTokTree>> {
|
||||
pub async fn lex(text: IStr, path: Sym, systems: &[System], ctx: &Ctx) -> OrcRes<Vec<ParsTokTree>> {
|
||||
let mut sub_trees = Vec::new();
|
||||
let mut ctx =
|
||||
LexCtx { source: &text, sub_trees: &mut sub_trees, tail: &text[..], systems, path: &path, ctx };
|
||||
|
||||
@@ -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<String>>,
|
||||
path: Substack<'_, IStr>,
|
||||
items: ParsSnippet<'_>,
|
||||
) -> OrcRes<Vec<Item>> {
|
||||
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<String>>,
|
||||
path: Substack<'_, IStr>,
|
||||
comments: Vec<Comment>,
|
||||
item: ParsSnippet<'_>,
|
||||
) -> OrcRes<Vec<Item>> {
|
||||
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<Vec<Import>> {
|
||||
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<Vec<Import>> {
|
||||
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<String>>,
|
||||
path: Substack<'_, IStr>,
|
||||
comments: Vec<Comment>,
|
||||
exported: bool,
|
||||
discr: Tok<String>,
|
||||
discr: IStr,
|
||||
tail: ParsSnippet<'a>,
|
||||
) -> OrcRes<Vec<Item>> {
|
||||
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<String>>,
|
||||
path: Substack<'_, IStr>,
|
||||
tail: ParsSnippet<'a>,
|
||||
) -> OrcRes<(Tok<String>, 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()],
|
||||
));
|
||||
};
|
||||
|
||||
@@ -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<Variants>: 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<Variants>: 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<Variants>: Rc::new(Variants::default().bounded("{0}\n{1}")))
|
||||
@@ -69,14 +69,14 @@ impl Format for Item {
|
||||
}
|
||||
|
||||
pub struct ParsedMember {
|
||||
pub name: Tok<String>,
|
||||
pub name: IStr,
|
||||
pub exported: bool,
|
||||
pub kind: ParsedMemberKind,
|
||||
}
|
||||
impl ParsedMember {
|
||||
#[must_use]
|
||||
pub fn name(&self) -> Tok<String> { self.name.clone() }
|
||||
pub fn new(exported: bool, name: Tok<String>, kind: impl Into<ParsedMemberKind>) -> Self {
|
||||
pub fn name(&self) -> IStr { self.name.clone() }
|
||||
pub fn new(exported: bool, name: IStr, kind: impl Into<ParsedMemberKind>) -> Self {
|
||||
Self { exported, name, kind: kind.into() }
|
||||
}
|
||||
}
|
||||
@@ -89,17 +89,14 @@ impl Debug for ParsedMember {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type ParsedExprCallback =
|
||||
Rc<dyn for<'a> Fn(&'a [Tok<String>]) -> LocalBoxFuture<'a, Expr>>;
|
||||
pub(crate) type ParsedExprCallback = Rc<dyn for<'a> 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<String>]) -> 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<ParsedModule> for ParsedMemberKind {
|
||||
}
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ParsedModule {
|
||||
pub exports: Vec<Tok<String>>,
|
||||
pub exports: Vec<IStr>,
|
||||
pub items: Vec<Item>,
|
||||
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<String>, 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<String>,
|
||||
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<Tok<String>> {
|
||||
fn children(&self, public_only: bool) -> HashSet<IStr> {
|
||||
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<Vec<Tok<String>>>,
|
||||
steps: IStrv,
|
||||
}
|
||||
impl ConstPath {
|
||||
#[must_use]
|
||||
pub fn to_const(steps: Tok<Vec<Tok<String>>>) -> 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 {
|
||||
|
||||
@@ -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<ExtInit> {
|
||||
pub async fn ext_command(cmd: std::process::Command, ctx: Ctx) -> io::Result<ExtPort> {
|
||||
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<Option<Child>>,
|
||||
stdin: Option<Mutex<Pin<Box<ChildStdin>>>>,
|
||||
stdout: Mutex<Pin<Box<ChildStdout>>>,
|
||||
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<Vec<u8>>> {
|
||||
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) })
|
||||
}
|
||||
|
||||
@@ -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<String>>;
|
||||
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<api::ParsedLine>,
|
||||
module: Substack<'_, Tok<String>>,
|
||||
callback: &'_ mut impl AsyncFnMut(Substack<'_, Tok<String>>, Vec<ParsTokTree>) -> OrcRes<Vec<Item>>,
|
||||
module: Substack<'_, IStr>,
|
||||
callback: &'_ mut impl AsyncFnMut(Substack<'_, IStr>, Vec<ParsTokTree>) -> OrcRes<Vec<Item>>,
|
||||
ctx: &mut ConvCtx<'_>,
|
||||
) -> OrcRes<Vec<Item>> {
|
||||
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 }),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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<Tok<String>>,
|
||||
line_types: Vec<IStr>,
|
||||
prelude: Vec<Sym>,
|
||||
owned_atoms: RwLock<HashMap<api::AtomId, WeakAtomHand>>,
|
||||
pub(crate) const_paths: MemoMap<api::ParsedConstId, Sym>,
|
||||
@@ -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<api::HostMsgSet> { 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<Sym> { 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<F: Future<Output = Option<api::SubLexed>>>(
|
||||
&self,
|
||||
source: Tok<String>,
|
||||
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<String>) -> Option<Parser> {
|
||||
pub fn get_parser(&self, ltyp: IStr) -> Option<Parser> {
|
||||
(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<Item = &Tok<String>> + '_ { self.0.line_types.iter() }
|
||||
pub fn line_types(&self) -> impl Iterator<Item = &IStr> + '_ { self.0.line_types.iter() }
|
||||
|
||||
#[must_use]
|
||||
pub async fn request(&self, req: Vec<u8>) -> Vec<u8> {
|
||||
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<u8>, 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<String>]) -> OrcRes<VName> + use<> {
|
||||
) -> impl AsyncFnMut(&[IStr]) -> OrcRes<VName> + 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<f64> { 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(),
|
||||
}));
|
||||
|
||||
@@ -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<Sym, Expr>,
|
||||
pub path: Tok<Vec<Tok<String>>>,
|
||||
pub path: IStrv,
|
||||
}
|
||||
impl<'a> TreeFromApiCtx<'a> {
|
||||
#[must_use]
|
||||
pub async fn push<'c>(&'c self, name: Tok<String>) -> 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<Tok<String>, Result<ResolvedImport, Vec<ResolvedImport>>>,
|
||||
pub members: HashMap<Tok<String>, Rc<Member>>,
|
||||
pub imports: HashMap<IStr, Result<ResolvedImport, Vec<ResolvedImport>>>,
|
||||
pub members: HashMap<IStr, Rc<Member>>,
|
||||
}
|
||||
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<Sym, Expr>,
|
||||
deferred_consts: &'a mut HashMap<Sym, (api::SysId, api::ParsedConstId)>,
|
||||
@@ -395,7 +390,7 @@ impl Tree for Module {
|
||||
type Ctx<'a> = (Ctx, &'a MemoMap<Sym, Expr>);
|
||||
async fn child(
|
||||
&self,
|
||||
key: Tok<String>,
|
||||
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<Tok<String>> {
|
||||
fn children(&self, public_only: bool) -> hashbrown::HashSet<IStr> {
|
||||
self.members.iter().filter(|(_, v)| !public_only || v.public).map(|(k, _)| k.clone()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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::<TAtom<MacTree>>(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 {
|
||||
|
||||
@@ -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<Comment>,
|
||||
line: PSnippet<'a>,
|
||||
) -> OrcRes<Vec<ParsedLine>> {
|
||||
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<MacTree> {
|
||||
stream::iter(line).filter_map(|tt| parse_tok(tt, ctx)).collect::<Vec<_>>().await
|
||||
async fn parse_tokv_no_lambdas(line: &[PTokTree]) -> Vec<MacTree> {
|
||||
stream::iter(line).filter_map(parse_tok).collect::<Vec<_>>().await
|
||||
}
|
||||
|
||||
pub async fn parse_tok(tree: &PTokTree, ctx: &impl ParseCtx) -> Option<MacTree> {
|
||||
pub async fn parse_tok(tree: &PTokTree) -> Option<MacTree> {
|
||||
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::<PhAtom>::try_from_expr(expr.clone()).await {
|
||||
@@ -117,8 +111,7 @@ pub async fn parse_tok(tree: &PTokTree, ctx: &impl ParseCtx) -> Option<MacTree>
|
||||
},
|
||||
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()))
|
||||
}
|
||||
|
||||
@@ -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<GenMember> {
|
||||
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", ","])
|
||||
|
||||
@@ -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<Vec<ParsedLine>> {
|
||||
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::<Int>::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::<Vec<_>>()
|
||||
.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::<Vec<_>>(),
|
||||
)
|
||||
.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
|
||||
}))
|
||||
|
||||
@@ -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<StdSystem>) -> Self::Instance { Self }
|
||||
fn inst(&self, _: SystemHandle<StdSystem>) -> 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<dyn ReqHandle<'a> + 'a>, req: Never) -> Receipt<'a> { match req {} }
|
||||
async fn prelude() -> Vec<Sym> {
|
||||
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<LexerObj> { vec![&MacTreeLexer, &PhLexer] }
|
||||
|
||||
@@ -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<MacroData>);
|
||||
pub struct Rule {
|
||||
pub pattern: MacTreeSeq,
|
||||
pub matcher: Matcher,
|
||||
pub ph_names: Vec<Tok<String>>,
|
||||
pub body: Tok<String>,
|
||||
pub ph_names: Vec<IStr>,
|
||||
pub body: IStr,
|
||||
}
|
||||
impl Atomic for Macro {
|
||||
type Data = ();
|
||||
|
||||
@@ -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<String>,
|
||||
pub name: IStr,
|
||||
pub kind: PhKind,
|
||||
}
|
||||
impl Display for Ph {
|
||||
|
||||
@@ -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<char>] = &['\''..='\''];
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Item = Sym> {
|
||||
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<GenMember> {
|
||||
},
|
||||
),
|
||||
fun(true, "matcher", async |names: HomoTpl<TAtom<SymAtom>>, 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<GenMember> {
|
||||
async |[value, rules]| {
|
||||
exec(async move |mut h| {
|
||||
let rule_lines = h
|
||||
.exec::<TAtom<Tuple>>(call(sym_ref(sym!(macros::resolve; i())), [
|
||||
.exec::<TAtom<Tuple>>(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<GenMember> {
|
||||
))
|
||||
.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<GenMember> {
|
||||
async |[pattern, mut value]| {
|
||||
exec(async move |mut h| -> OrcRes<Tpl<(TAtom<MatcherAtom>, GExpr)>> {
|
||||
let Ok(pat) = h
|
||||
.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve; i())), [
|
||||
.exec::<TAtom<MatcherAtom>>(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<GenMember> {
|
||||
.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(),
|
||||
|
||||
@@ -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<char>] = &['$'..='$', '.'..='.'];
|
||||
@@ -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::<String>(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))))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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::<TAtom<Macro>>(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())
|
||||
}
|
||||
|
||||
@@ -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<String>, 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<ScalMatcher> {
|
||||
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<ScalMatcher> {
|
||||
|
||||
#[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,
|
||||
];
|
||||
|
||||
@@ -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 => &[],
|
||||
|
||||
@@ -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<AnyMatcher>),
|
||||
Placeh { key: Tok<String> },
|
||||
Placeh { key: IStr },
|
||||
}
|
||||
|
||||
pub enum VecMatcher {
|
||||
Placeh {
|
||||
key: Tok<String>,
|
||||
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<Tok<String>>,
|
||||
key_order: Vec<IStr>,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Tok<String>, StateEntry<'a>>,
|
||||
placeholders: HashMap<IStr, StateEntry<'a>>,
|
||||
name_posv: HashMap<Sym, Vec<Pos>>,
|
||||
}
|
||||
impl<'a> MatchState<'a> {
|
||||
pub fn from_ph(key: Tok<String>, 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<String>) -> Option<usize> {
|
||||
pub fn ph_len(&self, key: &IStr) -> Option<usize> {
|
||||
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<Item = (Sym, &[Pos])> {
|
||||
self.name_posv.iter().map(|(sym, vec)| (sym.clone(), &vec[..]))
|
||||
}
|
||||
pub fn get(&self, key: &Tok<String>) -> Option<&StateEntry<'a>> { self.placeholders.get(key) }
|
||||
pub fn remove(&mut self, name: Tok<String>) -> Option<StateEntry<'a>> {
|
||||
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<StateEntry<'a>> { 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<Tok<String>, OwnedEntry>,
|
||||
placeholders: HashMap<IStr, OwnedEntry>,
|
||||
name_posv: HashMap<Sym, Vec<Pos>>,
|
||||
}
|
||||
impl OwnedState {
|
||||
pub fn get(&self, key: &Tok<String>) -> 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[..]) }
|
||||
}
|
||||
|
||||
@@ -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<String>, 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)),
|
||||
|
||||
@@ -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<GenMember> {
|
||||
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<GenMember> {
|
||||
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<GenMember> {
|
||||
.rule(mactreev!(std::tuple::t [ "...$" elements 0 ]), [|[elements]: [_; _]| {
|
||||
exec(async move |mut h| {
|
||||
let tup = h
|
||||
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym_ref(sym!(macros::resolve; i())), [
|
||||
.exec::<HomoTpl<TAtom<MacTree>>>(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<GenMember> {
|
||||
fn parse_tpl(elements: MacTree, tail_matcher: Option<MacTree>) -> impl Future<Output = GExpr> {
|
||||
exec(async move |mut h| -> OrcRes<MatcherAtom> {
|
||||
let tup = h
|
||||
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym_ref(sym!(macros::resolve; i())), [
|
||||
.exec::<HomoTpl<TAtom<MacTree>>>(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<MacTree>) -> impl Future<Ou
|
||||
for mac_a in &tup.0[..] {
|
||||
let mac = own(mac_a).await;
|
||||
let sub = h
|
||||
.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve; i())), [
|
||||
.exec::<TAtom<MatcherAtom>>(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<MacTree>) -> impl Future<Ou
|
||||
}
|
||||
let tail_matcher = match tail_matcher {
|
||||
Some(mac) => Some(
|
||||
h.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve; i())), [
|
||||
h.exec::<TAtom<MatcherAtom>>(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<MacTree>) -> impl Future<Ou
|
||||
.chain(stream::iter(&tail_matcher).flat_map(|mat| mat.keys()))
|
||||
.collect()
|
||||
.await,
|
||||
matcher: call(sym_ref(sym!(std::tuple::matcher_body; i())), [
|
||||
matcher: call(sym_ref(sym!(std::tuple::matcher_body)), [
|
||||
HomoTpl(subs).to_gen().await,
|
||||
OrcOpt(tail_matcher).to_gen().await,
|
||||
])
|
||||
@@ -162,8 +161,8 @@ fn tuple_matcher_body(
|
||||
None => (),
|
||||
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? {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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<GetTagIdMethod> for Int {
|
||||
async fn handle(&self, _: GetTagIdMethod) -> <GetTagIdMethod as Request>::Response {
|
||||
Sym::parse("std::number::Int", &i()).await.unwrap().to_api()
|
||||
Sym::parse("std::number::Int").await.unwrap().to_api()
|
||||
}
|
||||
}
|
||||
impl Supports<ToStringMethod> for Int {
|
||||
|
||||
@@ -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<char>] = &['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))))
|
||||
}
|
||||
|
||||
@@ -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::<bool>().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<T: TryFromExpr> TryFromExpr for OrcOpt<T> {
|
||||
impl<T: ToExpr + 'static> ToExpr for OrcOpt<T> {
|
||||
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<GenMember> {
|
||||
fun(true, "expect", async |opt: ForeignAtom, msg: OrcString| {
|
||||
match OrcOpt::try_from_expr(opt.clone().ex()).await? {
|
||||
OrcOpt(Some(ex)) => Ok::<Expr, _>(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(),
|
||||
])),
|
||||
}
|
||||
}),
|
||||
])
|
||||
|
||||
@@ -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<ParsedLine>,
|
||||
impls: &mut Vec<(Sym, Tok<String>)>,
|
||||
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),
|
||||
])),
|
||||
});
|
||||
|
||||
@@ -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<Comment>,
|
||||
line: PSnippet<'a>,
|
||||
) -> OrcRes<Vec<ParsedLine>> {
|
||||
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<Comment>,
|
||||
line: PSnippet<'a>,
|
||||
) -> OrcRes<Vec<ParsedLine>> {
|
||||
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()]));
|
||||
};
|
||||
|
||||
@@ -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<Comment>,
|
||||
line: PSnippet<'a>,
|
||||
) -> OrcRes<Vec<ParsedLine>> {
|
||||
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<Comment>,
|
||||
line: PSnippet<'a>,
|
||||
) -> OrcRes<Vec<ParsedLine>> {
|
||||
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()]));
|
||||
};
|
||||
|
||||
@@ -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<GetImplMethod> for Tag {
|
||||
async fn handle(&self, req: GetImplMethod) -> <GetImplMethod as Request>::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<GetImplMethod> for Tagged {
|
||||
|
||||
pub async fn get_impl(receiver: ForeignAtom, proto: ForeignAtom) -> OrcRes<Expr> {
|
||||
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<Expr>
|
||||
}
|
||||
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<Expr>
|
||||
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<GenMember> {
|
||||
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()],
|
||||
))
|
||||
}
|
||||
|
||||
@@ -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<HashMap<Tok<String>, Expr>>);
|
||||
pub struct Record(pub Rc<HashMap<IStr, Expr>>);
|
||||
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::<Vec<api::TStr>>().await.iter().map(|t| async { i().ex(*t).await }))
|
||||
.await;
|
||||
join_all(dctx.decode::<Vec<api::TStr>>().await.iter().map(|t| async { es(*t).await })).await;
|
||||
Record(Rc::new(keys.into_iter().zip(refs).collect()))
|
||||
}
|
||||
|
||||
|
||||
@@ -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<GenMember> {
|
||||
prefix("std::refl::sym", [
|
||||
fun(true, "from_str", async move |str: TAtom<IntStrAtom>| {
|
||||
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()],
|
||||
)),
|
||||
|
||||
@@ -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<dyn ReqHandle<'a> + '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<Sym> {
|
||||
vec![sym!(std; i()), sym!(std::tuple; i()), sym!(std::option; i())]
|
||||
}
|
||||
async fn prelude() -> Vec<Sym> { vec![sym!(std), sym!(std::tuple), sym!(std::option)] }
|
||||
}
|
||||
|
||||
@@ -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<String>);
|
||||
pub struct IntStrAtom(pub(crate) IStr);
|
||||
impl Atomic for IntStrAtom {
|
||||
type Variant = OwnedVariant;
|
||||
type Data = orchid_api::TStr;
|
||||
}
|
||||
impl From<Tok<String>> for IntStrAtom {
|
||||
fn from(value: Tok<String>) -> Self { Self(value) }
|
||||
impl From<IStr> 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::<String>().await;
|
||||
Self(i().i(&s).await)
|
||||
Self(is(&s).await)
|
||||
}
|
||||
}
|
||||
impl TryFromExpr for IntStrAtom {
|
||||
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
||||
Ok(IntStrAtom(i().ex(TAtom::<IntStrAtom>::try_from_expr(expr).await?.value).await))
|
||||
Ok(IntStrAtom(es(TAtom::<IntStrAtom>::try_from_expr(expr).await?.value).await))
|
||||
}
|
||||
}
|
||||
impl Supports<ToStringMethod> for IntStrAtom {
|
||||
async fn handle(&self, _: ToStringMethod) -> <ToStringMethod as Request>::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<String> {
|
||||
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::<IntStrAtom>::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())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String, StringError> {
|
||||
Ok(target)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct StringLexer;
|
||||
impl Lexer for StringLexer {
|
||||
const CHAR_FILTER: &'static [std::ops::RangeInclusive<char>] = &['"'..='"', '`'..='`'];
|
||||
@@ -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<GenTokTree>, 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()),
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<GenMember> {
|
||||
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<GenMember> {
|
||||
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
|
||||
|
||||
@@ -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<GetTagIdMethod> for AsStrTag {
|
||||
async fn handle(&self, _: GetTagIdMethod) -> <GetTagIdMethod as Request>::Response {
|
||||
Sym::parse("std::string::to_string", &i()).await.unwrap().to_api()
|
||||
Sym::parse("std::string::to_string").await.unwrap().to_api()
|
||||
}
|
||||
}
|
||||
impl Supports<GetImplMethod> for AsStrTag {
|
||||
|
||||
@@ -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<GenMember> {
|
||||
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<GenMember> {
|
||||
}
|
||||
}
|
||||
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<T>(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()]
|
||||
));
|
||||
|
||||
216
orcx/src/main.rs
216
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<Item = io::Result<Extension>> + '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<Self>) -> LocalBoxFuture<'static, ()> {
|
||||
Box::pin(async { self.0.await.unwrap() })
|
||||
}
|
||||
}
|
||||
|
||||
struct SpawnerImpl;
|
||||
impl Spawner for SpawnerImpl {
|
||||
fn spawn_obj(&self, fut: LocalBoxFuture<'static, ()>) -> Box<dyn JoinHandle> {
|
||||
Box::new(JoinHandleImpl(spawn_local(fut)))
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> io::Result<ExitCode> {
|
||||
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::<Vec<Extension>>()
|
||||
.await
|
||||
.unwrap();
|
||||
let ctx = &Ctx::new(msg_logger.clone(), SpawnerImpl);
|
||||
let extensions = get_all_extensions(&args, ctx).try_collect::<Vec<Extension>>().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<ExitCode> {
|
||||
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<ExitCode> {
|
||||
let add_imports = |items: &mut Vec<Item>, 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<ExitCode> {
|
||||
}));
|
||||
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<ExitCode> {
|
||||
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<ExitCode> {
|
||||
}
|
||||
}
|
||||
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<ExitCode> {
|
||||
}
|
||||
}
|
||||
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<ExitCode> {
|
||||
},
|
||||
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<ExitCode> {
|
||||
},
|
||||
}
|
||||
}
|
||||
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::<ParsTokTree>(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<ExitCode> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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<Root> {
|
||||
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<Option<ParsedModule>> {
|
||||
pub async fn parse_folder(root: &Root, path: PathBuf, ns: Sym, ctx: Ctx) -> OrcRes<Root> {
|
||||
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<Option<ParsedModule>> {
|
||||
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)))
|
||||
|
||||
Reference in New Issue
Block a user