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() {}`
6 use super::ExpandResult;
8 mbe_expander::{Binding, Bindings, Fragment},
9 parser::{parse_template, Op, RepeatKind, Separator},
14 fn contains(&self, name: &str) -> bool {
15 self.inner.contains_key(name)
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))
22 for nesting_state in nesting.iter_mut() {
23 nesting_state.hit = true;
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))
31 nesting_state.at_end = true;
32 return Err(ExpandError::BindingError(format!(
33 "could not find empty binding `{}`",
40 Binding::Fragment(it) => Ok(it),
41 Binding::Nested(_) => Err(ExpandError::BindingError(format!(
42 "expected simple binding, found nested binding `{}`",
45 Binding::Empty => Err(ExpandError::BindingError(format!(
46 "expected simple binding, found empty binding `{}`",
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)
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
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
72 struct ExpandCtx<'a> {
73 bindings: &'a Bindings,
74 nesting: Vec<NestingState>,
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();
85 for op in parse_template(template) {
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);
98 arena.push(tt.into());
100 Op::Var { name, .. } => {
101 let ExpandResult { value: fragment, err: e } = expand_var(ctx, name);
103 push_fragment(arena, fragment);
105 Op::Repeat { subtree, kind, separator } => {
106 let ExpandResult { value: fragment, err: e } =
107 expand_repeat(ctx, subtree, kind, separator, arena);
109 push_fragment(arena, fragment)
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 }
118 fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> {
120 // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path.
122 tt::Leaf::from(tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() })
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.
129 // macro_rules! foo {
130 // ($a:ident, $b:ident, $c:tt) => {
131 // macro_rules! bar {
133 // fn $bi() -> u8 {$c}
138 // We just treat it a normal tokens
139 let tt = tt::Subtree {
142 tt::Leaf::from(tt::Punct {
144 spacing: tt::Spacing::Alone,
145 id: tt::TokenId::unspecified(),
148 tt::Leaf::from(tt::Ident { text: v.clone(), id: tt::TokenId::unspecified() })
153 ExpandResult::ok(Fragment::Tokens(tt))
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()),
164 template: &tt::Subtree,
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
174 let mut has_seps = 0;
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 {
183 nesting_state.idx += 1;
184 nesting_state.hit = false;
187 if counter == limit {
189 "expand_tt excced in repeat pattern exceed limit => {:#?}\n{:#?}",
201 push_subtree(&mut buf, t);
203 if let Some(ref sep) = separator {
205 Separator::Ident(ident) => {
207 buf.push(tt::Leaf::from(ident.clone()).into());
209 Separator::Literal(lit) => {
211 buf.push(tt::Leaf::from(lit.clone()).into());
214 Separator::Puncts(puncts) => {
215 has_seps = puncts.len();
216 for punct in puncts {
217 buf.push(tt::Leaf::from(*punct).into());
223 if RepeatKind::ZeroOrOne == kind {
228 ctx.nesting.pop().unwrap();
229 for _ in 0..has_seps {
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();
237 if RepeatKind::OneOrMore == kind && counter == 0 {
238 return ExpandResult {
239 value: Fragment::Tokens(tt),
240 err: Some(ExpandError::UnexpectedToken),
243 ExpandResult::ok(Fragment::Tokens(tt))
246 fn push_fragment(buf: &mut Vec<tt::TokenTree>, fragment: Fragment) {
248 Fragment::Tokens(tt::TokenTree::Subtree(tt)) => push_subtree(buf, tt),
249 Fragment::Tokens(tt) | Fragment::Ast(tt) => buf.push(tt),
253 fn push_subtree(buf: &mut Vec<tt::TokenTree>, tt: tt::Subtree) {
255 None => buf.extend(tt.token_trees),
256 _ => buf.push(tt.into()),