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