use crate::ast::*;
use crate::parse::blocks::{at_ref_block, literal_block, reference_block, star_block};
use crate::parse::chain::parse_dot_chain;
use crate::parse::generic::empty;
use crate::parse::ident_blocks::ident_block;
use crate::parse::parse_atom::parse_group;
use crate::util::Cursor;
use proc_macro2::{Delimiter, Ident, Spacing, TokenTree};
use quote::{ToTokens, quote};
pub(crate) fn starts_block(t: &TokenTree) -> bool {
matches!(t, TokenTree::Ident(_) | TokenTree::Literal(_))
|| matches!(t, TokenTree::Group(_))
|| matches!(t, TokenTree::Punct(p)
if matches!(p.as_char(), '&' | '*' | '?' | '!' | '@' | '\'' | '#'))
}
pub(crate) fn cursor_is_dotdot(cursor: &Cursor) -> bool {
matches!(cursor.peek(), Some(TokenTree::Punct(p))
if p.as_char() == '.' && p.spacing() == Spacing::Joint
&& matches!(cursor.peek_at(1), Some(TokenTree::Punct(q)) if q.as_char() == '.'))
}
pub(crate) fn cursor_at_attachment(cursor: &Cursor) -> bool {
match cursor.peek() {
Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => true,
Some(TokenTree::Ident(id))
if (id == "where" || id == "impl")
&& matches!(cursor.peek_at(1), Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace) =>
{
true
}
_ => false,
}
}
pub(crate) fn parse_block(cursor: &mut Cursor, trait_name: Option<&Ident>) -> Option<Ty> {
let ty: Ty = match cursor.peek()? {
TokenTree::Punct(p)
if p.as_char() == '#'
&& matches!(cursor.peek_at(1), Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Bracket) =>
{
let attr = match cursor.peek_at(1) {
Some(TokenTree::Group(g)) => g.stream(),
_ => unreachable!(),
};
cursor.advance(2);
TyWithAttr(TyAttr(attr), None).to_ty()
}
TokenTree::Group(g) if g.delimiter() == Delimiter::Brace => {
let body = g.stream();
cursor.bump();
TyWithCode(None, TyCodeBlock(body)).to_ty()
}
TokenTree::Group(g) if g.delimiter() != Delimiter::None => {
let g = g.clone();
cursor.bump();
parse_group(&g, trait_name)
}
TokenTree::Group(g) => {
let args = g.stream().into_iter().collect::<Vec<_>>();
cursor.bump();
crate::parse::generic::parse_angle_bracket_contents(&args, trait_name, true).to_ty()
}
TokenTree::Punct(p) if p.as_char() == '&' => reference_block(cursor, trait_name),
TokenTree::Punct(p) if p.as_char() == '*' => star_block(cursor),
TokenTree::Punct(p) if p.as_char() == '@' => at_ref_block(cursor),
TokenTree::Punct(p)
if p.as_char() == '\'' && matches!(cursor.peek_at(1), Some(TokenTree::Ident(_))) =>
{
let lt = match cursor.peek_at(1) {
Some(TokenTree::Ident(id)) => Ident::new(&id.to_string(), id.span()),
_ => unreachable!(),
};
cursor.advance(2);
TyPrimitive(crate::parse::blocks::lifetime_tokens(<)).to_ty()
}
TokenTree::Punct(p) if matches!(p.as_char(), '?' | '!') => {
let p = p.as_char();
cursor.bump();
let inner = if cursor_at_attachment(cursor) {
None
} else {
parse_block(cursor, trait_name)
}
.unwrap_or_else(empty);
let p_tt = TokenTree::Punct(proc_macro2::Punct::new(p, Spacing::Alone));
TyPrimitive(quote!(#p_tt #inner)).to_ty()
}
TokenTree::Literal(_) => literal_block(cursor),
TokenTree::Ident(id) => ident_block(cursor, id.clone(), trait_name),
_ => return None,
};
Some(ty)
}
pub(crate) fn parse_return_expr(cursor: &mut Cursor, trait_name: Option<&Ident>) -> Ty {
let mut left = parse_dot_chain(cursor, trait_name).unwrap_or_else(empty);
while let Some(t) = cursor.peek() {
if !starts_block(t) || cursor_at_attachment(cursor) {
break;
}
let right = parse_dot_chain(cursor, trait_name).unwrap_or_else(empty);
left = left.apply(right);
}
left
}
pub(crate) fn parse_bound_expr(cursor: &mut Cursor, trait_name: Option<&Ident>) -> Ty {
let mut left = parse_dot_chain(cursor, trait_name).unwrap_or_else(empty);
loop {
match cursor.peek() {
Some(t) if starts_block(t) && !cursor_at_attachment(cursor) => {
let right = parse_dot_chain(cursor, trait_name).unwrap_or_else(empty);
left = left.apply(right);
}
_ => break,
}
}
if cursor.is_punct('+') {
let mut ts = left.to_token_stream();
while cursor.is_punct('+') {
ts.extend(cursor.peek().unwrap().to_token_stream());
cursor.bump();
if let Some(t) = cursor.peek()
&& starts_block(t)
&& !cursor_at_attachment(cursor)
{
let right = parse_dot_chain(cursor, trait_name).unwrap_or_else(empty);
ts.extend(right.to_token_stream());
}
}
return TyPrimitive(ts).to_ty();
}
left
}
pub(crate) fn parse_return_expr_tokens(cursor: &mut Cursor) {
if parse_block(cursor, None).is_none() {
return;
}
loop {
match cursor.peek() {
Some(t) if starts_block(t) && !cursor_at_attachment(cursor) => {
let _ = parse_block(cursor, None);
}
_ => break,
}
}
}