前回、トークンストリームを見てみた。もう少し詳しく見てみる。
Rustの手続き的マクロを考える(1)トークンストリームを確認してみる
以下のようなマクロを使用する側。この実行結果は「24」になる。
extern crate mymacro; fn main() { let value:i32; let seven:i32 = 7;
mymacro::print_tokens! { value = (seven + 5) * 2; }
print!("結果 {}",value); }
extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro] pub fn print_tokens(input: TokenStream) -> TokenStream { // TokenStreamをStringに変換 let input_str = input.to_string(); println!("受け取ったトークン: {}", input_str);// ビルド時画面に表示 // トークンの種類を特定 for token in input.clone().into_iter() { if let proc_macro::TokenTree::Literal( theItem ) = token{ println!("これはリテラル {}", theItem); } else if let proc_macro::TokenTree::Punct( theItem ) = token{ println!("これは記号 {}",theItem); } else if let proc_macro::TokenTree::Ident( theItem ) = token{ println!("これは識別子 {}",theItem); } else if let proc_macro::TokenTree::Group( theItem ) = token{ println!("これはグループ {}", theItem); } else{ println!("不明 {:?}", token); } }; // 入力をそのまま返す input }
トークンの扱いがわかったので、トークンを書き換えてみる。以下のマクロはprint_tokensに渡された内容が「2」であった場合は値を二倍(すなわち4)にする。
マクロに渡されたTokenStreamのinputを直接書き換えることはできないので、TokenTreeのVecを用意し、そこにトークンをpushしていく。その過程で編集したい内容を見つけたら新たなトークンを作成して既存のトークンと置き換える。
最後に、TokenTreeのVecをTokenStreamに変換して返す。
このマクロを使用して最初のプログラムを実行した結果は「48」になる。
extern crate proc_macro; use std::{string, str::FromStr}; use proc_macro::TokenStream; #[proc_macro] pub fn print_tokens(input: TokenStream) -> TokenStream { // TokenStreamをStringに変換 let input_str = input.to_string(); println!("受け取ったトークン: {}", input_str);// ビルド時画面に表示 // inputを書き換えることができないので、出力用のTokenStreamを作成 let mut output = Vec::new(); // トークン毎に処理 for token in input.into_iter() { // 所有権が移動しないように ref theItemとする if let proc_macro::TokenTree::Literal( ref theItem ) = token{ println!("リテラルを発見 {}", theItem); let new_token; let ival = theItem.to_string().parse::<i32>().unwrap();// リテラルの値を取得 if ival == 2{ println!("リテラルの値が2の時だけtoken書き換え"); // 新しいトークンを作成 new_token = proc_macro::TokenTree::from( proc_macro::Literal::i32_unsuffixed( ival * 2 /*二倍にする*/) ); // 新しいトークンを出力用の配列に追加 output.push(new_token); } else{ // 2以外の時はそのまま出力用の配列に追加 output.push(token.clone()); } } else{ // リテラル以外はそのまま出力用の配列に追加 output.push(token.clone()); } }; // 新しく作成したトークンの配列をTokenStreamに変換して返却 return TokenStream::from_iter(output); }