base and extension fully compiles, host in good shape

This commit is contained in:
2025-01-25 00:10:49 +01:00
parent 7be8716b19
commit 2b79e96dc9
49 changed files with 1719 additions and 1168 deletions

View File

@@ -20,6 +20,7 @@ orchid-api = { version = "0.1.0", path = "../orchid-api" }
orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" }
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
ordered-float = "4.6.0"
regex = "1.11.1"
rust-embed = "8.5.0"
rust_decimal = "1.36.0"
some_executor = "0.4.0"

View File

@@ -12,9 +12,9 @@ use crate::api;
/// There are no ordering guarantees about these
pub trait ExtPort {
fn send<'a>(&'a self, msg: &'a [u8]) -> LocalBoxFuture<'a, ()>;
fn recv<'a, 's: 'a>(
&'s self,
cb: Box<dyn FnOnce(&[u8]) -> LocalBoxFuture<'a, ()> + 'a>,
fn recv<'a>(
&'a self,
cb: Box<dyn FnOnce(&[u8]) -> LocalBoxFuture<'_, ()> + 'a>,
) -> LocalBoxFuture<'a, ()>;
}
@@ -26,7 +26,7 @@ impl ExtInit {
pub async fn send(&self, msg: &[u8]) { self.port.send(msg).await }
pub async fn recv<'a, 's: 'a>(
&'s self,
cb: Box<dyn FnOnce(&[u8]) -> LocalBoxFuture<'a, ()> + 'a>,
cb: Box<dyn FnOnce(&[u8]) -> LocalBoxFuture<'_, ()> + 'a>,
) {
self.port.recv(Box::new(cb)).await
}

89
orchid-base/src/format.rs Normal file
View File

@@ -0,0 +1,89 @@
use std::convert::Infallible;
use std::iter;
use std::rc::Rc;
use std::str::FromStr;
use itertools::Itertools;
use regex::Regex;
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct FmtUnit {
pub subs: Vec<FmtUnit>,
pub variants: Rc<Variants>,
}
impl FmtUnit {
pub fn new(variants: Rc<Variants>, subs: impl IntoIterator<Item = FmtUnit>) -> Self {
Self { subs: subs.into_iter().collect(), variants }
}
}
impl<T> From<T> for FmtUnit
where Variants: From<T>
{
fn from(value: T) -> Self { Self { subs: vec![], variants: Rc::new(Variants::from(value)) } }
}
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])) })
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum FmtElement {
Sub(u8),
String(Rc<String>),
InlineSub(u8),
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct Variants(pub Vec<Vec<FmtElement>>);
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()
})))
}
}
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)]]) }
}
impl FromStr for Variants {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> { Ok(Self::new([s])) }
}

View File

@@ -247,7 +247,7 @@ impl Interner {
tok
}
/// Extern an identifier; query the data it represents if not known locally
async fn ex<M: InternMarker>(&self, marker: M) -> Tok<M::Interned> {
pub async fn ex<M: InternMarker>(&self, marker: M) -> Tok<M::Interned> {
if let Some(tok) = M::Interned::bimap(&mut *self.interners.lock().await).by_marker(marker) {
return tok;
}
@@ -284,6 +284,7 @@ pub fn merge_retained(into: &mut api::Retained, from: &api::Retained) {
#[cfg(test)]
mod test {
use std::num::NonZero;
use std::pin::Pin;
use orchid_api_traits::{Decode, enc_vec};
use test_executors::spin_on;
@@ -300,9 +301,11 @@ mod test {
#[test]
fn test_coding() {
let coded = api::TStr(NonZero::new(3u64).unwrap());
let mut enc = &enc_vec(&coded)[..];
api::TStr::decode(&mut enc);
assert_eq!(enc, [], "Did not consume all of {enc:?}")
spin_on(async {
let coded = api::TStr(NonZero::new(3u64).unwrap());
let mut enc = &enc_vec(&coded).await[..];
api::TStr::decode(Pin::new(&mut enc)).await;
assert_eq!(enc, [], "Did not consume all of {enc:?}")
})
}
}

View File

@@ -9,6 +9,7 @@ pub mod clone;
pub mod combine;
pub mod error;
pub mod event;
pub mod format;
pub mod id_store;
pub mod interner;
pub mod join;

View File

@@ -1,4 +1,5 @@
use std::marker::PhantomData;
use std::rc::Rc;
use std::sync::Arc;
use async_std::stream;
@@ -22,13 +23,14 @@ impl MacroSlot<'_> {
trait_set! {
pub trait MacroAtomToApi<A> = for<'a> FnMut(&'a A) -> LocalBoxFuture<'a, api::MacroToken>;
pub trait MacroAtomFromApi<'a, A> = FnMut(&api::Atom) -> MTok<'a, A>;
pub trait MacroAtomFromApi<'a, A> =
for<'b> FnMut(&'b api::Atom) -> LocalBoxFuture<'b, MTok<'a, A>>;
}
#[derive(Clone, Debug)]
pub struct MTree<'a, A> {
pub pos: Pos,
pub tok: Arc<MTok<'a, A>>,
pub tok: Rc<MTok<'a, A>>,
}
impl<'a, A> MTree<'a, A> {
pub(crate) async fn from_api(
@@ -38,7 +40,7 @@ impl<'a, A> MTree<'a, A> {
) -> Self {
Self {
pos: Pos::from_api(&api.location, i).await,
tok: Arc::new(MTok::from_api(&api.token, do_atom, 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 {
@@ -66,17 +68,17 @@ pub enum MTok<'a, A> {
impl<'a, A> MTok<'a, A> {
pub(crate) async fn from_api(
api: &api::MacroToken,
do_atom: &mut impl MacroAtomFromApi<'a, A>,
i: &Interner,
do_atom: &mut impl MacroAtomFromApi<'a, A>,
) -> Self {
match_mapping!(&api, api::MacroToken => MTok::<'a, A> {
Lambda(x => mtreev_from_api(x, do_atom, i).await, b => mtreev_from_api(b, do_atom, i).await),
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, do_atom, i).await),
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)
api::MacroToken::Atom(a) => do_atom(a).await
})
}
pub(crate) async fn to_api(&self, do_atom: &mut impl MacroAtomToApi<A>) -> api::MacroToken {
@@ -93,13 +95,13 @@ impl<'a, A> MTok<'a, A> {
MTok::Atom(a) => do_atom(a).await,
})
}
pub fn at(self, pos: Pos) -> MTree<'a, A> { MTree { pos, tok: Arc::new(self) } }
pub fn at(self, pos: Pos) -> MTree<'a, A> { MTree { pos, tok: Rc::new(self) } }
}
pub async fn mtreev_from_api<'a, 'b, A>(
api: impl IntoIterator<Item = &'b api::MacroTree>,
do_atom: &mut impl MacroAtomFromApi<'a, A>,
i: &Interner,
do_atom: &mut impl MacroAtomFromApi<'a, A>,
) -> Vec<MTree<'a, A>> {
let do_atom_lk = Mutex::new(do_atom);
stream::from_iter(api)

View File

@@ -6,7 +6,7 @@ use orchid_api_traits::{Decode, Encode};
pub async fn send_msg(mut write: Pin<&mut impl Write>, msg: &[u8]) -> io::Result<()> {
let mut len_buf = vec![];
u32::try_from(msg.len()).unwrap().encode(&mut len_buf);
u32::try_from(msg.len()).unwrap().encode(Pin::new(&mut len_buf)).await;
write.write_all(&len_buf).await?;
write.write_all(msg).await?;
write.flush().await
@@ -15,7 +15,7 @@ pub async fn send_msg(mut write: Pin<&mut impl Write>, msg: &[u8]) -> io::Result
pub async fn recv_msg(mut read: Pin<&mut impl Read>) -> io::Result<Vec<u8>> {
let mut len_buf = [0u8; (u32::BITS / 8) as usize];
read.read_exact(&mut len_buf).await?;
let len = u32::decode(&mut &len_buf[..]);
let len = u32::decode(Pin::new(&mut &len_buf[..])).await;
let mut msg = vec![0u8; len as usize];
read.read_exact(&mut msg).await?;
Ok(msg)

View File

@@ -33,6 +33,7 @@ impl<'a, 'b, A: AtomRepr, X: ExtraTok> Snippet<'a, 'b, A, X> {
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 split_at(self, pos: u32) -> (Self, Self) {
let Self { prev, cur, interner } = self;
let fst = Self { prev, cur: &cur[..pos as usize], interner };
@@ -177,7 +178,7 @@ pub async fn expect_tok<'a, 'b, A: AtomRepr, X: ExtraTok>(
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}"),
format!("Expected {tok} but found {:?}", t.print().await),
[Pos::Range(head.range.clone()).into()],
)),
}
@@ -280,7 +281,7 @@ 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 {t} tokens"),
format!("Names cannot end with {:?} tokens", t.print().await),
[Pos::Range(name.range.clone()).into()],
));
},

View File

@@ -4,6 +4,7 @@ use std::future::Future;
use std::marker::PhantomData;
use std::mem;
use std::ops::{BitAnd, Deref};
use std::pin::Pin;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
@@ -74,7 +75,7 @@ impl<'a, MS: MsgSet + 'static> RequestHandle<'a, MS> {
pub async fn respond(&self, response: &impl Encode) -> Receipt<'a> {
assert!(!self.fulfilled.swap(true, Ordering::Relaxed), "Already responded to {}", self.id);
let mut buf = (!self.id).to_be_bytes().to_vec();
response.encode(&mut buf);
response.encode(Pin::new(&mut buf)).await;
let mut send = clone_box(&*self.reqnot().0.lock().await.send);
(send)(&buf, self.parent.clone()).await;
Receipt(PhantomData)
@@ -126,18 +127,19 @@ impl<T: MsgSet> ReqNot<T> {
let mut g = self.0.lock().await;
let (id, payload) = get_id(message);
if id == 0 {
let mut notif = clone_box(&*g.notif);
let mut notif_cb = clone_box(&*g.notif);
mem::drop(g);
notif(<T::In as Channel>::Notif::decode(&mut &payload[..]), self.clone()).await
let notif_val = <T::In as Channel>::Notif::decode(Pin::new(&mut &payload[..])).await;
notif_cb(notif_val, self.clone()).await
} else if 0 < id.bitand(1 << 63) {
let sender = g.responses.remove(&!id).expect("Received response for invalid message");
sender.send(message.to_vec()).await.unwrap();
} else {
let message = <T::In as Channel>::Req::decode(&mut &payload[..]);
let mut req = clone_box(&*g.req);
let message = <T::In as Channel>::Req::decode(Pin::new(&mut &payload[..])).await;
let mut req_cb = clone_box(&*g.req);
mem::drop(g);
let rn = self.clone();
req(RequestHandle::new(rn, id), message).await;
req_cb(RequestHandle::new(rn, id), message).await;
}
}
@@ -145,7 +147,7 @@ impl<T: MsgSet> ReqNot<T> {
let mut send = clone_box(&*self.0.lock().await.send);
let mut buf = vec![0; 8];
let msg: <T::Out as Channel>::Notif = notif.into();
msg.encode(&mut buf);
msg.encode(Pin::new(&mut buf)).await;
send(&buf, self.clone()).await
}
}
@@ -180,7 +182,7 @@ impl<T: MsgSet> DynRequester for ReqNot<T> {
let id = g.id;
g.id += 1;
let mut buf = id.to_be_bytes().to_vec();
req.encode(&mut buf);
req.encode(Pin::new(&mut buf)).await;
let (send, recv) = channel::bounded(1);
g.responses.insert(id, send);
let mut send = clone_box(&*g.send);
@@ -206,7 +208,7 @@ pub trait Requester: DynRequester {
}
impl<This: DynRequester + ?Sized> Requester for This {
async fn request<R: Request + Into<Self::Transfer>>(&self, data: R) -> R::Response {
R::Response::decode(&mut &self.raw_request(data.into()).await[..])
R::Response::decode(Pin::new(&mut &self.raw_request(data.into()).await[..])).await
}
}

View File

@@ -1,6 +1,6 @@
use std::borrow::Borrow;
use std::cell::RefCell;
use std::fmt::{self, Debug, Display};
use std::future::{Future, ready};
use std::iter;
use std::marker::PhantomData;
use std::ops::Range;
@@ -9,7 +9,7 @@ use std::sync::Arc;
pub use api::PhKind;
use async_std::stream;
use async_std::sync::Mutex;
use futures::future::LocalBoxFuture;
use futures::future::{LocalBoxFuture, join_all};
use futures::{FutureExt, StreamExt};
use itertools::Itertools;
use never::Never;
@@ -47,15 +47,22 @@ pub fn recur<'a, A: AtomRepr, X: ExtraTok>(
})
}
pub trait AtomRepr: fmt::Display + Clone + fmt::Debug {
pub trait AtomRepr: Clone {
type Ctx: ?Sized;
fn from_api(api: &api::Atom, pos: Pos, ctx: &mut Self::Ctx) -> Self;
fn to_api(&self) -> orchid_api::Atom;
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;
fn from_api(_: &api::Atom, _: Pos, _: &mut Self::Ctx) -> Self { panic!() }
fn to_api(&self) -> orchid_api::Atom { match *self {} }
#[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 {}) }
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
@@ -79,7 +86,7 @@ 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)),
Atom(a => A::from_api(a, Pos::Range(tt.range.clone()), ctx).await),
Bottom(e => OrcErrv::from_api(e, i).await),
LambdaHead(arg => ttv_from_api(arg, ctx, i).await),
Name(n => Tok::from_api(*n, i).await),
@@ -94,7 +101,7 @@ impl<'b, A: AtomRepr, X: ExtraTok> TokTree<'b, A, X> {
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()),
Atom(a.to_api().await),
BR,
NS,
Bottom(e.to_api()),
@@ -111,20 +118,20 @@ impl<'b, A: AtomRepr, X: ExtraTok> TokTree<'b, A, X> {
api::TokenTree { range: self.range.clone(), token }
}
pub fn into_api(
pub async fn into_api(
self,
do_extra: &mut impl FnMut(X, Range<u32>) -> api::TokenTree,
) -> api::TokenTree {
let token = match self.tok {
Token::Atom(a) => api::Token::Atom(a.to_api()),
Token::Atom(a) => api::Token::Atom(a.to_api().await),
Token::BR => api::Token::BR,
Token::NS => api::Token::NS,
Token::Bottom(e) => api::Token::Bottom(e.to_api()),
Token::Comment(c) => api::Token::Comment(c.clone()),
Token::LambdaHead(arg) => api::Token::LambdaHead(ttv_into_api(arg, do_extra)),
Token::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)),
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()),
@@ -146,10 +153,7 @@ 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)
}
}
impl<A: AtomRepr, X: ExtraTok> Display for TokTree<'_, A, X> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.tok) }
pub async fn print(&self) -> String { self.tok.print().await }
}
pub async fn ttv_from_api<A: AtomRepr, X: ExtraTok>(
@@ -161,7 +165,7 @@ pub async fn ttv_from_api<A: AtomRepr, X: ExtraTok>(
stream::from_iter(tokv.into_iter())
.then(|t| async {
let t = t;
TokTree::<A, X>::from_api(t.borrow(), *ctx_lk.lock().await, i).await
TokTree::<A, X>::from_api(t.borrow(), *ctx_lk.lock().await, i).boxed_local().await
})
.collect()
.await
@@ -178,11 +182,15 @@ pub async fn ttv_to_api<'a, A: AtomRepr, X: ExtraTok>(
output
}
pub fn ttv_into_api<'a, A: AtomRepr, X: ExtraTok>(
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,
) -> Vec<api::TokenTree> {
tokv.into_iter().map(|t| t.into_api(do_extra)).collect_vec()
let mut new_tokv = Vec::new();
for item in tokv {
new_tokv.push(item.into_api(do_extra).await)
}
new_tokv
}
/// This takes a position and not a range because it assigns the range to
@@ -237,50 +245,32 @@ impl<'a, A: AtomRepr, X: ExtraTok> Token<'a, A, X> {
_ => None,
}
}
}
impl<A: AtomRepr, X: ExtraTok> Display for Token<'_, A, X> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
thread_local! {
static PAREN_LEVEL: RefCell<usize> = 0.into();
}
fn get_indent() -> usize { PAREN_LEVEL.with_borrow(|t| *t) }
fn with_indent<T>(f: impl FnOnce() -> T) -> T {
PAREN_LEVEL.with_borrow_mut(|t| *t += 1);
let r = f();
PAREN_LEVEL.with_borrow_mut(|t| *t -= 1);
r
}
pub async fn print(&self) -> String {
match self {
Self::Atom(a) => f.write_str(&indent(&format!("{a} "), get_indent(), false)),
Self::BR => write!(f, "\n{}", " ".repeat(get_indent())),
Self::Bottom(err) if err.len() == 1 => write!(f, "Bottom({}) ", err.one().unwrap()),
Self::Bottom(err) => {
write!(f, "Botttom(\n{}) ", indent(&err.to_string(), get_indent() + 1, true))
},
Self::Comment(c) => write!(f, "--[{c}]-- "),
Self::LambdaHead(arg) => with_indent(|| write!(f, "\\ {} . ", ttv_fmt(arg))),
Self::NS => f.write_str(":: "),
Self::Name(n) => write!(f, "{n} "),
Self::Slot(th) => write!(f, "{th} "),
Self::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 => write!(f, "${name}"),
PhKind::Scalar => format!("${name}"),
PhKind::Vector { at_least_one, priority } => {
if *at_least_one {
write!(f, ".")?
}
write!(f, "..${name}")?;
if 0 < *priority { write!(f, "{priority}") } else { Ok(()) }
let prefix = if *at_least_one { "..." } else { ".." };
let suffix = if 0 < *priority { format!(":{priority}") } else { String::new() };
format!("{prefix}${name}{suffix}")
},
},
Self::S(p, b) => {
let (lp, rp, _) = PARENS.iter().find(|(_, _, par)| par == p).unwrap();
write!(f, "{lp} ")?;
with_indent(|| f.write_str(&ttv_fmt(b)))?;
write!(f, "{rp} ")
format!("{lp} {}{rp} ", indent(&ttv_fmt(b).await))
},
Self::X(x) => write!(f, "{x} "),
Self::Macro(None) => write!(f, "macro "),
Self::Macro(Some(prio)) => write!(f, "macro({prio})"),
Self::X(x) => format!("{x} "),
Self::Macro(None) => "macro ".to_string(),
Self::Macro(Some(prio)) => format!("macro({prio})"),
}
}
}
@@ -290,21 +280,13 @@ pub fn ttv_range(ttv: &[TokTree<'_, impl AtomRepr, impl ExtraTok>]) -> Range<u32
ttv.first().unwrap().range.start..ttv.last().unwrap().range.end
}
pub fn ttv_fmt<'a: 'b, 'b>(
pub async fn ttv_fmt<'a: 'b, 'b>(
ttv: impl IntoIterator<Item = &'b TokTree<'a, impl AtomRepr + 'b, impl ExtraTok + 'b>>,
) -> String {
ttv.into_iter().join("")
join_all(ttv.into_iter().map(|tt| tt.print())).await.join("")
}
pub fn indent(s: &str, lvl: usize, first: bool) -> String {
if first {
s.replace("\n", &("\n".to_string() + &" ".repeat(lvl)))
} else if let Some((fst, rest)) = s.split_once('\n') {
fst.to_string() + "\n" + &indent(rest, lvl, true)
} else {
s.to_string()
}
}
pub fn indent(s: &str) -> String { s.replace("\n", "\n ") }
#[derive(Clone, Debug)]
pub struct Ph {