forked from Orchid/orchid
Formatter introduced
This commit is contained in:
@@ -1,11 +1,17 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::convert::Infallible;
|
||||
use std::future::Future;
|
||||
use std::iter;
|
||||
use std::rc::Rc;
|
||||
use std::str::FromStr;
|
||||
|
||||
use itertools::Itertools;
|
||||
use never::Never;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::interner::Interner;
|
||||
use crate::{api, match_mapping};
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct FmtUnit {
|
||||
pub subs: Vec<FmtUnit>,
|
||||
@@ -15,6 +21,36 @@ impl FmtUnit {
|
||||
pub fn new(variants: Rc<Variants>, subs: impl IntoIterator<Item = FmtUnit>) -> Self {
|
||||
Self { subs: subs.into_iter().collect(), variants }
|
||||
}
|
||||
pub fn from_api(api: &api::FormattingUnit) -> Self {
|
||||
Self {
|
||||
subs: api.subs.iter().map(Self::from_api).collect(),
|
||||
variants: Rc::new(Variants(
|
||||
(api.variants.iter().map(|var| Variant {
|
||||
bounded: var.bounded,
|
||||
elements: var.elements.iter().map(FmtElement::from_api).collect(),
|
||||
}))
|
||||
.collect(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
pub fn to_api(&self) -> api::FormattingUnit {
|
||||
api::FormattingUnit {
|
||||
subs: self.subs.iter().map(Self::to_api).collect(),
|
||||
variants: (self.variants.0.iter().map(|var| api::FormattingVariant {
|
||||
bounded: var.bounded,
|
||||
elements: var.elements.iter().map(FmtElement::to_api).collect(),
|
||||
}))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
pub fn sequence(
|
||||
delim: &str,
|
||||
seq_bnd: Option<bool>,
|
||||
seq: impl IntoIterator<Item = FmtUnit>,
|
||||
) -> Self {
|
||||
let items = seq.into_iter().collect_vec();
|
||||
FmtUnit::new(Variants::sequence(items.len(), delim, seq_bnd), items)
|
||||
}
|
||||
}
|
||||
impl<T> From<T> for FmtUnit
|
||||
where Variants: From<T>
|
||||
@@ -24,66 +60,207 @@ where Variants: From<T>
|
||||
impl FromStr for FmtUnit {
|
||||
type Err = Infallible;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self { subs: vec![], variants: Rc::new(Variants::new([s])) })
|
||||
Ok(Self { subs: vec![], variants: Rc::new(Variants::default().bounded(s)) })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub enum FmtElement {
|
||||
Sub(u8),
|
||||
Sub { slot: u32, bounded: Option<bool> },
|
||||
String(Rc<String>),
|
||||
InlineSub(u8),
|
||||
Indent(Vec<FmtElement>),
|
||||
}
|
||||
impl FmtElement {
|
||||
pub fn str(s: &'_ str) -> Self { Self::String(Rc::new(s.to_string())) }
|
||||
pub fn sub(slot: u32, bounded: Option<bool>) -> Self { Self::Sub { slot, bounded } }
|
||||
pub fn bounded(i: u32) -> Self { Self::sub(i, Some(true)) }
|
||||
pub fn unbounded(i: u32) -> Self { Self::sub(i, Some(false)) }
|
||||
pub fn last(i: u32) -> Self { Self::sub(i, None) }
|
||||
pub fn sequence(len: usize, bounded: Option<bool>) -> impl Iterator<Item = Self> {
|
||||
let len32: u32 = len.try_into().unwrap();
|
||||
(0..len32 - 1).map(FmtElement::unbounded).chain([FmtElement::sub(len32 - 1, bounded)])
|
||||
}
|
||||
pub fn from_api(api: &api::FormattingElement) -> Self {
|
||||
match_mapping!(api, api::FormattingElement => FmtElement {
|
||||
Indent(v => v.iter().map(FmtElement::from_api).collect()),
|
||||
String(s => Rc::new(s.clone())),
|
||||
Sub{ *slot, *bounded },
|
||||
})
|
||||
}
|
||||
pub fn to_api(&self) -> api::FormattingElement {
|
||||
match_mapping!(self, FmtElement => api::FormattingElement {
|
||||
Indent(v => v.iter().map(FmtElement::to_api).collect()),
|
||||
String(s => s.to_string()),
|
||||
Sub{ *slot, *bounded },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct Variants(pub Vec<Vec<FmtElement>>);
|
||||
pub struct Variant {
|
||||
pub bounded: bool,
|
||||
pub elements: Vec<FmtElement>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, Default)]
|
||||
pub struct Variants(pub Vec<Variant>);
|
||||
impl Variants {
|
||||
pub fn new<'a>(variants: impl IntoIterator<Item = &'a str>) -> Self {
|
||||
let re = Regex::new(r"(?<tpl>\{\d+?-?\})|(\{\{)|(\}\})").unwrap();
|
||||
Self(Vec::from_iter(variants.into_iter().map(|s: &str| {
|
||||
let matches = re.captures_iter(s);
|
||||
let slots = matches.into_iter().filter_map(|m| m.name("tpl")).map(|tpl| {
|
||||
let no_opencurly = tpl.as_str().strip_prefix("{").expect("required by regex");
|
||||
let maybe_dash = no_opencurly.strip_suffix("}").expect("required by regex");
|
||||
let (num, had_dash) =
|
||||
maybe_dash.strip_suffix('-').map_or((maybe_dash, false), |s| (s, true));
|
||||
let idx = num.parse::<u8>().expect("Decimal digits required by regex");
|
||||
(tpl.range(), idx, had_dash)
|
||||
});
|
||||
(iter::once(None).chain(slots.into_iter().map(Some)).chain(None).tuple_windows())
|
||||
.flat_map(|(l, r)| {
|
||||
let string = match (l, &r) {
|
||||
(None, Some((r, ..))) => &s[..r.start],
|
||||
(Some((r1, ..)), Some((r2, ..))) => &s[r1.end..r2.start],
|
||||
(Some((r, ..)), None) => &s[r.end..],
|
||||
(None, None) => s,
|
||||
};
|
||||
let str_item = FmtElement::String(Rc::new(string.to_string()));
|
||||
match r {
|
||||
None => itertools::Either::Left([str_item]),
|
||||
Some((_, idx, inline)) => itertools::Either::Right([str_item, match inline {
|
||||
true => FmtElement::InlineSub(idx),
|
||||
false => FmtElement::Sub(idx),
|
||||
}]),
|
||||
}
|
||||
.into_iter()
|
||||
})
|
||||
.coalesce(|left, right| match (left, right) {
|
||||
(FmtElement::String(left), FmtElement::String(right)) =>
|
||||
Ok(FmtElement::String(Rc::new(left.to_string() + right.as_str()))),
|
||||
tuple => Err(tuple),
|
||||
})
|
||||
.collect_vec()
|
||||
})))
|
||||
fn parse_phs(s: &'_ str) -> Vec<FmtElement> {
|
||||
let re = Regex::new(r"(?<tpl>\{\d+?[bl]?\})| (\{\{)|(\}\})").unwrap();
|
||||
let matches = re.captures_iter(s);
|
||||
let slots = matches.into_iter().filter_map(|m| m.name("tpl")).map(|tpl| {
|
||||
let no_opencurly = tpl.as_str().strip_prefix("{").expect("required by regex");
|
||||
let maybe_dash = no_opencurly.strip_suffix("}").expect("required by regex");
|
||||
// we know it's not empty
|
||||
let last_char = maybe_dash.as_bytes()[maybe_dash.len() - 1] as char;
|
||||
let (num, bounded) = if !last_char.is_ascii_digit() {
|
||||
let bounded = match last_char {
|
||||
'b' => Some(true),
|
||||
'l' => None,
|
||||
_ => panic!("Invalid modifier char"),
|
||||
};
|
||||
(&maybe_dash[0..maybe_dash.len() - 1], bounded)
|
||||
} else {
|
||||
(maybe_dash, Some(false))
|
||||
};
|
||||
let idx = num.parse::<u32>().expect("Decimal digits required by regex");
|
||||
(tpl.range(), idx, bounded)
|
||||
});
|
||||
(iter::once(None).chain(slots.into_iter().map(Some)).chain(None).tuple_windows())
|
||||
.flat_map(|(l, r)| {
|
||||
let string = match (l, &r) {
|
||||
(None, Some((r, ..))) => &s[..r.start],
|
||||
(Some((r1, ..)), Some((r2, ..))) => &s[r1.end..r2.start],
|
||||
(Some((r, ..)), None) => &s[r.end..],
|
||||
(None, None) => s,
|
||||
};
|
||||
let str_item = FmtElement::String(Rc::new(string.to_string()));
|
||||
match r {
|
||||
None => itertools::Either::Left([str_item]),
|
||||
Some((_, idx, bounded)) =>
|
||||
itertools::Either::Right([str_item, FmtElement::Sub { slot: idx, bounded }]),
|
||||
}
|
||||
.into_iter()
|
||||
})
|
||||
.coalesce(|left, right| match (left, right) {
|
||||
(FmtElement::String(left), FmtElement::String(right)) =>
|
||||
Ok(FmtElement::String(Rc::new(left.to_string() + right.as_str()))),
|
||||
tuple => Err(tuple),
|
||||
})
|
||||
.collect_vec()
|
||||
}
|
||||
fn parse(s: &'_ str) -> Vec<FmtElement> {
|
||||
let mut lines = s.lines();
|
||||
let Some(mut cur) = lines.next() else { return vec![] };
|
||||
return indent_blk(&mut cur, &mut lines, 0);
|
||||
fn indent_blk<'a>(
|
||||
cur: &mut &'a str,
|
||||
lines: &mut impl Iterator<Item = &'a str>,
|
||||
blk_lv: usize,
|
||||
) -> Vec<FmtElement> {
|
||||
let mut out = Vec::new();
|
||||
loop {
|
||||
let line_lv = cur.chars().take_while(|c| *c == '\t').count();
|
||||
match line_lv.cmp(&blk_lv) {
|
||||
Ordering::Greater => out.push(FmtElement::Indent(indent_blk(cur, lines, blk_lv + 1))),
|
||||
Ordering::Equal => out.extend(Variants::parse_phs(&cur[blk_lv..])),
|
||||
Ordering::Less => return out,
|
||||
}
|
||||
match lines.next() {
|
||||
Some(line) => *cur = line,
|
||||
None => return out,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn add(&mut self, bounded: bool, s: &'_ str) {
|
||||
self.0.push(Variant { bounded, elements: Self::parse(s) })
|
||||
}
|
||||
// This option is available in all positions
|
||||
pub fn bounded(mut self, s: &'_ str) -> Self {
|
||||
self.add(true, s);
|
||||
self
|
||||
}
|
||||
// This option is only available in positions immediately preceding the end of
|
||||
// the sequence or a parenthesized subsequence.
|
||||
pub fn unbounded(mut self, s: &'_ str) -> Self {
|
||||
self.add(false, s);
|
||||
self
|
||||
}
|
||||
pub fn sequence(len: usize, delim: &str, seq_bnd: Option<bool>) -> Rc<Self> {
|
||||
let seq = Itertools::intersperse(FmtElement::sequence(len, seq_bnd), FmtElement::str(delim));
|
||||
Rc::new(Variants(vec![Variant { bounded: true, elements: seq.collect_vec() }]))
|
||||
}
|
||||
pub fn units(self: &Rc<Self>, subs: impl IntoIterator<Item = FmtUnit>) -> FmtUnit {
|
||||
FmtUnit::new(self.clone(), subs)
|
||||
}
|
||||
}
|
||||
impl From<Rc<String>> for Variants {
|
||||
fn from(value: Rc<String>) -> Self {
|
||||
Self(vec![Variant { elements: vec![FmtElement::String(value)], bounded: true }])
|
||||
}
|
||||
}
|
||||
impl From<String> for Variants {
|
||||
fn from(value: String) -> Self { Self(vec![vec![FmtElement::String(Rc::new(value))]]) }
|
||||
}
|
||||
impl From<Rc<String>> for Variants {
|
||||
fn from(value: Rc<String>) -> Self { Self(vec![vec![FmtElement::String(value)]]) }
|
||||
fn from(value: String) -> Self { Self::from(Rc::new(value)) }
|
||||
}
|
||||
impl FromStr for Variants {
|
||||
type Err = Infallible;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> { Ok(Self::new([s])) }
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> { Ok(Self::default().bounded(s)) }
|
||||
}
|
||||
|
||||
fn indent_str(s: &str, indent: u16) -> String {
|
||||
s.replace("\n", &format!("\n{}", "\t".repeat(indent.into())))
|
||||
}
|
||||
|
||||
fn fill_slots<'a, 'b>(
|
||||
elements: impl IntoIterator<Item = &'a FmtElement>,
|
||||
values: &[FmtUnit],
|
||||
indent: u16,
|
||||
last_bounded: bool,
|
||||
) -> String {
|
||||
elements
|
||||
.into_iter()
|
||||
.map(|el| match el {
|
||||
FmtElement::String(s) => indent_str(s, indent),
|
||||
FmtElement::Sub { slot, bounded } =>
|
||||
indent_str(&take_first(&values[*slot as usize], bounded.unwrap_or(last_bounded)), indent),
|
||||
FmtElement::Indent(elements) => fill_slots(elements, values, indent + 1, last_bounded),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// The simplest possible print strategy
|
||||
pub fn take_first(unit: &FmtUnit, bounded: bool) -> String {
|
||||
let first = unit.variants.0.iter().find(|v| v.bounded || bounded).expect("No bounded variant!");
|
||||
fill_slots(&first.elements, &unit.subs, 0, bounded)
|
||||
}
|
||||
|
||||
pub async fn take_first_fmt(v: &(impl Format + ?Sized), i: &Interner) -> String {
|
||||
take_first(&v.print(&FmtCtxImpl { i }).await, false)
|
||||
}
|
||||
|
||||
pub struct FmtCtxImpl<'a> {
|
||||
pub i: &'a Interner,
|
||||
}
|
||||
|
||||
pub trait FmtCtx {
|
||||
fn i(&self) -> &Interner;
|
||||
// fn print_as(&self, p: &(impl Format + ?Sized)) -> impl Future<Output =
|
||||
// String> where Self: Sized {
|
||||
// async {
|
||||
// // for now, always take the first option which is probably the one-line
|
||||
// form let variants = p.print(self).await;
|
||||
// take_first(&variants, true)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
impl FmtCtx for FmtCtxImpl<'_> {
|
||||
fn i(&self) -> &Interner { self.i }
|
||||
}
|
||||
|
||||
pub trait Format {
|
||||
fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> impl Future<Output = FmtUnit> + 'a;
|
||||
}
|
||||
impl Format for Never {
|
||||
async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { match *self {} }
|
||||
}
|
||||
|
||||
@@ -25,5 +25,6 @@ pub mod pure_seq;
|
||||
pub mod reqnot;
|
||||
pub mod sequence;
|
||||
pub mod side;
|
||||
mod tl_cache;
|
||||
pub mod tokens;
|
||||
pub mod tree;
|
||||
|
||||
@@ -3,16 +3,17 @@ use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_stream::stream;
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures::future::{LocalBoxFuture, 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};
|
||||
use crate::{api, match_mapping, tl_cache};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct MacroSlot<'a>(api::MacroTreeId, PhantomData<&'a ()>);
|
||||
@@ -49,6 +50,11 @@ impl<'a, A> MTree<'a, A> {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<A: Format> Format for MTree<'_, A> {
|
||||
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
self.tok.print(c).await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum MTok<'a, A> {
|
||||
@@ -96,6 +102,38 @@ impl<'a, A> MTok<'a, A> {
|
||||
}
|
||||
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).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).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>,
|
||||
@@ -121,3 +159,10 @@ pub async fn mtreev_to_api<'a: 'b, 'b, A: 'b>(
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ macro_rules! match_mapping {
|
||||
$($branches:tt)*
|
||||
} $({
|
||||
$($extra:tt)*
|
||||
})?) => {
|
||||
})?) => {{
|
||||
type Helper<T> = T;
|
||||
match_mapping!(@BRANCH_MUNCH
|
||||
(($input) ($($src)*) ($tgt) ($($($extra)*)?))
|
||||
()
|
||||
@@ -25,7 +26,7 @@ macro_rules! match_mapping {
|
||||
)
|
||||
// note: we're adding a comma to the input so the optional trailing comma becomes
|
||||
// an optional second comma which is easier to match
|
||||
};
|
||||
}};
|
||||
// ======== Process match branches
|
||||
// Can't generate branches individually so gather them into a collection and render them here
|
||||
(@BRANCHES_DONE ( ($input:expr) $src:tt ($tgt:ty) ($($extra:tt)*) )
|
||||
@@ -35,7 +36,7 @@ macro_rules! match_mapping {
|
||||
match $input {
|
||||
$(
|
||||
match_mapping!(@PAT ($src $variant) $($pat)*) =>
|
||||
match_mapping!(@VAL (< $tgt >:: $variant) $($pat)*),
|
||||
match_mapping!(@VAL (Helper::< $tgt >:: $variant) $($pat)*),
|
||||
)*
|
||||
$($extra)*
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::fmt::{self, Display};
|
||||
use std::iter;
|
||||
use std::ops::{Deref, Range};
|
||||
|
||||
@@ -6,7 +7,8 @@ use itertools::Itertools;
|
||||
|
||||
use crate::api;
|
||||
use crate::error::{OrcRes, Reporter, mk_err, mk_errv};
|
||||
use crate::interner::{Internable, Interned, Interner, Tok};
|
||||
use crate::format::{Format, take_first_fmt};
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::location::Pos;
|
||||
use crate::name::VPath;
|
||||
use crate::tree::{AtomRepr, ExtraTok, Paren, TokTree, Token};
|
||||
@@ -16,6 +18,8 @@ pub fn name_char(c: char) -> bool { name_start(c) || c.is_numeric() }
|
||||
pub fn op_char(c: char) -> bool { !name_char(c) && !c.is_whitespace() && !"()[]{}\\".contains(c) }
|
||||
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>,
|
||||
@@ -30,10 +34,7 @@ impl<'a, 'b, A: AtomRepr, X: ExtraTok> Snippet<'a, 'b, A, X> {
|
||||
) -> Self {
|
||||
Self { prev, cur, interner }
|
||||
}
|
||||
pub async fn i<T: Interned>(&self, arg: &(impl Internable<Interned = T> + ?Sized)) -> Tok<T> {
|
||||
self.interner.i(arg).await
|
||||
}
|
||||
pub fn interner(&self) -> &'a Interner { self.interner }
|
||||
pub fn i(&self) -> &'a Interner { self.interner }
|
||||
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 };
|
||||
@@ -66,7 +67,9 @@ impl<'a, 'b, A: AtomRepr, X: ExtraTok> Snippet<'a, 'b, A, X> {
|
||||
mut f: impl FnMut(&Token<'b, A, X>) -> bool,
|
||||
) -> impl Iterator<Item = Self> {
|
||||
iter::from_fn(move || {
|
||||
self.is_empty().then_some(())?;
|
||||
if self.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let (ret, next) = self.split_once(&mut f).unwrap_or(self.split_at(self.len()));
|
||||
self = next;
|
||||
Some(ret)
|
||||
@@ -77,6 +80,8 @@ impl<'a, 'b, A: AtomRepr, X: ExtraTok> Snippet<'a, 'b, A, X> {
|
||||
let non_fluff_start = self.find_idx(|t| !matches!(t, Token::NS | 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> {
|
||||
@@ -116,6 +121,10 @@ impl Comment {
|
||||
}
|
||||
}
|
||||
|
||||
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>> {
|
||||
@@ -134,7 +143,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(&**c).await, pos: Pos::Range(t.range.clone()) },
|
||||
Comment { text: tail.i().i(&**c).await, pos: Pos::Range(t.range.clone()) },
|
||||
_ => unreachable!("All are comments checked above"),
|
||||
}
|
||||
}))
|
||||
@@ -151,17 +160,18 @@ pub async fn try_pop_no_fluff<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
||||
) -> ParseRes<'a, 'b, &'a TokTree<'b, A, X>, A, X> {
|
||||
match snip.skip_fluff().pop_front() {
|
||||
Some((output, tail)) => Ok(Parsed { output, tail }),
|
||||
None => Err(mk_errv(snip.i("Unexpected end").await, "Pattern ends abruptly", [Pos::Range(
|
||||
snip.pos(),
|
||||
)
|
||||
.into()])),
|
||||
None =>
|
||||
Err(mk_errv(snip.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<()> {
|
||||
match snip.skip_fluff().get(0) {
|
||||
Some(surplus) => Err(mk_errv(
|
||||
snip.i("Extra code after end of line").await,
|
||||
snip.i().i("Extra code after end of line").await,
|
||||
"Code found after the end of the line",
|
||||
[Pos::Range(surplus.range.clone()).into()],
|
||||
)),
|
||||
@@ -177,8 +187,8 @@ pub async fn expect_tok<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
||||
match &head.tok {
|
||||
Token::Name(n) if *n == tok => Ok(Parsed { output: (), tail }),
|
||||
t => Err(mk_errv(
|
||||
snip.i("Expected specific keyword").await,
|
||||
format!("Expected {tok} but found {:?}", t.print().await),
|
||||
snip.i().i("Expected specific keyword").await,
|
||||
format!("Expected {tok} but found {:?}", snip.fmt(t).await),
|
||||
[Pos::Range(head.range.clone()).into()],
|
||||
)),
|
||||
}
|
||||
@@ -201,11 +211,11 @@ 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<(Vec<Tok<String>>, Option<Tok<String>>, Pos)>, A, X> {
|
||||
let comma = tail.i(",").await;
|
||||
let globstar = tail.i("*").await;
|
||||
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("Expected name").await,
|
||||
tail.i().i("Expected name").await,
|
||||
"Expected a name, a list of names, or a globstar.",
|
||||
[Pos::Range(tail.pos()).into()],
|
||||
));
|
||||
@@ -213,9 +223,10 @@ pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
||||
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("Unexpected name prefix").await, "Only names can precede ::", [
|
||||
Pos::Range(name.range.clone()).into(),
|
||||
])),
|
||||
_ =>
|
||||
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())),
|
||||
@@ -235,7 +246,7 @@ pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
||||
n if *n == globstar => None,
|
||||
n if n.starts_with(op_char) => {
|
||||
return Err(mk_errv(
|
||||
tail.i("Unescaped operator in multiname").await,
|
||||
tail.i().i("Unescaped operator in multiname").await,
|
||||
"Operators in multinames should be enclosed in []",
|
||||
[Pos::Range(name.range.clone()).into()],
|
||||
));
|
||||
@@ -252,7 +263,7 @@ pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
||||
ok.push((vec![], Some(n.clone()), Pos::Range(tt.range.clone()))),
|
||||
Token::BR | Token::Comment(_) => (),
|
||||
_ => ctx.report(mk_err(
|
||||
tail.i("Non-operator in escapement in multiname").await,
|
||||
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()],
|
||||
)),
|
||||
@@ -269,7 +280,7 @@ pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
||||
Ok(Parsed { output, tail }) => match tail.get(0) {
|
||||
None => ok.extend(output),
|
||||
Some(t) => ctx.report(mk_err(
|
||||
tail.i("Unexpected token in multiname group").await,
|
||||
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()],
|
||||
)),
|
||||
@@ -280,8 +291,8 @@ pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
||||
},
|
||||
t => {
|
||||
return Err(mk_errv(
|
||||
tail.i("Unrecognized name end").await,
|
||||
format!("Names cannot end with {:?} tokens", t.print().await),
|
||||
tail.i().i("Unrecognized name end").await,
|
||||
format!("Names cannot end with {:?} tokens", tail.fmt(t).await),
|
||||
[Pos::Range(name.range.clone()).into()],
|
||||
));
|
||||
},
|
||||
@@ -303,16 +314,11 @@ pub struct Import {
|
||||
pub path: VPath,
|
||||
pub name: Option<Tok<String>>,
|
||||
}
|
||||
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()),
|
||||
// }
|
||||
// }
|
||||
|
||||
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()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
9
orchid-base/src/tl_cache.rs
Normal file
9
orchid-base/src/tl_cache.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
#[macro_export]
|
||||
macro_rules! tl_cache {
|
||||
($ty:ty : $expr:expr) => {{
|
||||
thread_local! {
|
||||
static V: $ty = $expr;
|
||||
}
|
||||
V.with(|v| v.clone())
|
||||
}};
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::fmt::{self, Debug, Display};
|
||||
use std::future::{Future, ready};
|
||||
use std::future::Future;
|
||||
use std::iter;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Range;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use api::PhKind;
|
||||
@@ -16,16 +17,16 @@ use ordered_float::NotNan;
|
||||
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::PathSlice;
|
||||
use crate::parse::Snippet;
|
||||
use crate::tokens::PARENS;
|
||||
use crate::{api, match_mapping};
|
||||
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 = Display + Clone + fmt::Debug;
|
||||
pub trait ExtraTok = Format + Clone + fmt::Debug;
|
||||
pub trait RefDoExtra<X> =
|
||||
for<'b> FnMut(&'b X, Range<u32>) -> LocalBoxFuture<'b, api::TokenTree>;
|
||||
}
|
||||
@@ -46,22 +47,15 @@ pub fn recur<'a, A: AtomRepr, X: ExtraTok>(
|
||||
})
|
||||
}
|
||||
|
||||
pub trait AtomRepr: Clone {
|
||||
pub trait AtomRepr: Clone + Format {
|
||||
type Ctx: ?Sized;
|
||||
fn from_api(api: &api::Atom, pos: Pos, ctx: &mut Self::Ctx) -> impl Future<Output = Self>;
|
||||
fn to_api(&self) -> impl Future<Output = orchid_api::Atom> + '_;
|
||||
fn print(&self) -> impl Future<Output = String> + '_;
|
||||
}
|
||||
impl AtomRepr for Never {
|
||||
type Ctx = Never;
|
||||
#[allow(unreachable_code)]
|
||||
fn from_api(_: &api::Atom, _: Pos, ctx: &mut Self::Ctx) -> impl Future<Output = Self> {
|
||||
ready(match *ctx {})
|
||||
}
|
||||
#[allow(unreachable_code)]
|
||||
fn to_api(&self) -> impl Future<Output = orchid_api::Atom> + '_ { ready(match *self {}) }
|
||||
#[allow(unreachable_code)]
|
||||
fn print(&self) -> impl Future<Output = String> + '_ { ready(match *self {}) }
|
||||
async fn from_api(_: &api::Atom, _: Pos, ctx: &mut Self::Ctx) -> Self { match *ctx {} }
|
||||
async fn to_api(&self) -> orchid_api::Atom { match *self {} }
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||
@@ -152,7 +146,11 @@ impl<'b, A: AtomRepr, X: ExtraTok> TokTree<'b, A, X> {
|
||||
body.insert(0, Token::LambdaHead(arg).at(arg_range));
|
||||
Token::S(Paren::Round, body).at(s_range)
|
||||
}
|
||||
pub async fn print(&self) -> String { self.tok.print().await }
|
||||
}
|
||||
impl<A: AtomRepr, X: ExtraTok> Format for TokTree<'_, A, 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>(
|
||||
@@ -245,32 +243,34 @@ impl<'a, A: AtomRepr, X: ExtraTok> Token<'a, A, X> {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub async fn print(&self) -> String {
|
||||
}
|
||||
impl<A: AtomRepr, X: ExtraTok> Format for Token<'_, A, X> {
|
||||
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
match self {
|
||||
Self::Atom(a) => a.print().await,
|
||||
Self::BR => "\n".to_string(),
|
||||
Self::Bottom(err) if err.len() == 1 => format!("Bottom({}) ", err.one().unwrap()),
|
||||
Self::Bottom(err) => format!("Botttom(\n{}) ", indent(&err.to_string())),
|
||||
Self::Comment(c) => format!("--[{c}]-- "),
|
||||
Self::LambdaHead(arg) => format!("\\ {} . ", indent(&ttv_fmt(arg).await)),
|
||||
Self::NS => ":: ".to_string(),
|
||||
Self::Name(n) => format!("{n} "),
|
||||
Self::Slot(th) => format!("{th} "),
|
||||
Self::Ph(Ph { kind, name }) => match &kind {
|
||||
PhKind::Scalar => format!("${name}"),
|
||||
PhKind::Vector { at_least_one, priority } => {
|
||||
let prefix = if *at_least_one { "..." } else { ".." };
|
||||
let suffix = if 0 < *priority { format!(":{priority}") } else { String::new() };
|
||||
format!("{prefix}${name}{suffix}")
|
||||
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(),
|
||||
Self::Name(n) => format!("{n}").into(),
|
||||
Self::Slot(th) => format!("{th}").into(),
|
||||
Self::Ph(ph) => format!("{ph}").into(),
|
||||
Self::S(p, b) => 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}]")),
|
||||
},
|
||||
},
|
||||
Self::S(p, b) => {
|
||||
let (lp, rp, _) = PARENS.iter().find(|(_, _, par)| par == p).unwrap();
|
||||
format!("{lp} {}{rp} ", indent(&ttv_fmt(b).await))
|
||||
},
|
||||
Self::X(x) => format!("{x} "),
|
||||
Self::Macro(None) => "macro ".to_string(),
|
||||
Self::Macro(Some(prio)) => format!("macro({prio})"),
|
||||
[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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -282,8 +282,9 @@ pub fn ttv_range(ttv: &[TokTree<'_, impl AtomRepr, impl ExtraTok>]) -> Range<u32
|
||||
|
||||
pub async fn ttv_fmt<'a: 'b, 'b>(
|
||||
ttv: impl IntoIterator<Item = &'b TokTree<'a, impl AtomRepr + 'b, impl ExtraTok + 'b>>,
|
||||
) -> String {
|
||||
join_all(ttv.into_iter().map(|tt| tt.print())).await.join("")
|
||||
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 ") }
|
||||
@@ -301,6 +302,18 @@ impl Ph {
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user