スポンサーリンク

Rustの手続き的マクロを考える(2)トークンの種類を特定し、書き換えてみる

前回、トークンストリームを見てみた。もう少し詳しく見てみる。

Rustの手続き的マクロを考える(1)トークンストリームを確認してみる

トークンの種類を特定してみる

main関数側

以下のようなマクロを使用する側。この実行結果は「24」になる。

extern crate mymacro;

fn main() {

    let value:i32;
    let seven:i32 = 7;
    
    mymacro::print_tokens! {

        value = (seven + 5) * 2;

    }

print!("結果 {}",value); }

print_tokensマクロの実装

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
}
Compiling mymain v0.1.0 (D:\dev\Rust\mymacro\mymain)
受け取ったトークン: value = (seven + 5) * 2 ;
これは識別子  value
これは記号  =
これはグループ (seven + 5)
これは記号  *
これはリテラル 2
これは記号  ;
Finished dev [unoptimized + debuginfo] target(s) in 0.85s

トークンを書き換える

トークンの扱いがわかったので、トークンを書き換えてみる。以下のマクロは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);

}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: