use crate::mbe::macro_parser;
use crate::mbe::{Delimited, KleeneOp, KleeneToken, SequenceRepetition, TokenTree};
use rustc_ast::ast;
use rustc_ast::token::{self, Token};
use rustc_ast::tokenstream;
use rustc_ast_pretty::pprust;
use rustc_session::parse::ParseSess;
use rustc_span::symbol::kw;
use rustc_span::Span;
use rustc_data_structures::sync::Lrc;
pub(super) fn parse(
input: tokenstream::TokenStream,
expect_matchers: bool,
sess: &ParseSess,
) -> Vec<TokenTree> {
let mut result = Vec::new();
let mut trees = input.trees();
while let Some(tree) = trees.next() {
let tree = parse_tree(tree, &mut trees, expect_matchers, sess);
match tree {
TokenTree::MetaVar(start_sp, ident) if expect_matchers => {
let span = match trees.next() {
Some(tokenstream::TokenTree::Token(Token { kind: token::Colon, span })) => {
match trees.next() {
Some(tokenstream::TokenTree::Token(token)) => match token.ident() {
Some((kind, _)) => {
let span = token.span.with_lo(start_sp.lo());
result.push(TokenTree::MetaVarDecl(span, ident, kind));
continue;
}
_ => token.span,
},
tree => tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(span),
}
}
tree => tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(start_sp),
};
sess.missing_fragment_specifiers.borrow_mut().insert(span);
result.push(TokenTree::MetaVarDecl(span, ident, ast::Ident::invalid()));
}
_ => result.push(tree),
}
}
result
}
fn parse_tree(
tree: tokenstream::TokenTree,
trees: &mut impl Iterator<Item = tokenstream::TokenTree>,
expect_matchers: bool,
sess: &ParseSess,
) -> TokenTree {
match tree {
tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }) => match trees.next() {
Some(tokenstream::TokenTree::Delimited(span, delim, tts)) => {
if delim != token::Paren {
let tok = pprust::token_kind_to_string(&token::OpenDelim(delim));
let msg = format!("expected `(`, found `{}`", tok);
sess.span_diagnostic.span_err(span.entire(), &msg);
}
let sequence = parse(tts, expect_matchers, sess);
let (separator, kleene) = parse_sep_and_kleene_op(trees, span.entire(), sess);
let name_captures = macro_parser::count_names(&sequence);
TokenTree::Sequence(
span,
Lrc::new(SequenceRepetition {
tts: sequence,
separator,
kleene,
num_captures: name_captures,
}),
)
}
Some(tokenstream::TokenTree::Token(token)) if token.is_ident() => {
let (ident, is_raw) = token.ident().unwrap();
let span = ident.span.with_lo(span.lo());
if ident.name == kw::Crate && !is_raw {
TokenTree::token(token::Ident(kw::DollarCrate, is_raw), span)
} else {
TokenTree::MetaVar(span, ident)
}
}
Some(tokenstream::TokenTree::Token(token)) => {
let msg =
format!("expected identifier, found `{}`", pprust::token_to_string(&token),);
sess.span_diagnostic.span_err(token.span, &msg);
TokenTree::MetaVar(token.span, ast::Ident::invalid())
}
None => TokenTree::token(token::Dollar, span),
},
tokenstream::TokenTree::Token(token) => TokenTree::Token(token),
tokenstream::TokenTree::Delimited(span, delim, tts) => TokenTree::Delimited(
span,
Lrc::new(Delimited { delim, tts: parse(tts, expect_matchers, sess) }),
),
}
}
fn kleene_op(token: &Token) -> Option<KleeneOp> {
match token.kind {
token::BinOp(token::Star) => Some(KleeneOp::ZeroOrMore),
token::BinOp(token::Plus) => Some(KleeneOp::OneOrMore),
token::Question => Some(KleeneOp::ZeroOrOne),
_ => None,
}
}
fn parse_kleene_op(
input: &mut impl Iterator<Item = tokenstream::TokenTree>,
span: Span,
) -> Result<Result<(KleeneOp, Span), Token>, Span> {
match input.next() {
Some(tokenstream::TokenTree::Token(token)) => match kleene_op(&token) {
Some(op) => Ok(Ok((op, token.span))),
None => Ok(Err(token)),
},
tree => Err(tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(span)),
}
}
fn parse_sep_and_kleene_op(
input: &mut impl Iterator<Item = tokenstream::TokenTree>,
span: Span,
sess: &ParseSess,
) -> (Option<Token>, KleeneToken) {
let span = match parse_kleene_op(input, span) {
Ok(Ok((op, span))) => return (None, KleeneToken::new(op, span)),
Ok(Err(token)) => match parse_kleene_op(input, token.span) {
Ok(Ok((KleeneOp::ZeroOrOne, span))) => {
sess.span_diagnostic.span_err(
token.span,
"the `?` macro repetition operator does not take a separator",
);
return (None, KleeneToken::new(KleeneOp::ZeroOrMore, span));
}
Ok(Ok((op, span))) => return (Some(token), KleeneToken::new(op, span)),
Ok(Err(Token { span, .. })) | Err(span) => span,
},
Err(span) => span,
};
sess.span_diagnostic.span_err(span, "expected one of: `*`, `+`, or `?`");
(None, KleeneToken::new(KleeneOp::ZeroOrMore, span))
}