Implement ignore
and index
metavar expression
This commit is contained in:
parent
f7bb9327ad
commit
df66eb74ab
@ -1612,3 +1612,21 @@ impl Foo {
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_metavar_exprs() {
|
||||
check(
|
||||
r#"
|
||||
macro_rules! m {
|
||||
( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
|
||||
}
|
||||
const _: i32 = m!(a b c);
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! m {
|
||||
( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
|
||||
}
|
||||
const _: i32 = -0--1--2;
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -16,7 +16,9 @@ macro_rules! m {
|
||||
($($i:ident)*) => ($_);
|
||||
($($true:ident)*) => ($true);
|
||||
($($false:ident)*) => ($false);
|
||||
(double_dollar) => ($$);
|
||||
($) => (m!($););
|
||||
($($t:tt)*) => ($( ${ignore(t)} ${index()} )-*);
|
||||
}
|
||||
m!($);
|
||||
"#,
|
||||
@ -29,7 +31,9 @@ macro_rules! m {
|
||||
($($i:ident)*) => ($_);
|
||||
($($true:ident)*) => ($true);
|
||||
($($false:ident)*) => ($false);
|
||||
(double_dollar) => ($$);
|
||||
($) => (m!($););
|
||||
($($t:tt)*) => ($( ${ignore(t)} ${index()} )-*);
|
||||
}
|
||||
m!($);
|
||||
"#]],
|
||||
@ -59,6 +63,8 @@ f3!();
|
||||
|
||||
macro_rules! m1 { ($$i) => () }
|
||||
m1!();
|
||||
macro_rules! m2 { () => ( ${invalid()} ) }
|
||||
m2!();
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! i1 { invalid }
|
||||
@ -80,6 +86,8 @@ macro_rules! f3 { ($i:_) => () }
|
||||
|
||||
macro_rules! m1 { ($$i) => () }
|
||||
/* error: invalid macro definition: `$$` is not allowed on the pattern side */
|
||||
macro_rules! m2 { () => ( ${invalid()} ) }
|
||||
/* error: invalid macro definition: invalid metavariable expression */
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
@ -179,6 +179,7 @@ fn invocation_fixtures(rules: &FxHashMap<String, DeclarativeMacro>) -> Vec<(Stri
|
||||
});
|
||||
parent.token_trees.push(subtree.into());
|
||||
}
|
||||
Op::Ignore { .. } | Op::Index { .. } => {}
|
||||
};
|
||||
|
||||
// Simple linear congruential generator for determistic result
|
||||
|
@ -502,6 +502,7 @@ fn match_loop_inner<'t>(
|
||||
}
|
||||
try_push!(next_items, item);
|
||||
}
|
||||
OpDelimited::Op(Op::Ignore { .. } | Op::Index { .. }) => {}
|
||||
OpDelimited::Open => {
|
||||
if matches!(src.clone().next(), Some(tt::TokenTree::Subtree(..))) {
|
||||
item.dot.next();
|
||||
@ -747,6 +748,7 @@ fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate)
|
||||
Op::Leaf(_) => (),
|
||||
Op::Subtree { tokens, .. } => collect_vars(collector_fun, tokens),
|
||||
Op::Repeat { tokens, .. } => collect_vars(collector_fun, tokens),
|
||||
Op::Ignore { .. } | Op::Index { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -103,6 +103,23 @@ fn expand_subtree(
|
||||
err = err.or(e);
|
||||
push_fragment(arena, fragment)
|
||||
}
|
||||
Op::Ignore { name, id } => {
|
||||
// Expand the variable, but ignore the result. This registers the repetition count.
|
||||
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);
|
||||
arena.push(
|
||||
tt::Leaf::Literal(tt::Literal {
|
||||
text: index.to_string().into(),
|
||||
id: tt::TokenId::unspecified(),
|
||||
})
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// drain the elements added in this instance of expand_subtree
|
||||
|
@ -51,6 +51,8 @@ impl MetaTemplate {
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum Op {
|
||||
Var { name: SmolStr, kind: Option<SmolStr>, id: tt::TokenId },
|
||||
Ignore { name: SmolStr, id: tt::TokenId },
|
||||
Index { depth: u32 },
|
||||
Repeat { tokens: MetaTemplate, kind: RepeatKind, separator: Option<Separator> },
|
||||
Leaf(tt::Leaf),
|
||||
Subtree { tokens: MetaTemplate, delimiter: Option<tt::Delimiter> },
|
||||
@ -113,11 +115,30 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul
|
||||
Some(it) => it,
|
||||
};
|
||||
match second {
|
||||
tt::TokenTree::Subtree(subtree) => {
|
||||
let (separator, kind) = parse_repeat(src)?;
|
||||
let tokens = MetaTemplate::parse(subtree, mode)?;
|
||||
Op::Repeat { tokens, separator, kind }
|
||||
}
|
||||
tt::TokenTree::Subtree(subtree) => match subtree.delimiter_kind() {
|
||||
Some(tt::DelimiterKind::Parenthesis) => {
|
||||
let (separator, kind) = parse_repeat(src)?;
|
||||
let tokens = MetaTemplate::parse(subtree, mode)?;
|
||||
Op::Repeat { tokens, separator, kind }
|
||||
}
|
||||
Some(tt::DelimiterKind::Brace) => match mode {
|
||||
Mode::Template => {
|
||||
parse_metavar_expr(&mut TtIter::new(subtree)).map_err(|()| {
|
||||
ParseError::unexpected("invalid metavariable expression")
|
||||
})?
|
||||
}
|
||||
Mode::Pattern => {
|
||||
return Err(ParseError::unexpected(
|
||||
"`${}` metavariable expressions are not allowed in matchers",
|
||||
))
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return Err(ParseError::expected(
|
||||
"expected `$()` repetition or `${}` expression",
|
||||
))
|
||||
}
|
||||
},
|
||||
tt::TokenTree::Leaf(leaf) => match leaf {
|
||||
tt::Leaf::Ident(ident) if ident.text == "crate" => {
|
||||
// We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path.
|
||||
@ -209,3 +230,32 @@ fn parse_repeat(src: &mut TtIter) -> Result<(Option<Separator>, RepeatKind), Par
|
||||
}
|
||||
Err(ParseError::InvalidRepeat)
|
||||
}
|
||||
|
||||
fn parse_metavar_expr(src: &mut TtIter) -> Result<Op, ()> {
|
||||
let func = src.expect_ident()?;
|
||||
let args = src.expect_subtree()?;
|
||||
|
||||
if args.delimiter_kind() != Some(tt::DelimiterKind::Parenthesis) {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let mut args = TtIter::new(args);
|
||||
|
||||
let op = match &*func.text {
|
||||
"ignore" => {
|
||||
let ident = args.expect_ident()?;
|
||||
Op::Ignore { name: ident.text.clone(), id: ident.id }
|
||||
}
|
||||
"index" => {
|
||||
let depth = if args.len() == 0 { 0 } else { args.expect_u32_literal()? };
|
||||
Op::Index { depth }
|
||||
}
|
||||
_ => return Err(()),
|
||||
};
|
||||
|
||||
if args.next().is_some() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(op)
|
||||
}
|
||||
|
@ -73,6 +73,13 @@ impl<'a> TtIter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn expect_u32_literal(&mut self) -> Result<u32, ()> {
|
||||
match self.expect_literal()? {
|
||||
tt::Leaf::Literal(lit) => lit.text.parse().map_err(drop),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn expect_punct(&mut self) -> Result<&'a tt::Punct, ()> {
|
||||
match self.expect_leaf()? {
|
||||
tt::Leaf::Punct(it) => Ok(it),
|
||||
|
Loading…
x
Reference in New Issue
Block a user