Rustコンパイラの処理の流れ(2)

Rustコンパイラの処理の流れ(1) の続きとして、マクロの展開について書いた。マクロの使用法については https://doc.rust-lang.org/book/second-edition/appendix-04-macros.html を読むとよい。

Rustコンパイラは2つのパーサを持っている。1つは通常のパーサで、もう1つはマクロ用のパーサである。通常のパーサがパースした後で、マクロ用のパーサがマクロを展開し、その後で名前解決するという流れになっている。また、マクロ用のパーサはマクロ定義とマクロ呼び出しの両方をパースしている。

マクロ用のパーサは src/libsyntax/ext/tt/macro_parser.rs で定義されており、Earley parser とよく似たアルゴリズムが使用されている。

input

マクロ用のパーサのパース部分のインターフェースは以下のようになっている。

  • sess はパースに必要ないくつかのメタデータを保持している。発生したエラー情報をユーザに返すのが主な用途である。
  • tts はパース対象の入力である。マクロ用のパーサはトークンのストリーム(※1)から msとマッチしたメタ変数(※2)の束縛を得る。
  • ms は Tokenと区切り文字から構成されており、tss とマッチさせるためのパターンとして使用されている。
  • directory はファイルの位置に関する情報である。
  • recurse_into_modules は再帰呼び出しするかどうかを示す。

output

パース結果の NamedParseResultSuccess , Failure, Error の3パターンの結果を返す。

  • Sucess: tssms とマッチしたことを示す。つまり、トークンと一致したメタ変数の束縛を生成できたということである。
  • Failure: tssms とマッチしないことを示す。 "No rule expected token blah"のようなエラーが発生する。
  • Error: パーサ内でなんらかの致命的なエラーが発生したことを示す。マクロの定義が曖昧で、マッチしたものが複数ある場合などに発生する。

マクロ用のパーサは正規表現のパーサとほとんど同じであるが、メタ変数がどのフラグメント指定子(※3)にマッチしているかが考慮されている。

処理の具体的な流れは src/libsyntax/ext/tt/macro_parser.rs のコメントに書かれている。具体例は以下のとおりである。

注釈

※1 TokenStreamはTokenTreeのシーケンスであり、TokenTreeはTokenと区切り文字で構成されている。

※2 メタ変数とは下のようなマクロの定義があった場合、 $x のことを指す

※3 identblockexprpat など

参考

Software engineer

Software engineer