diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-09-17 00:06:38 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2019-09-17 00:06:38 +0100 |
commit | 8eb2697b7d2a98c952b3acd1711829a13e13cab1 (patch) | |
tree | b8bee4dd75c07f55b9f0d7e86380aba904557c3a /crates/ra_mbe/src/mbe_expander/transcriber.rs | |
parent | ba583091e60553633dd3cc9ab37a1d9f64827a1e (diff) | |
parent | 37ef8927c373b8eadd63edc1f70055428c49290e (diff) |
Merge #1855
1855: split mbe expander code into two modules r=matklad a=matklad
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_mbe/src/mbe_expander/transcriber.rs')
-rw-r--r-- | crates/ra_mbe/src/mbe_expander/transcriber.rs | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/crates/ra_mbe/src/mbe_expander/transcriber.rs b/crates/ra_mbe/src/mbe_expander/transcriber.rs new file mode 100644 index 000000000..a3df1b7de --- /dev/null +++ b/crates/ra_mbe/src/mbe_expander/transcriber.rs | |||
@@ -0,0 +1,227 @@ | |||
1 | use ra_syntax::SmolStr; | ||
2 | |||
3 | use crate::{ | ||
4 | mbe_expander::{Binding, Bindings, Fragment}, | ||
5 | ExpandError, | ||
6 | }; | ||
7 | |||
8 | impl Bindings { | ||
9 | fn contains(&self, name: &SmolStr) -> bool { | ||
10 | self.inner.contains_key(name) | ||
11 | } | ||
12 | |||
13 | fn get(&self, name: &SmolStr, nesting: &[usize]) -> Result<&Fragment, ExpandError> { | ||
14 | let mut b = self.inner.get(name).ok_or_else(|| { | ||
15 | ExpandError::BindingError(format!("could not find binding `{}`", name)) | ||
16 | })?; | ||
17 | for &idx in nesting.iter() { | ||
18 | b = match b { | ||
19 | Binding::Fragment(_) => break, | ||
20 | Binding::Nested(bs) => bs.get(idx).ok_or_else(|| { | ||
21 | ExpandError::BindingError(format!("could not find nested binding `{}`", name)) | ||
22 | })?, | ||
23 | Binding::Empty => { | ||
24 | return Err(ExpandError::BindingError(format!( | ||
25 | "could not find empty binding `{}`", | ||
26 | name | ||
27 | ))) | ||
28 | } | ||
29 | }; | ||
30 | } | ||
31 | match b { | ||
32 | Binding::Fragment(it) => Ok(it), | ||
33 | Binding::Nested(_) => Err(ExpandError::BindingError(format!( | ||
34 | "expected simple binding, found nested binding `{}`", | ||
35 | name | ||
36 | ))), | ||
37 | Binding::Empty => Err(ExpandError::BindingError(format!( | ||
38 | "expected simple binding, found empty binding `{}`", | ||
39 | name | ||
40 | ))), | ||
41 | } | ||
42 | } | ||
43 | } | ||
44 | |||
45 | pub(super) fn transcribe( | ||
46 | bindings: &Bindings, | ||
47 | template: &crate::Subtree, | ||
48 | ) -> Result<tt::Subtree, ExpandError> { | ||
49 | let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new(), var_expanded: false }; | ||
50 | expand_subtree(template, &mut ctx) | ||
51 | } | ||
52 | |||
53 | #[derive(Debug)] | ||
54 | struct ExpandCtx<'a> { | ||
55 | bindings: &'a Bindings, | ||
56 | nesting: Vec<usize>, | ||
57 | var_expanded: bool, | ||
58 | } | ||
59 | |||
60 | fn expand_subtree( | ||
61 | template: &crate::Subtree, | ||
62 | ctx: &mut ExpandCtx, | ||
63 | ) -> Result<tt::Subtree, ExpandError> { | ||
64 | let mut buf: Vec<tt::TokenTree> = Vec::new(); | ||
65 | for tt in template.token_trees.iter() { | ||
66 | let tt = expand_tt(tt, ctx)?; | ||
67 | push_fragment(&mut buf, tt); | ||
68 | } | ||
69 | |||
70 | Ok(tt::Subtree { delimiter: template.delimiter, token_trees: buf }) | ||
71 | } | ||
72 | |||
73 | fn expand_tt(template: &crate::TokenTree, ctx: &mut ExpandCtx) -> Result<Fragment, ExpandError> { | ||
74 | let res: tt::TokenTree = match template { | ||
75 | crate::TokenTree::Subtree(subtree) => expand_subtree(subtree, ctx)?.into(), | ||
76 | crate::TokenTree::Repeat(repeat) => { | ||
77 | let mut buf: Vec<tt::TokenTree> = Vec::new(); | ||
78 | ctx.nesting.push(0); | ||
79 | // Dirty hack to make macro-expansion terminate. | ||
80 | // This should be replaced by a propper macro-by-example implementation | ||
81 | let mut limit = 65536; | ||
82 | let mut has_seps = 0; | ||
83 | let mut counter = 0; | ||
84 | |||
85 | // We store the old var expanded value, and restore it later | ||
86 | // It is because before this `$repeat`, | ||
87 | // it is possible some variables already expanad in the same subtree | ||
88 | // | ||
89 | // `some_var_expanded` keep check if the deeper subtree has expanded variables | ||
90 | let mut some_var_expanded = false; | ||
91 | let old_var_expanded = ctx.var_expanded; | ||
92 | ctx.var_expanded = false; | ||
93 | |||
94 | while let Ok(t) = expand_subtree(&repeat.subtree, ctx) { | ||
95 | // if no var expanded in the child, we count it as a fail | ||
96 | if !ctx.var_expanded { | ||
97 | break; | ||
98 | } | ||
99 | |||
100 | // Reset `ctx.var_expandeded` to see if there is other expanded variable | ||
101 | // in the next matching | ||
102 | some_var_expanded = true; | ||
103 | ctx.var_expanded = false; | ||
104 | |||
105 | counter += 1; | ||
106 | limit -= 1; | ||
107 | if limit == 0 { | ||
108 | log::warn!( | ||
109 | "expand_tt excced in repeat pattern exceed limit => {:#?}\n{:#?}", | ||
110 | template, | ||
111 | ctx | ||
112 | ); | ||
113 | break; | ||
114 | } | ||
115 | |||
116 | let idx = ctx.nesting.pop().unwrap(); | ||
117 | ctx.nesting.push(idx + 1); | ||
118 | push_subtree(&mut buf, t); | ||
119 | |||
120 | if let Some(ref sep) = repeat.separator { | ||
121 | match sep { | ||
122 | crate::Separator::Ident(ident) => { | ||
123 | has_seps = 1; | ||
124 | buf.push(tt::Leaf::from(ident.clone()).into()); | ||
125 | } | ||
126 | crate::Separator::Literal(lit) => { | ||
127 | has_seps = 1; | ||
128 | buf.push(tt::Leaf::from(lit.clone()).into()); | ||
129 | } | ||
130 | |||
131 | crate::Separator::Puncts(puncts) => { | ||
132 | has_seps = puncts.len(); | ||
133 | for punct in puncts { | ||
134 | buf.push(tt::Leaf::from(*punct).into()); | ||
135 | } | ||
136 | } | ||
137 | } | ||
138 | } | ||
139 | |||
140 | if let crate::RepeatKind::ZeroOrOne = repeat.kind { | ||
141 | break; | ||
142 | } | ||
143 | } | ||
144 | |||
145 | // Restore the `var_expanded` by combining old one and the new one | ||
146 | ctx.var_expanded = some_var_expanded || old_var_expanded; | ||
147 | |||
148 | ctx.nesting.pop().unwrap(); | ||
149 | for _ in 0..has_seps { | ||
150 | buf.pop(); | ||
151 | } | ||
152 | |||
153 | if crate::RepeatKind::OneOrMore == repeat.kind && counter == 0 { | ||
154 | return Err(ExpandError::UnexpectedToken); | ||
155 | } | ||
156 | |||
157 | // Check if it is a single token subtree without any delimiter | ||
158 | // e.g {Delimiter:None> ['>'] /Delimiter:None>} | ||
159 | tt::Subtree { delimiter: tt::Delimiter::None, token_trees: buf }.into() | ||
160 | } | ||
161 | crate::TokenTree::Leaf(leaf) => match leaf { | ||
162 | crate::Leaf::Ident(ident) => tt::Leaf::from(tt::Ident { | ||
163 | text: ident.text.clone(), | ||
164 | id: tt::TokenId::unspecified(), | ||
165 | }) | ||
166 | .into(), | ||
167 | crate::Leaf::Punct(punct) => tt::Leaf::from(*punct).into(), | ||
168 | crate::Leaf::Var(v) => { | ||
169 | if v.text == "crate" { | ||
170 | // FIXME: Properly handle $crate token | ||
171 | tt::Leaf::from(tt::Ident { | ||
172 | text: "$crate".into(), | ||
173 | id: tt::TokenId::unspecified(), | ||
174 | }) | ||
175 | .into() | ||
176 | } else if !ctx.bindings.contains(&v.text) { | ||
177 | // Note that it is possible to have a `$var` inside a macro which is not bound. | ||
178 | // For example: | ||
179 | // ``` | ||
180 | // macro_rules! foo { | ||
181 | // ($a:ident, $b:ident, $c:tt) => { | ||
182 | // macro_rules! bar { | ||
183 | // ($bi:ident) => { | ||
184 | // fn $bi() -> u8 {$c} | ||
185 | // } | ||
186 | // } | ||
187 | // } | ||
188 | // ``` | ||
189 | // We just treat it a normal tokens | ||
190 | tt::Subtree { | ||
191 | delimiter: tt::Delimiter::None, | ||
192 | token_trees: vec![ | ||
193 | tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone }) | ||
194 | .into(), | ||
195 | tt::Leaf::from(tt::Ident { | ||
196 | text: v.text.clone(), | ||
197 | id: tt::TokenId::unspecified(), | ||
198 | }) | ||
199 | .into(), | ||
200 | ], | ||
201 | } | ||
202 | .into() | ||
203 | } else { | ||
204 | let fragment = ctx.bindings.get(&v.text, &ctx.nesting)?.clone(); | ||
205 | ctx.var_expanded = true; | ||
206 | return Ok(fragment); | ||
207 | } | ||
208 | } | ||
209 | crate::Leaf::Literal(l) => tt::Leaf::from(tt::Literal { text: l.text.clone() }).into(), | ||
210 | }, | ||
211 | }; | ||
212 | Ok(Fragment::Tokens(res)) | ||
213 | } | ||
214 | |||
215 | fn push_fragment(buf: &mut Vec<tt::TokenTree>, fragment: Fragment) { | ||
216 | match fragment { | ||
217 | Fragment::Tokens(tt::TokenTree::Subtree(tt)) => push_subtree(buf, tt), | ||
218 | Fragment::Tokens(tt) | Fragment::Ast(tt) => buf.push(tt), | ||
219 | } | ||
220 | } | ||
221 | |||
222 | fn push_subtree(buf: &mut Vec<tt::TokenTree>, tt: tt::Subtree) { | ||
223 | match tt.delimiter { | ||
224 | tt::Delimiter::None => buf.extend(tt.token_trees), | ||
225 | _ => buf.push(tt.into()), | ||
226 | } | ||
227 | } | ||