Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/mbe/src/expander/transcriber.rs')
-rw-r--r--crates/mbe/src/expander/transcriber.rs128
1 files changed, 113 insertions, 15 deletions
diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs
index dffb40d4bc..6161af1858 100644
--- a/crates/mbe/src/expander/transcriber.rs
+++ b/crates/mbe/src/expander/transcriber.rs
@@ -7,7 +7,7 @@ use crate::{
expander::{Binding, Bindings, Fragment},
parser::{MetaVarKind, Op, RepeatKind, Separator},
tt::{self, Delimiter},
- ExpandError, ExpandResult, MetaTemplate,
+ CountError, ExpandError, ExpandResult, MetaTemplate,
};
impl Bindings {
@@ -15,13 +15,23 @@ impl Bindings {
self.inner.contains_key(name)
}
- fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result<Fragment, ExpandError> {
+ fn get(&self, name: &str) -> Result<&Binding, ExpandError> {
+ match self.inner.get(name) {
+ Some(binding) => Ok(binding),
+ None => Err(ExpandError::binding_error(format!("could not find binding `{name}`"))),
+ }
+ }
+
+ fn get_fragment(
+ &self,
+ name: &str,
+ nesting: &mut [NestingState],
+ ) -> Result<Fragment, ExpandError> {
macro_rules! binding_err {
($($arg:tt)*) => { ExpandError::binding_error(format!($($arg)*)) };
}
- let mut b: &Binding =
- self.inner.get(name).ok_or_else(|| binding_err!("could not find binding `{name}`"))?;
+ let mut b = self.get(name)?;
for nesting_state in nesting.iter_mut() {
nesting_state.hit = true;
b = match b {
@@ -133,7 +143,7 @@ fn expand_subtree(
// 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
let start_elements = arena.len();
let mut err = None;
- for op in template.iter() {
+ 'ops: for op in template.iter() {
match op {
Op::Literal(it) => arena.push(tt::Leaf::from(it.clone()).into()),
Op::Ident(it) => arena.push(tt::Leaf::from(it.clone()).into()),
@@ -161,13 +171,12 @@ fn expand_subtree(
}
Op::Ignore { name, id } => {
// Expand the variable, but ignore the result. This registers the repetition count.
+ // FIXME: Any emitted errors are dropped.
expand_var(ctx, name, *id);
}
Op::Index { depth } => {
- let index = ctx
- .nesting
- .get(ctx.nesting.len() - 1 - (*depth as usize))
- .map_or(0, |nest| nest.idx);
+ let index =
+ ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |nest| nest.idx);
arena.push(
tt::Leaf::Literal(tt::Literal {
text: index.to_string().into(),
@@ -176,6 +185,65 @@ fn expand_subtree(
.into(),
);
}
+ Op::Count { name, depth } => {
+ let mut binding = match ctx.bindings.get(name.as_str()) {
+ Ok(b) => b,
+ Err(e) => {
+ if err.is_none() {
+ err = Some(e);
+ }
+ continue;
+ }
+ };
+ for state in ctx.nesting.iter_mut() {
+ state.hit = true;
+ match binding {
+ Binding::Fragment(_) | Binding::Missing(_) => {
+ // `count()` will report an error.
+ break;
+ }
+ Binding::Nested(bs) => {
+ if let Some(b) = bs.get(state.idx) {
+ binding = b;
+ } else {
+ state.at_end = true;
+ continue 'ops;
+ }
+ }
+ Binding::Empty => {
+ state.at_end = true;
+ // FIXME: Breaking here and proceeding to `count()` isn't the most
+ // correct thing to do here. This could be a binding of some named
+ // fragment which we don't know the depth of, so `count()` will just
+ // return 0 for this no matter what `depth` is. See test
+ // `count_interaction_with_empty_binding` for example.
+ break;
+ }
+ }
+ }
+
+ let c = match count(ctx, binding, 0, *depth) {
+ Ok(c) => c,
+ Err(e) => {
+ // XXX: It *might* make sense to emit a dummy integer value like `0` here.
+ // That would type inference a bit more robust in cases like
+ // `v[${count(t)}]` where index doesn't matter, but also coult also lead to
+ // wrong infefrence for cases like `tup.${count(t)}` where index itself
+ // does matter.
+ if err.is_none() {
+ err = Some(e.into());
+ }
+ continue;
+ }
+ };
+ arena.push(
+ tt::Leaf::Literal(tt::Literal {
+ text: c.to_string().into(),
+ span: tt::TokenId::unspecified(),
+ })
+ .into(),
+ );
+ }
}
}
// drain the elements added in this instance of expand_subtree
@@ -218,12 +286,9 @@ fn expand_var(ctx: &mut ExpandCtx<'_>, v: &SmolStr, id: tt::TokenId) -> ExpandRe
.into();
ExpandResult::ok(Fragment::Tokens(tt))
} else {
- ctx.bindings.get(v, &mut ctx.nesting).map_or_else(
+ ctx.bindings.get_fragment(v, &mut ctx.nesting).map_or_else(
|e| ExpandResult {
- value: Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree {
- delimiter: tt::Delimiter::unspecified(),
- token_trees: vec![],
- })),
+ value: Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree::empty())),
err: Some(e),
},
ExpandResult::ok,
@@ -245,6 +310,7 @@ fn expand_repeat(
let limit = 65536;
let mut has_seps = 0;
let mut counter = 0;
+ let mut err = None;
loop {
let ExpandResult { value: mut t, err: e } = expand_subtree(ctx, template, None, arena);
@@ -272,6 +338,7 @@ fn expand_repeat(
}
if e.is_some() {
+ err = err.or(e);
continue;
}
@@ -317,7 +384,7 @@ fn expand_repeat(
err: Some(ExpandError::UnexpectedToken),
};
}
- ExpandResult::ok(Fragment::Tokens(tt))
+ ExpandResult { value: Fragment::Tokens(tt), err }
}
fn push_fragment(buf: &mut Vec<tt::TokenTree>, fragment: Fragment) {
@@ -343,3 +410,34 @@ fn push_subtree(buf: &mut Vec<tt::TokenTree>, tt: tt::Subtree) {
_ => buf.push(tt.into()),
}
}
+
+/// Handles `${count(t, depth)}`. `our_depth` is the recursion depth and `count_depth` is the depth
+/// defined by the metavar expression.
+fn count(
+ ctx: &ExpandCtx<'_>,
+ binding: &Binding,
+ our_depth: usize,
+ count_depth: Option<usize>,
+) -> Result<usize, CountError> {
+ match binding {
+ Binding::Nested(bs) => match count_depth {
+ None => bs.iter().map(|b| count(ctx, b, our_depth + 1, None)).sum(),
+ Some(0) => Ok(bs.len()),
+ Some(d) => bs.iter().map(|b| count(ctx, b, our_depth + 1, Some(d - 1))).sum(),
+ },
+ Binding::Empty => Ok(0),
+ Binding::Fragment(_) | Binding::Missing(_) => {
+ if our_depth == 0 {
+ // `${count(t)}` is placed inside the innermost repetition. This includes cases
+ // where `t` is not a repeated fragment.
+ Err(CountError::Misplaced)
+ } else if count_depth.is_none() {
+ Ok(1)
+ } else {
+ // We've reached at the innermost repeated fragment, but the user wants us to go
+ // further!
+ Err(CountError::OutOfBounds)
+ }
+ }
+ }
+}