mod generic;
mod parse_atom;
use proc_macro2::{Ident, TokenStream, TokenTree};
use crate::apply::{err_ty, err_ty_at};
use crate::ast::*;
use crate::parse::generic::{
is_trait_base, parse_angle_bracket_contents, parse_generic, parse_type_params,
primitive,
};
use crate::parse::parse_atom::{
parse_attribute, parse_function, parse_group, parse_prefix, parse_range,
};
use crate::util::Cursor;
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 item.into();
}
if cursor.is_punct(',') {
cursor.bump();
if cursor.is_punct(',') {
let sp = cursor
.peek()
.map(|t| t.span())
.unwrap_or_else(proc_macro2::Span::call_site);
return err_ty_at(
"batch-impl: missing operand between consecutive commas `,,` (e.g. `A,,B`)",
sp,
)
.into();
}
} else {
return None;
}
},
Op::Dash => parse_binary_chain(cursor, Op::Dash, trait_name, '-', false),
Op::Caret => parse_binary_chain(cursor, Op::Caret, trait_name, '^', true),
Op::Prim => parse_primitive(cursor.take_rest(), trait_name).into(),
}
}
fn parse_binary_chain(
cursor: &mut Cursor, level: Op, trait_name: Option<&Ident>, op_punct: char,
right_assoc: bool,
) -> Option<Ty> {
let hint = if op_punct == '-' { " (e.g. `T-U`)" } else { " (e.g. `T^U`)" };
let mut items = match parse_operand(cursor, level, trait_name) {
Some(op) => vec![op],
None if cursor.at_end() => return None,
None => {
let sp = cursor
.peek()
.map(|t| t.span())
.unwrap_or_else(proc_macro2::Span::call_site);
return err_ty_at(
&format!("batch-impl: missing operand before `{}`{}", op_punct, hint),
sp,
)
.into();
}
};
if is_empty_operand(&items[0]) {
let sp = cursor
.peek()
.map(|t| t.span())
.unwrap_or_else(proc_macro2::Span::call_site);
return err_ty_at(
&format!("batch-impl: missing operand before `{}`{}", op_punct, hint),
sp,
)
.into();
}
while cursor.is_punct(op_punct) {
let op_span = cursor
.peek()
.map(|t| t.span())
.unwrap_or_else(proc_macro2::Span::call_site);
cursor.bump();
let Some(op) = parse_operand(cursor, level, trait_name) else {
return err_ty_at(
&format!("batch-impl: missing operand after `{}`{}", op_punct, hint),
op_span,
)
.into();
};
if is_empty_operand(&op) {
return err_ty_at(
&format!("batch-impl: missing operand after `{}`{}", op_punct, hint),
op_span,
)
.into();
}
items.push(op);
}
if right_assoc {
items.into_iter().rev().reduce(|acc, x| x.apply(acc))
} else {
items.into_iter().reduce(|acc, x| acc.apply(x))
}
}
fn is_empty_operand(ty: &Ty) -> bool {
matches!(&ty.kind, TyKind::Primitive(p) if p.0.is_empty())
}
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 mut attaches = vec![];
let mut rest = tokens;
loop {
let split = split_trailing_body(rest);
match (split.body, split.is_where) {
(Some(body), false) => {
attaches.push(TyWithCode(None, TyCodeBlock(body)).into());
rest = split.tokens;
}
(Some(w), true) => {
attaches.push(TyWithWhere(None, TyWhere(w)).into());
rest = split.tokens;
}
_ => break,
}
}
let mut ty = if rest.is_empty() {
match attaches.pop() {
Some(inner) => inner,
None => parse_primary(rest, trait_name),
}
} else {
parse_primary(rest, trait_name)
};
while let Some(block) = attaches.pop() {
ty = block.apply(ty);
}
ty
}
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![{}] => {
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: group.stream().into(),
is_where: true,
};
}
TrailingBody {
tokens: &tokens[..tokens.len() - 1],
body: group.stream().into(),
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() {
Ty::new(
proc_macro2::Span::call_site(),
TyKind::WithAttr(TyWithAttr(TyAttr(attr), None)),
)
} else {
Ty::new(
proc_macro2::Span::call_site(),
TyKind::WithAttr(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, false).into();
}
if let Some((prefix, rest)) = parse_prefix(tokens) {
if matches!(prefix, TyPrefix::Unsafe) && !rest.is_empty() {
if matches!(rest.first(), Some(TokenTree::Ident(f)) if f == "fn") {
let inner = parse_primitive(rest, trait_name);
return match inner.kind {
TyKind::Fn(mut f) => {
f.2 = true;
Ty::new(inner.span, TyKind::Fn(f))
}
other => Ty::new(inner.span, other),
};
}
return err_ty(
"batch-impl: `unsafe` can only qualify a fn type (e.g. `unsafe fn(u32) -> u32`) \
or act as a bare impl marker (e.g. `unsafe^T`)",
);
}
let inner = if rest.is_empty() {
Ty::new(
proc_macro2::Span::call_site(),
TyKind::WithPrefix(TyWithPrefix(prefix, None)),
)
} else {
Ty::new(
proc_macro2::Span::call_site(),
TyKind::WithPrefix(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
&& group.delimiter() != delimiter![<>]
{
return parse_group(group, trait_name);
}
if let Some((base, args, rest)) = parse_generic(tokens) {
let args_vec: Vec<_> = args.into_iter().collect();
let params = parse_angle_bracket_contents(&args_vec, 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(TokenTree::Group(g)) if g.delimiter() == delimiter![<>])
{
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 args_vec: Vec<_> = args.into_iter().collect();
let params = parse_angle_bracket_contents(&args_vec, trait_name);
let params = params.into();
return if rest.is_empty() {
params
} else {
params.apply(parse_primitive(&rest, trait_name))
};
}
primitive(tokens)
}