very elegant extension API and parts of it used in std as POC

This commit is contained in:
2024-07-01 20:11:22 +02:00
parent 93867e40c6
commit fc8441f080
63 changed files with 2040 additions and 925 deletions

View File

@@ -9,7 +9,7 @@ edition = "2021"
derive_destructure = "1.0.0"
dyn-clone = "1.0.17"
hashbrown = "0.14.3"
itertools = "0.12.1"
itertools = "0.13.0"
lazy_static = "1.4.0"
never = "0.1.0"
orchid-api = { version = "0.1.0", path = "../orchid-api" }

View File

@@ -0,0 +1,29 @@
use std::borrow::Borrow;
use std::ops::Deref;
use std::sync::Arc;
pub enum ArcCow<'a, T: ?Sized + ToOwned> {
Borrowed(&'a T),
Owned(Arc<T::Owned>),
}
impl<'a, T: ?Sized + ToOwned> ArcCow<'a, T> {
pub fn owned(value: T::Owned) -> Self { Self::Owned(Arc::new(value)) }
}
impl<'a, T: ?Sized + ToOwned> Clone for ArcCow<'a, T> {
fn clone(&self) -> Self {
match self {
Self::Borrowed(r) => Self::Borrowed(r),
Self::Owned(b) => Self::Owned(b.clone()),
}
}
}
impl<'a, T: ?Sized + ToOwned> Deref for ArcCow<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
Self::Borrowed(t) => t,
Self::Owned(b) => b.as_ref().borrow(),
}
}
}

View File

@@ -11,13 +11,12 @@ pub fn box_once<'a, T: 'a>(t: T) -> BoxedIter<'a, T> { Box::new(iter::once(t)) }
pub fn box_empty<'a, T: 'a>() -> BoxedIter<'a, T> { Box::new(iter::empty()) }
/// Chain various iterators into a [BoxedIter]
#[macro_export]
macro_rules! box_chain {
($curr:expr) => {
Box::new($curr) as BoxedIter<_>
Box::new($curr) as $crate::boxed_iter::BoxedIter<_>
};
($curr:expr, $($rest:expr),*) => {
Box::new($curr$(.chain($rest))*) as $crate::utils::boxed_iter::BoxedIter<_>
Box::new($curr$(.chain($rest))*) as $crate::boxed_iter::BoxedIter<_>
};
}
pub(crate) use box_chain;

48
orchid-base/src/error.rs Normal file
View File

@@ -0,0 +1,48 @@
use std::sync::Arc;
use orchid_api::error::{ProjErr, ProjErrLocation};
use crate::interner::{deintern, Tok};
use crate::location::Pos;
/// A point of interest in resolving the error, such as the point where
/// processing got stuck, a command that is likely to be incorrect
#[derive(Clone)]
pub struct ErrorPosition {
/// The suspected origin
pub position: Pos,
/// Any information about the role of this origin
pub message: Option<Arc<String>>,
}
impl ErrorPosition {
pub fn from_api(pel: &ProjErrLocation) -> Self {
Self {
message: Some(pel.message.clone()).filter(|s| !s.is_empty()),
position: Pos::from_api(&pel.location),
}
}
pub fn to_api(&self) -> ProjErrLocation {
ProjErrLocation {
message: self.message.clone().unwrap_or_default(),
location: self.position.to_api(),
}
}
}
impl From<Pos> for ErrorPosition {
fn from(origin: Pos) -> Self { Self { position: origin, message: None } }
}
pub struct ErrorDetails {
pub description: Tok<String>,
pub message: Arc<String>,
pub locations: Vec<ErrorPosition>,
}
impl ErrorDetails {
pub fn from_api(err: &ProjErr) -> Self {
Self {
description: deintern(err.description),
message: err.message.clone(),
locations: err.locations.iter().map(ErrorPosition::from_api).collect(),
}
}
}

View File

@@ -61,3 +61,7 @@ impl<T, U> Event<T, U> {
}
}
}
impl<T, U> Default for Event<T, U> {
fn default() -> Self { Self::new() }
}

View File

@@ -1,4 +1,3 @@
use std::fmt::Debug;
use std::num::NonZeroU64;
use std::ops::{Deref, DerefMut};
use std::sync::atomic::{AtomicU64, Ordering};
@@ -12,17 +11,12 @@ pub struct IdStore<T> {
}
impl<T> IdStore<T> {
pub const fn new() -> Self { Self { table: OnceLock::new(), id: AtomicU64::new(1) } }
pub fn add<R>(&self, t: T) -> R
where
NonZeroU64: TryInto<R>,
<NonZeroU64 as TryInto<R>>::Error: Debug,
{
pub fn add(&self, t: T) -> IdRecord<'_, T> {
let tbl = self.table.get_or_init(Mutex::default);
let mut tbl_g = tbl.lock().unwrap();
let id64: NonZeroU64 = self.id.fetch_add(1, Ordering::Relaxed).try_into().unwrap();
let id: R = id64.try_into().expect("Keyspace exhausted");
assert!(tbl_g.insert(id64, t).is_none(), "atom ID wraparound");
id
let id: NonZeroU64 = self.id.fetch_add(1, Ordering::Relaxed).try_into().unwrap();
assert!(tbl_g.insert(id, t).is_none(), "atom ID wraparound");
IdRecord(id, tbl_g)
}
pub fn get(&self, id: impl Into<NonZeroU64>) -> Option<IdRecord<'_, T>> {
let tbl = self.table.get_or_init(Mutex::default);
@@ -38,6 +32,7 @@ impl<T> Default for IdStore<T> {
pub struct IdRecord<'a, T>(NonZeroU64, MutexGuard<'a, HashMap<NonZeroU64, T>>);
impl<'a, T> IdRecord<'a, T> {
pub fn id(&self) -> NonZeroU64 { self.0 }
pub fn remove(mut self) -> T { self.1.remove(&self.0).unwrap() }
}
impl<'a, T> Deref for IdRecord<'a, T> {

View File

@@ -7,56 +7,62 @@ use std::{fmt, hash};
use hashbrown::{HashMap, HashSet};
use itertools::Itertools as _;
use orchid_api::intern::{
use orchid_api::interner::{
ExternStr, ExternStrv, IntReq, InternStr, InternStrv, Retained, TStr, TStrv,
};
use orchid_api_traits::Request;
use crate::reqnot::{DynRequester, Requester};
/// Clippy crashes while verifying `Tok: Sized` without this and I cba to create
/// a minimal example
#[derive(Clone)]
pub struct Token<T: ?Sized + Interned> {
struct ForceSized<T>(T);
#[derive(Clone)]
pub struct Tok<T: Interned> {
data: Arc<T>,
marker: T::Marker,
marker: ForceSized<T::Marker>,
}
impl<T: Interned + ?Sized> Token<T> {
pub fn marker(&self) -> T::Marker { self.marker }
impl<T: Interned> Tok<T> {
pub fn new(data: Arc<T>, marker: T::Marker) -> Self { Self { data, marker: ForceSized(marker) } }
pub fn marker(&self) -> T::Marker { self.marker.0 }
pub fn arc(&self) -> Arc<T> { self.data.clone() }
}
impl<T: Interned + ?Sized> Deref for Token<T> {
impl<T: Interned> Deref for Tok<T> {
type Target = T;
fn deref(&self) -> &Self::Target { self.data.as_ref() }
}
impl<T: Interned + ?Sized> Ord for Token<T> {
impl<T: Interned> Ord for Tok<T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.marker().cmp(&other.marker()) }
}
impl<T: Interned + ?Sized> PartialOrd for Token<T> {
impl<T: Interned> PartialOrd for Tok<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) }
}
impl<T: Interned + ?Sized> Eq for Token<T> {}
impl<T: Interned + ?Sized> PartialEq for Token<T> {
impl<T: Interned> Eq for Tok<T> {}
impl<T: Interned> PartialEq for Tok<T> {
fn eq(&self, other: &Self) -> bool { self.cmp(other).is_eq() }
}
impl<T: Interned + ?Sized> hash::Hash for Token<T> {
impl<T: Interned> hash::Hash for Tok<T> {
fn hash<H: hash::Hasher>(&self, state: &mut H) { self.marker().hash(state) }
}
impl<T: Interned + ?Sized + fmt::Display> fmt::Display for Token<T> {
impl<T: Interned + fmt::Display> fmt::Display for Tok<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &*self.data)
}
}
impl<T: Interned + ?Sized + fmt::Debug> fmt::Debug for Token<T> {
impl<T: Interned + fmt::Debug> fmt::Debug for Tok<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Token({} -> {:?})", self.marker().get_id(), self.data.as_ref())
}
}
pub trait Interned: Eq + hash::Hash + Clone {
type Marker: InternMarker<Interned = Self>;
type Marker: InternMarker<Interned = Self> + Sized;
fn intern(self: Arc<Self>, req: &(impl DynRequester<Transfer = IntReq> + ?Sized))
-> Self::Marker;
fn bimap(interner: &mut Interner) -> &mut Bimap<Self>;
fn bimap(interner: &mut TypedInterners) -> &mut Bimap<Self>;
}
pub trait Internable {
@@ -64,9 +70,9 @@ pub trait Internable {
fn get_owned(&self) -> Arc<Self::Interned>;
}
pub trait InternMarker: Copy + PartialEq + Eq + PartialOrd + Ord + hash::Hash {
pub trait InternMarker: Copy + PartialEq + Eq + PartialOrd + Ord + hash::Hash + Sized {
type Interned: Interned<Marker = Self>;
fn resolve(self, req: &(impl DynRequester<Transfer = IntReq> + ?Sized)) -> Token<Self::Interned>;
fn resolve(self, req: &(impl DynRequester<Transfer = IntReq> + ?Sized)) -> Tok<Self::Interned>;
fn get_id(self) -> NonZeroU64;
fn from_id(id: NonZeroU64) -> Self;
}
@@ -79,13 +85,13 @@ impl Interned for String {
) -> Self::Marker {
req.request(InternStr(self))
}
fn bimap(interner: &mut Interner) -> &mut Bimap<Self> { &mut interner.strings }
fn bimap(interners: &mut TypedInterners) -> &mut Bimap<Self> { &mut interners.strings }
}
impl InternMarker for TStr {
type Interned = String;
fn resolve(self, req: &(impl DynRequester<Transfer = IntReq> + ?Sized)) -> Token<Self::Interned> {
Token { marker: self, data: req.request(ExternStr(self)) }
fn resolve(self, req: &(impl DynRequester<Transfer = IntReq> + ?Sized)) -> Tok<Self::Interned> {
Tok::new(req.request(ExternStr(self)), self)
}
fn get_id(self) -> NonZeroU64 { self.0 }
fn from_id(id: NonZeroU64) -> Self { Self(id) }
@@ -101,7 +107,7 @@ impl Internable for String {
fn get_owned(&self) -> Arc<Self::Interned> { Arc::new(self.to_string()) }
}
impl Interned for Vec<Token<String>> {
impl Interned for Vec<Tok<String>> {
type Marker = TStrv;
fn intern(
self: Arc<Self>,
@@ -109,38 +115,38 @@ impl Interned for Vec<Token<String>> {
) -> Self::Marker {
req.request(InternStrv(Arc::new(self.iter().map(|t| t.marker()).collect())))
}
fn bimap(interner: &mut Interner) -> &mut Bimap<Self> { &mut interner.vecs }
fn bimap(interners: &mut TypedInterners) -> &mut Bimap<Self> { &mut interners.vecs }
}
impl InternMarker for TStrv {
type Interned = Vec<Token<String>>;
fn resolve(self, req: &(impl DynRequester<Transfer = IntReq> + ?Sized)) -> Token<Self::Interned> {
type Interned = Vec<Tok<String>>;
fn resolve(self, req: &(impl DynRequester<Transfer = IntReq> + ?Sized)) -> Tok<Self::Interned> {
let data = Arc::new(req.request(ExternStrv(self)).iter().map(|m| deintern(*m)).collect_vec());
Token { marker: self, data }
Tok::new(data, self)
}
fn get_id(self) -> NonZeroU64 { self.0 }
fn from_id(id: NonZeroU64) -> Self { Self(id) }
}
impl Internable for [Token<String>] {
type Interned = Vec<Token<String>>;
impl Internable for [Tok<String>] {
type Interned = Vec<Tok<String>>;
fn get_owned(&self) -> Arc<Self::Interned> { Arc::new(self.to_vec()) }
}
impl Internable for Vec<Token<String>> {
type Interned = Vec<Token<String>>;
impl Internable for Vec<Tok<String>> {
type Interned = Vec<Tok<String>>;
fn get_owned(&self) -> Arc<Self::Interned> { Arc::new(self.to_vec()) }
}
impl Internable for Vec<TStr> {
type Interned = Vec<Token<String>>;
type Interned = Vec<Tok<String>>;
fn get_owned(&self) -> Arc<Self::Interned> {
Arc::new(self.iter().map(|ts| deintern(*ts)).collect())
}
}
impl Internable for [TStr] {
type Interned = Vec<Token<String>>;
type Interned = Vec<Tok<String>>;
fn get_owned(&self) -> Arc<Self::Interned> {
Arc::new(self.iter().map(|ts| deintern(*ts)).collect())
}
@@ -151,27 +157,25 @@ const BASE_RC: usize = 3;
#[test]
fn base_rc_correct() {
let tok = Token { marker: TStr(1.try_into().unwrap()), data: Arc::new("foo".to_string()) };
let tok = Tok::new(Arc::new("foo".to_string()), TStr(1.try_into().unwrap()));
let mut bimap = Bimap::default();
bimap.insert(tok.clone());
assert_eq!(Arc::strong_count(&tok.data), BASE_RC + 1, "the bimap plus the current instance");
}
pub struct Bimap<T: Interned + ?Sized> {
intern: HashMap<Arc<T>, Token<T>>,
by_id: HashMap<T::Marker, Token<T>>,
pub struct Bimap<T: Interned> {
intern: HashMap<Arc<T>, Tok<T>>,
by_id: HashMap<T::Marker, Tok<T>>,
}
impl<T: Interned + ?Sized> Bimap<T> {
pub fn insert(&mut self, token: Token<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.marker(), token);
}
pub fn by_marker(&self, marker: T::Marker) -> Option<Token<T>> {
self.by_id.get(&marker).cloned()
}
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<Token<T>>
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)
@@ -193,7 +197,7 @@ impl<T: Interned + ?Sized> Bimap<T> {
}
}
impl<T: Interned + ?Sized> Default for Bimap<T> {
impl<T: Interned> Default for Bimap<T> {
fn default() -> Self { Self { by_id: HashMap::new(), intern: HashMap::new() } }
}
@@ -202,9 +206,14 @@ pub trait UpComm {
}
#[derive(Default)]
pub struct Interner {
pub struct TypedInterners {
strings: Bimap<String>,
vecs: Bimap<Vec<Token<String>>>,
vecs: Bimap<Vec<Tok<String>>>,
}
#[derive(Default)]
pub struct Interner {
interners: TypedInterners,
master: Option<Box<dyn DynRequester<Transfer = IntReq>>>,
}
@@ -231,32 +240,36 @@ pub fn init_replica(req: impl DynRequester<Transfer = IntReq> + 'static) {
let mut g = INTERNER.lock().unwrap();
assert!(g.is_none(), "Attempted to initialize replica interner after first use");
*g = Some(Interner {
strings: Bimap::default(),
vecs: Bimap::default(),
master: Some(Box::new(req)),
interners: TypedInterners { strings: Bimap::default(), vecs: Bimap::default() },
})
}
pub fn intern<T: Interned>(t: &(impl Internable<Interned = T> + ?Sized)) -> Token<T> {
pub fn intern<T: Interned>(t: &(impl Internable<Interned = T> + ?Sized)) -> Tok<T> {
let mut g = interner();
let data = t.get_owned();
let marker = (g.master.as_mut()).map_or_else(
|| T::Marker::from_id(NonZeroU64::new(ID.fetch_add(1, atomic::Ordering::Relaxed)).unwrap()),
|c| data.clone().intern(&**c),
);
let tok = Token { marker, data };
T::bimap(&mut g).insert(tok.clone());
let typed = T::bimap(&mut g.interners);
if let Some(tok) = typed.by_value(&data) {
return tok;
}
let marker = match &mut g.master {
Some(c) => data.clone().intern(&**c),
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.interners).insert(tok.clone());
tok
}
pub fn deintern<M: InternMarker>(marker: M) -> Token<M::Interned> {
pub fn deintern<M: InternMarker>(marker: M) -> Tok<M::Interned> {
let mut g = interner();
if let Some(tok) = M::Interned::bimap(&mut g).by_marker(marker) {
if let Some(tok) = M::Interned::bimap(&mut g.interners).by_marker(marker) {
return tok;
}
let master = g.master.as_mut().expect("ID not in local interner and this is master");
let token = marker.resolve(&**master);
M::Interned::bimap(&mut g).insert(token.clone());
M::Interned::bimap(&mut g.interners).insert(token.clone());
token
}
@@ -268,14 +281,7 @@ pub fn merge_retained(into: &mut Retained, from: &Retained) {
pub fn sweep_replica() -> Retained {
let mut g = interner();
assert!(g.master.is_some(), "Not a replica");
Retained { strings: g.strings.sweep_replica(), vecs: g.vecs.sweep_replica() }
}
pub fn sweep_master(retained: Retained) {
let mut g = interner();
assert!(g.master.is_none(), "Not master");
g.strings.sweep_master(retained.strings.into_iter().collect());
g.vecs.sweep_master(retained.vecs.into_iter().collect());
Retained { strings: g.interners.strings.sweep_replica(), vecs: g.interners.vecs.sweep_replica() }
}
/// Create a thread-local token instance and copy it. This ensures that the
@@ -286,17 +292,33 @@ pub fn sweep_master(retained: Retained) {
macro_rules! intern {
($ty:ty : $expr:expr) => {{
thread_local! {
static VALUE: $crate::intern::Token<<$ty as $crate::intern::Internable>::Interned>
= $crate::intern::intern($expr as &$ty);
static VALUE: $crate::interner::Tok<<$ty as $crate::interner::Internable>::Interned>
= $crate::interner::intern::<
<$ty as $crate::interner::Internable>::Interned
>($expr as &$ty);
}
VALUE.with(|v| v.clone())
}};
}
#[allow(unused)]
pub fn sweep_master(retained: Retained) {
eprintln!(
"Hello, {:?}",
intern!([Tok<String>]: &[
intern!(str: "bar"),
intern!(str: "baz")
])
);
let mut g = interner();
assert!(g.master.is_none(), "Not master");
g.interners.strings.sweep_master(retained.strings.into_iter().collect());
g.interners.vecs.sweep_master(retained.vecs.into_iter().collect());
}
#[test]
fn test_i() {
let _: Token<String> = intern!(str: "foo");
let _: Token<Vec<Token<String>>> = intern!([Token<String>]: &[
let _: Tok<String> = intern!(str: "foo");
let _: Tok<Vec<Tok<String>>> = intern!([Tok<String>]: &[
intern!(str: "bar"),
intern!(str: "baz")
]);

View File

@@ -5,14 +5,15 @@ pub mod event;
pub mod msg;
// pub mod gen;
pub mod api_utils;
pub mod box_cow;
pub mod char_filter;
pub mod error;
pub mod id_store;
pub mod intern;
pub mod interner;
pub mod join;
pub mod location;
pub mod name;
pub mod proj_error;
pub mod reqnot;
pub mod sequence;
pub mod tree;
pub mod virt_fs;
// pub mod virt_fs;

View File

@@ -5,88 +5,99 @@ use std::hash::Hash;
use std::ops::Range;
use std::sync::Arc;
use itertools::Itertools;
use orchid_api::location as api;
use trait_set::trait_set;
use crate::interner::{deintern, Tok};
use crate::name::Sym;
use crate::sym;
/// A full source code unit, such as a source file
#[derive(Clone, Eq)]
pub struct SourceCode {
pub(crate) path: Sym,
pub(crate) text: Arc<String>,
trait_set! {
pub trait GetSrc = FnMut(&Sym) -> Tok<String>;
}
impl SourceCode {
/// Create a new source file description
pub fn new(path: Sym, source: Arc<String>) -> Self { Self { path, text: source } }
/// Location the source code was loaded from in the virtual tree
pub fn path(&self) -> Sym { self.path.clone() }
/// Raw source code string
pub fn text(&self) -> Arc<String> { self.text.clone() }
#[derive(Debug, Clone)]
pub enum Pos {
None,
/// Used in functions to denote the generated code that carries on the
/// location of the call. Not allowed in the const tree.
Inherit,
Gen(CodeGenInfo),
/// Range and file
SourceRange(SourceRange),
/// Range only, file implied. Most notably used by parsers
Range(Range<u32>),
}
impl PartialEq for SourceCode {
fn eq(&self, other: &Self) -> bool { self.path == other.path }
}
impl Hash for SourceCode {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.path.hash(state) }
}
impl fmt::Debug for SourceCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "CodeInfo({self})") }
}
impl fmt::Display for SourceCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.orc", self.path.str_iter().join("/"))
impl Pos {
pub fn to_api(&self) -> api::Location {
match self {
Self::Inherit => api::Location::Inherit,
Self::None => api::Location::None,
Self::Range(r) => api::Location::Range(r.clone()),
Self::Gen(cgi) => api::Location::Gen(cgi.to_api()),
Self::SourceRange(sr) => api::Location::SourceRange(sr.to_api()),
}
}
pub fn from_api(loc: &api::Location) -> Self {
match loc {
api::Location::Inherit => Self::Inherit,
api::Location::None => Self::None,
api::Location::Range(r) => Self::Range(r.clone()),
api::Location::Gen(cgi) => CodeGenInfo::from_api(cgi).location(),
api::Location::SourceRange(sr) => SourceRange::from_api(sr).location(),
}
}
pub fn pretty_print(&self, get_src: &mut impl GetSrc) -> String {
match self {
Self::Gen(g) => g.to_string(),
Self::SourceRange(sr) => sr.pretty_print(&get_src(&sr.path)),
// Can't pretty print partial and meta-location
other => format!("{other:?}"),
}
}
}
impl AsRef<str> for SourceCode {
fn as_ref(&self) -> &str { &self.text }
}
/// Exact source code location. Includes where the code was loaded from, what
/// the original source code was, and a byte range.
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SourceRange {
pub(crate) code: SourceCode,
pub(crate) range: Range<usize>,
pub(crate) path: Sym,
pub(crate) range: Range<u32>,
}
impl SourceRange {
/// Create a new instance.
pub fn new(range: Range<usize>, code: SourceCode) -> Self { Self { code, range } }
pub fn new(range: &Range<u32>, path: &Sym) -> Self {
Self { range: range.clone(), path: path.clone() }
}
pub fn to_api(&self) -> api::SourceRange {
api::SourceRange { path: self.path.tok().marker(), range: self.range.clone() }
}
pub fn from_api(sr: &api::SourceRange) -> Self {
Self { path: Sym::from_tok(deintern(sr.path)).unwrap(), range: sr.range.clone() }
}
/// Create a dud [SourceRange] for testing. Its value is unspecified and
/// volatile.
pub fn mock() -> Self { Self::new(0..1, SourceCode::new(sym!(test), Arc::new(String::new()))) }
/// Source code
pub fn code(&self) -> SourceCode { self.code.clone() }
/// Source text
pub fn text(&self) -> Arc<String> { self.code.text.clone() }
pub fn mock() -> Self { Self { range: 0..1, path: sym!(test) } }
/// Path the source text was loaded from
pub fn path(&self) -> Sym { self.code.path.clone() }
pub fn path(&self) -> Sym { self.path.clone() }
/// Byte range
pub fn range(&self) -> Range<usize> { self.range.clone() }
pub fn range(&self) -> Range<u32> { self.range.clone() }
/// 0-based index of first byte
pub fn start(&self) -> usize { self.range.start }
pub fn start(&self) -> u32 { self.range.start }
/// 0-based index of last byte + 1
pub fn end(&self) -> usize { self.range.end }
pub fn end(&self) -> u32 { self.range.end }
/// Syntactic location
pub fn origin(&self) -> CodeOrigin { CodeOrigin::Source(self.clone()) }
pub fn location(&self) -> Pos { Pos::SourceRange(self.clone()) }
/// Transform the numeric byte range
pub fn map_range(&self, map: impl FnOnce(Range<usize>) -> Range<usize>) -> Self {
Self::new(map(self.range()), self.code())
pub fn map_range(&self, map: impl FnOnce(Range<u32>) -> Range<u32>) -> Self {
Self { range: map(self.range()), path: self.path() }
}
}
impl fmt::Debug for SourceRange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "CodeRange({self})") }
}
impl fmt::Display for SourceRange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { code, range } = self;
let (sl, sc) = pos2lc(code.text.as_str(), range.start);
let (el, ec) = pos2lc(code.text.as_str(), range.end);
write!(f, "{code} {sl}:{sc}")?;
if el == sl {
if sc + 1 == ec { Ok(()) } else { write!(f, "..{ec}") }
} else {
write!(f, "..{el}:{ec}")
pub fn pretty_print(&self, src: &str) -> String {
let (sl, sc) = pos2lc(src, self.range.start);
let (el, ec) = pos2lc(src, self.range.end);
match (el == sl, ec <= sc + 1) {
(true, true) => format!("{sl}:{sc}"),
(true, false) => format!("{sl}:{sc}..{ec}"),
(false, _) => format!("{sl}:{sc}..{el}:{ec}"),
}
}
}
@@ -106,6 +117,17 @@ impl CodeGenInfo {
pub fn details(generator: Sym, details: impl AsRef<str>) -> Self {
Self { generator, details: Arc::new(details.as_ref().to_string()) }
}
/// Syntactic location
pub fn location(&self) -> Pos { Pos::Gen(self.clone()) }
pub fn to_api(&self) -> api::CodeGenInfo {
api::CodeGenInfo { generator: self.generator.tok().marker(), details: self.details.to_string() }
}
pub fn from_api(cgi: &api::CodeGenInfo) -> Self {
Self {
generator: Sym::from_tok(deintern(cgi.generator)).unwrap(),
details: Arc::new(cgi.details.clone()),
}
}
}
impl fmt::Debug for CodeGenInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "CodeGenInfo({self})") }
@@ -117,72 +139,9 @@ impl fmt::Display for CodeGenInfo {
}
}
/// identifies a sequence of characters that contributed to the enclosing
/// construct or the reason the code was generated for generated code.
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum CodeOrigin {
/// Character sequence
Source(SourceRange),
/// Generated construct
Gen(CodeGenInfo),
}
impl fmt::Display for CodeOrigin {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Gen(info) => write!(f, "{info}"),
Self::Source(cr) => write!(f, "{cr}"),
}
}
}
impl fmt::Debug for CodeOrigin {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "CodeOrigin({self})") }
}
/// Location data associated with any code fragment. Identifies where the code
/// came from and where it resides in the tree.
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct CodeLocation {
pub(crate) origin: CodeOrigin,
pub(crate) module: Sym,
}
impl CodeLocation {
pub(crate) fn new_src(range: SourceRange, module: Sym) -> Self {
Self { origin: CodeOrigin::Source(range), module }
}
/// Create a location for generated code. The generator string must not be
/// empty. For code, the generator string must contain at least one `::`
pub fn new_gen(gen: CodeGenInfo) -> Self {
Self { module: gen.generator.clone(), origin: CodeOrigin::Gen(gen) }
}
/// Get the syntactic location
pub fn origin(&self) -> CodeOrigin { self.origin.clone() }
/// Get the name of the containing module
pub fn module(&self) -> Sym { self.module.clone() }
}
impl fmt::Display for CodeLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.module[..].is_empty() {
write!(f, "global {}", self.origin)
} else {
write!(f, "{} in {}", self.origin, self.module)
}
}
}
impl fmt::Debug for CodeLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "CodeLocation({self})") }
}
#[must_use]
fn pos2lc(s: &str, i: usize) -> (usize, usize) {
s.chars().take(i).fold(
(1, 1),
|(line, col), char| {
if char == '\n' { (line + 1, 1) } else { (line, col + 1) }
},
)
fn pos2lc(s: &str, i: u32) -> (u32, u32) {
s.chars()
.take(i.try_into().unwrap())
.fold((1, 1), |(line, col), char| if char == '\n' { (line + 1, 1) } else { (line, col + 1) })
}

View File

@@ -11,23 +11,23 @@ use std::{fmt, slice, vec};
use itertools::Itertools;
use trait_set::trait_set;
use crate::intern::{intern, InternMarker, Token};
use crate::interner::{intern, InternMarker, Tok};
trait_set! {
/// Traits that all name iterators should implement
pub trait NameIter = Iterator<Item = Token<String>> + DoubleEndedIterator + ExactSizeIterator;
pub trait NameIter = Iterator<Item = Tok<String>> + DoubleEndedIterator + ExactSizeIterator;
}
/// A borrowed name fragment which can be empty. See [VPath] for the owned
/// variant.
#[derive(Hash, PartialEq, Eq)]
#[repr(transparent)]
pub struct PathSlice([Token<String>]);
pub struct PathSlice([Tok<String>]);
impl PathSlice {
/// Create a new [PathSlice]
pub fn new(slice: &[Token<String>]) -> &PathSlice {
pub fn new(slice: &[Tok<String>]) -> &PathSlice {
// SAFETY: This is ok because PathSlice is #[repr(transparent)]
unsafe { &*(slice as *const [Token<String>] as *const PathSlice) }
unsafe { &*(slice as *const [Tok<String>] as *const PathSlice) }
}
/// Convert to an owned name fragment
pub fn to_vpath(&self) -> VPath { VPath(self.0.to_vec()) }
@@ -57,7 +57,7 @@ impl PathSlice {
pub fn is_empty(&self) -> bool { self.len() == 0 }
/// Obtain a reference to the held slice. With all indexing traits shadowed,
/// this is better done explicitly
pub fn as_slice(&self) -> &[Token<String>] { self }
pub fn as_slice(&self) -> &[Tok<String>] { self }
/// Global empty path slice
pub fn empty() -> &'static Self { PathSlice::new(&[]) }
}
@@ -69,12 +69,12 @@ impl fmt::Display for PathSlice {
write!(f, "{}", self.str_iter().join("::"))
}
}
impl Borrow<[Token<String>]> for PathSlice {
fn borrow(&self) -> &[Token<String>] { &self.0 }
impl Borrow<[Tok<String>]> for PathSlice {
fn borrow(&self) -> &[Tok<String>] { &self.0 }
}
impl<'a> IntoIterator for &'a PathSlice {
type IntoIter = Cloned<slice::Iter<'a, Token<String>>>;
type Item = Token<String>;
type IntoIter = Cloned<slice::Iter<'a, Tok<String>>>;
type Item = Tok<String>;
fn into_iter(self) -> Self::IntoIter { self.0.iter().cloned() }
}
@@ -82,10 +82,10 @@ mod idx_impls {
use std::ops;
use super::PathSlice;
use crate::intern::Token;
use crate::interner::Tok;
impl ops::Index<usize> for PathSlice {
type Output = Token<String>;
type Output = Tok<String>;
fn index(&self, index: usize) -> &Self::Output { &self.0[index] }
}
macro_rules! impl_range_index_for_pathslice {
@@ -106,27 +106,27 @@ mod idx_impls {
}
impl Deref for PathSlice {
type Target = [Token<String>];
type Target = [Tok<String>];
fn deref(&self) -> &Self::Target { &self.0 }
}
impl Borrow<PathSlice> for [Token<String>] {
impl Borrow<PathSlice> for [Tok<String>] {
fn borrow(&self) -> &PathSlice { PathSlice::new(self) }
}
impl<const N: usize> Borrow<PathSlice> for [Token<String>; N] {
impl<const N: usize> Borrow<PathSlice> for [Tok<String>; N] {
fn borrow(&self) -> &PathSlice { PathSlice::new(&self[..]) }
}
impl Borrow<PathSlice> for Vec<Token<String>> {
impl Borrow<PathSlice> for Vec<Tok<String>> {
fn borrow(&self) -> &PathSlice { PathSlice::new(&self[..]) }
}
/// A token path which may be empty. [VName] is the non-empty,
/// [PathSlice] is the borrowed version
#[derive(Clone, Default, Hash, PartialEq, Eq)]
pub struct VPath(pub Vec<Token<String>>);
pub struct VPath(pub Vec<Tok<String>>);
impl VPath {
/// Collect segments into a vector
pub fn new(items: impl IntoIterator<Item = Token<String>>) -> Self {
pub fn new(items: impl IntoIterator<Item = Tok<String>>) -> Self {
Self(items.into_iter().collect())
}
/// Number of path segments
@@ -135,11 +135,11 @@ impl VPath {
/// valid name
pub fn is_empty(&self) -> bool { self.len() == 0 }
/// Prepend some tokens to the path
pub fn prefix(self, items: impl IntoIterator<Item = Token<String>>) -> Self {
pub fn prefix(self, items: impl IntoIterator<Item = Tok<String>>) -> Self {
Self(items.into_iter().chain(self.0).collect())
}
/// Append some tokens to the path
pub fn suffix(self, items: impl IntoIterator<Item = Token<String>>) -> Self {
pub fn suffix(self, items: impl IntoIterator<Item = Tok<String>>) -> Self {
Self(self.0.into_iter().chain(items).collect())
}
/// Partition the string by `::` namespace separators
@@ -154,12 +154,12 @@ impl VPath {
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
/// into a name.
pub fn name_with_prefix(self, name: Token<String>) -> VName {
pub fn name_with_prefix(self, name: Tok<String>) -> VName {
VName(self.into_iter().chain([name]).collect())
}
/// Add a token to the beginning of the. Since now we know that it can't be
/// empty, turn it into a name.
pub fn name_with_suffix(self, name: Token<String>) -> VName {
pub fn name_with_suffix(self, name: Tok<String>) -> VName {
VName([name].into_iter().chain(self).collect())
}
@@ -182,18 +182,18 @@ impl fmt::Display for VPath {
write!(f, "{}", self.str_iter().join("::"))
}
}
impl FromIterator<Token<String>> for VPath {
fn from_iter<T: IntoIterator<Item = Token<String>>>(iter: T) -> Self {
impl FromIterator<Tok<String>> for VPath {
fn from_iter<T: IntoIterator<Item = Tok<String>>>(iter: T) -> Self {
Self(iter.into_iter().collect())
}
}
impl IntoIterator for VPath {
type Item = Token<String>;
type Item = Tok<String>;
type IntoIter = vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
}
impl Borrow<[Token<String>]> for VPath {
fn borrow(&self) -> &[Token<String>] { self.0.borrow() }
impl Borrow<[Tok<String>]> for VPath {
fn borrow(&self) -> &[Tok<String>] { self.0.borrow() }
}
impl Borrow<PathSlice> for VPath {
fn borrow(&self) -> &PathSlice { PathSlice::new(&self.0[..]) }
@@ -218,39 +218,40 @@ where PathSlice: Index<T>
/// See also [Sym] for the immutable representation, and [VPath] for possibly
/// empty values
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct VName(Vec<Token<String>>);
pub struct VName(Vec<Tok<String>>);
impl VName {
/// Assert that the sequence isn't empty and wrap it in [VName] to represent
/// this invariant
pub fn new(items: impl IntoIterator<Item = Token<String>>) -> Result<Self, EmptyNameError> {
pub fn new(items: impl IntoIterator<Item = Tok<String>>) -> Result<Self, EmptyNameError> {
let data: Vec<_> = items.into_iter().collect();
if data.is_empty() { Err(EmptyNameError) } else { Ok(Self(data)) }
}
/// Unwrap the enclosed vector
pub fn into_vec(self) -> Vec<Token<String>> { self.0 }
pub fn into_vec(self) -> Vec<Tok<String>> { self.0 }
/// Get a reference to the enclosed vector
pub fn vec(&self) -> &Vec<Token<String>> { &self.0 }
pub fn vec(&self) -> &Vec<Tok<String>> { &self.0 }
/// Mutable access to the underlying vector. To ensure correct results, this
/// must never be empty.
pub fn vec_mut(&mut self) -> &mut Vec<Token<String>> { &mut self.0 }
pub fn vec_mut(&mut self) -> &mut Vec<Tok<String>> { &mut self.0 }
/// Intern the name and return a [Sym]
pub fn to_sym(&self) -> Sym { Sym(intern(&self.0[..])) }
/// If this name has only one segment, return it
pub fn as_root(&self) -> Option<Token<String>> { self.0.iter().exactly_one().ok().cloned() }
pub fn as_root(&self) -> Option<Tok<String>> { self.0.iter().exactly_one().ok().cloned() }
/// Prepend the segments to this name
#[must_use = "This is a pure function"]
pub fn prefix(self, items: impl IntoIterator<Item = Token<String>>) -> Self {
pub fn prefix(self, items: impl IntoIterator<Item = Tok<String>>) -> Self {
Self(items.into_iter().chain(self.0).collect())
}
/// Append the segments to this name
#[must_use = "This is a pure function"]
pub fn suffix(self, items: impl IntoIterator<Item = Token<String>>) -> Self {
pub fn suffix(self, items: impl IntoIterator<Item = Tok<String>>) -> Self {
Self(self.0.into_iter().chain(items).collect())
}
/// Read a `::` separated namespaced name
pub fn parse(s: &str) -> Result<Self, EmptyNameError> { Self::new(VPath::parse(s)) }
pub fn literal(s: &'static str) -> Self { Self::parse(s).expect("empty literal !?") }
/// Obtain an iterator over the segments of the name
pub fn iter(&self) -> impl Iterator<Item = Token<String>> + '_ { self.0.iter().cloned() }
pub fn iter(&self) -> impl Iterator<Item = Tok<String>> + '_ { self.0.iter().cloned() }
}
impl fmt::Debug for VName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "VName({self})") }
@@ -261,7 +262,7 @@ impl fmt::Display for VName {
}
}
impl IntoIterator for VName {
type Item = Token<String>;
type Item = Tok<String>;
type IntoIter = vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
}
@@ -272,8 +273,8 @@ where PathSlice: Index<T>
fn index(&self, index: T) -> &Self::Output { &self.deref()[index] }
}
impl Borrow<[Token<String>]> for VName {
fn borrow(&self) -> &[Token<String>] { self.0.borrow() }
impl Borrow<[Tok<String>]> for VName {
fn borrow(&self) -> &[Tok<String>] { self.0.borrow() }
}
impl Borrow<PathSlice> for VName {
fn borrow(&self) -> &PathSlice { PathSlice::new(&self.0[..]) }
@@ -287,9 +288,9 @@ impl Deref for VName {
/// empty sequence
#[derive(Debug, Copy, Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct EmptyNameError;
impl TryFrom<&[Token<String>]> for VName {
impl TryFrom<&[Tok<String>]> for VName {
type Error = EmptyNameError;
fn try_from(value: &[Token<String>]) -> Result<Self, Self::Error> {
fn try_from(value: &[Tok<String>]) -> Result<Self, Self::Error> {
Self::new(value.iter().cloned())
}
}
@@ -300,11 +301,11 @@ impl TryFrom<&[Token<String>]> for VName {
///
/// See also [VName]
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct Sym(Token<Vec<Token<String>>>);
pub struct Sym(Tok<Vec<Tok<String>>>);
impl Sym {
/// Assert that the sequence isn't empty, intern it and wrap it in a [Sym] to
/// represent this invariant
pub fn new(v: impl IntoIterator<Item = Token<String>>) -> Result<Self, EmptyNameError> {
pub fn new(v: impl IntoIterator<Item = Tok<String>>) -> Result<Self, EmptyNameError> {
let items = v.into_iter().collect_vec();
Self::from_tok(intern(&items[..]))
}
@@ -313,11 +314,11 @@ impl Sym {
Ok(Sym(intern(&VName::parse(s)?.into_vec()[..])))
}
/// Assert that a token isn't empty, and wrap it in a [Sym]
pub fn from_tok(t: Token<Vec<Token<String>>>) -> Result<Self, EmptyNameError> {
pub fn from_tok(t: Tok<Vec<Tok<String>>>) -> Result<Self, EmptyNameError> {
if t.is_empty() { Err(EmptyNameError) } else { Ok(Self(t)) }
}
/// Grab the interner token
pub fn tok(&self) -> Token<Vec<Token<String>>> { self.0.clone() }
pub fn tok(&self) -> Tok<Vec<Tok<String>>> { self.0.clone() }
/// Get a number unique to this name suitable for arbitrary ordering.
pub fn id(&self) -> NonZeroU64 { self.0.marker().get_id() }
/// Extern the sym for editing
@@ -338,8 +339,8 @@ where PathSlice: Index<T>
fn index(&self, index: T) -> &Self::Output { &self.deref()[index] }
}
impl Borrow<[Token<String>]> for Sym {
fn borrow(&self) -> &[Token<String>] { &self.0[..] }
impl Borrow<[Tok<String>]> for Sym {
fn borrow(&self) -> &[Tok<String>] { &self.0[..] }
}
impl Borrow<PathSlice> for Sym {
fn borrow(&self) -> &PathSlice { PathSlice::new(&self.0[..]) }
@@ -356,7 +357,7 @@ pub trait NameLike:
'static + Clone + Eq + Hash + fmt::Debug + fmt::Display + Borrow<PathSlice>
{
/// Convert into held slice
fn as_slice(&self) -> &[Token<String>] { Borrow::<PathSlice>::borrow(self) }
fn as_slice(&self) -> &[Tok<String>] { Borrow::<PathSlice>::borrow(self) }
/// Get iterator over tokens
fn iter(&self) -> impl NameIter + '_ { self.as_slice().iter().cloned() }
/// Get iterator over string segments
@@ -373,19 +374,19 @@ pub trait NameLike:
NonZeroUsize::try_from(self.iter().count()).expect("NameLike never empty")
}
/// Like slice's `split_first` except we know that it always returns Some
fn split_first(&self) -> (Token<String>, &PathSlice) {
fn split_first(&self) -> (Tok<String>, &PathSlice) {
let (foot, torso) = self.as_slice().split_last().expect("NameLike never empty");
(foot.clone(), PathSlice::new(torso))
}
/// Like slice's `split_last` except we know that it always returns Some
fn split_last(&self) -> (Token<String>, &PathSlice) {
fn split_last(&self) -> (Tok<String>, &PathSlice) {
let (foot, torso) = self.as_slice().split_last().expect("NameLike never empty");
(foot.clone(), PathSlice::new(torso))
}
/// Get the first element
fn first(&self) -> Token<String> { self.split_first().0 }
fn first(&self) -> Tok<String> { self.split_first().0 }
/// Get the last element
fn last(&self) -> Token<String> { self.split_last().0 }
fn last(&self) -> Tok<String> { self.split_last().0 }
}
impl NameLike for Sym {}
@@ -399,7 +400,7 @@ impl NameLike for VName {}
#[macro_export]
macro_rules! sym {
($seg1:tt $( :: $seg:tt)*) => {
$crate::name::Sym::from_tok($crate::intern!([$crate::intern::Token<String>]: &[
$crate::name::Sym::from_tok($crate::intern!([$crate::interner::Tok<String>]: &[
$crate::intern!(str: stringify!($seg1))
$( , $crate::intern!(str: stringify!($seg)) )*
])).unwrap()
@@ -457,16 +458,16 @@ mod test {
use std::borrow::Borrow;
use super::{PathSlice, Sym, VName};
use crate::intern::{intern, Token};
use crate::interner::{intern, Tok};
use crate::name::VPath;
#[test]
fn recur() {
let myname = vname!(foo::bar);
let _borrowed_slice: &[Token<String>] = myname.borrow();
let _borrowed_slice: &[Tok<String>] = myname.borrow();
let _borrowed_pathslice: &PathSlice = myname.borrow();
let _deref_pathslice: &PathSlice = &myname;
let _as_slice_out: &[Token<String>] = myname.as_slice();
let _as_slice_out: &[Tok<String>] = myname.as_slice();
}
#[test]

View File

@@ -1,297 +0,0 @@
//! Abstractions for handling various code-related errors under a common trait
//! object.
use std::any::Any;
use std::cell::RefCell;
use std::sync::Arc;
use std::{fmt, process};
use dyn_clone::{clone_box, DynClone};
use itertools::Itertools;
use crate::boxed_iter::{box_once, BoxedIter};
use crate::location::CodeOrigin;
#[allow(unused)] // for doc
use crate::virt_fs::CodeNotFound;
/// A point of interest in resolving the error, such as the point where
/// processing got stuck, a command that is likely to be incorrect
#[derive(Clone)]
pub struct ErrorPosition {
/// The suspected origin
pub origin: CodeOrigin,
/// Any information about the role of this origin
pub message: Option<String>,
}
impl From<CodeOrigin> for ErrorPosition {
fn from(origin: CodeOrigin) -> Self { Self { origin, message: None } }
}
/// Errors addressed to the developer which are to be resolved with
/// code changes
pub trait ProjectError: Sized + Send + Sync + 'static {
/// A general description of this type of error
const DESCRIPTION: &'static str;
/// A formatted message that includes specific parameters
#[must_use]
fn message(&self) -> String { self.description().to_string() }
/// Code positions relevant to this error. If you don't implement this, you
/// must implement [ProjectError::one_position]
#[must_use]
fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> + '_ {
box_once(ErrorPosition { origin: self.one_position(), message: None })
}
/// Short way to provide a single origin. If you don't implement this, you
/// must implement [ProjectError::positions]
#[must_use]
fn one_position(&self) -> CodeOrigin { unimplemented!() }
/// Convert the error into an `Arc<dyn DynProjectError>` to be able to
/// handle various errors together
#[must_use]
fn pack(self) -> ProjectErrorObj { Arc::new(self) }
}
/// Object-safe version of [ProjectError]. Implement that instead of this.
pub trait DynProjectError: Send + Sync {
/// Access type information about this error
#[must_use]
fn as_any_ref(&self) -> &dyn Any;
/// Pack the error into a trait object, or leave it as-is if it's already a
/// trait object
#[must_use]
fn into_packed(self: Arc<Self>) -> ProjectErrorObj;
/// A general description of this type of error
#[must_use]
fn description(&self) -> &str;
/// A formatted message that includes specific parameters
#[must_use]
fn message(&self) -> String { self.description().to_string() }
/// Code positions relevant to this error.
#[must_use]
fn positions(&self) -> BoxedIter<'_, ErrorPosition>;
}
impl<T> DynProjectError for T
where T: ProjectError
{
fn as_any_ref(&self) -> &dyn Any { self }
fn into_packed(self: Arc<Self>) -> ProjectErrorObj { self }
fn description(&self) -> &str { T::DESCRIPTION }
fn message(&self) -> String { ProjectError::message(self) }
fn positions(&self) -> BoxedIter<ErrorPosition> {
Box::new(ProjectError::positions(self).into_iter())
}
}
impl DynProjectError for ProjectErrorObj {
fn as_any_ref(&self) -> &dyn Any { (**self).as_any_ref() }
fn description(&self) -> &str { (**self).description() }
fn into_packed(self: Arc<Self>) -> ProjectErrorObj { (*self).clone() }
fn message(&self) -> String { (**self).message() }
fn positions(&self) -> BoxedIter<'_, ErrorPosition> { (**self).positions() }
}
impl fmt::Display for dyn DynProjectError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let description = self.description();
let message = self.message();
let positions = self.positions().collect::<Vec<_>>();
writeln!(f, "Project error: {description}\n{message}")?;
if positions.is_empty() {
writeln!(f, "No origins specified")?;
} else {
for ErrorPosition { origin, message } in positions {
match message {
None => writeln!(f, "@{origin}"),
Some(msg) => writeln!(f, "@{origin}: {msg}"),
}?
}
}
Ok(())
}
}
impl fmt::Debug for dyn DynProjectError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{self}") }
}
/// Type-erased [ProjectError] implementor through the [DynProjectError]
/// object-trait
pub type ProjectErrorObj = Arc<dyn DynProjectError>;
/// Alias for a result with an error of [ProjectErrorObj]. This is the type of
/// result most commonly returned by pre-runtime operations.
pub type ProjectResult<T> = Result<T, ProjectErrorObj>;
/// A trait for error types that are only missing an origin. Do not depend on
/// this trait, refer to [DynErrorSansOrigin] instead.
pub trait ErrorSansOrigin: Clone + Sized + Send + Sync + 'static {
/// General description of the error condition
const DESCRIPTION: &'static str;
/// Specific description of the error including code fragments or concrete
/// data if possible
fn message(&self) -> String { Self::DESCRIPTION.to_string() }
/// Convert the error to a type-erased structure for handling on shared
/// channels
fn pack(self) -> ErrorSansOriginObj { Box::new(self) }
/// A shortcut to streamline switching code between [ErrorSansOriginObj] and
/// concrete types
fn bundle(self, origin: &CodeOrigin) -> ProjectErrorObj { self.pack().bundle(origin) }
}
/// Object-safe equivalent to [ErrorSansOrigin]. Implement that one instead of
/// this. Typically found as [ErrorSansOriginObj]
pub trait DynErrorSansOrigin: Any + Send + Sync + DynClone {
/// Allow to downcast the base object to distinguish between various errors.
/// The main intended purpose is to trigger a fallback when [CodeNotFound] is
/// encountered, but the possibilities are not limited to that.
fn as_any_ref(&self) -> &dyn Any;
/// Regularize the type
fn into_packed(self: Box<Self>) -> ErrorSansOriginObj;
/// Generic description of the error condition
fn description(&self) -> &str;
/// Specific description of this particular error
fn message(&self) -> String;
/// Add an origin
fn bundle(self: Box<Self>, origin: &CodeOrigin) -> ProjectErrorObj;
}
/// Type-erased [ErrorSansOrigin] implementor through the object-trait
/// [DynErrorSansOrigin]. This can be turned into a [ProjectErrorObj] with
/// [ErrorSansOriginObj::bundle].
pub type ErrorSansOriginObj = Box<dyn DynErrorSansOrigin>;
/// A generic project result without origin
pub type ResultSansOrigin<T> = Result<T, ErrorSansOriginObj>;
impl<T: ErrorSansOrigin + 'static> DynErrorSansOrigin for T {
fn description(&self) -> &str { Self::DESCRIPTION }
fn message(&self) -> String { (*self).message() }
fn as_any_ref(&self) -> &dyn Any { self }
fn into_packed(self: Box<Self>) -> ErrorSansOriginObj { (*self).pack() }
fn bundle(self: Box<Self>, origin: &CodeOrigin) -> ProjectErrorObj {
Arc::new(OriginBundle(origin.clone(), *self))
}
}
impl Clone for ErrorSansOriginObj {
fn clone(&self) -> Self { clone_box(&**self) }
}
impl DynErrorSansOrigin for ErrorSansOriginObj {
fn description(&self) -> &str { (**self).description() }
fn message(&self) -> String { (**self).message() }
fn as_any_ref(&self) -> &dyn Any { (**self).as_any_ref() }
fn into_packed(self: Box<Self>) -> ErrorSansOriginObj { *self }
fn bundle(self: Box<Self>, origin: &CodeOrigin) -> ProjectErrorObj { (*self).bundle(origin) }
}
impl fmt::Display for ErrorSansOriginObj {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "{}\nOrigin missing from error", self.message())
}
}
impl fmt::Debug for ErrorSansOriginObj {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{self}") }
}
struct OriginBundle<T: ErrorSansOrigin>(CodeOrigin, T);
impl<T: ErrorSansOrigin> DynProjectError for OriginBundle<T> {
fn as_any_ref(&self) -> &dyn Any { self.1.as_any_ref() }
fn into_packed(self: Arc<Self>) -> ProjectErrorObj { self }
fn description(&self) -> &str { self.1.description() }
fn message(&self) -> String { self.1.message() }
fn positions(&self) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { origin: self.0.clone(), message: None })
}
}
/// A collection for tracking fatal errors without halting. Participating
/// functions return [ProjectResult] even if they only ever construct [Ok]. When
/// they call other participating functions, instead of directly forwarding
/// errors with `?` they should prefer constructing a fallback value with
/// [Reporter::fallback]. If any error is added to a [Reporter] in a function,
/// the return value is valid but its meaning need not be related in any way to
/// the inputs.
///
/// Returning [Err] from a function that accepts `&mut Reporter` indicates not
/// that there was a fatal error but that it wasn't possible to construct a
/// fallback, so if it can, the caller should construct one.
pub struct Reporter(RefCell<Vec<ProjectErrorObj>>);
impl Reporter {
/// Create a new error reporter
pub fn new() -> Self { Self(RefCell::new(Vec::new())) }
/// Returns true if any errors were regorded. If this ever returns true, it
/// will always return true in the future.
pub fn failing(&self) -> bool { !self.0.borrow().is_empty() }
/// Report a fatal error
pub fn report(&self, error: ProjectErrorObj) {
match error.as_any_ref().downcast_ref::<MultiError>() {
None => self.0.borrow_mut().push(error),
Some(me) =>
for err in me.0.iter() {
self.report(err.clone())
},
}
}
/// Catch a fatal error, report it, and substitute the value
pub fn fallback<T>(&self, res: ProjectResult<T>, cb: impl FnOnce(ProjectErrorObj) -> T) -> T {
res.inspect_err(|e| self.report(e.clone())).unwrap_or_else(cb)
}
/// Panic if there were errors
pub fn assert(&self) { self.unwrap(Ok(())) }
/// Exit with code -1 if there were errors
pub fn assert_exit(&self) { self.unwrap_exit(Ok(())) }
/// Panic with descriptive messages if there were errors. If there were no
/// errors, unwrap the result
pub fn unwrap<T>(&self, res: ProjectResult<T>) -> T {
if self.failing() {
panic!("Errors were encountered: \n{}", self.0.borrow().iter().join("\n"));
}
res.unwrap()
}
/// Print errors and exit if any occurred. If there were no errors, unwrap
/// the result
pub fn unwrap_exit<T>(&self, res: ProjectResult<T>) -> T {
if self.failing() {
eprintln!("Errors were encountered: \n{}", self.0.borrow().iter().join("\n"));
process::exit(-1)
}
res.unwrap_or_else(|e| {
eprintln!("{e}");
process::exit(-1)
})
}
/// Take the errors out of the reporter
#[must_use]
pub fn into_errors(self) -> Option<Vec<ProjectErrorObj>> {
let v = self.0.into_inner();
if v.is_empty() { None } else { Some(v) }
}
/// Raise an error if the reporter contains any errors
pub fn bind(self) -> ProjectResult<()> {
match self.into_errors() {
None => Ok(()),
Some(v) if v.len() == 1 => Err(v.into_iter().exactly_one().unwrap()),
Some(v) => Err(MultiError(v).pack()),
}
}
}
impl Default for Reporter {
fn default() -> Self { Self::new() }
}
struct MultiError(Vec<ProjectErrorObj>);
impl ProjectError for MultiError {
const DESCRIPTION: &'static str = "Multiple errors occurred";
fn message(&self) -> String { format!("{} errors occurred", self.0.len()) }
fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> + '_ {
self.0.iter().flat_map(|e| {
e.positions().map(|pos| {
let emsg = e.message();
let msg = match pos.message {
None => emsg,
Some(s) if s.is_empty() => emsg,
Some(pmsg) => format!("{emsg}: {pmsg}"),
};
ErrorPosition { origin: pos.origin, message: Some(msg) }
})
})
}
}

View File

@@ -12,9 +12,9 @@ use trait_set::trait_set;
trait_set! {
pub trait SendFn<T: MsgSet> = for<'a> FnMut(&'a [u8], ReqNot<T>) + DynClone + Send + 'static;
pub trait ReqFn<T: MsgSet> = FnMut(RequestHandle<T>) + Send + 'static;
pub trait ReqFn<T: MsgSet> = FnMut(RequestHandle<T>) + DynClone + Send + 'static;
pub trait NotifFn<T: MsgSet> =
for<'a> FnMut(<T::In as Channel>::Notif, ReqNot<T>) + Send + Sync + 'static;
for<'a> FnMut(<T::In as Channel>::Notif, ReqNot<T>) + DynClone + Send + Sync + 'static;
}
fn get_id(message: &[u8]) -> (u64, &[u8]) {
@@ -39,7 +39,7 @@ impl<MS: MsgSet + 'static> RequestHandle<MS> {
}
pub fn handle<T: Request>(&self, _: &T, rep: &T::Response) { self.respond(rep) }
pub fn will_handle_as<T: Request>(&self, _: &T) -> ReqTypToken<T> { ReqTypToken(PhantomData) }
pub fn handle_as<T: Request>(&self, token: ReqTypToken<T>, rep: &T::Response) {
pub fn handle_as<T: Request>(&self, _token: ReqTypToken<T>, rep: &T::Response) {
self.respond(rep)
}
}
@@ -86,13 +86,17 @@ impl<T: MsgSet> ReqNot<T> {
let mut g = self.0.lock().unwrap();
let (id, payload) = get_id(&message[..]);
if id == 0 {
(g.notif)(<T::In as Channel>::Notif::decode(&mut &payload[..]), self.clone())
let mut notif = clone_box(&*g.notif);
mem::drop(g);
notif(<T::In as Channel>::Notif::decode(&mut &payload[..]), self.clone())
} else if 0 < id.bitand(1 << 63) {
let sender = g.responses.remove(&!id).expect("Received response for invalid message");
sender.send(message).unwrap();
} else {
let message = <T::In as Channel>::Req::decode(&mut &payload[..]);
(g.req)(RequestHandle { id, message, fulfilled: false.into(), parent: self.clone() })
let mut req = clone_box(&*g.req);
mem::drop(g);
req(RequestHandle { id, message, fulfilled: false.into(), parent: self.clone() })
}
}

View File

@@ -20,7 +20,7 @@ impl<'a, T: 'a> Sequence<'a, T> {
Self(Rc::new(move || Box::new(f().into_iter())))
}
/// Get an iterator from the function
pub fn iter(&self) -> impl Iterator<Item = T> + '_ { (self.0)() }
pub fn iter(&self) -> BoxedIter<'_, T> { (self.0)() }
}
impl<'a, T: 'a> Clone for Sequence<'a, T> {
fn clone(&self) -> Self { Self(self.0.clone()) }

View File

@@ -4,18 +4,15 @@
use std::fmt;
use hashbrown::HashMap;
use itertools::Itertools as _;
use never::Never;
use substack::Substack;
use trait_set::trait_set;
use crate::boxed_iter::BoxedIter;
use crate::combine::Combine;
use crate::intern::{intern, Token};
use crate::interner::{intern, Tok};
use crate::join::try_join_maps;
use crate::location::CodeOrigin;
use crate::name::{VName, VPath};
use crate::proj_error::{ProjectError, ProjectErrorObj};
use crate::sequence::Sequence;
/// An umbrella trait for operations you can carry out on any part of the tree
@@ -34,18 +31,18 @@ pub trait TreeTransforms: Sized {
/// Implementation for [TreeTransforms::map_data]
fn map_data_rec<T, U, V>(
self,
item: &mut impl FnMut(Substack<Token<String>>, Self::Item) -> T,
module: &mut impl FnMut(Substack<Token<String>>, Self::XMod) -> U,
entry: &mut impl FnMut(Substack<Token<String>>, Self::XEnt) -> V,
path: Substack<Token<String>>,
item: &mut impl FnMut(Substack<Tok<String>>, Self::Item) -> T,
module: &mut impl FnMut(Substack<Tok<String>>, Self::XMod) -> U,
entry: &mut impl FnMut(Substack<Tok<String>>, Self::XEnt) -> V,
path: Substack<Tok<String>>,
) -> Self::SelfType<T, U, V>;
/// Transform all the data in the tree without changing its structure
fn map_data<T, U, V>(
self,
mut item: impl FnMut(Substack<Token<String>>, Self::Item) -> T,
mut module: impl FnMut(Substack<Token<String>>, Self::XMod) -> U,
mut entry: impl FnMut(Substack<Token<String>>, Self::XEnt) -> V,
mut item: impl FnMut(Substack<Tok<String>>, Self::Item) -> T,
mut module: impl FnMut(Substack<Tok<String>>, Self::XMod) -> U,
mut entry: impl FnMut(Substack<Tok<String>>, Self::XEnt) -> V,
) -> Self::SelfType<T, U, V> {
self.map_data_rec(&mut item, &mut module, &mut entry, Substack::Bottom)
}
@@ -55,14 +52,14 @@ pub trait TreeTransforms: Sized {
///
/// * init - can be used for reduce, otherwise pass `()`
/// * callback - a callback applied on every module.
/// * [`Substack<Token<String>>`] - the walked path
/// * [`Substack<Tok<String>>`] - the walked path
/// * [Module] - the current module
/// * `T` - data for reduce.
fn search_all<'a, T>(
&'a self,
init: T,
mut callback: impl FnMut(
Substack<Token<String>>,
Substack<Tok<String>>,
ModMemberRef<'a, Self::Item, Self::XMod, Self::XEnt>,
T,
) -> T,
@@ -77,14 +74,14 @@ pub trait TreeTransforms: Sized {
/// * init - can be used for reduce, otherwise pass `()`
/// * callback - a callback applied on every module. Can return [Err] to
/// short-circuit the walk
/// * [`Substack<Token<String>>`] - the walked path
/// * [`Substack<Tok<String>>`] - the walked path
/// * [Module] - the current module
/// * `T` - data for reduce.
fn search<'a, T, E>(
&'a self,
init: T,
mut callback: impl FnMut(
Substack<Token<String>>,
Substack<Tok<String>>,
ModMemberRef<'a, Self::Item, Self::XMod, Self::XEnt>,
T,
) -> Result<T, E>,
@@ -96,9 +93,9 @@ pub trait TreeTransforms: Sized {
fn search_rec<'a, T, E>(
&'a self,
state: T,
stack: Substack<Token<String>>,
stack: Substack<Tok<String>>,
callback: &mut impl FnMut(
Substack<Token<String>>,
Substack<Tok<String>>,
ModMemberRef<'a, Self::Item, Self::XMod, Self::XEnt>,
T,
) -> Result<T, E>,
@@ -122,10 +119,10 @@ impl<Item, XMod, XEnt> TreeTransforms for ModMember<Item, XMod, XEnt> {
fn map_data_rec<T, U, V>(
self,
item: &mut impl FnMut(Substack<Token<String>>, Item) -> T,
module: &mut impl FnMut(Substack<Token<String>>, XMod) -> U,
entry: &mut impl FnMut(Substack<Token<String>>, XEnt) -> V,
path: Substack<Token<String>>,
item: &mut impl FnMut(Substack<Tok<String>>, Item) -> T,
module: &mut impl FnMut(Substack<Tok<String>>, XMod) -> U,
entry: &mut impl FnMut(Substack<Tok<String>>, XEnt) -> V,
path: Substack<Tok<String>>,
) -> Self::SelfType<T, U, V> {
match self {
Self::Item(it) => ModMember::Item(item(path, it)),
@@ -136,9 +133,9 @@ impl<Item, XMod, XEnt> TreeTransforms for ModMember<Item, XMod, XEnt> {
fn search_rec<'a, T, E>(
&'a self,
state: T,
stack: Substack<Token<String>>,
stack: Substack<Tok<String>>,
callback: &mut impl FnMut(
Substack<Token<String>>,
Substack<Tok<String>>,
ModMemberRef<'a, Item, XMod, XEnt>,
T,
) -> Result<T, E>,
@@ -209,7 +206,7 @@ pub struct TreeConflict<Item: Combine, XMod: Combine, XEnt: Combine> {
impl<Item: Combine, XMod: Combine, XEnt: Combine> TreeConflict<Item, XMod, XEnt> {
fn new(kind: ConflictKind<Item, XMod, XEnt>) -> Self { Self { path: VPath::new([]), kind } }
fn push(self, seg: Token<String>) -> Self {
fn push(self, seg: Tok<String>) -> Self {
Self { path: self.path.prefix([seg]), kind: self.kind }
}
}
@@ -280,10 +277,10 @@ impl<Item, XMod, XEnt> TreeTransforms for ModEntry<Item, XMod, XEnt> {
fn map_data_rec<T, U, V>(
self,
item: &mut impl FnMut(Substack<Token<String>>, Item) -> T,
module: &mut impl FnMut(Substack<Token<String>>, XMod) -> U,
entry: &mut impl FnMut(Substack<Token<String>>, XEnt) -> V,
path: Substack<Token<String>>,
item: &mut impl FnMut(Substack<Tok<String>>, Item) -> T,
module: &mut impl FnMut(Substack<Tok<String>>, XMod) -> U,
entry: &mut impl FnMut(Substack<Tok<String>>, XEnt) -> V,
path: Substack<Tok<String>>,
) -> Self::SelfType<T, U, V> {
ModEntry {
member: self.member.map_data_rec(item, module, entry, path.clone()),
@@ -294,9 +291,9 @@ impl<Item, XMod, XEnt> TreeTransforms for ModEntry<Item, XMod, XEnt> {
fn search_rec<'a, T, E>(
&'a self,
state: T,
stack: Substack<Token<String>>,
stack: Substack<Tok<String>>,
callback: &mut impl FnMut(
Substack<Token<String>>,
Substack<Tok<String>>,
ModMemberRef<'a, Item, XMod, XEnt>,
T,
) -> Result<T, E>,
@@ -357,7 +354,7 @@ impl<Item, XMod: Default, XEnt: Default> ModEntry<Item, XMod, XEnt> {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Module<Item, XMod, XEnt> {
/// Submodules and items by name
pub entries: HashMap<Token<String>, ModEntry<Item, XMod, XEnt>>,
pub entries: HashMap<Tok<String>, ModEntry<Item, XMod, XEnt>>,
/// Additional fields
pub x: XMod,
}
@@ -369,7 +366,7 @@ trait_set! {
}
/// A line in a [Module]
pub type Record<Item, XMod, XEnt> = (Token<String>, ModEntry<Item, XMod, XEnt>);
pub type Record<Item, XMod, XEnt> = (Tok<String>, ModEntry<Item, XMod, XEnt>);
impl<Item, XMod, XEnt> Module<Item, XMod, XEnt> {
/// Returns child names for which the value matches a filter
@@ -377,15 +374,15 @@ impl<Item, XMod, XEnt> Module<Item, XMod, XEnt> {
pub fn keys<'a>(
&'a self,
filter: impl for<'b> Fn(&'b ModEntry<Item, XMod, XEnt>) -> bool + 'a,
) -> BoxedIter<Token<String>> {
) -> BoxedIter<Tok<String>> {
Box::new((self.entries.iter()).filter(move |(_, v)| filter(v)).map(|(k, _)| k.clone()))
}
/// Return the module at the end of the given path
pub fn walk_ref<'a: 'b, 'b>(
&'a self,
prefix: &'b [Token<String>],
path: &'b [Token<String>],
prefix: &'b [Tok<String>],
path: &'b [Tok<String>],
filter: impl Filter<'b, Item, XMod, XEnt>,
) -> Result<&'a Self, WalkError<'b>> {
let mut module = self;
@@ -412,8 +409,8 @@ impl<Item, XMod, XEnt> Module<Item, XMod, XEnt> {
/// if path is empty, since the reference cannot be forwarded that way
pub fn walk1_ref<'a: 'b, 'b>(
&'a self,
prefix: &'b [Token<String>],
path: &'b [Token<String>],
prefix: &'b [Tok<String>],
path: &'b [Tok<String>],
filter: impl Filter<'b, Item, XMod, XEnt>,
) -> Result<(&'a ModEntry<Item, XMod, XEnt>, &'a Self), WalkError<'b>> {
let (last, parent) = path.split_last().expect("Path cannot be empty");
@@ -436,8 +433,8 @@ impl<Item, XMod, XEnt> Module<Item, XMod, XEnt> {
/// If the target is the root node
pub fn inner_walk<'a: 'b, 'b>(
&'a self,
origin: &[Token<String>],
target: &'b [Token<String>],
origin: &[Tok<String>],
target: &'b [Tok<String>],
is_exported: impl for<'c> Fn(&'c ModEntry<Item, XMod, XEnt>) -> bool + Clone + 'b,
) -> Result<(&'a ModEntry<Item, XMod, XEnt>, &'a Self), WalkError<'b>> {
let ignore_vis_len = 1 + origin.iter().zip(target).take_while(|(a, b)| a == b).count();
@@ -464,10 +461,10 @@ impl<Item, XMod, XEnt> TreeTransforms for Module<Item, XMod, XEnt> {
fn map_data_rec<T, U, V>(
self,
item: &mut impl FnMut(Substack<Token<String>>, Item) -> T,
module: &mut impl FnMut(Substack<Token<String>>, XMod) -> U,
entry: &mut impl FnMut(Substack<Token<String>>, XEnt) -> V,
path: Substack<Token<String>>,
item: &mut impl FnMut(Substack<Tok<String>>, Item) -> T,
module: &mut impl FnMut(Substack<Tok<String>>, XMod) -> U,
entry: &mut impl FnMut(Substack<Tok<String>>, XEnt) -> V,
path: Substack<Tok<String>>,
) -> Self::SelfType<T, U, V> {
Module {
x: module(path.clone(), self.x),
@@ -480,9 +477,9 @@ impl<Item, XMod, XEnt> TreeTransforms for Module<Item, XMod, XEnt> {
fn search_rec<'a, T, E>(
&'a self,
mut state: T,
stack: Substack<Token<String>>,
stack: Substack<Tok<String>>,
callback: &mut impl FnMut(
Substack<Token<String>>,
Substack<Tok<String>>,
ModMemberRef<'a, Item, XMod, XEnt>,
T,
) -> Result<T, E>,
@@ -538,6 +535,15 @@ pub enum ErrKind {
/// The path leads into a leaf node
NotModule,
}
impl ErrKind {
pub const fn msg(&self) -> &'static str {
match self {
Self::Filtered => "The path leads into a private module",
Self::Missing => "Nonexistent path",
Self::NotModule => "The path leads into a leaf",
}
}
}
#[derive(Clone)]
/// All details about a failed tree-walk
@@ -545,42 +551,31 @@ pub struct WalkError<'a> {
/// Failure mode
kind: ErrKind,
/// Path to the module where the walk started
prefix: &'a [Token<String>],
prefix: &'a [Tok<String>],
/// Planned walk path
path: &'a [Token<String>],
path: &'a [Tok<String>],
/// Index into walked path where the error occurred
pos: usize,
/// Alternatives to the failed steps
options: Sequence<'a, Token<String>>,
options: Sequence<'a, Tok<String>>,
}
impl<'a> WalkError<'a> {
/// Total length of the path represented by this error
#[must_use]
pub fn depth(&self) -> usize { self.prefix.len() + self.pos + 1 }
/// Attach a location to the error and convert into trait object for reporting
#[must_use]
pub fn at(self, origin: &CodeOrigin) -> ProjectErrorObj {
let details = WalkErrorDetails {
origin: origin.clone(),
path: VName::new((self.prefix.iter()).chain(self.path.iter().take(self.pos + 1)).cloned())
.expect("empty paths don't cause an error"),
options: self.options.iter().collect(),
};
match self.kind {
ErrKind::Filtered => FilteredError(details).pack(),
ErrKind::Missing => MissingError(details).pack(),
ErrKind::NotModule => NotModuleError(details).pack(),
}
pub fn alternatives(&self) -> BoxedIter<Tok<String>> { self.options.iter() }
/// Get the total path including the step that caused the error
pub fn full_path(&self) -> VName {
VName::new((self.prefix.iter()).chain(self.path.iter().take(self.pos + 1)).cloned())
.expect("empty paths don't cause an error")
}
/// Construct an error for the very last item in a slice. This is often done
/// outside [super::tree] so it gets a function rather than exposing the
/// fields of [WalkError]
pub fn last(
path: &'a [Token<String>],
kind: ErrKind,
options: Sequence<'a, Token<String>>,
) -> Self {
pub fn last(path: &'a [Tok<String>], kind: ErrKind, options: Sequence<'a, Tok<String>>) -> Self {
WalkError { kind, path, options, pos: path.len() - 1, prefix: &[] }
}
}
@@ -594,37 +589,3 @@ impl<'a> fmt::Debug for WalkError<'a> {
.finish_non_exhaustive()
}
}
struct WalkErrorDetails {
path: VName,
options: Vec<Token<String>>,
origin: CodeOrigin,
}
impl WalkErrorDetails {
fn print_options(&self) -> String { format!("options are {}", self.options.iter().join(", ")) }
}
struct FilteredError(WalkErrorDetails);
impl ProjectError for FilteredError {
const DESCRIPTION: &'static str = "The path leads into a private module";
fn one_position(&self) -> CodeOrigin { self.0.origin.clone() }
fn message(&self) -> String { format!("{} is private, {}", self.0.path, self.0.print_options()) }
}
struct MissingError(WalkErrorDetails);
impl ProjectError for MissingError {
const DESCRIPTION: &'static str = "Nonexistent path";
fn one_position(&self) -> CodeOrigin { self.0.origin.clone() }
fn message(&self) -> String {
format!("{} does not exist, {}", self.0.path, self.0.print_options())
}
}
struct NotModuleError(WalkErrorDetails);
impl ProjectError for NotModuleError {
const DESCRIPTION: &'static str = "The path leads into a leaf";
fn one_position(&self) -> CodeOrigin { self.0.origin.clone() }
fn message(&self) -> String {
format!("{} is not a module, {}", self.0.path, self.0.print_options())
}
}

View File

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

View File

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

View File

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

View File

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

View File

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