Orchid-base uses task-local context.

Everything else is broken at the moment.
This commit is contained in:
2025-12-14 17:17:43 +01:00
parent 8753d4c751
commit 0b2b05d44e
22 changed files with 463 additions and 1082 deletions

2
Cargo.lock generated
View File

@@ -1060,6 +1060,7 @@ dependencies = [
"substack", "substack",
"test_executors 0.3.5", "test_executors 0.3.5",
"trait-set", "trait-set",
"unsync-pipe",
] ]
[[package]] [[package]]
@@ -1949,6 +1950,7 @@ name = "unsync-pipe"
version = "0.2.0" version = "0.2.0"
dependencies = [ dependencies = [
"futures", "futures",
"futures-io",
"itertools", "itertools",
"rand 0.9.2", "rand 0.9.2",
"rand_chacha 0.9.0", "rand_chacha 0.9.0",

View File

@@ -1,5 +1,4 @@
use core::fmt; use core::fmt;
use std::future::Future;
use super::coding::Coding; use super::coding::Coding;
use crate::helpers::enc_vec; use crate::helpers::enc_vec;

View File

@@ -2,7 +2,6 @@ use std::collections::HashMap;
use std::fmt; use std::fmt;
use std::num::NonZeroU64; use std::num::NonZeroU64;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc;
use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request; use orchid_api_traits::Request;
@@ -48,7 +47,7 @@ pub enum Token {
/// NewExpr(Bottom) because it fails in dead branches too. /// NewExpr(Bottom) because it fails in dead branches too.
Bottom(Vec<OrcError>), Bottom(Vec<OrcError>),
/// A comment /// A comment
Comment(Rc<String>), Comment(TStr),
} }
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Coding)] #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Coding)]

View File

@@ -6,6 +6,7 @@ edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" }
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" } async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
async-once-cell = "0.5.4" async-once-cell = "0.5.4"
bound = "0.6.0" bound = "0.6.0"

View File

@@ -2,13 +2,15 @@ use std::cell::RefCell;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fmt; use std::fmt;
use std::ops::Add; use std::ops::Add;
use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use futures::future::join_all; use futures::future::join_all;
use itertools::Itertools; use itertools::Itertools;
use some_executor::task_local;
use crate::api; use crate::api;
use crate::interner::{Interner, Tok}; use crate::interner::{IStr, es, is};
use crate::location::Pos; use crate::location::Pos;
/// A point of interest in resolving the error, such as the point where /// 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 { pub fn new(msg: &str, position: Pos) -> Self {
Self { message: Some(Arc::new(msg.to_string())), position } 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 { Self {
message: Some(api.message.clone()).filter(|s| !s.is_empty()), 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 { fn to_api(&self) -> api::ErrLocation {
@@ -51,7 +53,7 @@ impl fmt::Display for ErrPos {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct OrcErr { pub struct OrcErr {
pub description: Tok<String>, pub description: IStr,
pub message: Arc<String>, pub message: Arc<String>,
pub positions: Vec<ErrPos>, pub positions: Vec<ErrPos>,
} }
@@ -63,16 +65,16 @@ impl OrcErr {
locations: self.positions.iter().map(ErrPos::to_api).collect(), 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 { Self {
description: Tok::from_api(api.description, i).await, description: es(api.description).await,
message: api.message.clone(), 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<Tok<String>> for OrcErr { impl PartialEq<IStr> for OrcErr {
fn eq(&self, other: &Tok<String>) -> bool { self.description == *other } fn eq(&self, other: &IStr) -> bool { self.description == *other }
} }
impl From<OrcErr> for Vec<OrcErr> { impl From<OrcErr> for Vec<OrcErr> {
fn from(value: OrcErr) -> Self { vec![value] } fn from(value: OrcErr) -> Self { vec![value] }
@@ -122,11 +124,8 @@ impl OrcErrv {
self.0.iter().flat_map(|e| e.positions.iter().cloned()) self.0.iter().flat_map(|e| e.positions.iter().cloned())
} }
pub fn to_api(&self) -> Vec<api::OrcError> { self.0.iter().map(OrcErr::to_api).collect() } pub fn to_api(&self) -> Vec<api::OrcError> { self.0.iter().map(OrcErr::to_api).collect() }
pub async fn from_api<'a>( pub async fn from_api<'a>(api: impl IntoIterator<Item = &'a api::OrcError>) -> Self {
api: impl IntoIterator<Item = &'a api::OrcError>, Self(join_all(api.into_iter().map(OrcErr::from_api)).await)
i: &Interner,
) -> Self {
Self(join_all(api.into_iter().map(|e| OrcErr::from_api(e, i))).await)
} }
} }
impl From<OrcErr> for OrcErrv { impl From<OrcErr> for OrcErrv {
@@ -191,12 +190,12 @@ macro_rules! join_ok {
(@VALUES) => { Ok(()) }; (@VALUES) => { Ok(()) };
} }
pub fn mk_errv_floating(description: Tok<String>, message: impl AsRef<str>) -> OrcErrv { pub fn mk_errv_floating(description: IStr, message: impl AsRef<str>) -> OrcErrv {
mk_errv::<Pos>(description, message, []) mk_errv::<Pos>(description, message, [])
} }
pub fn mk_errv<I: Into<ErrPos>>( pub fn mk_errv<I: Into<ErrPos>>(
description: Tok<String>, description: IStr,
message: impl AsRef<str>, message: impl AsRef<str>,
posv: impl IntoIterator<Item = I>, posv: impl IntoIterator<Item = I>,
) -> OrcErrv { ) -> OrcErrv {
@@ -210,45 +209,61 @@ pub fn mk_errv<I: Into<ErrPos>>(
pub async fn async_io_err<I: Into<ErrPos>>( pub async fn async_io_err<I: Into<ErrPos>>(
err: std::io::Error, err: std::io::Error,
i: &Interner,
posv: impl IntoIterator<Item = I>, posv: impl IntoIterator<Item = I>,
) -> OrcErrv { ) -> 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<ErrPos>>( pub async fn os_str_to_string<I: Into<ErrPos>>(
str: &'a OsStr, str: &OsStr,
i: &Interner,
posv: impl IntoIterator<Item = I>, posv: impl IntoIterator<Item = I>,
) -> OrcRes<&'a str> { ) -> OrcRes<&str> {
match str.to_str() { match str.to_str() {
Some(str) => Ok(str), Some(str) => Ok(str),
None => Err(mk_errv( None => Err(mk_errv(
i.i("Non-unicode string").await, is("Non-unicode string").await,
format!("{str:?} is not representable as unicode"), format!("{str:?} is not representable as unicode"),
posv, posv,
)), )),
} }
} }
pub struct Reporter { #[derive(Clone, Default)]
errors: RefCell<Vec<OrcErr>>, struct Reporter {
errors: Rc<RefCell<Vec<OrcErr>>>,
} }
impl Reporter { task_local! {
pub fn report(&self, e: impl Into<OrcErrv>) { self.errors.borrow_mut().extend(e.into()) } static REPORTER: Reporter;
pub fn new() -> Self { Self { errors: RefCell::new(vec![]) } }
pub fn errv(self) -> Option<OrcErrv> { OrcErrv::new(self.errors.into_inner()).ok() }
pub fn merge<T>(self, res: OrcRes<T>) -> OrcRes<T> {
match (res, self.errv()) {
(res, None) => res,
(Ok(_), Some(errv)) => Err(errv),
(Err(e), Some(errv)) => Err(e + errv),
}
}
pub fn is_empty(&self) -> bool { self.errors.borrow().is_empty() }
} }
impl Default for Reporter { pub async fn with_reporter<T>(fut: impl Future<Output = OrcRes<T>>) -> OrcRes<T> {
fn default() -> Self { Self::new() } 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 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<OrcErrv>) {
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}",
),
})
} }

View File

@@ -3,6 +3,7 @@ use std::cmp::Ordering;
use std::convert::Infallible; use std::convert::Infallible;
use std::future::Future; use std::future::Future;
use std::iter; use std::iter;
use std::marker::PhantomData;
use std::rc::Rc; use std::rc::Rc;
use std::str::FromStr; use std::str::FromStr;
@@ -11,7 +12,6 @@ use itertools::{Itertools, chain};
use never::Never; use never::Never;
use regex::Regex; use regex::Regex;
use crate::interner::Interner;
use crate::{api, match_mapping}; use crate::{api, match_mapping};
#[derive(Clone, Debug, Hash, PartialEq, Eq)] #[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) fill_slots(&first.elements, &unit.subs, 0, bounded)
} }
pub async fn take_first_fmt(v: &(impl Format + ?Sized), i: &Interner) -> String { pub async fn take_first_fmt(v: &(impl Format + ?Sized)) -> String {
take_first(&v.print(&FmtCtxImpl { i }).await, false) take_first(&v.print(&FmtCtxImpl { _foo: PhantomData }).await, false)
} }
pub struct FmtCtxImpl<'a> { pub struct FmtCtxImpl<'a> {
pub i: &'a Interner, _foo: PhantomData<&'a ()>,
} }
pub trait FmtCtx { pub trait FmtCtx {
fn i(&self) -> &Interner;
// fn print_as(&self, p: &(impl Format + ?Sized)) -> impl Future<Output = // fn print_as(&self, p: &(impl Format + ?Sized)) -> impl Future<Output =
// String> where Self: Sized { // String> where Self: Sized {
// async { // async {
@@ -319,9 +318,7 @@ pub trait FmtCtx {
// } // }
// } // }
} }
impl FmtCtx for FmtCtxImpl<'_> { impl FmtCtx for FmtCtxImpl<'_> {}
fn i(&self) -> &Interner { self.i }
}
pub trait Format { pub trait Format {
#[must_use] #[must_use]
@@ -332,13 +329,10 @@ impl Format for Never {
} }
/// Format with default strategy. Currently equal to [take_first_fmt] /// 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] /// Format a sequence with default strategy. Currently equal to [take_first_fmt]
pub async fn fmt_v<F: Format + ?Sized, R: Borrow<F>>( pub async fn fmt_v<F: Format + ?Sized, R: Borrow<F>>(
v: impl IntoIterator<Item = R>, v: impl IntoIterator<Item = R>,
i: &Interner,
) -> impl Iterator<Item = String> { ) -> impl Iterator<Item = String> {
join_all(v.into_iter().map(|f| async move { take_first_fmt(f.borrow(), i).await })) join_all(v.into_iter().map(|f| async move { take_first_fmt(f.borrow()).await })).await.into_iter()
.await
.into_iter()
} }

View File

@@ -1,28 +1,28 @@
use std::borrow::Borrow;
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
use std::future::Future; use std::future::Future;
use std::hash::{BuildHasher as _, Hash}; use std::hash::Hash;
use std::num::NonZeroU64;
use std::ops::Deref; use std::ops::Deref;
use std::rc::Rc; use std::rc::Rc;
use std::sync::atomic;
use std::{fmt, hash}; use std::{fmt, hash};
use futures::future::LocalBoxFuture; 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 some_executor::task_local;
use crate::api; use crate::api;
use crate::reqnot::{DynRequester, Requester};
pub trait IStrHandle: AsRef<str> {} pub trait IStrHandle: AsRef<str> {}
pub trait IStrvHandle: AsRef<[IStr]> {} pub trait IStrvHandle: AsRef<[IStr]> {}
#[derive(Clone)] #[derive(Clone)]
pub struct IStr(pub api::TStr, pub Rc<dyn IStrHandle>); pub struct IStr(pub api::TStr, pub Rc<dyn IStrHandle>);
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 { impl Deref for IStr {
type Target = str; type Target = str;
fn deref(&self) -> &Self::Target { self.1.as_ref().as_ref() } fn deref(&self) -> &Self::Target { self.1.as_ref().as_ref() }
@@ -42,6 +42,14 @@ impl Debug for IStr {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct IStrv(pub api::TStrv, pub Rc<dyn IStrvHandle>); pub struct IStrv(pub api::TStrv, pub Rc<dyn IStrvHandle>);
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 { impl Deref for IStrv {
type Target = [IStr]; type Target = [IStr];
fn deref(&self) -> &Self::Target { self.1.as_ref().as_ref() } 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 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 es(v: api::TStr) -> IStr { get_interner().es(v).await }
pub async fn ev(v: api::TStrv) -> IStrv { get_interner().ev(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>(T);
#[derive(Clone)]
pub struct Tok<T: Interned> {
data: Rc<T>,
marker: ForceSized<T::Marker>,
}
impl<T: Interned> Tok<T> {
pub fn new(data: Rc<T>, marker: T::Marker) -> Self { Self { data, marker: ForceSized(marker) } }
pub fn to_api(&self) -> T::Marker { self.marker.0 }
pub async fn from_api<M>(marker: M, i: &Interner) -> Self
where M: InternMarker<Interned = T> {
i.ex(marker).await
}
pub fn rc(&self) -> Rc<T> { self.data.clone() }
}
impl<T: Interned> Deref for Tok<T> {
type Target = T;
fn deref(&self) -> &Self::Target { self.data.as_ref() }
}
impl<T: Interned> Ord for Tok<T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.to_api().cmp(&other.to_api()) }
}
impl<T: Interned> PartialOrd for Tok<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) }
}
impl<T: Interned> Eq for Tok<T> {}
impl<T: Interned> PartialEq for Tok<T> {
fn eq(&self, other: &Self) -> bool { self.cmp(other).is_eq() }
}
impl<T: Interned> hash::Hash for Tok<T> {
fn hash<H: hash::Hasher>(&self, state: &mut H) { self.to_api().hash(state) }
}
impl<T: Interned + fmt::Display> fmt::Display for Tok<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &*self.data)
}
}
impl<T: Interned + fmt::Debug> fmt::Debug for Tok<T> {
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<Interned = Self> {
type Marker: InternMarker<Interned = Self> + Sized;
fn intern(
self: Rc<Self>,
req: &(impl DynRequester<Transfer = api::IntReq> + ?Sized),
) -> impl Future<Output = Self::Marker>;
fn bimap(interner: &mut TypedInterners) -> &mut Bimap<Self>;
}
pub trait Internable: fmt::Debug {
type Interned: Interned;
fn get_owned(&self) -> Rc<Self::Interned>;
}
pub trait InternMarker: Copy + PartialEq + Eq + PartialOrd + Ord + hash::Hash + Sized {
type Interned: Interned<Marker = Self>;
/// Only called on replicas
fn resolve(self, i: &Interner) -> impl Future<Output = Tok<Self::Interned>>;
fn get_id(self) -> NonZeroU64;
fn from_id(id: NonZeroU64) -> Self;
}
impl Interned for String {
type Marker = api::TStr;
async fn intern(
self: Rc<Self>,
req: &(impl DynRequester<Transfer = api::IntReq> + ?Sized),
) -> Self::Marker {
req.request(api::InternStr(self.to_string())).await
}
fn bimap(interners: &mut TypedInterners) -> &mut Bimap<Self> { &mut interners.strings }
}
impl InternMarker for api::TStr {
type Interned = String;
async fn resolve(self, i: &Interner) -> Tok<Self::Interned> {
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<Self::Interned> { Rc::new(self.to_string()) }
}
impl Internable for String {
type Interned = String;
fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_string()) }
}
impl Interned for Vec<Tok<String>> {
type Marker = api::TStrv;
async fn intern(
self: Rc<Self>,
req: &(impl DynRequester<Transfer = api::IntReq> + ?Sized),
) -> Self::Marker {
req.request(api::InternStrv(self.iter().map(|t| t.to_api()).collect())).await
}
fn bimap(interners: &mut TypedInterners) -> &mut Bimap<Self> { &mut interners.vecs }
}
impl InternMarker for api::TStrv {
type Interned = Vec<Tok<String>>;
async fn resolve(self, i: &Interner) -> Tok<Self::Interned> {
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<String>] {
type Interned = Vec<Tok<String>>;
fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_vec()) }
}
impl<const N: usize> Internable for [Tok<String>; N] {
type Interned = Vec<Tok<String>>;
fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_vec()) }
}
impl Internable for Vec<Tok<String>> {
type Interned = Vec<Tok<String>>;
fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_vec()) }
}
// impl Internable for Vec<api::TStr> {
// type Interned = Vec<Tok<String>>;
// fn get_owned(&self) -> Arc<Self::Interned> {
// Arc::new(self.iter().map(|ts| deintern(*ts)).collect())
// }
// }
// impl Internable for [api::TStr] {
// type Interned = Vec<Tok<String>>;
// fn get_owned(&self) -> Arc<Self::Interned> {
// 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<T: Interned> {
intern: HashMap<Rc<T>, Tok<T>>,
by_id: HashMap<T::Marker, Tok<T>>,
}
impl<T: Interned> Bimap<T> {
pub fn insert(&mut self, token: Tok<T>) {
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<Tok<T>> { self.by_id.get(&marker).cloned() }
pub fn by_value<Q: Eq + hash::Hash>(&self, q: &Q) -> Option<Tok<T>>
where T: Borrow<Q> {
(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<T::Marker> {
(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<T::Marker>) {
self.intern.retain(|k, v| BASE_RC < Rc::strong_count(k) || retained.contains(&v.to_api()))
}
}
impl<T: Interned> Default for Bimap<T> {
fn default() -> Self { Self { by_id: HashMap::new(), intern: HashMap::new() } }
}
pub trait UpComm {
fn up<R: Request>(&self, req: R) -> R::Response;
}
#[derive(Default)]
pub struct TypedInterners {
strings: Bimap<String>,
vecs: Bimap<Vec<Tok<String>>>,
}
#[derive(Default)]
pub struct InternerData {
interners: Mutex<TypedInterners>,
master: Option<Box<dyn DynRequester<Transfer = api::IntReq>>>,
}
#[derive(Clone, Default)]
pub struct Interner(Rc<InternerData>);
impl Interner {
pub fn new_master() -> Self { Self::default() }
pub fn new_replica(req: impl DynRequester<Transfer = api::IntReq> + '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<T: Interned>(&self, t: &(impl Internable<Interned = T> + ?Sized)) -> Tok<T> {
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<M: InternMarker>(&self, marker: M) -> Tok<M::Interned> {
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<String> = spin_on(i.i("foo"));
let _: Tok<Vec<Tok<String>>> = 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:?}")
})
}
}

View File

@@ -21,7 +21,6 @@ pub mod msg;
pub mod name; pub mod name;
pub mod number; pub mod number;
pub mod parse; pub mod parse;
pub mod pipe;
pub mod pure_seq; pub mod pure_seq;
pub mod reqnot; pub mod reqnot;
pub mod sequence; pub mod sequence;

View File

@@ -8,12 +8,12 @@ use futures::future::join_all;
use trait_set::trait_set; use trait_set::trait_set;
use crate::error::ErrPos; use crate::error::ErrPos;
use crate::interner::{Interner, Tok}; use crate::interner::{IStr, es, is};
use crate::name::Sym; use crate::name::Sym;
use crate::{api, match_mapping, sym}; use crate::{api, match_mapping, sym};
trait_set! { trait_set! {
pub trait GetSrc = FnMut(&Sym) -> Tok<String>; pub trait GetSrc = FnMut(&Sym) -> IStr;
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@@ -37,13 +37,13 @@ impl Pos {
other => format!("{other:?}"), 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 { match_mapping!(api, api::Location => Pos {
None, Inherit, SlotTarget, None, Inherit, SlotTarget,
Gen(cgi => CodeGenInfo::from_api(cgi, i).await), Gen(cgi => CodeGenInfo::from_api(cgi).await),
Multi(v => join_all(v.iter().map(|l| Pos::from_api(l, i))).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 { pub fn to_api(&self) -> api::Location {
@@ -108,7 +108,7 @@ impl SrcRange {
} }
/// Create a dud [SourceRange] for testing. Its value is unspecified and /// Create a dud [SourceRange] for testing. Its value is unspecified and
/// volatile. /// 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 /// Path the source text was loaded from
pub fn path(&self) -> Sym { self.path.clone() } pub fn path(&self) -> Sym { self.path.clone() }
/// Byte range /// Byte range
@@ -133,8 +133,8 @@ impl SrcRange {
} }
} }
pub fn zw(path: Sym, pos: u32) -> Self { Self { path, range: pos..pos } } pub fn zw(path: Sym, pos: u32) -> Self { Self { path, range: pos..pos } }
pub async fn from_api(api: &api::SourceRange, i: &Interner) -> Self { pub async fn from_api(api: &api::SourceRange) -> Self {
Self { path: Sym::from_api(api.path, i).await, range: api.range.clone() } Self { path: Sym::from_api(api.path).await, range: api.range.clone() }
} }
pub fn to_api(&self) -> api::SourceRange { pub fn to_api(&self) -> api::SourceRange {
api::SourceRange { path: self.path.to_api(), range: self.range.clone() } api::SourceRange { path: self.path.to_api(), range: self.range.clone() }
@@ -162,24 +162,19 @@ pub struct CodeGenInfo {
/// formatted like a Rust namespace /// formatted like a Rust namespace
pub generator: Sym, pub generator: Sym,
/// Unformatted user message with relevant circumstances and parameters /// Unformatted user message with relevant circumstances and parameters
pub details: Tok<String>, pub details: IStr,
} }
impl CodeGenInfo { impl CodeGenInfo {
/// A codegen marker with no user message and parameters /// A codegen marker with no user message and parameters
pub async fn new_short(generator: Sym, i: &Interner) -> Self { pub async fn new_short(generator: Sym) -> Self { Self { generator, details: is("").await } }
Self { generator, details: i.i("").await }
}
/// A codegen marker with a user message or parameters /// A codegen marker with a user message or parameters
pub async fn new_details(generator: Sym, details: impl AsRef<str>, i: &Interner) -> Self { pub async fn new_details(generator: Sym, details: impl AsRef<str>) -> Self {
Self { generator, details: i.i(details.as_ref()).await } Self { generator, details: is(details.as_ref()).await }
} }
/// Syntactic location /// Syntactic location
pub fn pos(&self) -> Pos { Pos::Gen(self.clone()) } pub fn pos(&self) -> Pos { Pos::Gen(self.clone()) }
pub async fn from_api(api: &api::CodeGenInfo, i: &Interner) -> Self { pub async fn from_api(api: &api::CodeGenInfo) -> Self {
Self { Self { generator: Sym::from_api(api.generator).await, details: es(api.details).await }
generator: Sym::from_api(api.generator, i).await,
details: Tok::from_api(api.details, i).await,
}
} }
pub fn to_api(&self) -> api::CodeGenInfo { pub fn to_api(&self) -> api::CodeGenInfo {
api::CodeGenInfo { generator: self.generator.to_api(), details: self.details.to_api() } api::CodeGenInfo { generator: self.generator.to_api(), details: self.details.to_api() }

View File

@@ -12,65 +12,60 @@ use itertools::Itertools;
use trait_set::trait_set; use trait_set::trait_set;
use crate::api; use crate::api;
use crate::interner::{InternMarker, Interner, Tok}; use crate::interner::{IStr, IStrv, es, ev, is, iv};
trait_set! { trait_set! {
/// Traits that all name iterators should implement /// Traits that all name iterators should implement
pub trait NameIter = Iterator<Item = Tok<String>> + DoubleEndedIterator + ExactSizeIterator; pub trait NameIter = Iterator<Item = IStr> + DoubleEndedIterator + ExactSizeIterator;
} }
/// A token path which may be empty. [VName] is the non-empty version /// A token path which may be empty. [VName] is the non-empty version
#[derive(Clone, Default, Hash, PartialEq, Eq)] #[derive(Clone, Default, Hash, PartialEq, Eq)]
pub struct VPath(Vec<Tok<String>>); pub struct VPath(Vec<IStr>);
impl VPath { impl VPath {
/// Collect segments into a vector /// Collect segments into a vector
pub fn new(items: impl IntoIterator<Item = Tok<String>>) -> Self { pub fn new(items: impl IntoIterator<Item = IStr>) -> Self { Self(items.into_iter().collect()) }
Self(items.into_iter().collect())
}
/// Number of path segments /// Number of path segments
pub fn len(&self) -> usize { self.0.len() } pub fn len(&self) -> usize { self.0.len() }
/// Whether there are any path segments. In other words, whether this is a /// Whether there are any path segments. In other words, whether this is a
/// valid name /// valid name
pub fn is_empty(&self) -> bool { self.len() == 0 } pub fn is_empty(&self) -> bool { self.len() == 0 }
/// Prepend some tokens to the path /// Prepend some tokens to the path
pub fn prefix(self, items: impl IntoIterator<Item = Tok<String>>) -> Self { pub fn prefix(self, items: impl IntoIterator<Item = IStr>) -> Self {
Self(items.into_iter().chain(self.0).collect()) Self(items.into_iter().chain(self.0).collect())
} }
/// Append some tokens to the path /// Append some tokens to the path
pub fn suffix(self, items: impl IntoIterator<Item = Tok<String>>) -> Self { pub fn suffix(self, items: impl IntoIterator<Item = IStr>) -> Self {
Self(self.0.into_iter().chain(items).collect()) Self(self.0.into_iter().chain(items).collect())
} }
/// Partition the string by `::` namespace separators /// Partition the string by `::` namespace separators
pub async fn parse(s: &str, i: &Interner) -> Self { pub async fn parse(s: &str) -> Self {
Self(if s.is_empty() { vec![] } else { join_all(s.split("::").map(|s| i.i(s))).await }) Self(if s.is_empty() { vec![] } else { join_all(s.split("::").map(is)).await })
} }
/// Walk over the segments /// Walk over the segments
pub fn str_iter(&self) -> impl Iterator<Item = &'_ str> { pub fn str_iter(&self) -> impl Iterator<Item = &'_ str> { Box::new(self.0.iter().map(|s| &**s)) }
Box::new(self.0.iter().map(|s| s.as_str()))
}
/// Try to convert into non-empty version /// Try to convert into non-empty version
pub fn into_name(self) -> Result<VName, EmptyNameError> { VName::new(self.0) } pub fn into_name(self) -> Result<VName, EmptyNameError> { VName::new(self.0) }
/// Add a token to the path. Since now we know that it can't be empty, turn it /// Add a token to the path. Since now we know that it can't be empty, turn it
/// into a name. /// into a name.
pub fn name_with_suffix(self, name: Tok<String>) -> VName { pub fn name_with_suffix(self, name: IStr) -> VName {
VName(self.into_iter().chain([name]).collect()) VName(self.into_iter().chain([name]).collect())
} }
/// Add a token to the beginning of the. Since now we know that it can't be /// Add a token to the beginning of the. Since now we know that it can't be
/// empty, turn it into a name. /// empty, turn it into a name.
pub fn name_with_prefix(self, name: Tok<String>) -> VName { pub fn name_with_prefix(self, name: IStr) -> VName {
VName([name].into_iter().chain(self).collect()) VName([name].into_iter().chain(self).collect())
} }
/// Convert a fs path to a vpath /// Convert a fs path to a vpath
pub async fn from_path(path: &Path, ext: &str, i: &Interner) -> Option<(Self, bool)> { pub async fn from_path(path: &Path, ext: &str) -> Option<(Self, bool)> {
async fn to_vpath(p: &Path, i: &Interner) -> Option<VPath> { async fn to_vpath(p: &Path) -> Option<VPath> {
let tok_opt_v = let tok_opt_v = join_all(p.iter().map(|c| OptionFuture::from(c.to_str().map(is)))).await;
join_all(p.iter().map(|c| OptionFuture::from(c.to_str().map(|s| i.i(s))))).await;
tok_opt_v.into_iter().collect::<Option<_>>().map(VPath) tok_opt_v.into_iter().collect::<Option<_>>().map(VPath)
} }
match path.extension().map(|s| s.to_str()) { match path.extension().map(|s| s.to_str()) {
Some(Some(s)) if s == ext => Some((to_vpath(&path.with_extension(""), i).await?, true)), Some(Some(s)) if s == ext => Some((to_vpath(&path.with_extension("")).await?, true)),
None => Some((to_vpath(path, i).await?, false)), None => Some((to_vpath(path).await?, false)),
Some(_) => None, Some(_) => None,
} }
} }
@@ -83,30 +78,28 @@ impl fmt::Display for VPath {
write!(f, "{}", self.str_iter().join("::")) write!(f, "{}", self.str_iter().join("::"))
} }
} }
impl FromIterator<Tok<String>> for VPath { impl FromIterator<IStr> for VPath {
fn from_iter<T: IntoIterator<Item = Tok<String>>>(iter: T) -> Self { fn from_iter<T: IntoIterator<Item = IStr>>(iter: T) -> Self { Self(iter.into_iter().collect()) }
Self(iter.into_iter().collect())
}
} }
impl IntoIterator for VPath { impl IntoIterator for VPath {
type Item = Tok<String>; type Item = IStr;
type IntoIter = vec::IntoIter<Self::Item>; type IntoIter = vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
} }
impl Borrow<[Tok<String>]> for VPath { impl Borrow<[IStr]> for VPath {
fn borrow(&self) -> &[Tok<String>] { &self.0[..] } fn borrow(&self) -> &[IStr] { &self.0[..] }
} }
impl Deref for VPath { impl Deref for VPath {
type Target = [Tok<String>]; type Target = [IStr];
fn deref(&self) -> &Self::Target { self.borrow() } fn deref(&self) -> &Self::Target { self.borrow() }
} }
impl<T> Index<T> for VPath impl<T> Index<T> for VPath
where [Tok<String>]: Index<T> where [IStr]: Index<T>
{ {
type Output = <[Tok<String>] as Index<T>>::Output; type Output = <[IStr] as Index<T>>::Output;
fn index(&self, index: T) -> &Self::Output { &Borrow::<[Tok<String>]>::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. /// A mutable representation of a namespaced identifier of at least one segment.
@@ -116,50 +109,43 @@ where [Tok<String>]: Index<T>
/// See also [Sym] for the immutable representation, and [VPath] for possibly /// See also [Sym] for the immutable representation, and [VPath] for possibly
/// empty values /// empty values
#[derive(Clone, Hash, PartialEq, Eq)] #[derive(Clone, Hash, PartialEq, Eq)]
pub struct VName(Vec<Tok<String>>); pub struct VName(Vec<IStr>);
impl VName { impl VName {
/// Assert that the sequence isn't empty and wrap it in [VName] to represent /// Assert that the sequence isn't empty and wrap it in [VName] to represent
/// this invariant /// this invariant
pub fn new(items: impl IntoIterator<Item = Tok<String>>) -> Result<Self, EmptyNameError> { pub fn new(items: impl IntoIterator<Item = IStr>) -> Result<Self, EmptyNameError> {
let data: Vec<_> = items.into_iter().collect(); let data: Vec<_> = items.into_iter().collect();
if data.is_empty() { Err(EmptyNameError) } else { Ok(Self(data)) } if data.is_empty() { Err(EmptyNameError) } else { Ok(Self(data)) }
} }
pub async fn deintern( pub async fn deintern(name: impl IntoIterator<Item = api::TStr>) -> Result<Self, EmptyNameError> {
name: impl IntoIterator<Item = api::TStr>, Self::new(join_all(name.into_iter().map(es)).await)
i: &Interner,
) -> Result<Self, EmptyNameError> {
Self::new(join_all(name.into_iter().map(|m| Tok::from_api(m, i))).await)
} }
/// Unwrap the enclosed vector /// Unwrap the enclosed vector
pub fn into_vec(self) -> Vec<Tok<String>> { self.0 } pub fn into_vec(self) -> Vec<IStr> { self.0 }
/// Get a reference to the enclosed vector /// Get a reference to the enclosed vector
pub fn vec(&self) -> &Vec<Tok<String>> { &self.0 } pub fn vec(&self) -> &Vec<IStr> { &self.0 }
/// Mutable access to the underlying vector. To ensure correct results, this /// Mutable access to the underlying vector. To ensure correct results, this
/// must never be empty. /// must never be empty.
pub fn vec_mut(&mut self) -> &mut Vec<Tok<String>> { &mut self.0 } pub fn vec_mut(&mut self) -> &mut Vec<IStr> { &mut self.0 }
/// Intern the name and return a [Sym] /// 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 /// If this name has only one segment, return it
pub fn as_root(&self) -> Option<Tok<String>> { self.0.iter().exactly_one().ok().cloned() } pub fn as_root(&self) -> Option<IStr> { self.0.iter().exactly_one().ok().cloned() }
/// Prepend the segments to this name /// Prepend the segments to this name
#[must_use = "This is a pure function"] #[must_use = "This is a pure function"]
pub fn prefix(self, items: impl IntoIterator<Item = Tok<String>>) -> Self { pub fn prefix(self, items: impl IntoIterator<Item = IStr>) -> Self {
Self(items.into_iter().chain(self.0).collect()) Self(items.into_iter().chain(self.0).collect())
} }
/// Append the segments to this name /// Append the segments to this name
#[must_use = "This is a pure function"] #[must_use = "This is a pure function"]
pub fn suffix(self, items: impl IntoIterator<Item = Tok<String>>) -> Self { pub fn suffix(self, items: impl IntoIterator<Item = IStr>) -> Self {
Self(self.0.into_iter().chain(items).collect()) Self(self.0.into_iter().chain(items).collect())
} }
/// Read a `::` separated namespaced name /// Read a `::` separated namespaced name
pub async fn parse(s: &str, i: &Interner) -> Result<Self, EmptyNameError> { pub async fn parse(s: &str) -> Result<Self, EmptyNameError> { Self::new(VPath::parse(s).await) }
Self::new(VPath::parse(s, i).await) pub async fn literal(s: &'static str) -> Self { Self::parse(s).await.expect("empty literal !?") }
}
pub async fn literal(s: &'static str, i: &Interner) -> Self {
Self::parse(s, i).await.expect("empty literal !?")
}
/// Obtain an iterator over the segments of the name /// Obtain an iterator over the segments of the name
pub fn iter(&self) -> impl Iterator<Item = Tok<String>> + '_ { self.0.iter().cloned() } pub fn iter(&self) -> impl Iterator<Item = IStr> + '_ { self.0.iter().cloned() }
} }
impl fmt::Debug for VName { impl fmt::Debug for VName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "VName({self})") } 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 { impl IntoIterator for VName {
type Item = Tok<String>; type Item = IStr;
type IntoIter = vec::IntoIter<Self::Item>; type IntoIter = vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
} }
impl<T> Index<T> for VName impl<T> Index<T> for VName
where [Tok<String>]: Index<T> where [IStr]: Index<T>
{ {
type Output = <[Tok<String>] as Index<T>>::Output; type Output = <[IStr] as Index<T>>::Output;
fn index(&self, index: T) -> &Self::Output { &self.deref()[index] } fn index(&self, index: T) -> &Self::Output { &self.deref()[index] }
} }
impl Borrow<[Tok<String>]> for VName { impl Borrow<[IStr]> for VName {
fn borrow(&self) -> &[Tok<String>] { self.0.borrow() } fn borrow(&self) -> &[IStr] { self.0.borrow() }
} }
impl Deref for VName { impl Deref for VName {
type Target = [Tok<String>]; type Target = [IStr];
fn deref(&self) -> &Self::Target { self.borrow() } fn deref(&self) -> &Self::Target { self.borrow() }
} }
@@ -193,11 +179,9 @@ impl Deref for VName {
/// empty sequence /// empty sequence
#[derive(Debug, Copy, Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Copy, Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct EmptyNameError; pub struct EmptyNameError;
impl TryFrom<&[Tok<String>]> for VName { impl TryFrom<&[IStr]> for VName {
type Error = EmptyNameError; type Error = EmptyNameError;
fn try_from(value: &[Tok<String>]) -> Result<Self, Self::Error> { fn try_from(value: &[IStr]) -> Result<Self, Self::Error> { Self::new(value.iter().cloned()) }
Self::new(value.iter().cloned())
}
} }
/// An interned representation of a namespaced identifier. /// An interned representation of a namespaced identifier.
@@ -206,37 +190,34 @@ impl TryFrom<&[Tok<String>]> for VName {
/// ///
/// See also [VName] /// See also [VName]
#[derive(Clone, Hash, PartialEq, Eq)] #[derive(Clone, Hash, PartialEq, Eq)]
pub struct Sym(Tok<Vec<Tok<String>>>); pub struct Sym(IStrv);
impl Sym { impl Sym {
/// Assert that the sequence isn't empty, intern it and wrap it in a [Sym] to /// Assert that the sequence isn't empty, intern it and wrap it in a [Sym] to
/// represent this invariant /// represent this invariant
pub async fn new( pub async fn new(v: impl IntoIterator<Item = IStr>) -> Result<Self, EmptyNameError> {
v: impl IntoIterator<Item = Tok<String>>,
i: &Interner,
) -> Result<Self, EmptyNameError> {
let items = v.into_iter().collect_vec(); 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. /// Read a `::` separated namespaced name.
pub async fn parse(s: &str, i: &Interner) -> Result<Self, EmptyNameError> { pub async fn parse(s: &str) -> Result<Self, EmptyNameError> {
Ok(Sym(i.i(&VName::parse(s, i).await?.into_vec()).await)) Ok(Sym(iv(&VName::parse(s).await?.into_vec()).await))
} }
/// Assert that a token isn't empty, and wrap it in a [Sym] /// Assert that a token isn't empty, and wrap it in a [Sym]
pub fn from_tok(t: Tok<Vec<Tok<String>>>) -> Result<Self, EmptyNameError> { pub fn from_tok(t: IStrv) -> Result<Self, EmptyNameError> {
if t.is_empty() { Err(EmptyNameError) } else { Ok(Self(t)) } if t.is_empty() { Err(EmptyNameError) } else { Ok(Self(t)) }
} }
/// Grab the interner token /// Grab the interner token
pub fn tok(&self) -> Tok<Vec<Tok<String>>> { self.0.clone() } pub fn tok(&self) -> IStrv { self.0.clone() }
/// Get a number unique to this name suitable for arbitrary ordering. /// 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 /// Extern the sym for editing
pub fn to_vname(&self) -> VName { VName(self[..].to_vec()) } pub fn to_vname(&self) -> VName { VName(self[..].to_vec()) }
pub async fn from_api(marker: api::TStrv, i: &Interner) -> Sym { pub async fn from_api(marker: api::TStrv) -> Sym {
Self::from_tok(Tok::from_api(marker, i).await).expect("Empty sequence found for serialized 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 fn to_api(&self) -> api::TStrv { self.tok().to_api() }
pub async fn suffix(&self, tokv: impl IntoIterator<Item = Tok<String>>, i: &Interner) -> Sym { pub async fn suffix(&self, tokv: impl IntoIterator<Item = IStr>) -> Sym {
Self::new(self.0.iter().cloned().chain(tokv), i).await.unwrap() Self::new(self.0.iter().cloned().chain(tokv)).await.unwrap()
} }
} }
impl fmt::Debug for Sym { impl fmt::Debug for Sym {
@@ -248,17 +229,17 @@ impl fmt::Display for Sym {
} }
} }
impl<T> Index<T> for Sym impl<T> Index<T> for Sym
where [Tok<String>]: Index<T> where [IStr]: Index<T>
{ {
type Output = <[Tok<String>] as Index<T>>::Output; type Output = <[IStr] as Index<T>>::Output;
fn index(&self, index: T) -> &Self::Output { &self.deref()[index] } fn index(&self, index: T) -> &Self::Output { &self.deref()[index] }
} }
impl Borrow<[Tok<String>]> for Sym { impl Borrow<[IStr]> for Sym {
fn borrow(&self) -> &[Tok<String>] { &self.0[..] } fn borrow(&self) -> &[IStr] { &self.0[..] }
} }
impl Deref for Sym { impl Deref for Sym {
type Target = [Tok<String>]; type Target = [IStr];
fn deref(&self) -> &Self::Target { self.borrow() } 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 /// handled together in datastructures. The names can never be empty
#[allow(clippy::len_without_is_empty)] // never empty #[allow(clippy::len_without_is_empty)] // never empty
pub trait NameLike: pub trait NameLike:
'static + Clone + Eq + Hash + fmt::Debug + fmt::Display + Borrow<[Tok<String>]> 'static + Clone + Eq + Hash + fmt::Debug + fmt::Display + Borrow<[IStr]>
{ {
/// Convert into held slice /// Convert into held slice
fn as_slice(&self) -> &[Tok<String>] { Borrow::<[Tok<String>]>::borrow(self) } fn as_slice(&self) -> &[IStr] { Borrow::<[IStr]>::borrow(self) }
/// Get iterator over tokens /// Get iterator over tokens
fn segs(&self) -> impl NameIter + '_ { self.as_slice().iter().cloned() } fn segs(&self) -> impl NameIter + '_ { self.as_slice().iter().cloned() }
/// Get iterator over string segments /// Get iterator over string segments
fn str_iter(&self) -> impl Iterator<Item = &'_ str> + '_ { fn str_iter(&self) -> impl Iterator<Item = &'_ str> + '_ { self.as_slice().iter().map(|t| &**t) }
self.as_slice().iter().map(|t| t.as_str())
}
/// Fully resolve the name for printing /// Fully resolve the name for printing
#[must_use] #[must_use]
fn to_strv(&self) -> Vec<String> { self.segs().map(|s| s.to_string()).collect() } fn to_strv(&self) -> Vec<String> { 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") NonZeroUsize::try_from(self.segs().count()).expect("NameLike never empty")
} }
/// Like slice's `split_first` except we know that it always returns Some /// Like slice's `split_first` except we know that it always returns Some
fn split_first_seg(&self) -> (Tok<String>, &[Tok<String>]) { fn split_first_seg(&self) -> (IStr, &[IStr]) {
let (foot, torso) = self.as_slice().split_last().expect("NameLike never empty"); let (foot, torso) = self.as_slice().split_last().expect("NameLike never empty");
(foot.clone(), torso) (foot.clone(), torso)
} }
/// Like slice's `split_last` except we know that it always returns Some /// Like slice's `split_last` except we know that it always returns Some
fn split_last_seg(&self) -> (Tok<String>, &[Tok<String>]) { fn split_last_seg(&self) -> (IStr, &[IStr]) {
let (foot, torso) = self.as_slice().split_last().expect("NameLike never empty"); let (foot, torso) = self.as_slice().split_last().expect("NameLike never empty");
(foot.clone(), torso) (foot.clone(), torso)
} }
/// Get the first element /// Get the first element
fn first_seg(&self) -> Tok<String> { self.split_first_seg().0 } fn first_seg(&self) -> IStr { self.split_first_seg().0 }
/// Get the last element /// Get the last element
fn last_seg(&self) -> Tok<String> { self.split_last_seg().0 } fn last_seg(&self) -> IStr { self.split_last_seg().0 }
} }
impl NameLike for Sym {} impl NameLike for Sym {}
@@ -311,11 +290,11 @@ impl NameLike for VName {}
/// cloning the token. /// cloning the token.
#[macro_export] #[macro_export]
macro_rules! sym { macro_rules! sym {
($seg1:tt $( :: $seg:tt)* ; $i:expr) => { ($seg1:tt $( :: $seg:tt)*) => {
$crate::name::Sym::from_tok( $crate::name::Sym::from_tok(
$i.i(&[ $crate::interner::iv(&[
$i.i(stringify!($seg1)).await $crate::interner::is(stringify!($seg1)).await
$( , $i.i(stringify!($seg)).await )* $( , $crate::interner::is(stringify!($seg)).await )*
]) ])
.await .await
).unwrap() ).unwrap()
@@ -327,10 +306,10 @@ macro_rules! sym {
/// The components are interned much like in [sym]. /// The components are interned much like in [sym].
#[macro_export] #[macro_export]
macro_rules! vname { macro_rules! vname {
($seg1:tt $( :: $seg:tt)* ; $i:expr) => { ($seg1:tt $( :: $seg:tt)*) => {
$crate::name::VName::new([ $crate::name::VName::new([
$i.i(stringify!($seg1)).await $crate::interner::is(stringify!($seg1)).await
$( , $i.i(stringify!($seg)).await )* $( , $crate::interner::is(stringify!($seg)).await )*
]).unwrap() ]).unwrap()
}; };
} }
@@ -340,10 +319,10 @@ macro_rules! vname {
/// The components are interned much like in [sym]. /// The components are interned much like in [sym].
#[macro_export] #[macro_export]
macro_rules! vpath { macro_rules! vpath {
($seg1:tt $( :: $seg:tt)+ ; $i:expr) => { ($seg1:tt $( :: $seg:tt)*) => {
$crate::name::VPath(vec![ $crate::name::VPath(vec![
$i.i(stringify!($seg1)).await $crate::interner::is(stringify!($seg1)).await
$( , $i.i(stringify!($seg)).await )+ $( , $crate::interner::is(stringify!($seg)).await )*
]) ])
}; };
() => { () => {
@@ -352,42 +331,33 @@ macro_rules! vpath {
} }
#[cfg(test)] #[cfg(test)]
mod test { pub mod test {
use std::borrow::Borrow; use std::borrow::Borrow;
use test_executors::spin_on;
use super::{NameLike, Sym, VName}; use super::{NameLike, Sym, VName};
use crate::interner::{Interner, Tok}; use crate::interner::{IStr, is};
use crate::name::VPath; use crate::name::VPath;
#[test] pub async fn recur() {
fn recur() { let myname = vname!(foo::bar);
spin_on(async { let _borrowed_slice: &[IStr] = myname.borrow();
let i = Interner::new_master(); let _deref_pathslice: &[IStr] = &myname;
let myname = vname!(foo::bar; i); let _as_slice_out: &[IStr] = myname.as_slice();
let _borrowed_slice: &[Tok<String>] = myname.borrow();
let _deref_pathslice: &[Tok<String>] = &myname;
let _as_slice_out: &[Tok<String>] = myname.as_slice();
})
} }
#[test] /// Tests that literals are correctly interned as equal
fn literals() { pub async fn literals() {
spin_on(async {
let i = Interner::new_master();
assert_eq!( assert_eq!(
sym!(foo::bar::baz; i), sym!(foo::bar::baz),
Sym::new([i.i("foo").await, i.i("bar").await, i.i("baz").await], &i).await.unwrap() Sym::new([is("foo").await, is("bar").await, is("baz").await]).await.unwrap()
); );
assert_eq!( assert_eq!(
vname!(foo::bar::baz; i), vname!(foo::bar::baz),
VName::new([i.i("foo").await, i.i("bar").await, i.i("baz").await]).unwrap() VName::new([is("foo").await, is("bar").await, is("baz").await]).unwrap()
); );
assert_eq!( assert_eq!(
vpath!(foo::bar::baz; i), vpath!(foo::bar::baz),
VPath::new([i.i("foo").await, i.i("bar").await, i.i("baz").await]) VPath::new([is("foo").await, is("bar").await, is("baz").await])
); );
})
} }
} }

View File

@@ -4,7 +4,7 @@ use std::ops::Range;
use ordered_float::NotNan; use ordered_float::NotNan;
use crate::error::{OrcErrv, mk_errv}; use crate::error::{OrcErrv, mk_errv};
use crate::interner::Interner; use crate::interner::is;
use crate::location::SrcRange; use crate::location::SrcRange;
use crate::name::Sym; use crate::name::Sym;
@@ -55,14 +55,9 @@ pub struct NumError {
pub kind: NumErrorKind, pub kind: NumErrorKind,
} }
pub async fn num_to_errv( pub async fn num_to_errv(NumError { kind, range }: NumError, offset: u32, source: &Sym) -> OrcErrv {
NumError { kind, range }: NumError,
offset: u32,
source: &Sym,
i: &Interner,
) -> OrcErrv {
mk_errv( mk_errv(
i.i("Failed to parse number").await, is("Failed to parse number").await,
match kind { match kind {
NumErrorKind::NaN => "NaN emerged during parsing", NumErrorKind::NaN => "NaN emerged during parsing",
NumErrorKind::InvalidDigit => "non-digit character encountered", NumErrorKind::InvalidDigit => "non-digit character encountered",

View File

@@ -7,28 +7,13 @@ use futures::future::join_all;
use itertools::Itertools; use itertools::Itertools;
use crate::api; 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::format::{FmtCtx, FmtUnit, Format, fmt};
use crate::interner::{Interner, Tok}; use crate::interner::{IStr, es, is};
use crate::location::SrcRange; use crate::location::SrcRange;
use crate::name::{Sym, VName, VPath}; use crate::name::{Sym, VName, VPath};
use crate::tree::{ExprRepr, ExtraTok, Paren, TokTree, Token, ttv_fmt, ttv_range}; 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_start(c: char) -> bool { c.is_alphabetic() || c == '_' }
pub fn name_char(c: char) -> bool { name_start(c) || c.is_numeric() } 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) } pub fn op_char(c: char) -> bool { !name_char(c) && !c.is_whitespace() && !"()[]{}\\".contains(c) }
@@ -103,22 +88,22 @@ impl<A: ExprRepr, X: ExtraTok> Format for Snippet<'_, A, X> {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Comment { pub struct Comment {
pub text: Tok<String>, pub text: IStr,
pub sr: SrcRange, pub sr: SrcRange,
} }
impl Comment { impl Comment {
// XXX: which of these four are actually used? // XXX: which of these four are actually used?
pub async fn from_api(c: &api::Comment, src: Sym, i: &Interner) -> Self { pub async fn from_api(c: &api::Comment, src: Sym) -> Self {
Self { text: i.ex(c.text).await, sr: SrcRange::new(c.range.clone(), &src) } Self { text: es(c.text).await, sr: SrcRange::new(c.range.clone(), &src) }
} }
pub async fn from_tk(tk: &TokTree<impl ExprRepr, impl ExtraTok>, i: &Interner) -> Option<Self> { pub async fn from_tk(tk: &TokTree<impl ExprRepr, impl ExtraTok>) -> Option<Self> {
match &tk.tok { 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, _ => None,
} }
} }
pub fn to_tk<R: ExprRepr, X: ExtraTok>(&self) -> TokTree<R, X> { pub fn to_tk<R: ExprRepr, X: ExtraTok>(&self) -> TokTree<R, X> {
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 { pub fn to_api(&self) -> api::Comment {
api::Comment { range: self.sr.range(), text: self.text.to_api() } 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>( pub async fn line_items<'a, A: ExprRepr, X: ExtraTok>(
ctx: &impl ParseCtx,
snip: Snippet<'a, A, X>, snip: Snippet<'a, A, X>,
) -> Vec<Parsed<'a, Vec<Comment>, A, X>> { ) -> Vec<Parsed<'a, Vec<Comment>, A, X>> {
let mut items = Vec::new(); let mut items = Vec::new();
@@ -145,9 +129,10 @@ pub async fn line_items<'a, A: ExprRepr, X: ExtraTok>(
None => comments.extend(line.cur), None => comments.extend(line.cur),
Some(i) => { Some(i) => {
let (cmts, tail) = line.split_at(i); let (cmts, tail) = line.split_at(i);
let comments = join_all(comments.drain(..).chain(cmts.cur).map(|t| async { let comments = join_all(
Comment::from_tk(t, ctx.i()).await.expect("All are comments checked above") (comments.drain(..).chain(cmts.cur))
})) .map(|t| async { Comment::from_tk(t).await.expect("All are comments checked above") }),
)
.await; .await;
items.push(Parsed { output: comments, tail }); 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>( pub async fn try_pop_no_fluff<'a, A: ExprRepr, X: ExtraTok>(
ctx: &impl ParseCtx,
snip: Snippet<'a, A, X>, snip: Snippet<'a, A, X>,
) -> ParseRes<'a, &'a TokTree<A, X>, A, X> { ) -> ParseRes<'a, &'a TokTree<A, X>, A, X> {
match snip.skip_fluff().pop_front() { match snip.skip_fluff().pop_front() {
Some((output, tail)) => Ok(Parsed { output, tail }), Some((output, tail)) => Ok(Parsed { output, tail }),
None => Err(mk_errv( None =>
ctx.i().i("Unexpected end").await, Err(mk_errv(is("Unexpected end").await, "Line ends abruptly; more tokens were expected", [
"Line ends abruptly; more tokens were expected", snip.sr(),
[snip.sr()], ])),
)),
} }
} }
pub async fn expect_end( pub async fn expect_end(snip: Snippet<'_, impl ExprRepr, impl ExtraTok>) -> OrcRes<()> {
ctx: &impl ParseCtx,
snip: Snippet<'_, impl ExprRepr, impl ExtraTok>,
) -> OrcRes<()> {
match snip.skip_fluff().get(0) { match snip.skip_fluff().get(0) {
Some(surplus) => Err(mk_errv( 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", "Code found after the end of the line",
[surplus.sr.pos()], [surplus.sr.pos()],
)), )),
@@ -185,28 +165,26 @@ pub async fn expect_end(
} }
pub async fn expect_tok<'a, A: ExprRepr, X: ExtraTok>( pub async fn expect_tok<'a, A: ExprRepr, X: ExtraTok>(
ctx: &impl ParseCtx,
snip: Snippet<'a, A, X>, snip: Snippet<'a, A, X>,
tok: Tok<String>, tok: IStr,
) -> ParseRes<'a, (), A, X> { ) -> 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 { match &head.tok {
Token::Name(n) if *n == tok => Ok(Parsed { output: (), tail }), Token::Name(n) if *n == tok => Ok(Parsed { output: (), tail }),
t => Err(mk_errv( t => Err(mk_errv(
ctx.i().i("Expected specific keyword").await, is("Expected specific keyword").await,
format!("Expected {tok} but found {:?}", fmt(t, ctx.i()).await), format!("Expected {tok} but found {:?}", fmt(t).await),
[head.sr()], [head.sr()],
)), )),
} }
} }
pub async fn token_errv<A: ExprRepr, X: ExtraTok>( pub async fn token_errv<A: ExprRepr, X: ExtraTok>(
ctx: &impl ParseCtx,
tok: &TokTree<A, X>, tok: &TokTree<A, X>,
description: &'static str, description: &'static str,
message: impl FnOnce(&str) -> String, message: impl FnOnce(&str) -> String,
) -> OrcErrv { ) -> 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> { 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<Parsed<'a, T, H, X>>; pub type ParseRes<'a, T, H, X> = OrcRes<Parsed<'a, T, H, X>>;
pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>( pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>(
ctx: &impl ParseCtx,
tail: Snippet<'a, A, X>, tail: Snippet<'a, A, X>,
) -> ParseRes<'a, Vec<Import>, A, X> { ) -> ParseRes<'a, Vec<Import>, A, X> {
let Some((tt, tail)) = tail.skip_fluff().pop_front() else { let Some((tt, tail)) = tail.skip_fluff().pop_front() else {
return Err(mk_errv( 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.", "Expected a name, a parenthesized list of names, or a globstar.",
[tail.sr().pos()], [tail.sr().pos()],
)); ));
}; };
let ret = rec(tt, ctx).await; let ret = rec(tt).await;
#[allow(clippy::type_complexity)] // it's an internal function #[allow(clippy::type_complexity)] // it's an internal function
pub async fn rec<A: ExprRepr, X: ExtraTok>( pub async fn rec<A: ExprRepr, X: ExtraTok>(
tt: &TokTree<A, X>, tt: &TokTree<A, X>,
ctx: &impl ParseCtx, ) -> OrcRes<Vec<(Vec<IStr>, Option<IStr>, SrcRange)>> {
) -> OrcRes<Vec<(Vec<Tok<String>>, Option<Tok<String>>, SrcRange)>> {
let ttpos = tt.sr.pos(); let ttpos = tt.sr.pos();
match &tt.tok { match &tt.tok {
Token::NS(ns, body) => { Token::NS(ns, body) => {
if !ns.starts_with(name_start) { if !ns.starts_with(name_start) {
ctx.rep().report(mk_errv( report(mk_errv(is("Unexpected name prefix").await, "Only names can precede ::", [ttpos]))
ctx.i().i("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()) Ok(out.into_iter().update(|i| i.0.push(ns.clone())).collect_vec())
}, },
Token::Name(ntok) => { Token::Name(ntok) => {
@@ -255,21 +227,19 @@ pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>(
let mut o = Vec::new(); let mut o = Vec::new();
let mut body = Snippet::new(tt, b); let mut body = Snippet::new(tt, b);
while let Some((output, tail)) = body.pop_front() { 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), Ok(names) => o.extend(names),
Err(e) => ctx.rep().report(e), Err(e) => report(e),
} }
body = tail; body = tail;
} }
Ok(o) Ok(o)
}, },
t => { t => Err(mk_errv(
return Err(mk_errv( is("Unrecognized name end").await,
ctx.i().i("Unrecognized name end").await, format!("Names cannot end with {:?} tokens", fmt(t).await),
format!("Names cannot end with {:?} tokens", fmt(t, ctx.i()).await),
[ttpos], [ttpos],
)); )),
},
} }
} }
ret.map(|output| { ret.map(|output| {
@@ -285,7 +255,7 @@ pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>(
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Import { pub struct Import {
pub path: VPath, pub path: VPath,
pub name: Option<Tok<String>>, pub name: Option<IStr>,
pub sr: SrcRange, pub sr: SrcRange,
} }
impl Import { impl Import {
@@ -296,14 +266,14 @@ impl Import {
None => self.path.into_name().expect("Import cannot be empty"), None => self.path.into_name().expect("Import cannot be empty"),
} }
} }
pub fn new(sr: SrcRange, path: VPath, name: Tok<String>) -> Self { pub fn new(sr: SrcRange, path: VPath, name: IStr) -> Self {
Import { path, name: Some(name), sr } Import { path, name: Some(name), sr }
} }
pub fn new_glob(sr: SrcRange, path: VPath) -> Self { Import { path, name: None, sr } } pub fn new_glob(sr: SrcRange, path: VPath) -> Self { Import { path, name: None, sr } }
} }
impl Display for Import { impl Display for Import {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 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))
} }
} }

View File

@@ -1 +0,0 @@

View File

@@ -3,31 +3,23 @@ use std::cell::RefCell;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::future::Future; use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem;
use std::ops::{BitAnd, Deref};
use std::pin::{Pin, pin}; use std::pin::{Pin, pin};
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc;
use std::task::Poll; use std::task::Poll;
use std::thread::panicking;
use async_fn_stream::stream; use async_fn_stream::stream;
use bound::Bound; use bound::Bound;
use derive_destructure::destructure; use derive_destructure::destructure;
use dyn_clone::{DynClone, clone_box};
use futures::channel::mpsc::{self, Receiver, Sender, channel}; use futures::channel::mpsc::{self, Receiver, Sender, channel};
use futures::channel::oneshot; use futures::channel::oneshot;
use futures::future::{LocalBoxFuture, select_all}; use futures::future::LocalBoxFuture;
use futures::lock::{Mutex, MutexGuard}; use futures::lock::{Mutex, MutexGuard};
use futures::{AsyncRead, AsyncWrite, SinkExt, Stream, StreamExt, stream, stream_select}; use futures::{AsyncRead, AsyncWrite, SinkExt, Stream, StreamExt, stream, stream_select};
use hashbrown::HashMap; use hashbrown::HashMap;
use orchid_api_traits::{Channel, Coding, Decode, Encode, MsgSet, Request, UnderRoot, enc_vec}; use orchid_api_traits::{Decode, Encode, Request, UnderRoot};
use some_executor::task_local;
use trait_set::trait_set;
use crate::clone;
use crate::logging::Logger;
#[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 ()>); pub struct Receipt<'a>(PhantomData<&'a mut ()>);
/// Write guard to outbound for the purpose of serializing a request. Only one /// Write guard to outbound for the purpose of serializing a request. Only one
@@ -98,11 +90,11 @@ impl<T: Client + ?Sized> ClientExt for T {}
/// A form of [Evidence] that doesn't require the value to be kept around /// A form of [Evidence] that doesn't require the value to be kept around
pub struct Witness<T>(PhantomData<T>); pub struct Witness<T>(PhantomData<T>);
impl<T> Witness<T> { impl<T> Witness<T> {
fn of(t: &T) -> Self { Self(PhantomData) } pub fn of(_: &T) -> Self { Self(PhantomData) }
} }
impl<T> Copy for Witness<T> {} impl<T> Copy for Witness<T> {}
impl<T> Clone for Witness<T> { impl<T> Clone for Witness<T> {
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 /// 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) {} pub async fn release(self) {}
} }
#[derive(Clone)]
struct IO {
i: IoLock<dyn AsyncRead>,
o: IoLock<dyn AsyncWrite>,
}
struct ReplySub { struct ReplySub {
id: u64, id: u64,
ack: oneshot::Sender<()>, ack: oneshot::Sender<()>,
@@ -192,9 +178,7 @@ struct IoClient {
notif_tid: TypeId, notif_tid: TypeId,
} }
impl IoClient { impl IoClient {
pub async fn new<Req: 'static, Not: 'static>( fn new<Req: 'static, Not: 'static>(output: IoLock<dyn AsyncWrite>) -> (Receiver<ReplySub>, Self) {
output: IoLock<dyn AsyncWrite>,
) -> (Receiver<ReplySub>, Self) {
let (req, rep) = mpsc::channel(0); let (req, rep) = mpsc::channel(0);
(rep, Self { (rep, Self {
output, output,
@@ -227,8 +211,8 @@ impl Client for IoClient {
}; };
let (cb, reply) = oneshot::channel(); let (cb, reply) = oneshot::channel();
let (ack, got_ack) = oneshot::channel(); let (ack, got_ack) = oneshot::channel();
self.subscribe.as_ref().clone().send(ReplySub { id, ack, cb }).await; self.subscribe.as_ref().clone().send(ReplySub { id, ack, cb }).await.unwrap();
got_ack.await; got_ack.await.unwrap();
let mut w = self.lock_out().await; let mut w = self.lock_out().await;
id.encode(w.as_mut()).await; id.encode(w.as_mut()).await;
Box::new(IoReqWriter { reply, w }) as Box<dyn ReqWriter> Box::new(IoReqWriter { reply, w }) as Box<dyn ReqWriter>
@@ -273,25 +257,25 @@ impl MsgWriter for IoNotifWriter {
pub struct CommCtx { pub struct CommCtx {
quit: Sender<()>, quit: Sender<()>,
client: Rc<IoClient>,
} }
impl CommCtx { impl CommCtx {
pub async fn quit(self) { self.quit.clone().send(()).await.expect("quit channel dropped"); } pub async fn quit(self) { self.quit.clone().send(()).await.expect("quit channel dropped"); }
pub fn client(&self) -> Rc<dyn Client> { self.client.clone() as Rc<dyn Client> }
} }
/// This function will exit only when one of the callbacks calls /// Establish bidirectional request-notification communication over a duplex
/// [CommCtx::quit]. /// channel. The returned [IoClient] can be used for notifications immediately,
pub async fn io_comm<Req: 'static, Not: 'static>( /// 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<Req: 'static, Not: 'static>(
o: Rc<Mutex<Pin<Box<dyn AsyncWrite>>>>, o: Rc<Mutex<Pin<Box<dyn AsyncWrite>>>>,
i: Mutex<Pin<Box<dyn AsyncRead>>>, i: Mutex<Pin<Box<dyn AsyncRead>>>,
notif: impl for<'a> AsyncFn(&mut CommCtx, NotifReader<'a>), 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<Output = ()>) {
let i = Rc::new(i); let i = Rc::new(i);
let (onsub, client) = IoClient::new::<Req, Not>(o.clone()).await; let (onsub, client) = IoClient::new::<Req, Not>(o.clone());
let client = Rc::new(client); (client, async move {
let (exit, onexit) = channel(1); let (exit, onexit) = channel(1);
enum Event { enum Event {
Input(u64), Input(u64),
@@ -318,6 +302,7 @@ pub async fn io_comm<Req: 'static, Not: 'static>(
}) })
.fuse() .fuse()
); );
let mut pending_replies = HashMap::new();
{ {
let mut shared = pin!(stream_select!( let mut shared = pin!(stream_select!(
pin!(input_stream) as Pin<&mut dyn Stream<Item = Event>>, pin!(input_stream) as Pin<&mut dyn Stream<Item = Event>>,
@@ -325,7 +310,6 @@ pub async fn io_comm<Req: 'static, Not: 'static>(
fork_stream.as_mut(), fork_stream.as_mut(),
onexit.map(|()| Event::Exit), onexit.map(|()| Event::Exit),
)); ));
let mut pending_replies = HashMap::new();
while let Some(next) = shared.next().await { while let Some(next) = shared.next().await {
match next { match next {
Event::Exit => { Event::Exit => {
@@ -334,28 +318,24 @@ pub async fn io_comm<Req: 'static, Not: 'static>(
}, },
Event::Sub(ReplySub { id, ack, cb }) => { Event::Sub(ReplySub { id, ack, cb }) => {
pending_replies.insert(id, cb); pending_replies.insert(id, cb);
ack.send(()); ack.send(()).unwrap();
}, },
Event::Input(id) if id == 0 => { Event::Input(0) => {
let (i, notif, exit, client) = (i.clone(), &notif, exit.clone(), client.clone()); let (i, notif, exit) = (i.clone(), &notif, exit.clone());
pending_reqs.borrow_mut().push_back(Box::pin(async move { pending_reqs.borrow_mut().push_back(Box::pin(async move {
let g = i.lock().await; let g = i.lock().await;
notif(&mut CommCtx { client, quit: exit.clone() }, NotifReader { read: g }).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 // id.msb == 0 is a request, !id where id.msb == 1 is the equivalent response
Event::Input(id) => Event::Input(id) =>
if (id & (1 << (u64::BITS - 1))) == 0 { if (id & (1 << (u64::BITS - 1))) == 0 {
let (i, o, req, exit, client) = let (i, o, req, exit) = (i.clone(), o.clone(), &req, exit.clone());
(i.clone(), o.clone(), &req, exit.clone(), client.clone());
pending_reqs.borrow_mut().push_back(Box::pin(async move { pending_reqs.borrow_mut().push_back(Box::pin(async move {
let g = i.lock().await; let g = i.lock().await;
req(&mut CommCtx { client, quit: exit.clone() }, ReqReader { let _ =
id, req(&mut CommCtx { quit: exit.clone() }, ReqReader { id, read: g, write: &o })
read: g, .await;
write: &*o,
})
.await
}) as LocalBoxFuture<()>); }) as LocalBoxFuture<()>);
} else { } else {
let cb = pending_replies.remove(&!id).expect("Reply to unrecognized request"); let cb = pending_replies.remove(&!id).expect("Reply to unrecognized request");
@@ -365,338 +345,88 @@ pub async fn io_comm<Req: 'static, Not: 'static>(
} }
} }
fork_stream.as_mut().count().await; fork_stream.as_mut().count().await;
}
trait_set! {
pub trait SendFn<T: MsgSet> =
for<'a> FnMut(&'a [u8], ReqNot<T>) -> LocalBoxFuture<'a, ()>
+ DynClone + 'static;
pub trait ReqFn<T: MsgSet> =
for<'a> FnMut(ReqReader<'a, T>, <T::In as Channel>::Req)
-> LocalBoxFuture<'a, Receipt<'a>>
+ DynClone + 'static;
pub trait NotifFn<T: MsgSet> =
FnMut(<T::In as Channel>::Notif, ReqNot<T>) -> 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<Output = ()> + 'static)
where Self: Sized {
self.defer_objsafe(Box::pin(cb));
}
fn defer_objsafe(&self, val: Pin<Box<dyn Future<Output = ()>>>);
}
impl ReqHandlish for &'_ dyn ReqHandlish {
fn defer_objsafe(&self, val: Pin<Box<dyn Future<Output = ()>>>) { (**self).defer_objsafe(val) }
}
type LocalAsyncFnOnceBox = Box<dyn FnOnce(Vec<u8>) -> LocalBoxFuture<'static, ()>>;
#[derive(destructure)]
pub struct RequestHandle<'a, MS: MsgSet> {
defer: RefCell<Vec<Pin<Box<dyn Future<Output = ()>>>>>,
_reqlt: PhantomData<&'a mut ()>,
parent: ReqNot<MS>,
raw_reply: RefCell<Option<LocalAsyncFnOnceBox>>,
}
impl<'a, MS: MsgSet + 'static> ReqReader<'a, MS> {
pub fn new(parent: ReqNot<MS>, raw_reply: impl AsyncFnOnce(Vec<u8>) + '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<MS> { self.parent.clone() }
pub async fn handle<U: Request>(&self, _: &U, rep: &U::Response) -> Receipt<'a> {
self.respond(rep).await
}
pub fn will_handle_as<U: Request>(&self, _: &U) -> ReqTypToken<U> { ReqTypToken(PhantomData) }
pub async fn handle_as<U: Request>(&self, _: ReqTypToken<U>, 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<MS: MsgSet> ReqHandlish for ReqReader<'_, MS> {
fn defer_objsafe(&self, val: Pin<Box<dyn Future<Output = ()>>>) {
self.defer.borrow_mut().push(val)
}
}
impl<MS: MsgSet> 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<T>(PhantomData<T>);
pub struct ReqNotData<T: MsgSet> {
id: u64,
send: Box<dyn SendFn<T>>,
notif: Box<dyn NotifFn<T>>,
req: Box<dyn ReqFn<T>>,
responses: HashMap<u64, mpsc::Sender<Vec<u8>>>,
}
/// 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<u8>);
impl Deref for RawReply {
type Target = [u8];
fn deref(&self) -> &Self::Target { get_id(&self.0[..]).1 }
}
pub struct ReqNot<T: MsgSet>(Arc<Mutex<ReqNotData<T>>>, Logger);
impl<T: MsgSet> ReqNot<T> {
pub fn new(
logger: Logger,
send: impl SendFn<T>,
notif: impl NotifFn<T>,
req: impl ReqFn<T>,
) -> 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 = <T::In as Channel>::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 = <T::In as Channel>::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<N: Coding + Into<<T::Out as Channel>::Notif>>(&self, notif: N) {
let mut send = clone_box(&*self.0.lock().await.send);
let mut buf = vec![0; 8];
let msg: <T::Out as Channel>::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<dyn Fn(T) -> LocalBoxFuture<'a, RawReply> + 'a>, Logger);
impl<'a, T> MappedRequester<'a, T> {
fn new<U: DynRequester + 'a, F: Fn(T) -> 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<T> 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<T: MsgSet> DynRequester for ReqNot<T> {
type Transfer = <T::Out as Channel>::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<R: Request + Into<Self::Transfer>>(
&self,
data: R,
) -> impl Future<Output = R::Response>;
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<This: DynRequester + ?Sized> Requester for This {
async fn request<R: Request + Into<Self::Transfer>>(&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<T: MsgSet> Clone for ReqNot<T> {
fn clone(&self) -> Self { Self(self.0.clone(), self.1.clone()) }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc;
use futures::FutureExt; use futures::join;
use futures::lock::Mutex; use futures::lock::Mutex;
use orchid_api_derive::Coding; use never::Never;
use orchid_api_traits::{Channel, Request}; use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request;
use test_executors::spin_on; use test_executors::spin_on;
use unsync_pipe::pipe;
use super::{MsgSet, ReqNot}; use crate::reqnot::{ClientExt, NotifReader, io_comm};
use crate::logging::Logger;
use crate::reqnot::Requester as _;
use crate::{api, clone};
#[derive(Clone, Debug, Coding, PartialEq)] #[derive(Clone, Debug, PartialEq, Coding, Hierarchy)]
pub struct TestReq(u8); #[extendable]
impl Request for TestReq { struct TestNotif(u64);
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;
}
#[test] #[test]
fn notification() { fn notification() {
spin_on(async { spin_on(async {
let logger = Logger::new(api::LogStrategy::StdErr); let (in1, out2) = pipe(1024);
let received = Arc::new(Mutex::new(None)); let (in2, out1) = pipe(1024);
let receiver = ReqNot::<TestMsgSet>::new( let received = RefCell::new(None);
logger.clone(), let (_, run_receiver) = io_comm::<Never, u64>(
|_, _| panic!("Should not send anything"), Rc::new(Mutex::new(Box::pin(in2))),
clone!(received; move |notif, _| clone!(received; async move { Mutex::new(Box::pin(out2)),
*received.lock().await = Some(notif); async |_, notif: NotifReader| {
}.boxed_local())), *received.borrow_mut() = Some(notif.read::<TestNotif>().await)
|_, _| panic!("Not receiving a request"), },
async |_, _| panic!("Should receive notif, not request"),
); );
let sender = ReqNot::<TestMsgSet>::new( let (sender, _) = io_comm::<Never, Never>(
logger, Rc::new(Mutex::new(Box::pin(in1))),
clone!(receiver; move |d, _| clone!(receiver; Box::pin(async move { Mutex::new(Box::pin(out1)),
receiver.receive(d).await async |_, _| panic!("Should not receive notif"),
}))), async |_, _| panic!("Should not receive request"),
|_, _| panic!("Should not receive notif"),
|_, _| panic!("Should not receive request"),
); );
sender.notify(3).await; join!(run_receiver, async {
assert_eq!(*received.lock().await, Some(3)); sender.notify(TestNotif(3)).await;
sender.notify(4).await; assert_eq!(*received.borrow(), Some(TestNotif(3)));
assert_eq!(*received.lock().await, Some(4)); 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] #[test]
fn request() { fn request() {
spin_on(async { spin_on(async {
let logger = Logger::new(api::LogStrategy::StdErr); let (in1, out2) = pipe(1024);
let receiver = Rc::new(Mutex::<Option<ReqNot<TestMsgSet>>>::new(None)); let (in2, out1) = pipe(1024);
let sender = Rc::new(ReqNot::<TestMsgSet>::new( let (_, run_server) = io_comm::<Never, Never>(
logger.clone(), Rc::new(Mutex::new(Box::pin(in2))),
clone!(receiver; move |d, _| clone!(receiver; Box::pin(async move { Mutex::new(Box::pin(out2)),
receiver.lock().await.as_ref().unwrap().receive(d).await async |_, _| panic!("No notifs expected"),
}))), async |_, mut req| {
|_, _| panic!("Should not receive notif"), let val = req.read_req::<DummyRequest>().await;
|_, _| panic!("Should not receive request"), req.reply(&val, &(val.0 + 1)).await
));
*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 response = sender.request(TestReq(5)).await; let (client, run_client) = io_comm::<DummyRequest, Never>(
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); assert_eq!(response, 6);
});
}) })
} }
} }

View File

@@ -14,7 +14,7 @@ use trait_set::trait_set;
use crate::error::OrcErrv; use crate::error::OrcErrv;
use crate::format::{FmtCtx, FmtUnit, Format, Variants}; use crate::format::{FmtCtx, FmtUnit, Format, Variants};
use crate::interner::{Interner, Tok}; use crate::interner::{IStr, es};
use crate::location::{Pos, SrcRange}; use crate::location::{Pos, SrcRange};
use crate::name::{Sym, VName, VPath}; use crate::name::{Sym, VName, VPath};
use crate::parse::Snippet; use crate::parse::Snippet;
@@ -28,7 +28,6 @@ pub trait TokenVariant<ApiEquiv: Clone + Debug + Coding>: Format + Clone + fmt::
api: &ApiEquiv, api: &ApiEquiv,
ctx: &mut Self::FromApiCtx<'_>, ctx: &mut Self::FromApiCtx<'_>,
pos: SrcRange, pos: SrcRange,
i: &Interner,
) -> impl Future<Output = Self>; ) -> impl Future<Output = Self>;
#[must_use] #[must_use]
fn into_api(self, ctx: &mut Self::ToApiCtx<'_>) -> impl Future<Output = ApiEquiv>; fn into_api(self, ctx: &mut Self::ToApiCtx<'_>) -> impl Future<Output = ApiEquiv>;
@@ -36,7 +35,7 @@ pub trait TokenVariant<ApiEquiv: Clone + Debug + Coding>: Format + Clone + fmt::
impl<T: Clone + Debug + Coding> TokenVariant<T> for Never { impl<T: Clone + Debug + Coding> TokenVariant<T> for Never {
type FromApiCtx<'a> = (); type FromApiCtx<'a> = ();
type ToApiCtx<'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") panic!("Cannot deserialize Never")
} }
async fn into_api(self, _: &mut Self::ToApiCtx<'_>) -> T { match self {} } async fn into_api(self, _: &mut Self::ToApiCtx<'_>) -> T { match self {} }
@@ -108,20 +107,19 @@ impl<H: ExprRepr, X: ExtraTok> TokTree<H, X> {
hctx: &mut H::FromApiCtx<'_>, hctx: &mut H::FromApiCtx<'_>,
xctx: &mut X::FromApiCtx<'_>, xctx: &mut X::FromApiCtx<'_>,
src: &Sym, src: &Sym,
i: &Interner,
) -> Self { ) -> Self {
let pos = SrcRange::new(tt.range.clone(), src); let pos = SrcRange::new(tt.range.clone(), src);
let tok = match_mapping!(&tt.token, api::Token => Token::<H, X> { let tok = match_mapping!(&tt.token, api::Token => Token::<H, X> {
BR, BR,
NS(n => Tok::from_api(*n, i).await, NS(n => es(*n).await,
b => Box::new(Self::from_api(b, hctx, xctx, src, i).boxed_local().await)), b => Box::new(Self::from_api(b, hctx, xctx, src).boxed_local().await)),
Bottom(e => OrcErrv::from_api(e, i).await), Bottom(e => OrcErrv::from_api(e).await),
LambdaHead(arg => Box::new(Self::from_api(arg, hctx, xctx, src, i).boxed_local().await)), LambdaHead(arg => Box::new(Self::from_api(arg, hctx, xctx, src).boxed_local().await)),
Name(n => Tok::from_api(*n, i).await), Name(n => es(*n).await),
S(*par, b => ttv_from_api(b, hctx, xctx, src, i).await), S(*par, b => ttv_from_api(b, hctx, xctx, src).await),
Comment(c.clone()), Comment(c => es(*c).await),
NewExpr(expr => X::from_api(expr, xctx, pos.clone(), i).await), NewExpr(expr => X::from_api(expr, xctx, pos.clone()).await),
Handle(tk => H::from_api(tk, hctx, pos.clone(), i).await) Handle(tk => H::from_api(tk, hctx, pos.clone()).await)
}); });
Self { sr: pos, tok } Self { sr: pos, tok }
} }
@@ -135,7 +133,7 @@ impl<H: ExprRepr, X: ExtraTok> TokTree<H, X> {
BR, BR,
NS(n.to_api(), b => Box::new(b.into_api(hctx, xctx).boxed_local().await)), NS(n.to_api(), b => Box::new(b.into_api(hctx, xctx).boxed_local().await)),
Bottom(e.to_api()), Bottom(e.to_api()),
Comment(c.clone()), Comment(c.to_api()),
LambdaHead(arg => Box::new(arg.into_api(hctx, xctx).boxed_local().await)), LambdaHead(arg => Box::new(arg.into_api(hctx, xctx).boxed_local().await)),
Name(nn.to_api()), Name(nn.to_api()),
S(p, b => ttv_into_api(b, hctx, xctx).boxed_local().await), S(p, b => ttv_into_api(b, hctx, xctx).boxed_local().await),
@@ -145,8 +143,8 @@ impl<H: ExprRepr, X: ExtraTok> TokTree<H, X> {
api::TokenTree { range: self.sr.range.clone(), token } api::TokenTree { range: self.sr.range.clone(), token }
} }
pub fn is_kw(&self, tk: Tok<String>) -> bool { self.tok.is_kw(tk) } pub fn is_kw(&self, tk: IStr) -> bool { self.tok.is_kw(tk) }
pub fn as_name(&self) -> Option<Tok<String>> { pub fn as_name(&self) -> Option<IStr> {
if let Token::Name(n) = &self.tok { Some(n.clone()) } else { None } if let Token::Name(n) = &self.tok { Some(n.clone()) } else { None }
} }
pub fn as_multiname(&self) -> Result<VName, &TokTree<H, X>> { pub fn as_multiname(&self) -> Result<VName, &TokTree<H, X>> {
@@ -193,11 +191,10 @@ pub async fn ttv_from_api<H: ExprRepr, X: ExtraTok>(
hctx: &mut H::FromApiCtx<'_>, hctx: &mut H::FromApiCtx<'_>,
xctx: &mut X::FromApiCtx<'_>, xctx: &mut X::FromApiCtx<'_>,
src: &Sym, src: &Sym,
i: &Interner,
) -> Vec<TokTree<H, X>> { ) -> Vec<TokTree<H, X>> {
stream(async |mut cx| { stream(async |mut cx| {
for tok in tokv { for tok in tokv {
cx.emit(TokTree::<H, X>::from_api(tok.borrow(), hctx, xctx, src, i).boxed_local().await).await cx.emit(TokTree::<H, X>::from_api(tok.borrow(), hctx, xctx, src).boxed_local().await).await
} }
}) })
.collect() .collect()
@@ -240,14 +237,14 @@ pub enum Token<H: ExprRepr, X: ExtraTok> {
/// Information about the code addressed to the human reader or dev tooling /// 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 /// It has no effect on the behaviour of the program unless it's explicitly
/// read via reflection /// read via reflection
Comment(Rc<String>), Comment(IStr),
/// The part of a lambda between `\` and `.` enclosing the argument. The body /// 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 /// stretches to the end of the enclosing parens or the end of the const line
LambdaHead(Box<TokTree<H, X>>), LambdaHead(Box<TokTree<H, X>>),
/// A binding, operator, or a segment of a namespaced::name /// A binding, operator, or a segment of a namespaced::name
Name(Tok<String>), Name(IStr),
/// A namespace prefix, like `my_ns::` followed by a token /// A namespace prefix, like `my_ns::` followed by a token
NS(Tok<String>, Box<TokTree<H, X>>), NS(IStr, Box<TokTree<H, X>>),
/// A line break /// A line break
BR, BR,
/// `()`, `[]`, or `{}` /// `()`, `[]`, or `{}`
@@ -263,7 +260,7 @@ pub enum Token<H: ExprRepr, X: ExtraTok> {
} }
impl<H: ExprRepr, X: ExtraTok> Token<H, X> { impl<H: ExprRepr, X: ExtraTok> Token<H, X> {
pub fn at(self, sr: SrcRange) -> TokTree<H, X> { TokTree { sr, tok: self } } pub fn at(self, sr: SrcRange) -> TokTree<H, X> { TokTree { sr, tok: self } }
pub fn is_kw(&self, tk: Tok<String>) -> 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<H, X>]> { pub fn as_s(&self, par: Paren) -> Option<&[TokTree<H, X>]> {
match self { match self {
Self::S(p, b) if *p == par => Some(b), Self::S(p, b) if *p == par => Some(b),

View File

@@ -13,13 +13,13 @@ pub enum Loaded {
Code(Arc<String>), Code(Arc<String>),
/// Conceptually equivalent to the list of *.orc files in a folder, without /// Conceptually equivalent to the list of *.orc files in a folder, without
/// the extension /// the extension
Collection(Arc<Vec<Tok<String>>>), Collection(Arc<Vec<IStr>>),
} }
impl Loaded { impl Loaded {
/// Is the loaded item source code (not a collection)? /// Is the loaded item source code (not a collection)?
pub fn is_code(&self) -> bool { matches!(self, Loaded::Code(_)) } pub fn is_code(&self) -> bool { matches!(self, Loaded::Code(_)) }
/// Collect the elements in a collection rreport /// Collect the elements in a collection rreport
pub fn collection(items: impl IntoIterator<Item = Tok<String>>) -> Self { pub fn collection(items: impl IntoIterator<Item = IStr>) -> Self {
Self::Collection(Arc::new(items.into_iter().collect())) Self::Collection(Arc::new(items.into_iter().collect()))
} }
} }
@@ -55,7 +55,7 @@ impl ErrorSansOrigin for CodeNotFound {
/// formats and other sources for libraries and dependencies. /// formats and other sources for libraries and dependencies.
pub trait VirtFS { pub trait VirtFS {
/// Implementation of [VirtFS::read] /// Implementation of [VirtFS::read]
fn get(&self, path: &[Tok<String>], full_path: &PathSlice) -> FSResult; fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult;
/// Discover information about a path without reading it. /// Discover information about a path without reading it.
/// ///
/// Implement this if your vfs backend can do expensive operations /// Implement this if your vfs backend can do expensive operations
@@ -68,7 +68,7 @@ pub trait VirtFS {
} }
/// Convert a path into a human-readable string that is meaningful in the /// Convert a path into a human-readable string that is meaningful in the
/// target context. /// target context.
fn display(&self, path: &[Tok<String>]) -> Option<String>; fn display(&self, path: &[IStr]) -> Option<String>;
/// Convert the FS handler into a type-erased version of itself for packing in /// Convert the FS handler into a type-erased version of itself for packing in
/// a tree. /// a tree.
fn rc(self) -> Rc<dyn VirtFS> fn rc(self) -> Rc<dyn VirtFS>
@@ -81,15 +81,11 @@ pub trait VirtFS {
} }
impl VirtFS for &dyn VirtFS { impl VirtFS for &dyn VirtFS {
fn get(&self, path: &[Tok<String>], full_path: &PathSlice) -> FSResult { fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult { (*self).get(path, full_path) }
(*self).get(path, full_path) fn display(&self, path: &[IStr]) -> Option<String> { (*self).display(path) }
}
fn display(&self, path: &[Tok<String>]) -> Option<String> { (*self).display(path) }
} }
impl<T: VirtFS + ?Sized> VirtFS for Rc<T> { impl<T: VirtFS + ?Sized> VirtFS for Rc<T> {
fn get(&self, path: &[Tok<String>], full_path: &PathSlice) -> FSResult { fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult { (**self).get(path, full_path) }
(**self).get(path, full_path) fn display(&self, path: &[IStr]) -> Option<String> { (**self).display(path) }
}
fn display(&self, path: &[Tok<String>]) -> Option<String> { (**self).display(path) }
} }

View File

@@ -32,7 +32,7 @@ impl<'a> Combine for &'a dyn VirtFS {
pub type DeclTree = ModEntry<Rc<dyn VirtFS>, (), ()>; pub type DeclTree = ModEntry<Rc<dyn VirtFS>, (), ()>;
impl VirtFS for DeclTree { impl VirtFS for DeclTree {
fn get(&self, path: &[Tok<String>], full_path: &PathSlice) -> FSResult { fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult {
match &self.member { match &self.member {
ModMember::Item(it) => it.get(path, full_path), ModMember::Item(it) => it.get(path, full_path),
ModMember::Sub(module) => match path.split_first() { ModMember::Sub(module) => match path.split_first() {
@@ -44,7 +44,7 @@ impl VirtFS for DeclTree {
} }
} }
fn display(&self, path: &[Tok<String>]) -> Option<String> { fn display(&self, path: &[IStr]) -> Option<String> {
let (head, tail) = path.split_first()?; let (head, tail) = path.split_first()?;
match &self.member { match &self.member {
ModMember::Item(it) => it.display(path), ModMember::Item(it) => it.display(path),
@@ -54,16 +54,16 @@ impl VirtFS for DeclTree {
} }
impl VirtFS for String { impl VirtFS for String {
fn display(&self, _: &[Tok<String>]) -> Option<String> { None } fn display(&self, _: &[IStr]) -> Option<String> { None }
fn get(&self, path: &[Tok<String>], full_path: &PathSlice) -> FSResult { fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult {
(path.is_empty().then(|| Loaded::Code(Arc::new(self.as_str().to_string())))) (path.is_empty().then(|| Loaded::Code(Arc::new(self.as_str().to_string()))))
.ok_or_else(|| CodeNotFound::new(full_path.to_vpath()).pack()) .ok_or_else(|| CodeNotFound::new(full_path.to_vpath()).pack())
} }
} }
impl<'a> VirtFS for &'a str { impl<'a> VirtFS for &'a str {
fn display(&self, _: &[Tok<String>]) -> Option<String> { None } fn display(&self, _: &[IStr]) -> Option<String> { None }
fn get(&self, path: &[Tok<String>], full_path: &PathSlice) -> FSResult { fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult {
(path.is_empty().then(|| Loaded::Code(Arc::new(self.to_string())))) (path.is_empty().then(|| Loaded::Code(Arc::new(self.to_string()))))
.ok_or_else(|| CodeNotFound::new(full_path.to_vpath()).pack()) .ok_or_else(|| CodeNotFound::new(full_path.to_vpath()).pack())
} }

View File

@@ -99,14 +99,14 @@ impl DirNode {
} }
} }
fn mk_pathbuf(&self, path: &[Tok<String>]) -> PathBuf { fn mk_pathbuf(&self, path: &[IStr]) -> PathBuf {
let mut fpath = self.root.clone(); let mut fpath = self.root.clone();
path.iter().for_each(|seg| fpath.push(seg.as_str())); path.iter().for_each(|seg| fpath.push(seg.as_str()));
fpath fpath
} }
} }
impl VirtFS for DirNode { impl VirtFS for DirNode {
fn get(&self, path: &[Tok<String>], full_path: &PathSlice) -> FSResult { fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult {
let fpath = self.mk_pathbuf(path); let fpath = self.mk_pathbuf(path);
let mut binding = self.cached.borrow_mut(); let mut binding = self.cached.borrow_mut();
let (_, res) = (binding.raw_entry_mut().from_key(&fpath)) let (_, res) = (binding.raw_entry_mut().from_key(&fpath))
@@ -114,7 +114,7 @@ impl VirtFS for DirNode {
res.clone() res.clone()
} }
fn display(&self, path: &[Tok<String>]) -> Option<String> { fn display(&self, path: &[IStr]) -> Option<String> {
let pathbuf = self.mk_pathbuf(path).with_extension(self.ext()); let pathbuf = self.mk_pathbuf(path).with_extension(self.ext());
Some(pathbuf.to_string_lossy().to_string()) Some(pathbuf.to_string_lossy().to_string())
} }

View File

@@ -56,7 +56,7 @@ impl EmbeddedFS {
} }
impl VirtFS for EmbeddedFS { impl VirtFS for EmbeddedFS {
fn get(&self, path: &[Tok<String>], full_path: &PathSlice) -> FSResult { fn get(&self, path: &[IStr], full_path: &PathSlice) -> FSResult {
if path.is_empty() { if path.is_empty() {
return Ok(Loaded::collection(self.tree.keys(|_| true))); return Ok(Loaded::collection(self.tree.keys(|_| true)));
} }
@@ -67,7 +67,7 @@ impl VirtFS for EmbeddedFS {
ModMember::Sub(sub) => Loaded::collection(sub.keys(|_| true)), ModMember::Sub(sub) => Loaded::collection(sub.keys(|_| true)),
}) })
} }
fn display(&self, path: &[Tok<String>]) -> Option<String> { fn display(&self, path: &[IStr]) -> Option<String> {
let Self { gen, suffix, .. } = self; let Self { gen, suffix, .. } = self;
Some(format!("{}{suffix} in {gen}", path.iter().join("/"))) Some(format!("{}{suffix} in {gen}", path.iter().join("/")))
} }

View File

@@ -21,18 +21,18 @@ impl<'a> PrefixFS<'a> {
add: VPath::parse(add.as_ref()), add: VPath::parse(add.as_ref()),
} }
} }
fn proc_path(&self, path: &[Tok<String>]) -> Option<Vec<Tok<String>>> { fn proc_path(&self, path: &[IStr]) -> Option<Vec<IStr>> {
let path = path.strip_prefix(self.remove.as_slice())?; let path = path.strip_prefix(self.remove.as_slice())?;
Some(self.add.0.iter().chain(path).cloned().collect_vec()) Some(self.add.0.iter().chain(path).cloned().collect_vec())
} }
} }
impl<'a> VirtFS for PrefixFS<'a> { impl<'a> VirtFS for PrefixFS<'a> {
fn get(&self, path: &[Tok<String>], full_path: &PathSlice) -> super::FSResult { fn get(&self, path: &[IStr], full_path: &PathSlice) -> super::FSResult {
let path = let path =
self.proc_path(path).ok_or_else(|| CodeNotFound::new(full_path.to_vpath()).pack())?; self.proc_path(path).ok_or_else(|| CodeNotFound::new(full_path.to_vpath()).pack())?;
self.wrapped.get(&path, full_path) self.wrapped.get(&path, full_path)
} }
fn display(&self, path: &[Tok<String>]) -> Option<String> { fn display(&self, path: &[IStr]) -> Option<String> {
self.wrapped.display(&self.proc_path(path)?) self.wrapped.display(&self.proc_path(path)?)
} }
} }

View File

@@ -8,10 +8,11 @@ repository = "https://git.lbfalvy.com/Orchid/orchid"
homepage = "https://git.lbfalvy.com/Orchid/orchid" homepage = "https://git.lbfalvy.com/Orchid/orchid"
[dev-dependencies] [dev-dependencies]
futures = "0.3.31"
itertools = "0.14.0" itertools = "0.14.0"
rand = "0.9.2" rand = "0.9.2"
rand_chacha = "0.9.0" rand_chacha = "0.9.0"
test_executors = "0.4.0" test_executors = "0.4.0"
[dependencies] [dependencies]
futures = "0.3.31" futures-io = "0.3.31"

View File

@@ -11,7 +11,7 @@ use std::ptr::{null, null_mut, slice_from_raw_parts};
use std::task::{Context, Poll, Waker}; use std::task::{Context, Poll, Waker};
use std::{io, mem}; 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") } fn pipe_layout(bs: usize) -> Layout { Layout::from_size_align(bs, 1).expect("1-align is trivial") }