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