mod chain;
mod generic;
mod parse_atom;
mod primary;
mod trailing;
pub(crate) use chain::parse_item;
pub(crate) use generic::split_at_depth0;
pub(crate) use primary::parse_primary;
pub(crate) use trailing::split_trailing_body;
use proc_macro2::{Group, Ident, TokenStream, TokenTree};
use crate::apply::err_ty_at;
use crate::ast::fresh::at_ref_name;
use crate::ast::*;
use crate::util::{MAX_NEST_DEPTH, compile_error_str};
pub(crate) fn resolve_at_refs(tokens: &[TokenTree]) -> Result<Vec<TokenTree>, TokenStream> {
let mut out = Vec::with_capacity(tokens.len());
let mut i = 0;
while i < tokens.len() {
match &tokens[i] {
TokenTree::Punct(p) if p.as_char() == '@' => {
let at_span = p.span();
match tokens.get(i + 1) {
Some(TokenTree::Literal(lit)) => {
let name = at_ref_name(&lit.to_string()).ok_or_else(|| {
compile_error_str(
"batch-impl: `@` in a type must be followed by a \
position digit (e.g. `@0` or `@0_1`)",
at_span,
)
})?;
let ident = Ident::new(&name, at_span);
out.push(TokenTree::Ident(ident));
i += 2;
}
_ => {
return Err(compile_error_str(
"batch-impl: `@` in a type must be a position digit (e.g. `@0` or `@0_1`)",
at_span,
));
}
}
}
TokenTree::Group(g) => {
let inner = g.stream().into_iter().collect::<Vec<_>>();
let mut new_g =
Group::new(g.delimiter(), resolve_at_refs(&inner)?.into_iter().collect());
new_g.set_span(g.span());
out.push(TokenTree::Group(new_g));
i += 1;
}
_ => {
out.push(tokens[i].clone());
i += 1;
}
}
}
Ok(out)
}
pub(crate) fn parse_primitive(
tokens: &[TokenTree], trait_name: Option<&Ident>, depth: usize,
) -> Ty {
if depth > MAX_NEST_DEPTH {
return err_ty_at(
&format!(
"batch-impl: chained type segments exceed {} levels (limit {}); \
split into separate impl-specs",
depth, MAX_NEST_DEPTH,
),
tokens.first().map_or_else(proc_macro2::Span::call_site, |t| t.span()),
);
}
let mut attaches = vec![];
let mut rest = tokens;
loop {
let split = split_trailing_body(rest);
match (split.body, split.is_where, split.is_impl) {
(Some(body), false, false) => {
attaches.push(TyWithCode(None, TyCodeBlock(body)).into());
rest = split.tokens;
}
(Some(w), true, false) => {
attaches.push(TyWithWhere(None, TyWhere(w)).into());
rest = split.tokens;
}
(Some(t), false, true) => {
attaches.push(TyWithImpl(None, TyImplTemplate(t)).into());
rest = split.tokens;
}
_ => break,
}
}
if attaches.len() > MAX_NEST_DEPTH {
return err_ty_at(
&format!(
"batch-impl: trailing attachment chain (`{{...}}` / `where{{...}}` / \
`impl{{...}}`) exceeds {} levels (limit {}); split into separate impl-specs",
attaches.len(),
MAX_NEST_DEPTH,
),
tokens.first().map_or_else(proc_macro2::Span::call_site, |t| t.span()),
);
}
let mut ty = if rest.is_empty() {
match attaches.pop() {
Some(inner) => inner,
None => parse_primary(rest, trait_name, depth),
}
} else {
parse_primary(rest, trait_name, depth)
};
while let Some(block) = attaches.pop() {
ty = block.apply(ty);
}
ty
}