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