diff options
Diffstat (limited to 'crates/ra_mbe/src/parser.rs')
-rw-r--r-- | crates/ra_mbe/src/parser.rs | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/crates/ra_mbe/src/parser.rs b/crates/ra_mbe/src/parser.rs new file mode 100644 index 000000000..575f587cf --- /dev/null +++ b/crates/ra_mbe/src/parser.rs | |||
@@ -0,0 +1,187 @@ | |||
1 | //! Parser recognizes special macro syntax, `$var` and `$(repeat)*`, in token | ||
2 | //! trees. | ||
3 | |||
4 | use ra_syntax::SmolStr; | ||
5 | use smallvec::SmallVec; | ||
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<'a>( | ||
49 | template: &'a tt::Subtree, | ||
50 | ) -> impl Iterator<Item = Result<Op<'a>, ExpandError>> { | ||
51 | parse_inner(template, Mode::Template) | ||
52 | } | ||
53 | |||
54 | pub(crate) fn parse_pattern<'a>( | ||
55 | pattern: &'a tt::Subtree, | ||
56 | ) -> impl Iterator<Item = Result<Op<'a>, 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<'a>( | ||
67 | src: &'a tt::Subtree, | ||
68 | mode: Mode, | ||
69 | ) -> impl Iterator<Item = Result<Op<'a>, ExpandError>> { | ||
70 | let mut src = TtIter::new(src); | ||
71 | std::iter::from_fn(move || { | ||
72 | let first = src.next()?; | ||
73 | Some(next_op(first, &mut src, mode)) | ||
74 | }) | ||
75 | } | ||
76 | |||
77 | macro_rules! err { | ||
78 | ($($tt:tt)*) => { | ||
79 | ExpandError::UnexpectedToken | ||
80 | }; | ||
81 | } | ||
82 | |||
83 | macro_rules! bail { | ||
84 | ($($tt:tt)*) => { | ||
85 | return Err(err!($($tt)*)) | ||
86 | }; | ||
87 | } | ||
88 | |||
89 | fn next_op<'a>( | ||
90 | first: &'a tt::TokenTree, | ||
91 | src: &mut TtIter<'a>, | ||
92 | mode: Mode, | ||
93 | ) -> Result<Op<'a>, ExpandError> { | ||
94 | let res = match first { | ||
95 | tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '$', .. })) => { | ||
96 | let second = src.next().ok_or_else(|| err!("bad var 1"))?; | ||
97 | match second { | ||
98 | tt::TokenTree::Subtree(subtree) => { | ||
99 | let (separator, kind) = parse_repeat(src)?; | ||
100 | Op::Repeat { subtree, separator, kind } | ||
101 | } | ||
102 | tt::TokenTree::Leaf(leaf) => match leaf { | ||
103 | tt::Leaf::Punct(..) => Err(ExpandError::UnexpectedToken)?, | ||
104 | tt::Leaf::Ident(ident) => { | ||
105 | let name = &ident.text; | ||
106 | let kind = eat_fragment_kind(src, mode)?; | ||
107 | Op::Var { name, kind } | ||
108 | } | ||
109 | tt::Leaf::Literal(lit) => { | ||
110 | if is_boolean_literal(lit) { | ||
111 | let name = &lit.text; | ||
112 | let kind = eat_fragment_kind(src, mode)?; | ||
113 | Op::Var { name, kind } | ||
114 | } else { | ||
115 | bail!("bad var 2"); | ||
116 | } | ||
117 | } | ||
118 | }, | ||
119 | } | ||
120 | } | ||
121 | tt => Op::TokenTree(tt), | ||
122 | }; | ||
123 | Ok(res) | ||
124 | } | ||
125 | |||
126 | fn eat_fragment_kind<'a>( | ||
127 | src: &mut TtIter<'a>, | ||
128 | mode: Mode, | ||
129 | ) -> Result<Option<&'a SmolStr>, ExpandError> { | ||
130 | if let Mode::Pattern = mode { | ||
131 | src.expect_char(':').map_err(|()| err!("bad fragment specifier 1"))?; | ||
132 | let ident = src.expect_ident().map_err(|()| err!("bad fragment specifier 1"))?; | ||
133 | return Ok(Some(&ident.text)); | ||
134 | }; | ||
135 | Ok(None) | ||
136 | } | ||
137 | |||
138 | fn is_boolean_literal(lit: &tt::Literal) -> bool { | ||
139 | match lit.text.as_str() { | ||
140 | "true" | "false" => true, | ||
141 | _ => false, | ||
142 | } | ||
143 | } | ||
144 | |||
145 | ///TOOD: impl for slice iter | ||
146 | fn parse_repeat(src: &mut TtIter) -> Result<(Option<Separator>, RepeatKind), ExpandError> { | ||
147 | let mut separator = Separator::Puncts(SmallVec::new()); | ||
148 | for tt in src { | ||
149 | let tt = match tt { | ||
150 | tt::TokenTree::Leaf(leaf) => leaf, | ||
151 | tt::TokenTree::Subtree(_) => Err(ExpandError::InvalidRepeat)?, | ||
152 | }; | ||
153 | let has_sep = match &separator { | ||
154 | Separator::Puncts(puncts) => puncts.len() != 0, | ||
155 | _ => true, | ||
156 | }; | ||
157 | match tt { | ||
158 | tt::Leaf::Ident(_) | tt::Leaf::Literal(_) if has_sep => { | ||
159 | Err(ExpandError::InvalidRepeat)? | ||
160 | } | ||
161 | tt::Leaf::Ident(ident) => separator = Separator::Ident(ident.clone()), | ||
162 | tt::Leaf::Literal(lit) => separator = Separator::Literal(lit.clone()), | ||
163 | tt::Leaf::Punct(punct) => { | ||
164 | let repeat_kind = match punct.char { | ||
165 | '*' => RepeatKind::ZeroOrMore, | ||
166 | '+' => RepeatKind::OneOrMore, | ||
167 | '?' => RepeatKind::ZeroOrOne, | ||
168 | _ => { | ||
169 | match &mut separator { | ||
170 | Separator::Puncts(puncts) => { | ||
171 | if puncts.len() == 3 { | ||
172 | Err(ExpandError::InvalidRepeat)? | ||
173 | } | ||
174 | puncts.push(punct.clone()) | ||
175 | } | ||
176 | _ => Err(ExpandError::InvalidRepeat)?, | ||
177 | } | ||
178 | continue; | ||
179 | } | ||
180 | }; | ||
181 | let separator = if has_sep { Some(separator) } else { None }; | ||
182 | return Ok((separator, repeat_kind)); | ||
183 | } | ||
184 | } | ||
185 | } | ||
186 | Err(ExpandError::InvalidRepeat) | ||
187 | } | ||