commit before easter break
This commit is contained in:
@@ -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 }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user