use crate::parse_extern::{parse_extern_block, parse_extern_crate};
use crate::parse_fn::{consume_fn, consume_macro, NotFunction};
use crate::parse_mod::parse_mod;
use crate::parse_type::{consume_bound, consume_generic_params, consume_where_clause};
use crate::parse_utils::{
consume_ident, consume_inner_attributes, consume_outer_attributes, consume_punct,
consume_stuff_until, consume_vis_marker, parse_any_ident, parse_ident, parse_punct,
};
use crate::types::{
Attribute, Constant, GroupSpan, Impl, ImplMember, Item, Trait, TraitMember, TypeAlias,
TypeExpr, ValueExpr, VisMarker,
};
use proc_macro2::{Delimiter, Group, TokenTree};
use quote::ToTokens;
use std::iter::Peekable;
type TokenIter = Peekable<proc_macro2::token_stream::IntoIter>;
pub(crate) fn parse_const_or_static(
tokens: &mut TokenIter,
attributes: Vec<Attribute>,
vis_marker: Option<VisMarker>,
) -> Constant {
let (tk_const_or_static, is_const) = match tokens.next() {
Some(TokenTree::Ident(ident)) if ident == "const" => (ident, true),
Some(TokenTree::Ident(ident)) if ident == "static" => (ident, false),
_ => panic!("cannot parse const/static"),
};
let tk_mut = match tokens.peek() {
Some(TokenTree::Ident(ident)) if ident == "mut" => {
assert!(!is_const, "`const mut` is not a valid declaration");
Some(ident.clone())
}
_ => None,
};
let name = parse_any_ident(tokens, "const/static");
let tk_colon = parse_punct(tokens, ':', "const/static");
let ty_tokens = consume_stuff_until(
tokens,
|tt| matches!(tt, TokenTree::Punct(punct) if punct.as_char() == '=' || punct.as_char() == ';'),
true,
);
let tk_equals = consume_punct(tokens, '=');
let value_tokens = consume_stuff_until(
tokens,
|tt| matches!(tt, TokenTree::Punct(punct) if punct.as_char() == ';'),
true,
);
let initializer = if value_tokens.is_empty() {
None
} else {
Some(ValueExpr {
tokens: value_tokens,
})
};
let tk_semicolon = parse_punct(tokens, ';', "const/static");
Constant {
attributes,
vis_marker,
tk_const_or_static,
tk_mut,
name,
tk_colon,
ty: TypeExpr { tokens: ty_tokens },
tk_equals,
initializer,
tk_semicolon,
}
}
pub(crate) fn consume_ty_definition(
tokens: &mut TokenIter,
attributes: Vec<Attribute>,
vis_marker: Option<VisMarker>,
) -> Option<TypeAlias> {
let context = "associated type";
let tk_type = parse_ident(tokens, "type", context);
let name = parse_any_ident(tokens, context);
let bound = consume_bound(tokens, |token| {
matches!(
token,
TokenTree::Punct(punct) if punct.as_char() == '=' || punct.as_char() == ';'
)
});
let tk_equals = consume_punct(tokens, '=');
let initializer_ty = if tk_equals.is_some() {
let ty_tokens = consume_stuff_until(
tokens,
|tt| matches!(tt, TokenTree::Punct(punct) if punct.as_char() == ';'),
true,
);
Some(TypeExpr { tokens: ty_tokens })
} else {
None
};
let tk_semicolon = parse_punct(tokens, ';', context);
Some(TypeAlias {
attributes,
vis_marker,
tk_type,
name,
bound,
tk_equals,
initializer_ty,
tk_semicolon,
})
}
pub(crate) fn consume_either_fn_type_const_static_impl(
tokens: &mut TokenIter,
attributes: Vec<Attribute>,
vis_marker: Option<VisMarker>,
context: &str, ) -> Item {
if let Some(TokenTree::Ident(ident)) = tokens.peek() {
let keyword = ident.to_string();
match keyword.as_str() {
"type" => {
let assoc_ty = consume_ty_definition(tokens, attributes, vis_marker);
Item::TypeAlias(assoc_ty.unwrap())
}
"default" | "const" | "static" | "async" | "unsafe" | "extern" | "fn" => {
match consume_fn(tokens, attributes.clone(), vis_marker.clone()) {
Ok(method) => Item::Function(method),
Err(NotFunction::Const) => {
let constant = parse_const_or_static(tokens, attributes, vis_marker);
Item::Constant(constant)
}
Err(NotFunction::Static) => {
let static_decl = parse_const_or_static(tokens, attributes, vis_marker);
Item::Constant(static_decl)
}
Err(NotFunction::Trait) => {
let trait_decl = parse_trait(tokens, attributes, vis_marker);
Item::Trait(trait_decl)
}
Err(NotFunction::Impl) => {
let impl_decl = parse_impl(tokens, attributes);
Item::Impl(impl_decl)
}
Err(NotFunction::Mod) => {
let mod_decl = parse_mod(tokens, attributes, vis_marker);
Item::Module(mod_decl)
}
Err(NotFunction::ExternBlock) => {
let extern_decl = parse_extern_block(tokens, attributes, vis_marker);
Item::ExternBlock(extern_decl)
}
Err(NotFunction::ExternCrate) => {
let crate_decl = parse_extern_crate(tokens, attributes, vis_marker);
Item::ExternCrate(crate_decl)
}
}
}
ident => {
if let Some(vis_marker) = vis_marker {
panic!(
"unsupported visibility marker `{}`",
vis_marker.to_token_stream()
);
}
match consume_macro(tokens, attributes.clone()) {
Some(macro_) => Item::Macro(macro_),
None => panic!("unsupported {} item `{}`", context, ident),
}
}
}
} else {
panic!("unsupported {} element: {:?}", context, tokens.peek())
}
}
pub(crate) fn parse_impl_body(
token_group: Group,
allow_static: bool,
) -> (GroupSpan, Vec<Attribute>, Vec<ImplMember>) {
let mut body_items = vec![];
let mut tokens = token_group.stream().into_iter().peekable();
let inner_attributes = consume_inner_attributes(&mut tokens);
loop {
if tokens.peek().is_none() {
break;
}
let attributes = consume_outer_attributes(&mut tokens);
let vis_marker = consume_vis_marker(&mut tokens);
let item = match consume_either_fn_type_const_static_impl(
&mut tokens,
attributes,
vis_marker,
"impl",
) {
Item::Function(function) => ImplMember::AssocFunction(function),
Item::Constant(const_) if const_.tk_const_or_static == "const" => {
ImplMember::AssocConstant(const_)
}
Item::Constant(static_) if allow_static => {
ImplMember::AssocConstant(static_)
}
Item::TypeAlias(ty_def) => ImplMember::AssocType(ty_def),
Item::Macro(macro_) => ImplMember::Macro(macro_),
_ => panic!("unsupported impl item `{:?}`", tokens.peek()),
};
body_items.push(item);
}
(GroupSpan::new(&token_group), inner_attributes, body_items)
}
pub(crate) fn parse_impl(tokens: &mut TokenIter, attributes: Vec<Attribute>) -> Impl {
let tk_unsafe = consume_ident(tokens, "unsafe");
let tk_impl = parse_ident(tokens, "impl", "impl block");
let impl_generic_params = consume_generic_params(tokens);
let trait_or_self_ty = consume_stuff_until(
tokens,
|tk| match tk {
TokenTree::Group(group) if group.delimiter() == Delimiter::Brace => true,
TokenTree::Ident(ident) if ident == "for" || ident == "where" => true,
_ => false,
},
true,
);
let (tk_for, trait_ty, self_ty) = if let Some(tk_for) = consume_ident(tokens, "for") {
let self_ty = consume_stuff_until(
tokens,
|tk| match tk {
TokenTree::Group(group) if group.delimiter() == Delimiter::Brace => true,
TokenTree::Ident(ident) if ident == "where" => true,
_ => false,
},
true,
);
(
Some(tk_for),
Some(TypeExpr {
tokens: trait_or_self_ty,
}),
TypeExpr { tokens: self_ty },
)
} else {
(
None,
None,
TypeExpr {
tokens: trait_or_self_ty,
},
)
};
let where_clause = consume_where_clause(tokens);
let (tk_braces, inner_attributes, body_items) = match tokens.next().unwrap() {
TokenTree::Group(group) if group.delimiter() == Delimiter::Brace => {
parse_impl_body(group, false)
}
token => panic!("cannot parse impl: unexpected token {:?}", token),
};
Impl {
attributes,
tk_unsafe,
tk_impl,
impl_generic_params,
trait_ty,
tk_for,
self_ty,
where_clause,
body_items,
inner_attributes,
tk_braces,
}
}
pub(crate) fn parse_trait(
tokens: &mut TokenIter,
attributes: Vec<Attribute>,
vis_marker: Option<VisMarker>,
) -> Trait {
let tk_unsafe = consume_ident(tokens, "unsafe");
let tk_trait = parse_ident(tokens, "trait", "trait declaration");
let name = parse_any_ident(tokens, "trait name");
let generic_params = consume_generic_params(tokens);
let bound = consume_bound(tokens, |token| match token {
TokenTree::Ident(ident) if ident == "where" => true,
TokenTree::Group(group) if group.delimiter() == Delimiter::Brace => true,
_ => false,
});
let where_clause = consume_where_clause(tokens);
let (tk_braces, inner_attributes, body_items) = match tokens.next().unwrap() {
TokenTree::Group(group) if group.delimiter() == Delimiter::Brace => {
parse_impl_body(group, false)
}
token => panic!("cannot parse trait: unexpected token {:?}", token),
};
let body_items = body_items
.into_iter()
.map(|item| match item {
ImplMember::AssocFunction(function) => TraitMember::AssocFunction(function),
ImplMember::AssocConstant(constant) => TraitMember::AssocConstant(constant),
ImplMember::AssocType(assoc_ty) => TraitMember::AssocType(assoc_ty),
ImplMember::Macro(macro_) => TraitMember::Macro(macro_),
})
.collect();
Trait {
attributes,
vis_marker,
tk_unsafe,
tk_trait,
name,
generic_params,
bound,
where_clause,
body_items,
inner_attributes,
tk_braces,
}
}