forked from Orchid/orchid
Finally figured out how I want atoms to work
This commit is contained in:
@@ -0,0 +1 @@
|
||||
|
||||
|
||||
45
orchid-base/src/char_filter.rs
Normal file
45
orchid-base/src/char_filter.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use itertools::Itertools;
|
||||
use orchid_api::parser::CharFilter;
|
||||
|
||||
pub type CRange = RangeInclusive<char>;
|
||||
|
||||
fn try_merge_char_ranges(left: CRange, right: CRange) -> Result<CRange, (CRange, CRange)> {
|
||||
match *left.end() as u32 + 1 < *right.start() as u32 {
|
||||
true => Err((left, right)),
|
||||
false => Ok(*left.start()..=*right.end()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Process the character ranges to make them adhere to the structural
|
||||
/// requirements of [CharFilter]
|
||||
pub fn mk_char_filter(items: impl IntoIterator<Item = CRange>) -> CharFilter {
|
||||
CharFilter(
|
||||
(items.into_iter())
|
||||
.filter(|r| *r.start() as u32 + 1 < *r.end() as u32)
|
||||
.sorted_by_key(|r| *r.start() as u32)
|
||||
.coalesce(try_merge_char_ranges)
|
||||
.collect_vec(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Decide whether a char filter matches a character via binary search
|
||||
pub fn char_filter_match(cf: &CharFilter, c: char) -> bool {
|
||||
match cf.0.binary_search_by_key(&c, |l| *l.end()) {
|
||||
Ok(_) => true, // c is the end of a range
|
||||
Err(i) if i == cf.0.len() => false, // all ranges end before c
|
||||
Err(i) => cf.0[i].contains(&c), // c between cf.0[i-1]?.end and cf.0[i].end, check [i]
|
||||
}
|
||||
}
|
||||
|
||||
/// Merge two char filters into a filter that matches if either of the
|
||||
/// constituents would match.
|
||||
pub fn char_filter_union(l: &CharFilter, r: &CharFilter) -> CharFilter {
|
||||
CharFilter(
|
||||
(l.0.iter().merge_by(&r.0, |l, r| l.start() <= r.start()))
|
||||
.cloned()
|
||||
.coalesce(try_merge_char_ranges)
|
||||
.collect_vec(),
|
||||
)
|
||||
}
|
||||
53
orchid-base/src/id_store.rs
Normal file
53
orchid-base/src/id_store.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use std::fmt::Debug;
|
||||
use std::num::NonZeroU64;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::{Mutex, MutexGuard, OnceLock};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
|
||||
pub struct IdStore<T> {
|
||||
table: OnceLock<Mutex<HashMap<NonZeroU64, T>>>,
|
||||
id: AtomicU64,
|
||||
}
|
||||
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,
|
||||
{
|
||||
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
|
||||
}
|
||||
pub fn get(&self, id: impl Into<NonZeroU64>) -> Option<IdRecord<'_, T>> {
|
||||
let tbl = self.table.get_or_init(Mutex::default);
|
||||
let tbl_g = tbl.lock().unwrap();
|
||||
let id64 = id.into();
|
||||
if tbl_g.contains_key(&id64) { Some(IdRecord(id64, tbl_g)) } else { None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for IdStore<T> {
|
||||
fn default() -> Self { Self::new() }
|
||||
}
|
||||
|
||||
pub struct IdRecord<'a, T>(NonZeroU64, MutexGuard<'a, HashMap<NonZeroU64, T>>);
|
||||
impl<'a, T> IdRecord<'a, T> {
|
||||
pub fn remove(mut self) -> T { self.1.remove(&self.0).unwrap() }
|
||||
}
|
||||
impl<'a, T> Deref for IdRecord<'a, T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.1.get(&self.0).expect("Existence checked on construction")
|
||||
}
|
||||
}
|
||||
impl<'a, T> DerefMut for IdRecord<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.1.get_mut(&self.0).expect("Existence checked on construction")
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
pub mod boxed_iter;
|
||||
pub mod msg;
|
||||
pub mod clone;
|
||||
pub mod combine;
|
||||
pub mod event;
|
||||
pub mod msg;
|
||||
// pub mod gen;
|
||||
pub mod api_utils;
|
||||
pub mod char_filter;
|
||||
pub mod id_store;
|
||||
pub mod intern;
|
||||
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 join;
|
||||
pub mod sequence;
|
||||
pub mod api_utils;
|
||||
|
||||
@@ -9,8 +9,8 @@ use std::{fmt, process};
|
||||
use dyn_clone::{clone_box, DynClone};
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::location::CodeOrigin;
|
||||
use crate::boxed_iter::{box_once, BoxedIter};
|
||||
use crate::location::CodeOrigin;
|
||||
#[allow(unused)] // for doc
|
||||
use crate::virt_fs::CodeNotFound;
|
||||
|
||||
@@ -220,7 +220,15 @@ impl Reporter {
|
||||
/// 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) { self.0.borrow_mut().push(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)
|
||||
@@ -277,7 +285,11 @@ impl ProjectError for MultiError {
|
||||
self.0.iter().flat_map(|e| {
|
||||
e.positions().map(|pos| {
|
||||
let emsg = e.message();
|
||||
let msg = if let Some(pmsg) = pos.message { format!("{emsg}: {pmsg}") } else { emsg };
|
||||
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) }
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::ops::{BitAnd, Deref};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
@@ -12,7 +13,8 @@ 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 NotifFn<T: MsgSet> = for<'a> FnMut(<T::In as Channel>::Notif, ReqNot<T>) + Send + Sync + 'static;
|
||||
pub trait NotifFn<T: MsgSet> =
|
||||
for<'a> FnMut(<T::In as Channel>::Notif, ReqNot<T>) + Send + Sync + 'static;
|
||||
}
|
||||
|
||||
fn get_id(message: &[u8]) -> (u64, &[u8]) {
|
||||
@@ -22,20 +24,24 @@ fn get_id(message: &[u8]) -> (u64, &[u8]) {
|
||||
pub struct RequestHandle<T: MsgSet> {
|
||||
id: u64,
|
||||
message: <T::In as Channel>::Req,
|
||||
send: Box<dyn SendFn<T>>,
|
||||
parent: ReqNot<T>,
|
||||
fulfilled: AtomicBool,
|
||||
}
|
||||
impl<MS: MsgSet> RequestHandle<MS> {
|
||||
impl<MS: MsgSet + 'static> RequestHandle<MS> {
|
||||
pub fn reqnot(&self) -> ReqNot<MS> { self.parent.clone() }
|
||||
pub fn req(&self) -> &<MS::In as Channel>::Req { &self.message }
|
||||
fn respond(&self, response: &impl Encode) {
|
||||
assert!(!self.fulfilled.swap(true, Ordering::Relaxed), "Already responded");
|
||||
let mut buf = (!self.id).to_be_bytes().to_vec();
|
||||
response.encode(&mut buf);
|
||||
clone_box(&*self.send)(&buf, self.parent.clone());
|
||||
let mut send = clone_box(&*self.reqnot().0.lock().unwrap().send);
|
||||
(send)(&buf, self.parent.clone());
|
||||
}
|
||||
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) {
|
||||
self.respond(rep)
|
||||
}
|
||||
}
|
||||
impl<MS: MsgSet> Drop for RequestHandle<MS> {
|
||||
fn drop(&mut self) {
|
||||
@@ -43,6 +49,8 @@ impl<MS: MsgSet> Drop for RequestHandle<MS> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReqTypToken<T>(PhantomData<T>);
|
||||
|
||||
pub fn respond_with<R: Request>(r: &R, f: impl FnOnce(&R) -> R::Response) -> Vec<u8> {
|
||||
r.respond(f(r))
|
||||
}
|
||||
@@ -83,9 +91,8 @@ impl<T: MsgSet> ReqNot<T> {
|
||||
let sender = g.responses.remove(&!id).expect("Received response for invalid message");
|
||||
sender.send(message).unwrap();
|
||||
} else {
|
||||
let send = clone_box(&*g.send);
|
||||
let message = <T::In as Channel>::Req::decode(&mut &payload[..]);
|
||||
(g.req)(RequestHandle { id, message, send, fulfilled: false.into(), parent: self.clone() })
|
||||
(g.req)(RequestHandle { id, message, fulfilled: false.into(), parent: self.clone() })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +166,8 @@ mod test {
|
||||
use orchid_api_traits::{Channel, Request};
|
||||
|
||||
use super::{MsgSet, ReqNot};
|
||||
use crate::{clone, reqnot::Requester as _};
|
||||
use crate::clone;
|
||||
use crate::reqnot::Requester as _;
|
||||
|
||||
#[derive(Clone, Debug, Coding, PartialEq)]
|
||||
pub struct TestReq(u8);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::proj_error::{ErrorSansOrigin, ErrorSansOriginObj};
|
||||
use crate::intern::Token;
|
||||
use crate::name::{PathSlice, VPath};
|
||||
use crate::proj_error::{ErrorSansOrigin, ErrorSansOriginObj};
|
||||
|
||||
/// Represents the result of loading code from a string-tree form such
|
||||
/// as the file system. Cheap to clone.
|
||||
|
||||
@@ -3,11 +3,11 @@ use std::sync::Arc;
|
||||
|
||||
use super::common::CodeNotFound;
|
||||
use super::{FSResult, Loaded, VirtFS};
|
||||
use crate::intern::Token;
|
||||
use crate::proj_error::ErrorSansOrigin;
|
||||
use crate::name::PathSlice;
|
||||
use crate::tree::{ModEntry, ModMember};
|
||||
use crate::combine::Combine;
|
||||
use crate::intern::Token;
|
||||
use crate::name::PathSlice;
|
||||
use crate::proj_error::ErrorSansOrigin;
|
||||
use crate::tree::{ModEntry, ModMember};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ConflictingTrees;
|
||||
|
||||
@@ -10,8 +10,8 @@ use hashbrown::HashMap;
|
||||
use super::common::CodeNotFound;
|
||||
use super::{FSResult, Loaded, VirtFS};
|
||||
use crate::intern::{intern, Token};
|
||||
use crate::proj_error::{ErrorSansOrigin, ErrorSansOriginObj};
|
||||
use crate::name::PathSlice;
|
||||
use crate::proj_error::{ErrorSansOrigin, ErrorSansOriginObj};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct OpenError {
|
||||
|
||||
@@ -6,9 +6,9 @@ use rust_embed::RustEmbed;
|
||||
use super::common::CodeNotFound;
|
||||
use super::{FSResult, Loaded, VirtFS};
|
||||
use crate::intern::{intern, Token};
|
||||
use crate::proj_error::ErrorSansOrigin;
|
||||
use crate::location::CodeGenInfo;
|
||||
use crate::name::PathSlice;
|
||||
use crate::proj_error::ErrorSansOrigin;
|
||||
use crate::tree::{ModEntry, ModMember, Module};
|
||||
|
||||
/// An in-memory FS tree for libraries managed internally by the interpreter
|
||||
|
||||
Reference in New Issue
Block a user