A zero dependency proc-macro for practical metaprogramming.
Macro expressions using familiar Rust syntax, evaluated by a tiny interpreter.
Two specialized entrypoints for common usecases (metamatch! and #[replicate]),
two generalized versions for maximum flexibility (template! and eval!).
The original motivation and namesake of this crate.
Match arms for differently typed variants cannot be combined, even if the are
syntactically identical. This macro lets you stamp out the neccessary
copies using (#[expand]).
This macro is fully compatible rustfmt and rust-analyzer.
It will be correctly formatted like a regular
match expression, and is targettable even by auto refactorings
that affect the #[expand], like changing the name of an enum variant.
use metamatch::metamatch;
enum DynVec {
I8(Vec<i8>),
I16(Vec<i16>),
I32(Vec<i32>),
I64(Vec<i64>),
}
impl DynVec{
fn len(&self) -> usize {
metamatch!(match self {
#[expand(for X in [I8, I16, I32, I64])]
DynVec::X(v) => v.len(),
})
}
}Evaluate simple expressions.
use metamatch::eval;
const ARRAY: [i32; 4] = eval! {
let ELEMENTS = for X in 1..5 {
quote!(X,)
};
quote!([ELEMENTS])
};
assert_eq!(ARRAY, [1, 2, 3, 4]);i
variable in both contexts. Screaming case identifiers indicate to metamatch that
you want these names to be replaced inside of nested quote! blocks.
Embed dynamic chunks into a larger body of Rust source.
It uses [< ... >] styled template tags for readable templating
within large blocks of rust code with few dynamic parts.
use metamatch::template;
template! {
enum ErrorCode {
[<for err_id in 0..=42>]
[<ident("E" + str(err_id))>](String),
[</for>]
}
};
let err = ErrorCode::E42("oh noes!".to_owned());See the documentation of
template! for
a full list of template tags.
You can switch back and forth between template and eval mode from within
any macro using the [<eval>] tag and quote! pseudo-macro respectively.
Generate repetitive syntax like trait impls using an annotation style macro.
Just like metamatch!, this style preserves support for rustfmt and rust-analyzer.
use metamatch::replicate;
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct NodeIdx(usize);
#[metamatch::replicate(
let traits = [Add, Sub, Mul, Div, Rem];
let trait_fns = traits.map(lowercase);
for (TRAIT, FN) in zip(traits, trait_fns)
)]
impl std::ops::TRAIT for NodeIdx {
type Output = Self;
fn FN(self, rhs: Self) -> Self {
NodeIdx(self.0.FN(rhs.0))
}
}
assert!(NodeIdx(1) + NodeIdx(2) == NodeIdx(3));letstatements and basic pattern matching.loop,while,while let, andforloops, includingcontinueandbreakifandelseblocks- Functions (
fn) and lambdas (|..|) - Arrays (
[1,2,3]) - Ranges (
0..=10) - All basic operators:
+,-,*,/,%,<<,>>,=,+=,-=,*=,/=,%=,<<=,>>=,&=,|=,^=,&,|,^,!,<,>,<=,>=,==,!=,&&,||,[..]
struct,enum,match,type,trait, ...
Functions support UFCS, so [1,2,3].len() == len([1,2,3])
All str -> str functions also work token -> token.
lowercase(str) -> strenv(str) -> struppercase(str) -> strcapitalize(str) -> strreplace(src, pat, replacement) -> src: String replacementenumerate([T]) -> [(int, T)]zip([A], [B], ..) -> [(A, B, ..)]flatten([[T]]) -> [T]: Flatten nested iterables into a single listcombinations([A], [B], ..) -> [(A, B, ..)]: Cartesian product of iterablesmap([T], Fn(T) -> U) -> [U]chars(str) -> [char]bytes(str) -> [int]ident(str) -> tokenstr(any) -> strlen([T]) -> intlist(iterable) -> [T]: Convert a range or other iterable to a mutable listtokens([T]) -> tokens: Convert a list of values back to raw tokenseq_same_span(a, b) -> bool: Like==but also requires spans to match
quote!(..) -> [token]: Like a nestedtemplate!, this evaluates to a list of raw tokens.raw!(..) -> [token]or#(..): Likequote!, but no meta variable expansion or template tags.assert!(cond[, "message {expr}"]): Compile-time assertion - fails compilation ifcondis false. Supports format-style message with inline expressions.format!("text {expr}"): String formatting with inline expressions. Use{{and}}for literal braces. Format specifiers (:) are not supported.
Just like Rust macros, you can use any of {}, [], and ()
interchangably for these macro invocations.
Metamatch supports extern function and let declarations to share
state between macro invocations.
metamatch::eval! {
extern let MY_CONST = 21;
extern fn double_me (x) {
x * 2
};
}
let res = metamatch::eval! {
// import extern symbols from other macros
use {MY_CONST, double_me};
double_me(MY_CONST)
};
assert_eq!(res, 42);Extern symbols desugar into a declarative macro (macro_rules!).
They therefore follow the same scoping rules as declarative macros do (definitions must come lexically before uses).
You can use pub extern to have a #[macro_export]
attribute generated for an extern symbol.
MIT or Apache Version 2.0, at your option.