rust - Is there a way to count with macros? -
i want create macro prints "hello" specified number of times. it's used like:
many_greetings!(3); // expands 3 `println!("hello");` statements
the naive way create macro is:
macro_rules! many_greetings { ($times:expr) => {{ println!("hello"); many_greetings!($times - 1); }}; (0) => (); }
however, doesn't work because compiler not evaluate expressions; $times - 1
isn't calculated, fed new expression macro.
while ordinary macro system not enable repeat macro expansion many times, there no problem using loop in macro:
macro_rules! many_greetings { ($times:expr) => {{ _ in 0..$times { println!("hello"); } }}; }
if need repeat macro, have procedural macros/compiler plugins (which of 1.4 unstable, , bit harder write).
edit: there better ways of implementing this, i've spent long enough on today, here goes. repeat!
, macro duplicates block of code number of times:
main.rs
#![feature(plugin)] #![plugin(repeat)] fn main() { let mut n = 0; repeat!{ 4 { println!("hello {}", n); n += 1; }}; }
lib.rs
#![feature(plugin_registrar, rustc_private)] extern crate syntax; extern crate rustc; use syntax::codemap::span; use syntax::ast::tokentree; use syntax::ext::base::{extctxt, macresult, maceager, dummyresult}; use rustc::plugin::registry; use syntax::util::small_vector::smallvector; use syntax::ast::lit_; use std::error::error; fn expand_repeat(cx: &mut extctxt, sp: span, tts: &[tokentree]) -> box<macresult + 'static> { let mut parser = cx.new_parser_from_tts(tts); let times = match parser.parse_lit() { ok(lit) => match lit.node { lit_::litint(n, _) => n, _ => { cx.span_err(lit.span, "expected literal integer"); return dummyresult::any(sp); } }, err(e) => { cx.span_err(sp, e.description()); return dummyresult::any(sp); } }; let res = parser.parse_block(); match res { ok(block) => { let mut stmts = smallvector::many(block.stmts.clone()); _ in 1..times { let rep_stmts = smallvector::many(block.stmts.clone()); stmts.push_all(rep_stmts); } maceager::stmts(stmts) } err(e) => { cx.span_err(sp, e.description()); dummyresult::any(sp) } } } #[plugin_registrar] pub fn plugin_registrar(reg: &mut registry) { reg.register_macro("repeat", expand_repeat); }
added cargo.toml
[lib] name = "repeat" plugin = true
note if don't want looping, expanding @ compile-time, have things requiring literal numbers. after all, not able evaluate variables , function calls reference other parts of program @ compile time.
Comments
Post a Comment