]> git.lizzy.rs Git - rust.git/blob - crates/mbe/src/parser.rs
Simpilfy mbe parsing
[rust.git] / crates / mbe / src / parser.rs
1 //! Parser recognizes special macro syntax, `$var` and `$(repeat)*`, in token
2 //! trees.
3
4 use smallvec::SmallVec;
5 use syntax::SmolStr;
6 use tt::Delimiter;
7
8 use crate::{tt_iter::TtIter, MetaTemplate, ParseError};
9
10 #[derive(Clone, Debug, PartialEq, Eq)]
11 pub(crate) enum Op {
12     Var { name: SmolStr, kind: Option<SmolStr>, id: tt::TokenId },
13     Repeat { tokens: MetaTemplate, kind: RepeatKind, separator: Option<Separator> },
14     Leaf(tt::Leaf),
15     Subtree { tokens: MetaTemplate, delimiter: Option<Delimiter> },
16 }
17
18 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
19 pub(crate) enum RepeatKind {
20     ZeroOrMore,
21     OneOrMore,
22     ZeroOrOne,
23 }
24
25 #[derive(Clone, Debug, Eq)]
26 pub(crate) enum Separator {
27     Literal(tt::Literal),
28     Ident(tt::Ident),
29     Puncts(SmallVec<[tt::Punct; 3]>),
30 }
31
32 // Note that when we compare a Separator, we just care about its textual value.
33 impl PartialEq for Separator {
34     fn eq(&self, other: &Separator) -> bool {
35         use Separator::*;
36
37         match (self, other) {
38             (Ident(ref a), Ident(ref b)) => a.text == b.text,
39             (Literal(ref a), Literal(ref b)) => a.text == b.text,
40             (Puncts(ref a), Puncts(ref b)) if a.len() == b.len() => {
41                 let a_iter = a.iter().map(|a| a.char);
42                 let b_iter = b.iter().map(|b| b.char);
43                 a_iter.eq(b_iter)
44             }
45             _ => false,
46         }
47     }
48 }
49
50 pub(crate) fn parse_template(template: &tt::Subtree) -> Result<Vec<Op>, ParseError> {
51     parse_inner(&template, Mode::Template).into_iter().collect()
52 }
53
54 pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Result<Vec<Op>, ParseError> {
55     parse_inner(&pattern, Mode::Pattern).into_iter().collect()
56 }
57
58 #[derive(Clone, Copy)]
59 enum Mode {
60     Pattern,
61     Template,
62 }
63
64 fn parse_inner(tt: &tt::Subtree, mode: Mode) -> Vec<Result<Op, ParseError>> {
65     let mut src = TtIter::new(&tt);
66     std::iter::from_fn(move || {
67         let first = src.next()?;
68         Some(next_op(first, &mut src, mode))
69     })
70     .collect()
71 }
72
73 macro_rules! err {
74     ($($tt:tt)*) => {
75         ParseError::UnexpectedToken(($($tt)*).to_string())
76     };
77 }
78
79 macro_rules! bail {
80     ($($tt:tt)*) => {
81         return Err(err!($($tt)*))
82     };
83 }
84
85 fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Result<Op, ParseError> {
86     let res = match first {
87         tt::TokenTree::Leaf(leaf @ tt::Leaf::Punct(tt::Punct { char: '$', .. })) => {
88             // Note that the '$' itself is a valid token inside macro_rules.
89             let second = match src.next() {
90                 None => return Ok(Op::Leaf(leaf.clone())),
91                 Some(it) => it,
92             };
93             match second {
94                 tt::TokenTree::Subtree(subtree) => {
95                     let (separator, kind) = parse_repeat(src)?;
96                     let tokens = parse_inner(&subtree, mode)
97                         .into_iter()
98                         .collect::<Result<Vec<Op>, ParseError>>()?;
99                     Op::Repeat { tokens: MetaTemplate(tokens), separator, kind }
100                 }
101                 tt::TokenTree::Leaf(leaf) => match leaf {
102                     tt::Leaf::Punct(punct) => {
103                         static UNDERSCORE: SmolStr = SmolStr::new_inline("_");
104
105                         if punct.char != '_' {
106                             return Err(ParseError::Expected("_".to_string()));
107                         }
108                         let name = UNDERSCORE.clone();
109                         let kind = eat_fragment_kind(src, mode)?;
110                         let id = punct.id;
111                         Op::Var { name, kind, id }
112                     }
113                     tt::Leaf::Ident(ident) if ident.text == "crate" => {
114                         // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path.
115                         Op::Leaf(tt::Leaf::from(tt::Ident { text: "$crate".into(), id: ident.id }))
116                     }
117                     tt::Leaf::Ident(ident) => {
118                         let name = ident.text.clone();
119                         let kind = eat_fragment_kind(src, mode)?;
120                         let id = ident.id;
121                         Op::Var { name, kind, id }
122                     }
123                     tt::Leaf::Literal(lit) => {
124                         if is_boolean_literal(&lit) {
125                             let name = lit.text.clone();
126                             let kind = eat_fragment_kind(src, mode)?;
127                             let id = lit.id;
128                             Op::Var { name, kind, id }
129                         } else {
130                             bail!("bad var 2");
131                         }
132                     }
133                 },
134             }
135         }
136         tt::TokenTree::Leaf(tt) => Op::Leaf(tt.clone()),
137         tt::TokenTree::Subtree(subtree) => {
138             let tokens =
139                 parse_inner(&subtree, mode).into_iter().collect::<Result<Vec<Op>, ParseError>>()?;
140             Op::Subtree { tokens: MetaTemplate(tokens), delimiter: subtree.delimiter }
141         }
142     };
143     Ok(res)
144 }
145
146 fn eat_fragment_kind<'a>(src: &mut TtIter<'a>, mode: Mode) -> Result<Option<SmolStr>, ParseError> {
147     if let Mode::Pattern = mode {
148         src.expect_char(':').map_err(|()| err!("bad fragment specifier 1"))?;
149         let ident = src.expect_ident().map_err(|()| err!("bad fragment specifier 1"))?;
150         return Ok(Some(ident.text.clone()));
151     };
152     Ok(None)
153 }
154
155 fn is_boolean_literal(lit: &tt::Literal) -> bool {
156     matches!(lit.text.as_str(), "true" | "false")
157 }
158
159 fn parse_repeat(src: &mut TtIter) -> Result<(Option<Separator>, RepeatKind), ParseError> {
160     let mut separator = Separator::Puncts(SmallVec::new());
161     for tt in src {
162         let tt = match tt {
163             tt::TokenTree::Leaf(leaf) => leaf,
164             tt::TokenTree::Subtree(_) => return Err(ParseError::InvalidRepeat),
165         };
166         let has_sep = match &separator {
167             Separator::Puncts(puncts) => !puncts.is_empty(),
168             _ => true,
169         };
170         match tt {
171             tt::Leaf::Ident(_) | tt::Leaf::Literal(_) if has_sep => {
172                 return Err(ParseError::InvalidRepeat)
173             }
174             tt::Leaf::Ident(ident) => separator = Separator::Ident(ident.clone()),
175             tt::Leaf::Literal(lit) => separator = Separator::Literal(lit.clone()),
176             tt::Leaf::Punct(punct) => {
177                 let repeat_kind = match punct.char {
178                     '*' => RepeatKind::ZeroOrMore,
179                     '+' => RepeatKind::OneOrMore,
180                     '?' => RepeatKind::ZeroOrOne,
181                     _ => {
182                         match &mut separator {
183                             Separator::Puncts(puncts) => {
184                                 if puncts.len() == 3 {
185                                     return Err(ParseError::InvalidRepeat);
186                                 }
187                                 puncts.push(punct.clone())
188                             }
189                             _ => return Err(ParseError::InvalidRepeat),
190                         }
191                         continue;
192                     }
193                 };
194                 let separator = if has_sep { Some(separator) } else { None };
195                 return Ok((separator, repeat_kind));
196             }
197         }
198     }
199     Err(ParseError::InvalidRepeat)
200 }