Temporary commit to try fix halting

This commit is contained in:
2025-09-14 13:29:35 +02:00
parent ee45dbd28e
commit cd1d640174
59 changed files with 1091 additions and 778 deletions

View File

@@ -5,7 +5,7 @@ use orchid_api_traits::Request;
use crate::{ use crate::{
ExprTicket, Expression, ExtHostReq, FormattingUnit, HostExtNotif, HostExtReq, OrcResult, SysId, ExprTicket, Expression, ExtHostReq, FormattingUnit, HostExtNotif, HostExtReq, OrcResult, SysId,
TStrv, TVec,
}; };
pub type AtomData = Vec<u8>; pub type AtomData = Vec<u8>;
@@ -83,14 +83,14 @@ impl Request for DeserAtom {
/// A request blindly routed to the system that provides an atom. /// A request blindly routed to the system that provides an atom.
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)] #[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
#[extends(AtomReq, HostExtReq)] #[extends(AtomReq, HostExtReq)]
pub struct Fwded(pub Atom, pub TStrv, pub Vec<u8>); pub struct Fwded(pub Atom, pub TVec, pub Vec<u8>);
impl Request for Fwded { impl Request for Fwded {
type Response = Option<Vec<u8>>; type Response = Option<Vec<u8>>;
} }
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)] #[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
#[extends(ExtHostReq)] #[extends(ExtHostReq)]
pub struct Fwd(pub Atom, pub TStrv, pub Vec<u8>); pub struct Fwd(pub Atom, pub TVec, pub Vec<u8>);
impl Request for Fwd { impl Request for Fwd {
type Response = Option<Vec<u8>>; type Response = Option<Vec<u8>>;
} }

View File

@@ -3,7 +3,7 @@ use std::num::NonZeroU64;
use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request; use orchid_api_traits::Request;
use crate::{Atom, ExtHostNotif, ExtHostReq, Location, OrcError, SysId, TStrv}; use crate::{Atom, ExtHostNotif, ExtHostReq, Location, OrcError, SysId, TVec};
/// An arbitrary ID associated with an expression on the host side. Incoming /// An arbitrary ID associated with an expression on the host side. Incoming
/// tickets always come with some lifetime guarantee, which can be extended with /// tickets always come with some lifetime guarantee, which can be extended with
@@ -72,7 +72,7 @@ pub enum ExpressionKind {
/// Because the atom is newly constructed, it also must belong to this system. /// Because the atom is newly constructed, it also must belong to this system.
NewAtom(Atom), NewAtom(Atom),
/// A reference to a constant /// A reference to a constant
Const(TStrv), Const(TVec),
/// A static runtime error. /// A static runtime error.
Bottom(Vec<OrcError>), Bottom(Vec<OrcError>),
} }

View File

@@ -47,7 +47,7 @@ impl Request for ExternStr {
#[extends(IntReq, ExtHostReq)] #[extends(IntReq, ExtHostReq)]
pub struct InternStrv(pub Vec<TStr>); pub struct InternStrv(pub Vec<TStr>);
impl Request for InternStrv { impl Request for InternStrv {
type Response = TStrv; type Response = TVec;
} }
/// replica -> master to find the vector of interned strings corresponding to a /// replica -> master to find the vector of interned strings corresponding to a
/// token /// token
@@ -57,7 +57,7 @@ impl Request for InternStrv {
/// See [IntReq] /// See [IntReq]
#[derive(Clone, Debug, Coding, Hierarchy)] #[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(IntReq, ExtHostReq)] #[extends(IntReq, ExtHostReq)]
pub struct ExternStrv(pub TStrv); pub struct ExternStrv(pub TVec);
impl Request for ExternStrv { impl Request for ExternStrv {
type Response = Vec<TStr>; type Response = Vec<TStr>;
} }
@@ -68,7 +68,7 @@ pub struct TStr(pub NonZeroU64);
/// A substitute for an interned string sequence in serialized datastructures. /// A substitute for an interned string sequence in serialized datastructures.
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)] #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)]
pub struct TStrv(pub NonZeroU64); pub struct TVec(pub NonZeroU64);
/// A request to sweep the replica. The master will not be sweeped until all /// A request to sweep the replica. The master will not be sweeped until all
/// replicas respond, as it must retain everything the replicas retained /// replicas respond, as it must retain everything the replicas retained
@@ -84,5 +84,5 @@ impl Request for Sweep {
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub struct Retained { pub struct Retained {
pub strings: Vec<TStr>, pub strings: Vec<TStr>,
pub vecs: Vec<TStrv>, pub vecs: Vec<TVec>,
} }

View File

@@ -3,7 +3,7 @@ use std::ops::RangeInclusive;
use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request; use orchid_api_traits::Request;
use crate::{ExtHostReq, HostExtReq, OrcResult, ParsId, SysId, TStr, TStrv, TokenTree}; use crate::{ExtHostReq, HostExtReq, OrcResult, ParsId, SysId, TStr, TVec, TokenTree};
/// - All ranges contain at least one character /// - All ranges contain at least one character
/// - All ranges are in increasing characeter order /// - All ranges are in increasing characeter order
@@ -19,7 +19,7 @@ pub struct LexExpr {
pub text: TStr, pub text: TStr,
pub pos: u32, pub pos: u32,
/// Source root module path /// Source root module path
pub src: TStrv, pub src: TVec,
} }
impl Request for LexExpr { impl Request for LexExpr {
type Response = Option<OrcResult<LexedExpr>>; type Response = Option<OrcResult<LexedExpr>>;

View File

@@ -2,7 +2,7 @@ use std::ops::Range;
use orchid_api_derive::Coding; use orchid_api_derive::Coding;
use crate::{TStr, TStrv}; use crate::{TStr, TVec};
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub enum Location { pub enum Location {
@@ -21,12 +21,12 @@ pub enum Location {
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub struct SourceRange { pub struct SourceRange {
pub path: TStrv, pub path: TVec,
pub range: Range<u32>, pub range: Range<u32>,
} }
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub struct CodeGenInfo { pub struct CodeGenInfo {
pub generator: TStrv, pub generator: TVec,
pub details: TStr, pub details: TStr,
} }

View File

@@ -5,7 +5,7 @@ use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request; use orchid_api_traits::Request;
use crate::{ use crate::{
Expression, ExtHostReq, HostExtReq, OrcResult, SourceRange, SysId, TStr, TStrv, TokenTree, Expression, ExtHostReq, HostExtReq, OrcResult, SourceRange, SysId, TStr, TVec, TokenTree,
}; };
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)] #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)]
@@ -24,10 +24,10 @@ pub struct ParsedConstId(pub NonZeroU64);
pub struct ParseLine { pub struct ParseLine {
pub sys: SysId, pub sys: SysId,
/// The immediately enclosing module path /// The immediately enclosing module path
pub module: TStrv, pub module: TVec,
/// The root module path for the snipppet of source code, prefix of /// The root module path for the snipppet of source code, prefix of
/// [ParseLine#module] /// [ParseLine#module]
pub src: TStrv, pub src: TVec,
pub comments: Vec<Comment>, pub comments: Vec<Comment>,
pub exported: bool, pub exported: bool,
pub idx: u16, pub idx: u16,
@@ -97,9 +97,9 @@ pub struct Comment {
pub struct ResolveNames { pub struct ResolveNames {
pub sys: SysId, pub sys: SysId,
pub constid: ParsedConstId, pub constid: ParsedConstId,
pub names: Vec<TStrv>, pub names: Vec<TVec>,
} }
impl Request for ResolveNames { impl Request for ResolveNames {
type Response = Vec<OrcResult<TStrv>>; type Response = Vec<OrcResult<TVec>>;
} }

View File

@@ -5,7 +5,7 @@ use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request; use orchid_api_traits::Request;
use ordered_float::NotNan; use ordered_float::NotNan;
use crate::{CharFilter, ExtHostReq, HostExtNotif, HostExtReq, MemberKind, TStr, TStrv}; use crate::{CharFilter, ExtHostReq, HostExtNotif, HostExtReq, MemberKind, TStr, TVec};
/// ID of a system type /// ID of a system type
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)] #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)]
@@ -63,7 +63,7 @@ pub struct NewSystemResponse {
pub lex_filter: CharFilter, pub lex_filter: CharFilter,
pub line_types: Vec<TStr>, pub line_types: Vec<TStr>,
pub const_root: HashMap<TStr, MemberKind>, pub const_root: HashMap<TStr, MemberKind>,
pub prelude: Vec<TStrv>, pub prelude: Vec<TVec>,
} }
#[derive(Clone, Debug, Coding, Hierarchy)] #[derive(Clone, Debug, Coding, Hierarchy)]

View File

@@ -6,7 +6,7 @@ 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;
use crate::{ExprTicket, Expression, ExtHostReq, HostExtReq, OrcError, SysId, TStr, TStrv}; use crate::{ExprTicket, Expression, ExtHostReq, HostExtReq, OrcError, SysId, TStr, TVec};
/// A token tree from a lexer recursion request. Its lifetime is the lex call, /// A token tree from a lexer recursion request. Its lifetime is the lex call,
/// the lexer can include it in its output or discard it by implication. /// the lexer can include it in its output or discard it by implication.
@@ -92,7 +92,7 @@ impl Request for GetMember {
/// an atom call. /// an atom call.
#[derive(Clone, Copy, Debug, Coding, Hierarchy)] #[derive(Clone, Copy, Debug, Coding, Hierarchy)]
#[extends(ExtHostReq)] #[extends(ExtHostReq)]
pub struct LsModule(pub SysId, pub TStrv); pub struct LsModule(pub SysId, pub TVec);
impl Request for LsModule { impl Request for LsModule {
type Response = Result<ModuleInfo, LsModuleError>; type Response = Result<ModuleInfo, LsModuleError>;
} }

View File

@@ -3,6 +3,9 @@ name = "orchid-base"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
[features]
mocks = []
# 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]

28
orchid-base/src/ctx.rs Normal file
View File

@@ -0,0 +1,28 @@
use std::any::{TypeId, type_name};
use std::marker::PhantomData;
use std::rc::Rc;
use orchid_api_traits::MsgSet;
use crate::error::Reporter;
use crate::interner::Interner;
use crate::reqnot::{Client, DynClient};
pub trait CtxDyn {
fn i(&self) -> Interner;
fn rep(&self) -> &Reporter;
fn client(&self, msg_set: TypeId) -> Option<Rc<dyn DynClient>>;
fn msg_set_type(&self) -> TypeId;
}
pub struct Ctx(Rc<dyn CtxDyn>);
impl Ctx {
pub fn i(&self) -> Interner { self.0.i() }
pub fn rep(&self) -> &Reporter { self.0.rep() }
pub fn client<T: MsgSet>(&self) -> Client<T> {
let Some(dyn_client) = self.0.client(TypeId::of::<T>()) else {
panic!("Incorrect message set {} passed", type_name::<T>());
};
Client(dyn_client, PhantomData)
}
}

View File

@@ -1,14 +1,22 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fmt; use std::fmt::{self, Display};
use std::ops::Add; use std::ops::{Add, AddAssign, Deref};
use std::pin::Pin;
use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use futures::future::join_all; use async_fn_stream::stream;
use async_once_cell::{Lazy, OnceCell};
use futures::future::{join_all, ready};
use futures::lock::Mutex;
use futures::{Stream, StreamExt, stream};
use itertools::Itertools; use itertools::Itertools;
use crate::api; use crate::api;
use crate::interner::{Interner, Tok}; use crate::ctx::Ctx;
use crate::format::{FmtCtx, FmtUnit, Format};
use crate::interner::{IStr, Interner};
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
@@ -50,40 +58,126 @@ impl fmt::Display for ErrPos {
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct OrcErr { struct SingleError {
pub description: Tok<String>, pub description: IStr,
pub message: Arc<String>, pub message: Arc<String>,
pub positions: Vec<ErrPos>, pub positions: Vec<ErrPos>,
} }
impl OrcErr { impl fmt::Display for SingleError {
fn to_api(&self) -> api::OrcError {
api::OrcError {
description: self.description.to_api(),
message: self.message.clone(),
locations: self.positions.iter().map(ErrPos::to_api).collect(),
}
}
async fn from_api(api: &api::OrcError, i: &Interner) -> Self {
Self {
description: Tok::from_api(api.description, i).await,
message: api.message.clone(),
positions: join_all(api.locations.iter().map(|e| ErrPos::from_api(e, i))).await,
}
}
}
impl PartialEq<Tok<String>> for OrcErr {
fn eq(&self, other: &Tok<String>) -> bool { self.description == *other }
}
impl From<OrcErr> for Vec<OrcErr> {
fn from(value: OrcErr) -> Self { vec![value] }
}
impl fmt::Display for OrcErr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let pstr = self.positions.iter().map(|p| format!("{p}")).join("; "); let pstr = self.positions.iter().map(|p| format!("{p}")).join("; ");
write!(f, "{}: {} @ {}", self.description, self.message, pstr) write!(f, "{}: {} @ {}", self.description, self.message, pstr)
} }
} }
pub struct OrcErr {
singles: OnceCell<OwnedOrcErr>,
futures: Mutex<Vec<Pin<Box<dyn Future<Output = OwnedOrcErr>>>>>,
}
impl OrcErr {
pub async fn into_owned(self) -> OwnedOrcErr {
self.to_owned().await;
self.singles.into_inner().expect("Initialized above")
}
pub async fn to_owned(&self) -> &OwnedOrcErr {
self
.singles
.get_or_init(async {
let results = join_all(self.futures.lock().await.drain(..)).await;
OwnedOrcErr(results.iter().flat_map(|err| err.0.iter()).cloned().collect())
})
.await
}
fn into_futures(self) -> Vec<Pin<Box<dyn Future<Output = OwnedOrcErr>>>> {
match self.singles.into_inner() {
Some(val) => vec![Box::pin(ready(val))],
None => self.futures.into_inner(),
}
}
}
impl From<OwnedOrcErr> for OrcErr {
fn from(value: OwnedOrcErr) -> Self {
Self { singles: OnceCell::from(value), futures: Mutex::new(vec![]) }
}
}
impl<T: Future<Output = OrcErr> + 'static> From<T> for OrcErr {
fn from(value: T) -> Self {
Self {
singles: OnceCell::new(),
futures: Mutex::new(vec![Box::pin(async { value.await.into_owned().await })]),
}
}
}
impl Add for OrcErr {
type Output = Self;
fn add(mut self, mut rhs: Self) -> Self::Output {
if let (Some(l), Some(r)) = (self.singles.get_mut(), rhs.singles.get_mut()) {
l.0.extend(r.0.drain(..));
return self;
}
Self {
singles: OnceCell::new(),
futures: Mutex::new(self.into_futures().into_iter().chain(rhs.into_futures()).collect()),
}
}
}
impl AddAssign for OrcErr {
fn add_assign(&mut self, mut rhs: Self) {
if let (Some(l), Some(r)) = (self.singles.get_mut(), rhs.singles.get_mut()) {
l.0.extend(r.0.drain(..));
} else {
let mut temp = Self { futures: Mutex::default(), singles: OnceCell::new() };
std::mem::swap(self, &mut temp);
self.futures.get_mut().extend(temp.into_futures().into_iter().chain(rhs.into_futures()));
}
}
}
impl Format for OrcErr {
async fn print<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
format!("{}", self.to_owned().await).into()
}
}
#[derive(Clone)]
pub struct OwnedOrcErr(Vec<SingleError>);
impl OwnedOrcErr {
pub fn to_api(&self) -> Vec<api::OrcError> {
self
.0
.iter()
.map(|err| api::OrcError {
description: err.description.to_api(),
message: err.message.clone(),
locations: err.positions.iter().map(|pos| pos.to_api()).collect(),
})
.collect_vec()
}
pub async fn from_api(api: impl IntoIterator<Item = &api::OrcError>, i: &Interner) -> Self {
Self(
join_all(api.into_iter().map(|e| async {
SingleError {
description: i.es(e.description).await,
message: e.message.clone(),
positions: join_all(e.locations.iter().map(|pos| ErrPos::from_api(pos, i))).await,
}
}))
.await,
)
}
}
impl Display for OwnedOrcErr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0.iter().join("\n"))
}
}
impl fmt::Debug for OwnedOrcErr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "OwnedOrcErr({self}") }
}
impl Add for OwnedOrcErr {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output { Self(self.0.into_iter().chain(rhs.0).collect()) }
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct EmptyErrv; pub struct EmptyErrv;
impl fmt::Display for EmptyErrv { impl fmt::Display for EmptyErrv {
@@ -92,70 +186,7 @@ impl fmt::Display for EmptyErrv {
} }
} }
#[derive(Clone, Debug)] pub type OrcRes<T> = Result<T, OrcErr>;
pub struct OrcErrv(Vec<OrcErr>);
impl OrcErrv {
pub fn new(errors: impl IntoIterator<Item = OrcErr>) -> Result<Self, EmptyErrv> {
let v = errors.into_iter().collect_vec();
if v.is_empty() { Err(EmptyErrv) } else { Ok(Self(v)) }
}
#[must_use]
pub fn extended<T>(mut self, errors: impl IntoIterator<Item = T>) -> Self
where Self: Extend<T> {
self.extend(errors);
self
}
#[must_use]
pub fn len(&self) -> usize { self.0.len() }
#[must_use]
pub fn is_empty(&self) -> bool { self.len() == 0 }
#[must_use]
pub fn any(&self, f: impl FnMut(&OrcErr) -> bool) -> bool { self.0.iter().any(f) }
#[must_use]
pub fn keep_only(self, f: impl FnMut(&OrcErr) -> bool) -> Option<Self> {
let v = self.0.into_iter().filter(f).collect_vec();
if v.is_empty() { None } else { Some(Self(v)) }
}
#[must_use]
pub fn one(&self) -> Option<&OrcErr> { (self.0.len() == 1).then(|| &self.0[9]) }
pub fn pos_iter(&self) -> impl Iterator<Item = ErrPos> + '_ {
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 async fn from_api<'a>(
api: impl IntoIterator<Item = &'a api::OrcError>,
i: &Interner,
) -> Self {
Self(join_all(api.into_iter().map(|e| OrcErr::from_api(e, i))).await)
}
}
impl From<OrcErr> for OrcErrv {
fn from(value: OrcErr) -> Self { Self(vec![value]) }
}
impl Add for OrcErrv {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output { Self(self.0.into_iter().chain(rhs.0).collect_vec()) }
}
impl Extend<OrcErr> for OrcErrv {
fn extend<T: IntoIterator<Item = OrcErr>>(&mut self, iter: T) { self.0.extend(iter) }
}
impl Extend<OrcErrv> for OrcErrv {
fn extend<T: IntoIterator<Item = OrcErrv>>(&mut self, iter: T) {
self.0.extend(iter.into_iter().flatten())
}
}
impl IntoIterator for OrcErrv {
type IntoIter = std::vec::IntoIter<OrcErr>;
type Item = OrcErr;
fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
}
impl fmt::Display for OrcErrv {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0.iter().join("\n"))
}
}
pub type OrcRes<T> = Result<T, OrcErrv>;
pub fn join_ok<T, U>(left: OrcRes<T>, right: OrcRes<U>) -> OrcRes<(T, U)> { pub fn join_ok<T, U>(left: OrcRes<T>, right: OrcRes<U>) -> OrcRes<(T, U)> {
match (left, right) { match (left, right) {
@@ -191,62 +222,80 @@ macro_rules! join_ok {
(@VALUES) => { Ok(()) }; (@VALUES) => { Ok(()) };
} }
pub fn mk_errv_floating(description: Tok<String>, message: impl AsRef<str>) -> OrcErrv { impl Ctx {
mk_errv::<Pos>(description, message, []) pub fn mk_err_floating(
} &self,
description: impl AsRef<str> + 'static,
pub fn mk_errv<I: Into<ErrPos>>( message: impl AsRef<str> + 'static,
description: Tok<String>, ) -> OrcErr {
message: impl AsRef<str>, self.mk_err::<Pos>(description, message, [])
posv: impl IntoIterator<Item = I>, }
) -> OrcErrv { pub fn mk_err<I: Into<ErrPos>>(
OrcErr { &self,
description, description: impl AsRef<str> + 'static,
message: impl AsRef<str> + 'static,
posv: impl IntoIterator<Item = I> + 'static,
) -> OrcErr {
let i = self.i();
async move {
OwnedOrcErr(vec![SingleError {
description: i.is(description.as_ref()).await,
message: Arc::new(message.as_ref().to_string()), message: Arc::new(message.as_ref().to_string()),
positions: posv.into_iter().map_into().collect(), positions: posv.into_iter().map_into().collect(),
}])
.into()
} }
.into() .into()
} }
pub async fn async_io_err<I: Into<ErrPos>>(
pub async fn async_io_err<I: Into<ErrPos>>( &self,
err: std::io::Error, err: std::io::Error,
i: &Interner, posv: impl IntoIterator<Item = I> + 'static,
posv: impl IntoIterator<Item = I>, ) -> OrcErr {
) -> OrcErrv { self.mk_err(err.kind().to_string(), err.to_string(), posv)
mk_errv(i.i(&err.kind().to_string()).await, err.to_string(), posv) }
} pub fn os_str_to_string<'a, I: Into<ErrPos>>(
&self,
pub async fn os_str_to_string<'a, I: Into<ErrPos>>(
str: &'a OsStr, str: &'a OsStr,
i: &Interner, posv: impl IntoIterator<Item = I> + 'static,
posv: impl IntoIterator<Item = I>, ) -> OrcRes<&'a str> {
) -> OrcRes<&'a str> {
match str.to_str() { match str.to_str() {
Some(str) => Ok(str), Some(str) => Ok(str),
None => Err(mk_errv( None => Err(self.mk_err(
i.i("Non-unicode string").await, "Non-unicode string",
format!("{str:?} is not representable as unicode"), format!("{str:?} is not representable as unicode"),
posv, posv,
)), )),
} }
}
} }
pub struct Reporter { pub struct Reporter {
errors: RefCell<Vec<OrcErr>>, errors: RefCell<Option<OrcErr>>,
} }
impl Reporter { impl Reporter {
pub fn report(&self, e: impl Into<OrcErrv>) { self.errors.borrow_mut().extend(e.into()) } pub fn report(&self, e: impl Into<OrcErr>) {
pub fn new() -> Self { Self { errors: RefCell::new(vec![]) } } match &mut *self.errors.borrow_mut() {
pub fn errv(self) -> Option<OrcErrv> { OrcErrv::new(self.errors.into_inner()).ok() } slot @ None => *slot = Some(e.into()),
Some(err) => *err += e.into(),
}
}
pub fn new() -> Self { Self { errors: RefCell::new(None) } }
pub fn res(self) -> Result<(), OrcErr> {
match self.errors.into_inner() {
Some(e) => Err(e),
None => Ok(()),
}
}
pub fn merge<T>(self, res: OrcRes<T>) -> OrcRes<T> { pub fn merge<T>(self, res: OrcRes<T>) -> OrcRes<T> {
match (res, self.errv()) { match (res, self.res()) {
(res, None) => res, (res, Ok(())) => res,
(Ok(_), Some(errv)) => Err(errv), (Ok(_), Err(e)) => Err(e),
(Err(e), Some(errv)) => Err(e + errv), (Err(e), Err(e2)) => Err(e + e2),
} }
} }
pub fn is_empty(&self) -> bool { self.errors.borrow().is_empty() } pub fn is_empty(&self) -> bool { self.errors.borrow().is_none() }
} }
impl Default for Reporter { impl Default for Reporter {

View File

@@ -1,12 +1,14 @@
use std::any::Any;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::future::Future; use std::future::Future;
use std::hash::BuildHasher as _; use std::hash::{BuildHasher as _, Hash};
use std::num::NonZeroU64; 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::sync::atomic;
use std::{fmt, hash}; use std::{fmt, hash};
use futures::future::LocalBoxFuture;
use futures::lock::Mutex; use futures::lock::Mutex;
use hashbrown::{HashMap, HashSet}; use hashbrown::{HashMap, HashSet};
use itertools::Itertools as _; use itertools::Itertools as _;
@@ -15,296 +17,460 @@ use orchid_api_traits::Request;
use crate::api; use crate::api;
use crate::reqnot::{DynRequester, Requester}; use crate::reqnot::{DynRequester, Requester};
/// Clippy crashes while verifying `Tok: Sized` without this and I cba to create // /// Clippy crashes while verifying `Tok: Sized` without this and I cba to
/// a minimal example // create /// a minimal example
#[derive(Clone)] // #[derive(Clone)]
struct ForceSized<T>(T); // struct ForceSized<T>(T);
#[derive(Clone)] // #[derive(Clone)]
pub struct Tok<T: Interned> { // pub struct Tok<T: Interned> {
data: Rc<T>, // data: Rc<T>,
marker: ForceSized<T::Marker>, // marker: ForceSized<T::Marker>,
} // }
impl<T: Interned> Tok<T> { // impl<T: Interned> Tok<T> {
pub fn new(data: Rc<T>, marker: T::Marker) -> Self { Self { data, marker: ForceSized(marker) } } // pub fn new(data: Rc<T>, marker: T::Marker) -> Self { Self { data, marker:
pub fn to_api(&self) -> T::Marker { self.marker.0 } // ForceSized(marker) } } pub fn to_api(&self) -> T::Marker { self.marker.0 }
pub async fn from_api<M>(marker: M, i: &Interner) -> Self // pub async fn from_api<M>(marker: M, i: &Interner) -> Self
where M: InternMarker<Interned = T> { // where M: InternMarker<Interned = T> {
i.ex(marker).await // i.ex(marker).await
} // }
pub fn rc(&self) -> Rc<T> { self.data.clone() } // pub fn rc(&self) -> Rc<T> { self.data.clone() }
} // }
impl<T: Interned> Deref for Tok<T> { // impl<T: Interned> Deref for Tok<T> {
type Target = T; // type Target = T;
fn deref(&self) -> &Self::Target { self.data.as_ref() } // fn deref(&self) -> &Self::Target { self.data.as_ref() }
} // }
impl<T: Interned> Ord for Tok<T> { // impl<T: Interned> Ord for Tok<T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.to_api().cmp(&other.to_api()) } // fn cmp(&self, other: &Self) -> std::cmp::Ordering {
} // self.to_api().cmp(&other.to_api()) } }
impl<T: Interned> PartialOrd for Tok<T> { // impl<T: Interned> PartialOrd for Tok<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) } // fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
} // Some(self.cmp(other)) } }
impl<T: Interned> Eq for Tok<T> {} // impl<T: Interned> Eq for Tok<T> {}
impl<T: Interned> PartialEq for Tok<T> { // impl<T: Interned> PartialEq for Tok<T> {
fn eq(&self, other: &Self) -> bool { self.cmp(other).is_eq() } // fn eq(&self, other: &Self) -> bool { self.cmp(other).is_eq() }
} // }
impl<T: Interned> hash::Hash for Tok<T> { // impl<T: Interned> hash::Hash for Tok<T> {
fn hash<H: hash::Hasher>(&self, state: &mut H) { self.to_api().hash(state) } // fn hash<H: hash::Hasher>(&self, state: &mut H) { self.to_api().hash(state) }
} // }
impl<T: Interned + fmt::Display> fmt::Display for Tok<T> { // impl<T: Interned + fmt::Display> fmt::Display for Tok<T> {
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.data) // write!(f, "{}", &*self.data)
} // }
} // }
impl<T: Interned + fmt::Debug> fmt::Debug for Tok<T> { // impl<T: Interned + fmt::Debug> fmt::Debug for Tok<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Token({} -> {:?})", self.to_api().get_id(), self.data.as_ref()) // write!(f, "Token({} -> {:?})", self.to_api().get_id(), self.data.as_ref())
} // }
} // }
pub trait Interned: Eq + hash::Hash + Clone + fmt::Debug + Internable<Interned = Self> { // pub trait Interned: Eq + hash::Hash + Clone + fmt::Debug +
type Marker: InternMarker<Interned = Self> + Sized; // Internable<Interned = Self> { type Marker: InternMarker<Interned = Self> +
fn intern( // Sized; fn intern(
self: Rc<Self>, // self: Rc<Self>,
req: &(impl DynRequester<Transfer = api::IntReq> + ?Sized), // req: &(impl DynRequester<Transfer = api::IntReq> + ?Sized),
) -> impl Future<Output = Self::Marker>; // ) -> impl Future<Output = Self::Marker>;
fn bimap(interner: &mut TypedInterners) -> &mut Bimap<Self>; // fn bimap(interner: &mut TypedInterners) -> &mut Bimap<Self>;
} // }
pub trait Internable: fmt::Debug { // pub trait Internable: fmt::Debug {
type Interned: Interned; // type Interned: Interned;
fn get_owned(&self) -> Rc<Self::Interned>; // fn get_owned(&self) -> Rc<Self::Interned>;
} // }
pub trait InternMarker: Copy + PartialEq + Eq + PartialOrd + Ord + hash::Hash + Sized { // pub trait InternMarker: Copy + PartialEq + Eq + PartialOrd + Ord + hash::Hash
type Interned: Interned<Marker = Self>; // + Sized { type Interned: Interned<Marker = Self>;
/// Only called on replicas // /// Only called on replicas
fn resolve(self, i: &Interner) -> impl Future<Output = Tok<Self::Interned>>; // fn resolve(self, i: &Interner) -> impl Future<Output = Tok<Self::Interned>>;
fn get_id(self) -> NonZeroU64; // fn get_id(self) -> NonZeroU64;
fn from_id(id: NonZeroU64) -> Self; // fn from_id(id: NonZeroU64) -> Self;
} // }
impl Interned for String { // impl Interned for String {
type Marker = api::TStr; // type Marker = api::TStr;
async fn intern( // async fn intern(
self: Rc<Self>, // self: Rc<Self>,
req: &(impl DynRequester<Transfer = api::IntReq> + ?Sized), // req: &(impl DynRequester<Transfer = api::IntReq> + ?Sized),
) -> Self::Marker { // ) -> Self::Marker {
req.request(api::InternStr(self.to_string())).await // req.request(api::InternStr(self.to_string())).await
} // }
fn bimap(interners: &mut TypedInterners) -> &mut Bimap<Self> { &mut interners.strings } // fn bimap(interners: &mut TypedInterners) -> &mut Bimap<Self> { &mut
} // interners.strings } }
impl InternMarker for api::TStr { // impl InternMarker for api::TStr {
type Interned = String; // type Interned = String;
async fn resolve(self, i: &Interner) -> Tok<Self::Interned> { // 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) // Tok::new(Rc::new(i.0.master.as_ref().unwrap().
} // request(api::ExternStr(self)).await), self) }
fn get_id(self) -> NonZeroU64 { self.0 } // fn get_id(self) -> NonZeroU64 { self.0 }
fn from_id(id: NonZeroU64) -> Self { Self(id) } // fn from_id(id: NonZeroU64) -> Self { Self(id) }
} // }
impl Internable for str { // impl Internable for str {
type Interned = String; // type Interned = String;
fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_string()) } // fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_string()) }
} // }
impl Internable for String { // impl Internable for String {
type Interned = String; // type Interned = String;
fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_string()) } // fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_string()) }
} // }
impl Interned for Vec<Tok<String>> { // impl Interned for Vec<IStr> {
type Marker = api::TStrv; // type Marker = api::TStrv;
async fn intern( // async fn intern(
self: Rc<Self>, // self: Rc<Self>,
req: &(impl DynRequester<Transfer = api::IntReq> + ?Sized), // req: &(impl DynRequester<Transfer = api::IntReq> + ?Sized),
) -> Self::Marker { // ) -> Self::Marker {
req.request(api::InternStrv(self.iter().map(|t| t.to_api()).collect())).await // req.request(api::InternStrv(self.iter().map(|t|
} // t.to_api()).collect())).await }
fn bimap(interners: &mut TypedInterners) -> &mut Bimap<Self> { &mut interners.vecs } // fn bimap(interners: &mut TypedInterners) -> &mut Bimap<Self> { &mut
} // interners.vecs } }
impl InternMarker for api::TStrv { // impl InternMarker for api::TStrv {
type Interned = Vec<Tok<String>>; // type Interned = Vec<IStr>;
async fn resolve(self, i: &Interner) -> Tok<Self::Interned> { // async fn resolve(self, i: &Interner) -> Tok<Self::Interned> {
let rep = i.0.master.as_ref().unwrap().request(api::ExternStrv(self)).await; // let rep =
let data = futures::future::join_all(rep.into_iter().map(|m| i.ex(m))).await; // i.0.master.as_ref().unwrap().request(api::ExternStrv(self)).await; let data
Tok::new(Rc::new(data), self) // = 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) } // 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>>; // impl Internable for [IStr] {
fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_vec()) } // type Interned = Vec<IStr>;
} // 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>>; // impl<const N: usize> Internable for [IStr; N] {
fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_vec()) } // type Interned = Vec<IStr>;
} // fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_vec()) }
impl Internable for Vec<Tok<String>> { // }
type Interned = Vec<Tok<String>>; // impl Internable for Vec<IStr> {
fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_vec()) } // type Interned = Vec<IStr>;
} // fn get_owned(&self) -> Rc<Self::Interned> { Rc::new(self.to_vec()) }
// }
// impl Internable for Vec<api::TStr> { // impl Internable for Vec<api::TStr> {
// type Interned = Vec<Tok<String>>; // type Interned = Vec<IStr>;
// fn get_owned(&self) -> Arc<Self::Interned> { // fn get_owned(&self) -> Arc<Self::Interned> {
// Arc::new(self.iter().map(|ts| deintern(*ts)).collect()) // Arc::new(self.iter().map(|ts| deintern(*ts)).collect())
// } // }
// } // }
// impl Internable for [api::TStr] { // impl Internable for [api::TStr] {
// type Interned = Vec<Tok<String>>; // type Interned = Vec<IStr>;
// fn get_owned(&self) -> Arc<Self::Interned> { // fn get_owned(&self) -> Arc<Self::Interned> {
// Arc::new(self.iter().map(|ts| deintern(*ts)).collect()) // Arc::new(self.iter().map(|ts| deintern(*ts)).collect())
// } // }
// } // }
/// The number of references held to any token by the interner. macro_rules! token_def {
const BASE_RC: usize = 3; ($type:ident, $trait:ident, $deref:ty, $api_repr:ty, $type_name:expr) => {
#[derive(Clone)]
#[test] pub struct $type(Rc<dyn $trait>);
fn base_rc_correct() { impl $type {
let tok = Tok::new(Rc::new("foo".to_string()), api::TStr(1.try_into().unwrap())); pub fn new<T: $trait + 'static>(t: T) -> Self { Self(Rc::new(t) as _) }
let mut bimap = Bimap::default(); pub fn to_api(&self) -> $api_repr { self.0.to_api() }
bimap.insert(tok.clone()); pub fn inner(&self) -> &dyn Any { self.0.as_ref() }
assert_eq!(Rc::strong_count(&tok.data), BASE_RC + 1, "the bimap plus the current instance"); fn addr(&self) -> usize { Rc::as_ptr(&self.0).addr() }
}
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 trait $trait: Deref<Target = $deref> + Any {
pub fn by_marker(&self, marker: T::Marker) -> Option<Tok<T>> { self.by_id.get(&marker).cloned() } fn to_api(&self) -> $api_repr;
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())
} }
impl Deref for $type {
pub fn sweep_replica(&mut self) -> Vec<T::Marker> { type Target = $deref;
(self.intern) fn deref(&self) -> &Self::Target { self.0.deref() }
.extract_if(|k, _| Rc::strong_count(k) == BASE_RC)
.map(|(_, v)| {
self.by_id.remove(&v.to_api());
v.to_api()
})
.collect()
} }
impl Eq for $type {}
pub fn sweep_master(&mut self, retained: HashSet<T::Marker>) { impl PartialEq for $type {
self.intern.retain(|k, v| BASE_RC < Rc::strong_count(k) || retained.contains(&v.to_api())) fn eq(&self, other: &Self) -> bool { Rc::ptr_eq(&self.0, &other.0) }
} }
} impl Ord for $type {
fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.addr().cmp(&other.addr()) }
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 impl PartialOrd for $type {
pub async fn i<T: Interned>(&self, t: &(impl Internable<Interned = T> + ?Sized)) -> Tok<T> { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) }
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 { impl Hash for $type {
Some(c) => data.clone().intern(&**c).await, fn hash<H: hash::Hasher>(&self, state: &mut H) { self.addr().hash(state) }
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 impl std::fmt::Debug for $type {
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_replica(&self) -> api::Retained {
assert!(self.0.master.is_some(), "Not a replica");
let mut g = self.0.interners.lock().await;
api::Retained { strings: g.strings.sweep_replica(), vecs: g.vecs.sweep_replica() }
}
pub async fn sweep_master(&self, retained: api::Retained) {
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 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Interner{{ replica: {} }}", self.0.master.is_none()) f.debug_tuple($type_name).field(&self.to_api().0).field(&self.deref()).finish()
} }
}
};
} }
static ID: atomic::AtomicU64 = atomic::AtomicU64::new(1); token_def!(IStr, IStrDyn, str, api::TStr, "IStr");
impl std::fmt::Display for IStr {
pub fn merge_retained(into: &mut api::Retained, from: &api::Retained) { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.deref()) }
into.strings = into.strings.iter().chain(&from.strings).copied().unique().collect();
into.vecs = into.vecs.iter().chain(&from.vecs).copied().unique().collect();
} }
#[cfg(test)] token_def!(IVec, IVecDyn, [IStr], api::TVec, "IVec");
mod test {
#[derive(Clone)]
pub struct Interner(Rc<dyn InternerDyn>);
impl Interner {
pub fn new<T: InternerDyn + 'static>(t: T) -> Self { Self(Rc::new(t) as _) }
pub async fn is(&self, s: &(impl Borrow<str> + ?Sized)) -> IStr {
IStr(self.0.is(s.borrow()).await)
}
pub async fn iv(&self, s: &(impl Borrow<[IStr]> + ?Sized)) -> IVec {
IVec(self.0.iv(s.borrow()).await)
}
pub async fn es(&self, m: api::TStr) -> IStr { IStr(self.0.es(m).await) }
pub async fn ev(&self, m: api::TVec) -> IVec { IVec(self.0.ev(m).await) }
}
pub trait InternerDyn {
fn is(&self, s: &str) -> LocalBoxFuture<Rc<dyn IStrDyn>>;
fn iv(&self, v: &[IStr]) -> LocalBoxFuture<Rc<dyn IVecDyn>>;
fn es(&self, m: api::TStr) -> LocalBoxFuture<Rc<dyn IStrDyn>>;
fn ev(&self, m: api::TVec) -> LocalBoxFuture<Rc<dyn IVecDyn>>;
}
#[cfg(any(feature = "mocks", test))]
pub mod test {
use std::cell::RefCell;
use std::num::NonZero; use std::num::NonZero;
use std::pin::Pin; use std::sync::atomic::AtomicU64;
use orchid_api_traits::{Decode, enc_vec};
use test_executors::spin_on;
use super::*; use super::*;
use crate::api; use crate::testing::AsyncMonitor;
#[test] pub(crate) struct DummyIStr(NonZeroU64, String);
fn test_i() { impl Deref for DummyIStr {
let i = Interner::new_master(); type Target = str;
let _: Tok<String> = spin_on(i.i("foo")); fn deref(&self) -> &Self::Target { &self.1 }
let _: Tok<Vec<Tok<String>>> = spin_on(i.i(&[spin_on(i.i("bar")), spin_on(i.i("baz"))])); }
impl IStrDyn for DummyIStr {
fn to_api(&self) -> api::TStr { api::TStr(self.0) }
} }
#[test] pub(crate) struct DummyIStrv(NonZeroU64, Vec<IStr>);
fn test_coding() { impl Deref for DummyIStrv {
spin_on(async { type Target = [IStr];
let coded = api::TStr(NonZero::new(3u64).unwrap()); fn deref(&self) -> &Self::Target { &self.1 }
let mut enc = &enc_vec(&coded).await[..]; }
api::TStr::decode(Pin::new(&mut enc)).await; impl IVecDyn for DummyIStrv {
assert_eq!(enc, [], "Did not consume all of {enc:?}") fn to_api(&self) -> api::TVec { api::TVec(self.0) }
}
pub(crate) struct DummyInterner(
RefCell<(
HashMap<String, NonZeroU64>,
HashMap<NonZeroU64, Rc<DummyIStr>>,
HashMap<NonZeroU64, Rc<DummyIStrv>>,
)>,
AsyncMonitor<InternerEvent>,
);
pub enum InternerEvent {
ExternStr(Rc<DummyIStr>),
ExternVec(Rc<DummyIStrv>),
InternStr { token: Rc<DummyIStr>, new: bool },
InternVec { token: Rc<DummyIStrv>, new: bool },
}
impl DummyInterner {
pub fn new(monitor: AsyncMonitor<InternerEvent>) -> Interner {
Interner(Rc::new(Self(RefCell::default(), monitor)))
}
}
impl InternerDyn for DummyInterner {
fn es(&self, m: api::TStr) -> LocalBoxFuture<Rc<dyn IStrDyn>> {
let state = self.0.borrow();
let istr = state.1.get(&m.0).unwrap_or_else(|| panic!("Externed nonexistent {m:?}")).clone();
Box::pin(async {
self.1.notify(InternerEvent::ExternStr(istr.clone())).await;
istr as Rc<dyn IStrDyn>
}) })
} }
fn ev(&self, m: api::TVec) -> LocalBoxFuture<Rc<dyn IVecDyn>> {
let state = self.0.borrow();
let ivec = state.2.get(&m.0).unwrap_or_else(|| panic!("Externed nonexistent {m:?}")).clone();
Box::pin(async {
self.1.notify(InternerEvent::ExternVec(ivec.clone())).await;
ivec as Rc<dyn IVecDyn>
})
}
fn is(&self, s: &str) -> LocalBoxFuture<Rc<dyn IStrDyn>> {
let mut this = self.0.borrow_mut();
let id = *(this.0.entry(format!("{s:?}")))
.or_insert_with(|| NonZero::new(COUNTER.fetch_add(1, atomic::Ordering::Relaxed)).unwrap());
let (tok, new) = match this.1.entry(id) {
hashbrown::hash_map::Entry::Occupied(ent) => (ent.get().clone(), false),
hashbrown::hash_map::Entry::Vacant(ent) =>
(ent.insert(Rc::new(DummyIStr(id, s.to_string()))).clone(), true),
};
Box::pin(async move {
self.1.notify(InternerEvent::InternStr { token: tok.clone(), new }).await;
tok as _
})
}
fn iv(&self, s: &[IStr]) -> LocalBoxFuture<Rc<dyn IVecDyn>> {
let mut this = self.0.borrow_mut();
let id = *(this.0.entry(format!("{s:?}")))
.or_insert_with(|| NonZero::new(COUNTER.fetch_add(1, atomic::Ordering::Relaxed)).unwrap());
let (tok, new) = match this.2.entry(id) {
hashbrown::hash_map::Entry::Occupied(ent) => (ent.get().clone(), false),
hashbrown::hash_map::Entry::Vacant(ent) =>
(ent.insert(Rc::new(DummyIStrv(id, s.to_vec()))).clone(), true),
};
Box::pin(async move {
self.1.notify(InternerEvent::InternVec { token: tok.clone(), new }).await;
tok as _
})
}
}
static COUNTER: AtomicU64 = AtomicU64::new(1);
} }
// /// 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<K, V, Tok> {
// intern: HashMap<V, Tok>,
// by_id: HashMap<K, Tok>,
// }
// impl<K> 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<IStr>>,
// }
// #[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_replica(&self) -> api::Retained {
// assert!(self.0.master.is_some(), "Not a replica");
// let mut g = self.0.interners.lock().await;
// api::Retained { strings: g.strings.sweep_replica(), vecs:
// g.vecs.sweep_replica() } }
// pub async fn sweep_master(&self, retained: api::Retained) {
// 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::Retained, from: &api::Retained) {
// 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 _: IStr = spin_on(i.i("foo"));
// let _: Tok<Vec<IStr>> = 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

@@ -7,6 +7,7 @@ pub mod builtin;
pub mod char_filter; pub mod char_filter;
pub mod clone; pub mod clone;
pub mod combine; pub mod combine;
pub mod ctx;
pub mod error; pub mod error;
pub mod event; pub mod event;
pub mod format; pub mod format;
@@ -25,6 +26,7 @@ pub mod pure_seq;
pub mod reqnot; pub mod reqnot;
pub mod sequence; pub mod sequence;
pub mod side; pub mod side;
pub mod testing;
mod tl_cache; mod tl_cache;
pub mod tokens; pub mod tokens;
pub mod tree; pub mod tree;

View File

@@ -7,12 +7,12 @@ use std::ops::Range;
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, Interner};
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)]
@@ -131,24 +131,21 @@ 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, i: &Interner) -> Self {
Self { generator, details: i.i("").await } Self { generator, details: i.is("").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>, i: &Interner) -> Self {
Self { generator, details: i.i(details.as_ref()).await } Self { generator, details: i.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, i: &Interner) -> Self {
Self { Self { generator: Sym::from_api(api.generator, i).await, details: i.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,52 +12,48 @@ 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, IVec, Interner};
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, i: &Interner) -> 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(|s| i.is(s))).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> { self.0.iter().map(|s| s.as_ref()) }
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())
} }
@@ -65,7 +61,7 @@ impl VPath {
pub async fn from_path(path: &Path, ext: &str, i: &Interner) -> Option<(Self, bool)> { pub async fn from_path(path: &Path, ext: &str, i: &Interner) -> Option<(Self, bool)> {
async fn to_vpath(p: &Path, i: &Interner) -> Option<VPath> { async fn to_vpath(p: &Path, i: &Interner) -> Option<VPath> {
let tok_opt_v = let tok_opt_v =
join_all(p.iter().map(|c| OptionFuture::from(c.to_str().map(|s| i.i(s))))).await; join_all(p.iter().map(|c| OptionFuture::from(c.to_str().map(|s| i.is(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()) {
@@ -83,30 +79,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,11 +110,11 @@ 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)) }
} }
@@ -128,27 +122,27 @@ impl VName {
name: impl IntoIterator<Item = api::TStr>, name: impl IntoIterator<Item = api::TStr>,
i: &Interner, i: &Interner,
) -> Result<Self, EmptyNameError> { ) -> Result<Self, EmptyNameError> {
Self::new(join_all(name.into_iter().map(|m| Tok::from_api(m, i))).await) Self::new(join_all(name.into_iter().map(|m| i.es(m))).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, i: &Interner) -> Sym { Sym(i.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
@@ -159,7 +153,7 @@ impl VName {
Self::parse(s, i).await.expect("empty literal !?") 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 +164,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 +187,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,36 +198,36 @@ 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(IVec);
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 = Tok<String>>, v: impl IntoIterator<Item = IStr>,
i: &Interner, i: &Interner,
) -> Result<Self, EmptyNameError> { ) -> 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(i.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, i: &Interner) -> Result<Self, EmptyNameError> {
Ok(Sym(i.i(&VName::parse(s, i).await?.into_vec()).await)) Ok(Sym(i.iv(&VName::parse(s, i).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: IVec) -> 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) -> IVec { 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::TVec, i: &Interner) -> Sym {
Self::from_tok(Tok::from_api(marker, i).await).expect("Empty sequence found for serialized Sym") Self::from_tok(i.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::TVec { 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>, i: &Interner) -> Sym {
Self::new(self.0.iter().cloned().chain(tokv), i).await.unwrap() Self::new(self.0.iter().cloned().chain(tokv), i).await.unwrap()
} }
} }
@@ -248,17 +240,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,15 +258,15 @@ 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.as_str()) self.as_slice().iter().map(|t| t.as_ref())
} }
/// Fully resolve the name for printing /// Fully resolve the name for printing
#[must_use] #[must_use]
@@ -286,19 +278,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 {}
@@ -313,9 +305,9 @@ impl NameLike for VName {}
macro_rules! sym { macro_rules! sym {
($seg1:tt $( :: $seg:tt)* ; $i:expr) => { async { ($seg1:tt $( :: $seg:tt)* ; $i:expr) => { async {
$crate::name::Sym::from_tok( $crate::name::Sym::from_tok(
$i.i(&[ $i.iv(&[
$i.i(stringify!($seg1)).await $i.is(stringify!($seg1)).await
$( , $i.i(stringify!($seg)).await )* $( , $i.is(stringify!($seg)).await )*
]) ])
.await .await
).unwrap() ).unwrap()
@@ -331,8 +323,8 @@ macro_rules! sym {
macro_rules! vname { macro_rules! vname {
($seg1:tt $( :: $seg:tt)* ; $i:expr) => { async { ($seg1:tt $( :: $seg:tt)* ; $i:expr) => { async {
$crate::name::VName::new([ $crate::name::VName::new([
$i.i(stringify!($seg1)).await $i.is(stringify!($seg1)).await
$( , $i.i(stringify!($seg)).await )* $( , $i.is(stringify!($seg)).await )*
]).unwrap() ]).unwrap()
} }; } };
} }
@@ -344,8 +336,8 @@ macro_rules! vname {
macro_rules! vpath { macro_rules! vpath {
($seg1:tt $( :: $seg:tt)+ ; $i:expr) => { async { ($seg1:tt $( :: $seg:tt)+ ; $i:expr) => { async {
$crate::name::VPath(vec![ $crate::name::VPath(vec![
$i.i(stringify!($seg1)).await $i.is(stringify!($seg1)).await
$( , $i.i(stringify!($seg)).await )+ $( , $i.is(stringify!($seg)).await )+
]) ])
} }; } };
() => { () => {
@@ -360,35 +352,37 @@ mod test {
use test_executors::spin_on; use test_executors::spin_on;
use super::{NameLike, Sym, VName}; use super::{NameLike, Sym, VName};
use crate::interner::{Interner, Tok}; use crate::interner::test::DummyInterner;
use crate::interner::{IStr, Interner};
use crate::name::VPath; use crate::name::VPath;
use crate::testing::AsyncMonitor;
#[test] #[test]
fn recur() { fn recur() {
spin_on(async { spin_on(async {
let i = Interner::new_master(); let i = DummyInterner::new(AsyncMonitor::default());
let myname = vname!(foo::bar; i).await; let myname = vname!(foo::bar; i).await;
let _borrowed_slice: &[Tok<String>] = myname.borrow(); let _borrowed_slice: &[IStr] = myname.borrow();
let _deref_pathslice: &[Tok<String>] = &myname; let _deref_pathslice: &[IStr] = &myname;
let _as_slice_out: &[Tok<String>] = myname.as_slice(); let _as_slice_out: &[IStr] = myname.as_slice();
}) })
} }
#[test] #[test]
fn literals() { fn literals() {
spin_on(async { spin_on(async {
let i = Interner::new_master(); let i = DummyInterner::new(AsyncMonitor::default());
assert_eq!( assert_eq!(
sym!(foo::bar::baz; i).await, sym!(foo::bar::baz; i).await,
Sym::new([i.i("foo").await, i.i("bar").await, i.i("baz").await], &i).await.unwrap() Sym::new([i.is("foo").await, i.is("bar").await, i.is("baz").await], &i).await.unwrap()
); );
assert_eq!( assert_eq!(
vname!(foo::bar::baz; i).await, vname!(foo::bar::baz; i).await,
VName::new([i.i("foo").await, i.i("bar").await, i.i("baz").await]).unwrap() VName::new([i.is("foo").await, i.is("bar").await, i.is("baz").await]).unwrap()
); );
assert_eq!( assert_eq!(
vpath!(foo::bar::baz; i).await, vpath!(foo::bar::baz; i).await,
VPath::new([i.i("foo").await, i.i("bar").await, i.i("baz").await]) VPath::new([i.is("foo").await, i.is("bar").await, i.is("baz").await])
); );
}) })
} }

View File

@@ -3,8 +3,8 @@ use std::ops::Range;
use ordered_float::NotNan; use ordered_float::NotNan;
use crate::error::{OrcErrv, mk_errv}; use crate::ctx::Ctx;
use crate::interner::Interner; use crate::error::OrcErr;
use crate::location::SrcRange; use crate::location::SrcRange;
use crate::name::Sym; use crate::name::Sym;
@@ -55,14 +55,14 @@ pub struct NumError {
pub kind: NumErrorKind, pub kind: NumErrorKind,
} }
pub async fn num_to_errv( pub fn num_to_errv(
NumError { kind, range }: NumError, NumError { kind, range }: NumError,
offset: u32, offset: u32,
source: &Sym, source: &Sym,
i: &Interner, ctx: &Ctx,
) -> OrcErrv { ) -> OrcErr {
mk_errv( ctx.mk_err(
i.i("Failed to parse number").await, "Failed to parse number",
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

@@ -1,34 +1,21 @@
use std::fmt::{self, Display}; use std::fmt::{self, Display};
use std::iter; use std::iter;
use std::ops::Deref; use std::ops::Deref;
use std::rc::Rc;
use futures::FutureExt; use futures::FutureExt;
use futures::future::join_all; 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::ctx::Ctx;
use crate::error::{OrcErr, OrcRes, Reporter};
use crate::format::{FmtCtx, FmtUnit, Format, fmt}; use crate::format::{FmtCtx, FmtUnit, Format, fmt};
use crate::interner::{Interner, Tok}; use crate::interner::{IStr, Interner};
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 +90,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, cx: &Ctx) -> Self {
Self { text: i.ex(c.text).await, sr: SrcRange::new(c.range.clone(), &src) } Self { text: cx.i().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>, cx: &Ctx) -> 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: cx.i().is(&**text).await, sr: tk.sr.clone() }),
_ => None, _ => None,
} }
} }
pub fn to_tk<R: ExprRepr, X: ExtraTok>(&self) -> TokTree<R, X> { pub fn to_tk<A: ExprRepr, X: ExtraTok>(&self) -> TokTree<A, X> {
TokTree { tok: Token::Comment(self.text.rc().clone()), sr: self.sr.clone() } TokTree { tok: Token::Comment(Rc::new(self.text.to_string())), 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 +117,7 @@ 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, ctx: &Ctx,
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();
@@ -146,7 +133,7 @@ pub async fn line_items<'a, A: ExprRepr, X: ExtraTok>(
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(comments.drain(..).chain(cmts.cur).map(|t| async {
Comment::from_tk(t, ctx.i()).await.expect("All are comments checked above") Comment::from_tk(t, ctx).await.expect("All are comments checked above")
})) }))
.await; .await;
items.push(Parsed { output: comments, tail }); items.push(Parsed { output: comments, tail });
@@ -157,56 +144,50 @@ 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, ctx: &Ctx,
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 => Err(
ctx.i().i("Unexpected end").await, ctx.mk_err("Unexpected end", "Line ends abruptly; more tokens were expected", [snip.sr()]),
"Line ends abruptly; more tokens were expected", ),
[snip.sr()],
)),
} }
} }
pub async fn expect_end( pub async fn expect_end(ctx: &Ctx, 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) =>
ctx.i().i("Extra code after end of line").await, Err(ctx.mk_err("Extra code after end of line", "Code found after the end of the line", [
"Code found after the end of the line", surplus.sr.pos(),
[surplus.sr.pos()], ])),
)),
None => Ok(()), None => Ok(()),
} }
} }
pub async fn expect_tok<'a, A: ExprRepr, X: ExtraTok>( pub async fn expect_tok<'a, A: ExprRepr, X: ExtraTok>(
ctx: &impl ParseCtx, ctx: &Ctx,
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(ctx, 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(ctx.mk_err(
ctx.i().i("Expected specific keyword").await, "Expected specific keyword",
format!("Expected {tok} but found {:?}", fmt(t, ctx.i()).await), format!("Expected {tok} but found {:?}", fmt(t, &ctx.i()).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, ctx: &Ctx,
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 { ) -> OrcErr {
mk_errv(ctx.i().i(description).await, message(&fmt(tok, ctx.i()).await), [tok.sr.pos()]) ctx.mk_err(description, message(&fmt(tok, &ctx.i()).await), [tok.sr.pos()])
} }
pub struct Parsed<'a, T, H: ExprRepr, X: ExtraTok> { pub struct Parsed<'a, T, H: ExprRepr, X: ExtraTok> {
@@ -217,12 +198,12 @@ 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, ctx: &Ctx,
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(ctx.mk_err(
ctx.i().i("Expected token").await, "Expected token",
"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()],
)); ));
@@ -231,17 +212,14 @@ pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>(
#[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, ctx: &Ctx,
) -> OrcRes<Vec<(Vec<Tok<String>>, Option<Tok<String>>, SrcRange)>> { ) -> OrcRes<Vec<(Vec<IStr>, Option<IStr>, 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( let err = ctx.mk_err("Unexpected name prefix", "Only names can precede ::", [ttpos]);
ctx.i().i("Unexpected name prefix").await, ctx.rep().report(err)
"Only names can precede ::",
[ttpos],
))
}; };
let out = Box::pin(rec(body, ctx)).await?; let out = Box::pin(rec(body, ctx)).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())
@@ -264,9 +242,9 @@ pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>(
Ok(o) Ok(o)
}, },
t => { t => {
return Err(mk_errv( return Err(ctx.mk_err(
ctx.i().i("Unrecognized name end").await, "Unrecognized name end",
format!("Names cannot end with {:?} tokens", fmt(t, ctx.i()).await), format!("Names cannot end with {:?} tokens", fmt(t, &ctx.i()).await),
[ttpos], [ttpos],
)); ));
}, },
@@ -285,7 +263,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 +274,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, self.name.as_deref().unwrap_or("*"))
} }
} }

View File

@@ -5,15 +5,18 @@ use std::marker::PhantomData;
use std::mem; use std::mem;
use std::ops::{BitAnd, Deref}; use std::ops::{BitAnd, Deref};
use std::pin::Pin; use std::pin::Pin;
use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use derive_destructure::destructure; use derive_destructure::destructure;
use dyn_clone::{DynClone, clone_box}; use dyn_clone::{DynClone, clone_box};
use futures::channel::mpsc; use futures::channel::mpsc::{self, Sender};
use futures::channel::oneshot;
use futures::future::LocalBoxFuture; use futures::future::LocalBoxFuture;
use futures::lock::Mutex; use futures::lock::Mutex;
use futures::{SinkExt, StreamExt}; use futures::{AsyncBufRead, AsyncWrite, SinkExt, Stream, StreamExt};
use hashbrown::HashMap; use hashbrown::HashMap;
use orchid_api_traits::{Channel, Coding, Decode, Encode, MsgSet, Request}; use orchid_api_traits::{Channel, Coding, Decode, Encode, MsgSet, Request};
use trait_set::trait_set; use trait_set::trait_set;
@@ -23,6 +26,71 @@ use crate::logging::Logger;
pub struct Receipt<'a>(PhantomData<&'a mut ()>); pub struct Receipt<'a>(PhantomData<&'a mut ()>);
/// This object holds an exclusive lock on the outbound pipe.
pub trait DynRequestWriter {
fn writer(&mut self) -> Pin<&mut dyn AsyncWrite>;
/// Release the outbound pipe and wait for the response to begin.
fn get_response(self: Box<Self>) -> Pin<Box<dyn Future<Output = Box<dyn DynResponseHandle>>>>;
}
/// This object holds an exclusive lock on the inbound pipe.
pub trait DynResponseHandle {
fn reader(&mut self) -> Pin<&mut dyn AsyncBufRead>;
fn finish(self: Box<Self>) -> Pin<Box<dyn Future<Output = ()>>>;
}
/// This object holds an exclusive lock on the outbound pipe.
pub trait DynNotifWriter {
fn writer(&mut self) -> Pin<&mut dyn AsyncWrite>;
fn finish(self: Box<Self>) -> Pin<Box<dyn Future<Output = ()>>>;
}
pub trait DynClient {
fn request(&self) -> Pin<Box<dyn Future<Output = Box<dyn DynRequestWriter>>>>;
fn notif(&self) -> Pin<Box<dyn Future<Output = Box<dyn DynNotifWriter>>>>;
}
pub struct Client<T: MsgSet>(pub(crate) Rc<dyn DynClient>, pub(crate) PhantomData<T>);
impl<T: MsgSet> Client<T> {
pub async fn notify<Notif: Into<<T::Out as Channel>::Notif>>(&self, notif: Notif) {
let mut notif_writer = self.0.notif().await;
notif.into().encode(notif_writer.writer()).await;
notif_writer.finish().await;
}
pub async fn request<Req: Request + Into<<T::Out as Channel>::Req>>(
&self,
req: Req,
) -> Req::Response {
let root_req = req.into();
let mut req_writer = self.0.request().await;
root_req.encode(req_writer.writer()).await;
let mut req_hand = req_writer.get_response().await;
let res = Req::Response::decode(req_hand.reader()).await;
req_hand.finish().await;
res
}
}
pub struct DuplexServerState {
pending_outbound: HashMap<u64, Box<dyn FnOnce(&mut dyn AsyncBufRead)>>,
sender: Pin<Box<dyn AsyncWrite>>,
receiver: Pin<Box<dyn AsyncBufRead>>,
}
pub enum ServerEvent<T: MsgSet> {
Notif(<T::In as Channel>::Notif),
Req(RequestHandle<T>, <T::In as Channel>::Req),
}
pub async fn run_duplex_server<T: MsgSet>(
sender: Pin<Box<dyn AsyncWrite>>,
receiver: Pin<Box<dyn AsyncBufRead>>,
) -> (impl Stream<Item = ServerEvent<T>>, Client<T>) {
let sender = Rc::new(Mutex::new(sender));
let receiver = Rc::new(Mutex::new(receiver));
let pending_outbound = Rc::new(Mutex::new(HashMap::new()));
}
pub struct DuplexServer(Rc<Mutex<DuplexServerState>>);
impl DuplexServer {
pub fn receive(msg: )
}
trait_set! { trait_set! {
pub trait SendFn<T: MsgSet> = pub trait SendFn<T: MsgSet> =
for<'a> FnMut(&'a [u8], ReqNot<T>) -> LocalBoxFuture<'a, ()> for<'a> FnMut(&'a [u8], ReqNot<T>) -> LocalBoxFuture<'a, ()>
@@ -52,11 +120,10 @@ impl ReqHandlish for &'_ dyn ReqHandlish {
} }
#[derive(destructure)] #[derive(destructure)]
pub struct RequestHandle<'a, MS: MsgSet> { pub struct RequestHandle<MS: MsgSet> {
defer_drop: RefCell<Vec<Box<dyn Any>>>, defer_drop: RefCell<Vec<Box<dyn Any>>>,
fulfilled: AtomicBool, fulfilled: AtomicBool,
id: u64, id: u64,
_reqlt: PhantomData<&'a mut ()>,
parent: ReqNot<MS>, parent: ReqNot<MS>,
} }
impl<'a, MS: MsgSet + 'static> RequestHandle<'a, MS> { impl<'a, MS: MsgSet + 'static> RequestHandle<'a, MS> {
@@ -89,7 +156,7 @@ impl<'a, MS: MsgSet + 'static> RequestHandle<'a, MS> {
impl<MS: MsgSet> ReqHandlish for RequestHandle<'_, MS> { impl<MS: MsgSet> ReqHandlish for RequestHandle<'_, MS> {
fn defer_drop_objsafe(&self, val: Box<dyn Any>) { self.defer_drop.borrow_mut().push(val); } fn defer_drop_objsafe(&self, val: Box<dyn Any>) { self.defer_drop.borrow_mut().push(val); }
} }
impl<MS: MsgSet> Drop for RequestHandle<'_, MS> { impl<MS: MsgSet> Drop for RequestHandle<MS> {
fn drop(&mut self) { fn drop(&mut self) {
let done = self.fulfilled.load(Ordering::Relaxed); let done = self.fulfilled.load(Ordering::Relaxed);
debug_assert!(done, "Request {} dropped without response", self.id) debug_assert!(done, "Request {} dropped without response", self.id)
@@ -123,7 +190,7 @@ impl<T: MsgSet> ReqNot<T> {
notif: impl NotifFn<T>, notif: impl NotifFn<T>,
req: impl ReqFn<T>, req: impl ReqFn<T>,
) -> Self { ) -> Self {
Self( let this = Self(
Arc::new(Mutex::new(ReqNotData { Arc::new(Mutex::new(ReqNotData {
id: 1, id: 1,
send: Box::new(send), send: Box::new(send),
@@ -132,7 +199,13 @@ impl<T: MsgSet> ReqNot<T> {
responses: HashMap::new(), responses: HashMap::new(),
})), })),
logger, logger,
) );
let (sig_send, sig_recv) = std::sync::mpsc::sync_channel(0);
std::thread::spawn(move || {
std::thread::sleep(Duration::from_secs(10));
sig_send.send(()).expect("Crash!");
});
this
} }
/// Can be called from a polling thread or dispatched in any other way /// Can be called from a polling thread or dispatched in any other way

View File

@@ -0,0 +1,23 @@
#![cfg(any(feature = "mocks", test))]
use std::future::ready;
use std::pin::Pin;
use std::rc::Rc;
pub struct AsyncMonitor<E: 'static>(Rc<dyn Fn(E) -> Pin<Box<dyn Future<Output = ()>>>>);
impl<E: 'static> AsyncMonitor<E> {
pub fn new<F: AsyncFn(E) -> () + 'static>(f: F) -> Self {
let f_rc = Rc::new(f);
AsyncMonitor(Rc::new(move |e| {
let f_rc = f_rc.clone();
Box::pin(async move { f_rc(e).await })
}))
}
pub async fn notify(&self, e: E) -> () { (self.0)(e).await }
}
impl<E: 'static> Default for AsyncMonitor<E> {
fn default() -> Self { Self(Rc::new(|_| Box::pin(ready(())))) }
}
impl<E: 'static> Clone for AsyncMonitor<E> {
fn clone(&self) -> Self { Self(self.0.clone()) }
}

View File

@@ -12,9 +12,9 @@ use never::Never;
use orchid_api_traits::Coding; use orchid_api_traits::Coding;
use trait_set::trait_set; use trait_set::trait_set;
use crate::error::OrcErrv; use crate::error::OwnedOrcErr;
use crate::format::{FmtCtx, FmtUnit, Format, Variants}; use crate::format::{FmtCtx, FmtUnit, Format, Variants};
use crate::interner::{Interner, Tok}; use crate::interner::{IStr, Interner};
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;
@@ -113,11 +113,11 @@ impl<H: ExprRepr, X: ExtraTok> TokTree<H, X> {
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 => i.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, i).boxed_local().await)),
Bottom(e => OrcErrv::from_api(e, i).await), Bottom(e => OwnedOrcErr::from_api(e, i).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, i).boxed_local().await)),
Name(n => Tok::from_api(*n, i).await), Name(n => i.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, i).await),
Comment(c.clone()), Comment(c.clone()),
NewExpr(expr => X::from_api(expr, xctx, pos.clone(), i).await), NewExpr(expr => X::from_api(expr, xctx, pos.clone(), i).await),
@@ -145,8 +145,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>> {
@@ -245,9 +245,9 @@ pub enum Token<H: ExprRepr, X: ExtraTok> {
/// 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 `{}`
@@ -259,11 +259,11 @@ pub enum Token<H: ExprRepr, X: ExtraTok> {
/// A grammar error emitted by a lexer plugin if it was possible to continue /// A grammar error emitted by a lexer plugin if it was possible to continue
/// reading. Parsers should treat it as an atom unless it prevents parsing, /// reading. Parsers should treat it as an atom unless it prevents parsing,
/// in which case both this and a relevant error should be returned. /// in which case both this and a relevant error should be returned.
Bottom(OrcErrv), Bottom(OwnedOrcErr),
} }
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),
@@ -275,8 +275,7 @@ impl<H: ExprRepr, X: ExtraTok> Format for Token<H, X> {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
match self { match self {
Self::BR => "\n".to_string().into(), Self::BR => "\n".to_string().into(),
Self::Bottom(err) if err.len() == 1 => format!("Bottom({}) ", err.one().unwrap()).into(), Self::Bottom(err) => format!("Botttom({}) ", indent(&err.to_string())).into(),
Self::Bottom(err) => format!("Botttom(\n{}) ", indent(&err.to_string())).into(),
Self::Comment(c) => format!("--[{c}]--").into(), Self::Comment(c) => format!("--[{c}]--").into(),
Self::LambdaHead(arg) => Self::LambdaHead(arg) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("\\{0b}."))) tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("\\{0b}.")))

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,15 @@ 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: &[Tok<String>]) -> Option<String> { (*self).display(path) } fn display(&self, path: &[IStr]) -> 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: &[Tok<String>]) -> Option<String> { (**self).display(path) } fn display(&self, path: &[IStr]) -> 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

@@ -13,7 +13,8 @@ use futures::{AsyncRead, AsyncWrite, FutureExt, StreamExt, stream};
use orchid_api_derive::Coding; use orchid_api_derive::Coding;
use orchid_api_traits::{Coding, Decode, Encode, Request, enc_vec}; use orchid_api_traits::{Coding, Decode, Encode, Request, enc_vec};
use orchid_base::clone; use orchid_base::clone;
use orchid_base::error::{OrcErrv, OrcRes, mk_errv, mk_errv_floating}; use orchid_base::ctx::Ctx;
use orchid_base::error::{OrcErr, OrcRes};
use orchid_base::format::{FmtCtx, FmtUnit, Format}; use orchid_base::format::{FmtCtx, FmtUnit, Format};
use orchid_base::interner::Interner; use orchid_base::interner::Interner;
use orchid_base::location::Pos; use orchid_base::location::Pos;
@@ -135,12 +136,10 @@ pub struct NotTypAtom {
pub ctx: SysCtx, pub ctx: SysCtx,
} }
impl NotTypAtom { impl NotTypAtom {
pub async fn mk_err(&self) -> OrcErrv { pub fn mk_err(&self, ctx: &Ctx) -> OrcErr {
mk_errv( ctx.mk_err("Not the expected type", format!("This expression is not a {}", self.typ.name()), [
self.ctx.i().i("Not the expected type").await, self.pos.clone(),
format!("This expression is not a {}", self.typ.name()), ])
[self.pos.clone()],
)
} }
} }
@@ -329,10 +328,10 @@ impl Format for AtomFactory {
} }
} }
pub async fn err_not_callable(i: &Interner) -> OrcErrv { pub fn err_not_callable(cx: &Ctx) -> OrcErr {
mk_errv_floating(i.i("This atom is not callable").await, "Attempted to apply value as function") cx.mk_err_floating("This atom is not callable", "Attempted to apply value as function")
} }
pub async fn err_not_command(i: &Interner) -> OrcErrv { pub fn err_not_command(cx: &Ctx) -> OrcErr {
mk_errv_floating(i.i("This atom is not a command").await, "Settled on an inactionable value") cx.mk_err_floating("This atom is not a command", "Settled on an inactionable value")
} }

View File

@@ -1,7 +1,7 @@
use std::future::Future; use std::future::Future;
use never::Never; use never::Never;
use orchid_base::error::{OrcErrv, OrcRes, mk_errv}; use orchid_base::error::{OrcErrv, OrcRes, mk_err};
use orchid_base::interner::Interner; use orchid_base::interner::Interner;
use orchid_base::location::Pos; use orchid_base::location::Pos;
@@ -25,11 +25,11 @@ impl<T: TryFromExpr, U: TryFromExpr> TryFromExpr for (T, U) {
} }
async fn err_not_atom(pos: Pos, i: &Interner) -> OrcErrv { async fn err_not_atom(pos: Pos, i: &Interner) -> OrcErrv {
mk_errv(i.i("Expected an atom").await, "This expression is not an atom", [pos]) mk_err(i.i("Expected an atom").await, "This expression is not an atom", [pos])
} }
async fn err_type(pos: Pos, i: &Interner) -> OrcErrv { async fn err_type(pos: Pos, i: &Interner) -> OrcErrv {
mk_errv(i.i("Type error").await, "The atom is a different type than expected", [pos]) mk_err(i.i("Type error").await, "The atom is a different type than expected", [pos])
} }
impl TryFromExpr for ForeignAtom { impl TryFromExpr for ForeignAtom {

View File

@@ -49,7 +49,7 @@ impl ExtensionData {
} }
pub enum MemberRecord { pub enum MemberRecord {
Gen(Vec<Tok<String>>, LazyMemberFactory), Gen(Vec<IStr>, LazyMemberFactory),
Res, Res,
} }

View File

@@ -0,0 +1,7 @@
use orchid_base::interner::{ApiStrTok, ApiStrvTok, IStr};
pub struct ExtIStr(ApiStrTok, Rc<String>);
impl Deref for ExtIStr {}
pub struct ExtIStrv(ApiStrvTok, Rc<Vec<IStr>>);
pub struct ExtInterner {}

View File

@@ -4,7 +4,7 @@ use std::ops::RangeInclusive;
use futures::FutureExt; use futures::FutureExt;
use futures::future::LocalBoxFuture; use futures::future::LocalBoxFuture;
use orchid_base::error::{OrcErrv, OrcRes, Reporter, mk_errv}; use orchid_base::error::{OrcErrv, OrcRes, Reporter, mk_err};
use orchid_base::interner::{Interner, Tok}; use orchid_base::interner::{Interner, Tok};
use orchid_base::location::{Pos, SrcRange}; use orchid_base::location::{Pos, SrcRange};
use orchid_base::name::Sym; use orchid_base::name::Sym;
@@ -17,27 +17,27 @@ use crate::parser::PTokTree;
use crate::system::SysCtx; use crate::system::SysCtx;
use crate::tree::GenTokTree; use crate::tree::GenTokTree;
pub async fn ekey_cascade(i: &Interner) -> Tok<String> { pub async fn ekey_cascade(i: &Interner) -> IStr {
i.i("An error cascading from a recursive call").await i.i("An error cascading from a recursive call").await
} }
pub async fn ekey_not_applicable(i: &Interner) -> Tok<String> { pub async fn ekey_not_applicable(i: &Interner) -> IStr {
i.i("Pseudo-error to communicate that the current branch in a dispatch doesn't apply").await i.i("Pseudo-error to communicate that the current branch in a dispatch doesn't apply").await
} }
const MSG_INTERNAL_ERROR: &str = "This error is a sentinel for the extension library.\ const MSG_INTERNAL_ERROR: &str = "This error is a sentinel for the extension library.\
it should not be emitted by the extension."; it should not be emitted by the extension.";
pub async fn err_cascade(i: &Interner) -> OrcErrv { pub async fn err_cascade(i: &Interner) -> OrcErrv {
mk_errv(ekey_cascade(i).await, MSG_INTERNAL_ERROR, [Pos::None]) mk_err(ekey_cascade(i).await, MSG_INTERNAL_ERROR, [Pos::None])
} }
pub async fn err_not_applicable(i: &Interner) -> OrcErrv { pub async fn err_not_applicable(i: &Interner) -> OrcErrv {
mk_errv(ekey_not_applicable(i).await, MSG_INTERNAL_ERROR, [Pos::None]) mk_err(ekey_not_applicable(i).await, MSG_INTERNAL_ERROR, [Pos::None])
} }
pub struct LexContext<'a> { pub struct LexContext<'a> {
pub(crate) exprs: &'a BorrowedExprStore, pub(crate) exprs: &'a BorrowedExprStore,
pub ctx: SysCtx, pub ctx: SysCtx,
pub text: &'a Tok<String>, pub text: &'a IStr,
pub id: api::ParsId, pub id: api::ParsId,
pub pos: u32, pub pos: u32,
pub(crate) src: Sym, pub(crate) src: Sym,

View File

@@ -11,6 +11,7 @@ pub mod func_atom;
pub mod gen_expr; pub mod gen_expr;
pub mod lexer; pub mod lexer;
// pub mod msg; // pub mod msg;
mod interner;
pub mod other_system; pub mod other_system;
pub mod parser; pub mod parser;
pub mod reflection; pub mod reflection;

View File

@@ -115,7 +115,7 @@ impl ParsedLine {
sr: &SrcRange, sr: &SrcRange,
comments: impl IntoIterator<Item = &'a Comment>, comments: impl IntoIterator<Item = &'a Comment>,
exported: bool, exported: bool,
name: Tok<String>, name: IStr,
f: F, f: F,
) -> Self { ) -> Self {
let cb = Box::new(|ctx| async move { f(ctx).await.to_expr().await }.boxed_local()); let cb = Box::new(|ctx| async move { f(ctx).await.to_expr().await }.boxed_local());
@@ -127,7 +127,7 @@ impl ParsedLine {
sr: &SrcRange, sr: &SrcRange,
comments: impl IntoIterator<Item = &'a Comment>, comments: impl IntoIterator<Item = &'a Comment>,
exported: bool, exported: bool,
name: &Tok<String>, name: &IStr,
use_prelude: bool, use_prelude: bool,
lines: impl IntoIterator<Item = ParsedLine>, lines: impl IntoIterator<Item = ParsedLine>,
) -> Self { ) -> Self {
@@ -175,7 +175,7 @@ pub enum ParsedLineKind {
} }
pub struct ParsedMem { pub struct ParsedMem {
pub name: Tok<String>, pub name: IStr,
pub exported: bool, pub exported: bool,
pub kind: ParsedMemKind, pub kind: ParsedMemKind,
} }

View File

@@ -32,7 +32,7 @@ pub struct ReflModData {
inferred: Mutex<bool>, inferred: Mutex<bool>,
path: VPath, path: VPath,
ctx: WeakSysCtx, ctx: WeakSysCtx,
members: MemoMap<Tok<String>, ReflMem>, members: MemoMap<IStr, ReflMem>,
} }
#[derive(Clone)] #[derive(Clone)]
@@ -41,7 +41,7 @@ impl ReflMod {
fn ctx(&self) -> SysCtx { fn ctx(&self) -> SysCtx {
self.0.ctx.upgrade().expect("ReflectedModule accessed after context drop") self.0.ctx.upgrade().expect("ReflectedModule accessed after context drop")
} }
pub fn path(&self) -> &[Tok<String>] { &self.0.path[..] } pub fn path(&self) -> &[IStr] { &self.0.path[..] }
pub fn is_root(&self) -> bool { self.0.path.is_empty() } pub fn is_root(&self) -> bool { self.0.path.is_empty() }
async fn try_populate(&self) -> Result<(), api::LsModuleError> { async fn try_populate(&self) -> Result<(), api::LsModuleError> {
let ctx = self.ctx(); let ctx = self.ctx();
@@ -70,7 +70,7 @@ impl ReflMod {
} }
Ok(()) Ok(())
} }
pub async fn get_child(&self, key: &Tok<String>) -> Option<ReflMem> { pub async fn get_child(&self, key: &IStr) -> Option<ReflMem> {
let inferred_g = self.0.inferred.lock().await; let inferred_g = self.0.inferred.lock().await;
if let Some(mem) = self.0.members.get(key) { if let Some(mem) = self.0.members.get(key) {
return Some(mem.clone()); return Some(mem.clone());
@@ -88,7 +88,7 @@ impl ReflMod {
} }
self.0.members.get(key).cloned() self.0.members.get(key).cloned()
} }
pub async fn get_by_path(&self, path: &[Tok<String>]) -> Result<ReflMem, InvalidPathError> { pub async fn get_by_path(&self, path: &[IStr]) -> Result<ReflMem, InvalidPathError> {
let ctx = self.ctx(); let ctx = self.ctx();
let (next, tail) = path.split_first().expect("Attempted to walk by empty path"); let (next, tail) = path.split_first().expect("Attempted to walk by empty path");
let inferred_g = self.0.inferred.lock().await; let inferred_g = self.0.inferred.lock().await;

View File

@@ -206,21 +206,21 @@ impl MemKind {
pub trait TreeIntoApiCtx { pub trait TreeIntoApiCtx {
fn sys(&self) -> SysCtx; fn sys(&self) -> SysCtx;
fn with_lazy(&mut self, fac: LazyMemberFactory) -> api::TreeId; fn with_lazy(&mut self, fac: LazyMemberFactory) -> api::TreeId;
fn push_path(&mut self, seg: Tok<String>) -> impl TreeIntoApiCtx; fn push_path(&mut self, seg: IStr) -> impl TreeIntoApiCtx;
fn req(&self) -> &impl ReqHandlish; fn req(&self) -> &impl ReqHandlish;
} }
pub struct TreeIntoApiCtxImpl<'a, 'b, RH: ReqHandlish> { pub struct TreeIntoApiCtxImpl<'a, 'b, RH: ReqHandlish> {
pub sys: SysCtx, pub sys: SysCtx,
pub basepath: &'a [Tok<String>], pub basepath: &'a [IStr],
pub path: Substack<'a, Tok<String>>, pub path: Substack<'a, IStr>,
pub lazy_members: &'b mut HashMap<api::TreeId, MemberRecord>, pub lazy_members: &'b mut HashMap<api::TreeId, MemberRecord>,
pub req: &'a RH, pub req: &'a RH,
} }
impl<RH: ReqHandlish> TreeIntoApiCtx for TreeIntoApiCtxImpl<'_, '_, RH> { impl<RH: ReqHandlish> TreeIntoApiCtx for TreeIntoApiCtxImpl<'_, '_, RH> {
fn sys(&self) -> SysCtx { self.sys.clone() } fn sys(&self) -> SysCtx { self.sys.clone() }
fn push_path(&mut self, seg: Tok<String>) -> impl TreeIntoApiCtx { fn push_path(&mut self, seg: IStr) -> impl TreeIntoApiCtx {
TreeIntoApiCtxImpl { TreeIntoApiCtxImpl {
req: self.req, req: self.req,
lazy_members: self.lazy_members, lazy_members: self.lazy_members,

View File

@@ -73,7 +73,7 @@ impl AtomHand {
pub fn sys(&self) -> &System { &self.0.owner } pub fn sys(&self) -> &System { &self.0.owner }
#[must_use] #[must_use]
pub fn ext(&self) -> &Extension { self.sys().ext() } pub fn ext(&self) -> &Extension { self.sys().ext() }
pub async fn req(&self, key: api::TStrv, req: Vec<u8>) -> Option<Vec<u8>> { pub async fn req(&self, key: api::TVec, req: Vec<u8>) -> Option<Vec<u8>> {
self.0.owner.reqnot().request(api::Fwded(self.0.api_ref(), key, req)).await self.0.owner.reqnot().request(api::Fwded(self.0.api_ref(), key, req)).await
} }
#[must_use] #[must_use]

View File

@@ -1,6 +1,6 @@
use hashbrown::HashSet; use hashbrown::HashSet;
use itertools::Itertools; use itertools::Itertools;
use orchid_base::error::{OrcErrv, OrcRes, Reporter, mk_errv}; use orchid_base::error::{OrcErrv, OrcRes, Reporter, mk_err};
use orchid_base::interner::{Interner, Tok}; use orchid_base::interner::{Interner, Tok};
use orchid_base::location::Pos; use orchid_base::location::Pos;
use orchid_base::name::VName; use orchid_base::name::VName;
@@ -30,7 +30,7 @@ impl AbsPathError {
format!("{path} is leading outside the root."), format!("{path} is leading outside the root."),
), ),
}; };
mk_errv(descr, msg, [pos]) mk_err(descr, msg, [pos])
} }
} }
@@ -42,8 +42,8 @@ impl AbsPathError {
/// if the relative path contains as many or more `super` segments than the /// if the relative path contains as many or more `super` segments than the
/// length of the absolute path. /// length of the absolute path.
pub async fn absolute_path( pub async fn absolute_path(
mut cwd: &[Tok<String>], mut cwd: &[IStr],
mut rel: &[Tok<String>], mut rel: &[IStr],
i: &Interner, i: &Interner,
) -> Result<VName, AbsPathError> { ) -> Result<VName, AbsPathError> {
let i_self = i.i("self").await; let i_self = i.i("self").await;
@@ -67,13 +67,13 @@ pub struct DealiasCtx<'a> {
} }
pub async fn resolv_glob<Mod: Tree>( pub async fn resolv_glob<Mod: Tree>(
cwd: &[Tok<String>], cwd: &[IStr],
root: &Mod, root: &Mod,
abs_path: &[Tok<String>], abs_path: &[IStr],
pos: Pos, pos: Pos,
i: &Interner, i: &Interner,
ctx: &mut Mod::Ctx<'_>, ctx: &mut Mod::Ctx<'_>,
) -> OrcRes<HashSet<Tok<String>>> { ) -> OrcRes<HashSet<IStr>> {
let coprefix_len = cwd.iter().zip(abs_path).take_while(|(a, b)| a == b).count(); let coprefix_len = cwd.iter().zip(abs_path).take_while(|(a, b)| a == b).count();
let (co_prefix, diff_path) = abs_path.split_at(abs_path.len().min(coprefix_len + 1)); let (co_prefix, diff_path) = abs_path.split_at(abs_path.len().min(coprefix_len + 1));
let fst_diff = let fst_diff =
@@ -87,7 +87,7 @@ pub async fn resolv_glob<Mod: Tree>(
ChildErrorKind::Missing => ("Invalid import path", format!("{path} not found")), ChildErrorKind::Missing => ("Invalid import path", format!("{path} not found")),
ChildErrorKind::Private => ("Import inaccessible", format!("{path} is private")), ChildErrorKind::Private => ("Import inaccessible", format!("{path} is private")),
}; };
return Err(mk_errv(i.i(tk).await, msg, [pos])); return Err(mk_err(i.i(tk).await, msg, [pos]));
}, },
}; };
Ok(target_module.children(coprefix_len < abs_path.len())) Ok(target_module.children(coprefix_len < abs_path.len()))
@@ -98,11 +98,11 @@ pub type ChildResult<'a, T> = Result<&'a T, ChildErrorKind>;
pub trait Tree { pub trait Tree {
type Ctx<'a>; type Ctx<'a>;
#[must_use] #[must_use]
fn children(&self, public_only: bool) -> HashSet<Tok<String>>; fn children(&self, public_only: bool) -> HashSet<IStr>;
#[must_use] #[must_use]
fn child( fn child(
&self, &self,
key: Tok<String>, key: IStr,
public_only: bool, public_only: bool,
ctx: &mut Self::Ctx<'_>, ctx: &mut Self::Ctx<'_>,
) -> impl Future<Output = ChildResult<'_, Self>>; ) -> impl Future<Output = ChildResult<'_, Self>>;
@@ -133,7 +133,7 @@ pub struct ChildError {
pub async fn walk<'a, T: Tree>( pub async fn walk<'a, T: Tree>(
root: &'a T, root: &'a T,
public_only: bool, public_only: bool,
path: impl IntoIterator<Item = Tok<String>>, path: impl IntoIterator<Item = IStr>,
ctx: &mut T::Ctx<'_>, ctx: &mut T::Ctx<'_>,
) -> Result<&'a T, ChildError> { ) -> Result<&'a T, ChildError> {
let mut cur = root; let mut cur = root;

View File

@@ -260,7 +260,7 @@ impl Extension {
} }
pub(crate) async fn lex_req<F: Future<Output = Option<api::SubLexed>>>( pub(crate) async fn lex_req<F: Future<Output = Option<api::SubLexed>>>(
&self, &self,
source: Tok<String>, source: IStr,
src: Sym, src: Sym,
pos: u32, pos: u32,
sys: api::SysId, sys: api::SysId,

View File

@@ -2,7 +2,7 @@ use std::rc::Rc;
use futures::FutureExt; use futures::FutureExt;
use futures::lock::Mutex; use futures::lock::Mutex;
use orchid_base::error::{OrcErrv, OrcRes, mk_errv}; use orchid_base::error::{OrcErrv, OrcRes, mk_err};
use orchid_base::interner::Tok; use orchid_base::interner::Tok;
use orchid_base::location::SrcRange; use orchid_base::location::SrcRange;
use orchid_base::name::Sym; use orchid_base::name::Sym;
@@ -17,7 +17,7 @@ use crate::system::System;
pub struct LexCtx<'a> { pub struct LexCtx<'a> {
pub systems: &'a [System], pub systems: &'a [System],
pub source: &'a Tok<String>, pub source: &'a IStr,
pub path: &'a Sym, pub path: &'a Sym,
pub tail: &'a str, pub tail: &'a str,
pub sub_trees: &'a mut Vec<Expr>, pub sub_trees: &'a mut Vec<Expr>,
@@ -105,7 +105,7 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
ParsTok::NS(ctx.ctx.i.i(name).await, Box::new(body)) ParsTok::NS(ctx.ctx.i.i(name).await, Box::new(body))
} else if ctx.strip_prefix("--[") { } else if ctx.strip_prefix("--[") {
let Some((cmt, tail)) = ctx.tail.split_once("]--") else { let Some((cmt, tail)) = ctx.tail.split_once("]--") else {
return Err(mk_errv( return Err(mk_err(
ctx.ctx.i.i("Unterminated block comment").await, ctx.ctx.i.i("Unterminated block comment").await,
"This block comment has no ending ]--", "This block comment has no ending ]--",
[SrcRange::new(start..start + 3, ctx.path)], [SrcRange::new(start..start + 3, ctx.path)],
@@ -128,7 +128,7 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
ctx.trim_ws(); ctx.trim_ws();
while !ctx.strip_char(*rp) { while !ctx.strip_char(*rp) {
if ctx.tail.is_empty() { if ctx.tail.is_empty() {
return Err(mk_errv( return Err(mk_err(
ctx.ctx.i.i("unclosed paren").await, ctx.ctx.i.i("unclosed paren").await,
format!("this {lp} has no matching {rp}"), format!("this {lp} has no matching {rp}"),
[SrcRange::new(start..start + 1, ctx.path)], [SrcRange::new(start..start + 1, ctx.path)],
@@ -178,7 +178,7 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
} else if ctx.tail.starts_with(op_char) { } else if ctx.tail.starts_with(op_char) {
ParsTok::Name(ctx.ctx.i.i(ctx.get_start_matches(op_char)).await) ParsTok::Name(ctx.ctx.i.i(ctx.get_start_matches(op_char)).await)
} else { } else {
return Err(mk_errv( return Err(mk_err(
ctx.ctx.i.i("Unrecognized character").await, ctx.ctx.i.i("Unrecognized character").await,
"The following syntax is meaningless.", "The following syntax is meaningless.",
[SrcRange::new(start..start + 1, ctx.path)], [SrcRange::new(start..start + 1, ctx.path)],
@@ -188,12 +188,7 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
Ok(ParsTokTree { tok, sr: SrcRange::new(start..ctx.get_pos(), ctx.path) }) Ok(ParsTokTree { tok, sr: SrcRange::new(start..ctx.get_pos(), ctx.path) })
} }
pub async fn lex( pub async fn lex(text: IStr, path: Sym, systems: &[System], ctx: &Ctx) -> OrcRes<Vec<ParsTokTree>> {
text: Tok<String>,
path: Sym,
systems: &[System],
ctx: &Ctx,
) -> OrcRes<Vec<ParsTokTree>> {
let mut sub_trees = Vec::new(); let mut sub_trees = Vec::new();
let mut ctx = let mut ctx =
LexCtx { source: &text, sub_trees: &mut sub_trees, tail: &text[..], systems, path: &path, ctx }; LexCtx { source: &text, sub_trees: &mut sub_trees, tail: &text[..], systems, path: &path, ctx };

View File

@@ -1,6 +1,6 @@
use futures::future::join_all; use futures::future::join_all;
use itertools::Itertools; use itertools::Itertools;
use orchid_base::error::{OrcRes, Reporter, mk_errv}; use orchid_base::error::{OrcRes, Reporter, mk_err};
use orchid_base::format::fmt; use orchid_base::format::fmt;
use orchid_base::interner::{Interner, Tok}; use orchid_base::interner::{Interner, Tok};
use orchid_base::name::Sym; use orchid_base::name::Sym;
@@ -47,7 +47,7 @@ pub trait HostParseCtx: ParseCtx {
pub async fn parse_items( pub async fn parse_items(
ctx: &impl HostParseCtx, ctx: &impl HostParseCtx,
path: Substack<'_, Tok<String>>, path: Substack<'_, IStr>,
items: ParsSnippet<'_>, items: ParsSnippet<'_>,
) -> OrcRes<Vec<Item>> { ) -> OrcRes<Vec<Item>> {
let lines = line_items(ctx, items).await; let lines = line_items(ctx, items).await;
@@ -58,7 +58,7 @@ pub async fn parse_items(
pub async fn parse_item( pub async fn parse_item(
ctx: &impl HostParseCtx, ctx: &impl HostParseCtx,
path: Substack<'_, Tok<String>>, path: Substack<'_, IStr>,
comments: Vec<Comment>, comments: Vec<Comment>,
item: ParsSnippet<'_>, item: ParsSnippet<'_>,
) -> OrcRes<Vec<Item>> { ) -> OrcRes<Vec<Item>> {
@@ -67,7 +67,7 @@ pub async fn parse_item(
n if *n == ctx.i().i("export").await => match try_pop_no_fluff(ctx, postdisc).await? { n if *n == ctx.i().i("export").await => match try_pop_no_fluff(ctx, postdisc).await? {
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } => Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } =>
parse_exportable_item(ctx, path, comments, true, n.clone(), tail).await, parse_exportable_item(ctx, path, comments, true, n.clone(), tail).await,
Parsed { output, tail: _ } => Err(mk_errv( Parsed { output, tail: _ } => Err(mk_err(
ctx.i().i("Malformed export").await, ctx.i().i("Malformed export").await,
"`export` can either prefix other lines or list names inside ( )", "`export` can either prefix other lines or list names inside ( )",
[output.sr()], [output.sr()],
@@ -83,11 +83,10 @@ pub async fn parse_item(
}, },
n => parse_exportable_item(ctx, path, comments, false, n.clone(), postdisc).await, n => parse_exportable_item(ctx, path, comments, false, n.clone(), postdisc).await,
}, },
Some(_) => Err(mk_errv( Some(_) =>
ctx.i().i("Expected a line type").await, Err(mk_err(ctx.i().i("Expected a line type").await, "All lines must begin with a keyword", [
"All lines must begin with a keyword", item.sr(),
[item.sr()], ])),
)),
None => unreachable!("These lines are filtered and aggregated in earlier stages"), None => unreachable!("These lines are filtered and aggregated in earlier stages"),
} }
} }
@@ -103,10 +102,10 @@ pub async fn parse_import<'a>(
pub async fn parse_exportable_item<'a>( pub async fn parse_exportable_item<'a>(
ctx: &impl HostParseCtx, ctx: &impl HostParseCtx,
path: Substack<'_, Tok<String>>, path: Substack<'_, IStr>,
comments: Vec<Comment>, comments: Vec<Comment>,
exported: bool, exported: bool,
discr: Tok<String>, discr: IStr,
tail: ParsSnippet<'a>, tail: ParsSnippet<'a>,
) -> OrcRes<Vec<Item>> { ) -> OrcRes<Vec<Item>> {
let kind = if discr == ctx.i().i("mod").await { let kind = if discr == ctx.i().i("mod").await {
@@ -121,7 +120,7 @@ pub async fn parse_exportable_item<'a>(
.await; .await;
} else { } else {
let ext_lines = ctx.systems().flat_map(System::line_types).join(", "); let ext_lines = ctx.systems().flat_map(System::line_types).join(", ");
return Err(mk_errv( return Err(mk_err(
ctx.i().i("Unrecognized line type").await, ctx.i().i("Unrecognized line type").await,
format!("Line types are: mod, {ext_lines}"), format!("Line types are: mod, {ext_lines}"),
[tail.prev().sr()], [tail.prev().sr()],
@@ -132,13 +131,13 @@ pub async fn parse_exportable_item<'a>(
pub async fn parse_module<'a>( pub async fn parse_module<'a>(
ctx: &impl HostParseCtx, ctx: &impl HostParseCtx,
path: Substack<'_, Tok<String>>, path: Substack<'_, IStr>,
tail: ParsSnippet<'a>, tail: ParsSnippet<'a>,
) -> OrcRes<(Tok<String>, ParsedModule)> { ) -> OrcRes<(IStr, ParsedModule)> {
let (name, tail) = match try_pop_no_fluff(ctx, tail).await? { let (name, tail) = match try_pop_no_fluff(ctx, tail).await? {
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } => (n.clone(), tail), Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } => (n.clone(), tail),
Parsed { output, .. } => { Parsed { output, .. } => {
return Err(mk_errv( return Err(mk_err(
ctx.i().i("Missing module name").await, ctx.i().i("Missing module name").await,
format!("A name was expected, {} was found", fmt(output, ctx.i()).await), format!("A name was expected, {} was found", fmt(output, ctx.i()).await),
[output.sr()], [output.sr()],
@@ -148,7 +147,7 @@ pub async fn parse_module<'a>(
let Parsed { output, tail: surplus } = try_pop_no_fluff(ctx, tail).await?; let Parsed { output, tail: surplus } = try_pop_no_fluff(ctx, tail).await?;
expect_end(ctx, surplus).await?; expect_end(ctx, surplus).await?;
let Some(body) = output.as_s(Paren::Round) else { let Some(body) = output.as_s(Paren::Round) else {
return Err(mk_errv( return Err(mk_err(
ctx.i().i("Expected module body").await, ctx.i().i("Expected module body").await,
format!("A ( block ) was expected, {} was found", fmt(output, ctx.i()).await), format!("A ( block ) was expected, {} was found", fmt(output, ctx.i()).await),
[output.sr()], [output.sr()],

View File

@@ -69,14 +69,14 @@ impl Format for Item {
} }
pub struct ParsedMember { pub struct ParsedMember {
pub name: Tok<String>, pub name: IStr,
pub exported: bool, pub exported: bool,
pub kind: ParsedMemberKind, pub kind: ParsedMemberKind,
} }
impl ParsedMember { impl ParsedMember {
#[must_use] #[must_use]
pub fn name(&self) -> Tok<String> { self.name.clone() } pub fn name(&self) -> IStr { self.name.clone() }
pub fn new(exported: bool, name: Tok<String>, kind: impl Into<ParsedMemberKind>) -> Self { pub fn new(exported: bool, name: IStr, kind: impl Into<ParsedMemberKind>) -> Self {
Self { exported, name, kind: kind.into() } Self { exported, name, kind: kind.into() }
} }
} }
@@ -90,14 +90,14 @@ impl Debug for ParsedMember {
} }
pub(crate) type ParsedExprCallback = pub(crate) type ParsedExprCallback =
Rc<dyn for<'a> Fn(&'a [Tok<String>]) -> LocalBoxFuture<'a, Expr>>; Rc<dyn for<'a> Fn(&'a [IStr]) -> LocalBoxFuture<'a, Expr>>;
pub struct ParsedExpr { pub struct ParsedExpr {
pub(crate) debug: String, pub(crate) debug: String,
pub(crate) callback: ParsedExprCallback, pub(crate) callback: ParsedExprCallback,
} }
impl ParsedExpr { impl ParsedExpr {
pub async fn run(self, imported_names: &[Tok<String>]) -> Expr { pub async fn run(self, imported_names: &[IStr]) -> Expr {
(self.callback)(imported_names).await (self.callback)(imported_names).await
} }
} }
@@ -116,7 +116,7 @@ impl From<ParsedModule> for ParsedMemberKind {
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct ParsedModule { pub struct ParsedModule {
pub exports: Vec<Tok<String>>, pub exports: Vec<IStr>,
pub items: Vec<Item>, pub items: Vec<Item>,
pub use_prelude: bool, pub use_prelude: bool,
} }
@@ -142,7 +142,7 @@ impl ParsedModule {
(self.items.iter()) (self.items.iter())
.filter_map(|it| if let ItemKind::Import(i) = &it.kind { Some(i) } else { None }) .filter_map(|it| if let ItemKind::Import(i) = &it.kind { Some(i) } else { None })
} }
pub fn default_item(self, name: Tok<String>, sr: SrcRange) -> Item { pub fn default_item(self, name: IStr, sr: SrcRange) -> Item {
let mem = ParsedMember { exported: true, name, kind: ParsedMemberKind::Mod(self) }; let mem = ParsedMember { exported: true, name, kind: ParsedMemberKind::Mod(self) };
Item { comments: vec![], sr, kind: ItemKind::Member(mem) } Item { comments: vec![], sr, kind: ItemKind::Member(mem) }
} }
@@ -151,7 +151,7 @@ impl Tree for ParsedModule {
type Ctx<'a> = (); type Ctx<'a> = ();
async fn child( async fn child(
&self, &self,
key: Tok<String>, key: IStr,
public_only: bool, public_only: bool,
(): &mut Self::Ctx<'_>, (): &mut Self::Ctx<'_>,
) -> ChildResult<'_, Self> { ) -> ChildResult<'_, Self> {
@@ -169,7 +169,7 @@ impl Tree for ParsedModule {
} }
ChildResult::Err(ChildErrorKind::Missing) ChildResult::Err(ChildErrorKind::Missing)
} }
fn children(&self, public_only: bool) -> HashSet<Tok<String>> { fn children(&self, public_only: bool) -> HashSet<IStr> {
let mut public: HashSet<_> = self.exports.iter().cloned().collect(); let mut public: HashSet<_> = self.exports.iter().cloned().collect();
if !public_only { if !public_only {
public.extend( public.extend(
@@ -198,11 +198,11 @@ impl Format for ParsedModule {
/// point to a module and rule_loc selects a macro rule within that module /// point to a module and rule_loc selects a macro rule within that module
#[derive(Clone, Debug, Hash, PartialEq, Eq)] #[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct ConstPath { pub struct ConstPath {
steps: Tok<Vec<Tok<String>>>, steps: Tok<Vec<IStr>>,
} }
impl ConstPath { impl ConstPath {
#[must_use] #[must_use]
pub fn to_const(steps: Tok<Vec<Tok<String>>>) -> Self { Self { steps } } pub fn to_const(steps: Tok<Vec<IStr>>) -> Self { Self { steps } }
} }
pub async fn tt_to_api(exprs: &mut ExprStore, subtree: ParsTokTree) -> api::TokenTree { pub async fn tt_to_api(exprs: &mut ExprStore, subtree: ParsTokTree) -> api::TokenTree {

View File

@@ -23,7 +23,7 @@ pub struct Parser {
pub(crate) system: System, pub(crate) system: System,
pub(crate) idx: u16, pub(crate) idx: u16,
} }
type ModPath<'a> = Substack<'a, Tok<String>>; type ModPath<'a> = Substack<'a, IStr>;
impl Parser { impl Parser {
pub async fn parse( pub async fn parse(
@@ -80,8 +80,8 @@ struct ConvCtx<'a> {
} }
async fn conv( async fn conv(
parsed_v: Vec<api::ParsedLine>, parsed_v: Vec<api::ParsedLine>,
module: Substack<'_, Tok<String>>, module: Substack<'_, IStr>,
callback: &'_ mut impl AsyncFnMut(Substack<'_, Tok<String>>, Vec<ParsTokTree>) -> OrcRes<Vec<Item>>, callback: &'_ mut impl AsyncFnMut(Substack<'_, IStr>, Vec<ParsTokTree>) -> OrcRes<Vec<Item>>,
ctx: &mut ConvCtx<'_>, ctx: &mut ConvCtx<'_>,
) -> OrcRes<Vec<Item>> { ) -> OrcRes<Vec<Item>> {
let mut items = Vec::new(); let mut items = Vec::new();

View File

@@ -10,7 +10,7 @@ use hashbrown::HashMap;
use itertools::Itertools; use itertools::Itertools;
use memo_map::MemoMap; use memo_map::MemoMap;
use orchid_base::char_filter::char_filter_match; use orchid_base::char_filter::char_filter_match;
use orchid_base::error::{OrcRes, mk_errv_floating}; use orchid_base::error::{OrcRes, mk_err_floating};
use orchid_base::format::{FmtCtx, FmtUnit, Format}; use orchid_base::format::{FmtCtx, FmtUnit, Format};
use orchid_base::interner::{Interner, Tok}; use orchid_base::interner::{Interner, Tok};
use orchid_base::iter_utils::IteratorPrint; use orchid_base::iter_utils::IteratorPrint;
@@ -35,7 +35,7 @@ pub(crate) struct SystemInstData {
decl_id: api::SysDeclId, decl_id: api::SysDeclId,
lex_filter: api::CharFilter, lex_filter: api::CharFilter,
id: api::SysId, id: api::SysId,
line_types: Vec<Tok<String>>, line_types: Vec<IStr>,
prelude: Vec<Sym>, prelude: Vec<Sym>,
owned_atoms: RwLock<HashMap<api::AtomId, WeakAtomHand>>, owned_atoms: RwLock<HashMap<api::AtomId, WeakAtomHand>>,
pub(crate) const_paths: MemoMap<api::ParsedConstId, Sym>, pub(crate) const_paths: MemoMap<api::ParsedConstId, Sym>,
@@ -88,7 +88,7 @@ impl System {
/// [Self::can_lex] was called and returned true. /// [Self::can_lex] was called and returned true.
pub async fn lex<F: Future<Output = Option<api::SubLexed>>>( pub async fn lex<F: Future<Output = Option<api::SubLexed>>>(
&self, &self,
source: Tok<String>, source: IStr,
src: Sym, src: Sym,
pos: u32, pos: u32,
r: impl FnMut(u32) -> F, r: impl FnMut(u32) -> F,
@@ -96,12 +96,12 @@ impl System {
self.0.ext.lex_req(source, src, pos, self.id(), r).await self.0.ext.lex_req(source, src, pos, self.id(), r).await
} }
#[must_use] #[must_use]
pub fn get_parser(&self, ltyp: Tok<String>) -> Option<Parser> { pub fn get_parser(&self, ltyp: IStr) -> Option<Parser> {
(self.0.line_types.iter().enumerate()) (self.0.line_types.iter().enumerate())
.find(|(_, txt)| *txt == &ltyp) .find(|(_, txt)| *txt == &ltyp)
.map(|(idx, _)| Parser { idx: idx as u16, system: self.clone() }) .map(|(idx, _)| Parser { idx: idx as u16, system: self.clone() })
} }
pub fn line_types(&self) -> impl Iterator<Item = &Tok<String>> + '_ { self.0.line_types.iter() } pub fn line_types(&self) -> impl Iterator<Item = &IStr> + '_ { self.0.line_types.iter() }
#[must_use] #[must_use]
pub async fn request(&self, req: Vec<u8>) -> Vec<u8> { pub async fn request(&self, req: Vec<u8>) -> Vec<u8> {
@@ -130,7 +130,7 @@ impl System {
pub(crate) async fn name_resolver( pub(crate) async fn name_resolver(
&self, &self,
orig: api::ParsedConstId, orig: api::ParsedConstId,
) -> impl AsyncFnMut(&[Tok<String>]) -> OrcRes<VName> + use<> { ) -> impl AsyncFnMut(&[IStr]) -> OrcRes<VName> + use<> {
let root = self.0.ctx.root.read().await.upgrade().expect("find_names when root not in context"); let root = self.0.ctx.root.read().await.upgrade().expect("find_names when root not in context");
let orig = self.0.const_paths.get(&orig).expect("origin for find_names invalid").clone(); let orig = self.0.const_paths.get(&orig).expect("origin for find_names invalid").clone();
let ctx = self.0.ctx.clone(); let ctx = self.0.ctx.clone();
@@ -147,7 +147,7 @@ impl System {
match cmod.imports.get(selector) { match cmod.imports.get(selector) {
Some(Ok(dest)) => return Ok(dest.target.to_vname().suffix(tail.iter().cloned())), Some(Ok(dest)) => return Ok(dest.target.to_vname().suffix(tail.iter().cloned())),
Some(Err(dests)) => Some(Err(dests)) =>
return Err(mk_errv_floating( return Err(mk_err_floating(
ctx.i.i("Ambiguous name").await, ctx.i.i("Ambiguous name").await,
format!( format!(
"{selector} could refer to {}", "{selector} could refer to {}",
@@ -159,7 +159,7 @@ impl System {
if tail.is_empty() { if tail.is_empty() {
return Ok(VPath::new(cwd.iter().cloned()).name_with_suffix(selector.clone())); return Ok(VPath::new(cwd.iter().cloned()).name_with_suffix(selector.clone()));
} }
Err(mk_errv_floating( Err(mk_err_floating(
ctx.i.i("Invalid name").await, ctx.i.i("Invalid name").await,
format!("{selector} doesn't refer to a module"), format!("{selector} doesn't refer to a module"),
)) ))

View File

@@ -12,7 +12,7 @@ use hashbrown::hash_map::Entry;
use itertools::Itertools; use itertools::Itertools;
use memo_map::MemoMap; use memo_map::MemoMap;
use orchid_base::clone; use orchid_base::clone;
use orchid_base::error::{OrcRes, Reporter, mk_errv}; use orchid_base::error::{OrcRes, Reporter, mk_err};
use orchid_base::interner::Tok; use orchid_base::interner::Tok;
use orchid_base::location::{CodeGenInfo, Pos}; use orchid_base::location::{CodeGenInfo, Pos};
use orchid_base::name::{NameLike, Sym, VPath}; use orchid_base::name::{NameLike, Sym, VPath};
@@ -109,7 +109,7 @@ impl Root {
return Ok(val.clone()); return Ok(val.clone());
} }
match module { match module {
Ok(_) => Err(mk_errv( Ok(_) => Err(mk_err(
ctx.i.i("module used as constant").await, ctx.i.i("module used as constant").await,
format!("{name} is a module, not a constant"), format!("{name} is a module, not a constant"),
[pos], [pos],
@@ -117,7 +117,7 @@ impl Root {
Err(e) => match e.kind { Err(e) => match e.kind {
ChildErrorKind::Private => panic!("public_only is false"), ChildErrorKind::Private => panic!("public_only is false"),
ChildErrorKind::Constant => panic!("Tree refers to constant not in table"), ChildErrorKind::Constant => panic!("Tree refers to constant not in table"),
ChildErrorKind::Missing => Err(mk_errv( ChildErrorKind::Missing => Err(mk_err(
ctx.i.i("Constant does not exist").await, ctx.i.i("Constant does not exist").await,
format!("{name} does not refer to a constant"), format!("{name} does not refer to a constant"),
[pos], [pos],
@@ -144,11 +144,11 @@ impl Default for WeakRoot {
pub struct TreeFromApiCtx<'a> { pub struct TreeFromApiCtx<'a> {
pub sys: &'a System, pub sys: &'a System,
pub consts: &'a MemoMap<Sym, Expr>, pub consts: &'a MemoMap<Sym, Expr>,
pub path: Tok<Vec<Tok<String>>>, pub path: Tok<Vec<IStr>>,
} }
impl<'a> TreeFromApiCtx<'a> { impl<'a> TreeFromApiCtx<'a> {
#[must_use] #[must_use]
pub async fn push<'c>(&'c self, name: Tok<String>) -> TreeFromApiCtx<'c> { pub async fn push<'c>(&'c self, name: IStr) -> TreeFromApiCtx<'c> {
let path = self.sys.ctx().i.i(&self.path.iter().cloned().chain([name]).collect_vec()).await; let path = self.sys.ctx().i.i(&self.path.iter().cloned().chain([name]).collect_vec()).await;
TreeFromApiCtx { path, consts: self.consts, sys: self.sys } TreeFromApiCtx { path, consts: self.consts, sys: self.sys }
} }
@@ -162,8 +162,8 @@ pub struct ResolvedImport {
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct Module { pub struct Module {
pub imports: HashMap<Tok<String>, Result<ResolvedImport, Vec<ResolvedImport>>>, pub imports: HashMap<IStr, Result<ResolvedImport, Vec<ResolvedImport>>>,
pub members: HashMap<Tok<String>, Rc<Member>>, pub members: HashMap<IStr, Rc<Member>>,
} }
impl Module { impl Module {
#[must_use] #[must_use]
@@ -268,7 +268,7 @@ impl Module {
} }
} else { } else {
for item in values { for item in values {
ctx.rep.report(mk_errv( ctx.rep.report(mk_err(
conflicting_imports_msg.clone(), conflicting_imports_msg.clone(),
format!("{key} is imported multiple times from different modules"), format!("{key} is imported multiple times from different modules"),
[item.sr.pos()], [item.sr.pos()],
@@ -298,7 +298,7 @@ impl Module {
let Ok(import) = value else { continue }; let Ok(import) = value else { continue };
if import.target.strip_prefix(&path[..]).is_some_and(|t| t.starts_with(slice::from_ref(key))) if import.target.strip_prefix(&path[..]).is_some_and(|t| t.starts_with(slice::from_ref(key)))
{ {
ctx.rep.report(mk_errv( ctx.rep.report(mk_err(
self_referential_msg.clone(), self_referential_msg.clone(),
format!("import {} points to itself or a path within itself", &import.target), format!("import {} points to itself or a path within itself", &import.target),
[import.pos.clone()], [import.pos.clone()],
@@ -396,7 +396,7 @@ impl Tree for Module {
type Ctx<'a> = (Ctx, &'a MemoMap<Sym, Expr>); type Ctx<'a> = (Ctx, &'a MemoMap<Sym, Expr>);
async fn child( async fn child(
&self, &self,
key: Tok<String>, key: IStr,
public_only: bool, public_only: bool,
(ctx, consts): &mut Self::Ctx<'_>, (ctx, consts): &mut Self::Ctx<'_>,
) -> crate::dealias::ChildResult<'_, Self> { ) -> crate::dealias::ChildResult<'_, Self> {
@@ -411,7 +411,7 @@ impl Tree for Module {
MemberKind::Const => Err(ChildErrorKind::Constant), MemberKind::Const => Err(ChildErrorKind::Constant),
} }
} }
fn children(&self, public_only: bool) -> hashbrown::HashSet<Tok<String>> { fn children(&self, public_only: bool) -> hashbrown::HashSet<IStr> {
self.members.iter().filter(|(_, v)| !public_only || v.public).map(|(k, _)| k.clone()).collect() self.members.iter().filter(|(_, v)| !public_only || v.public).map(|(k, _)| k.clone()).collect()
} }
} }

View File

@@ -39,7 +39,7 @@ impl Parser for LetLine {
let rep = Reporter::new(); let rep = Reporter::new();
let dealiased = dealias_mac_v(aliased, &ctx, &rep).await; let dealiased = dealias_mac_v(aliased, &ctx, &rep).await;
let macro_input = MacTok::S(Paren::Round, dealiased).at(sr.pos()); let macro_input = MacTok::S(Paren::Round, dealiased).at(sr.pos());
if let Some(e) = rep.errv() { if let Some(e) = rep.res() {
return Err(e); return Err(e);
} }
Ok(call([ Ok(call([

View File

@@ -40,7 +40,7 @@ pub fn gen_macro_lib() -> Vec<GenMember> {
fun(true, "lower", |tpl: TypAtom<MacTree>| async move { fun(true, "lower", |tpl: TypAtom<MacTree>| async move {
let ctx = LowerCtx { sys: tpl.untyped.ctx().clone(), rep: &Reporter::new() }; let ctx = LowerCtx { sys: tpl.untyped.ctx().clone(), rep: &Reporter::new() };
let res = own(tpl).await.lower(ctx, Substack::Bottom).await; let res = own(tpl).await.lower(ctx, Substack::Bottom).await;
if let Some(e) = Reporter::new().errv() { Err(e) } else { Ok(res) } if let Some(e) = Reporter::new().res() { Err(e) } else { Ok(res) }
}), }),
fun(true, "resolve_recur", |state: TypAtom<RecurState>, tpl: TypAtom<MacTree>| async move { fun(true, "resolve_recur", |state: TypAtom<RecurState>, tpl: TypAtom<MacTree>| async move {
exec("macros::resolve_recur", async move |mut h| { exec("macros::resolve_recur", async move |mut h| {

View File

@@ -7,7 +7,7 @@ use futures::{StreamExt, stream};
use hashbrown::{HashMap, HashSet}; use hashbrown::{HashMap, HashSet};
use itertools::Itertools; use itertools::Itertools;
use never::Never; use never::Never;
use orchid_base::error::{OrcRes, Reporter, mk_errv}; use orchid_base::error::{OrcRes, Reporter, mk_err};
use orchid_base::interner::Tok; use orchid_base::interner::Tok;
use orchid_base::location::Pos; use orchid_base::location::Pos;
use orchid_base::name::Sym; use orchid_base::name::Sym;
@@ -40,7 +40,7 @@ impl Parser for MacroLine {
line: PSnippet<'a>, line: PSnippet<'a>,
) -> OrcRes<Vec<ParsedLine>> { ) -> OrcRes<Vec<ParsedLine>> {
if exported { if exported {
return Err(mk_errv( return Err(mk_err(
ctx.i().i("macros are always exported").await, ctx.i().i("macros are always exported").await,
"The export keyword is forbidden here to avoid confusion\n\ "The export keyword is forbidden here to avoid confusion\n\
because macros are exported by default", because macros are exported by default",
@@ -94,7 +94,7 @@ impl Parser for MacroLine {
} }
} }
let Some(macro_name) = keywords.keys().next().cloned() else { let Some(macro_name) = keywords.keys().next().cloned() else {
return Err(mk_errv( return Err(mk_err(
ctx.i().i("macro with no keywords").await, ctx.i().i("macro with no keywords").await,
"Macros must define at least one macro of their own.", "Macros must define at least one macro of their own.",
[kw_line.tail.sr()], [kw_line.tail.sr()],
@@ -109,7 +109,7 @@ impl Parser for MacroLine {
let Parsed { tail, .. } = expect_tok(&ctx, line.tail, ctx.i().i("rule").await).await?; let Parsed { tail, .. } = expect_tok(&ctx, line.tail, ctx.i().i("rule").await).await?;
let arrow_token = ctx.i().i("=>").await; let arrow_token = ctx.i().i("=>").await;
let Some((pattern, body)) = tail.split_once(|tok| tok.is_kw(arrow_token.clone())) else { let Some((pattern, body)) = tail.split_once(|tok| tok.is_kw(arrow_token.clone())) else {
ctx.rep().report(mk_errv( ctx.rep().report(mk_err(
ctx.i().i("Missing => in rule").await, ctx.i().i("Missing => in rule").await,
"Rule lines are of the form `rule ...pattern => ...body`", "Rule lines are of the form `rule ...pattern => ...body`",
[line.tail.sr()], [line.tail.sr()],
@@ -137,7 +137,7 @@ impl Parser for MacroLine {
let rep = Reporter::new(); let rep = Reporter::new();
let body = dealias_mac_v(body_mactree, &ctx, &rep).await; let body = dealias_mac_v(body_mactree, &ctx, &rep).await;
let macro_input = MacTok::S(Paren::Round, body).at(body_sr.pos()); let macro_input = MacTok::S(Paren::Round, body).at(body_sr.pos());
if let Some(e) = rep.errv() { if let Some(e) = rep.res() {
return Err(e); return Err(e);
} }
Ok(call([ Ok(call([
@@ -199,7 +199,7 @@ pub struct MacroData {
pub module: Sym, pub module: Sym,
pub prio: Option<u64>, pub prio: Option<u64>,
pub rules: Vec<Rule>, pub rules: Vec<Rule>,
pub own_kws: Vec<Tok<String>>, pub own_kws: Vec<IStr>,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@@ -210,8 +210,8 @@ pub struct Rule {
pub pos: Pos, pub pos: Pos,
pub pattern: Matcher, pub pattern: Matcher,
pub glossary: HashSet<Sym>, pub glossary: HashSet<Sym>,
pub placeholders: Vec<Tok<String>>, pub placeholders: Vec<IStr>,
pub body_name: Tok<String>, pub body_name: IStr,
} }
#[derive(Debug)] #[derive(Debug)]
pub enum Matcher { pub enum Matcher {

View File

@@ -6,7 +6,7 @@ use futures::FutureExt;
use futures::future::join_all; use futures::future::join_all;
use hashbrown::HashSet; use hashbrown::HashSet;
use itertools::Itertools; use itertools::Itertools;
use orchid_base::error::{OrcErrv, Reporter, mk_errv}; use orchid_base::error::{OrcErrv, Reporter, mk_err};
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants, fmt}; use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants, fmt};
use orchid_base::interner::Tok; use orchid_base::interner::Tok;
use orchid_base::location::Pos; use orchid_base::location::Pos;
@@ -42,7 +42,7 @@ impl MacTree {
MacTok::Bottom(e) => bot(e.clone()), MacTok::Bottom(e) => bot(e.clone()),
MacTok::Lambda(arg, body) => { MacTok::Lambda(arg, body) => {
let MacTok::Name(name) = &*arg.tok else { let MacTok::Name(name) = &*arg.tok else {
let err = mk_errv( let err = mk_err(
ctx.sys.i().i("Syntax error after macros").await, ctx.sys.i().i("Syntax error after macros").await,
"This token ends up as a binding, consider replacing it with a name", "This token ends up as a binding, consider replacing it with a name",
[arg.pos()], [arg.pos()],
@@ -57,7 +57,7 @@ impl MacTree {
Some((i, _)) => arg((args.len() - i) as u64), Some((i, _)) => arg((args.len() - i) as u64),
}, },
MacTok::Ph(ph) => { MacTok::Ph(ph) => {
let err = mk_errv( let err = mk_err(
ctx.sys.i().i("Placeholder in value").await, ctx.sys.i().i("Placeholder in value").await,
format!("Placeholder {ph} is only supported in macro patterns"), format!("Placeholder {ph} is only supported in macro patterns"),
[self.pos()], [self.pos()],
@@ -67,7 +67,7 @@ impl MacTree {
}, },
MacTok::S(Paren::Round, body) => call(lower_v(body, ctx, args).await), MacTok::S(Paren::Round, body) => call(lower_v(body, ctx, args).await),
MacTok::S(..) => { MacTok::S(..) => {
let err = mk_errv( let err = mk_err(
ctx.sys.i().i("[] or {} after macros").await, ctx.sys.i().i("[] or {} after macros").await,
format!("{} didn't match any macro", fmt(self, ctx.sys.i()).await), format!("{} didn't match any macro", fmt(self, ctx.sys.i()).await),
[self.pos()], [self.pos()],
@@ -166,7 +166,7 @@ pub async fn mtreev_fmt<'b>(
#[derive(Clone, Debug, Hash, PartialEq, Eq)] #[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct Ph { pub struct Ph {
pub name: Tok<String>, pub name: IStr,
pub kind: PhKind, pub kind: PhKind,
} }
impl Display for Ph { impl Display for Ph {

View File

@@ -1,7 +1,7 @@
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
use futures::FutureExt; use futures::FutureExt;
use orchid_base::error::{OrcRes, mk_errv}; use orchid_base::error::{OrcRes, mk_err};
use orchid_base::parse::ParseCtx; use orchid_base::parse::ParseCtx;
use orchid_base::sym; use orchid_base::sym;
use orchid_base::tokens::PARENS; use orchid_base::tokens::PARENS;
@@ -55,7 +55,7 @@ impl Lexer for MacTreeLexer {
if let Some(tail3) = tail2.strip_prefix(*rp) { if let Some(tail3) = tail2.strip_prefix(*rp) {
break Ok((tail3, MacTok::S(*paren, items).at(ctx.pos_tt(tail, tail3).pos()))); break Ok((tail3, MacTok::S(*paren, items).at(ctx.pos_tt(tail, tail3).pos())));
} else if tail2.is_empty() { } else if tail2.is_empty() {
return Err(mk_errv( return Err(mk_err(
ctx.i().i("Unclosed block").await, ctx.i().i("Unclosed block").await,
format!("Expected closing {rp}"), format!("Expected closing {rp}"),
[ctx.pos_lt(1, tail)], [ctx.pos_lt(1, tail)],

View File

@@ -11,7 +11,7 @@ use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant};
#[derive(Clone, PartialEq, Eq, Hash)] #[derive(Clone, PartialEq, Eq, Hash)]
pub struct RulePath { pub struct RulePath {
pub module: Sym, pub module: Sym,
pub main_kw: Tok<String>, pub main_kw: IStr,
pub rule: u32, pub rule: u32,
} }
impl RulePath { impl RulePath {

View File

@@ -1,7 +1,7 @@
use futures::FutureExt; use futures::FutureExt;
use hashbrown::HashMap; use hashbrown::HashMap;
use itertools::Itertools; use itertools::Itertools;
use orchid_base::error::mk_errv; use orchid_base::error::mk_err;
use orchid_base::location::Pos; use orchid_base::location::Pos;
use orchid_base::name::Sym; use orchid_base::name::Sym;
use orchid_base::sym; use orchid_base::sym;
@@ -91,7 +91,7 @@ async fn mk_body_call(
let rule_path = let rule_path =
RulePath { module: mac.0.module.clone(), main_kw: mac.0.own_kws[0].clone(), rule: rule.index }; RulePath { module: mac.0.module.clone(), main_kw: mac.0.own_kws[0].clone(), rule: rule.index };
let Some(new_recur) = recur.push(rule_path.clone()) else { let Some(new_recur) = recur.push(rule_path.clone()) else {
return bot(mk_errv( return bot(mk_err(
ctx.i().i("Circular macro dependency").await, ctx.i().i("Circular macro dependency").await,
format!("The definition of {rule_path} is circular"), format!("The definition of {rule_path} is circular"),
[rule.pos.clone()], [rule.pos.clone()],

View File

@@ -1,7 +1,7 @@
use futures::FutureExt; use futures::FutureExt;
use futures::future::join_all; use futures::future::join_all;
use itertools::Itertools; use itertools::Itertools;
use orchid_base::error::{OrcRes, mk_errv}; use orchid_base::error::{OrcRes, mk_err};
use orchid_base::interner::{Interner, Tok}; use orchid_base::interner::{Interner, Tok};
use orchid_base::join_ok; use orchid_base::join_ok;
use orchid_base::side::Side; use orchid_base::side::Side;
@@ -11,7 +11,7 @@ use super::vec_attrs::vec_attrs;
use crate::macros::mactree::{Ph, PhKind}; use crate::macros::mactree::{Ph, PhKind};
use crate::macros::{MacTok, MacTree}; use crate::macros::{MacTok, MacTree};
pub type MaxVecSplit<'a> = (&'a [MacTree], (Tok<String>, u8, bool), &'a [MacTree]); pub type MaxVecSplit<'a> = (&'a [MacTree], (IStr, u8, bool), &'a [MacTree]);
/// Derive the details of the central vectorial and the two sides from a /// Derive the details of the central vectorial and the two sides from a
/// slice of Expr's /// slice of Expr's
@@ -124,7 +124,7 @@ async fn mk_scalar(pattern: &MacTree, i: &Interner) -> OrcRes<ScalMatcher> {
}, },
MacTok::S(c, body) => ScalMatcher::S(*c, Box::new(mk_any(body, i).boxed_local().await?)), MacTok::S(c, body) => ScalMatcher::S(*c, Box::new(mk_any(body, i).boxed_local().await?)),
MacTok::Lambda(..) => MacTok::Lambda(..) =>
return Err(mk_errv( return Err(mk_err(
i.i("Lambda in matcher").await, i.i("Lambda in matcher").await,
"Lambdas can't be matched for, only generated in templates", "Lambdas can't be matched for, only generated in templates",
[pattern.pos()], [pattern.pos()],

View File

@@ -18,7 +18,7 @@ use crate::macros::{MacTok, MacTree};
pub struct NamedMatcher { pub struct NamedMatcher {
inner: AnyMatcher, inner: AnyMatcher,
head: Sym, head: Sym,
after_tok: Tok<String>, after_tok: IStr,
} }
impl NamedMatcher { impl NamedMatcher {
pub async fn new(pattern: &[MacTree], i: &Interner) -> OrcRes<Self> { pub async fn new(pattern: &[MacTree], i: &Interner) -> OrcRes<Self> {

View File

@@ -11,12 +11,12 @@ use orchid_base::tokens::{PARENS, Paren};
pub enum ScalMatcher { pub enum ScalMatcher {
Name(Sym), Name(Sym),
S(Paren, Box<AnyMatcher>), S(Paren, Box<AnyMatcher>),
Placeh { key: Tok<String> }, Placeh { key: IStr },
} }
pub enum VecMatcher { pub enum VecMatcher {
Placeh { Placeh {
key: Tok<String>, key: IStr,
nonzero: bool, nonzero: bool,
}, },
Scan { Scan {
@@ -41,7 +41,7 @@ pub enum VecMatcher {
/// the length of matches on either side. /// the length of matches on either side.
/// ///
/// Vectorial keys that appear on either side, in priority order /// Vectorial keys that appear on either side, in priority order
key_order: Vec<Tok<String>>, key_order: Vec<IStr>,
}, },
} }

View File

@@ -30,11 +30,11 @@ pub enum StateEntry<'a> {
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct MatchState<'a> { pub struct MatchState<'a> {
placeholders: HashMap<Tok<String>, StateEntry<'a>>, placeholders: HashMap<IStr, StateEntry<'a>>,
name_posv: HashMap<Sym, Vec<Pos>>, name_posv: HashMap<Sym, Vec<Pos>>,
} }
impl<'a> MatchState<'a> { impl<'a> MatchState<'a> {
pub fn from_ph(key: Tok<String>, entry: StateEntry<'a>) -> Self { pub fn from_ph(key: IStr, entry: StateEntry<'a>) -> Self {
Self { placeholders: HashMap::from([(key, entry)]), name_posv: HashMap::new() } Self { placeholders: HashMap::from([(key, entry)]), name_posv: HashMap::new() }
} }
pub fn combine(self, s: Self) -> Self { pub fn combine(self, s: Self) -> Self {
@@ -45,7 +45,7 @@ impl<'a> MatchState<'a> {
}), }),
} }
} }
pub fn ph_len(&self, key: &Tok<String>) -> Option<usize> { pub fn ph_len(&self, key: &IStr) -> Option<usize> {
match self.placeholders.get(key)? { match self.placeholders.get(key)? {
StateEntry::Vec(slc) => Some(slc.len()), StateEntry::Vec(slc) => Some(slc.len()),
_ => None, _ => None,
@@ -54,8 +54,8 @@ impl<'a> MatchState<'a> {
pub fn from_name(name: Sym, location: Pos) -> Self { pub fn from_name(name: Sym, location: Pos) -> Self {
Self { name_posv: HashMap::from([(name, vec![location])]), placeholders: HashMap::new() } Self { name_posv: HashMap::from([(name, vec![location])]), placeholders: HashMap::new() }
} }
pub fn get(&self, key: &Tok<String>) -> Option<&StateEntry<'a>> { self.placeholders.get(key) } pub fn get(&self, key: &IStr) -> Option<&StateEntry<'a>> { self.placeholders.get(key) }
pub fn remove(&mut self, name: Tok<String>) -> Option<StateEntry<'a>> { pub fn remove(&mut self, name: IStr) -> Option<StateEntry<'a>> {
self.placeholders.remove(&name) self.placeholders.remove(&name)
} }
pub fn mk_owned(self) -> OwnedState { pub fn mk_owned(self) -> OwnedState {
@@ -85,10 +85,10 @@ pub enum OwnedEntry {
Scalar(MacTree), Scalar(MacTree),
} }
pub struct OwnedState { pub struct OwnedState {
placeholders: HashMap<Tok<String>, OwnedEntry>, placeholders: HashMap<IStr, OwnedEntry>,
name_posv: HashMap<Sym, Vec<Pos>>, name_posv: HashMap<Sym, Vec<Pos>>,
} }
impl OwnedState { impl OwnedState {
pub fn get(&self, key: &Tok<String>) -> Option<&OwnedEntry> { self.placeholders.get(key) } pub fn get(&self, key: &IStr) -> Option<&OwnedEntry> { self.placeholders.get(key) }
pub fn positions(&self, name: &Sym) -> &[Pos] { self.name_posv.get(name).map_or(&[], |v| &v[..]) } pub fn positions(&self, name: &Sym) -> &[Pos] { self.name_posv.get(name).map_or(&[], |v| &v[..]) }
} }

View File

@@ -6,7 +6,7 @@ use crate::macros::{MacTok, MacTree};
/// Returns the name, priority and at_least_one of the expression if it is /// Returns the name, priority and at_least_one of the expression if it is
/// a vectorial placeholder /// a vectorial placeholder
#[must_use] #[must_use]
pub fn vec_attrs(expr: &MacTree) -> Option<(Tok<String>, u8, bool)> { pub fn vec_attrs(expr: &MacTree) -> Option<(IStr, u8, bool)> {
match (*expr.tok).clone() { match (*expr.tok).clone() {
MacTok::Ph(Ph { kind: PhKind::Vector { priority, at_least_one }, name }) => MacTok::Ph(Ph { kind: PhKind::Vector { priority, at_least_one }, name }) =>
Some((name, priority, at_least_one)), Some((name, priority, at_least_one)),

View File

@@ -6,7 +6,7 @@ use std::rc::Rc;
use futures::AsyncWrite; use futures::AsyncWrite;
use orchid_api_derive::Coding; use orchid_api_derive::Coding;
use orchid_api_traits::{Encode, Request}; use orchid_api_traits::{Encode, Request};
use orchid_base::error::{OrcRes, mk_errv}; use orchid_base::error::{OrcRes, mk_err};
use orchid_base::format::{FmtCtx, FmtUnit}; use orchid_base::format::{FmtCtx, FmtUnit};
use orchid_base::interner::Tok; use orchid_base::interner::Tok;
use orchid_extension::atom::{AtomMethod, Atomic, MethodSetBuilder, Supports, TypAtom}; use orchid_extension::atom::{AtomMethod, Atomic, MethodSetBuilder, Supports, TypAtom};
@@ -58,13 +58,13 @@ impl OwnedAtom for StrAtom {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct IntStrAtom(Tok<String>); pub struct IntStrAtom(IStr);
impl Atomic for IntStrAtom { impl Atomic for IntStrAtom {
type Variant = OwnedVariant; type Variant = OwnedVariant;
type Data = orchid_api::TStr; type Data = orchid_api::TStr;
} }
impl From<Tok<String>> for IntStrAtom { impl From<IStr> for IntStrAtom {
fn from(value: Tok<String>) -> Self { Self(value) } fn from(value: IStr) -> Self { Self(value) }
} }
impl OwnedAtom for IntStrAtom { impl OwnedAtom for IntStrAtom {
type Refs = (); type Refs = ();
@@ -109,7 +109,7 @@ impl TryFromExpr for OrcString {
let ctx = expr.ctx(); let ctx = expr.ctx();
match TypAtom::<IntStrAtom>::try_from_expr(expr).await { match TypAtom::<IntStrAtom>::try_from_expr(expr).await {
Ok(t) => Ok(OrcString { ctx: t.untyped.ctx().clone(), kind: OrcStringKind::Int(t) }), Ok(t) => Ok(OrcString { ctx: t.untyped.ctx().clone(), kind: OrcStringKind::Int(t) }),
Err(e) => Err(mk_errv(ctx.i().i("A string was expected").await, "", e.pos_iter())), Err(e) => Err(mk_err(ctx.i().i("A string was expected").await, "", e.pos_iter())),
} }
} }
} }

View File

@@ -1,5 +1,5 @@
use itertools::Itertools; use itertools::Itertools;
use orchid_base::error::{OrcErr, OrcErrv, OrcRes, mk_errv}; use orchid_base::error::{OrcErr, OrcErrv, OrcRes, mk_err};
use orchid_base::interner::Interner; use orchid_base::interner::Interner;
use orchid_base::location::SrcRange; use orchid_base::location::SrcRange;
use orchid_base::name::Sym; use orchid_base::name::Sym;
@@ -36,7 +36,7 @@ impl StringError {
/// Convert into project error for reporting /// Convert into project error for reporting
pub async fn into_proj(self, path: &Sym, pos: u32, i: &Interner) -> OrcErrv { pub async fn into_proj(self, path: &Sym, pos: u32, i: &Interner) -> OrcErrv {
let start = pos + self.pos; let start = pos + self.pos;
mk_errv( mk_err(
i.i("Failed to parse string").await, i.i("Failed to parse string").await,
match self.kind { match self.kind {
StringErrorKind::NotHex => "Expected a hex digit", StringErrorKind::NotHex => "Expected a hex digit",
@@ -144,7 +144,7 @@ impl Lexer for StringLexer {
tail = ch.as_str(); tail = ch.as_str();
} else { } else {
let range = ctx.pos(all)..ctx.pos(""); let range = ctx.pos(all)..ctx.pos("");
return Err(mk_errv( return Err(mk_err(
ctx.i().i("No string end").await, ctx.i().i("No string end").await,
"String never terminated with \"", "String never terminated with \"",
[SrcRange::new(range.clone(), ctx.src())], [SrcRange::new(range.clone(), ctx.src())],

View File

@@ -131,7 +131,7 @@ async fn main() -> io::Result<ExitCode> {
}; };
let snip = Snippet::new(first, &lexemes); let snip = Snippet::new(first, &lexemes);
let ptree = parse_items(&pctx, Substack::Bottom, snip).await.unwrap(); let ptree = parse_items(&pctx, Substack::Bottom, snip).await.unwrap();
if let Some(errv) = reporter.errv() { if let Some(errv) = reporter.res() {
eprintln!("{errv}"); eprintln!("{errv}");
*exit_code1.borrow_mut() = ExitCode::FAILURE; *exit_code1.borrow_mut() = ExitCode::FAILURE;
return; return;

View File

@@ -3,7 +3,7 @@ use std::path::{Path, PathBuf};
use futures::FutureExt; use futures::FutureExt;
use itertools::Itertools; use itertools::Itertools;
use orchid_base::error::{OrcRes, Reporter, async_io_err, mk_errv, os_str_to_string}; use orchid_base::error::{OrcRes, Reporter, async_io_err, mk_err, os_str_to_string};
use orchid_base::location::SrcRange; use orchid_base::location::SrcRange;
use orchid_base::name::Sym; use orchid_base::name::Sym;
use orchid_base::parse::Snippet; use orchid_base::parse::Snippet;
@@ -30,7 +30,7 @@ pub async fn parse_folder(
let sr = SrcRange::new(0..0, &ns); let sr = SrcRange::new(0..0, &ns);
if path.is_dir() { if path.is_dir() {
let Some(name_os) = path.file_name() else { let Some(name_os) = path.file_name() else {
return Err(mk_errv( return Err(mk_err(
ctx.i.i("Could not read directory name").await, ctx.i.i("Could not read directory name").await,
format!("Path {} ends in ..", path.to_string_lossy()), format!("Path {} ends in ..", path.to_string_lossy()),
[sr], [sr],