From 0b2b05d44ea5c999ad0d965069fae7ec2f118fe5 Mon Sep 17 00:00:00 2001 From: Lawrence Bethlenfalvy Date: Sun, 14 Dec 2025 17:17:43 +0100 Subject: [PATCH] Orchid-base uses task-local context. Everything else is broken at the moment. --- Cargo.lock | 2 + orchid-api-traits/src/relations.rs | 1 - orchid-api/src/tree.rs | 3 +- orchid-base/Cargo.toml | 1 + orchid-base/src/error.rs | 91 +++-- orchid-base/src/format.rs | 20 +- orchid-base/src/interner.rs | 315 +---------------- orchid-base/src/lib.rs | 1 - orchid-base/src/location.rs | 35 +- orchid-base/src/name.rs | 234 ++++++------ orchid-base/src/number.rs | 11 +- orchid-base/src/parse.rs | 106 ++---- orchid-base/src/pipe.rs | 1 - orchid-base/src/reqnot.rs | 548 ++++++++--------------------- orchid-base/src/tree.rs | 41 +-- orchid-base/src/virt_fs/common.rs | 102 +++--- orchid-base/src/virt_fs/decl.rs | 12 +- orchid-base/src/virt_fs/dir.rs | 6 +- orchid-base/src/virt_fs/embed.rs | 4 +- orchid-base/src/virt_fs/prefix.rs | 6 +- unsync-pipe/Cargo.toml | 3 +- unsync-pipe/src/lib.rs | 2 +- 22 files changed, 463 insertions(+), 1082 deletions(-) delete mode 100644 orchid-base/src/pipe.rs diff --git a/Cargo.lock b/Cargo.lock index fc633b3..5a036dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1060,6 +1060,7 @@ dependencies = [ "substack", "test_executors 0.3.5", "trait-set", + "unsync-pipe", ] [[package]] @@ -1949,6 +1950,7 @@ name = "unsync-pipe" version = "0.2.0" dependencies = [ "futures", + "futures-io", "itertools", "rand 0.9.2", "rand_chacha 0.9.0", diff --git a/orchid-api-traits/src/relations.rs b/orchid-api-traits/src/relations.rs index b86b619..8cb925e 100644 --- a/orchid-api-traits/src/relations.rs +++ b/orchid-api-traits/src/relations.rs @@ -1,5 +1,4 @@ use core::fmt; -use std::future::Future; use super::coding::Coding; use crate::helpers::enc_vec; diff --git a/orchid-api/src/tree.rs b/orchid-api/src/tree.rs index eec6d7f..d964464 100644 --- a/orchid-api/src/tree.rs +++ b/orchid-api/src/tree.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use std::fmt; use std::num::NonZeroU64; use std::ops::Range; -use std::rc::Rc; use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_traits::Request; @@ -48,7 +47,7 @@ pub enum Token { /// NewExpr(Bottom) because it fails in dead branches too. Bottom(Vec), /// A comment - Comment(Rc), + Comment(TStr), } #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Coding)] diff --git a/orchid-base/Cargo.toml b/orchid-base/Cargo.toml index 7168160..8311b59 100644 --- a/orchid-base/Cargo.toml +++ b/orchid-base/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" } async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" } async-once-cell = "0.5.4" bound = "0.6.0" diff --git a/orchid-base/src/error.rs b/orchid-base/src/error.rs index 4d127c6..deb2301 100644 --- a/orchid-base/src/error.rs +++ b/orchid-base/src/error.rs @@ -2,13 +2,15 @@ use std::cell::RefCell; use std::ffi::OsStr; use std::fmt; use std::ops::Add; +use std::rc::Rc; use std::sync::Arc; use futures::future::join_all; use itertools::Itertools; +use some_executor::task_local; use crate::api; -use crate::interner::{Interner, Tok}; +use crate::interner::{IStr, es, is}; use crate::location::Pos; /// A point of interest in resolving the error, such as the point where @@ -24,10 +26,10 @@ impl ErrPos { pub fn new(msg: &str, position: Pos) -> Self { Self { message: Some(Arc::new(msg.to_string())), position } } - async fn from_api(api: &api::ErrLocation, i: &Interner) -> Self { + async fn from_api(api: &api::ErrLocation) -> Self { Self { message: Some(api.message.clone()).filter(|s| !s.is_empty()), - position: Pos::from_api(&api.location, i).await, + position: Pos::from_api(&api.location).await, } } fn to_api(&self) -> api::ErrLocation { @@ -51,7 +53,7 @@ impl fmt::Display for ErrPos { #[derive(Clone, Debug)] pub struct OrcErr { - pub description: Tok, + pub description: IStr, pub message: Arc, pub positions: Vec, } @@ -63,16 +65,16 @@ impl OrcErr { locations: self.positions.iter().map(ErrPos::to_api).collect(), } } - async fn from_api(api: &api::OrcError, i: &Interner) -> Self { + async fn from_api(api: &api::OrcError) -> Self { Self { - description: Tok::from_api(api.description, i).await, + description: es(api.description).await, message: api.message.clone(), - positions: join_all(api.locations.iter().map(|e| ErrPos::from_api(e, i))).await, + positions: join_all(api.locations.iter().map(ErrPos::from_api)).await, } } } -impl PartialEq> for OrcErr { - fn eq(&self, other: &Tok) -> bool { self.description == *other } +impl PartialEq for OrcErr { + fn eq(&self, other: &IStr) -> bool { self.description == *other } } impl From for Vec { fn from(value: OrcErr) -> Self { vec![value] } @@ -122,11 +124,8 @@ impl OrcErrv { self.0.iter().flat_map(|e| e.positions.iter().cloned()) } pub fn to_api(&self) -> Vec { self.0.iter().map(OrcErr::to_api).collect() } - pub async fn from_api<'a>( - api: impl IntoIterator, - i: &Interner, - ) -> Self { - Self(join_all(api.into_iter().map(|e| OrcErr::from_api(e, i))).await) + pub async fn from_api<'a>(api: impl IntoIterator) -> Self { + Self(join_all(api.into_iter().map(OrcErr::from_api)).await) } } impl From for OrcErrv { @@ -191,12 +190,12 @@ macro_rules! join_ok { (@VALUES) => { Ok(()) }; } -pub fn mk_errv_floating(description: Tok, message: impl AsRef) -> OrcErrv { +pub fn mk_errv_floating(description: IStr, message: impl AsRef) -> OrcErrv { mk_errv::(description, message, []) } pub fn mk_errv>( - description: Tok, + description: IStr, message: impl AsRef, posv: impl IntoIterator, ) -> OrcErrv { @@ -210,45 +209,61 @@ pub fn mk_errv>( pub async fn async_io_err>( err: std::io::Error, - i: &Interner, posv: impl IntoIterator, ) -> OrcErrv { - mk_errv(i.i(&err.kind().to_string()).await, err.to_string(), posv) + mk_errv(is(&err.kind().to_string()).await, err.to_string(), posv) } -pub async fn os_str_to_string<'a, I: Into>( - str: &'a OsStr, - i: &Interner, +pub async fn os_str_to_string>( + str: &OsStr, posv: impl IntoIterator, -) -> OrcRes<&'a str> { +) -> OrcRes<&str> { match str.to_str() { Some(str) => Ok(str), None => Err(mk_errv( - i.i("Non-unicode string").await, + is("Non-unicode string").await, format!("{str:?} is not representable as unicode"), posv, )), } } -pub struct Reporter { - errors: RefCell>, +#[derive(Clone, Default)] +struct Reporter { + errors: Rc>>, } -impl Reporter { - pub fn report(&self, e: impl Into) { self.errors.borrow_mut().extend(e.into()) } - pub fn new() -> Self { Self { errors: RefCell::new(vec![]) } } - pub fn errv(self) -> Option { OrcErrv::new(self.errors.into_inner()).ok() } - pub fn merge(self, res: OrcRes) -> OrcRes { - match (res, self.errv()) { - (res, None) => res, - (Ok(_), Some(errv)) => Err(errv), - (Err(e), Some(errv)) => Err(e + errv), - } +task_local! { + static REPORTER: Reporter; +} + +pub async fn with_reporter(fut: impl Future>) -> OrcRes { + let rep = Reporter::default(); + let res = REPORTER.scope(rep.clone(), fut).await; + let errors = rep.errors.take(); + match (res, &errors[..]) { + (Ok(t), []) => Ok(t), + (Ok(_), [_, ..]) => Err(OrcErrv::new(errors).unwrap()), + (Err(e), _) => Err(e.extended(errors)), } - pub fn is_empty(&self) -> bool { self.errors.borrow().is_empty() } } -impl Default for Reporter { - fn default() -> Self { Self::new() } +pub async fn is_erroring() -> bool { + REPORTER.with(|r| { + !r.expect("Sidechannel errors must be caught by a reporter").errors.borrow().is_empty() + }) +} + +/// Report an error that is fatal and prevents a correct output, but +/// still allows the current task to continue and produce an approximate output. +/// This can be used for +pub fn report(e: impl Into) { + let errv = e.into(); + REPORTER.with(|r| match r { + Some(r) => r.errors.borrow_mut().extend(errv), + None => panic!( + "Unhandled error! Sidechannel errors must be caught by an enclosing call to with_reporter.\n\ + Error: {errv}", + ), + }) } diff --git a/orchid-base/src/format.rs b/orchid-base/src/format.rs index bb42821..331737b 100644 --- a/orchid-base/src/format.rs +++ b/orchid-base/src/format.rs @@ -3,6 +3,7 @@ use std::cmp::Ordering; use std::convert::Infallible; use std::future::Future; use std::iter; +use std::marker::PhantomData; use std::rc::Rc; use std::str::FromStr; @@ -11,7 +12,6 @@ use itertools::{Itertools, chain}; use never::Never; use regex::Regex; -use crate::interner::Interner; use crate::{api, match_mapping}; #[derive(Clone, Debug, Hash, PartialEq, Eq)] @@ -300,16 +300,15 @@ pub fn take_first(unit: &FmtUnit, bounded: bool) -> String { fill_slots(&first.elements, &unit.subs, 0, bounded) } -pub async fn take_first_fmt(v: &(impl Format + ?Sized), i: &Interner) -> String { - take_first(&v.print(&FmtCtxImpl { i }).await, false) +pub async fn take_first_fmt(v: &(impl Format + ?Sized)) -> String { + take_first(&v.print(&FmtCtxImpl { _foo: PhantomData }).await, false) } pub struct FmtCtxImpl<'a> { - pub i: &'a Interner, + _foo: PhantomData<&'a ()>, } pub trait FmtCtx { - fn i(&self) -> &Interner; // fn print_as(&self, p: &(impl Format + ?Sized)) -> impl Future where Self: Sized { // async { @@ -319,9 +318,7 @@ pub trait FmtCtx { // } // } } -impl FmtCtx for FmtCtxImpl<'_> { - fn i(&self) -> &Interner { self.i } -} +impl FmtCtx for FmtCtxImpl<'_> {} pub trait Format { #[must_use] @@ -332,13 +329,10 @@ impl Format for Never { } /// Format with default strategy. Currently equal to [take_first_fmt] -pub async fn fmt(v: &(impl Format + ?Sized), i: &Interner) -> String { take_first_fmt(v, i).await } +pub async fn fmt(v: &(impl Format + ?Sized)) -> String { take_first_fmt(v).await } /// Format a sequence with default strategy. Currently equal to [take_first_fmt] pub async fn fmt_v>( v: impl IntoIterator, - i: &Interner, ) -> impl Iterator { - join_all(v.into_iter().map(|f| async move { take_first_fmt(f.borrow(), i).await })) - .await - .into_iter() + join_all(v.into_iter().map(|f| async move { take_first_fmt(f.borrow()).await })).await.into_iter() } diff --git a/orchid-base/src/interner.rs b/orchid-base/src/interner.rs index 8675fa9..fb8e5b1 100644 --- a/orchid-base/src/interner.rs +++ b/orchid-base/src/interner.rs @@ -1,28 +1,28 @@ -use std::borrow::Borrow; use std::fmt::{Debug, Display}; use std::future::Future; -use std::hash::{BuildHasher as _, Hash}; -use std::num::NonZeroU64; +use std::hash::Hash; use std::ops::Deref; use std::rc::Rc; -use std::sync::atomic; use std::{fmt, hash}; use futures::future::LocalBoxFuture; -use futures::lock::Mutex; -use hashbrown::{HashMap, HashSet}; -use itertools::Itertools as _; -use orchid_api_traits::Request; use some_executor::task_local; use crate::api; -use crate::reqnot::{DynRequester, Requester}; pub trait IStrHandle: AsRef {} pub trait IStrvHandle: AsRef<[IStr]> {} #[derive(Clone)] pub struct IStr(pub api::TStr, pub Rc); +impl IStr { + /// Obtain a unique ID for this interned data. + /// + /// NOTICE: the ID is guaranteed to be the same for any interned instance of + /// 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 } +} impl Deref for IStr { type Target = str; fn deref(&self) -> &Self::Target { self.1.as_ref().as_ref() } @@ -42,6 +42,14 @@ impl Debug for IStr { } #[derive(Clone)] pub struct IStrv(pub api::TStrv, pub Rc); +impl IStrv { + /// Obtain a unique ID for this interned data. + /// + /// NOTICE: the ID is guaranteed to be the same for any interned instance of + /// 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 } +} impl Deref for IStrv { type Target = [IStr]; fn deref(&self) -> &Self::Target { self.1.as_ref().as_ref() } @@ -93,292 +101,3 @@ 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 } - -/// Clippy crashes while verifying `Tok: Sized` without this and I cba to create -/// a minimal example -#[derive(Clone)] -struct ForceSized(T); - -#[derive(Clone)] -pub struct Tok { - data: Rc, - marker: ForceSized, -} -impl Tok { - pub fn new(data: Rc, marker: T::Marker) -> Self { Self { data, marker: ForceSized(marker) } } - pub fn to_api(&self) -> T::Marker { self.marker.0 } - pub async fn from_api(marker: M, i: &Interner) -> Self - where M: InternMarker { - i.ex(marker).await - } - pub fn rc(&self) -> Rc { self.data.clone() } -} -impl Deref for Tok { - type Target = T; - - fn deref(&self) -> &Self::Target { self.data.as_ref() } -} -impl Ord for Tok { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.to_api().cmp(&other.to_api()) } -} -impl PartialOrd for Tok { - fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } -} -impl Eq for Tok {} -impl PartialEq for Tok { - fn eq(&self, other: &Self) -> bool { self.cmp(other).is_eq() } -} -impl hash::Hash for Tok { - fn hash(&self, state: &mut H) { self.to_api().hash(state) } -} -impl fmt::Display for Tok { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", &*self.data) - } -} -impl fmt::Debug for Tok { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Token({} -> {:?})", self.to_api().get_id(), self.data.as_ref()) - } -} - -pub trait Interned: Eq + hash::Hash + Clone + fmt::Debug + Internable { - type Marker: InternMarker + Sized; - fn intern( - self: Rc, - req: &(impl DynRequester + ?Sized), - ) -> impl Future; - fn bimap(interner: &mut TypedInterners) -> &mut Bimap; -} - -pub trait Internable: fmt::Debug { - type Interned: Interned; - fn get_owned(&self) -> Rc; -} - -pub trait InternMarker: Copy + PartialEq + Eq + PartialOrd + Ord + hash::Hash + Sized { - type Interned: Interned; - /// Only called on replicas - fn resolve(self, i: &Interner) -> impl Future>; - fn get_id(self) -> NonZeroU64; - fn from_id(id: NonZeroU64) -> Self; -} - -impl Interned for String { - type Marker = api::TStr; - async fn intern( - self: Rc, - req: &(impl DynRequester + ?Sized), - ) -> Self::Marker { - req.request(api::InternStr(self.to_string())).await - } - fn bimap(interners: &mut TypedInterners) -> &mut Bimap { &mut interners.strings } -} -impl InternMarker for api::TStr { - type Interned = String; - async fn resolve(self, i: &Interner) -> Tok { - Tok::new(Rc::new(i.0.master.as_ref().unwrap().request(api::ExternStr(self)).await), self) - } - fn get_id(self) -> NonZeroU64 { self.0 } - fn from_id(id: NonZeroU64) -> Self { Self(id) } -} -impl Internable for str { - type Interned = String; - fn get_owned(&self) -> Rc { Rc::new(self.to_string()) } -} -impl Internable for String { - type Interned = String; - fn get_owned(&self) -> Rc { Rc::new(self.to_string()) } -} - -impl Interned for Vec> { - type Marker = api::TStrv; - async fn intern( - self: Rc, - req: &(impl DynRequester + ?Sized), - ) -> Self::Marker { - req.request(api::InternStrv(self.iter().map(|t| t.to_api()).collect())).await - } - fn bimap(interners: &mut TypedInterners) -> &mut Bimap { &mut interners.vecs } -} -impl InternMarker for api::TStrv { - type Interned = Vec>; - async fn resolve(self, i: &Interner) -> Tok { - let rep = i.0.master.as_ref().unwrap().request(api::ExternStrv(self)).await; - let data = futures::future::join_all(rep.into_iter().map(|m| i.ex(m))).await; - Tok::new(Rc::new(data), self) - } - fn get_id(self) -> NonZeroU64 { self.0 } - fn from_id(id: NonZeroU64) -> Self { Self(id) } -} -impl Internable for [Tok] { - type Interned = Vec>; - fn get_owned(&self) -> Rc { Rc::new(self.to_vec()) } -} -impl Internable for [Tok; N] { - type Interned = Vec>; - fn get_owned(&self) -> Rc { Rc::new(self.to_vec()) } -} -impl Internable for Vec> { - type Interned = Vec>; - fn get_owned(&self) -> Rc { Rc::new(self.to_vec()) } -} -// impl Internable for Vec { -// type Interned = Vec>; -// fn get_owned(&self) -> Arc { -// Arc::new(self.iter().map(|ts| deintern(*ts)).collect()) -// } -// } -// impl Internable for [api::TStr] { -// type Interned = Vec>; -// fn get_owned(&self) -> Arc { -// Arc::new(self.iter().map(|ts| deintern(*ts)).collect()) -// } -// } - -/// The number of references held to any token by the interner. -const BASE_RC: usize = 3; - -#[test] -fn base_rc_correct() { - let tok = Tok::new(Rc::new("foo".to_string()), api::TStr(1.try_into().unwrap())); - let mut bimap = Bimap::default(); - bimap.insert(tok.clone()); - assert_eq!(Rc::strong_count(&tok.data), BASE_RC + 1, "the bimap plus the current instance"); -} - -pub struct Bimap { - intern: HashMap, Tok>, - by_id: HashMap>, -} -impl Bimap { - pub fn insert(&mut self, token: Tok) { - self.intern.insert(token.data.clone(), token.clone()); - self.by_id.insert(token.to_api(), token); - } - - pub fn by_marker(&self, marker: T::Marker) -> Option> { self.by_id.get(&marker).cloned() } - - pub fn by_value(&self, q: &Q) -> Option> - where T: Borrow { - (self.intern.raw_entry()) - .from_hash(self.intern.hasher().hash_one(q), |k| k.as_ref().borrow() == q) - .map(|p| p.1.clone()) - } - - pub fn sweep_replica(&mut self) -> Vec { - (self.intern) - .extract_if(|k, _| Rc::strong_count(k) == BASE_RC) - .map(|(_, v)| { - self.by_id.remove(&v.to_api()); - v.to_api() - }) - .collect() - } - - pub fn sweep_master(&mut self, retained: HashSet) { - self.intern.retain(|k, v| BASE_RC < Rc::strong_count(k) || retained.contains(&v.to_api())) - } -} - -impl Default for Bimap { - fn default() -> Self { Self { by_id: HashMap::new(), intern: HashMap::new() } } -} - -pub trait UpComm { - fn up(&self, req: R) -> R::Response; -} - -#[derive(Default)] -pub struct TypedInterners { - strings: Bimap, - vecs: Bimap>>, -} - -#[derive(Default)] -pub struct InternerData { - interners: Mutex, - master: Option>>, -} -#[derive(Clone, Default)] -pub struct Interner(Rc); -impl Interner { - pub fn new_master() -> Self { Self::default() } - pub fn new_replica(req: impl DynRequester + 'static) -> Self { - Self(Rc::new(InternerData { master: Some(Box::new(req)), interners: Mutex::default() })) - } - /// Intern some data; query its identifier if not known locally - pub async fn i(&self, t: &(impl Internable + ?Sized)) -> Tok { - let data = t.get_owned(); - let mut g = self.0.interners.lock().await; - let typed = T::bimap(&mut g); - if let Some(tok) = typed.by_value(&data) { - return tok; - } - let marker = match &self.0.master { - Some(c) => data.clone().intern(&**c).await, - None => - T::Marker::from_id(NonZeroU64::new(ID.fetch_add(1, atomic::Ordering::Relaxed)).unwrap()), - }; - let tok = Tok::new(data, marker); - T::bimap(&mut g).insert(tok.clone()); - tok - } - /// Extern an identifier; query the data it represents if not known locally - pub async fn ex(&self, marker: M) -> Tok { - if let Some(tok) = M::Interned::bimap(&mut *self.0.interners.lock().await).by_marker(marker) { - return tok; - } - assert!(self.0.master.is_some(), "ID not in local interner and this is master"); - let token = marker.resolve(self).await; - M::Interned::bimap(&mut *self.0.interners.lock().await).insert(token.clone()); - token - } - pub async fn sweep_master(&self, retained: api::Sweeped) { - assert!(self.0.master.is_none(), "Not master"); - let mut g = self.0.interners.lock().await; - g.strings.sweep_master(retained.strings.into_iter().collect()); - g.vecs.sweep_master(retained.vecs.into_iter().collect()); - } -} -impl fmt::Debug for Interner { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Interner{{ replica: {} }}", self.0.master.is_none()) - } -} - -static ID: atomic::AtomicU64 = atomic::AtomicU64::new(1); - -pub fn merge_retained(into: &mut api::Sweeped, from: &api::Sweeped) { - into.strings = into.strings.iter().chain(&from.strings).copied().unique().collect(); - into.vecs = into.vecs.iter().chain(&from.vecs).copied().unique().collect(); -} - -#[cfg(test)] -mod test { - use std::num::NonZero; - use std::pin::Pin; - - use orchid_api_traits::{Decode, enc_vec}; - use test_executors::spin_on; - - use super::*; - use crate::api; - - #[test] - fn test_i() { - let i = Interner::new_master(); - let _: Tok = spin_on(i.i("foo")); - let _: Tok>> = spin_on(i.i(&[spin_on(i.i("bar")), spin_on(i.i("baz"))])); - } - - #[test] - fn test_coding() { - spin_on(async { - let coded = api::TStr(NonZero::new(3u64).unwrap()); - let mut enc = &enc_vec(&coded).await[..]; - api::TStr::decode(Pin::new(&mut enc)).await; - assert_eq!(enc, [], "Did not consume all of {enc:?}") - }) - } -} diff --git a/orchid-base/src/lib.rs b/orchid-base/src/lib.rs index 09ba43a..b812005 100644 --- a/orchid-base/src/lib.rs +++ b/orchid-base/src/lib.rs @@ -21,7 +21,6 @@ pub mod msg; pub mod name; pub mod number; pub mod parse; -pub mod pipe; pub mod pure_seq; pub mod reqnot; pub mod sequence; diff --git a/orchid-base/src/location.rs b/orchid-base/src/location.rs index 27fa11f..ef0b5bb 100644 --- a/orchid-base/src/location.rs +++ b/orchid-base/src/location.rs @@ -8,12 +8,12 @@ use futures::future::join_all; use trait_set::trait_set; use crate::error::ErrPos; -use crate::interner::{Interner, Tok}; +use crate::interner::{IStr, es, is}; use crate::name::Sym; use crate::{api, match_mapping, sym}; trait_set! { - pub trait GetSrc = FnMut(&Sym) -> Tok; + pub trait GetSrc = FnMut(&Sym) -> IStr; } #[derive(Debug, Clone, PartialEq, Eq)] @@ -37,13 +37,13 @@ impl Pos { other => format!("{other:?}"), } } - pub async fn from_api(api: &api::Location, i: &Interner) -> Self { + pub async fn from_api(api: &api::Location) -> Self { match_mapping!(api, api::Location => Pos { None, Inherit, SlotTarget, - Gen(cgi => CodeGenInfo::from_api(cgi, i).await), - Multi(v => join_all(v.iter().map(|l| Pos::from_api(l, i))).await) + Gen(cgi => CodeGenInfo::from_api(cgi).await), + Multi(v => join_all(v.iter().map(Pos::from_api)).await) } { - api::Location::SourceRange(sr) => Self::SrcRange(SrcRange::from_api(sr, i).await) + api::Location::SourceRange(sr) => Self::SrcRange(SrcRange::from_api(sr).await) }) } pub fn to_api(&self) -> api::Location { @@ -108,7 +108,7 @@ impl SrcRange { } /// Create a dud [SourceRange] for testing. Its value is unspecified and /// volatile. - pub async fn mock(i: &Interner) -> Self { Self { range: 0..1, path: sym!(test; i) } } + pub async fn mock() -> Self { Self { range: 0..1, path: sym!(test) } } /// Path the source text was loaded from pub fn path(&self) -> Sym { self.path.clone() } /// Byte range @@ -133,8 +133,8 @@ impl SrcRange { } } pub fn zw(path: Sym, pos: u32) -> Self { Self { path, range: pos..pos } } - pub async fn from_api(api: &api::SourceRange, i: &Interner) -> Self { - Self { path: Sym::from_api(api.path, i).await, range: api.range.clone() } + pub async fn from_api(api: &api::SourceRange) -> Self { + Self { path: Sym::from_api(api.path).await, range: api.range.clone() } } pub fn to_api(&self) -> api::SourceRange { api::SourceRange { path: self.path.to_api(), range: self.range.clone() } @@ -162,24 +162,19 @@ pub struct CodeGenInfo { /// formatted like a Rust namespace pub generator: Sym, /// Unformatted user message with relevant circumstances and parameters - pub details: Tok, + pub details: IStr, } impl CodeGenInfo { /// A codegen marker with no user message and parameters - pub async fn new_short(generator: Sym, i: &Interner) -> Self { - Self { generator, details: i.i("").await } - } + pub async fn new_short(generator: Sym) -> Self { Self { generator, details: is("").await } } /// A codegen marker with a user message or parameters - pub async fn new_details(generator: Sym, details: impl AsRef, i: &Interner) -> Self { - Self { generator, details: i.i(details.as_ref()).await } + pub async fn new_details(generator: Sym, details: impl AsRef) -> Self { + Self { generator, details: is(details.as_ref()).await } } /// Syntactic location pub fn pos(&self) -> Pos { Pos::Gen(self.clone()) } - pub async fn from_api(api: &api::CodeGenInfo, i: &Interner) -> Self { - Self { - generator: Sym::from_api(api.generator, i).await, - details: Tok::from_api(api.details, i).await, - } + pub async fn from_api(api: &api::CodeGenInfo) -> Self { + Self { generator: Sym::from_api(api.generator).await, details: es(api.details).await } } pub fn to_api(&self) -> api::CodeGenInfo { api::CodeGenInfo { generator: self.generator.to_api(), details: self.details.to_api() } diff --git a/orchid-base/src/name.rs b/orchid-base/src/name.rs index 4951ba6..5951c5f 100644 --- a/orchid-base/src/name.rs +++ b/orchid-base/src/name.rs @@ -12,65 +12,60 @@ use itertools::Itertools; use trait_set::trait_set; use crate::api; -use crate::interner::{InternMarker, Interner, Tok}; +use crate::interner::{IStr, IStrv, es, ev, is, iv}; trait_set! { /// Traits that all name iterators should implement - pub trait NameIter = Iterator> + DoubleEndedIterator + ExactSizeIterator; + pub trait NameIter = Iterator + DoubleEndedIterator + ExactSizeIterator; } /// A token path which may be empty. [VName] is the non-empty version #[derive(Clone, Default, Hash, PartialEq, Eq)] -pub struct VPath(Vec>); +pub struct VPath(Vec); impl VPath { /// Collect segments into a vector - pub fn new(items: impl IntoIterator>) -> Self { - Self(items.into_iter().collect()) - } + pub fn new(items: impl IntoIterator) -> Self { Self(items.into_iter().collect()) } /// Number of path segments pub fn len(&self) -> usize { self.0.len() } /// Whether there are any path segments. In other words, whether this is a /// valid name pub fn is_empty(&self) -> bool { self.len() == 0 } /// Prepend some tokens to the path - pub fn prefix(self, items: impl IntoIterator>) -> Self { + pub fn prefix(self, items: impl IntoIterator) -> Self { Self(items.into_iter().chain(self.0).collect()) } /// Append some tokens to the path - pub fn suffix(self, items: impl IntoIterator>) -> Self { + pub fn suffix(self, items: impl IntoIterator) -> Self { Self(self.0.into_iter().chain(items).collect()) } /// Partition the string by `::` namespace separators - pub async fn parse(s: &str, i: &Interner) -> Self { - Self(if s.is_empty() { vec![] } else { join_all(s.split("::").map(|s| i.i(s))).await }) + pub async fn parse(s: &str) -> Self { + Self(if s.is_empty() { vec![] } else { join_all(s.split("::").map(is)).await }) } /// Walk over the segments - pub fn str_iter(&self) -> impl Iterator { - Box::new(self.0.iter().map(|s| s.as_str())) - } + pub fn str_iter(&self) -> impl Iterator { Box::new(self.0.iter().map(|s| &**s)) } /// Try to convert into non-empty version pub fn into_name(self) -> Result { VName::new(self.0) } /// Add a token to the path. Since now we know that it can't be empty, turn it /// into a name. - pub fn name_with_suffix(self, name: Tok) -> VName { + pub fn name_with_suffix(self, name: IStr) -> VName { VName(self.into_iter().chain([name]).collect()) } /// Add a token to the beginning of the. Since now we know that it can't be /// empty, turn it into a name. - pub fn name_with_prefix(self, name: Tok) -> VName { + pub fn name_with_prefix(self, name: IStr) -> VName { VName([name].into_iter().chain(self).collect()) } /// Convert a fs path to a vpath - pub async fn from_path(path: &Path, ext: &str, i: &Interner) -> Option<(Self, bool)> { - async fn to_vpath(p: &Path, i: &Interner) -> Option { - let tok_opt_v = - join_all(p.iter().map(|c| OptionFuture::from(c.to_str().map(|s| i.i(s))))).await; + pub async fn from_path(path: &Path, ext: &str) -> Option<(Self, bool)> { + async fn to_vpath(p: &Path) -> Option { + let tok_opt_v = join_all(p.iter().map(|c| OptionFuture::from(c.to_str().map(is)))).await; tok_opt_v.into_iter().collect::>().map(VPath) } match path.extension().map(|s| s.to_str()) { - Some(Some(s)) if s == ext => Some((to_vpath(&path.with_extension(""), i).await?, true)), - None => Some((to_vpath(path, i).await?, false)), + Some(Some(s)) if s == ext => Some((to_vpath(&path.with_extension("")).await?, true)), + None => Some((to_vpath(path).await?, false)), Some(_) => None, } } @@ -83,30 +78,28 @@ impl fmt::Display for VPath { write!(f, "{}", self.str_iter().join("::")) } } -impl FromIterator> for VPath { - fn from_iter>>(iter: T) -> Self { - Self(iter.into_iter().collect()) - } +impl FromIterator for VPath { + fn from_iter>(iter: T) -> Self { Self(iter.into_iter().collect()) } } impl IntoIterator for VPath { - type Item = Tok; + type Item = IStr; type IntoIter = vec::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } -impl Borrow<[Tok]> for VPath { - fn borrow(&self) -> &[Tok] { &self.0[..] } +impl Borrow<[IStr]> for VPath { + fn borrow(&self) -> &[IStr] { &self.0[..] } } impl Deref for VPath { - type Target = [Tok]; + type Target = [IStr]; fn deref(&self) -> &Self::Target { self.borrow() } } impl Index for VPath -where [Tok]: Index +where [IStr]: Index { - type Output = <[Tok] as Index>::Output; + type Output = <[IStr] as Index>::Output; - fn index(&self, index: T) -> &Self::Output { &Borrow::<[Tok]>::borrow(self)[index] } + fn index(&self, index: T) -> &Self::Output { &Borrow::<[IStr]>::borrow(self)[index] } } /// A mutable representation of a namespaced identifier of at least one segment. @@ -116,50 +109,43 @@ where [Tok]: Index /// See also [Sym] for the immutable representation, and [VPath] for possibly /// empty values #[derive(Clone, Hash, PartialEq, Eq)] -pub struct VName(Vec>); +pub struct VName(Vec); impl VName { /// Assert that the sequence isn't empty and wrap it in [VName] to represent /// this invariant - pub fn new(items: impl IntoIterator>) -> Result { + pub fn new(items: impl IntoIterator) -> Result { let data: Vec<_> = items.into_iter().collect(); if data.is_empty() { Err(EmptyNameError) } else { Ok(Self(data)) } } - pub async fn deintern( - name: impl IntoIterator, - i: &Interner, - ) -> Result { - Self::new(join_all(name.into_iter().map(|m| Tok::from_api(m, i))).await) + pub async fn deintern(name: impl IntoIterator) -> Result { + Self::new(join_all(name.into_iter().map(es)).await) } /// Unwrap the enclosed vector - pub fn into_vec(self) -> Vec> { self.0 } + pub fn into_vec(self) -> Vec { self.0 } /// Get a reference to the enclosed vector - pub fn vec(&self) -> &Vec> { &self.0 } + pub fn vec(&self) -> &Vec { &self.0 } /// Mutable access to the underlying vector. To ensure correct results, this /// must never be empty. - pub fn vec_mut(&mut self) -> &mut Vec> { &mut self.0 } + pub fn vec_mut(&mut self) -> &mut Vec { &mut self.0 } /// Intern the name and return a [Sym] - pub async fn to_sym(&self, i: &Interner) -> Sym { Sym(i.i(&self.0[..]).await) } + pub async fn to_sym(&self) -> Sym { Sym(iv(&self.0[..]).await) } /// If this name has only one segment, return it - pub fn as_root(&self) -> Option> { self.0.iter().exactly_one().ok().cloned() } + pub fn as_root(&self) -> Option { self.0.iter().exactly_one().ok().cloned() } /// Prepend the segments to this name #[must_use = "This is a pure function"] - pub fn prefix(self, items: impl IntoIterator>) -> Self { + pub fn prefix(self, items: impl IntoIterator) -> Self { Self(items.into_iter().chain(self.0).collect()) } /// Append the segments to this name #[must_use = "This is a pure function"] - pub fn suffix(self, items: impl IntoIterator>) -> Self { + pub fn suffix(self, items: impl IntoIterator) -> Self { Self(self.0.into_iter().chain(items).collect()) } /// Read a `::` separated namespaced name - pub async fn parse(s: &str, i: &Interner) -> Result { - Self::new(VPath::parse(s, i).await) - } - pub async fn literal(s: &'static str, i: &Interner) -> Self { - Self::parse(s, i).await.expect("empty literal !?") - } + pub async fn parse(s: &str) -> Result { Self::new(VPath::parse(s).await) } + pub async fn literal(s: &'static str) -> Self { Self::parse(s).await.expect("empty literal !?") } /// Obtain an iterator over the segments of the name - pub fn iter(&self) -> impl Iterator> + '_ { self.0.iter().cloned() } + pub fn iter(&self) -> impl Iterator + '_ { self.0.iter().cloned() } } impl fmt::Debug for VName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "VName({self})") } @@ -170,22 +156,22 @@ impl fmt::Display for VName { } } impl IntoIterator for VName { - type Item = Tok; + type Item = IStr; type IntoIter = vec::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } impl Index for VName -where [Tok]: Index +where [IStr]: Index { - type Output = <[Tok] as Index>::Output; + type Output = <[IStr] as Index>::Output; fn index(&self, index: T) -> &Self::Output { &self.deref()[index] } } -impl Borrow<[Tok]> for VName { - fn borrow(&self) -> &[Tok] { self.0.borrow() } +impl Borrow<[IStr]> for VName { + fn borrow(&self) -> &[IStr] { self.0.borrow() } } impl Deref for VName { - type Target = [Tok]; + type Target = [IStr]; fn deref(&self) -> &Self::Target { self.borrow() } } @@ -193,11 +179,9 @@ impl Deref for VName { /// empty sequence #[derive(Debug, Copy, Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct EmptyNameError; -impl TryFrom<&[Tok]> for VName { +impl TryFrom<&[IStr]> for VName { type Error = EmptyNameError; - fn try_from(value: &[Tok]) -> Result { - Self::new(value.iter().cloned()) - } + fn try_from(value: &[IStr]) -> Result { Self::new(value.iter().cloned()) } } /// An interned representation of a namespaced identifier. @@ -206,37 +190,34 @@ impl TryFrom<&[Tok]> for VName { /// /// See also [VName] #[derive(Clone, Hash, PartialEq, Eq)] -pub struct Sym(Tok>>); +pub struct Sym(IStrv); impl Sym { /// Assert that the sequence isn't empty, intern it and wrap it in a [Sym] to /// represent this invariant - pub async fn new( - v: impl IntoIterator>, - i: &Interner, - ) -> Result { + pub async fn new(v: impl IntoIterator) -> Result { let items = v.into_iter().collect_vec(); - Self::from_tok(i.i(&items).await) + Self::from_tok(iv(&items).await) } /// Read a `::` separated namespaced name. - pub async fn parse(s: &str, i: &Interner) -> Result { - Ok(Sym(i.i(&VName::parse(s, i).await?.into_vec()).await)) + pub async fn parse(s: &str) -> Result { + Ok(Sym(iv(&VName::parse(s).await?.into_vec()).await)) } /// Assert that a token isn't empty, and wrap it in a [Sym] - pub fn from_tok(t: Tok>>) -> Result { + pub fn from_tok(t: IStrv) -> Result { if t.is_empty() { Err(EmptyNameError) } else { Ok(Self(t)) } } /// Grab the interner token - pub fn tok(&self) -> Tok>> { self.0.clone() } + pub fn tok(&self) -> IStrv { self.0.clone() } /// Get a number unique to this name suitable for arbitrary ordering. - pub fn id(&self) -> NonZeroU64 { self.0.to_api().get_id() } + pub fn id(&self) -> NonZeroU64 { self.0.to_api().0 } /// Extern the sym for editing pub fn to_vname(&self) -> VName { VName(self[..].to_vec()) } - pub async fn from_api(marker: api::TStrv, i: &Interner) -> Sym { - Self::from_tok(Tok::from_api(marker, i).await).expect("Empty sequence found for serialized Sym") + pub async fn from_api(marker: api::TStrv) -> Sym { + Self::from_tok(ev(marker).await).expect("Empty sequence found for serialized Sym") } pub fn to_api(&self) -> api::TStrv { self.tok().to_api() } - pub async fn suffix(&self, tokv: impl IntoIterator>, i: &Interner) -> Sym { - Self::new(self.0.iter().cloned().chain(tokv), i).await.unwrap() + pub async fn suffix(&self, tokv: impl IntoIterator) -> Sym { + Self::new(self.0.iter().cloned().chain(tokv)).await.unwrap() } } impl fmt::Debug for Sym { @@ -248,17 +229,17 @@ impl fmt::Display for Sym { } } impl Index for Sym -where [Tok]: Index +where [IStr]: Index { - type Output = <[Tok] as Index>::Output; + type Output = <[IStr] as Index>::Output; fn index(&self, index: T) -> &Self::Output { &self.deref()[index] } } -impl Borrow<[Tok]> for Sym { - fn borrow(&self) -> &[Tok] { &self.0[..] } +impl Borrow<[IStr]> for Sym { + fn borrow(&self) -> &[IStr] { &self.0[..] } } impl Deref for Sym { - type Target = [Tok]; + type Target = [IStr]; fn deref(&self) -> &Self::Target { self.borrow() } } @@ -266,16 +247,14 @@ impl Deref for Sym { /// handled together in datastructures. The names can never be empty #[allow(clippy::len_without_is_empty)] // never empty pub trait NameLike: - 'static + Clone + Eq + Hash + fmt::Debug + fmt::Display + Borrow<[Tok]> + 'static + Clone + Eq + Hash + fmt::Debug + fmt::Display + Borrow<[IStr]> { /// Convert into held slice - fn as_slice(&self) -> &[Tok] { Borrow::<[Tok]>::borrow(self) } + fn as_slice(&self) -> &[IStr] { Borrow::<[IStr]>::borrow(self) } /// Get iterator over tokens fn segs(&self) -> impl NameIter + '_ { self.as_slice().iter().cloned() } /// Get iterator over string segments - fn str_iter(&self) -> impl Iterator + '_ { - self.as_slice().iter().map(|t| t.as_str()) - } + fn str_iter(&self) -> impl Iterator + '_ { self.as_slice().iter().map(|t| &**t) } /// Fully resolve the name for printing #[must_use] fn to_strv(&self) -> Vec { self.segs().map(|s| s.to_string()).collect() } @@ -286,19 +265,19 @@ pub trait NameLike: NonZeroUsize::try_from(self.segs().count()).expect("NameLike never empty") } /// Like slice's `split_first` except we know that it always returns Some - fn split_first_seg(&self) -> (Tok, &[Tok]) { + fn split_first_seg(&self) -> (IStr, &[IStr]) { let (foot, torso) = self.as_slice().split_last().expect("NameLike never empty"); (foot.clone(), torso) } /// Like slice's `split_last` except we know that it always returns Some - fn split_last_seg(&self) -> (Tok, &[Tok]) { + fn split_last_seg(&self) -> (IStr, &[IStr]) { let (foot, torso) = self.as_slice().split_last().expect("NameLike never empty"); (foot.clone(), torso) } /// Get the first element - fn first_seg(&self) -> Tok { self.split_first_seg().0 } + fn first_seg(&self) -> IStr { self.split_first_seg().0 } /// Get the last element - fn last_seg(&self) -> Tok { self.split_last_seg().0 } + fn last_seg(&self) -> IStr { self.split_last_seg().0 } } impl NameLike for Sym {} @@ -311,11 +290,11 @@ impl NameLike for VName {} /// cloning the token. #[macro_export] macro_rules! sym { - ($seg1:tt $( :: $seg:tt)* ; $i:expr) => { + ($seg1:tt $( :: $seg:tt)*) => { $crate::name::Sym::from_tok( - $i.i(&[ - $i.i(stringify!($seg1)).await - $( , $i.i(stringify!($seg)).await )* + $crate::interner::iv(&[ + $crate::interner::is(stringify!($seg1)).await + $( , $crate::interner::is(stringify!($seg)).await )* ]) .await ).unwrap() @@ -327,10 +306,10 @@ macro_rules! sym { /// The components are interned much like in [sym]. #[macro_export] macro_rules! vname { - ($seg1:tt $( :: $seg:tt)* ; $i:expr) => { + ($seg1:tt $( :: $seg:tt)*) => { $crate::name::VName::new([ - $i.i(stringify!($seg1)).await - $( , $i.i(stringify!($seg)).await )* + $crate::interner::is(stringify!($seg1)).await + $( , $crate::interner::is(stringify!($seg)).await )* ]).unwrap() }; } @@ -340,10 +319,10 @@ macro_rules! vname { /// The components are interned much like in [sym]. #[macro_export] macro_rules! vpath { - ($seg1:tt $( :: $seg:tt)+ ; $i:expr) => { + ($seg1:tt $( :: $seg:tt)*) => { $crate::name::VPath(vec![ - $i.i(stringify!($seg1)).await - $( , $i.i(stringify!($seg)).await )+ + $crate::interner::is(stringify!($seg1)).await + $( , $crate::interner::is(stringify!($seg)).await )* ]) }; () => { @@ -352,42 +331,33 @@ macro_rules! vpath { } #[cfg(test)] -mod test { +pub mod test { use std::borrow::Borrow; - use test_executors::spin_on; - use super::{NameLike, Sym, VName}; - use crate::interner::{Interner, Tok}; + use crate::interner::{IStr, is}; use crate::name::VPath; - #[test] - fn recur() { - spin_on(async { - let i = Interner::new_master(); - let myname = vname!(foo::bar; i); - let _borrowed_slice: &[Tok] = myname.borrow(); - let _deref_pathslice: &[Tok] = &myname; - let _as_slice_out: &[Tok] = myname.as_slice(); - }) + pub async fn recur() { + let myname = vname!(foo::bar); + let _borrowed_slice: &[IStr] = myname.borrow(); + let _deref_pathslice: &[IStr] = &myname; + let _as_slice_out: &[IStr] = myname.as_slice(); } - #[test] - fn literals() { - spin_on(async { - let i = Interner::new_master(); - assert_eq!( - sym!(foo::bar::baz; i), - Sym::new([i.i("foo").await, i.i("bar").await, i.i("baz").await], &i).await.unwrap() - ); - assert_eq!( - vname!(foo::bar::baz; i), - VName::new([i.i("foo").await, i.i("bar").await, i.i("baz").await]).unwrap() - ); - assert_eq!( - vpath!(foo::bar::baz; i), - VPath::new([i.i("foo").await, i.i("bar").await, i.i("baz").await]) - ); - }) + /// Tests that literals are correctly interned as equal + pub async fn literals() { + assert_eq!( + sym!(foo::bar::baz), + Sym::new([is("foo").await, is("bar").await, is("baz").await]).await.unwrap() + ); + assert_eq!( + vname!(foo::bar::baz), + VName::new([is("foo").await, is("bar").await, is("baz").await]).unwrap() + ); + assert_eq!( + vpath!(foo::bar::baz), + VPath::new([is("foo").await, is("bar").await, is("baz").await]) + ); } } diff --git a/orchid-base/src/number.rs b/orchid-base/src/number.rs index 83d1ae6..88a18ce 100644 --- a/orchid-base/src/number.rs +++ b/orchid-base/src/number.rs @@ -4,7 +4,7 @@ use std::ops::Range; use ordered_float::NotNan; use crate::error::{OrcErrv, mk_errv}; -use crate::interner::Interner; +use crate::interner::is; use crate::location::SrcRange; use crate::name::Sym; @@ -55,14 +55,9 @@ pub struct NumError { pub kind: NumErrorKind, } -pub async fn num_to_errv( - NumError { kind, range }: NumError, - offset: u32, - source: &Sym, - i: &Interner, -) -> OrcErrv { +pub async fn num_to_errv(NumError { kind, range }: NumError, offset: u32, source: &Sym) -> OrcErrv { mk_errv( - i.i("Failed to parse number").await, + is("Failed to parse number").await, match kind { NumErrorKind::NaN => "NaN emerged during parsing", NumErrorKind::InvalidDigit => "non-digit character encountered", diff --git a/orchid-base/src/parse.rs b/orchid-base/src/parse.rs index c14b57b..90c2ec2 100644 --- a/orchid-base/src/parse.rs +++ b/orchid-base/src/parse.rs @@ -7,28 +7,13 @@ use futures::future::join_all; use itertools::Itertools; use crate::api; -use crate::error::{OrcErrv, OrcRes, Reporter, mk_errv}; +use crate::error::{OrcErrv, OrcRes, mk_errv, report}; use crate::format::{FmtCtx, FmtUnit, Format, fmt}; -use crate::interner::{Interner, Tok}; +use crate::interner::{IStr, es, is}; use crate::location::SrcRange; use crate::name::{Sym, VName, VPath}; use crate::tree::{ExprRepr, ExtraTok, Paren, TokTree, Token, ttv_fmt, ttv_range}; -pub trait ParseCtx { - #[must_use] - fn i(&self) -> &Interner; - #[must_use] - fn rep(&self) -> &Reporter; -} -pub struct ParseCtxImpl<'a> { - pub i: &'a Interner, - pub r: &'a Reporter, -} -impl ParseCtx for ParseCtxImpl<'_> { - fn i(&self) -> &Interner { self.i } - fn rep(&self) -> &Reporter { self.r } -} - pub fn name_start(c: char) -> bool { c.is_alphabetic() || c == '_' } pub fn name_char(c: char) -> bool { name_start(c) || c.is_numeric() } pub fn op_char(c: char) -> bool { !name_char(c) && !c.is_whitespace() && !"()[]{}\\".contains(c) } @@ -103,22 +88,22 @@ impl Format for Snippet<'_, A, X> { #[derive(Clone, Debug)] pub struct Comment { - pub text: Tok, + pub text: IStr, pub sr: SrcRange, } impl Comment { // XXX: which of these four are actually used? - pub async fn from_api(c: &api::Comment, src: Sym, i: &Interner) -> Self { - Self { text: i.ex(c.text).await, sr: SrcRange::new(c.range.clone(), &src) } + pub async fn from_api(c: &api::Comment, src: Sym) -> Self { + Self { text: es(c.text).await, sr: SrcRange::new(c.range.clone(), &src) } } - pub async fn from_tk(tk: &TokTree, i: &Interner) -> Option { + pub async fn from_tk(tk: &TokTree) -> Option { match &tk.tok { - Token::Comment(text) => Some(Self { text: i.i(&**text).await, sr: tk.sr.clone() }), + Token::Comment(text) => Some(Self { text: text.clone(), sr: tk.sr.clone() }), _ => None, } } pub fn to_tk(&self) -> TokTree { - TokTree { tok: Token::Comment(self.text.rc().clone()), sr: self.sr.clone() } + TokTree { tok: Token::Comment(self.text.clone()), sr: self.sr.clone() } } pub fn to_api(&self) -> api::Comment { api::Comment { range: self.sr.range(), text: self.text.to_api() } @@ -130,7 +115,6 @@ impl fmt::Display for Comment { } pub async fn line_items<'a, A: ExprRepr, X: ExtraTok>( - ctx: &impl ParseCtx, snip: Snippet<'a, A, X>, ) -> Vec, A, X>> { let mut items = Vec::new(); @@ -145,9 +129,10 @@ pub async fn line_items<'a, A: ExprRepr, X: ExtraTok>( None => comments.extend(line.cur), Some(i) => { let (cmts, tail) = line.split_at(i); - let comments = join_all(comments.drain(..).chain(cmts.cur).map(|t| async { - Comment::from_tk(t, ctx.i()).await.expect("All are comments checked above") - })) + let comments = join_all( + (comments.drain(..).chain(cmts.cur)) + .map(|t| async { Comment::from_tk(t).await.expect("All are comments checked above") }), + ) .await; items.push(Parsed { output: comments, tail }); }, @@ -157,26 +142,21 @@ pub async fn line_items<'a, A: ExprRepr, X: ExtraTok>( } pub async fn try_pop_no_fluff<'a, A: ExprRepr, X: ExtraTok>( - ctx: &impl ParseCtx, snip: Snippet<'a, A, X>, ) -> ParseRes<'a, &'a TokTree, A, X> { match snip.skip_fluff().pop_front() { Some((output, tail)) => Ok(Parsed { output, tail }), - None => Err(mk_errv( - ctx.i().i("Unexpected end").await, - "Line ends abruptly; more tokens were expected", - [snip.sr()], - )), + None => + Err(mk_errv(is("Unexpected end").await, "Line ends abruptly; more tokens were expected", [ + snip.sr(), + ])), } } -pub async fn expect_end( - ctx: &impl ParseCtx, - snip: Snippet<'_, impl ExprRepr, impl ExtraTok>, -) -> OrcRes<()> { +pub async fn expect_end(snip: Snippet<'_, impl ExprRepr, impl ExtraTok>) -> OrcRes<()> { match snip.skip_fluff().get(0) { Some(surplus) => Err(mk_errv( - ctx.i().i("Extra code after end of line").await, + is("Extra code after end of line").await, "Code found after the end of the line", [surplus.sr.pos()], )), @@ -185,28 +165,26 @@ pub async fn expect_end( } pub async fn expect_tok<'a, A: ExprRepr, X: ExtraTok>( - ctx: &impl ParseCtx, snip: Snippet<'a, A, X>, - tok: Tok, + tok: IStr, ) -> ParseRes<'a, (), A, X> { - let Parsed { output: head, tail } = try_pop_no_fluff(ctx, snip).await?; + let Parsed { output: head, tail } = try_pop_no_fluff(snip).await?; match &head.tok { Token::Name(n) if *n == tok => Ok(Parsed { output: (), tail }), t => Err(mk_errv( - ctx.i().i("Expected specific keyword").await, - format!("Expected {tok} but found {:?}", fmt(t, ctx.i()).await), + is("Expected specific keyword").await, + format!("Expected {tok} but found {:?}", fmt(t).await), [head.sr()], )), } } pub async fn token_errv( - ctx: &impl ParseCtx, tok: &TokTree, description: &'static str, message: impl FnOnce(&str) -> String, ) -> OrcErrv { - mk_errv(ctx.i().i(description).await, message(&fmt(tok, ctx.i()).await), [tok.sr.pos()]) + mk_errv(is(description).await, message(&fmt(tok).await), [tok.sr.pos()]) } pub struct Parsed<'a, T, H: ExprRepr, X: ExtraTok> { @@ -217,33 +195,27 @@ pub struct Parsed<'a, T, H: ExprRepr, X: ExtraTok> { pub type ParseRes<'a, T, H, X> = OrcRes>; pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>( - ctx: &impl ParseCtx, tail: Snippet<'a, A, X>, ) -> ParseRes<'a, Vec, A, X> { let Some((tt, tail)) = tail.skip_fluff().pop_front() else { return Err(mk_errv( - ctx.i().i("Expected token").await, + is("Expected token").await, "Expected a name, a parenthesized list of names, or a globstar.", [tail.sr().pos()], )); }; - let ret = rec(tt, ctx).await; + let ret = rec(tt).await; #[allow(clippy::type_complexity)] // it's an internal function pub async fn rec( tt: &TokTree, - ctx: &impl ParseCtx, - ) -> OrcRes>, Option>, SrcRange)>> { + ) -> OrcRes, Option, SrcRange)>> { let ttpos = tt.sr.pos(); match &tt.tok { Token::NS(ns, body) => { if !ns.starts_with(name_start) { - ctx.rep().report(mk_errv( - ctx.i().i("Unexpected name prefix").await, - "Only names can precede ::", - [ttpos], - )) + report(mk_errv(is("Unexpected name prefix").await, "Only names can precede ::", [ttpos])) }; - let out = Box::pin(rec(body, ctx)).await?; + let out = Box::pin(rec(body)).await?; Ok(out.into_iter().update(|i| i.0.push(ns.clone())).collect_vec()) }, Token::Name(ntok) => { @@ -255,21 +227,19 @@ pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>( let mut o = Vec::new(); let mut body = Snippet::new(tt, b); while let Some((output, tail)) = body.pop_front() { - match rec(output, ctx).boxed_local().await { + match rec(output).boxed_local().await { Ok(names) => o.extend(names), - Err(e) => ctx.rep().report(e), + Err(e) => report(e), } body = tail; } Ok(o) }, - t => { - return Err(mk_errv( - ctx.i().i("Unrecognized name end").await, - format!("Names cannot end with {:?} tokens", fmt(t, ctx.i()).await), - [ttpos], - )); - }, + t => Err(mk_errv( + is("Unrecognized name end").await, + format!("Names cannot end with {:?} tokens", fmt(t).await), + [ttpos], + )), } } ret.map(|output| { @@ -285,7 +255,7 @@ pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>( #[derive(Debug, Clone)] pub struct Import { pub path: VPath, - pub name: Option>, + pub name: Option, pub sr: SrcRange, } impl Import { @@ -296,14 +266,14 @@ impl Import { None => self.path.into_name().expect("Import cannot be empty"), } } - pub fn new(sr: SrcRange, path: VPath, name: Tok) -> Self { + pub fn new(sr: SrcRange, path: VPath, name: IStr) -> Self { Import { path, name: Some(name), sr } } pub fn new_glob(sr: SrcRange, path: VPath) -> Self { Import { path, name: None, sr } } } impl Display for Import { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}::{}", self.path.iter().join("::"), self.name.as_ref().map_or("*", |t| t.as_str())) + write!(f, "{}::{}", self.path.iter().join("::"), self.name.as_ref().map_or("*", |t| &**t)) } } diff --git a/orchid-base/src/pipe.rs b/orchid-base/src/pipe.rs deleted file mode 100644 index 8b13789..0000000 --- a/orchid-base/src/pipe.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/orchid-base/src/reqnot.rs b/orchid-base/src/reqnot.rs index 8d843da..5dd9b9e 100644 --- a/orchid-base/src/reqnot.rs +++ b/orchid-base/src/reqnot.rs @@ -3,31 +3,23 @@ use std::cell::RefCell; use std::collections::VecDeque; use std::future::Future; use std::marker::PhantomData; -use std::mem; -use std::ops::{BitAnd, Deref}; use std::pin::{Pin, pin}; use std::rc::Rc; -use std::sync::Arc; use std::task::Poll; -use std::thread::panicking; use async_fn_stream::stream; use bound::Bound; use derive_destructure::destructure; -use dyn_clone::{DynClone, clone_box}; use futures::channel::mpsc::{self, Receiver, Sender, channel}; use futures::channel::oneshot; -use futures::future::{LocalBoxFuture, select_all}; +use futures::future::LocalBoxFuture; use futures::lock::{Mutex, MutexGuard}; use futures::{AsyncRead, AsyncWrite, SinkExt, Stream, StreamExt, stream, stream_select}; use hashbrown::HashMap; -use orchid_api_traits::{Channel, Coding, Decode, Encode, MsgSet, Request, UnderRoot, enc_vec}; -use some_executor::task_local; -use trait_set::trait_set; - -use crate::clone; -use crate::logging::Logger; +use orchid_api_traits::{Decode, Encode, Request, UnderRoot}; +#[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 ()>); /// Write guard to outbound for the purpose of serializing a request. Only one @@ -98,11 +90,11 @@ impl ClientExt for T {} /// A form of [Evidence] that doesn't require the value to be kept around pub struct Witness(PhantomData); impl Witness { - fn of(t: &T) -> Self { Self(PhantomData) } + pub fn of(_: &T) -> Self { Self(PhantomData) } } impl Copy for Witness {} impl Clone for Witness { - fn clone(&self) -> Self { Self(PhantomData) } + fn clone(&self) -> Self { *self } } /// A proxy for the type of a value either previously saved into a [Witness] or @@ -172,12 +164,6 @@ impl<'a> NotifReader<'a> { pub async fn release(self) {} } -#[derive(Clone)] -struct IO { - i: IoLock, - o: IoLock, -} - struct ReplySub { id: u64, ack: oneshot::Sender<()>, @@ -192,9 +178,7 @@ struct IoClient { notif_tid: TypeId, } impl IoClient { - pub async fn new( - output: IoLock, - ) -> (Receiver, Self) { + fn new(output: IoLock) -> (Receiver, Self) { let (req, rep) = mpsc::channel(0); (rep, Self { output, @@ -227,8 +211,8 @@ impl Client for IoClient { }; let (cb, reply) = oneshot::channel(); let (ack, got_ack) = oneshot::channel(); - self.subscribe.as_ref().clone().send(ReplySub { id, ack, cb }).await; - got_ack.await; + 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 @@ -273,430 +257,176 @@ impl MsgWriter for IoNotifWriter { pub struct CommCtx { quit: Sender<()>, - client: Rc, } impl CommCtx { pub async fn quit(self) { self.quit.clone().send(()).await.expect("quit channel dropped"); } - pub fn client(&self) -> Rc { self.client.clone() as Rc } } -/// This function will exit only when one of the callbacks calls -/// [CommCtx::quit]. -pub async fn io_comm( +/// Establish bidirectional request-notification communication over a duplex +/// channel. The returned [IoClient] can be used for notifications immediately, +/// but requests can only be received while the future is running. The future +/// will only resolve when [CommCtx::quit] is called. +pub fn io_comm( o: Rc>>>, i: Mutex>>, notif: impl for<'a> AsyncFn(&mut CommCtx, NotifReader<'a>), - req: impl for<'a> AsyncFn(&mut CommCtx, ReqReader<'a>), -) { + req: impl for<'a> AsyncFn(&mut CommCtx, ReqReader<'a>) -> Receipt<'a>, +) -> (impl Client, impl Future) { let i = Rc::new(i); - let (onsub, client) = IoClient::new::(o.clone()).await; - let client = Rc::new(client); - let (exit, onexit) = channel(1); - enum Event { - Input(u64), - Sub(ReplySub), - Exit, - } - let exiting = RefCell::new(false); - let input_stream = stream(async |mut h| { - loop { - let id = u64::decode(i.lock().await.as_mut()).await; - h.emit(Event::Input(id)).await; + let (onsub, client) = IoClient::new::(o.clone()); + (client, async move { + let (exit, onexit) = channel(1); + enum Event { + Input(u64), + Sub(ReplySub), + Exit, } - }); - let pending_reqs = RefCell::new(VecDeque::>::new()); - // this stream will never yield a value - let mut fork_stream = pin!( - stream::poll_fn(|cx| { - let mut reqs_g = pending_reqs.borrow_mut(); - reqs_g.retain_mut(|req| match req.as_mut().poll(cx) { - Poll::Pending => true, - Poll::Ready(()) => false, - }); - if *exiting.borrow() { Poll::Ready(None) } else { Poll::Pending } - }) - .fuse() - ); - { - let mut shared = pin!(stream_select!( - pin!(input_stream) as Pin<&mut dyn Stream>, - onsub.map(Event::Sub), - fork_stream.as_mut(), - onexit.map(|()| Event::Exit), - )); + let exiting = RefCell::new(false); + let input_stream = stream(async |mut h| { + loop { + let id = u64::decode(i.lock().await.as_mut()).await; + h.emit(Event::Input(id)).await; + } + }); + let pending_reqs = RefCell::new(VecDeque::>::new()); + // this stream will never yield a value + let mut fork_stream = pin!( + stream::poll_fn(|cx| { + let mut reqs_g = pending_reqs.borrow_mut(); + reqs_g.retain_mut(|req| match req.as_mut().poll(cx) { + Poll::Pending => true, + Poll::Ready(()) => false, + }); + if *exiting.borrow() { Poll::Ready(None) } else { Poll::Pending } + }) + .fuse() + ); let mut pending_replies = HashMap::new(); - while let Some(next) = shared.next().await { - match next { - Event::Exit => { - *exiting.borrow_mut() = true; - break; - }, - Event::Sub(ReplySub { id, ack, cb }) => { - pending_replies.insert(id, cb); - ack.send(()); - }, - Event::Input(id) if id == 0 => { - let (i, notif, exit, client) = (i.clone(), ¬if, exit.clone(), client.clone()); - pending_reqs.borrow_mut().push_back(Box::pin(async move { - let g = i.lock().await; - notif(&mut CommCtx { client, quit: exit.clone() }, NotifReader { read: g }).await - })); - }, - // id.msb == 0 is a request, !id where id.msb == 1 is the equivalent response - Event::Input(id) => - if (id & (1 << (u64::BITS - 1))) == 0 { - let (i, o, req, exit, client) = - (i.clone(), o.clone(), &req, exit.clone(), client.clone()); + { + let mut shared = pin!(stream_select!( + pin!(input_stream) as Pin<&mut dyn Stream>, + onsub.map(Event::Sub), + fork_stream.as_mut(), + onexit.map(|()| Event::Exit), + )); + while let Some(next) = shared.next().await { + match next { + Event::Exit => { + *exiting.borrow_mut() = true; + break; + }, + Event::Sub(ReplySub { id, ack, cb }) => { + pending_replies.insert(id, cb); + ack.send(()).unwrap(); + }, + Event::Input(0) => { + let (i, notif, exit) = (i.clone(), ¬if, exit.clone()); pending_reqs.borrow_mut().push_back(Box::pin(async move { let g = i.lock().await; - req(&mut CommCtx { client, quit: exit.clone() }, ReqReader { - id, - read: g, - write: &*o, - }) - .await - }) as LocalBoxFuture<()>); - } else { - let cb = pending_replies.remove(&!id).expect("Reply to unrecognized request"); - let _ = cb.send(Bound::async_new(i.clone(), |i| i.lock()).await); + notif(&mut CommCtx { quit: exit.clone() }, NotifReader { read: g }).await + })); }, + // id.msb == 0 is a request, !id where id.msb == 1 is the equivalent response + Event::Input(id) => + if (id & (1 << (u64::BITS - 1))) == 0 { + let (i, o, req, exit) = (i.clone(), o.clone(), &req, exit.clone()); + pending_reqs.borrow_mut().push_back(Box::pin(async move { + let g = i.lock().await; + let _ = + req(&mut CommCtx { quit: exit.clone() }, ReqReader { id, read: g, write: &o }) + .await; + }) as LocalBoxFuture<()>); + } else { + let cb = pending_replies.remove(&!id).expect("Reply to unrecognized request"); + let _ = cb.send(Bound::async_new(i.clone(), |i| i.lock()).await); + }, + } } } - } - fork_stream.as_mut().count().await; -} - -trait_set! { - pub trait SendFn = - for<'a> FnMut(&'a [u8], ReqNot) -> LocalBoxFuture<'a, ()> - + DynClone + 'static; - pub trait ReqFn = - for<'a> FnMut(ReqReader<'a, T>, ::Req) - -> LocalBoxFuture<'a, Receipt<'a>> - + DynClone + 'static; - pub trait NotifFn = - FnMut(::Notif, ReqNot) -> LocalBoxFuture<'static, ()> - + DynClone + 'static; -} - -fn get_id(message: &[u8]) -> (u64, &[u8]) { - (u64::from_be_bytes(message[..8].to_vec().try_into().unwrap()), &message[8..]) -} - -pub trait ReqHandlish { - fn defer(&self, cb: impl Future + 'static) - where Self: Sized { - self.defer_objsafe(Box::pin(cb)); - } - fn defer_objsafe(&self, val: Pin>>); -} -impl ReqHandlish for &'_ dyn ReqHandlish { - fn defer_objsafe(&self, val: Pin>>) { (**self).defer_objsafe(val) } -} - -type LocalAsyncFnOnceBox = Box) -> LocalBoxFuture<'static, ()>>; - -#[derive(destructure)] -pub struct RequestHandle<'a, MS: MsgSet> { - defer: RefCell>>>>, - _reqlt: PhantomData<&'a mut ()>, - parent: ReqNot, - raw_reply: RefCell>, -} -impl<'a, MS: MsgSet + 'static> ReqReader<'a, MS> { - pub fn new(parent: ReqNot, raw_reply: impl AsyncFnOnce(Vec) + 'static) -> Self { - Self { - defer: RefCell::default(), - _reqlt: PhantomData, - parent, - raw_reply: RefCell::new(Some(Box::new(|v| Box::pin(raw_reply(v))))), - } - } - pub fn reqnot(&self) -> ReqNot { self.parent.clone() } - pub async fn handle(&self, _: &U, rep: &U::Response) -> Receipt<'a> { - self.respond(rep).await - } - pub fn will_handle_as(&self, _: &U) -> ReqTypToken { ReqTypToken(PhantomData) } - pub async fn handle_as(&self, _: ReqTypToken, rep: &U::Response) -> Receipt<'a> { - self.respond(rep).await - } - pub async fn respond(&self, response: &impl Encode) -> Receipt<'a> { - let replier = self.raw_reply.borrow_mut().take().expect("Already responded to request"); - let buf = enc_vec(response).await; - (replier)(buf).await; - let deferred = mem::take(&mut *self.defer.borrow_mut()); - for item in deferred { - item.await - } - Receipt(PhantomData) - } -} -impl ReqHandlish for ReqReader<'_, MS> { - fn defer_objsafe(&self, val: Pin>>) { - self.defer.borrow_mut().push(val) - } -} -impl Drop for ReqReader<'_, MS> { - fn drop(&mut self) { - if !panicking() { - debug_assert!(self.raw_reply.borrow().is_none(), "Request dropped without response") - } - } -} - -pub struct ReqTypToken(PhantomData); - -pub struct ReqNotData { - id: u64, - send: Box>, - notif: Box>, - req: Box>, - responses: HashMap>>, -} - -/// Wraps a raw message buffer to save on copying. -/// Dereferences to the tail of the message buffer, cutting off the ID -#[derive(Debug, Clone)] -pub struct RawReply(Vec); -impl Deref for RawReply { - type Target = [u8]; - fn deref(&self) -> &Self::Target { get_id(&self.0[..]).1 } -} - -pub struct ReqNot(Arc>>, Logger); -impl ReqNot { - pub fn new( - logger: Logger, - send: impl SendFn, - notif: impl NotifFn, - req: impl ReqFn, - ) -> Self { - Self( - Arc::new(Mutex::new(ReqNotData { - id: 1, - send: Box::new(send), - notif: Box::new(notif), - req: Box::new(req), - responses: HashMap::new(), - })), - logger, - ) - } - - /// Can be called from a polling thread or dispatched in any other way - pub async fn receive(&self, message: &[u8]) { - let mut g = self.0.lock().await; - let (id, payload) = get_id(message); - if id == 0 { - let mut notif_cb = clone_box(&*g.notif); - mem::drop(g); - let notif_val = ::Notif::decode(Pin::new(&mut &payload[..])).await; - notif_cb(notif_val, self.clone()).await - } else if 0 < id.bitand(1 << 63) { - let mut sender = g.responses.remove(&!id).expect("Received response for invalid message"); - let _ = sender.send(message.to_vec()).await; - } else { - let message = ::Req::decode(Pin::new(&mut &payload[..])).await; - let mut req_cb = clone_box(&*g.req); - mem::drop(g); - let rn = self.clone(); - let rn2 = self.clone(); - req_cb( - ReqReader::new(rn, async move |vec| { - let mut buf = (!id).to_be_bytes().to_vec(); - buf.extend(vec); - let mut send = clone_box(&*rn2.0.lock().await.send); - (send)(&buf, rn2.clone()).await; - }), - message, - ) - .await; - } - } - - pub async fn notify::Notif>>(&self, notif: N) { - let mut send = clone_box(&*self.0.lock().await.send); - let mut buf = vec![0; 8]; - let msg: ::Notif = notif.into(); - msg.encode(Pin::new(&mut buf)).await; - send(&buf, self.clone()).await - } -} - -pub trait DynRequester { - type Transfer; - fn logger(&self) -> &Logger; - /// Encode and send a request, then receive the response buffer. - fn raw_request(&self, data: Self::Transfer) -> LocalBoxFuture<'_, RawReply>; -} - -pub struct MappedRequester<'a, T: 'a>(Box LocalBoxFuture<'a, RawReply> + 'a>, Logger); -impl<'a, T> MappedRequester<'a, T> { - fn new U::Transfer + 'a>( - req: U, - cb: F, - logger: Logger, - ) -> Self { - let req_arc = Arc::new(req); - let cb_arc = Arc::new(cb); - MappedRequester( - Box::new(move |t| { - Box::pin(clone!(req_arc, cb_arc; async move { req_arc.raw_request(cb_arc(t)).await})) - }), - logger, - ) - } -} - -impl DynRequester for MappedRequester<'_, T> { - type Transfer = T; - fn logger(&self) -> &Logger { &self.1 } - fn raw_request(&self, data: Self::Transfer) -> LocalBoxFuture<'_, RawReply> { self.0(data) } -} - -impl DynRequester for ReqNot { - type Transfer = ::Req; - fn logger(&self) -> &Logger { &self.1 } - fn raw_request(&self, req: Self::Transfer) -> LocalBoxFuture<'_, RawReply> { - Box::pin(async move { - let mut g = self.0.lock().await; - let id = g.id; - g.id += 1; - let mut buf = id.to_be_bytes().to_vec(); - req.encode(Pin::new(&mut buf)).await; - let (send, mut recv) = mpsc::channel(1); - g.responses.insert(id, send); - let mut send = clone_box(&*g.send); - mem::drop(g); - let rn = self.clone(); - send(&buf, rn).await; - let items = recv.next().await; - RawReply(items.unwrap()) - }) - } -} - -pub trait Requester: DynRequester { - #[must_use = "These types are subject to change with protocol versions. \ - If you don't want to use the return value, At a minimum, force the type."] - fn request>( - &self, - data: R, - ) -> impl Future; - fn map<'a, U>(self, cb: impl Fn(U) -> Self::Transfer + 'a) -> MappedRequester<'a, U> - where Self: Sized + 'a { - let logger = self.logger().clone(); - MappedRequester::new(self, cb, logger) - } -} - -impl Requester for This { - async fn request>(&self, data: R) -> R::Response { - let req = format!("{data:?}"); - let rep = R::Response::decode(Pin::new(&mut &self.raw_request(data.into()).await[..])).await; - let req_str = req.to_string(); - if !req_str.starts_with("AtomPrint") && !req_str.starts_with("ExtAtomPrint") { - writeln!(self.logger(), "Request {req} got response {rep:?}"); - } - rep - } -} - -impl Clone for ReqNot { - fn clone(&self) -> Self { Self(self.0.clone(), self.1.clone()) } + fork_stream.as_mut().count().await; + }) } #[cfg(test)] mod test { + use std::cell::RefCell; use std::rc::Rc; - use std::sync::Arc; - use futures::FutureExt; + use futures::join; use futures::lock::Mutex; - use orchid_api_derive::Coding; - use orchid_api_traits::{Channel, Request}; + use never::Never; + use orchid_api_derive::{Coding, Hierarchy}; + use orchid_api_traits::Request; use test_executors::spin_on; + use unsync_pipe::pipe; - use super::{MsgSet, ReqNot}; - use crate::logging::Logger; - use crate::reqnot::Requester as _; - use crate::{api, clone}; + use crate::reqnot::{ClientExt, NotifReader, io_comm}; - #[derive(Clone, Debug, Coding, PartialEq)] - pub struct TestReq(u8); - impl Request for TestReq { - type Response = u8; - } - - pub struct TestChan; - impl Channel for TestChan { - type Notif = u8; - type Req = TestReq; - } - - pub struct TestMsgSet; - impl MsgSet for TestMsgSet { - type In = TestChan; - type Out = TestChan; - } + #[derive(Clone, Debug, PartialEq, Coding, Hierarchy)] + #[extendable] + struct TestNotif(u64); #[test] fn notification() { spin_on(async { - let logger = Logger::new(api::LogStrategy::StdErr); - let received = Arc::new(Mutex::new(None)); - let receiver = ReqNot::::new( - logger.clone(), - |_, _| panic!("Should not send anything"), - clone!(received; move |notif, _| clone!(received; async move { - *received.lock().await = Some(notif); - }.boxed_local())), - |_, _| panic!("Not receiving a request"), + let (in1, out2) = pipe(1024); + let (in2, out1) = pipe(1024); + let received = RefCell::new(None); + let (_, run_receiver) = io_comm::( + Rc::new(Mutex::new(Box::pin(in2))), + Mutex::new(Box::pin(out2)), + async |_, notif: NotifReader| { + *received.borrow_mut() = Some(notif.read::().await) + }, + async |_, _| panic!("Should receive notif, not request"), ); - let sender = ReqNot::::new( - logger, - clone!(receiver; move |d, _| clone!(receiver; Box::pin(async move { - receiver.receive(d).await - }))), - |_, _| panic!("Should not receive notif"), - |_, _| panic!("Should not receive 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"), ); - sender.notify(3).await; - assert_eq!(*received.lock().await, Some(3)); - sender.notify(4).await; - assert_eq!(*received.lock().await, Some(4)); + join!(run_receiver, async { + sender.notify(TestNotif(3)).await; + assert_eq!(*received.borrow(), Some(TestNotif(3))); + sender.notify(TestNotif(4)).await; + assert_eq!(*received.borrow(), Some(TestNotif(4))); + }); }) } + #[derive(Clone, Debug, Coding, Hierarchy)] + #[extendable] + struct DummyRequest(u64); + impl Request for DummyRequest { + type Response = u64; + } + #[test] fn request() { spin_on(async { - let logger = Logger::new(api::LogStrategy::StdErr); - let receiver = Rc::new(Mutex::>>::new(None)); - let sender = Rc::new(ReqNot::::new( - logger.clone(), - clone!(receiver; move |d, _| clone!(receiver; Box::pin(async move { - receiver.lock().await.as_ref().unwrap().receive(d).await - }))), - |_, _| panic!("Should not receive notif"), - |_, _| panic!("Should not receive request"), - )); - *receiver.lock().await = Some(ReqNot::new( - logger, - clone!(sender; move |d, _| clone!(sender; Box::pin(async move { - sender.receive(d).await - }))), - |_, _| panic!("Not receiving notifs"), - |hand, req| { - Box::pin(async move { - assert_eq!(req, TestReq(5)); - hand.respond(&6u8).await - }) + let (in1, out2) = pipe(1024); + let (in2, out1) = pipe(1024); + let (_, run_server) = io_comm::( + Rc::new(Mutex::new(Box::pin(in2))), + Mutex::new(Box::pin(out2)), + async |_, _| panic!("No notifs expected"), + async |_, mut req| { + let val = req.read_req::().await; + req.reply(&val, &(val.0 + 1)).await }, - )); - let response = sender.request(TestReq(5)).await; - assert_eq!(response, 6); + ); + let (client, 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_server, run_client, async { + let response = client.request(DummyRequest(5)).await; + assert_eq!(response, 6); + }); }) } } diff --git a/orchid-base/src/tree.rs b/orchid-base/src/tree.rs index 32adc07..6056f52 100644 --- a/orchid-base/src/tree.rs +++ b/orchid-base/src/tree.rs @@ -14,7 +14,7 @@ use trait_set::trait_set; use crate::error::OrcErrv; use crate::format::{FmtCtx, FmtUnit, Format, Variants}; -use crate::interner::{Interner, Tok}; +use crate::interner::{IStr, es}; use crate::location::{Pos, SrcRange}; use crate::name::{Sym, VName, VPath}; use crate::parse::Snippet; @@ -28,7 +28,6 @@ pub trait TokenVariant: Format + Clone + fmt:: api: &ApiEquiv, ctx: &mut Self::FromApiCtx<'_>, pos: SrcRange, - i: &Interner, ) -> impl Future; #[must_use] fn into_api(self, ctx: &mut Self::ToApiCtx<'_>) -> impl Future; @@ -36,7 +35,7 @@ pub trait TokenVariant: Format + Clone + fmt:: impl TokenVariant for Never { type FromApiCtx<'a> = (); type ToApiCtx<'a> = (); - async fn from_api(_: &T, _: &mut Self::FromApiCtx<'_>, _: SrcRange, _: &Interner) -> Self { + async fn from_api(_: &T, _: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self { panic!("Cannot deserialize Never") } async fn into_api(self, _: &mut Self::ToApiCtx<'_>) -> T { match self {} } @@ -108,20 +107,19 @@ impl TokTree { hctx: &mut H::FromApiCtx<'_>, xctx: &mut X::FromApiCtx<'_>, src: &Sym, - i: &Interner, ) -> Self { let pos = SrcRange::new(tt.range.clone(), src); let tok = match_mapping!(&tt.token, api::Token => Token:: { BR, - NS(n => Tok::from_api(*n, i).await, - b => Box::new(Self::from_api(b, hctx, xctx, src, i).boxed_local().await)), - Bottom(e => OrcErrv::from_api(e, i).await), - LambdaHead(arg => Box::new(Self::from_api(arg, hctx, xctx, src, i).boxed_local().await)), - Name(n => Tok::from_api(*n, i).await), - S(*par, b => ttv_from_api(b, hctx, xctx, src, i).await), - Comment(c.clone()), - NewExpr(expr => X::from_api(expr, xctx, pos.clone(), i).await), - Handle(tk => H::from_api(tk, hctx, pos.clone(), i).await) + NS(n => es(*n).await, + b => Box::new(Self::from_api(b, hctx, xctx, src).boxed_local().await)), + Bottom(e => OrcErrv::from_api(e).await), + LambdaHead(arg => Box::new(Self::from_api(arg, hctx, xctx, src).boxed_local().await)), + Name(n => es(*n).await), + S(*par, b => ttv_from_api(b, hctx, xctx, src).await), + Comment(c => es(*c).await), + NewExpr(expr => X::from_api(expr, xctx, pos.clone()).await), + Handle(tk => H::from_api(tk, hctx, pos.clone()).await) }); Self { sr: pos, tok } } @@ -135,7 +133,7 @@ impl TokTree { BR, NS(n.to_api(), b => Box::new(b.into_api(hctx, xctx).boxed_local().await)), Bottom(e.to_api()), - Comment(c.clone()), + Comment(c.to_api()), LambdaHead(arg => Box::new(arg.into_api(hctx, xctx).boxed_local().await)), Name(nn.to_api()), S(p, b => ttv_into_api(b, hctx, xctx).boxed_local().await), @@ -145,8 +143,8 @@ impl TokTree { api::TokenTree { range: self.sr.range.clone(), token } } - pub fn is_kw(&self, tk: Tok) -> bool { self.tok.is_kw(tk) } - pub fn as_name(&self) -> Option> { + pub fn is_kw(&self, tk: IStr) -> bool { self.tok.is_kw(tk) } + pub fn as_name(&self) -> Option { if let Token::Name(n) = &self.tok { Some(n.clone()) } else { None } } pub fn as_multiname(&self) -> Result> { @@ -193,11 +191,10 @@ pub async fn ttv_from_api( hctx: &mut H::FromApiCtx<'_>, xctx: &mut X::FromApiCtx<'_>, src: &Sym, - i: &Interner, ) -> Vec> { stream(async |mut cx| { for tok in tokv { - cx.emit(TokTree::::from_api(tok.borrow(), hctx, xctx, src, i).boxed_local().await).await + cx.emit(TokTree::::from_api(tok.borrow(), hctx, xctx, src).boxed_local().await).await } }) .collect() @@ -240,14 +237,14 @@ pub enum Token { /// Information about the code addressed to the human reader or dev tooling /// It has no effect on the behaviour of the program unless it's explicitly /// read via reflection - Comment(Rc), + Comment(IStr), /// The part of a lambda between `\` and `.` enclosing the argument. The body /// stretches to the end of the enclosing parens or the end of the const line LambdaHead(Box>), /// A binding, operator, or a segment of a namespaced::name - Name(Tok), + Name(IStr), /// A namespace prefix, like `my_ns::` followed by a token - NS(Tok, Box>), + NS(IStr, Box>), /// A line break BR, /// `()`, `[]`, or `{}` @@ -263,7 +260,7 @@ pub enum Token { } impl Token { pub fn at(self, sr: SrcRange) -> TokTree { TokTree { sr, tok: self } } - pub fn is_kw(&self, tk: Tok) -> bool { matches!(self, Token::Name(n) if *n == tk) } + pub fn is_kw(&self, tk: IStr) -> bool { matches!(self, Token::Name(n) if *n == tk) } pub fn as_s(&self, par: Paren) -> Option<&[TokTree]> { match self { Self::S(p, b) if *p == par => Some(b), diff --git a/orchid-base/src/virt_fs/common.rs b/orchid-base/src/virt_fs/common.rs index 47689be..e22876a 100644 --- a/orchid-base/src/virt_fs/common.rs +++ b/orchid-base/src/virt_fs/common.rs @@ -9,19 +9,19 @@ use crate::proj_error::{ErrorSansOrigin, ErrorSansOriginObj}; /// as the file system. Cheap to clone. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Loaded { - /// Conceptually equivalent to a sourcefile - Code(Arc), - /// Conceptually equivalent to the list of *.orc files in a folder, without - /// the extension - Collection(Arc>>), + /// Conceptually equivalent to a sourcefile + Code(Arc), + /// Conceptually equivalent to the list of *.orc files in a folder, without + /// the extension + Collection(Arc>), } impl Loaded { - /// Is the loaded item source code (not a collection)? - pub fn is_code(&self) -> bool { matches!(self, Loaded::Code(_)) } - /// Collect the elements in a collection rreport - pub fn collection(items: impl IntoIterator>) -> Self { - Self::Collection(Arc::new(items.into_iter().collect())) - } + /// Is the loaded item source code (not a collection)? + pub fn is_code(&self) -> bool { matches!(self, Loaded::Code(_)) } + /// Collect the elements in a collection rreport + pub fn collection(items: impl IntoIterator) -> Self { + Self::Collection(Arc::new(items.into_iter().collect())) + } } /// Returned by any source loading callback @@ -30,66 +30,62 @@ pub type FSResult = Result; /// Type that indicates the type of an entry without reading the contents #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub enum FSKind { - /// Invalid path or read error - None, - /// Source code - Code, - /// Internal tree node - Collection, + /// Invalid path or read error + None, + /// Source code + Code, + /// Internal tree node + Collection, } /// Distinguished error for missing code #[derive(Clone, PartialEq, Eq)] pub struct CodeNotFound(pub VPath); impl CodeNotFound { - /// Instantiate error - pub fn new(path: VPath) -> Self { Self(path) } + /// Instantiate error + pub fn new(path: VPath) -> Self { Self(path) } } impl ErrorSansOrigin for CodeNotFound { - const DESCRIPTION: &'static str = "No source code for path"; - fn message(&self) -> String { format!("{} not found", self.0) } + const DESCRIPTION: &'static str = "No source code for path"; + fn message(&self) -> String { format!("{} not found", self.0) } } /// A simplified view of a file system for the purposes of source code loading. /// This includes the real FS and source code, but also various in-memory /// formats and other sources for libraries and dependencies. pub trait VirtFS { - /// Implementation of [VirtFS::read] - fn get(&self, path: &[Tok], full_path: &PathSlice) -> FSResult; - /// Discover information about a path without reading it. - /// - /// Implement this if your vfs backend can do expensive operations - fn kind(&self, path: &PathSlice) -> FSKind { - match self.read(path) { - Err(_) => FSKind::None, - Ok(Loaded::Code(_)) => FSKind::Code, - Ok(Loaded::Collection(_)) => FSKind::Collection, - } - } - /// Convert a path into a human-readable string that is meaningful in the - /// target context. - fn display(&self, path: &[Tok]) -> Option; - /// Convert the FS handler into a type-erased version of itself for packing in - /// a tree. - fn rc(self) -> Rc - where Self: Sized + 'static { - Rc::new(self) - } - /// Read a path, returning either a text file, a directory listing or an - /// error. Wrapper for [VirtFS::get] - fn read(&self, path: &PathSlice) -> FSResult { self.get(path, path) } + /// Implementation of [VirtFS::read] + fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult; + /// Discover information about a path without reading it. + /// + /// Implement this if your vfs backend can do expensive operations + fn kind(&self, path: &PathSlice) -> FSKind { + match self.read(path) { + Err(_) => FSKind::None, + Ok(Loaded::Code(_)) => FSKind::Code, + Ok(Loaded::Collection(_)) => FSKind::Collection, + } + } + /// Convert a path into a human-readable string that is meaningful in the + /// target context. + fn display(&self, path: &[IStr]) -> Option; + /// Convert the FS handler into a type-erased version of itself for packing in + /// a tree. + fn rc(self) -> Rc + where Self: Sized + 'static { + Rc::new(self) + } + /// Read a path, returning either a text file, a directory listing or an + /// error. Wrapper for [VirtFS::get] + fn read(&self, path: &PathSlice) -> FSResult { self.get(path, path) } } impl VirtFS for &dyn VirtFS { - fn get(&self, path: &[Tok], full_path: &PathSlice) -> FSResult { - (*self).get(path, full_path) - } - fn display(&self, path: &[Tok]) -> Option { (*self).display(path) } + fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult { (*self).get(path, full_path) } + fn display(&self, path: &[IStr]) -> Option { (*self).display(path) } } impl VirtFS for Rc { - fn get(&self, path: &[Tok], full_path: &PathSlice) -> FSResult { - (**self).get(path, full_path) - } - fn display(&self, path: &[Tok]) -> Option { (**self).display(path) } + fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult { (**self).get(path, full_path) } + fn display(&self, path: &[IStr]) -> Option { (**self).display(path) } } diff --git a/orchid-base/src/virt_fs/decl.rs b/orchid-base/src/virt_fs/decl.rs index ffc060c..84c9f46 100644 --- a/orchid-base/src/virt_fs/decl.rs +++ b/orchid-base/src/virt_fs/decl.rs @@ -32,7 +32,7 @@ impl<'a> Combine for &'a dyn VirtFS { pub type DeclTree = ModEntry, (), ()>; impl VirtFS for DeclTree { - fn get(&self, path: &[Tok], full_path: &PathSlice) -> FSResult { + fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult { match &self.member { ModMember::Item(it) => it.get(path, full_path), ModMember::Sub(module) => match path.split_first() { @@ -44,7 +44,7 @@ impl VirtFS for DeclTree { } } - fn display(&self, path: &[Tok]) -> Option { + fn display(&self, path: &[IStr]) -> Option { let (head, tail) = path.split_first()?; match &self.member { ModMember::Item(it) => it.display(path), @@ -54,16 +54,16 @@ impl VirtFS for DeclTree { } impl VirtFS for String { - fn display(&self, _: &[Tok]) -> Option { None } - fn get(&self, path: &[Tok], full_path: &PathSlice) -> FSResult { + fn display(&self, _: &[IStr]) -> Option { None } + fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult { (path.is_empty().then(|| Loaded::Code(Arc::new(self.as_str().to_string())))) .ok_or_else(|| CodeNotFound::new(full_path.to_vpath()).pack()) } } impl<'a> VirtFS for &'a str { - fn display(&self, _: &[Tok]) -> Option { None } - fn get(&self, path: &[Tok], full_path: &PathSlice) -> FSResult { + fn display(&self, _: &[IStr]) -> Option { None } + fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult { (path.is_empty().then(|| Loaded::Code(Arc::new(self.to_string())))) .ok_or_else(|| CodeNotFound::new(full_path.to_vpath()).pack()) } diff --git a/orchid-base/src/virt_fs/dir.rs b/orchid-base/src/virt_fs/dir.rs index 728b802..6ea89c3 100644 --- a/orchid-base/src/virt_fs/dir.rs +++ b/orchid-base/src/virt_fs/dir.rs @@ -99,14 +99,14 @@ impl DirNode { } } - fn mk_pathbuf(&self, path: &[Tok]) -> PathBuf { + fn mk_pathbuf(&self, path: &[IStr]) -> PathBuf { let mut fpath = self.root.clone(); path.iter().for_each(|seg| fpath.push(seg.as_str())); fpath } } impl VirtFS for DirNode { - fn get(&self, path: &[Tok], full_path: &PathSlice) -> FSResult { + fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult { let fpath = self.mk_pathbuf(path); let mut binding = self.cached.borrow_mut(); let (_, res) = (binding.raw_entry_mut().from_key(&fpath)) @@ -114,7 +114,7 @@ impl VirtFS for DirNode { res.clone() } - fn display(&self, path: &[Tok]) -> Option { + fn display(&self, path: &[IStr]) -> Option { let pathbuf = self.mk_pathbuf(path).with_extension(self.ext()); Some(pathbuf.to_string_lossy().to_string()) } diff --git a/orchid-base/src/virt_fs/embed.rs b/orchid-base/src/virt_fs/embed.rs index b0ec218..6357b7b 100644 --- a/orchid-base/src/virt_fs/embed.rs +++ b/orchid-base/src/virt_fs/embed.rs @@ -56,7 +56,7 @@ impl EmbeddedFS { } impl VirtFS for EmbeddedFS { - fn get(&self, path: &[Tok], full_path: &PathSlice) -> FSResult { + fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult { if path.is_empty() { return Ok(Loaded::collection(self.tree.keys(|_| true))); } @@ -67,7 +67,7 @@ impl VirtFS for EmbeddedFS { ModMember::Sub(sub) => Loaded::collection(sub.keys(|_| true)), }) } - fn display(&self, path: &[Tok]) -> Option { + fn display(&self, path: &[IStr]) -> Option { let Self { gen, suffix, .. } = self; Some(format!("{}{suffix} in {gen}", path.iter().join("/"))) } diff --git a/orchid-base/src/virt_fs/prefix.rs b/orchid-base/src/virt_fs/prefix.rs index cb29d60..ae036ef 100644 --- a/orchid-base/src/virt_fs/prefix.rs +++ b/orchid-base/src/virt_fs/prefix.rs @@ -21,18 +21,18 @@ impl<'a> PrefixFS<'a> { add: VPath::parse(add.as_ref()), } } - fn proc_path(&self, path: &[Tok]) -> Option>> { + fn proc_path(&self, path: &[IStr]) -> Option> { let path = path.strip_prefix(self.remove.as_slice())?; Some(self.add.0.iter().chain(path).cloned().collect_vec()) } } impl<'a> VirtFS for PrefixFS<'a> { - fn get(&self, path: &[Tok], full_path: &PathSlice) -> super::FSResult { + fn get(&self, path: &[IStr], full_path: &PathSlice) -> super::FSResult { let path = self.proc_path(path).ok_or_else(|| CodeNotFound::new(full_path.to_vpath()).pack())?; self.wrapped.get(&path, full_path) } - fn display(&self, path: &[Tok]) -> Option { + fn display(&self, path: &[IStr]) -> Option { self.wrapped.display(&self.proc_path(path)?) } } diff --git a/unsync-pipe/Cargo.toml b/unsync-pipe/Cargo.toml index 5ffce61..a1a2856 100644 --- a/unsync-pipe/Cargo.toml +++ b/unsync-pipe/Cargo.toml @@ -8,10 +8,11 @@ repository = "https://git.lbfalvy.com/Orchid/orchid" homepage = "https://git.lbfalvy.com/Orchid/orchid" [dev-dependencies] +futures = "0.3.31" itertools = "0.14.0" rand = "0.9.2" rand_chacha = "0.9.0" test_executors = "0.4.0" [dependencies] -futures = "0.3.31" +futures-io = "0.3.31" diff --git a/unsync-pipe/src/lib.rs b/unsync-pipe/src/lib.rs index 8efab4f..227d7d7 100644 --- a/unsync-pipe/src/lib.rs +++ b/unsync-pipe/src/lib.rs @@ -11,7 +11,7 @@ use std::ptr::{null, null_mut, slice_from_raw_parts}; use std::task::{Context, Poll, Waker}; use std::{io, mem}; -use futures::{AsyncRead, AsyncWrite}; +use futures_io::{AsyncRead, AsyncWrite}; fn pipe_layout(bs: usize) -> Layout { Layout::from_size_align(bs, 1).expect("1-align is trivial") }