Macro expansion works on raw tokens. If you follow the macro expansion process...
#![feature(log_syntax)]
macro_rules! count {
(1) => {{
log_syntax!(MatchOne count!(1));
0
}};
(2) => {{
log_syntax!(MatchTwo count!(2));
0
}};
($n:tt) => {{
log_syntax!(MatchNumber count!($n));
1 + count!($n - 1)
}};
($($n:tt)*) => {{
log_syntax!(MatchAny count!($($n)*));
}};
}
Playground.
Outputs:
MatchNumber count! (5)
MatchAny count! (5 - 1)
The second invocation didn't get 4
, but 5 - 1
, the raw tokens.
What you want can be done with procedural macros, for example:
extern crate proc_macro;
use itertools::Itertools; // When std's intersperse() will be stable, can get rid of this.
use proc_macro::{Delimiter, Group, Literal, Punct, Spacing, TokenStream, TokenTree};
#[proc_macro]
pub fn count(number: TokenStream) -> TokenStream {
let number = number.to_string().parse().expect("expected a number"); // A simple way to parse int literal without `syn`
let result = if number == 0 {
TokenTree::Literal(Literal::u64_unsuffixed(0))
} else {
TokenTree::Group(Group::new(
Delimiter::None,
std::iter::repeat(TokenTree::Literal(Literal::u64_unsuffixed(1)))
.take(number)
.intersperse(TokenTree::Punct(Punct::new('+', Spacing::Alone)))
.collect(),
))
};
result.into()
}
However, this is probably not what you want.