Skip to content

Instantly share code, notes, and snippets.

@ubnt-intrepid
Created October 20, 2017 12:30
Show Gist options
  • Select an option

  • Save ubnt-intrepid/dcf039f74f64d1a483c3f5dab6aae01f to your computer and use it in GitHub Desktop.

Select an option

Save ubnt-intrepid/dcf039f74f64d1a483c3f5dab6aae01f to your computer and use it in GitHub Desktop.

Syntax Extension について

手続き的マクロやらcustom Derive周りを制御している周りの調査。 ほとんどソースファイルのコメントに書いていることなので新規性はない。

Procedural macro

Rust は macro_rules! で定義する宣言的 (declarative) なマクロの他に、手続き的 (procedural) なマクロを使用することが出来る。これは、通常のRustコードを用いてマクロ定義を行うものであり、次の利点を持っている。

  • 単純なパターンマッチでは記述の不可能なマクロを定義できる
  • 具象構文を介することなく、抽象構文を直接構築することが出来る

procedural macro では、次に挙げる3つの形式のマクロを定義することができる。

  • Function (Bung) 形式
    foo!(...)
  • Attribute 形式
    #[hoge(...)]
    mod foo;
    • 元の要素を書き換えたり無視することが可能
      • #[cfg(...)] とか #[test] は多分これ
  • Derive 形式
    #[derive(...)]
    struct Hoge {
        #[foo(bar)]
        item: u32,
    }
    • 元の要素を置き換えない(新たにコードを生成するだけ)

残念ながら本記事の執筆時の最新安定版である 1.21 ではすべての機能が安定化されておらず、custom Derive のみが stable チャンネルで使用可能になっている。procedural macro を定義するためには次の2つの方法を取る。

  • compiler plugin を用いる
    • すべての機能が使える
    • nightly 限定
  • proc_macro を有効にしたクレートを用いる
    • stable/nightly双方で使用可能
    • 一部の機能が unstable になっており、外部クレートで代用する必要がある

SyntaxExtension

src/syntax/ext/base.rs で定義されている列挙体であり、以下のヴァリアントを持つ。 各項目には、ExtCtxtSpan を除く関数のシグネチャを併記した。

(以下のヴァリアントは SyntaxExtension::is_modern()false を返す)

  • NormalTT { Box<TTMacroExpander>, Option<(NodeId, Span)>, bool, bool }
    • function 形式の procedural macro
    • TokenStream -> Box<MacResult>
  • MultiModifier(Box<MultiItemModifier>)
    • attribute 形式の procedural macro
    • (&MetaItem, Annotatable) -> Vec<Annotatable>
  • BuiltinDerive(BuiltinDeriveFn)
    • derive 形式の syntax extension
    • (&MetaItem, &Annotatable, &mut FnMut(Annotatable)) -> ()

(以下のヴァリアントは SyntaxExtension::is_modern()true を返す)

  • ProcMacro(Box<ProcMacro>)
    • function 形式の procedural macro
    • TokenStream -> TokenStream
  • AttrProcMacro(Box<AttrProcMacro>)
    • attribute 形式の procedural macro
    • (TokenStream, TokenStream) -> TokenStream
  • ProcMacroDerive(Box<MultiItemModifier>, Vec<Symbol>)
    • derive 形式の procedural macro
    • (MetaItem, Annotable) -> Vec<Annotable>

(その他)

  • DeclMacro(Box<TTMacroExpander>, Option<(NodeId, Span)>)
    • declarative macro
      • おそらく RFC 1584 で提案されている macros 2.0 のこと
    • TokenStream -> Box<MacResult>
  • IdentTT(Box<IdentMacroExpander>, Span, bool)
    • おそらく macro_rules! のこと
      • macro_rules! 自体の注入箇所が見当たらない…
    • (Ident, Vec<TokenTree>) -> Box<MacResult>

(謎)

  • MultiDecorator(Box<MultiItemDecorator>)
    • attribute 形式の syntax extension
      • #[derive(...)] がこれを用いているそう
    • (&MetaItem, &Annotatable, &mut FnMut(Annotatable)) -> ()
      • BuiltinDerive との違いは…?
        • こちらは関数ポインタ以外も受け取れる

備考

  • NormalTTsyntax_ext で組み込みマクロを登録するために用いられている。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment