use proc_macro2::{Delimiter, Ident, TokenStream, TokenTree};
use crate::apply::Apply;
use crate::generic::{
is_trait_base, parse_angle_bracket_contents, parse_generic, parse_type_params,
primitive,
};
use crate::parse_atom::{
parse_attribute, parse_function, parse_group, parse_prefix, parse_range,
};
use crate::scan::{Cursor, is_punct};
use crate::types::*;
pub(crate) fn parse_item(
cursor: &mut Cursor, level: Op, trait_name: Option<&Ident>,
) -> Option<Ty> {
match level {
Op::Semi | Op::Comma => loop {
if let Some(item) = parse_operand(cursor, level, trait_name) {
return Some(item);
}
if cursor.is_punct(',') {
cursor.bump();
} else {
return None;
}
},
Op::Dash => {
let mut result = parse_operand(cursor, Op::Dash, trait_name)?;
while cursor.is_punct('-') {
cursor.bump();
result = result.apply(parse_operand(cursor, Op::Dash, trait_name)?);
}
Some(result)
}
Op::Caret => {
let mut items = vec![parse_operand(cursor, Op::Caret, trait_name)?];
while cursor.is_punct('^') {
cursor.bump();
items.push(parse_operand(cursor, Op::Caret, trait_name)?);
}
let mut result = items.pop()?;
while let Some(left) = items.pop() {
result = left.apply(result);
}
Some(result)
}
Op::Prim => Some(parse_primitive(cursor.take_rest(), trait_name)),
}
}
fn parse_operand(
cursor: &mut Cursor, level: Op, trait_name: Option<&Ident>,
) -> Option<Ty> {
if cursor.at_end() {
return None;
}
let segment = cursor.take_segment(level.stop_chars());
parse_item(&mut Cursor::new(segment), level.next()?, trait_name)
}
pub(crate) fn parse_primitive(
tokens: &[TokenTree], trait_name: Option<&Ident>,
) -> Ty {
let split = split_trailing_body(tokens);
match (split.body, split.is_where) {
(Some(body), false) => Ty::WithCode(TyWithCode(None, TyCodeBlock(body)))
.apply(parse_primitive(split.tokens, trait_name)),
(Some(w), true) => Ty::WithWhere(TyWithWhere(None, TyWhere(w)))
.apply(parse_primitive(split.tokens, trait_name)),
_ => parse_primary(split.tokens, trait_name),
}
}
struct TrailingBody<'a> {
tokens: &'a [TokenTree],
body: Option<TokenStream>,
is_where: bool,
}
fn split_trailing_body(tokens: &[TokenTree]) -> TrailingBody<'_> {
match tokens.last() {
Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Brace => {
if tokens.len() >= 2
&& let TokenTree::Punct(p) = &tokens[tokens.len() - 2]
&& p.as_char() == '!'
{
return TrailingBody { tokens, body: None, is_where: false };
}
if tokens.len() >= 2
&& let TokenTree::Ident(i) = &tokens[tokens.len() - 2]
&& *i == "where"
{
return TrailingBody {
tokens: &tokens[..tokens.len() - 2],
body: Some(group.stream()),
is_where: true,
};
}
TrailingBody {
tokens: &tokens[..tokens.len() - 1],
body: Some(group.stream()),
is_where: false,
}
}
_ => TrailingBody { tokens, body: None, is_where: false },
}
}
fn parse_primary(tokens: &[TokenTree], trait_name: Option<&Ident>) -> Ty {
if let Some((attr, rest)) = parse_attribute(tokens) {
let inner = if rest.is_empty() {
TyWithAttr(TyAttr(attr), None).into()
} else {
TyWithAttr(TyAttr(attr), None).apply(parse_primitive(rest, trait_name))
};
return inner;
}
if let Some(function) = parse_function(tokens, trait_name) {
return function;
}
if let [TokenTree::Ident(name)] = tokens
&& name == "fn"
{
return TyFn(None, None).into();
}
if let Some((prefix, rest)) = parse_prefix(tokens) {
let inner = if rest.is_empty() {
TyWithPrefix(prefix, None).into()
} else {
TyWithPrefix(prefix, None).apply(parse_primitive(rest, trait_name))
};
return inner;
}
if let Some(range) = parse_range(tokens) {
return range;
}
if let [TokenTree::Literal(literal)] = tokens
&& let Ok(number) = literal.to_string().parse()
{
return TyNum(number).into();
}
if let [TokenTree::Group(group)] = tokens {
return parse_group(group, trait_name);
}
if let Some((base, args, rest)) = parse_generic(tokens) {
let params = parse_angle_bracket_contents(args, trait_name);
let generic = if is_trait_base(base, trait_name) {
TyTrait(base.iter().cloned().collect(), params).into()
} else {
if !rest.is_empty()
&& !matches!(rest.first(), Some(t) if is_punct(t, '<'))
{
return primitive(tokens);
}
TyGeneric(primitive(base).into(), params).into()
};
return if rest.is_empty() {
generic
} else {
generic.apply(parse_primitive(rest, trait_name))
};
}
if let Some((args, rest)) = parse_type_params(tokens) {
let params = parse_angle_bracket_contents(args, trait_name);
let params = params.into();
return if rest.is_empty() {
params
} else {
params.apply(parse_primitive(rest, trait_name))
};
}
primitive(tokens)
}