| use crate::iter::{self, Iter, IterImpl}; |
| use crate::{Define, Error, Export, ExportArgs, FakeCallSite, Input, Macro, Visibility}; |
| use proc_macro::Delimiter::{Brace, Bracket, Parenthesis}; |
| use proc_macro::{Delimiter, Ident, Span, TokenStream, TokenTree}; |
| |
| pub(crate) fn parse_input(tokens: Iter) -> Result<Input, Error> { |
| let attrs = parse_attributes(tokens)?; |
| let vis = parse_visibility(tokens)?; |
| let kw = parse_ident(tokens)?; |
| if kw.to_string() == "use" { |
| parse_export(attrs, vis, tokens).map(Input::Export) |
| } else if kw.to_string() == "fn" { |
| parse_define(attrs, vis, kw.span(), tokens).map(Input::Define) |
| } else { |
| Err(Error::new( |
| kw.span(), |
| "unexpected input to #[proc_macro_hack]", |
| )) |
| } |
| } |
| |
| fn parse_export(attrs: TokenStream, vis: Visibility, tokens: Iter) -> Result<Export, Error> { |
| let _ = parse_punct(tokens, ':'); |
| let _ = parse_punct(tokens, ':'); |
| let from = parse_ident(tokens)?; |
| parse_punct(tokens, ':')?; |
| parse_punct(tokens, ':')?; |
| |
| let mut macros = Vec::new(); |
| match tokens.peek() { |
| Some(TokenTree::Group(group)) if group.delimiter() == Brace => { |
| let ref mut content = iter::new(group.stream()); |
| loop { |
| macros.push(parse_macro(content)?); |
| if content.peek().is_none() { |
| break; |
| } |
| parse_punct(content, ',')?; |
| if content.peek().is_none() { |
| break; |
| } |
| } |
| tokens.next().unwrap(); |
| } |
| _ => macros.push(parse_macro(tokens)?), |
| } |
| |
| parse_punct(tokens, ';')?; |
| Ok(Export { |
| attrs, |
| vis, |
| from, |
| macros, |
| }) |
| } |
| |
| fn parse_punct(tokens: Iter, ch: char) -> Result<(), Error> { |
| match tokens.peek() { |
| Some(TokenTree::Punct(punct)) if punct.as_char() == ch => { |
| tokens.next().unwrap(); |
| Ok(()) |
| } |
| tt => Err(Error::new( |
| tt.map_or_else(Span::call_site, TokenTree::span), |
| format!("expected `{}`", ch), |
| )), |
| } |
| } |
| |
| fn parse_define( |
| attrs: TokenStream, |
| vis: Visibility, |
| fn_token: Span, |
| tokens: Iter, |
| ) -> Result<Define, Error> { |
| if vis.is_none() { |
| return Err(Error::new( |
| fn_token, |
| "functions tagged with `#[proc_macro_hack]` must be `pub`", |
| )); |
| } |
| let name = parse_ident(tokens)?; |
| let body = tokens.collect(); |
| Ok(Define { attrs, name, body }) |
| } |
| |
| fn parse_macro(tokens: Iter) -> Result<Macro, Error> { |
| let name = parse_ident(tokens)?; |
| let export_as = match tokens.peek() { |
| Some(TokenTree::Ident(ident)) if ident.to_string() == "as" => { |
| tokens.next().unwrap(); |
| parse_ident(tokens)? |
| } |
| _ => name.clone(), |
| }; |
| Ok(Macro { name, export_as }) |
| } |
| |
| fn parse_ident(tokens: Iter) -> Result<Ident, Error> { |
| match tokens.next() { |
| Some(TokenTree::Ident(ident)) => Ok(ident), |
| tt => Err(Error::new( |
| tt.as_ref().map_or_else(Span::call_site, TokenTree::span), |
| "expected identifier", |
| )), |
| } |
| } |
| |
| fn parse_keyword(tokens: Iter, kw: &'static str) -> Result<(), Error> { |
| match &tokens.next() { |
| Some(TokenTree::Ident(ident)) if ident.to_string() == kw => Ok(()), |
| tt => Err(Error::new( |
| tt.as_ref().map_or_else(Span::call_site, TokenTree::span), |
| format!("expected `{}`", kw), |
| )), |
| } |
| } |
| |
| fn parse_int(tokens: Iter) -> Result<u16, Span> { |
| match tokens.next() { |
| Some(TokenTree::Literal(lit)) => lit.to_string().parse().map_err(|_| lit.span()), |
| Some(tt) => Err(tt.span()), |
| None => Err(Span::call_site()), |
| } |
| } |
| |
| fn parse_group(tokens: Iter, delimiter: Delimiter) -> Result<IterImpl, Error> { |
| match &tokens.next() { |
| Some(TokenTree::Group(group)) if group.delimiter() == delimiter => { |
| Ok(iter::new(group.stream())) |
| } |
| tt => Err(Error::new( |
| tt.as_ref().map_or_else(Span::call_site, TokenTree::span), |
| "expected delimiter", |
| )), |
| } |
| } |
| |
| fn parse_visibility(tokens: Iter) -> Result<Visibility, Error> { |
| if let Some(TokenTree::Ident(ident)) = tokens.peek() { |
| if ident.to_string() == "pub" { |
| return Ok(Some(tokens.next().unwrap().span())); |
| } |
| } |
| Ok(None) |
| } |
| |
| fn parse_attributes(tokens: Iter) -> Result<TokenStream, Error> { |
| let mut attrs = TokenStream::new(); |
| while let Some(TokenTree::Punct(punct)) = tokens.peek() { |
| if punct.as_char() != '#' { |
| break; |
| } |
| let span = punct.span(); |
| attrs.extend(tokens.next()); |
| match tokens.peek() { |
| Some(TokenTree::Group(group)) if group.delimiter() == Bracket => { |
| attrs.extend(tokens.next()); |
| } |
| _ => return Err(Error::new(span, "unexpected input")), |
| } |
| } |
| Ok(attrs) |
| } |
| |
| pub(crate) fn parse_export_args(tokens: Iter) -> Result<ExportArgs, Error> { |
| let mut args = ExportArgs { |
| support_nested: false, |
| internal_macro_calls: 0, |
| fake_call_site: false, |
| }; |
| |
| while let Some(tt) = tokens.next() { |
| match &tt { |
| TokenTree::Ident(ident) if ident.to_string() == "support_nested" => { |
| args.support_nested = true; |
| } |
| TokenTree::Ident(ident) if ident.to_string() == "internal_macro_calls" => { |
| parse_punct(tokens, '=')?; |
| let calls = parse_int(tokens).map_err(|span| { |
| Error::new(span, "expected integer value for internal_macro_calls") |
| })?; |
| args.internal_macro_calls = calls; |
| } |
| TokenTree::Ident(ident) if ident.to_string() == "fake_call_site" => { |
| args.fake_call_site = true; |
| } |
| _ => { |
| return Err(Error::new( |
| tt.span(), |
| "expected one of: `support_nested`, `internal_macro_calls`, `fake_call_site`", |
| )) |
| } |
| } |
| if tokens.peek().is_none() { |
| break; |
| } |
| parse_punct(tokens, ',')?; |
| } |
| |
| Ok(args) |
| } |
| |
| pub(crate) fn parse_define_args(tokens: Iter) -> Result<(), Error> { |
| if tokens.peek().is_none() { |
| Ok(()) |
| } else { |
| Err(Error::new(Span::call_site(), "unexpected input")) |
| } |
| } |
| |
| pub(crate) fn parse_enum_hack(tokens: Iter) -> Result<TokenStream, Error> { |
| parse_keyword(tokens, "enum")?; |
| parse_ident(tokens)?; |
| |
| let ref mut braces = parse_group(tokens, Brace)?; |
| parse_ident(braces)?; |
| parse_punct(braces, '=')?; |
| |
| let ref mut parens = parse_group(braces, Parenthesis)?; |
| parse_ident(parens)?; |
| parse_punct(parens, '!')?; |
| |
| let ref mut inner = parse_group(parens, Brace)?; |
| let token_stream = inner.collect(); |
| |
| parse_punct(parens, ',')?; |
| let _ = parens.next(); |
| parse_punct(braces, '.')?; |
| let _ = braces.next(); |
| parse_punct(braces, ',')?; |
| |
| Ok(token_stream) |
| } |
| |
| pub(crate) fn parse_fake_call_site(tokens: Iter) -> Result<FakeCallSite, Error> { |
| parse_punct(tokens, '#')?; |
| let ref mut attr = parse_group(tokens, Bracket)?; |
| parse_keyword(attr, "derive")?; |
| let ref mut path = parse_group(attr, Parenthesis)?; |
| Ok(FakeCallSite { |
| derive: parse_ident(path)?, |
| rest: tokens.collect(), |
| }) |
| } |