forked from Orchid/orchid
Began implementing fully isomorphic macros
Like Rust's Proc macros. Now we have preprocessor recursion to worry about. I also made a cool macro for enums
This commit is contained in:
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::fmt;
|
||||
use std::ops::Add;
|
||||
use std::sync::Arc;
|
||||
|
||||
use itertools::Itertools;
|
||||
@@ -65,16 +67,86 @@ impl PartialEq for OrcErr {
|
||||
impl From<OrcErr> for Vec<OrcErr> {
|
||||
fn from(value: OrcErr) -> Self { vec![value] }
|
||||
}
|
||||
|
||||
pub fn errv_to_apiv<'a>(errv: impl IntoIterator<Item = &'a OrcErr>) -> Vec<api::OrcError> {
|
||||
errv.into_iter().map(OrcErr::to_api).collect_vec()
|
||||
impl fmt::Display for OrcErr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let pstr = self.positions.iter().map(|p| format!("{p:?}")).join("; ");
|
||||
write!(f, "{}: {} @ {}", self.description, self.message, pstr)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn errv_from_apiv<'a>(err: impl IntoIterator<Item = &'a api::OrcError>) -> Vec<OrcErr> {
|
||||
err.into_iter().map(OrcErr::from_api).collect()
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EmptyErrv;
|
||||
impl fmt::Display for EmptyErrv {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "OrcErrv must not be empty")
|
||||
}
|
||||
}
|
||||
|
||||
pub type OrcRes<T> = Result<T, Vec<OrcErr>>;
|
||||
#[derive(Clone, Debug)]
|
||||
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 to_api(&self) -> Vec<api::OrcError> { self.0.iter().map(OrcErr::to_api).collect_vec() }
|
||||
#[must_use]
|
||||
pub fn from_api<'a>(apiv: impl IntoIterator<Item = &'a api::OrcError>) -> Self {
|
||||
let v = apiv.into_iter().map(OrcErr::from_api).collect_vec();
|
||||
assert!(!v.is_empty(), "Error condition with 0 errors");
|
||||
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())
|
||||
}
|
||||
}
|
||||
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 mk_err(
|
||||
description: Tok<String>,
|
||||
@@ -88,6 +160,14 @@ pub fn mk_err(
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Reporter {
|
||||
fn report(&self, e: OrcErr);
|
||||
pub fn mk_errv(
|
||||
description: Tok<String>,
|
||||
message: impl AsRef<str>,
|
||||
posv: impl IntoIterator<Item = ErrPos>,
|
||||
) -> OrcErrv {
|
||||
mk_err(description, message, posv).into()
|
||||
}
|
||||
|
||||
pub trait Reporter {
|
||||
fn report(&self, e: impl Into<OrcErrv>);
|
||||
}
|
||||
|
||||
@@ -3,13 +3,14 @@ use std::hash::BuildHasher as _;
|
||||
use std::num::NonZeroU64;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::{atomic, Arc, Mutex, MutexGuard};
|
||||
use std::{fmt, hash};
|
||||
use std::{fmt, hash, mem};
|
||||
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use itertools::Itertools as _;
|
||||
use orchid_api_traits::{Decode, Encode, Request};
|
||||
|
||||
use crate::api;
|
||||
use orchid_api_traits::{ApiEquiv, FromApi, ToApi};
|
||||
use crate::reqnot::{DynRequester, Requester};
|
||||
|
||||
/// Clippy crashes while verifying `Tok: Sized` without this and I cba to create
|
||||
@@ -62,7 +63,7 @@ impl<T: Interned + Decode> Decode for Tok<T> {
|
||||
fn decode<R: std::io::Read + ?Sized>(read: &mut R) -> Self { intern(&T::decode(read)) }
|
||||
}
|
||||
|
||||
pub trait Interned: Eq + hash::Hash + Clone + Internable<Interned = Self> {
|
||||
pub trait Interned: Eq + hash::Hash + Clone + fmt::Debug + Internable<Interned = Self> {
|
||||
type Marker: InternMarker<Interned = Self> + Sized;
|
||||
fn intern(
|
||||
self: Arc<Self>,
|
||||
@@ -71,7 +72,7 @@ pub trait Interned: Eq + hash::Hash + Clone + Internable<Interned = Self> {
|
||||
fn bimap(interner: &mut TypedInterners) -> &mut Bimap<Self>;
|
||||
}
|
||||
|
||||
pub trait Internable {
|
||||
pub trait Internable: fmt::Debug {
|
||||
type Interned: Interned;
|
||||
fn get_owned(&self) -> Arc<Self::Interned>;
|
||||
}
|
||||
@@ -96,7 +97,6 @@ impl Interned for String {
|
||||
}
|
||||
fn bimap(interners: &mut TypedInterners) -> &mut Bimap<Self> { &mut interners.strings }
|
||||
}
|
||||
|
||||
impl InternMarker for api::TStr {
|
||||
type Interned = String;
|
||||
fn resolve(
|
||||
@@ -108,17 +108,27 @@ impl InternMarker for api::TStr {
|
||||
fn get_id(self) -> NonZeroU64 { self.0 }
|
||||
fn from_id(id: NonZeroU64) -> Self { Self(id) }
|
||||
}
|
||||
|
||||
impl Internable for str {
|
||||
type Interned = String;
|
||||
fn get_owned(&self) -> Arc<Self::Interned> { Arc::new(self.to_string()) }
|
||||
}
|
||||
|
||||
impl Internable for String {
|
||||
type Interned = String;
|
||||
fn get_owned(&self) -> Arc<Self::Interned> { Arc::new(self.to_string()) }
|
||||
}
|
||||
|
||||
impl ApiEquiv for Tok<String> {
|
||||
type Api = api::TStr;
|
||||
}
|
||||
impl ToApi for Tok<String> {
|
||||
type Ctx = ();
|
||||
fn to_api(&self, _: &mut Self::Ctx) -> Self::Api { self.marker() }
|
||||
}
|
||||
impl FromApi for Tok<String> {
|
||||
type Ctx = ();
|
||||
fn from_api(api: &Self::Api, _: &mut Self::Ctx) -> Self { deintern(*api) }
|
||||
}
|
||||
|
||||
impl Interned for Vec<Tok<String>> {
|
||||
type Marker = api::TStrv;
|
||||
fn intern(
|
||||
@@ -129,7 +139,6 @@ impl Interned for Vec<Tok<String>> {
|
||||
}
|
||||
fn bimap(interners: &mut TypedInterners) -> &mut Bimap<Self> { &mut interners.vecs }
|
||||
}
|
||||
|
||||
impl InternMarker for api::TStrv {
|
||||
type Interned = Vec<Tok<String>>;
|
||||
fn resolve(
|
||||
@@ -143,30 +152,37 @@ impl InternMarker for api::TStrv {
|
||||
fn get_id(self) -> NonZeroU64 { self.0 }
|
||||
fn from_id(id: NonZeroU64) -> Self { Self(id) }
|
||||
}
|
||||
|
||||
impl Internable for [Tok<String>] {
|
||||
type Interned = Vec<Tok<String>>;
|
||||
fn get_owned(&self) -> Arc<Self::Interned> { Arc::new(self.to_vec()) }
|
||||
}
|
||||
|
||||
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<api::TStr> {
|
||||
type Interned = Vec<Tok<String>>;
|
||||
fn get_owned(&self) -> Arc<Self::Interned> {
|
||||
Arc::new(self.iter().map(|ts| deintern(*ts)).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl Internable for [api::TStr] {
|
||||
type Interned = Vec<Tok<String>>;
|
||||
fn get_owned(&self) -> Arc<Self::Interned> {
|
||||
Arc::new(self.iter().map(|ts| deintern(*ts)).collect())
|
||||
}
|
||||
}
|
||||
impl ApiEquiv for Tok<Vec<Tok<String>>> {
|
||||
type Api = api::TStrv;
|
||||
}
|
||||
impl ToApi for Tok<Vec<Tok<String>>> {
|
||||
type Ctx = ();
|
||||
fn to_api(&self, _: &mut Self::Ctx) -> Self::Api { self.marker() }
|
||||
}
|
||||
impl FromApi for Tok<Vec<Tok<String>>> {
|
||||
type Ctx = ();
|
||||
fn from_api(api: &Self::Api, _: &mut Self::Ctx) -> Self { deintern(*api) }
|
||||
}
|
||||
|
||||
/// The number of references held to any token by the interner.
|
||||
const BASE_RC: usize = 3;
|
||||
@@ -262,8 +278,10 @@ pub fn init_replica(req: impl DynRequester<Transfer = api::IntReq> + 'static) {
|
||||
}
|
||||
|
||||
pub fn intern<T: Interned>(t: &(impl Internable<Interned = T> + ?Sized)) -> Tok<T> {
|
||||
let mut g = interner();
|
||||
let data = t.get_owned();
|
||||
let mut g = interner();
|
||||
let job = format!("{t:?} in {}", if g.master.is_some() { "replica" } else { "master" });
|
||||
eprintln!("Interning {job}");
|
||||
let typed = T::bimap(&mut g.interners);
|
||||
if let Some(tok) = typed.by_value(&data) {
|
||||
return tok;
|
||||
@@ -275,6 +293,8 @@ pub fn intern<T: Interned>(t: &(impl Internable<Interned = T> + ?Sized)) -> Tok<
|
||||
};
|
||||
let tok = Tok::new(data, marker);
|
||||
T::bimap(&mut g.interners).insert(tok.clone());
|
||||
mem::drop(g);
|
||||
eprintln!("Interned {job}");
|
||||
tok
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ pub mod clone;
|
||||
pub mod combine;
|
||||
pub mod event;
|
||||
pub mod msg;
|
||||
// pub mod gen;
|
||||
pub mod api_utils;
|
||||
pub mod box_cow;
|
||||
pub mod char_filter;
|
||||
pub mod error;
|
||||
@@ -18,7 +16,10 @@ pub mod logging;
|
||||
pub mod name;
|
||||
pub mod number;
|
||||
pub mod parse;
|
||||
pub mod pure_seq;
|
||||
pub mod reqnot;
|
||||
pub mod sequence;
|
||||
pub mod tokens;
|
||||
pub mod tree;
|
||||
pub mod macros;
|
||||
mod match_mapping;
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
//! Structures that show where code or semantic elements came from
|
||||
|
||||
use crate::match_mapping;
|
||||
use std::fmt;
|
||||
use std::hash::Hash;
|
||||
use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::interner::{deintern, Tok};
|
||||
use orchid_api_traits::{ApiEquiv, FromApi, ToApi};
|
||||
use crate::interner::{deintern, intern, Tok};
|
||||
use crate::name::Sym;
|
||||
use crate::{api, sym};
|
||||
use crate::{api, intern, sym};
|
||||
|
||||
trait_set! {
|
||||
pub trait GetSrc = FnMut(&Sym) -> Tok<String>;
|
||||
@@ -18,6 +19,7 @@ trait_set! {
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Pos {
|
||||
None,
|
||||
SlotTarget,
|
||||
/// Used in functions to denote the generated code that carries on the
|
||||
/// location of the call. Not allowed in the const tree.
|
||||
Inherit,
|
||||
@@ -28,24 +30,6 @@ pub enum Pos {
|
||||
Range(Range<u32>),
|
||||
}
|
||||
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(),
|
||||
@@ -55,6 +39,32 @@ impl Pos {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ApiEquiv for Pos {
|
||||
type Api = api::Location;
|
||||
}
|
||||
impl FromApi for Pos {
|
||||
type Ctx = ();
|
||||
fn from_api(api: &Self::Api, ctx: &mut Self::Ctx) -> Self {
|
||||
match_mapping!(api, api::Location => Pos {
|
||||
None, Inherit, SlotTarget,
|
||||
Range(r.clone()),
|
||||
Gen(cgi => CodeGenInfo::from_api(cgi, &mut ())),
|
||||
SourceRange(sr => CodeGenInfo::from_api(sr, &mut ()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToApi for Pos {
|
||||
type Ctx = ();
|
||||
fn to_api(&self, ctx: &mut Self::Ctx) -> Self::Api {
|
||||
match_mapping!(self, Pos => Self::Api {
|
||||
None, Inherit, SlotTarget,
|
||||
Range(r.clone()),
|
||||
Gen(cgi.to_api(ctx)),
|
||||
SourceRange(sr.to_api(ctx)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Exact source code location. Includes where the code was loaded from, what
|
||||
/// the original source code was, and a byte range.
|
||||
@@ -67,12 +77,6 @@ impl SourceRange {
|
||||
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 { range: 0..1, path: sym!(test) } }
|
||||
@@ -85,7 +89,7 @@ impl SourceRange {
|
||||
/// 0-based index of last byte + 1
|
||||
pub fn end(&self) -> u32 { self.range.end }
|
||||
/// Syntactic location
|
||||
pub fn location(&self) -> Pos { Pos::SourceRange(self.clone()) }
|
||||
pub fn pos(&self) -> Pos { Pos::SourceRange(self.clone()) }
|
||||
/// Transform the numeric byte range
|
||||
pub fn map_range(&self, map: impl FnOnce(Range<u32>) -> Range<u32>) -> Self {
|
||||
Self { range: map(self.range()), path: self.path() }
|
||||
@@ -99,6 +103,24 @@ impl SourceRange {
|
||||
(false, _) => format!("{sl}:{sc}..{el}:{ec}"),
|
||||
}
|
||||
}
|
||||
pub fn zw(path: Sym, pos: u32) -> Self {
|
||||
Self { path, range: pos..pos }
|
||||
}
|
||||
}
|
||||
impl ApiEquiv for SourceRange {
|
||||
type Api = api::SourceRange;
|
||||
}
|
||||
impl FromApi for SourceRange {
|
||||
type Ctx = ();
|
||||
fn from_api(api: &Self::Api, ctx: &mut Self::Ctx) -> Self {
|
||||
Self { path: Sym::from_api(&api.path, ctx), range: api.range.clone() }
|
||||
}
|
||||
}
|
||||
impl ToApi for SourceRange {
|
||||
type Ctx = ();
|
||||
fn to_api(&self, ctx: &mut Self::Ctx) -> Self::Api {
|
||||
api::SourceRange { path: self.path.to_api(ctx), range: self.range.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about a code generator attached to the generated code
|
||||
@@ -107,26 +129,17 @@ pub struct CodeGenInfo {
|
||||
/// formatted like a Rust namespace
|
||||
pub generator: Sym,
|
||||
/// Unformatted user message with relevant circumstances and parameters
|
||||
pub details: Arc<String>,
|
||||
pub details: Tok<String>,
|
||||
}
|
||||
impl CodeGenInfo {
|
||||
/// A codegen marker with no user message and parameters
|
||||
pub fn no_details(generator: Sym) -> Self { Self { generator, details: Arc::new(String::new()) } }
|
||||
pub fn no_details(generator: Sym) -> Self { Self { generator, details: intern!(str: "") } }
|
||||
/// A codegen marker with a user message or parameters
|
||||
pub fn details(generator: Sym, details: impl AsRef<str>) -> Self {
|
||||
Self { generator, details: Arc::new(details.as_ref().to_string()) }
|
||||
Self { generator, details: intern(details.as_ref()) }
|
||||
}
|
||||
/// 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()),
|
||||
}
|
||||
}
|
||||
pub fn pos(&self) -> Pos { Pos::Gen(self.clone()) }
|
||||
}
|
||||
impl fmt::Debug for CodeGenInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "CodeGenInfo({self})") }
|
||||
@@ -137,6 +150,24 @@ impl fmt::Display for CodeGenInfo {
|
||||
if !self.details.is_empty() { write!(f, ", details: {}", self.details) } else { write!(f, ".") }
|
||||
}
|
||||
}
|
||||
impl ApiEquiv for CodeGenInfo {
|
||||
type Api = api::CodeGenInfo;
|
||||
}
|
||||
impl FromApi for CodeGenInfo {
|
||||
type Ctx = ();
|
||||
fn from_api(api: &Self::Api, ctx: &mut Self::Ctx) -> Self {
|
||||
Self {
|
||||
generator: Sym::from_api(&api.generator, ctx),
|
||||
details: Tok::from_api(&api.details, ctx),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ToApi for CodeGenInfo {
|
||||
type Ctx = ();
|
||||
fn to_api(&self, ctx: &mut Self::Ctx) -> Self::Api {
|
||||
api::CodeGenInfo { generator: self.generator.to_api(ctx), details: self.details.to_api(ctx) }
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn pos2lc(s: &str, i: u32) -> (u32, u32) {
|
||||
|
||||
@@ -14,7 +14,7 @@ impl Logger {
|
||||
pub fn log(&self, msg: impl AsRef<str>) { writeln!(self, "{}", msg.as_ref()) }
|
||||
pub fn strat(&self) -> api::LogStrategy { self.0.clone() }
|
||||
pub fn log_buf(&self, event: impl AsRef<str>, buf: &[u8]) {
|
||||
if !std::env::var("ORCHID_LOG_BUFFERS").unwrap().is_empty() {
|
||||
if std::env::var("ORCHID_LOG_BUFFERS").is_ok_and(|v| !v.is_empty()) {
|
||||
writeln!(self, "{}: [{}]", event.as_ref(), buf.iter().map(|b| format!("{b:02x}")).join(" "))
|
||||
}
|
||||
}
|
||||
|
||||
61
orchid-base/src/macros.rs
Normal file
61
orchid-base/src/macros.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{name::Sym, tree::{AtomTok, Paren, Ph, TokTree}};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::{api, location::Pos};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MacroSlot<'a>(api::MacroTreeId, PhantomData<&'a ()>);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MTree<'a, A: AtomTok> {
|
||||
pub pos: Pos,
|
||||
pub tok: MTok<'a, A>
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum MTok<'a, A> {
|
||||
S(Paren, Vec<MTree<'a, A>>),
|
||||
Name(Sym),
|
||||
Slot(MacroSlot<'a>),
|
||||
Lambda(Vec<MTree<'a, A>>, Vec<MTree<'a, A>>),
|
||||
Ph(Ph),
|
||||
Atom(A)
|
||||
}
|
||||
impl<'a, A> MTree<'a, A> {
|
||||
pub(crate) fn from_api(api: &api::MacroTree) -> Self {
|
||||
use api::MacroToken as MTK;
|
||||
let tok = match &api.token {
|
||||
MTK::Lambda(x, b) => MTok::Lambda(mtreev_from_api(x), mtreev_from_api(b)),
|
||||
MTK::Name(t) => MTok::Name(Sym::deintern(*t)),
|
||||
MTK::Slot(tk) => MTok::Slot(MacroSlot(tk.clone(), PhantomData)),
|
||||
MTK::S(p, b) => MTok::S(p.clone(), mtreev_from_api(b)),
|
||||
MTK::Ph(ph) => MTok::Ph(Ph::from_api(ph)),
|
||||
};
|
||||
Self { pos: Pos::from_api(&api.location), tok }
|
||||
}
|
||||
pub(crate) fn to_api(&self) -> api::MacroTree {
|
||||
use api::MacroToken as MTK;
|
||||
let token = match &self.tok {
|
||||
MTok::Lambda(x, b) => MTK::Lambda(mtreev_to_api(x), mtreev_to_api(b)),
|
||||
MTok::Name(t) => MTK::Name(t.tok().marker()),
|
||||
MTok::Ph(ph) => MTK::Ph(ph.to_api()),
|
||||
MTok::S(p, b) => MTK::S(p.clone(), mtreev_to_api(b)),
|
||||
MTok::Slot(tk) => MTK::Slot(tk.0.clone()),
|
||||
};
|
||||
api::MacroTree { location: self.pos.to_api(), token }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mtreev_from_api<'a, 'b, A>(
|
||||
api: impl IntoIterator<Item = &'b api::MacroTree>
|
||||
) -> Vec<MTree<'a, A>> {
|
||||
api.into_iter().map(MTree::from_api).collect_vec()
|
||||
}
|
||||
|
||||
pub fn mtreev_to_api<'a: 'b, 'b, A: 'b>(
|
||||
v: impl IntoIterator<Item = &'b MTree<'a, A>>
|
||||
) -> Vec<api::MacroTree> {
|
||||
v.into_iter().map(MTree::to_api).collect_vec()
|
||||
}
|
||||
95
orchid-base/src/match_mapping.rs
Normal file
95
orchid-base/src/match_mapping.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
/// A shorthand for mapping over enums with identical structure. Used for converting between
|
||||
/// owned enums and the corresponding API enums that only differ in the type of their
|
||||
/// fields.
|
||||
///
|
||||
/// The basic form is
|
||||
/// ```ignore
|
||||
/// match_mapping!(self, ThisType => OtherType {
|
||||
/// EmptyVariant,
|
||||
/// TupleVariant(foo => intern(foo), bar.clone()),
|
||||
/// StructVariant{ a.to_api(), b => b.}
|
||||
/// })
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! match_mapping {
|
||||
($input:expr, $src:ty => $tgt:ty {
|
||||
$($branches:tt)*
|
||||
}) => {
|
||||
match_mapping!(@BRANCH_MUNCH (($input) ($src) ($tgt)) () $($branches)* ,)
|
||||
};
|
||||
(@BRANCHES_DONE ( ($input:expr) ($src:ty) ($tgt:ty) )
|
||||
$( ( $variant:ident $($pat:tt)*) )*
|
||||
) => {
|
||||
{
|
||||
use $src as Foo;
|
||||
match $input {
|
||||
$(
|
||||
match_mapping!(@PAT (Foo :: $variant) $($pat)*) =>
|
||||
match_mapping!(@VAL (< $tgt >:: $variant) $($pat)*),
|
||||
)*
|
||||
}
|
||||
}
|
||||
};
|
||||
(@BRANCH_MUNCH $ext:tt ( $($branches:tt)* ) $(,)?) => {
|
||||
match_mapping!(@BRANCHES_DONE $ext $($branches)* )
|
||||
};
|
||||
(@BRANCH_MUNCH $ext:tt ( $($branches:tt)* ) $variant:ident , $($tail:tt)*) => {
|
||||
match_mapping!(@BRANCH_MUNCH $ext ( $($branches)* ($variant) ) $($tail)*)
|
||||
};
|
||||
(@BRANCH_MUNCH $ext:tt ( $($branches:tt)* ) $variant:ident $pat:tt , $($tail:tt)*) => {
|
||||
match_mapping!(@BRANCH_MUNCH $ext
|
||||
( $($branches)* ($variant $pat) )
|
||||
$($tail)*)
|
||||
};
|
||||
(@PAT ($($prefix:tt)*) ( $($fields:tt)* )) => {
|
||||
$($prefix)* ( match_mapping!(@PAT_MUNCH () $($fields)*) )
|
||||
};
|
||||
(@PAT ($($prefix:tt)*) { $($fields:tt)* }) => {
|
||||
$($prefix)* { match_mapping!(@PAT_MUNCH () $($fields)*) }
|
||||
};
|
||||
(@PAT ($($path:tt)*)) => { $($path)* };
|
||||
(@PAT_MUNCH ($($names:ident)*) $name:ident => $value:expr) => { $($names ,)* $name };
|
||||
(@PAT_MUNCH ($($names:ident)*) $name:ident => $value:expr , $($tail:tt)*) => {
|
||||
match_mapping!(@PAT_MUNCH ($($names)* $name) $($tail)*)
|
||||
};
|
||||
(@PAT_MUNCH ($($names:ident)*) $name:ident . $($tail:tt)*) => {
|
||||
match_mapping!(@PAT_DOT_MUNCH ($($names)* $name) $($tail)*)
|
||||
};
|
||||
(@PAT_MUNCH ($($names:ident)*)) => { $($names),* };
|
||||
(@PAT_DOT_MUNCH $names:tt , $($tail:tt)*) => {
|
||||
match_mapping!(@PAT_MUNCH $names $($tail)*)
|
||||
};
|
||||
(@PAT_DOT_MUNCH $names:tt $_:tt $($tail:tt)*) => {
|
||||
match_mapping!(@PAT_DOT_MUNCH $names $($tail)*)
|
||||
};
|
||||
(@PAT_DOT_MUNCH ($($names:tt)*)) => { $($names),* };
|
||||
(@VAL ($($prefix:tt)*)) => { $($prefix)* };
|
||||
(@VAL ($($prefix:tt)*) ( $($fields:tt)* )) => {
|
||||
$($prefix)* ( match_mapping!(@VAL_MUNCH () () $($fields)* ) )
|
||||
};
|
||||
(@VAL ($($prefix:tt)*) { $($fields:tt)* }) => {
|
||||
$($prefix)* { match_mapping!(@VAL_MUNCH {} () $($fields)* ) }
|
||||
};
|
||||
(@VAL_MUNCH () ($($prefix:tt)*) $name:ident => $value:expr) => { $($prefix)* $value };
|
||||
(@VAL_MUNCH () ($($prefix:tt)*) $name:ident => $value:expr , $($tail:tt)*) => {
|
||||
match_mapping!(@VAL_MUNCH () ($($prefix)* $value, ) $($tail)*)
|
||||
};
|
||||
(@VAL_MUNCH {} ($($prefix:tt)*) $name:ident => $value:expr) => { $($prefix)* $name: $value };
|
||||
(@VAL_MUNCH {} ($($prefix:tt)*) $name:ident => $value:expr , $($tail:tt)*) => {
|
||||
match_mapping!(@VAL_MUNCH {} ($($prefix)* $name: $value, ) $($tail)*)
|
||||
};
|
||||
(@VAL_MUNCH () ($($prefix:tt)*) $name:ident . $member:tt $($tail:tt)*) => {
|
||||
match_mapping!(@VAL_DOT_MUNCH () ($($prefix)* $name . $member ) $($tail)*)
|
||||
};
|
||||
(@VAL_MUNCH {} ($($prefix:tt)*) $name:ident . $member:tt $($tail:tt)*) => {
|
||||
match_mapping!(@VAL_DOT_MUNCH {} ($($prefix)* $name: $name . $member) $($tail)*)
|
||||
};
|
||||
(@VAL_DOT_MUNCH $ptyp:tt ($($prefix:tt)*) , $($tail:tt)*) => {
|
||||
match_mapping!(@VAL_MUNCH $ptyp ($($prefix)* ,) $($tail)*)
|
||||
};
|
||||
(@VAL_DOT_MUNCH $ptyp:tt ($($prefix:tt)*) $tt:tt $($tail:tt)*) => {
|
||||
match_mapping!(@VAL_DOT_MUNCH $ptyp ($($prefix)* $tt) $($tail)*)
|
||||
};
|
||||
(@VAL_DOT_MUNCH $ptyp:tt ($($prefix:tt)*)) => { $($prefix)* };
|
||||
(@VAL_MUNCH $_ptyp:tt ($($prefix:tt)*)) => { $($prefix)* };
|
||||
}
|
||||
@@ -9,10 +9,10 @@ use std::path::Path;
|
||||
use std::{fmt, slice, vec};
|
||||
|
||||
use itertools::Itertools;
|
||||
use orchid_api::TStrv;
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::api;
|
||||
use crate::api_conv::{ApiEquiv, FromApi, ToApi};
|
||||
use crate::interner::{deintern, intern, InternMarker, Tok};
|
||||
|
||||
trait_set! {
|
||||
@@ -357,7 +357,7 @@ impl Sym {
|
||||
pub fn id(&self) -> NonZeroU64 { self.0.marker().get_id() }
|
||||
/// Extern the sym for editing
|
||||
pub fn to_vname(&self) -> VName { VName(self[..].to_vec()) }
|
||||
pub fn deintern(marker: TStrv) -> Sym {
|
||||
pub fn deintern(marker: api::TStrv) -> Sym {
|
||||
Self::from_tok(deintern(marker)).expect("Empty sequence found for serialized Sym")
|
||||
}
|
||||
}
|
||||
@@ -386,6 +386,17 @@ impl Deref for Sym {
|
||||
type Target = PathSlice;
|
||||
fn deref(&self) -> &Self::Target { self.borrow() }
|
||||
}
|
||||
impl ApiEquiv for Sym {
|
||||
type Api = api::TStrv;
|
||||
}
|
||||
impl<C> ToApi<C> for Sym {
|
||||
fn to_api(&self, ctx: &mut C) -> Self::Api { self.tok().to_api(ctx) }
|
||||
}
|
||||
impl<C> FromApi<C> for Sym {
|
||||
fn from_api(api: &Self::Api, ctx: &mut C) -> Self {
|
||||
Self::from_tok(Tok::from_api(api, ctx)).expect("Empty sequence found for serialized Sym")
|
||||
}
|
||||
}
|
||||
|
||||
/// An abstraction over tokenized vs non-tokenized names so that they can be
|
||||
/// handled together in datastructures. The names can never be empty
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::ops::Range;
|
||||
|
||||
use ordered_float::NotNan;
|
||||
use rust_decimal::Decimal;
|
||||
use num_traits::ToPrimitive;
|
||||
|
||||
use crate::error::{mk_err, OrcErr};
|
||||
use crate::intern;
|
||||
@@ -21,6 +22,16 @@ pub enum Numeric {
|
||||
impl Numeric {
|
||||
pub fn decimal(num: i64, scale: u32) -> Self { Self::Decimal(Decimal::new(num, scale)) }
|
||||
pub fn float(value: f64) -> Self { Self::Float(NotNan::new(value).unwrap()) }
|
||||
pub fn to_f64(self) -> NotNan<f64> {
|
||||
match self {
|
||||
Self::Float(f) => f,
|
||||
Self::Decimal(d) => {
|
||||
let f = d.to_f64().expect("This is apparently always possible");
|
||||
NotNan::new(f).expect("decimal was nan")
|
||||
},
|
||||
Self::Uint(i) => NotNan::new(i as f64).expect("int cannot be NaN"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Rasons why [parse_num] might fail. See [NumError].
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
use std::iter;
|
||||
use std::ops::{Deref, Range};
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, iter};
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::error::{mk_err, OrcRes, Reporter};
|
||||
use crate::interner::{deintern, Tok};
|
||||
use crate::error::{mk_err, mk_errv, OrcRes, Reporter};
|
||||
use crate::interner::{deintern, intern, Tok};
|
||||
use crate::location::Pos;
|
||||
use crate::name::VPath;
|
||||
use crate::tree::{AtomInTok, Paren, TokTree, Token};
|
||||
use crate::tree::{AtomTok, ExtraTok, Paren, TokTree, Token};
|
||||
use crate::{api, intern};
|
||||
|
||||
pub fn name_start(c: char) -> bool { c.is_alphabetic() || c == '_' }
|
||||
@@ -17,11 +16,11 @@ pub fn op_char(c: char) -> bool { !name_char(c) && !c.is_whitespace() && !"()[]{
|
||||
pub fn unrep_space(c: char) -> bool { c.is_whitespace() && !"\r\n".contains(c) }
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Snippet<'a, 'b, A: AtomInTok, X> {
|
||||
pub struct Snippet<'a, 'b, A: AtomTok, X: ExtraTok> {
|
||||
prev: &'a TokTree<'b, A, X>,
|
||||
cur: &'a [TokTree<'b, A, X>],
|
||||
}
|
||||
impl<'a, 'b, A: AtomInTok, X> Snippet<'a, 'b, A, X> {
|
||||
impl<'a, 'b, A: AtomTok, X: ExtraTok> Snippet<'a, 'b, A, X> {
|
||||
pub fn new(prev: &'a TokTree<'b, A, X>, cur: &'a [TokTree<'b, A, X>]) -> Self {
|
||||
Self { prev, cur }
|
||||
}
|
||||
@@ -44,6 +43,9 @@ impl<'a, 'b, A: AtomInTok, X> Snippet<'a, 'b, A, X> {
|
||||
pub fn pop_front(self) -> Option<(&'a TokTree<'b, A, X>, Self)> {
|
||||
self.cur.first().map(|r| (r, self.split_at(1).1))
|
||||
}
|
||||
pub fn pop_back(self) -> Option<(Self, &'a TokTree<'b, A, X>)> {
|
||||
self.cur.last().map(|r| (self.split_at(self.len() - 1).0, r))
|
||||
}
|
||||
pub fn split_once(self, f: impl FnMut(&Token<'b, A, X>) -> bool) -> Option<(Self, Self)> {
|
||||
let idx = self.find_idx(f)?;
|
||||
Some((self.split_at(idx).0, self.split_at(idx + 1).1))
|
||||
@@ -65,25 +67,25 @@ impl<'a, 'b, A: AtomInTok, X> Snippet<'a, 'b, A, X> {
|
||||
self.split_at(non_fluff_start.unwrap_or(self.len())).1
|
||||
}
|
||||
}
|
||||
impl<'a, 'b, A: AtomInTok, X> Copy for Snippet<'a, 'b, A, X> {}
|
||||
impl<'a, 'b, A: AtomInTok, X> Clone for Snippet<'a, 'b, A, X> {
|
||||
impl<'a, 'b, A: AtomTok, X: ExtraTok> Copy for Snippet<'a, 'b, A, X> {}
|
||||
impl<'a, 'b, A: AtomTok, X: ExtraTok> Clone for Snippet<'a, 'b, A, X> {
|
||||
fn clone(&self) -> Self { *self }
|
||||
}
|
||||
impl<'a, 'b, A: AtomInTok, X> Deref for Snippet<'a, 'b, A, X> {
|
||||
impl<'a, 'b, A: AtomTok, X: ExtraTok> Deref for Snippet<'a, 'b, A, X> {
|
||||
type Target = [TokTree<'b, A, X>];
|
||||
fn deref(&self) -> &Self::Target { self.cur }
|
||||
}
|
||||
|
||||
/// Remove tokens that aren't meaningful in expression context, such as comments
|
||||
/// or line breaks
|
||||
pub fn strip_fluff<'a, A: AtomInTok, X: Clone>(
|
||||
pub fn strip_fluff<'a, A: AtomTok, X: ExtraTok>(
|
||||
tt: &TokTree<'a, A, X>,
|
||||
) -> Option<TokTree<'a, A, X>> {
|
||||
let tok = match &tt.tok {
|
||||
Token::BR => return None,
|
||||
Token::Comment(_) => return None,
|
||||
Token::LambdaHead(arg) => Token::LambdaHead(arg.iter().filter_map(strip_fluff).collect()),
|
||||
Token::S(p, b) => Token::S(p.clone(), b.iter().filter_map(strip_fluff).collect()),
|
||||
Token::S(p, b) => Token::S(*p, b.iter().filter_map(strip_fluff).collect()),
|
||||
t => t.clone(),
|
||||
};
|
||||
Some(TokTree { tok, range: tt.range.clone() })
|
||||
@@ -91,13 +93,21 @@ pub fn strip_fluff<'a, A: AtomInTok, X: Clone>(
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Comment {
|
||||
pub text: Arc<String>,
|
||||
pub text: Tok<String>,
|
||||
pub pos: Pos,
|
||||
}
|
||||
impl Comment {
|
||||
pub fn from_api(api: &api::Comment) -> Self {
|
||||
Self { pos: Pos::from_api(&api.location), text: deintern(api.text) }
|
||||
}
|
||||
pub fn to_api(&self) -> api::Comment {
|
||||
api::Comment { location: self.pos.to_api(), text: self.text.marker() }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn line_items<'a, 'b, A: AtomInTok, X>(
|
||||
pub fn line_items<'a, 'b, A: AtomTok, X: ExtraTok>(
|
||||
snip: Snippet<'a, 'b, A, X>,
|
||||
) -> Vec<(Vec<Comment>, Snippet<'a, 'b, A, X>)> {
|
||||
) -> Vec<Parsed<'a, 'b, Vec<Comment>, A, X>> {
|
||||
let mut items = Vec::new();
|
||||
let mut comments = Vec::new();
|
||||
for mut line in snip.split(|t| matches!(t, Token::BR)) {
|
||||
@@ -109,72 +119,79 @@ pub fn line_items<'a, 'b, A: AtomInTok, X>(
|
||||
match line.find_idx(|t| !matches!(t, Token::Comment(_))) {
|
||||
None => comments.extend(line.cur),
|
||||
Some(i) => {
|
||||
let (cmts, line) = line.split_at(i);
|
||||
let (cmts, tail) = line.split_at(i);
|
||||
let comments = Vec::from_iter(comments.drain(..).chain(cmts.cur).map(|t| match &t.tok {
|
||||
Token::Comment(c) => Comment { text: c.clone(), pos: Pos::Range(t.range.clone()) },
|
||||
Token::Comment(c) => Comment { text: intern(&**c), pos: Pos::Range(t.range.clone()) },
|
||||
_ => unreachable!("All are comments checked above"),
|
||||
}));
|
||||
items.push((comments, line));
|
||||
items.push(Parsed { output: comments, tail });
|
||||
},
|
||||
}
|
||||
}
|
||||
items
|
||||
}
|
||||
|
||||
pub fn try_pop_no_fluff<'a, 'b, A: AtomInTok, X>(
|
||||
pub fn try_pop_no_fluff<'a, 'b, A: AtomTok, X: ExtraTok>(
|
||||
snip: Snippet<'a, 'b, A, X>,
|
||||
) -> OrcRes<(&'a TokTree<'b, A, X>, Snippet<'a, 'b, A, X>)> {
|
||||
snip.skip_fluff().pop_front().ok_or_else(|| {
|
||||
vec![mk_err(intern!(str: "Unexpected end"), "Pattern ends abruptly", [
|
||||
Pos::Range(snip.pos()).into()
|
||||
])]
|
||||
) -> ParseRes<'a, 'b, &'a TokTree<'b, A, X>, A, X> {
|
||||
snip.skip_fluff().pop_front().map(|(output, tail)| Parsed { output, tail }).ok_or_else(|| {
|
||||
mk_errv(
|
||||
intern!(str: "Unexpected end"),
|
||||
"Pattern ends abruptly",
|
||||
[Pos::Range(snip.pos()).into()],
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn expect_end(snip: Snippet<'_, '_, impl AtomInTok, impl Sized>) -> OrcRes<()> {
|
||||
pub fn expect_end(snip: Snippet<'_, '_, impl AtomTok, impl ExtraTok>) -> OrcRes<()> {
|
||||
match snip.skip_fluff().get(0) {
|
||||
Some(surplus) => Err(vec![mk_err(
|
||||
Some(surplus) => Err(mk_errv(
|
||||
intern!(str: "Extra code after end of line"),
|
||||
"Code found after the end of the line",
|
||||
[Pos::Range(surplus.range.clone()).into()],
|
||||
)]),
|
||||
)),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_tok<'a, 'b, A: AtomInTok, X: fmt::Display>(
|
||||
pub fn expect_tok<'a, 'b, A: AtomTok, X: ExtraTok>(
|
||||
snip: Snippet<'a, 'b, A, X>,
|
||||
tok: Tok<String>,
|
||||
) -> OrcRes<Snippet<'a, 'b, A, X>> {
|
||||
let (head, tail) = try_pop_no_fluff(snip)?;
|
||||
) -> ParseRes<'a, 'b, (), A, X> {
|
||||
let Parsed { output: head, tail } = try_pop_no_fluff(snip)?;
|
||||
match &head.tok {
|
||||
Token::Name(n) if *n == tok => Ok(tail),
|
||||
t => Err(vec![mk_err(
|
||||
Token::Name(n) if *n == tok => Ok(Parsed { output: (), tail }),
|
||||
t => Err(mk_errv(
|
||||
intern!(str: "Expected specific keyword"),
|
||||
format!("Expected {tok} but found {t}"),
|
||||
[Pos::Range(head.range.clone()).into()],
|
||||
)]),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_multiname<'a, 'b, A: AtomInTok, X: fmt::Display>(
|
||||
pub struct Parsed<'a, 'b, T, A: AtomTok, X: ExtraTok> {
|
||||
pub output: T,
|
||||
pub tail: Snippet<'a, 'b, A, X>,
|
||||
}
|
||||
|
||||
pub type ParseRes<'a, 'b, T, A, X> = OrcRes<Parsed<'a, 'b, T, A, X>>;
|
||||
|
||||
pub fn parse_multiname<'a, 'b, A: AtomTok, X: ExtraTok>(
|
||||
ctx: &impl Reporter,
|
||||
tail: Snippet<'a, 'b, A, X>,
|
||||
) -> OrcRes<(Vec<CompName>, Snippet<'a, 'b, A, X>)> {
|
||||
) -> ParseRes<'a, 'b, Vec<(Import, Pos)>, A, X> {
|
||||
let ret = rec(ctx, tail);
|
||||
#[allow(clippy::type_complexity)] // it's an internal function
|
||||
pub fn rec<'a, 'b, A: AtomInTok, X: fmt::Display>(
|
||||
pub fn rec<'a, 'b, A: AtomTok, X: ExtraTok>(
|
||||
ctx: &impl Reporter,
|
||||
tail: Snippet<'a, 'b, A, X>,
|
||||
) -> OrcRes<(Vec<(Vec<Tok<String>>, Option<Tok<String>>, Pos)>, Snippet<'a, 'b, A, X>)> {
|
||||
) -> ParseRes<'a, 'b, Vec<(Vec<Tok<String>>, Option<Tok<String>>, Pos)>, A, X> {
|
||||
let comma = intern!(str: ",");
|
||||
let globstar = intern!(str: "*");
|
||||
let (name, tail) = tail.skip_fluff().pop_front().ok_or_else(|| {
|
||||
vec![mk_err(
|
||||
intern!(str: "Expected name"),
|
||||
"Expected a name, a list of names, or a globstar.",
|
||||
[Pos::Range(tail.pos()).into()],
|
||||
)]
|
||||
mk_err(intern!(str: "Expected name"), "Expected a name, a list of names, or a globstar.", [
|
||||
Pos::Range(tail.pos()).into(),
|
||||
])
|
||||
})?;
|
||||
if let Some((Token::NS, tail)) = tail.skip_fluff().pop_front().map(|(tt, s)| (&tt.tok, s)) {
|
||||
let n = match &name.tok {
|
||||
@@ -184,25 +201,27 @@ pub fn parse_multiname<'a, 'b, A: AtomInTok, X: fmt::Display>(
|
||||
])),
|
||||
};
|
||||
match (rec(ctx, tail), n) {
|
||||
(Err(ev), n) => Err(Vec::from_iter(ev.into_iter().chain(n.err()))),
|
||||
(Ok((_, tail)), Err(e)) => {
|
||||
(Err(ev), n) => Err(ev.extended(n.err())),
|
||||
(Ok(Parsed { tail, .. }), Err(e)) => {
|
||||
ctx.report(e);
|
||||
Ok((vec![], tail))
|
||||
Ok(Parsed { output: vec![], tail })
|
||||
},
|
||||
(Ok((n, tail)), Ok(pre)) =>
|
||||
Ok((n.into_iter().update(|i| i.0.push(pre.clone())).collect_vec(), tail)),
|
||||
(Ok(Parsed { tail, output }), Ok(pre)) => Ok(Parsed {
|
||||
output: output.into_iter().update(|i| i.0.push(pre.clone())).collect_vec(),
|
||||
tail,
|
||||
}),
|
||||
}
|
||||
} else {
|
||||
let names = match &name.tok {
|
||||
let output = match &name.tok {
|
||||
Token::Name(ntok) => {
|
||||
let nopt = match ntok {
|
||||
n if *n == globstar => None,
|
||||
n if n.starts_with(op_char) =>
|
||||
return Err(vec![mk_err(
|
||||
return Err(mk_errv(
|
||||
intern!(str: "Unescaped operator in multiname"),
|
||||
"Operators in multinames should be enclosed in []",
|
||||
[Pos::Range(name.range.clone()).into()],
|
||||
)]),
|
||||
)),
|
||||
n => Some(n.clone()),
|
||||
};
|
||||
vec![(vec![], nopt, Pos::Range(name.range.clone()))]
|
||||
@@ -226,9 +245,9 @@ pub fn parse_multiname<'a, 'b, A: AtomInTok, X: fmt::Display>(
|
||||
let body = Snippet::new(name, b);
|
||||
for csent in body.split(|n| matches!(n, Token::Name(n) if *n == comma)) {
|
||||
match rec(ctx, csent) {
|
||||
Err(e) => e.into_iter().for_each(|e| ctx.report(e)),
|
||||
Ok((v, surplus)) => match surplus.get(0) {
|
||||
None => ok.extend(v),
|
||||
Err(e) => ctx.report(e),
|
||||
Ok(Parsed { output, tail }) => match tail.get(0) {
|
||||
None => ok.extend(output),
|
||||
Some(t) => ctx.report(mk_err(
|
||||
intern!(str: "Unexpected token in multiname group"),
|
||||
"Unexpected token. Likely missing a :: or , or wanted [] instead of ()",
|
||||
@@ -240,40 +259,39 @@ pub fn parse_multiname<'a, 'b, A: AtomInTok, X: fmt::Display>(
|
||||
ok
|
||||
},
|
||||
t =>
|
||||
return Err(vec![mk_err(
|
||||
return Err(mk_errv(
|
||||
intern!(str: "Unrecognized name end"),
|
||||
format!("Names cannot end with {t} tokens"),
|
||||
[Pos::Range(name.range.clone()).into()],
|
||||
)]),
|
||||
)),
|
||||
};
|
||||
Ok((names, tail))
|
||||
Ok(Parsed { output, tail })
|
||||
}
|
||||
}
|
||||
ret.map(|(i, tail)| {
|
||||
let i = Vec::from_iter((i.into_iter()).map(|(p, name, pos)| CompName {
|
||||
path: VPath::new(p.into_iter().rev()),
|
||||
name,
|
||||
pos,
|
||||
}));
|
||||
(i, tail)
|
||||
ret.map(|Parsed { output, tail }| {
|
||||
let output = (output.into_iter())
|
||||
.map(|(p, name, pos)| (Import { path: VPath::new(p.into_iter().rev()), name }, pos))
|
||||
.collect_vec();
|
||||
Parsed { output, tail }
|
||||
})
|
||||
}
|
||||
|
||||
/// A compound name, possibly ending with a globstar
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CompName {
|
||||
pub struct Import {
|
||||
pub path: VPath,
|
||||
pub name: Option<Tok<String>>,
|
||||
pub pos: Pos,
|
||||
}
|
||||
impl CompName {
|
||||
pub fn from_api(i: api::CompName) -> Self {
|
||||
Self {
|
||||
path: VPath::new(i.path.into_iter().map(deintern)),
|
||||
name: i.name.map(deintern),
|
||||
pos: Pos::from_api(&i.location),
|
||||
}
|
||||
}
|
||||
impl Import {
|
||||
// pub fn from_api(i: api::CompName) -> Self {
|
||||
// Self { path: VPath::new(i.path.into_iter().map(deintern)), name: i.name.map(deintern) }
|
||||
// }
|
||||
// pub fn to_api(&self) -> api::CompName {
|
||||
// api::CompName {
|
||||
// path: self.path.iter().map(|t| t.marker()).collect(),
|
||||
// name: self.name.as_ref().map(|t| t.marker()),
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -282,6 +300,14 @@ mod test {
|
||||
|
||||
use super::Snippet;
|
||||
|
||||
fn _covary_snip_a<'a, 'b>(x: Snippet<'static, 'b, Never, ()>) -> Snippet<'a, 'b, Never, ()> { x }
|
||||
fn _covary_snip_b<'a, 'b>(x: Snippet<'a, 'static, Never, ()>) -> Snippet<'a, 'b, Never, ()> { x }
|
||||
fn _covary_snip_a<'a, 'b>(
|
||||
x: Snippet<'static, 'b, Never, Never>,
|
||||
) -> Snippet<'a, 'b, Never, Never> {
|
||||
x
|
||||
}
|
||||
fn _covary_snip_b<'a, 'b>(
|
||||
x: Snippet<'a, 'static, Never, Never>,
|
||||
) -> Snippet<'a, 'b, Never, Never> {
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
35
orchid-base/src/pure_seq.rs
Normal file
35
orchid-base/src/pure_seq.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
//! Methods to operate on Rust vectors in a declarative manner
|
||||
|
||||
use std::iter;
|
||||
|
||||
/// Pure version of [Vec::push]
|
||||
///
|
||||
/// Create a new vector consisting of the provided vector with the
|
||||
/// element appended. See [pushed_ref] to use it with a slice
|
||||
pub fn pushed<I: IntoIterator, C: FromIterator<I::Item>>(vec: I, t: I::Item) -> C {
|
||||
vec.into_iter().chain(iter::once(t)).collect()
|
||||
}
|
||||
|
||||
/// Pure version of [Vec::push]
|
||||
///
|
||||
/// Create a new vector consisting of the provided slice with the
|
||||
/// element appended. See [pushed] for the owned version
|
||||
pub fn pushed_ref<'a, T: Clone + 'a, C: FromIterator<T>>(
|
||||
vec: impl IntoIterator<Item = &'a T>,
|
||||
t: T,
|
||||
) -> C {
|
||||
vec.into_iter().cloned().chain(iter::once(t)).collect()
|
||||
}
|
||||
|
||||
/// Push an element on the adhoc stack, pass it to the callback, then pop the
|
||||
/// element out again.
|
||||
pub fn with_pushed<T, U>(
|
||||
vec: &mut Vec<T>,
|
||||
item: T,
|
||||
cb: impl for<'a> FnOnce(&'a mut Vec<T>) -> U,
|
||||
) -> (T, U) {
|
||||
vec.push(item);
|
||||
let out = cb(vec);
|
||||
let item = vec.pop().expect("top element stolen by callback");
|
||||
(item, out)
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{BitAnd, Deref};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
@@ -5,17 +7,24 @@ use std::sync::mpsc::{sync_channel, SyncSender};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{mem, thread};
|
||||
|
||||
use derive_destructure::destructure;
|
||||
use dyn_clone::{clone_box, DynClone};
|
||||
use hashbrown::HashMap;
|
||||
use orchid_api_traits::{Channel, Coding, Decode, Encode, MsgSet, Request};
|
||||
use trait_set::trait_set;
|
||||
|
||||
pub struct ReplyToken;
|
||||
pub struct Receipt;
|
||||
impl Receipt {
|
||||
pub fn off_thread(name: String, cb: impl FnOnce() -> Self + Send + 'static) -> Self {
|
||||
thread::Builder::new().name(name).spawn(cb).unwrap();
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
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>) -> ReplyToken + DynClone + Send + Sync + 'static;
|
||||
FnMut(RequestHandle<T>, <T::In as Channel>::Req) -> Receipt + DynClone + Send + Sync + 'static;
|
||||
pub trait NotifFn<T: MsgSet> =
|
||||
for<'a> FnMut(<T::In as Channel>::Notif, ReqNot<T>) + DynClone + Send + Sync + 'static;
|
||||
}
|
||||
@@ -24,29 +33,39 @@ fn get_id(message: &[u8]) -> (u64, &[u8]) {
|
||||
(u64::from_be_bytes(message[..8].to_vec().try_into().unwrap()), &message[8..])
|
||||
}
|
||||
|
||||
pub struct RequestHandle<T: MsgSet> {
|
||||
id: u64,
|
||||
message: <T::In as Channel>::Req,
|
||||
parent: ReqNot<T>,
|
||||
pub trait ReqHandlish {
|
||||
fn defer_drop(&self, val: impl Any + 'static);
|
||||
}
|
||||
|
||||
#[derive(destructure)]
|
||||
pub struct RequestHandle<MS: MsgSet> {
|
||||
defer_drop: RefCell<Vec<Box<dyn Any>>>,
|
||||
fulfilled: AtomicBool,
|
||||
id: u64,
|
||||
parent: ReqNot<MS>,
|
||||
}
|
||||
impl<MS: MsgSet + 'static> RequestHandle<MS> {
|
||||
fn new(parent: ReqNot<MS>, id: u64) -> Self {
|
||||
Self { defer_drop: RefCell::default(), fulfilled: false.into(), parent, id }
|
||||
}
|
||||
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) -> ReplyToken {
|
||||
pub fn handle<U: Request>(&self, _: &U, rep: &U::Response) -> Receipt { self.respond(rep) }
|
||||
pub fn will_handle_as<U: Request>(&self, _: &U) -> ReqTypToken<U> { ReqTypToken(PhantomData) }
|
||||
pub fn handle_as<U: Request>(&self, _: ReqTypToken<U>, rep: &U::Response) -> Receipt {
|
||||
self.respond(rep)
|
||||
}
|
||||
pub fn respond(&self, response: &impl Encode) -> Receipt {
|
||||
assert!(!self.fulfilled.swap(true, Ordering::Relaxed), "Already responded to {}", self.id);
|
||||
let mut buf = (!self.id).to_be_bytes().to_vec();
|
||||
response.encode(&mut buf);
|
||||
let mut send = clone_box(&*self.reqnot().0.lock().unwrap().send);
|
||||
(send)(&buf, self.parent.clone());
|
||||
ReplyToken
|
||||
}
|
||||
pub fn handle<T: Request>(&self, _: &T, rep: &T::Response) -> ReplyToken { 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) -> ReplyToken {
|
||||
self.respond(rep)
|
||||
Receipt
|
||||
}
|
||||
}
|
||||
impl<MS: MsgSet> ReqHandlish for RequestHandle<MS> {
|
||||
fn defer_drop(&self, val: impl Any) { self.defer_drop.borrow_mut().push(Box::new(val)) }
|
||||
}
|
||||
impl<MS: MsgSet> Drop for RequestHandle<MS> {
|
||||
fn drop(&mut self) {
|
||||
let done = self.fulfilled.load(Ordering::Relaxed);
|
||||
@@ -56,10 +75,6 @@ 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))
|
||||
}
|
||||
|
||||
pub struct ReqNotData<T: MsgSet> {
|
||||
id: u64,
|
||||
send: Box<dyn SendFn<T>>,
|
||||
@@ -104,8 +119,11 @@ impl<T: MsgSet> ReqNot<T> {
|
||||
let message = <T::In as Channel>::Req::decode(&mut &payload[..]);
|
||||
let mut req = clone_box(&*g.req);
|
||||
mem::drop(g);
|
||||
let handle = RequestHandle { id, message, fulfilled: false.into(), parent: self.clone() };
|
||||
thread::Builder::new().name(format!("request {id}")).spawn(move || req(handle)).unwrap();
|
||||
let rn = self.clone();
|
||||
thread::Builder::new()
|
||||
.name(format!("request {id}"))
|
||||
.spawn(move || req(RequestHandle::new(rn, id), message))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,12 +226,12 @@ mod test {
|
||||
let receiver = ReqNot::<TestMsgSet>::new(
|
||||
|_, _| panic!("Should not send anything"),
|
||||
clone!(received; move |notif, _| *received.lock().unwrap() = Some(notif)),
|
||||
|_| panic!("Not receiving a request"),
|
||||
|_, _| panic!("Not receiving a request"),
|
||||
);
|
||||
let sender = ReqNot::<TestMsgSet>::new(
|
||||
clone!(receiver; move |d, _| receiver.receive(d.to_vec())),
|
||||
|_, _| panic!("Should not receive notif"),
|
||||
|_| panic!("Should not receive request"),
|
||||
|_, _| panic!("Should not receive request"),
|
||||
);
|
||||
sender.notify(3);
|
||||
assert_eq!(*received.lock().unwrap(), Some(3));
|
||||
@@ -230,7 +248,7 @@ mod test {
|
||||
move |d, _| receiver.lock().unwrap().as_ref().unwrap().receive(d.to_vec())
|
||||
},
|
||||
|_, _| panic!("Should not receive notif"),
|
||||
|_| panic!("Should not receive request"),
|
||||
|_, _| panic!("Should not receive request"),
|
||||
));
|
||||
*receiver.lock().unwrap() = Some(ReqNot::new(
|
||||
{
|
||||
@@ -238,9 +256,9 @@ mod test {
|
||||
move |d, _| sender.receive(d.to_vec())
|
||||
},
|
||||
|_, _| panic!("Not receiving notifs"),
|
||||
|req| {
|
||||
assert_eq!(req.req(), &TestReq(5));
|
||||
req.respond(&6u8)
|
||||
|hand, req| {
|
||||
assert_eq!(req, TestReq(5));
|
||||
hand.respond(&6u8)
|
||||
},
|
||||
));
|
||||
let response = sender.request(TestReq(5));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::{self, Display, Write};
|
||||
use std::fmt::{self, Debug, Display};
|
||||
use std::iter;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Range;
|
||||
@@ -8,26 +8,32 @@ use std::sync::Arc;
|
||||
|
||||
use itertools::Itertools;
|
||||
use never::Never;
|
||||
use orchid_api::Placeholder;
|
||||
use ordered_float::NotNan;
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::api;
|
||||
use crate::error::OrcErr;
|
||||
use crate::error::OrcErrv;
|
||||
use crate::interner::{deintern, Tok};
|
||||
use crate::name::{NameLike, VName};
|
||||
use crate::name::PathSlice;
|
||||
use crate::parse::Snippet;
|
||||
use crate::tokens::PARENS;
|
||||
|
||||
pub use api::PhKind as PhKind;
|
||||
|
||||
trait_set! {
|
||||
pub trait RecurCB<'a, A: AtomInTok, X> = Fn(TokTree<'a, A, X>) -> TokTree<'a, A, X>;
|
||||
pub trait RecurCB<'a, A: AtomTok, X: ExtraTok> = Fn(TokTree<'a, A, X>) -> TokTree<'a, A, X>;
|
||||
pub trait ExtraTok = Display + Clone + fmt::Debug;
|
||||
}
|
||||
|
||||
pub fn recur<'a, A: AtomInTok, X>(
|
||||
pub fn recur<'a, A: AtomTok, X: ExtraTok>(
|
||||
tt: TokTree<'a, A, X>,
|
||||
f: &impl Fn(TokTree<'a, A, X>, &dyn RecurCB<'a, A, X>) -> TokTree<'a, A, X>,
|
||||
) -> TokTree<'a, A, X> {
|
||||
f(tt, &|TokTree { range, tok }| {
|
||||
let tok = match tok {
|
||||
tok @ (Token::Atom(_) | Token::BR | Token::Bottom(_) | Token::Comment(_) | Token::NS) => tok,
|
||||
tok @ (Token::Name(_) | Token::Slot(_) | Token::X(_)) => tok,
|
||||
tok @ (Token::Name(_) | Token::Slot(_) | Token::X(_) | Token::Ph(_) | Token::Macro(_)) => tok,
|
||||
Token::LambdaHead(arg) =>
|
||||
Token::LambdaHead(arg.into_iter().map(|tt| recur(tt, f)).collect_vec()),
|
||||
Token::S(p, b) => Token::S(p, b.into_iter().map(|tt| recur(tt, f)).collect_vec()),
|
||||
@@ -36,46 +42,48 @@ pub fn recur<'a, A: AtomInTok, X>(
|
||||
})
|
||||
}
|
||||
|
||||
pub trait AtomInTok: Display + Clone {
|
||||
pub trait AtomTok: fmt::Display + Clone + fmt::Debug {
|
||||
type Context: ?Sized;
|
||||
fn from_api(atom: &api::Atom, pos: Range<u32>, ctx: &mut Self::Context) -> Self;
|
||||
fn to_api(&self) -> api::Atom;
|
||||
}
|
||||
impl AtomInTok for Never {
|
||||
impl AtomTok for Never {
|
||||
type Context = Never;
|
||||
fn from_api(_: &api::Atom, _: Range<u32>, _: &mut Self::Context) -> Self { panic!() }
|
||||
fn to_api(&self) -> orchid_api::Atom { match *self {} }
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct TreeHandle<'a>(api::TreeTicket, PhantomData<&'a ()>);
|
||||
impl TreeHandle<'static> {
|
||||
pub fn new(tt: api::TreeTicket) -> Self { TreeHandle(tt, PhantomData) }
|
||||
pub struct TokHandle<'a>(api::TreeTicket, PhantomData<&'a ()>);
|
||||
impl TokHandle<'static> {
|
||||
pub fn new(tt: api::TreeTicket) -> Self { TokHandle(tt, PhantomData) }
|
||||
}
|
||||
impl<'a> TreeHandle<'a> {
|
||||
impl<'a> TokHandle<'a> {
|
||||
pub fn ticket(self) -> api::TreeTicket { self.0 }
|
||||
}
|
||||
impl<'a> Display for TreeHandle<'a> {
|
||||
impl<'a> Display for TokHandle<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Handle({})", self.0.0) }
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TokTree<'a, A: AtomInTok, X> {
|
||||
pub struct TokTree<'a, A: AtomTok, X: ExtraTok> {
|
||||
pub tok: Token<'a, A, X>,
|
||||
pub range: Range<u32>,
|
||||
}
|
||||
impl<'a, A: AtomInTok, X> TokTree<'a, A, X> {
|
||||
impl<'a, A: AtomTok, X: ExtraTok> TokTree<'a, A, X> {
|
||||
pub fn from_api(tt: &api::TokenTree, ctx: &mut A::Context) -> Self {
|
||||
let tok = match &tt.token {
|
||||
api::Token::Atom(a) => Token::Atom(A::from_api(a, tt.range.clone(), ctx)),
|
||||
api::Token::BR => Token::BR,
|
||||
api::Token::NS => Token::NS,
|
||||
api::Token::Bottom(e) => Token::Bottom(e.iter().map(OrcErr::from_api).collect()),
|
||||
api::Token::Lambda(arg) => Token::LambdaHead(ttv_from_api(arg, ctx)),
|
||||
api::Token::Bottom(e) => Token::Bottom(OrcErrv::from_api(e)),
|
||||
api::Token::LambdaHead(arg) => Token::LambdaHead(ttv_from_api(arg, ctx)),
|
||||
api::Token::Name(name) => Token::Name(deintern(*name)),
|
||||
api::Token::S(par, b) => Token::S(par.clone(), ttv_from_api(b, ctx)),
|
||||
api::Token::S(par, b) => Token::S(*par, ttv_from_api(b, ctx)),
|
||||
api::Token::Comment(c) => Token::Comment(c.clone()),
|
||||
api::Token::Slot(id) => Token::Slot(TreeHandle::new(*id)),
|
||||
api::Token::Slot(id) => Token::Slot(TokHandle::new(*id)),
|
||||
api::Token::Ph(ph) => Token::Ph(Ph {name: deintern(ph.name), kind: ph.kind }),
|
||||
api::Token::Macro(prio) => Token::Macro(*prio)
|
||||
};
|
||||
Self { range: tt.range.clone(), tok }
|
||||
}
|
||||
@@ -88,66 +96,110 @@ impl<'a, A: AtomInTok, X> TokTree<'a, A, X> {
|
||||
Token::Atom(a) => api::Token::Atom(a.to_api()),
|
||||
Token::BR => api::Token::BR,
|
||||
Token::NS => api::Token::NS,
|
||||
Token::Bottom(e) => api::Token::Bottom(e.iter().map(OrcErr::to_api).collect()),
|
||||
Token::Bottom(e) => api::Token::Bottom(e.to_api()),
|
||||
Token::Comment(c) => api::Token::Comment(c.clone()),
|
||||
Token::LambdaHead(arg) =>
|
||||
api::Token::Lambda(arg.iter().map(|t| t.to_api(do_extra)).collect_vec()),
|
||||
Token::LambdaHead(arg) => api::Token::LambdaHead(ttv_to_api(arg, do_extra)),
|
||||
Token::Name(n) => api::Token::Name(n.marker()),
|
||||
Token::Slot(tt) => api::Token::Slot(tt.ticket()),
|
||||
Token::S(p, b) => api::Token::S(p.clone(), b.iter().map(|t| t.to_api(do_extra)).collect()),
|
||||
Token::S(p, b) => api::Token::S(*p, ttv_to_api(b, do_extra)),
|
||||
Token::Ph(Ph { name, kind }) => api::Token::Ph(Placeholder { name: name.marker(), kind: *kind }),
|
||||
Token::X(x) => return do_extra(x, self.range.clone()),
|
||||
Token::Macro(prio) => api::Token::Macro(*prio),
|
||||
};
|
||||
api::TokenTree { range: self.range.clone(), token }
|
||||
}
|
||||
|
||||
pub fn into_api(
|
||||
self,
|
||||
do_extra: &mut impl FnMut(X, Range<u32>) -> api::TokenTree,
|
||||
) -> api::TokenTree {
|
||||
let token = match self.tok {
|
||||
Token::Atom(a) => api::Token::Atom(a.to_api()),
|
||||
Token::BR => api::Token::BR,
|
||||
Token::NS => api::Token::NS,
|
||||
Token::Bottom(e) => api::Token::Bottom(e.to_api()),
|
||||
Token::Comment(c) => api::Token::Comment(c.clone()),
|
||||
Token::LambdaHead(arg) => api::Token::LambdaHead(ttv_into_api(arg, do_extra)),
|
||||
Token::Name(n) => api::Token::Name(n.marker()),
|
||||
Token::Slot(tt) => api::Token::Slot(tt.ticket()),
|
||||
Token::S(p, b) => api::Token::S(p, ttv_into_api(b, do_extra)),
|
||||
Token::Ph(Ph { kind, name }) => api::Token::Ph(Placeholder { name: name.marker(), kind }),
|
||||
Token::X(x) => return do_extra(x, self.range.clone()),
|
||||
Token::Macro(prio) => api::Token::Macro(prio),
|
||||
};
|
||||
api::TokenTree { range: self.range.clone(), token }
|
||||
}
|
||||
|
||||
pub fn is_kw(&self, tk: Tok<String>) -> bool { self.tok.is_kw(tk) }
|
||||
pub fn as_name(&self) -> Option<Tok<String>> {
|
||||
if let Token::Name(n) = &self.tok { Some(n.clone()) } else { None }
|
||||
}
|
||||
pub fn as_s(&self, par: Paren) -> Option<Snippet<'_, 'a, A, X>> {
|
||||
self.tok.as_s(par).map(|slc| Snippet::new(self, slc))
|
||||
}
|
||||
pub fn lambda(arg: Vec<Self>, mut body: Vec<Self>) -> Self {
|
||||
let arg_range = ttv_range(&arg);
|
||||
let s_range = arg_range.start..body.last().expect("Lambda with empty body!").range.end;
|
||||
body.insert(0, Token::LambdaHead(arg).at(arg_range));
|
||||
Token::S(Paren::Round, body).at(s_range)
|
||||
}
|
||||
}
|
||||
impl<'a, A: AtomInTok + Display, X: Display> Display for TokTree<'a, A, X> {
|
||||
|
||||
impl<'a, A: AtomTok, X: ExtraTok> Display for TokTree<'a, A, X> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.tok) }
|
||||
}
|
||||
|
||||
pub fn ttv_from_api<A: AtomInTok, X>(
|
||||
pub fn ttv_from_api<A: AtomTok, X: ExtraTok>(
|
||||
tokv: impl IntoIterator<Item: Borrow<api::TokenTree>>,
|
||||
ctx: &mut A::Context,
|
||||
) -> Vec<TokTree<'static, A, X>> {
|
||||
tokv.into_iter().map(|t| TokTree::<A, X>::from_api(t.borrow(), ctx)).collect()
|
||||
}
|
||||
|
||||
pub fn ttv_to_api<'a, A: AtomInTok, X>(
|
||||
pub fn ttv_to_api<'a, A: AtomTok, X: ExtraTok>(
|
||||
tokv: impl IntoIterator<Item: Borrow<TokTree<'a, A, X>>>,
|
||||
do_extra: &mut impl FnMut(&X, Range<u32>) -> api::TokenTree,
|
||||
) -> Vec<api::TokenTree> {
|
||||
tokv
|
||||
.into_iter()
|
||||
.map(|tok| {
|
||||
let tt: &TokTree<A, X> = tok.borrow();
|
||||
tt.to_api(do_extra)
|
||||
})
|
||||
.collect_vec()
|
||||
tokv.into_iter().map(|tok| Borrow::<TokTree<A, X>>::borrow(&tok).to_api(do_extra)).collect_vec()
|
||||
}
|
||||
|
||||
pub fn vname_tv<'a: 'b, 'b, A: AtomInTok + 'a, X: 'a>(
|
||||
name: &'b VName,
|
||||
ran: Range<u32>,
|
||||
pub fn ttv_into_api<'a, A: AtomTok, X: ExtraTok>(
|
||||
tokv: impl IntoIterator<Item = TokTree<'a, A, X>>,
|
||||
do_extra: &mut impl FnMut(X, Range<u32>) -> api::TokenTree,
|
||||
) -> Vec<api::TokenTree> {
|
||||
tokv.into_iter().map(|t| t.into_api(do_extra)).collect_vec()
|
||||
}
|
||||
|
||||
/// This takes a position and not a range because it assigns the range to
|
||||
/// multiple leaf tokens, which is only valid if it's a zero-width range
|
||||
pub fn vname_tv<'a: 'b, 'b, A: AtomTok + 'a, X: ExtraTok + 'a>(
|
||||
name: &'b PathSlice,
|
||||
pos: u32,
|
||||
) -> impl Iterator<Item = TokTree<'a, A, X>> + 'b {
|
||||
let (head, tail) = name.split_first();
|
||||
iter::once(Token::Name(head))
|
||||
.chain(tail.iter().flat_map(|t| [Token::NS, Token::Name(t)]))
|
||||
.map(move |t| t.at(ran.clone()))
|
||||
let (head, tail) = name.split_first().expect("Empty vname");
|
||||
iter::once(Token::Name(head.clone()))
|
||||
.chain(tail.iter().flat_map(|t| [Token::NS, Token::Name(t.clone())]))
|
||||
.map(move |t| t.at(pos..pos))
|
||||
}
|
||||
|
||||
pub fn wrap_tokv<'a, A: AtomInTok + 'a, X: 'a>(
|
||||
items: Vec<TokTree<'a, A, X>>,
|
||||
range: Range<u32>,
|
||||
pub fn wrap_tokv<'a, A: AtomTok, X: ExtraTok>(
|
||||
items: impl IntoIterator<Item = TokTree<'a, A, X>>
|
||||
) -> TokTree<'a, A, X> {
|
||||
match items.len() {
|
||||
1 => items.into_iter().next().unwrap(),
|
||||
_ => Token::S(api::Paren::Round, items).at(range),
|
||||
let items_v = items.into_iter().collect_vec();
|
||||
match items_v.len() {
|
||||
0 => panic!("A tokv with no elements is illegal"),
|
||||
1 => items_v.into_iter().next().unwrap(),
|
||||
_ => {
|
||||
let range = items_v.first().unwrap().range.start..items_v.last().unwrap().range.end;
|
||||
Token::S(api::Paren::Round, items_v).at(range)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub use api::Paren;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Token<'a, A: AtomInTok, X> {
|
||||
pub enum Token<'a, A: AtomTok, X: ExtraTok> {
|
||||
Comment(Arc<String>),
|
||||
LambdaHead(Vec<TokTree<'a, A, X>>),
|
||||
Name(Tok<String>),
|
||||
@@ -155,14 +207,25 @@ pub enum Token<'a, A: AtomInTok, X> {
|
||||
BR,
|
||||
S(Paren, Vec<TokTree<'a, A, X>>),
|
||||
Atom(A),
|
||||
Bottom(Vec<OrcErr>),
|
||||
Slot(TreeHandle<'a>),
|
||||
Bottom(OrcErrv),
|
||||
Slot(TokHandle<'a>),
|
||||
X(X),
|
||||
Ph(Ph),
|
||||
Macro(Option<NotNan<f64>>),
|
||||
}
|
||||
impl<'a, A: AtomInTok, X> Token<'a, A, X> {
|
||||
impl<'a, A: AtomTok, X: ExtraTok> Token<'a, A, X> {
|
||||
pub fn at(self, range: Range<u32>) -> TokTree<'a, A, X> { TokTree { range, tok: self } }
|
||||
pub fn is_kw(&self, tk: Tok<String>) -> bool {
|
||||
matches!(self, Token::Name(n) if *n == tk)
|
||||
}
|
||||
pub fn as_s(&self, par: Paren) -> Option<&[TokTree<'a, A, X>]> {
|
||||
match self {
|
||||
Self::S(p, b) if *p == par => Some(b),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a, A: AtomInTok + Display, X: Display> Display for Token<'a, A, X> {
|
||||
impl<'a, A: AtomTok, X: ExtraTok> Display for Token<'a, A, X> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
thread_local! {
|
||||
static PAREN_LEVEL: RefCell<usize> = 0.into();
|
||||
@@ -175,33 +238,47 @@ impl<'a, A: AtomInTok + Display, X: Display> Display for Token<'a, A, X> {
|
||||
r
|
||||
}
|
||||
match self {
|
||||
Self::Atom(a) => f.write_str(&indent(&format!("{a}"), get_indent(), false)),
|
||||
Self::Atom(a) => f.write_str(&indent(&format!("{a} "), get_indent(), false)),
|
||||
Self::BR => write!(f, "\n{}", " ".repeat(get_indent())),
|
||||
Self::Bottom(err) => write!(
|
||||
f,
|
||||
"Botttom({})",
|
||||
err.iter().map(|e| format!("{}: {}", e.description, e.message)).join(", ")
|
||||
),
|
||||
Self::Comment(c) => write!(f, "--[{c}]--"),
|
||||
Self::LambdaHead(arg) => with_indent(|| write!(f, "\\ {} .", ttv_fmt(arg))),
|
||||
Self::NS => f.write_str("::"),
|
||||
Self::Name(n) => f.write_str(n),
|
||||
Self::Slot(th) => write!(f, "{th}"),
|
||||
Self::Bottom(err) if err.len() == 1 => write!(f, "Bottom({}) ", err.one().unwrap()),
|
||||
Self::Bottom(err) => {
|
||||
write!(f, "Botttom(\n{}) ", indent(&err.to_string(), get_indent() + 1, true))
|
||||
},
|
||||
Self::Comment(c) => write!(f, "--[{c}]-- "),
|
||||
Self::LambdaHead(arg) => with_indent(|| write!(f, "\\ {} . ", ttv_fmt(arg))),
|
||||
Self::NS => f.write_str(":: "),
|
||||
Self::Name(n) => write!(f, "{n} "),
|
||||
Self::Slot(th) => write!(f, "{th} "),
|
||||
Self::Ph(Ph { kind, name }) => match &kind {
|
||||
PhKind::Scalar => write!(f, "${name}"),
|
||||
PhKind::Vector { at_least_one, priority } => {
|
||||
if *at_least_one { write!(f, ".")? }
|
||||
write!(f, "..${name}")?;
|
||||
if 0 < *priority { write!(f, "{priority}") } else { Ok(()) }
|
||||
}
|
||||
}
|
||||
Self::S(p, b) => {
|
||||
let (lp, rp, _) = PARENS.iter().find(|(_, _, par)| par == p).unwrap();
|
||||
f.write_char(*lp)?;
|
||||
write!(f, "{lp} ")?;
|
||||
with_indent(|| f.write_str(&ttv_fmt(b)))?;
|
||||
f.write_char(*rp)
|
||||
write!(f, "{rp} ")
|
||||
},
|
||||
Self::X(x) => write!(f, "{x}"),
|
||||
Self::X(x) => write!(f, "{x} "),
|
||||
Self::Macro(None) => write!(f, "macro "),
|
||||
Self::Macro(Some(prio)) => write!(f, "macro({prio})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ttv_fmt<'a>(
|
||||
ttv: impl IntoIterator<Item = &'a TokTree<'a, impl AtomInTok + 'a, impl Display + 'a>>,
|
||||
pub fn ttv_range(ttv: &[TokTree<'_, impl AtomTok, impl ExtraTok>]) -> Range<u32> {
|
||||
assert!(!ttv.is_empty(), "Empty slice has no range");
|
||||
ttv.first().unwrap().range.start..ttv.last().unwrap().range.end
|
||||
}
|
||||
|
||||
pub fn ttv_fmt<'a: 'b, 'b>(
|
||||
ttv: impl IntoIterator<Item = &'b TokTree<'a, impl AtomTok + 'b, impl ExtraTok + 'b>>,
|
||||
) -> String {
|
||||
ttv.into_iter().join(" ")
|
||||
ttv.into_iter().join("")
|
||||
}
|
||||
|
||||
pub fn indent(s: &str, lvl: usize, first: bool) -> String {
|
||||
@@ -214,13 +291,23 @@ pub fn indent(s: &str, lvl: usize, first: bool) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Ph {
|
||||
pub name: Tok<String>,
|
||||
pub kind: PhKind,
|
||||
}
|
||||
impl Ph {
|
||||
pub fn from_api(api: &Placeholder) -> Self { Self { name: deintern(api.name), kind: api.kind } }
|
||||
pub fn to_api(&self) -> Placeholder { Placeholder { name: self.name.marker(), kind: self.kind } }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_covariance() {
|
||||
fn _f<'a>(x: Token<'static, Never, ()>) -> Token<'a, Never, ()> { x }
|
||||
fn _f<'a>(x: Token<'static, Never, Never>) -> Token<'a, Never, Never> { x }
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user