2019-09-17 02:54:22 +03:00
|
|
|
//! Transcraber takes a template, like `fn $ident() {}`, a set of bindings like
|
|
|
|
//! `$ident => foo`, interpolates variables in the template, to get `fn foo() {}`
|
|
|
|
|
2019-09-17 02:06:14 +03:00
|
|
|
use ra_syntax::SmolStr;
|
|
|
|
|
2020-03-13 13:03:31 +01:00
|
|
|
use super::ExpandResult;
|
2019-09-17 02:06:14 +03:00
|
|
|
use crate::{
|
|
|
|
mbe_expander::{Binding, Bindings, Fragment},
|
2019-09-17 02:54:22 +03:00
|
|
|
parser::{parse_template, Op, RepeatKind, Separator},
|
2019-09-17 02:06:14 +03:00
|
|
|
ExpandError,
|
|
|
|
};
|
|
|
|
|
|
|
|
impl Bindings {
|
2019-09-17 02:54:22 +03:00
|
|
|
fn contains(&self, name: &str) -> bool {
|
2019-09-17 02:06:14 +03:00
|
|
|
self.inner.contains_key(name)
|
|
|
|
}
|
|
|
|
|
2019-12-28 22:26:24 +01:00
|
|
|
fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result<&Fragment, ExpandError> {
|
2019-09-17 02:06:14 +03:00
|
|
|
let mut b = self.inner.get(name).ok_or_else(|| {
|
|
|
|
ExpandError::BindingError(format!("could not find binding `{}`", name))
|
|
|
|
})?;
|
2019-12-30 17:07:23 +01:00
|
|
|
for nesting_state in nesting.iter_mut() {
|
|
|
|
nesting_state.hit = true;
|
2019-09-17 02:06:14 +03:00
|
|
|
b = match b {
|
|
|
|
Binding::Fragment(_) => break,
|
2019-12-30 17:07:23 +01:00
|
|
|
Binding::Nested(bs) => bs.get(nesting_state.idx).ok_or_else(|| {
|
|
|
|
nesting_state.at_end = true;
|
2019-09-17 02:06:14 +03:00
|
|
|
ExpandError::BindingError(format!("could not find nested binding `{}`", name))
|
|
|
|
})?,
|
|
|
|
Binding::Empty => {
|
2019-12-30 17:07:23 +01:00
|
|
|
nesting_state.at_end = true;
|
2019-09-17 02:06:14 +03:00
|
|
|
return Err(ExpandError::BindingError(format!(
|
|
|
|
"could not find empty binding `{}`",
|
|
|
|
name
|
2019-12-28 22:26:24 +01:00
|
|
|
)));
|
2019-09-17 02:06:14 +03:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
match b {
|
|
|
|
Binding::Fragment(it) => Ok(it),
|
|
|
|
Binding::Nested(_) => Err(ExpandError::BindingError(format!(
|
|
|
|
"expected simple binding, found nested binding `{}`",
|
|
|
|
name
|
|
|
|
))),
|
|
|
|
Binding::Empty => Err(ExpandError::BindingError(format!(
|
|
|
|
"expected simple binding, found empty binding `{}`",
|
|
|
|
name
|
|
|
|
))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-13 13:03:31 +01:00
|
|
|
pub(super) fn transcribe(template: &tt::Subtree, bindings: &Bindings) -> ExpandResult<tt::Subtree> {
|
2019-12-13 21:53:34 +08:00
|
|
|
assert!(template.delimiter == None);
|
2019-12-28 22:26:24 +01:00
|
|
|
let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() };
|
2019-09-17 02:54:22 +03:00
|
|
|
expand_subtree(&mut ctx, template)
|
2019-09-17 02:06:14 +03:00
|
|
|
}
|
|
|
|
|
2019-12-28 22:26:24 +01:00
|
|
|
#[derive(Debug)]
|
|
|
|
struct NestingState {
|
|
|
|
idx: usize,
|
2019-12-30 17:07:23 +01:00
|
|
|
/// `hit` is currently necessary to tell `expand_repeat` if it should stop
|
|
|
|
/// because there is no variable in use by the current repetition
|
2019-12-28 22:26:24 +01:00
|
|
|
hit: bool,
|
2019-12-30 17:07:23 +01:00
|
|
|
/// `at_end` is currently necessary to tell `expand_repeat` if it should stop
|
|
|
|
/// because there is no more value avaible for the current repetition
|
2019-12-28 22:26:24 +01:00
|
|
|
at_end: bool,
|
|
|
|
}
|
|
|
|
|
2019-09-17 02:06:14 +03:00
|
|
|
#[derive(Debug)]
|
|
|
|
struct ExpandCtx<'a> {
|
|
|
|
bindings: &'a Bindings,
|
2019-12-28 22:26:24 +01:00
|
|
|
nesting: Vec<NestingState>,
|
2019-09-17 02:06:14 +03:00
|
|
|
}
|
|
|
|
|
2020-03-13 13:03:31 +01:00
|
|
|
fn expand_subtree(ctx: &mut ExpandCtx, template: &tt::Subtree) -> ExpandResult<tt::Subtree> {
|
2019-09-17 02:06:14 +03:00
|
|
|
let mut buf: Vec<tt::TokenTree> = Vec::new();
|
2020-03-13 13:03:31 +01:00
|
|
|
let mut err = None;
|
2019-09-17 02:54:22 +03:00
|
|
|
for op in parse_template(template) {
|
2020-03-13 13:03:31 +01:00
|
|
|
let op = match op {
|
|
|
|
Ok(op) => op,
|
|
|
|
Err(e) => {
|
|
|
|
err = Some(e);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
match op {
|
2019-09-17 02:54:22 +03:00
|
|
|
Op::TokenTree(tt @ tt::TokenTree::Leaf(..)) => buf.push(tt.clone()),
|
|
|
|
Op::TokenTree(tt::TokenTree::Subtree(tt)) => {
|
2020-03-13 13:03:31 +01:00
|
|
|
let (tt, e) = expand_subtree(ctx, tt);
|
|
|
|
err = err.or(e);
|
2019-09-17 02:54:22 +03:00
|
|
|
buf.push(tt.into());
|
|
|
|
}
|
|
|
|
Op::Var { name, kind: _ } => {
|
2020-03-13 13:03:31 +01:00
|
|
|
let (fragment, e) = expand_var(ctx, name);
|
|
|
|
err = err.or(e);
|
2019-09-17 02:54:22 +03:00
|
|
|
push_fragment(&mut buf, fragment);
|
|
|
|
}
|
|
|
|
Op::Repeat { subtree, kind, separator } => {
|
2020-03-13 13:03:31 +01:00
|
|
|
let (fragment, e) = expand_repeat(ctx, subtree, kind, separator);
|
|
|
|
err = err.or(e);
|
2019-09-17 02:54:22 +03:00
|
|
|
push_fragment(&mut buf, fragment)
|
|
|
|
}
|
|
|
|
}
|
2019-09-17 02:06:14 +03:00
|
|
|
}
|
2020-03-13 13:03:31 +01:00
|
|
|
(tt::Subtree { delimiter: template.delimiter, token_trees: buf }, err)
|
2019-09-17 02:06:14 +03:00
|
|
|
}
|
|
|
|
|
2020-03-13 13:03:31 +01:00
|
|
|
fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> {
|
|
|
|
if v == "crate" {
|
2019-09-27 01:59:38 +08:00
|
|
|
// We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path.
|
2019-09-17 02:54:22 +03:00
|
|
|
let tt =
|
|
|
|
tt::Leaf::from(tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() })
|
|
|
|
.into();
|
2020-03-13 13:03:31 +01:00
|
|
|
(Fragment::Tokens(tt), None)
|
2019-09-17 02:54:22 +03:00
|
|
|
} else if !ctx.bindings.contains(v) {
|
|
|
|
// Note that it is possible to have a `$var` inside a macro which is not bound.
|
|
|
|
// For example:
|
|
|
|
// ```
|
|
|
|
// macro_rules! foo {
|
|
|
|
// ($a:ident, $b:ident, $c:tt) => {
|
|
|
|
// macro_rules! bar {
|
|
|
|
// ($bi:ident) => {
|
|
|
|
// fn $bi() -> u8 {$c}
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// ```
|
|
|
|
// We just treat it a normal tokens
|
|
|
|
let tt = tt::Subtree {
|
2019-12-13 21:53:34 +08:00
|
|
|
delimiter: None,
|
2019-09-17 02:54:22 +03:00
|
|
|
token_trees: vec![
|
2019-12-12 21:47:54 +08:00
|
|
|
tt::Leaf::from(tt::Punct {
|
|
|
|
char: '$',
|
|
|
|
spacing: tt::Spacing::Alone,
|
|
|
|
id: tt::TokenId::unspecified(),
|
|
|
|
})
|
|
|
|
.into(),
|
2019-09-17 02:54:22 +03:00
|
|
|
tt::Leaf::from(tt::Ident { text: v.clone(), id: tt::TokenId::unspecified() })
|
|
|
|
.into(),
|
|
|
|
],
|
|
|
|
}
|
|
|
|
.into();
|
2020-03-13 13:03:31 +01:00
|
|
|
(Fragment::Tokens(tt), None)
|
2019-09-17 02:54:22 +03:00
|
|
|
} else {
|
2020-03-13 13:03:31 +01:00
|
|
|
ctx.bindings.get(&v, &mut ctx.nesting).map_or_else(
|
|
|
|
|e| (Fragment::Tokens(tt::TokenTree::empty()), Some(e)),
|
|
|
|
|b| (b.clone(), None),
|
|
|
|
)
|
|
|
|
}
|
2019-09-17 02:54:22 +03:00
|
|
|
}
|
2019-09-17 02:06:14 +03:00
|
|
|
|
2019-09-17 02:54:22 +03:00
|
|
|
fn expand_repeat(
|
|
|
|
ctx: &mut ExpandCtx,
|
|
|
|
template: &tt::Subtree,
|
|
|
|
kind: RepeatKind,
|
|
|
|
separator: Option<Separator>,
|
2020-03-13 13:03:31 +01:00
|
|
|
) -> ExpandResult<Fragment> {
|
2019-09-17 02:54:22 +03:00
|
|
|
let mut buf: Vec<tt::TokenTree> = Vec::new();
|
2019-12-28 22:26:24 +01:00
|
|
|
ctx.nesting.push(NestingState { idx: 0, at_end: false, hit: false });
|
2019-09-17 02:54:22 +03:00
|
|
|
// Dirty hack to make macro-expansion terminate.
|
2020-03-13 13:03:31 +01:00
|
|
|
// This should be replaced by a proper macro-by-example implementation
|
2019-12-28 22:26:24 +01:00
|
|
|
let limit = 65536;
|
2019-09-17 02:54:22 +03:00
|
|
|
let mut has_seps = 0;
|
|
|
|
let mut counter = 0;
|
|
|
|
|
2019-12-28 22:26:24 +01:00
|
|
|
loop {
|
2020-03-13 13:03:31 +01:00
|
|
|
let (mut t, e) = expand_subtree(ctx, template);
|
2019-12-28 22:26:24 +01:00
|
|
|
let nesting_state = ctx.nesting.last_mut().unwrap();
|
|
|
|
if nesting_state.at_end || !nesting_state.hit {
|
2019-09-17 02:54:22 +03:00
|
|
|
break;
|
|
|
|
}
|
2019-12-28 22:26:24 +01:00
|
|
|
nesting_state.idx += 1;
|
|
|
|
nesting_state.hit = false;
|
2019-09-17 02:54:22 +03:00
|
|
|
|
|
|
|
counter += 1;
|
2019-12-28 22:26:24 +01:00
|
|
|
if counter == limit {
|
2019-09-17 02:54:22 +03:00
|
|
|
log::warn!(
|
|
|
|
"expand_tt excced in repeat pattern exceed limit => {:#?}\n{:#?}",
|
|
|
|
template,
|
|
|
|
ctx
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-03-13 13:03:31 +01:00
|
|
|
if e.is_some() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-12-28 22:26:24 +01:00
|
|
|
t.delimiter = None;
|
2019-09-17 02:54:22 +03:00
|
|
|
push_subtree(&mut buf, t);
|
|
|
|
|
|
|
|
if let Some(ref sep) = separator {
|
|
|
|
match sep {
|
|
|
|
Separator::Ident(ident) => {
|
|
|
|
has_seps = 1;
|
|
|
|
buf.push(tt::Leaf::from(ident.clone()).into());
|
|
|
|
}
|
|
|
|
Separator::Literal(lit) => {
|
|
|
|
has_seps = 1;
|
|
|
|
buf.push(tt::Leaf::from(lit.clone()).into());
|
2019-09-17 02:06:14 +03:00
|
|
|
}
|
|
|
|
|
2019-09-17 02:54:22 +03:00
|
|
|
Separator::Puncts(puncts) => {
|
|
|
|
has_seps = puncts.len();
|
|
|
|
for punct in puncts {
|
|
|
|
buf.push(tt::Leaf::from(*punct).into());
|
|
|
|
}
|
2019-09-17 02:06:14 +03:00
|
|
|
}
|
|
|
|
}
|
2019-09-17 02:54:22 +03:00
|
|
|
}
|
2019-09-17 02:06:14 +03:00
|
|
|
|
2019-09-17 02:54:22 +03:00
|
|
|
if RepeatKind::ZeroOrOne == kind {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-09-17 02:06:14 +03:00
|
|
|
|
2019-09-17 02:54:22 +03:00
|
|
|
ctx.nesting.pop().unwrap();
|
|
|
|
for _ in 0..has_seps {
|
|
|
|
buf.pop();
|
|
|
|
}
|
2019-09-17 02:06:14 +03:00
|
|
|
|
2019-09-17 02:54:22 +03:00
|
|
|
// Check if it is a single token subtree without any delimiter
|
|
|
|
// e.g {Delimiter:None> ['>'] /Delimiter:None>}
|
2019-12-13 21:53:34 +08:00
|
|
|
let tt = tt::Subtree { delimiter: None, token_trees: buf }.into();
|
2020-03-13 13:03:31 +01:00
|
|
|
|
|
|
|
if RepeatKind::OneOrMore == kind && counter == 0 {
|
|
|
|
return (Fragment::Tokens(tt), Some(ExpandError::UnexpectedToken));
|
|
|
|
}
|
|
|
|
(Fragment::Tokens(tt), None)
|
2019-09-17 02:06:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn push_fragment(buf: &mut Vec<tt::TokenTree>, fragment: Fragment) {
|
|
|
|
match fragment {
|
|
|
|
Fragment::Tokens(tt::TokenTree::Subtree(tt)) => push_subtree(buf, tt),
|
|
|
|
Fragment::Tokens(tt) | Fragment::Ast(tt) => buf.push(tt),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn push_subtree(buf: &mut Vec<tt::TokenTree>, tt: tt::Subtree) {
|
|
|
|
match tt.delimiter {
|
2019-12-13 21:53:34 +08:00
|
|
|
None => buf.extend(tt.token_trees),
|
2019-09-17 02:06:14 +03:00
|
|
|
_ => buf.push(tt.into()),
|
|
|
|
}
|
|
|
|
}
|