スポンサーリンク

Rustの手続き的マクロを考える(3)synで構文解析(1)

手続き型マクロは、与えられたトークンストリームを編集することでコードをコンパイル時に書き換えるのだが、この作業を楽にするためにsynクレートというものがあり、synを使うと構文木の操作がかなり楽にできる(らしい)。

syn::parse_macro_input!で構文解析を行い、構文木(=AST=Abstract Syntax Tree)を作成する。

この時、トークンストリームを、ItemFnやExprなどに変換して渡す必要がある。マクロに渡された内容が関数であればItemFn式であればExprに変換する。

synの基本

Lib.rs

extern crate proc_macro;

use proc_macro::TokenStream;

use syn::{
    parse_macro_input,
    Expr,
    // Cargo.toml 内  syn = {"2.0", features = ["full"] }
    ItemFn, // "full" が必要
};

// 関数を処理する場合 as ItemFn を使用
#[proc_macro]
pub fn tokenstream_analyze_for_function(input: TokenStream) -> TokenStream {

    let inputcopy = input.clone();
    
    let myinput:ItemFn = parse_macro_input!(inputcopy as ItemFn);// トークンストリームを解析

    return input;
}

// 式を処理する場合 as Expr を使用
#[proc_macro]
pub fn tokenstream_analyze_for_formula(input: TokenStream) -> TokenStream {

    let inputcopy = input.clone();

    let myinput:Expr = parse_macro_input!(inputcopy as Expr);// トークンストリームを解析

    return input;
}

synを使用するためにはCargo.tomlに以下の記述が必要。ItemFnを使用するためには、features=["full"]を加える必要がある。

[dependencies]
syn = {version = "2.0", features = ["full"] }

main.rs

このマクロは、以下のようにして使用する。(注意 結果は何も変わらない)

extern crate mymacro;

// 関数に対してマクロを適用
mymacro::tokenstream_analyze_for_function!{

    fn myfunction(i:i32)->i32{
        i + 1
    }
    
}
fn main() {

    let value:i32;
    let seven:i32 = 7;

    // 式に対してマクロを適用
    mymacro::tokenstream_analyze_for_formula! {

        value = (seven + 5) * 2

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

quoteで文字列化して表示

上記のマクロは何も起こらないので、syn::ItemFnやsyn::Expr型の変数myinputにちゃんと値が入っているのか確認したい。そのためにquoteクレートを使用して構文木を表示してみる。

extern crate proc_macro;

use std::{string, str::FromStr};

use proc_macro::TokenStream;


use syn::{
    parse_macro_input,
    Expr,
    ItemFn // "full" が必要
};


use quote::ToTokens;


// 関数 #[proc_macro] pub fn tokenstream_analyze_for_function(input: TokenStream) -> TokenStream { // トークンストリームを解析 let inputcopy = input.clone(); let myinput:ItemFn = parse_macro_input!(inputcopy as ItemFn); println!("** 関数の構文木ができているか確認 *********************"); // quoteを使って文字列化 let astext = myinput.to_token_stream().to_string(); println!("{}", astext); input }
// 式 #[proc_macro] pub fn tokenstream_analyze_for_formula(input: TokenStream) -> TokenStream { // トークンストリームを解析 let inputcopy = input.clone(); let myinput:Expr = parse_macro_input!(inputcopy as Expr); println!("** 式の構文木ができているか確認 *********************"); // quoteを使って文字列化 let astext = myinput.to_token_stream().to_string(); println!("{}", astext); input }

これをビルドすると、ビルド中に以下が表示される。

Compiling mymain v0.1.0 (D:\mydev\mymacro-syn\mymain)
** 関数の構文木ができているか確認 *********************
fn myfunction(i : i32) -> i32 { i + 1 }
** 式の構文木ができているか確認 *********************
value = (seven + 5) * 2

コメントを残す

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

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


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