use proc_macro2::{Delimiter, Ident, TokenStream, TokenTree};
use crate::apply::Apply;
use crate::generic::{eat_where_suffix, 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 (tokens, body) = split_trailing_body(tokens);
attach_body(tokens, trait_name, body)
}
fn attach_body(
tokens: &[TokenTree],
trait_name: Option<&Ident>,
body: Option<TokenStream>,
) -> Ty {
match body {
Some(body) => Ty::CodeBlock(TyCodeBlock(body))
.apply(parse_primitive(tokens, trait_name)),
None => parse_primary(tokens, trait_name),
}
}
fn split_trailing_body(
tokens: &[TokenTree],
) -> (&[TokenTree], Option<TokenStream>) {
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 (tokens, None);
}
(&tokens[..tokens.len() - 1], Some(group.stream()))
},
_ => (tokens, None),
}
}
fn parse_primary(tokens: &[TokenTree], trait_name: Option<&Ident>) -> Ty {
if let Some((attr, rest)) = parse_attribute(tokens) {
let inner = if rest.is_empty() {
TyAttr(attr).into()
} else {
TyAttr(attr).apply(parse_primitive(rest, trait_name))
};
return inner;
}
if let Some(function) = parse_function(tokens, trait_name) {
return function;
}
if let Some((prefix, rest)) = parse_prefix(tokens) {
let inner = if rest.is_empty() {
Ty::Prefix(prefix)
} else {
prefix.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::<u8>()
{
return Ty::Num(TyNum(number));
}
if let [TokenTree::Group(group)] = tokens {
return parse_group(group, trait_name);
}
if let Some((base, args, rest)) = parse_generic(tokens) {
let mut params = parse_angle_bracket_contents(args, trait_name);
let (wheres, rest) = eat_where_suffix(rest);
params.where_clauses.extend(wheres);
let generic = if is_trait_base(base, trait_name) {
Ty::Trait(TyTrait(base.iter().cloned().collect(), params))
} else {
if !rest.is_empty()
&& !matches!(rest.first(), Some(t) if is_punct(t, '<'))
{
return primitive(tokens);
}
Ty::Generic(TyGeneric(Box::new(primitive(base)), params))
};
return if rest.is_empty() {
generic
} else {
generic.apply(parse_primitive(rest, trait_name))
};
}
if let Some((args, rest)) = parse_type_params(tokens) {
let mut params = parse_angle_bracket_contents(args, trait_name);
let (wheres, rest) = eat_where_suffix(rest);
params.where_clauses.extend(wheres);
let params = Ty::TypeParam(params);
return if rest.is_empty() {
params
} else {
params.apply(parse_primitive(rest, trait_name))
};
}
primitive(tokens)
}