1 //! Parse the macro input into an intermediate representation.
2 
3 use proc_macro2::{
4     Span, TokenStream, Delimiter, TokenTree, Spacing,
5     token_stream::IntoIter as TokenIterator,
6 };
7 use std::{collections::HashMap, convert::TryFrom};
8 use crate::{
9     err::Error,
10     ir::{Expr, WriteInput, FormatStr, FormatArgs},
11 };
12 
13 mod fmt;
14 mod style;
15 
16 
17 /// Helper function to parse from a token stream. Makes sure the iterator is
18 /// empty after `f` returns.
parse<F, O>(tokens: TokenStream, f: F) -> Result<O, Error> where F: FnOnce(&mut TokenIterator) -> Result<O, Error>,19 pub(crate) fn parse<F, O>(tokens: TokenStream, f: F) -> Result<O, Error>
20 where
21     F: FnOnce(&mut TokenIterator) -> Result<O, Error>,
22 {
23     let mut it = tokens.into_iter();
24     let out = f(&mut it)?;
25 
26     if let Some(tt) = it.next() {
27         return Err(err!(tt.span(), "unexpected additional tokens"));
28     }
29 
30     Ok(out)
31 }
32 
33 /// Tries to parse a helper group (a group with `None` or `()` delimiter).
34 ///
35 /// These groups are inserted by the declarative macro wrappers in `bunt` to
36 /// make parsing in `bunt-macros` easier. In particular, all expressions are
37 /// wrapped in these group, allowing us to skip over them without having a Rust
38 /// expression parser.
expect_helper_group(tt: Option<TokenTree>) -> Result<(TokenStream, Span), Error>39 fn expect_helper_group(tt: Option<TokenTree>) -> Result<(TokenStream, Span), Error> {
40     match tt {
41         Some(TokenTree::Group(g))
42             if g.delimiter() == Delimiter::None || g.delimiter() == Delimiter::Parenthesis =>
43         {
44             Ok((g.stream(), g.span()))
45         }
46         Some(TokenTree::Group(g)) => {
47             Err(err!(
48                 g.span(),
49                 "expected none or () delimited group, but delimiter is {:?} (note: do not use \
50                     the macros from `bunt-macros` directly, but only through `bunt`)",
51                 g.delimiter(),
52             ))
53         }
54         Some(tt) => {
55             Err(err!(
56                 tt.span(),
57                 "expected none or () delimited group, but found different token tree (note: do \
58                     not use the macros from `bunt-macros` directly, but only through `bunt`)",
59             ))
60         }
61         None => Err(err!("expected none or () delimited group, found EOF")),
62     }
63 }
64 
65 /// Tries to parse a string literal.
expect_str_literal(it: &mut TokenIterator) -> Result<(String, Span), Error>66 pub(super) fn expect_str_literal(it: &mut TokenIterator) -> Result<(String, Span), Error> {
67     let tt = it.next().ok_or(err!("expected string literal, found EOF"))?;
68     let lit = litrs::StringLit::try_from(&tt)
69         .map_err(|e| err!(tt.span(), "{}", e))?;
70 
71     Ok((lit.into_value().into_owned(), tt.span()))
72 }
73 
74 impl WriteInput {
parse(it: &mut TokenIterator) -> Result<Self, Error>75     pub(crate) fn parse(it: &mut TokenIterator) -> Result<Self, Error> {
76         let target = Expr::parse(it)?;
77         let format_str = FormatStr::parse(it)?;
78         let args = FormatArgs::parse(it)?;
79 
80         Ok(Self { target, format_str, args })
81     }
82 }
83 
84 impl Expr {
parse(it: &mut TokenIterator) -> Result<Self, Error>85     pub(crate) fn parse(it: &mut TokenIterator) -> Result<Self, Error> {
86         let (tokens, span) = expect_helper_group(it.next())?;
87         Ok(Self { tokens, span })
88     }
89 }
90 
91 impl FormatArgs {
parse(it: &mut TokenIterator) -> Result<Self, Error>92     fn parse(it: &mut TokenIterator) -> Result<Self, Error> {
93         /// Checks if the token stream starting with `tt0` and `tt1` is a named
94         /// argument. If so, returns the name of the argument, otherwise
95         /// (positional argument) returns `None`.
96         fn get_name(tt0: &Option<TokenTree>, tt1: &Option<TokenTree>) -> Option<String> {
97             if let (Some(TokenTree::Ident(name)), Some(TokenTree::Punct(punct))) = (tt0, tt1) {
98                 if punct.as_char() == '=' && punct.spacing() == Spacing::Alone {
99                     return Some(name.to_string())
100                 }
101             }
102 
103             None
104         }
105 
106         let mut exprs = Vec::new();
107         let mut name_indices = HashMap::new();
108         let mut saw_named = false;
109 
110         // The remaining tokens should all be `None` delimited groups each
111         // representing one argument.
112         for arg_group in it {
113             let (arg, span) = expect_helper_group(Some(arg_group))?;
114             let mut it = arg.into_iter();
115             let tt0 = it.next();
116             let tt1 = it.next();
117 
118             if let Some(name) = get_name(&tt0, &tt1) {
119                 saw_named = true;
120 
121                 let expr = Expr {
122                     tokens: it.collect(),
123                     span,
124                 };
125                 let index = exprs.len();
126                 exprs.push(expr);
127                 name_indices.insert(name, index);
128             } else {
129                 if saw_named {
130                     let e = err!(span, "positional argument after named arguments is not allowed");
131                     return Err(e);
132                 }
133 
134                 let expr = Expr {
135                     tokens: vec![tt0, tt1].into_iter().filter_map(|tt| tt).chain(it).collect(),
136                     span,
137                 };
138                 exprs.push(expr);
139             }
140 
141         }
142 
143         Ok(Self { exprs, name_indices })
144     }
145 }
146