forked from Orchid/orchid
Commit pending merge
This commit is contained in:
@@ -23,15 +23,13 @@ pub enum Pos {
|
||||
Inherit,
|
||||
Gen(CodeGenInfo),
|
||||
/// Range and file
|
||||
SourceRange(SourceRange),
|
||||
/// Range only, file implied. Most notably used by parsers
|
||||
Range(Range<u32>),
|
||||
SrcRange(SrcRange),
|
||||
}
|
||||
impl Pos {
|
||||
pub fn pretty_print(&self, get_src: &mut impl GetSrc) -> String {
|
||||
match self {
|
||||
Self::Gen(g) => g.to_string(),
|
||||
Self::SourceRange(sr) => sr.pretty_print(&get_src(&sr.path)),
|
||||
Self::SrcRange(sr) => sr.pretty_print(&get_src(&sr.path)),
|
||||
// Can't pretty print partial and meta-location
|
||||
other => format!("{other:?}"),
|
||||
}
|
||||
@@ -39,17 +37,17 @@ impl Pos {
|
||||
pub async fn from_api(api: &api::Location, i: &Interner) -> Self {
|
||||
match_mapping!(api, api::Location => Pos {
|
||||
None, Inherit, SlotTarget,
|
||||
Range(r.clone()),
|
||||
Gen(cgi => CodeGenInfo::from_api(cgi, i).await),
|
||||
SourceRange(sr => SourceRange::from_api(sr, i).await)
|
||||
} {
|
||||
api::Location::SourceRange(sr) => Self::SrcRange(SrcRange::from_api(sr, i).await)
|
||||
})
|
||||
}
|
||||
pub fn to_api(&self) -> api::Location {
|
||||
match_mapping!(self, Pos => api::Location {
|
||||
None, Inherit, SlotTarget,
|
||||
Range(r.clone()),
|
||||
Gen(cgi.to_api()),
|
||||
SourceRange(sr.to_api()),
|
||||
} {
|
||||
Self::SrcRange(sr) => api::Location::SourceRange(sr.to_api()),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -57,12 +55,12 @@ impl Pos {
|
||||
/// Exact source code location. Includes where the code was loaded from, what
|
||||
/// the original source code was, and a byte range.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct SourceRange {
|
||||
pub struct SrcRange {
|
||||
pub(crate) path: Sym,
|
||||
pub(crate) range: Range<u32>,
|
||||
}
|
||||
impl SourceRange {
|
||||
pub fn new(range: &Range<u32>, path: &Sym) -> Self {
|
||||
impl SrcRange {
|
||||
pub fn new(range: Range<u32>, path: &Sym) -> Self {
|
||||
Self { range: range.clone(), path: path.clone() }
|
||||
}
|
||||
/// Create a dud [SourceRange] for testing. Its value is unspecified and
|
||||
@@ -77,7 +75,7 @@ impl SourceRange {
|
||||
/// 0-based index of last byte + 1
|
||||
pub fn end(&self) -> u32 { self.range.end }
|
||||
/// Syntactic location
|
||||
pub fn pos(&self) -> Pos { Pos::SourceRange(self.clone()) }
|
||||
pub fn pos(&self) -> Pos { Pos::SrcRange(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() }
|
||||
|
||||
@@ -5,7 +5,8 @@ use ordered_float::NotNan;
|
||||
|
||||
use crate::error::{OrcErr, mk_err};
|
||||
use crate::interner::Interner;
|
||||
use crate::location::Pos;
|
||||
use crate::location::SrcRange;
|
||||
use crate::name::Sym;
|
||||
|
||||
/// A number, either floating point or unsigned int, parsed by Orchid.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
@@ -54,7 +55,12 @@ pub struct NumError {
|
||||
pub kind: NumErrorKind,
|
||||
}
|
||||
|
||||
pub async fn num_to_err(NumError { kind, range }: NumError, offset: u32, i: &Interner) -> OrcErr {
|
||||
pub async fn num_to_err(
|
||||
NumError { kind, range }: NumError,
|
||||
offset: u32,
|
||||
source: &Sym,
|
||||
i: &Interner,
|
||||
) -> OrcErr {
|
||||
mk_err(
|
||||
i.i("Failed to parse number").await,
|
||||
match kind {
|
||||
@@ -62,7 +68,7 @@ pub async fn num_to_err(NumError { kind, range }: NumError, offset: u32, i: &Int
|
||||
NumErrorKind::InvalidDigit => "non-digit character encountered",
|
||||
NumErrorKind::Overflow => "The number being described is too large or too accurate",
|
||||
},
|
||||
[Pos::Range(offset + range.start as u32..offset + range.end as u32).into()],
|
||||
[SrcRange::new(offset + range.start as u32..offset + range.end as u32, source).pos().into()],
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::fmt::{self, Display};
|
||||
use std::iter;
|
||||
use std::ops::{Deref, Range};
|
||||
use std::ops::Deref;
|
||||
|
||||
use futures::FutureExt;
|
||||
use futures::future::join_all;
|
||||
@@ -10,9 +10,9 @@ use crate::api;
|
||||
use crate::error::{OrcRes, Reporter, mk_err, mk_errv};
|
||||
use crate::format::fmt;
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::location::Pos;
|
||||
use crate::name::VPath;
|
||||
use crate::tree::{ExprRepr, ExtraTok, Paren, TokTree, Token};
|
||||
use crate::location::{Pos, SrcRange};
|
||||
use crate::name::{VName, VPath};
|
||||
use crate::tree::{ExprRepr, ExtraTok, Paren, TokTree, Token, ttv_range};
|
||||
|
||||
pub trait ParseCtx {
|
||||
fn i(&self) -> &Interner;
|
||||
@@ -58,10 +58,7 @@ where
|
||||
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<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 sr(self) -> SrcRange { ttv_range(self.cur).unwrap_or_else(|| self.prev.sr.clone()) }
|
||||
pub fn pop_front(self) -> Option<(&'a TokTree<A, X>, Self)> {
|
||||
self.cur.first().map(|r| (r, self.split_at(1).1))
|
||||
}
|
||||
@@ -107,7 +104,7 @@ pub fn strip_fluff<A: ExprRepr, X: ExtraTok>(tt: &TokTree<A, X>) -> Option<TokTr
|
||||
Token::S(p, b) => Token::S(*p, b.iter().filter_map(strip_fluff).collect()),
|
||||
t => t.clone(),
|
||||
};
|
||||
Some(TokTree { tok, range: tt.range.clone() })
|
||||
Some(TokTree { tok, sr: tt.sr.clone() })
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -116,6 +113,7 @@ pub struct Comment {
|
||||
pub range: Range<u32>,
|
||||
}
|
||||
impl Comment {
|
||||
// XXX: which of these four are actually used?
|
||||
pub async fn from_api(c: &api::Comment, i: &Interner) -> Self {
|
||||
Self { text: i.ex(c.text).await, range: c.range.clone() }
|
||||
}
|
||||
@@ -170,10 +168,11 @@ pub async fn try_pop_no_fluff<'a, A: ExprRepr, X: ExtraTok>(
|
||||
) -> 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(ctx.i().i("Unexpected end").await, "Pattern ends abruptly", [Pos::Range(
|
||||
snip.pos(),
|
||||
)
|
||||
.into()])),
|
||||
None => Err(mk_errv(
|
||||
ctx.i().i("Unexpected end").await,
|
||||
"Line ends abruptly; more tokens were expected",
|
||||
[snip.sr().pos().into()],
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +184,7 @@ pub async fn expect_end(
|
||||
Some(surplus) => Err(mk_errv(
|
||||
ctx.i().i("Extra code after end of line").await,
|
||||
"Code found after the end of the line",
|
||||
[Pos::Range(surplus.range.clone()).into()],
|
||||
[surplus.sr.pos().into()],
|
||||
)),
|
||||
None => Ok(()),
|
||||
}
|
||||
@@ -202,7 +201,7 @@ pub async fn expect_tok<'a, A: ExprRepr, X: ExtraTok>(
|
||||
t => Err(mk_errv(
|
||||
ctx.i().i("Expected specific keyword").await,
|
||||
format!("Expected {tok} but found {:?}", fmt(t, ctx.i()).await),
|
||||
[Pos::Range(head.range.clone()).into()],
|
||||
[head.sr.pos().into()],
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -217,12 +216,12 @@ pub type ParseRes<'a, T, H, X> = OrcRes<Parsed<'a, T, H, X>>;
|
||||
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> {
|
||||
) -> ParseRes<'a, Vec<Import>, 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()],
|
||||
[tail.sr().pos().into()],
|
||||
));
|
||||
};
|
||||
let ret = rec(tt, ctx).await;
|
||||
@@ -230,8 +229,8 @@ pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>(
|
||||
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());
|
||||
) -> OrcRes<Vec<(Vec<Tok<String>>, Option<Tok<String>>, SrcRange)>> {
|
||||
let ttpos = tt.sr.pos();
|
||||
match &tt.tok {
|
||||
Token::NS(ns, body) => {
|
||||
if !ns.starts_with(name_start) {
|
||||
@@ -247,7 +246,7 @@ pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>(
|
||||
Token::Name(ntok) => {
|
||||
let n = ntok;
|
||||
let nopt = Some(n.clone());
|
||||
Ok(vec![(vec![], nopt, Pos::Range(tt.range.clone()))])
|
||||
Ok(vec![(vec![], nopt, tt.sr.clone())])
|
||||
},
|
||||
Token::S(Paren::Round, b) => {
|
||||
let mut o = Vec::new();
|
||||
@@ -272,19 +271,29 @@ pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>(
|
||||
}
|
||||
ret.map(|output| {
|
||||
let output = (output.into_iter())
|
||||
.map(|(p, name, pos)| (Import { path: VPath::new(p.into_iter().rev()), name }, pos))
|
||||
.map(|(p, name, sr)| Import { path: VPath::new(p.into_iter().rev()), name, sr })
|
||||
.collect_vec();
|
||||
Parsed { output, tail }
|
||||
})
|
||||
}
|
||||
|
||||
/// A compound name, possibly ending with a globstar
|
||||
/// A compound name, possibly ending with a globstar. It cannot be just a
|
||||
/// globstar; either the name has to be known or the path has to be non-empty.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Import {
|
||||
pub path: VPath,
|
||||
pub name: Option<Tok<String>>,
|
||||
pub sr: SrcRange,
|
||||
}
|
||||
impl Import {
|
||||
/// Most specific concrete path
|
||||
pub fn mspath(self) -> VName {
|
||||
match self.name {
|
||||
Some(n) => self.path.name_with_suffix(n),
|
||||
None => self.path.into_name().expect("Import cannot be empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Import {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}::{}", self.path.iter().join("::"), self.name.as_ref().map_or("*", |t| t.as_str()))
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::borrow::Borrow;
|
||||
use std::fmt::{self, Debug, Display};
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Range;
|
||||
use std::rc::Rc;
|
||||
|
||||
use async_stream::stream;
|
||||
@@ -16,7 +15,8 @@ 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::location::{Pos, SrcRange};
|
||||
use crate::name::Sym;
|
||||
use crate::parse::Snippet;
|
||||
use crate::{api, match_mapping, tl_cache};
|
||||
|
||||
@@ -26,7 +26,7 @@ pub trait TokenVariant<ApiEquiv: Clone + Debug + Coding>: Format + Clone + fmt::
|
||||
fn from_api(
|
||||
api: &ApiEquiv,
|
||||
ctx: &mut Self::FromApiCtx<'_>,
|
||||
pos: Pos,
|
||||
pos: SrcRange,
|
||||
i: &Interner,
|
||||
) -> impl Future<Output = Self>;
|
||||
fn into_api(self, ctx: &mut Self::ToApiCtx<'_>) -> impl Future<Output = ApiEquiv>;
|
||||
@@ -34,7 +34,7 @@ pub trait TokenVariant<ApiEquiv: Clone + Debug + Coding>: Format + Clone + fmt::
|
||||
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 {
|
||||
async fn from_api(_: &T, _: &mut Self::FromApiCtx<'_>, _: SrcRange, _: &Interner) -> Self {
|
||||
panic!("Cannot deserialize Never")
|
||||
}
|
||||
async fn into_api(self, _: &mut Self::ToApiCtx<'_>) -> T { match self {} }
|
||||
@@ -55,7 +55,7 @@ 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 }| {
|
||||
f(tt, &|TokTree { sr: range, tok }| {
|
||||
let tok = match tok {
|
||||
tok @ (Token::BR | Token::Bottom(_) | Token::Comment(_) | Token::Name(_)) => tok,
|
||||
tok @ (Token::Handle(_) | Token::NewExpr(_)) => tok,
|
||||
@@ -64,7 +64,7 @@ pub fn recur<H: ExprRepr, X: ExtraTok>(
|
||||
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()),
|
||||
};
|
||||
TokTree { range, tok }
|
||||
TokTree { sr: range, tok }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -94,28 +94,33 @@ impl Display for TokHandle<'_> {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TokTree<H: ExprRepr, X: ExtraTok> {
|
||||
pub tok: Token<H, X>,
|
||||
pub range: Range<u32>,
|
||||
/// The protocol has a Range<u32> because these are always transmitted in the
|
||||
/// context of a given snippet, but internal logic and error reporting is
|
||||
/// easier if the in-memory representation also includes the snippet path.
|
||||
pub sr: SrcRange,
|
||||
}
|
||||
impl<H: ExprRepr, X: ExtraTok> TokTree<H, X> {
|
||||
pub async fn from_api(
|
||||
tt: &api::TokenTree,
|
||||
hctx: &mut H::FromApiCtx<'_>,
|
||||
xctx: &mut X::FromApiCtx<'_>,
|
||||
src: &Sym,
|
||||
i: &Interner,
|
||||
) -> Self {
|
||||
let pos = SrcRange::new(tt.range.clone(), src);
|
||||
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)),
|
||||
b => Box::new(Self::from_api(b, hctx, xctx, src, i).boxed_local().await)),
|
||||
Bottom(e => OrcErrv::from_api(e, i).await),
|
||||
LambdaHead(arg => ttv_from_api(arg, hctx, xctx, i).await),
|
||||
LambdaHead(arg => ttv_from_api(arg, hctx, xctx, src, i).await),
|
||||
Name(n => Tok::from_api(*n, i).await),
|
||||
S(*par, b => ttv_from_api(b, hctx, xctx, i).await),
|
||||
S(*par, b => ttv_from_api(b, hctx, xctx, src, i).await),
|
||||
Comment(c.clone()),
|
||||
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)
|
||||
NewExpr(expr => X::from_api(expr, xctx, pos.clone(), i).await),
|
||||
Handle(tk => H::from_api(tk, hctx, pos.clone(), i).await)
|
||||
});
|
||||
Self { range: tt.range.clone(), tok }
|
||||
Self { sr: pos, tok }
|
||||
}
|
||||
|
||||
pub async fn into_api(
|
||||
@@ -134,7 +139,7 @@ impl<H: ExprRepr, X: ExtraTok> TokTree<H, X> {
|
||||
Handle(hand.into_api(hctx).await),
|
||||
NewExpr(expr.into_api(xctx).await),
|
||||
});
|
||||
api::TokenTree { range: self.range.clone(), token }
|
||||
api::TokenTree { range: self.sr.range.clone(), token }
|
||||
}
|
||||
|
||||
pub fn is_kw(&self, tk: Tok<String>) -> bool { self.tok.is_kw(tk) }
|
||||
@@ -152,8 +157,9 @@ impl<H: ExprRepr, X: ExtraTok> TokTree<H, X> {
|
||||
}
|
||||
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;
|
||||
let arg_range = ttv_range(&arg).expect("Lambda with empty arg!");
|
||||
let mut s_range = arg_range.clone();
|
||||
s_range.range.end = body.last().expect("Lambda with empty body!").sr.range.end;
|
||||
body.insert(0, Token::LambdaHead(arg).at(arg_range));
|
||||
Token::S(Paren::Round, body).at(s_range)
|
||||
}
|
||||
@@ -168,11 +174,12 @@ pub async fn ttv_from_api<H: ExprRepr, X: ExtraTok>(
|
||||
tokv: impl IntoIterator<Item: Borrow<api::TokenTree>>,
|
||||
hctx: &mut H::FromApiCtx<'_>,
|
||||
xctx: &mut X::FromApiCtx<'_>,
|
||||
src: &Sym,
|
||||
i: &Interner,
|
||||
) -> Vec<TokTree<H, X>> {
|
||||
stream! {
|
||||
for tok in tokv {
|
||||
yield TokTree::<H, X>::from_api(tok.borrow(), hctx, xctx, i).boxed_local().await
|
||||
yield TokTree::<H, X>::from_api(tok.borrow(), hctx, xctx, src, i).boxed_local().await
|
||||
}
|
||||
}
|
||||
.collect()
|
||||
@@ -201,8 +208,8 @@ pub fn wrap_tokv<H: ExprRepr, X: ExtraTok>(
|
||||
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)
|
||||
let sr = ttv_range(&items_v).expect("empty handled above");
|
||||
Token::S(api::Paren::Round, items_v).at(sr)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -237,7 +244,7 @@ pub enum Token<H: ExprRepr, X: ExtraTok> {
|
||||
Bottom(OrcErrv),
|
||||
}
|
||||
impl<H: ExprRepr, X: ExtraTok> Token<H, X> {
|
||||
pub fn at(self, range: Range<u32>) -> TokTree<H, X> { TokTree { range, tok: self } }
|
||||
pub fn at(self, sr: SrcRange) -> TokTree<H, X> { TokTree { sr, 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<H, X>]> {
|
||||
match self {
|
||||
@@ -273,9 +280,9 @@ impl<H: ExprRepr, X: ExtraTok> Format for Token<H, X> {
|
||||
}
|
||||
}
|
||||
|
||||
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 fn ttv_range<'a>(ttv: &[TokTree<impl ExprRepr + 'a, impl ExtraTok + 'a>]) -> Option<SrcRange> {
|
||||
let range = ttv.first()?.sr.range.start..ttv.last().unwrap().sr.range.end;
|
||||
Some(SrcRange { path: ttv.first().unwrap().sr.path(), range })
|
||||
}
|
||||
|
||||
pub async fn ttv_fmt<'a: 'b, 'b>(
|
||||
|
||||
Reference in New Issue
Block a user