]> git.lizzy.rs Git - rust.git/blob - crates/mbe/src/mbe_expander/transcriber.rs
Merge #6921
[rust.git] / crates / mbe / src / mbe_expander / transcriber.rs
1 //! Transcriber takes a template, like `fn $ident() {}`, a set of bindings like
2 //! `$ident => foo`, interpolates variables in the template, to get `fn foo() {}`
3
4 use syntax::SmolStr;
5
6 use super::ExpandResult;
7 use crate::{
8     mbe_expander::{Binding, Bindings, Fragment},
9     parser::{parse_template, Op, RepeatKind, Separator},
10     ExpandError,
11 };
12
13 impl Bindings {
14     fn contains(&self, name: &str) -> bool {
15         self.inner.contains_key(name)
16     }
17
18     fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result<&Fragment, ExpandError> {
19         let mut b = self.inner.get(name).ok_or_else(|| {
20             ExpandError::BindingError(format!("could not find binding `{}`", name))
21         })?;
22         for nesting_state in nesting.iter_mut() {
23             nesting_state.hit = true;
24             b = match b {
25                 Binding::Fragment(_) => break,
26                 Binding::Nested(bs) => bs.get(nesting_state.idx).ok_or_else(|| {
27                     nesting_state.at_end = true;
28                     ExpandError::BindingError(format!("could not find nested binding `{}`", name))
29                 })?,
30                 Binding::Empty => {
31                     nesting_state.at_end = true;
32                     return Err(ExpandError::BindingError(format!(
33                         "could not find empty binding `{}`",
34                         name
35                     )));
36                 }
37             };
38         }
39         match b {
40             Binding::Fragment(it) => Ok(it),
41             Binding::Nested(_) => Err(ExpandError::BindingError(format!(
42                 "expected simple binding, found nested binding `{}`",
43                 name
44             ))),
45             Binding::Empty => Err(ExpandError::BindingError(format!(
46                 "expected simple binding, found empty binding `{}`",
47                 name
48             ))),
49         }
50     }
51 }
52
53 pub(super) fn transcribe(template: &tt::Subtree, bindings: &Bindings) -> ExpandResult<tt::Subtree> {
54     assert!(template.delimiter == None);
55     let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() };
56     let mut arena: Vec<tt::TokenTree> = Vec::new();
57     expand_subtree(&mut ctx, template, &mut arena)
58 }
59
60 #[derive(Debug)]
61 struct NestingState {
62     idx: usize,
63     /// `hit` is currently necessary to tell `expand_repeat` if it should stop
64     /// because there is no variable in use by the current repetition
65     hit: bool,
66     /// `at_end` is currently necessary to tell `expand_repeat` if it should stop
67     /// because there is no more value avaible for the current repetition
68     at_end: bool,
69 }
70
71 #[derive(Debug)]
72 struct ExpandCtx<'a> {
73     bindings: &'a Bindings,
74     nesting: Vec<NestingState>,
75 }
76
77 fn expand_subtree(
78     ctx: &mut ExpandCtx,
79     template: &tt::Subtree,
80     arena: &mut Vec<tt::TokenTree>,
81 ) -> ExpandResult<tt::Subtree> {
82     // remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation
83     let start_elements = arena.len();
84     let mut err = None;
85     for op in parse_template(template) {
86         let op = match op {
87             Ok(op) => op,
88             Err(e) => {
89                 err = Some(e);
90                 break;
91             }
92         };
93         match op {
94             Op::TokenTree(tt @ tt::TokenTree::Leaf(..)) => arena.push(tt.clone()),
95             Op::TokenTree(tt::TokenTree::Subtree(tt)) => {
96                 let ExpandResult { value: tt, err: e } = expand_subtree(ctx, tt, arena);
97                 err = err.or(e);
98                 arena.push(tt.into());
99             }
100             Op::Var { name, .. } => {
101                 let ExpandResult { value: fragment, err: e } = expand_var(ctx, name);
102                 err = err.or(e);
103                 push_fragment(arena, fragment);
104             }
105             Op::Repeat { subtree, kind, separator } => {
106                 let ExpandResult { value: fragment, err: e } =
107                     expand_repeat(ctx, subtree, kind, separator, arena);
108                 err = err.or(e);
109                 push_fragment(arena, fragment)
110             }
111         }
112     }
113     // drain the elements added in this instance of expand_subtree
114     let tts = arena.drain(start_elements..arena.len()).collect();
115     ExpandResult { value: tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err }
116 }
117
118 fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> {
119     if v == "crate" {
120         // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path.
121         let tt =
122             tt::Leaf::from(tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() })
123                 .into();
124         ExpandResult::ok(Fragment::Tokens(tt))
125     } else if !ctx.bindings.contains(v) {
126         // Note that it is possible to have a `$var` inside a macro which is not bound.
127         // For example:
128         // ```
129         // macro_rules! foo {
130         //     ($a:ident, $b:ident, $c:tt) => {
131         //         macro_rules! bar {
132         //             ($bi:ident) => {
133         //                 fn $bi() -> u8 {$c}
134         //             }
135         //         }
136         //     }
137         // ```
138         // We just treat it a normal tokens
139         let tt = tt::Subtree {
140             delimiter: None,
141             token_trees: vec![
142                 tt::Leaf::from(tt::Punct {
143                     char: '$',
144                     spacing: tt::Spacing::Alone,
145                     id: tt::TokenId::unspecified(),
146                 })
147                 .into(),
148                 tt::Leaf::from(tt::Ident { text: v.clone(), id: tt::TokenId::unspecified() })
149                     .into(),
150             ],
151         }
152         .into();
153         ExpandResult::ok(Fragment::Tokens(tt))
154     } else {
155         ctx.bindings.get(&v, &mut ctx.nesting).map_or_else(
156             |e| ExpandResult { value: Fragment::Tokens(tt::TokenTree::empty()), err: Some(e) },
157             |b| ExpandResult::ok(b.clone()),
158         )
159     }
160 }
161
162 fn expand_repeat(
163     ctx: &mut ExpandCtx,
164     template: &tt::Subtree,
165     kind: RepeatKind,
166     separator: Option<Separator>,
167     arena: &mut Vec<tt::TokenTree>,
168 ) -> ExpandResult<Fragment> {
169     let mut buf: Vec<tt::TokenTree> = Vec::new();
170     ctx.nesting.push(NestingState { idx: 0, at_end: false, hit: false });
171     // Dirty hack to make macro-expansion terminate.
172     // This should be replaced by a proper macro-by-example implementation
173     let limit = 65536;
174     let mut has_seps = 0;
175     let mut counter = 0;
176
177     loop {
178         let ExpandResult { value: mut t, err: e } = expand_subtree(ctx, template, arena);
179         let nesting_state = ctx.nesting.last_mut().unwrap();
180         if nesting_state.at_end || !nesting_state.hit {
181             break;
182         }
183         nesting_state.idx += 1;
184         nesting_state.hit = false;
185
186         counter += 1;
187         if counter == limit {
188             log::warn!(
189                 "expand_tt excced in repeat pattern exceed limit => {:#?}\n{:#?}",
190                 template,
191                 ctx
192             );
193             break;
194         }
195
196         if e.is_some() {
197             continue;
198         }
199
200         t.delimiter = None;
201         push_subtree(&mut buf, t);
202
203         if let Some(ref sep) = separator {
204             match sep {
205                 Separator::Ident(ident) => {
206                     has_seps = 1;
207                     buf.push(tt::Leaf::from(ident.clone()).into());
208                 }
209                 Separator::Literal(lit) => {
210                     has_seps = 1;
211                     buf.push(tt::Leaf::from(lit.clone()).into());
212                 }
213
214                 Separator::Puncts(puncts) => {
215                     has_seps = puncts.len();
216                     for punct in puncts {
217                         buf.push(tt::Leaf::from(*punct).into());
218                     }
219                 }
220             }
221         }
222
223         if RepeatKind::ZeroOrOne == kind {
224             break;
225         }
226     }
227
228     ctx.nesting.pop().unwrap();
229     for _ in 0..has_seps {
230         buf.pop();
231     }
232
233     // Check if it is a single token subtree without any delimiter
234     // e.g {Delimiter:None> ['>'] /Delimiter:None>}
235     let tt = tt::Subtree { delimiter: None, token_trees: buf }.into();
236
237     if RepeatKind::OneOrMore == kind && counter == 0 {
238         return ExpandResult {
239             value: Fragment::Tokens(tt),
240             err: Some(ExpandError::UnexpectedToken),
241         };
242     }
243     ExpandResult::ok(Fragment::Tokens(tt))
244 }
245
246 fn push_fragment(buf: &mut Vec<tt::TokenTree>, fragment: Fragment) {
247     match fragment {
248         Fragment::Tokens(tt::TokenTree::Subtree(tt)) => push_subtree(buf, tt),
249         Fragment::Tokens(tt) | Fragment::Ast(tt) => buf.push(tt),
250     }
251 }
252
253 fn push_subtree(buf: &mut Vec<tt::TokenTree>, tt: tt::Subtree) {
254     match tt.delimiter {
255         None => buf.extend(tt.token_trees),
256         _ => buf.push(tt.into()),
257     }
258 }