commit before easter break

This commit is contained in:
2025-04-15 00:34:45 +02:00
parent f783445a76
commit 94958bfbf5
85 changed files with 1874 additions and 2189 deletions

View File

@@ -169,20 +169,16 @@ pub fn mk_errv(
mk_err(description, message, posv).into()
}
pub trait Reporter {
fn report(&self, e: impl Into<OrcErrv>);
}
pub struct ReporterImpl {
pub struct Reporter {
errors: RefCell<Vec<OrcErr>>,
}
impl ReporterImpl {
impl Reporter {
pub fn report(&self, e: impl Into<OrcErrv>) { self.errors.borrow_mut().extend(e.into()) }
pub fn new() -> Self { Self { errors: RefCell::new(vec![]) } }
pub fn errv(self) -> Option<OrcErrv> { OrcErrv::new(self.errors.into_inner()).ok() }
}
impl Reporter for ReporterImpl {
fn report(&self, e: impl Into<OrcErrv>) { self.errors.borrow_mut().extend(e.into()) }
}
impl Default for ReporterImpl {
impl Default for Reporter {
fn default() -> Self { Self::new() }
}

View File

@@ -270,3 +270,6 @@ pub trait Format {
impl Format for Never {
async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { match *self {} }
}
/// Format with default strategy. Currently equal to [take_first_fmt]
pub async fn fmt(v: &(impl Format + ?Sized), i: &Interner) -> String { take_first_fmt(v, i).await }

View File

@@ -15,7 +15,6 @@ pub mod interner;
pub mod join;
pub mod location;
pub mod logging;
pub mod macros;
mod match_mapping;
pub mod msg;
pub mod name;

View File

@@ -1,167 +0,0 @@
use std::marker::PhantomData;
use std::rc::Rc;
use std::sync::Arc;
use async_stream::stream;
use futures::future::join_all;
use futures::{FutureExt, StreamExt};
use never::Never;
use trait_set::trait_set;
use crate::format::{FmtCtx, FmtUnit, Format, Variants};
use crate::interner::Interner;
use crate::location::Pos;
use crate::name::Sym;
use crate::tree::{Paren, Ph};
use crate::{api, match_mapping, tl_cache};
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct MacroSlot<'a>(api::MacroTreeId, PhantomData<&'a ()>);
impl MacroSlot<'_> {
pub fn id(self) -> api::MacroTreeId { self.0 }
}
trait_set! {
pub trait MacroAtomToApi<A> = AsyncFnMut(&A) -> api::MacroToken;
pub trait MacroAtomFromApi<'a, A> = AsyncFnMut(&api::Atom) -> MTok<'a, A>;
}
#[derive(Clone, Debug)]
pub struct MTree<'a, A> {
pub pos: Pos,
pub tok: Rc<MTok<'a, A>>,
}
impl<'a, A> MTree<'a, A> {
pub(crate) async fn from_api(
api: &api::MacroTree,
do_atom: &mut impl MacroAtomFromApi<'a, A>,
i: &Interner,
) -> Self {
Self {
pos: Pos::from_api(&api.location, i).await,
tok: Rc::new(MTok::from_api(&api.token, i, do_atom).await),
}
}
pub(crate) async fn to_api(&self, do_atom: &mut impl MacroAtomToApi<A>) -> api::MacroTree {
api::MacroTree {
location: self.pos.to_api(),
token: self.tok.to_api(do_atom).boxed_local().await,
}
}
}
impl<A: Format> Format for MTree<'_, A> {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
self.tok.print(c).boxed_local().await
}
}
#[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),
/// Used in extensions to directly return input
Ref(Arc<MTok<'a, Never>>),
/// Used in the matcher to skip previous macro output which can only go in
/// vectorial placeholders
Done(Rc<MTok<'a, A>>),
}
impl<'a, A> MTok<'a, A> {
pub(crate) async fn from_api(
api: &api::MacroToken,
i: &Interner,
do_atom: &mut impl MacroAtomFromApi<'a, A>,
) -> Self {
match_mapping!(&api, api::MacroToken => MTok::<'a, A> {
Lambda(x => mtreev_from_api(x, i, do_atom).await, b => mtreev_from_api(b, i, do_atom).await),
Name(t => Sym::from_api(*t, i).await),
Slot(tk => MacroSlot(*tk, PhantomData)),
S(p.clone(), b => mtreev_from_api(b, i, do_atom).await),
Ph(ph => Ph::from_api(ph, i).await),
} {
api::MacroToken::Atom(a) => do_atom(a).await
})
}
pub(crate) async fn to_api(&self, do_atom: &mut impl MacroAtomToApi<A>) -> api::MacroToken {
async fn sink<T>(n: &Never) -> T { match *n {} }
match_mapping!(&self, MTok => api::MacroToken {
Lambda(x => mtreev_to_api(x, do_atom).await, b => mtreev_to_api(b, do_atom).await),
Name(t.tok().to_api()),
Ph(ph.to_api()),
S(p.clone(), b => mtreev_to_api(b, do_atom).await),
Slot(tk.0.clone()),
} {
MTok::Ref(r) => r.to_api(&mut sink).boxed_local().await,
MTok::Done(t) => t.to_api(do_atom).boxed_local().await,
MTok::Atom(a) => do_atom(a).await,
})
}
pub fn at(self, pos: Pos) -> MTree<'a, A> { MTree { pos, tok: Rc::new(self) } }
}
impl<A: Format> Format for MTok<'_, A> {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
match self {
Self::Atom(a) => a.print(c).await,
Self::Done(d) =>
FmtUnit::new(tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("(Done){0l}"))), [
d.print(c).boxed_local().await,
]),
Self::Lambda(arg, b) => FmtUnit::new(
tl_cache!(Rc<Variants>: Rc::new(Variants::default()
.unbounded("\\{0b}.{1l}")
.bounded("(\\{0b}.{1b})"))),
[mtreev_fmt(arg, c).await, mtreev_fmt(b, c).await],
),
Self::Name(n) => format!("{n}").into(),
Self::Ph(ph) => format!("{ph}").into(),
Self::Ref(r) =>
FmtUnit::new(tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("(ref){0l}"))), [
r.print(c).boxed_local().await,
]),
Self::S(p, body) => FmtUnit::new(
match *p {
Paren::Round => Rc::new(Variants::default().bounded("({0b})")),
Paren::Curly => Rc::new(Variants::default().bounded("{{0b}}")),
Paren::Square => Rc::new(Variants::default().bounded("[{0b}]")),
},
[mtreev_fmt(body, c).await],
),
Self::Slot(slot) => format!("{:?}", slot.0).into(),
}
}
}
pub async fn mtreev_from_api<'a, 'b, A>(
apiv: impl IntoIterator<Item = &'b api::MacroTree>,
i: &Interner,
do_atom: &'b mut (impl MacroAtomFromApi<'a, A> + 'b),
) -> Vec<MTree<'a, A>> {
stream! {
for api in apiv {
yield MTree::from_api(api, do_atom, i).boxed_local().await
}
}
.collect()
.await
}
pub async fn mtreev_to_api<'a: 'b, 'b, A: 'b>(
v: impl IntoIterator<Item = &'b MTree<'a, A>>,
do_atom: &mut impl MacroAtomToApi<A>,
) -> Vec<api::MacroTree> {
let mut out = Vec::new();
for t in v {
out.push(t.to_api(do_atom).await);
}
out
}
pub async fn mtreev_fmt<'a: 'b, 'b, A: 'b + Format>(
v: impl IntoIterator<Item = &'b MTree<'a, A>>,
c: &(impl FmtCtx + ?Sized),
) -> FmtUnit {
FmtUnit::sequence(" ", None, join_all(v.into_iter().map(|t| t.print(c))).await)
}

View File

@@ -2,11 +2,10 @@
use std::borrow::Borrow;
use std::hash::Hash;
use std::iter::Cloned;
use std::num::{NonZeroU64, NonZeroUsize};
use std::ops::{Bound, Deref, Index, RangeBounds};
use std::ops::{Deref, Index};
use std::path::Path;
use std::{fmt, slice, vec};
use std::{fmt, vec};
use futures::future::{OptionFuture, join_all};
use itertools::Itertools;

View File

@@ -2,16 +2,30 @@ use std::fmt::{self, Display};
use std::iter;
use std::ops::{Deref, Range};
use futures::FutureExt;
use futures::future::join_all;
use itertools::Itertools;
use crate::api;
use crate::error::{OrcRes, Reporter, mk_err, mk_errv};
use crate::format::{Format, take_first_fmt};
use crate::format::fmt;
use crate::interner::{Interner, Tok};
use crate::location::Pos;
use crate::name::{VName, VPath};
use crate::tree::{AtomRepr, ExtraTok, Paren, TokTree, Token};
use crate::name::VPath;
use crate::tree::{ExprRepr, ExtraTok, Paren, TokTree, Token};
pub trait ParseCtx {
fn i(&self) -> &Interner;
fn reporter(&self) -> &Reporter;
}
pub struct ParseCtxImpl<'a> {
pub i: &'a Interner,
pub r: &'a Reporter,
}
impl ParseCtx for ParseCtxImpl<'_> {
fn i(&self) -> &Interner { self.i }
fn reporter(&self) -> &Reporter { self.r }
}
pub fn name_start(c: char) -> bool { c.is_alphabetic() || c == '_' }
pub fn name_char(c: char) -> bool { name_start(c) || c.is_numeric() }
@@ -21,51 +35,44 @@ pub fn unrep_space(c: char) -> bool { c.is_whitespace() && !"\r\n".contains(c) }
/// A cheaply copiable subsection of a document that holds onto context data and
/// one token for error reporting on empty subsections.
#[derive(Debug)]
pub struct Snippet<'a, 'b, A: AtomRepr, X: ExtraTok> {
prev: &'a TokTree<'b, A, X>,
cur: &'a [TokTree<'b, A, X>],
interner: &'a Interner,
pub struct Snippet<'a, A: ExprRepr, X: ExtraTok> {
prev: &'a TokTree<A, X>,
cur: &'a [TokTree<A, X>],
}
impl<'a, 'b, A: AtomRepr, X: ExtraTok> Snippet<'a, 'b, A, X> {
pub fn new(
prev: &'a TokTree<'b, A, X>,
cur: &'a [TokTree<'b, A, X>],
interner: &'a Interner,
) -> Self {
Self { prev, cur, interner }
}
pub fn i(&self) -> &'a Interner { self.interner }
impl<'a, A, X> Snippet<'a, A, X>
where
A: ExprRepr,
X: ExtraTok,
{
pub fn new(prev: &'a TokTree<A, X>, cur: &'a [TokTree<A, X>]) -> Self { Self { prev, cur } }
pub fn split_at(self, pos: u32) -> (Self, Self) {
let Self { prev, cur, interner } = self;
let fst = Self { prev, cur: &cur[..pos as usize], interner };
let Self { prev, cur } = self;
let fst = Self { prev, cur: &cur[..pos as usize] };
let new_prev = if pos == 0 { self.prev } else { &self.cur[pos as usize - 1] };
let snd = Self { prev: new_prev, cur: &self.cur[pos as usize..], interner };
let snd = Self { prev: new_prev, cur: &self.cur[pos as usize..] };
(fst, snd)
}
pub fn find_idx(self, mut f: impl FnMut(&Token<'b, A, X>) -> bool) -> Option<u32> {
pub fn find_idx(self, mut f: impl FnMut(&Token<A, X>) -> bool) -> Option<u32> {
self.cur.iter().position(|t| f(&t.tok)).map(|t| t as u32)
}
pub fn get(self, idx: u32) -> Option<&'a TokTree<'b, A, X>> { self.cur.get(idx as usize) }
pub fn get(self, idx: u32) -> Option<&'a TokTree<A, X>> { self.cur.get(idx as usize) }
pub fn len(self) -> u32 { self.cur.len() as u32 }
pub fn prev(self) -> &'a TokTree<'b, A, X> { self.prev }
pub fn prev(self) -> &'a TokTree<A, X> { self.prev }
pub fn pos(self) -> Range<u32> {
(self.cur.first().map(|f| f.range.start..self.cur.last().unwrap().range.end))
.unwrap_or(self.prev.range.clone())
}
pub fn pop_front(self) -> Option<(&'a TokTree<'b, A, X>, Self)> {
pub fn pop_front(self) -> Option<(&'a TokTree<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>)> {
pub fn pop_back(self) -> Option<(Self, &'a TokTree<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)> {
pub fn split_once(self, f: impl FnMut(&Token<A, X>) -> bool) -> Option<(Self, Self)> {
let idx = self.find_idx(f)?;
Some((self.split_at(idx).0, self.split_at(idx + 1).1))
}
pub fn split(
mut self,
mut f: impl FnMut(&Token<'b, A, X>) -> bool,
) -> impl Iterator<Item = Self> {
pub fn split(mut self, mut f: impl FnMut(&Token<A, X>) -> bool) -> impl Iterator<Item = Self> {
iter::from_fn(move || {
if self.is_empty() {
return None;
@@ -77,26 +84,22 @@ impl<'a, 'b, A: AtomRepr, X: ExtraTok> Snippet<'a, 'b, A, X> {
}
pub fn is_empty(self) -> bool { self.len() == 0 }
pub fn skip_fluff(self) -> Self {
let non_fluff_start = self.find_idx(|t| !matches!(t, Token::NS | Token::Comment(_)));
let non_fluff_start = self.find_idx(|t| !matches!(t, Token::BR | Token::Comment(_)));
self.split_at(non_fluff_start.unwrap_or(self.len())).1
}
/// Format the argument using the context held in this snippet
pub async fn fmt(self, v: &(impl Format + ?Sized)) -> String { take_first_fmt(v, self.i()).await }
}
impl<A: AtomRepr, X: ExtraTok> Copy for Snippet<'_, '_, A, X> {}
impl<A: AtomRepr, X: ExtraTok> Clone for Snippet<'_, '_, A, X> {
impl<A: ExprRepr, X: ExtraTok> Copy for Snippet<'_, A, X> {}
impl<A: ExprRepr, X: ExtraTok> Clone for Snippet<'_, A, X> {
fn clone(&self) -> Self { *self }
}
impl<'b, A: AtomRepr, X: ExtraTok> Deref for Snippet<'_, 'b, A, X> {
type Target = [TokTree<'b, A, X>];
impl<A: ExprRepr, X: ExtraTok> Deref for Snippet<'_, A, X> {
type Target = [TokTree<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: AtomRepr, X: ExtraTok>(
tt: &TokTree<'a, A, X>,
) -> Option<TokTree<'a, A, X>> {
pub fn strip_fluff<A: ExprRepr, X: ExtraTok>(tt: &TokTree<A, X>) -> Option<TokTree<A, X>> {
let tok = match &tt.tok {
Token::BR => return None,
Token::Comment(_) => return None,
@@ -125,9 +128,10 @@ impl fmt::Display for Comment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "--[{}]--", self.text) }
}
pub async fn line_items<'a, 'b, A: AtomRepr, X: ExtraTok>(
snip: Snippet<'a, 'b, A, X>,
) -> Vec<Parsed<'a, 'b, Vec<Comment>, A, X>> {
pub async fn line_items<'a, A: ExprRepr, X: ExtraTok>(
ctx: &impl ParseCtx,
snip: Snippet<'a, A, X>,
) -> Vec<Parsed<'a, 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)) {
@@ -143,7 +147,7 @@ pub async fn line_items<'a, 'b, A: AtomRepr, X: ExtraTok>(
let comments = join_all(comments.drain(..).chain(cmts.cur).map(|t| async {
match &t.tok {
Token::Comment(c) =>
Comment { text: tail.i().i(&**c).await, pos: Pos::Range(t.range.clone()) },
Comment { text: ctx.i().i(&**c).await, pos: Pos::Range(t.range.clone()) },
_ => unreachable!("All are comments checked above"),
}
}))
@@ -155,23 +159,26 @@ pub async fn line_items<'a, 'b, A: AtomRepr, X: ExtraTok>(
items
}
pub async fn try_pop_no_fluff<'a, 'b, A: AtomRepr, X: ExtraTok>(
snip: Snippet<'a, 'b, A, X>,
) -> ParseRes<'a, 'b, &'a TokTree<'b, A, X>, A, X> {
pub async fn try_pop_no_fluff<'a, A: ExprRepr, X: ExtraTok>(
ctx: &impl ParseCtx,
snip: Snippet<'a, A, X>,
) -> ParseRes<'a, &'a TokTree<A, X>, A, X> {
match snip.skip_fluff().pop_front() {
Some((output, tail)) => Ok(Parsed { output, tail }),
None =>
Err(mk_errv(snip.i().i("Unexpected end").await, "Pattern ends abruptly", [Pos::Range(
snip.pos(),
)
.into()])),
None => Err(mk_errv(ctx.i().i("Unexpected end").await, "Pattern ends abruptly", [Pos::Range(
snip.pos(),
)
.into()])),
}
}
pub async fn expect_end(snip: Snippet<'_, '_, impl AtomRepr, impl ExtraTok>) -> OrcRes<()> {
pub async fn expect_end(
ctx: &impl ParseCtx,
snip: Snippet<'_, impl ExprRepr, impl ExtraTok>,
) -> OrcRes<()> {
match snip.skip_fluff().get(0) {
Some(surplus) => Err(mk_errv(
snip.i().i("Extra code after end of line").await,
ctx.i().i("Extra code after end of line").await,
"Code found after the end of the line",
[Pos::Range(surplus.range.clone()).into()],
)),
@@ -179,128 +186,86 @@ pub async fn expect_end(snip: Snippet<'_, '_, impl AtomRepr, impl ExtraTok>) ->
}
}
pub async fn expect_tok<'a, 'b, A: AtomRepr, X: ExtraTok>(
snip: Snippet<'a, 'b, A, X>,
pub async fn expect_tok<'a, A: ExprRepr, X: ExtraTok>(
ctx: &impl ParseCtx,
snip: Snippet<'a, A, X>,
tok: Tok<String>,
) -> ParseRes<'a, 'b, (), A, X> {
let Parsed { output: head, tail } = try_pop_no_fluff(snip).await?;
) -> ParseRes<'a, (), A, X> {
let Parsed { output: head, tail } = try_pop_no_fluff(ctx, snip).await?;
match &head.tok {
Token::Name(n) if *n == tok => Ok(Parsed { output: (), tail }),
t => Err(mk_errv(
snip.i().i("Expected specific keyword").await,
format!("Expected {tok} but found {:?}", snip.fmt(t).await),
ctx.i().i("Expected specific keyword").await,
format!("Expected {tok} but found {:?}", fmt(t, ctx.i()).await),
[Pos::Range(head.range.clone()).into()],
)),
}
}
pub struct Parsed<'a, 'b, T, A: AtomRepr, X: ExtraTok> {
pub struct Parsed<'a, T, H: ExprRepr, X: ExtraTok> {
pub output: T,
pub tail: Snippet<'a, 'b, A, X>,
pub tail: Snippet<'a, H, X>,
}
pub type ParseRes<'a, 'b, T, A, X> = OrcRes<Parsed<'a, 'b, T, A, X>>;
pub type ParseRes<'a, T, H, X> = OrcRes<Parsed<'a, T, H, X>>;
pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>(
ctx: &(impl Reporter + ?Sized),
tail: Snippet<'a, 'b, A, X>,
) -> ParseRes<'a, 'b, Vec<(Import, Pos)>, A, X> {
let ret = rec(ctx, tail).await;
pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>(
ctx: &impl ParseCtx,
tail: Snippet<'a, A, X>,
) -> ParseRes<'a, Vec<(Import, Pos)>, A, X> {
let Some((tt, tail)) = tail.skip_fluff().pop_front() else {
return Err(mk_errv(
ctx.i().i("Expected token").await,
"Expected a name, a parenthesized list of names, or a globstar.",
[Pos::Range(tail.pos()).into()],
));
};
let ret = rec(tt, ctx).await;
#[allow(clippy::type_complexity)] // it's an internal function
pub async fn rec<'a, 'b, A: AtomRepr, X: ExtraTok>(
ctx: &(impl Reporter + ?Sized),
tail: Snippet<'a, 'b, A, X>,
) -> ParseRes<'a, 'b, Vec<(Vec<Tok<String>>, Option<Tok<String>>, Pos)>, A, X> {
let comma = tail.i().i(",").await;
let globstar = tail.i().i("*").await;
let Some((name, tail)) = tail.skip_fluff().pop_front() else {
return Err(mk_errv(
tail.i().i("Expected name").await,
"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 {
Token::Name(n) if n.starts_with(name_start) => Ok(n),
_ =>
Err(mk_err(tail.i().i("Unexpected name prefix").await, "Only names can precede ::", [
Pos::Range(name.range.clone()).into(),
])),
};
match (Box::pin(rec(ctx, tail)).await, n) {
(Err(ev), n) => Err(ev.extended(n.err())),
(Ok(Parsed { tail, .. }), Err(e)) => {
ctx.report(e);
Ok(Parsed { output: 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 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(mk_errv(
tail.i().i("Unescaped operator in multiname").await,
"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()))]
},
Token::S(Paren::Square, b) => {
let mut ok = Vec::new();
for tt in b.iter() {
match &tt.tok {
Token::Name(n) if n.starts_with(op_char) =>
ok.push((vec![], Some(n.clone()), Pos::Range(tt.range.clone()))),
Token::BR | Token::Comment(_) => (),
_ => ctx.report(mk_err(
tail.i().i("Non-operator in escapement in multiname").await,
"In multinames, [] functions as a literal name list reserved for operators",
[Pos::Range(name.range.clone()).into()],
)),
}
pub async fn rec<A: ExprRepr, X: ExtraTok>(
tt: &TokTree<A, X>,
ctx: &impl ParseCtx,
) -> OrcRes<Vec<(Vec<Tok<String>>, Option<Tok<String>>, Pos)>> {
let ttpos = Pos::Range(tt.range.clone());
match &tt.tok {
Token::NS(ns, body) => {
if !ns.starts_with(name_start) {
ctx.reporter().report(mk_err(
ctx.i().i("Unexpected name prefix").await,
"Only names can precede ::",
[ttpos.into()],
))
};
let out = Box::pin(rec(body, ctx)).await?;
Ok(out.into_iter().update(|i| i.0.push(ns.clone())).collect_vec())
},
Token::Name(ntok) => {
let n = ntok;
let nopt = Some(n.clone());
Ok(vec![(vec![], nopt, Pos::Range(tt.range.clone()))])
},
Token::S(Paren::Round, b) => {
let mut o = Vec::new();
let mut body = Snippet::new(tt, b);
while let Some((output, tail)) = body.pop_front() {
match rec(output, ctx).boxed_local().await {
Ok(names) => o.extend(names),
Err(e) => ctx.reporter().report(e),
}
ok
},
Token::S(Paren::Round, b) => {
let mut ok = Vec::new();
let body = Snippet::new(name, b, tail.interner);
for csent in body.split(|n| matches!(n, Token::Name(n) if *n == comma)) {
match Box::pin(rec(ctx, csent)).await {
Err(e) => ctx.report(e),
Ok(Parsed { output, tail }) => match tail.get(0) {
None => ok.extend(output),
Some(t) => ctx.report(mk_err(
tail.i().i("Unexpected token in multiname group").await,
"Unexpected token. Likely missing a :: or , or wanted [] instead of ()",
[Pos::Range(t.range.clone()).into()],
)),
},
}
}
ok
},
t => {
return Err(mk_errv(
tail.i().i("Unrecognized name end").await,
format!("Names cannot end with {:?} tokens", tail.fmt(t).await),
[Pos::Range(name.range.clone()).into()],
));
},
};
Ok(Parsed { output, tail })
body = tail;
}
Ok(o)
},
t => {
return Err(mk_errv(
ctx.i().i("Unrecognized name end").await,
format!("Names cannot end with {:?} tokens", fmt(t, ctx.i()).await),
[ttpos.into()],
));
},
}
}
ret.map(|Parsed { output, tail }| {
ret.map(|output| {
let output = (output.into_iter())
.map(|(p, name, pos)| (Import { path: VPath::new(p.into_iter().rev()), name }, pos))
.collect_vec();
@@ -327,14 +292,5 @@ mod test {
use super::Snippet;
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
}
fn _covary_snip_a<'a>(x: Snippet<'static, Never, Never>) -> Snippet<'a, Never, Never> { x }
}

View File

@@ -1,4 +1,4 @@
use std::any::{Any, TypeId};
use std::any::Any;
use std::cell::RefCell;
use std::future::Future;
use std::marker::PhantomData;
@@ -46,6 +46,9 @@ pub trait ReqHandlish {
}
fn defer_drop_objsafe(&self, val: Box<dyn Any>);
}
impl ReqHandlish for &'_ dyn ReqHandlish {
fn defer_drop_objsafe(&self, val: Box<dyn Any>) { (**self).defer_drop_objsafe(val) }
}
#[derive(destructure)]
pub struct RequestHandle<'a, MS: MsgSet> {
@@ -170,12 +173,16 @@ pub trait DynRequester {
pub struct MappedRequester<'a, T: 'a>(Box<dyn Fn(T) -> LocalBoxFuture<'a, RawReply> + 'a>, Logger);
impl<'a, T> MappedRequester<'a, T> {
fn new<U: DynRequester + 'a>(req: U, logger: Logger) -> Self
where T: Into<U::Transfer> {
fn new<U: DynRequester + 'a, F: Fn(T) -> U::Transfer + 'a>(
req: U,
cb: F,
logger: Logger,
) -> Self {
let req_arc = Arc::new(req);
let cb_arc = Arc::new(cb);
MappedRequester(
Box::new(move |t| {
Box::pin(clone!(req_arc; async move { req_arc.raw_request(t.into()).await}))
Box::pin(clone!(req_arc, cb_arc; async move { req_arc.raw_request(cb_arc(t)).await}))
}),
logger,
)
@@ -217,10 +224,10 @@ pub trait Requester: DynRequester {
&self,
data: R,
) -> impl Future<Output = R::Response>;
fn map<'a, U: Into<Self::Transfer>>(self) -> MappedRequester<'a, U>
fn map<'a, U>(self, cb: impl Fn(U) -> Self::Transfer + 'a) -> MappedRequester<'a, U>
where Self: Sized + 'a {
let logger = self.logger().clone();
MappedRequester::new(self, logger)
MappedRequester::new(self, cb, logger)
}
}

View File

@@ -6,38 +6,61 @@ use std::ops::Range;
use std::rc::Rc;
use std::sync::Arc;
pub use api::PhKind;
use async_stream::stream;
use futures::future::join_all;
use futures::{FutureExt, StreamExt};
use itertools::Itertools;
use never::Never;
use ordered_float::NotNan;
use orchid_api_traits::Coding;
use trait_set::trait_set;
use crate::error::OrcErrv;
use crate::format::{FmtCtx, FmtUnit, Format, Variants};
use crate::interner::{Interner, Tok};
use crate::location::Pos;
use crate::name::Sym;
use crate::parse::Snippet;
use crate::{api, match_mapping, tl_cache};
trait_set! {
pub trait RecurCB<'a, A: AtomRepr, X: ExtraTok> = Fn(TokTree<'a, A, X>) -> TokTree<'a, A, X>;
pub trait ExtraTok = Format + Clone + fmt::Debug;
pub trait RefDoExtra<X> = AsyncFnMut(&X, Range<u32>) -> api::TokenTree;
pub trait TokenVariant<ApiEquiv: Clone + Debug + Coding>: Format + Clone + fmt::Debug {
type FromApiCtx<'a>;
type ToApiCtx<'a>;
fn from_api(
api: &ApiEquiv,
ctx: &mut Self::FromApiCtx<'_>,
pos: Pos,
i: &Interner,
) -> impl Future<Output = Self>;
fn into_api(self, ctx: &mut Self::ToApiCtx<'_>) -> impl Future<Output = ApiEquiv>;
}
impl<T: Clone + Debug + Coding> TokenVariant<T> for Never {
type FromApiCtx<'a> = ();
type ToApiCtx<'a> = ();
async fn from_api(_: &T, _: &mut Self::FromApiCtx<'_>, _: Pos, _: &Interner) -> Self {
panic!("Cannot deserialize Never")
}
async fn into_api(self, _: &mut Self::ToApiCtx<'_>) -> T { match self {} }
}
pub fn recur<'a, A: AtomRepr, 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> {
trait_set! {
// TokenHandle
pub trait ExprRepr = TokenVariant<api::ExprTicket>;
// TokenExpr
pub trait ExtraTok = TokenVariant<api::Expression>;
}
trait_set! {
pub trait RecurCB<H: ExprRepr, X: ExtraTok> = Fn(TokTree<H, X>) -> TokTree<H, X>;
}
pub fn recur<H: ExprRepr, X: ExtraTok>(
tt: TokTree<H, X>,
f: &impl Fn(TokTree<H, X>, &dyn RecurCB<H, X>) -> TokTree<H, X>,
) -> TokTree<H, 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(_) | Token::Ph(_) | Token::Macro(_)) => tok,
tok @ Token::Reference(_) => tok,
tok @ (Token::BR | Token::Bottom(_) | Token::Comment(_) | Token::Name(_)) => tok,
tok @ (Token::Handle(_) | Token::NewExpr(_)) => tok,
Token::NS(n, b) => Token::NS(n, Box::new(recur(*b, f))),
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()),
@@ -70,68 +93,48 @@ impl Display for TokHandle<'_> {
}
#[derive(Clone, Debug)]
pub struct TokTree<'a, A: AtomRepr, X: ExtraTok> {
pub tok: Token<'a, A, X>,
pub struct TokTree<H: ExprRepr, X: ExtraTok> {
pub tok: Token<H, X>,
pub range: Range<u32>,
}
impl<'b, A: AtomRepr, X: ExtraTok> TokTree<'b, A, X> {
pub async fn from_api(tt: &api::TokenTree, ctx: &mut A::Ctx, i: &Interner) -> Self {
let tok = match_mapping!(&tt.token, api::Token => Token::<'b, A, X> {
BR, NS,
Atom(a => A::from_api(a, Pos::Range(tt.range.clone()), ctx).await),
impl<H: ExprRepr, X: ExtraTok> TokTree<H, X> {
pub async fn from_api(
tt: &api::TokenTree,
hctx: &mut H::FromApiCtx<'_>,
xctx: &mut X::FromApiCtx<'_>,
i: &Interner,
) -> Self {
let tok = match_mapping!(&tt.token, api::Token => Token::<H, X> {
BR,
NS(n => Tok::from_api(*n, i).await,
b => Box::new(Self::from_api(b, hctx, xctx, i).boxed_local().await)),
Bottom(e => OrcErrv::from_api(e, i).await),
LambdaHead(arg => ttv_from_api(arg, ctx, i).await),
LambdaHead(arg => ttv_from_api(arg, hctx, xctx, i).await),
Name(n => Tok::from_api(*n, i).await),
S(*par, b => ttv_from_api(b, ctx, i).await),
S(*par, b => ttv_from_api(b, hctx, xctx, i).await),
Comment(c.clone()),
Slot(id => TokHandle::new(*id)),
Ph(ph => Ph::from_api(ph, i).await),
Macro(*prio),
Reference(tok => Sym::from_api(*tok, i).await)
NewExpr(expr => X::from_api(expr, xctx, Pos::Range(tt.range.clone()), i).await),
Handle(tk => H::from_api(tk, hctx, Pos::Range(tt.range.clone()), i).await)
});
Self { range: tt.range.clone(), tok }
}
pub async fn to_api(&self, do_extra: &mut impl RefDoExtra<X>) -> api::TokenTree {
let token = match_mapping!(&self.tok, Token => api::Token {
Atom(a.to_api().await),
BR,
NS,
Bottom(e.to_api()),
Comment(c.clone()),
LambdaHead(arg => ttv_to_api(arg, do_extra).boxed_local().await),
Name(n.to_api()),
Slot(tt.ticket()),
S(*p, b => ttv_to_api(b, do_extra).boxed_local().await),
Ph(ph.to_api()),
Macro(*prio),
Reference(sym.to_api()),
} {
Token::X(x) => return do_extra(x, self.range.clone()).await
});
api::TokenTree { range: self.range.clone(), token }
}
pub async fn into_api(
self,
do_extra: &mut impl FnMut(X, Range<u32>) -> api::TokenTree,
hctx: &mut H::ToApiCtx<'_>,
xctx: &mut X::ToApiCtx<'_>,
) -> api::TokenTree {
let token = match self.tok {
Token::Atom(a) => api::Token::Atom(a.to_api().await),
Token::Reference(sym) => api::Token::Reference(sym.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).await),
Token::Name(n) => api::Token::Name(n.to_api()),
Token::Slot(tt) => api::Token::Slot(tt.ticket()),
Token::S(p, b) => api::Token::S(p, ttv_into_api(b, do_extra).await),
Token::Ph(Ph { kind, name }) =>
api::Token::Ph(api::Placeholder { name: name.to_api(), kind }),
Token::X(x) => return do_extra(x, self.range.clone()),
Token::Macro(prio) => api::Token::Macro(prio),
};
let token = match_mapping!(self.tok, Token => api::Token {
BR,
NS(n.to_api(), b => Box::new(b.into_api(hctx, xctx).boxed_local().await)),
Bottom(e.to_api()),
Comment(c.clone()),
LambdaHead(arg => ttv_into_api(arg, hctx, xctx).await),
Name(nn.to_api()),
S(p, b => ttv_into_api(b, hctx, xctx).await),
Handle(hand.into_api(hctx).await),
NewExpr(expr.into_api(xctx).await),
});
api::TokenTree { range: self.range.clone(), token }
}
@@ -139,9 +142,16 @@ impl<'b, A: AtomRepr, X: ExtraTok> TokTree<'b, A, X> {
pub fn as_name(&self) -> Option<Tok<String>> {
if let Token::Name(n) = &self.tok { Some(n.clone()) } else { None }
}
pub fn as_s<'a>(&'a self, par: Paren, i: &'a Interner) -> Option<Snippet<'a, 'b, A, X>> {
self.tok.as_s(par).map(|slc| Snippet::new(self, slc, i))
pub fn as_s(&self, par: Paren) -> Option<Snippet<'_, H, X>> {
self.tok.as_s(par).map(|slc| Snippet::new(self, slc))
}
pub fn as_lambda(&self) -> Option<Snippet<'_, H, X>> {
match &self.tok {
Token::LambdaHead(arg) => Some(Snippet::new(self, arg)),
_ => None,
}
}
pub fn is_fluff(&self) -> bool { matches!(self.tok, Token::Comment(_) | Token::BR) }
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;
@@ -149,53 +159,44 @@ impl<'b, A: AtomRepr, X: ExtraTok> TokTree<'b, A, X> {
Token::S(Paren::Round, body).at(s_range)
}
}
impl<A: AtomRepr, X: ExtraTok> Format for TokTree<'_, A, X> {
impl<H: ExprRepr, X: ExtraTok> Format for TokTree<H, X> {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
self.tok.print(c).await
}
}
pub async fn ttv_from_api<A: AtomRepr, X: ExtraTok>(
pub async fn ttv_from_api<H: ExprRepr, X: ExtraTok>(
tokv: impl IntoIterator<Item: Borrow<api::TokenTree>>,
ctx: &mut A::Ctx,
hctx: &mut H::FromApiCtx<'_>,
xctx: &mut X::FromApiCtx<'_>,
i: &Interner,
) -> Vec<TokTree<'static, A, X>> {
) -> Vec<TokTree<H, X>> {
stream! {
for tok in tokv {
yield TokTree::<A, X>::from_api(tok.borrow(), ctx, i).boxed_local().await
yield TokTree::<H, X>::from_api(tok.borrow(), hctx, xctx, i).boxed_local().await
}
}
.collect()
.await
}
pub async fn ttv_to_api<'a, A: AtomRepr, X: ExtraTok>(
tokv: impl IntoIterator<Item: Borrow<TokTree<'a, A, X>>>,
do_extra: &mut impl RefDoExtra<X>,
) -> Vec<api::TokenTree> {
let mut output = Vec::new();
for tok in tokv {
output.push(Borrow::<TokTree<A, X>>::borrow(&tok).to_api(do_extra).await)
}
output
}
pub async fn ttv_into_api<'a, A: AtomRepr, X: ExtraTok>(
tokv: impl IntoIterator<Item = TokTree<'a, A, X>>,
do_extra: &mut impl FnMut(X, Range<u32>) -> api::TokenTree,
pub async fn ttv_into_api<H: ExprRepr, X: ExtraTok>(
tokv: impl IntoIterator<Item = TokTree<H, X>>,
hctx: &mut H::ToApiCtx<'_>,
xctx: &mut X::ToApiCtx<'_>,
) -> Vec<api::TokenTree> {
stream! {
for tok in tokv {
yield tok.into_api(do_extra).await
yield tok.into_api(hctx, xctx).await
}
}
.collect()
.await
}
pub fn wrap_tokv<'a, A: AtomRepr, X: ExtraTok>(
items: impl IntoIterator<Item = TokTree<'a, A, X>>,
) -> TokTree<'a, A, X> {
pub fn wrap_tokv<H: ExprRepr, X: ExtraTok>(
items: impl IntoIterator<Item = TokTree<H, X>>,
) -> TokTree<H, X> {
let items_v = items.into_iter().collect_vec();
match items_v.len() {
0 => panic!("A tokv with no elements is illegal"),
@@ -211,70 +212,54 @@ pub use api::Paren;
/// Lexer output variant
#[derive(Clone, Debug)]
pub enum Token<'a, A: AtomRepr, X: ExtraTok> {
pub enum Token<H: ExprRepr, X: ExtraTok> {
/// Information about the code addressed to the human reader or dev tooling
/// It has no effect on the behaviour of the program unless it's explicitly
/// read via reflection
Comment(Arc<String>),
/// The part of a lambda between `\` and `.` enclosing the argument. The body
/// stretches to the end of the enclosing parens or the end of the const line
LambdaHead(Vec<TokTree<'a, A, X>>),
LambdaHead(Vec<TokTree<H, X>>),
/// A binding, operator, or a segment of a namespaced::name
Name(Tok<String>),
/// The namespace separator ::
NS,
/// A namespace prefix, like `my_ns::` followed by a token
NS(Tok<String>, Box<TokTree<H, X>>),
/// A line break
BR,
/// `()`, `[]`, or `{}`
S(Paren, Vec<TokTree<'a, A, X>>),
/// A fully formed reference to external code emitted by a lexer plugin
Reference(Sym),
/// A value emitted by a lexer plugin
Atom(A),
S(Paren, Vec<TokTree<H, X>>),
/// A newly instantiated expression
NewExpr(X),
/// An existing expr from a nested lexer
Handle(H),
/// A grammar error emitted by a lexer plugin if it was possible to continue
/// reading. Parsers should treat it as an atom unless it prevents parsing,
/// in which case both this and a relevant error should be returned.
Bottom(OrcErrv),
/// An instruction from a plugin for the lexer to embed a subexpression
/// without retransmitting it. It should not appear anywhere outside lexer
/// plugin responses.
Slot(TokHandle<'a>),
/// Additional domain-specific token types
X(X),
/// A placeholder for metaprogramming, either $name, ..$name, ..$name:N,
/// ...$name, or ...$name:N
Ph(Ph),
/// `macro` or `macro(`X`)` where X is any valid floating point number
/// expression. `macro` is not a valid name in Orchid for this reason.
Macro(Option<NotNan<f64>>),
}
impl<'a, A: AtomRepr, X: ExtraTok> Token<'a, A, X> {
pub fn at(self, range: Range<u32>) -> TokTree<'a, A, X> { TokTree { range, tok: self } }
impl<H: ExprRepr, X: ExtraTok> Token<H, X> {
pub fn at(self, range: Range<u32>) -> TokTree<H, 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>]> {
pub fn as_s(&self, par: Paren) -> Option<&[TokTree<H, X>]> {
match self {
Self::S(p, b) if *p == par => Some(b),
_ => None,
}
}
}
impl<A: AtomRepr, X: ExtraTok> Format for Token<'_, A, X> {
impl<H: ExprRepr, X: ExtraTok> Format for Token<H, X> {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
match self {
Self::Atom(a) => a.print(c).await,
Self::BR => "\n".to_string().into(),
Self::Bottom(err) if err.len() == 1 => format!("Bottom({}) ", err.one().unwrap()).into(),
Self::Bottom(err) => format!("Botttom(\n{}) ", indent(&err.to_string())).into(),
Self::Comment(c) => format!("--[{c}]--").into(),
Self::LambdaHead(arg) =>
FmtUnit::new(tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("\\{0b}."))), [
ttv_fmt(arg, c).await,
]),
Self::NS => "::".to_string().into(),
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("\\{0b}.")))
.units([ttv_fmt(arg, c).await]),
Self::NS(n, b) => tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{0}::{1l}")))
.units([n.to_string().into(), b.print(c).boxed_local().await]),
Self::Name(n) => format!("{n}").into(),
Self::Reference(sym) => format!("{sym}").into(),
Self::Slot(th) => format!("{th}").into(),
Self::Ph(ph) => format!("{ph}").into(),
Self::S(p, b) => FmtUnit::new(
match *p {
Paren::Round => tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("({0b})"))),
@@ -283,67 +268,22 @@ impl<A: AtomRepr, X: ExtraTok> Format for Token<'_, A, X> {
},
[ttv_fmt(b, c).await],
),
Self::X(x) => x.print(c).await,
Self::Macro(None) => "macro".to_string().into(),
Self::Macro(Some(prio)) => format!("macro({prio})").into(),
Self::Handle(h) => h.print(c).await,
Self::NewExpr(ex) => ex.print(c).await,
}
}
}
pub fn ttv_range(ttv: &[TokTree<'_, impl AtomRepr, impl ExtraTok>]) -> Range<u32> {
pub fn ttv_range<'a>(ttv: &[TokTree<impl ExprRepr + 'a, impl ExtraTok + 'a>]) -> Range<u32> {
assert!(!ttv.is_empty(), "Empty slice has no range");
ttv.first().unwrap().range.start..ttv.last().unwrap().range.end
}
pub async fn ttv_fmt<'a: 'b, 'b>(
ttv: impl IntoIterator<Item = &'b TokTree<'a, impl AtomRepr + 'b, impl ExtraTok + 'b>>,
ttv: impl IntoIterator<Item = &'b TokTree<impl ExprRepr + 'a, impl ExtraTok + 'a>>,
c: &(impl FmtCtx + ?Sized),
) -> FmtUnit {
FmtUnit::sequence(" ", None, join_all(ttv.into_iter().map(|t| t.print(c))).await)
}
pub fn indent(s: &str) -> String { s.replace("\n", "\n ") }
#[derive(Clone, Debug)]
pub struct Ph {
pub name: Tok<String>,
pub kind: PhKind,
}
impl Ph {
pub async fn from_api(api: &api::Placeholder, i: &Interner) -> Self {
Self { name: Tok::from_api(api.name, i).await, kind: api.kind }
}
pub fn to_api(&self) -> api::Placeholder {
api::Placeholder { name: self.name.to_api(), kind: self.kind }
}
}
impl Display for Ph {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let n = &self.name;
match self.kind {
PhKind::Scalar => write!(f, "${n}"),
PhKind::Vector { priority: 0, at_least_one: true } => write!(f, "...${}", self.name),
PhKind::Vector { priority: p, at_least_one: true } => write!(f, "...${}:{}", self.name, p),
PhKind::Vector { priority: 0, at_least_one: false } => write!(f, "..${}", self.name),
PhKind::Vector { priority: p, at_least_one: false } => write!(f, "..${}:{}", self.name, p),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_covariance() {
fn _f<'a>(x: Token<'static, Never, Never>) -> Token<'a, Never, Never> { x }
}
#[test]
fn fail_covariance() {
// this fails to compile
// fn _f<'a, 'b>(x: &'a mut &'static ()) -> &'a mut &'b () { x }
// this passes because it's covariant
fn _f<'a, 'b>(x: &'a fn() -> &'static ()) -> &'a fn() -> &'b () { x }
}
}