翻訳者から
手続き型マクロは、Rustの最も期待される機能の1つです。 現時点では、手続き型マクロは、不安定なバージョンのコンパイラに対してのみ記述できますが、 syntexなど、安定したコンパイラ内でのコード生成を制限できるコンテナがいくつかあります。 ただし、これは状況を特に容易にするものではありません。ASTへのインターフェイスは不安定なままであり、syntexの作成者はナイトリービルドに対応しようとしますが、AST構造の変更により失敗することがあります。
このブログ投稿では、コアチームメンバーの1人であるNick Cameronが、手続き型マクロの将来に関する彼のビジョンを共有しました。 投稿はコンパイラ内部の技術的な詳細でいっぱいですが、habrasocietyはRust開発の舞台裏を少し覗き見することに興味があるように思えました。
手続きマクロフレームワーク
この投稿では、私の意見では、手続きマクロがどのように見えるかを説明します。 構文については別の投稿で既に説明しましたが、手続き型マクロのAPIを公開するときに、彼に関する投稿も作成します。 マクロシステムのいくつかの変更については既に説明しているので、ここでは何らかの方法で(以前の投稿とは反対に)繰り返しますが、詳細を明らかにします。
マクロビュー
手続き型マクロには、関数マクロと属性マクロの2種類しかありません。 最初は
#[macro]
属性でマークされた関数で、2番目は
#[macro_attribute]
マークされた関数です。 関数マクロは
foo!(tokens)
の形で使用され、属性マクロは
#[foo]
または
#[foo(tokens)]
の形で使用され、ASTノードに接続し、Rustの属性を使用する通常の規則に従います。 明らかなセマンティクスに従って、属性
#![...]
もサポートされています(次のASTノードではなく、親ノード(約Transl。)を参照します) 。
関数マクロには次のシグネチャがあります。
#[macro] pub fn foo(TokenStream, &mut MacroContext) -> TokenStream
属性マクロには次のシグネチャがあります。
#[macro_attribute] pub fn foo(Option<TokenStream>, TokenStream, &mut MacroContext) -> TokenStream
最初の引数は、マクロ属性自体からのトークンのオプションのストリームです(
#[foo(tokens)]
からの
#[foo(tokens)]
)。 2番目の引数は
TokenStream
で、マクロ属性が属するASTノードからのトークンストリームです。 返された
TokenStream
は、元のASTノードを置き換えます
TokenStream
個以上のASTノードにすることができます(つまり、
Modifier
と
Decorator
構文拡張を一度に置き換えます)。
2番目の
TokenStream
有効なASTノードに解析される一方で、最初の
TokenStream
解析される場合とそうでない場合があります。
手続き型マクロは、返された
TokenStream
マクロ呼び出しのコンテキストで解析されることを保証する必要があります。
libmacro
Libmacroは、標準言語パッケージに追加される新しいライブラリです。 主に手続き型マクロによって使用されることが想定されています。 その内容は、他のライブラリコンテナと同じ安定化ルールに従います(つまり、すべての機能が不安定として導入され、その後、有用性が証明されると安定します)。
libsyntax
ライブラリは残りますが、コンパイラー実装の詳細になります。手続き型マクロは使用しないでください。また、安定としてマークされません(つまり、安定マクロは使用しないでください)。
その考えは、
libmacro
がかなり低レベルのインターフェースを提供するということです。 より高いレベルのライブラリを備えたコンテナがエコシステムに登場することを期待しています。 特に、
libmacro
はASTの概念はありません。 より広いエコシステムのコンテナは、ASTを提供するだけでなく、AST内のトークンを解析し、ASTを構築する機能も期待されています。
Libmacroには、トークンの構造(
libsyntax
から再エクスポート
libsyntax
)とマクロに渡される
MacroContext
が含まれます。 Libmacroには次の機能が含まれます。
- トークンの文字列を解析し、
- 準引用(テキスト変数とメタ変数をトークンに変換)、
- トークンのパターンマッチング、
- ストリングインターン
- さまざまな衛生設定を持つ新しい識別子の生成、
- トークン衛生情報の操作、
- マクロの適用(名前解決を含む)、
- スパンの操作(「スパン」-ASTピースのソースコードへのマッピングを表し、それをより適切に翻訳する方法はわかりません-約翻訳) (特に、トレースを開いて新しいスパンを作成する)、およびスパンからソースコードの場所に関する情報を取得する、
- 機能フラグ(「機能ゲート」、フォーム
#[feature(name)]
属性、さまざまな、通常は不安定なコンパイラー機能-およその翻訳を含む#[feature(name)]
チェック、およびコード生成中に使用する#[feature(name)]
フラグの設定 - 属性を使用済みとしてマークする、
- エラーメッセージなど
- 属性引数に示されているように、トークンをキーと値のペアに解析します。
この機能のほとんどは、
MacroContext
メソッドとして利用できます。
このAPIについては、今後の投稿で詳しく説明します。 ここで、トークンと
MacroContext
いくつかの側面を明らかにします。
トークン
トークンの効果的で人間工学的な表現を作成すると、多くの分野に影響を与えます。 最初のスケッチは次のとおりです。
mod tokens { use {Span, HygieneObject, InternedString}; pub struct TokenStream(Vec<TokenTree>); impl TokenStream { // Methods for adding and removing tokens, etc. } pub struct TokenTree { pub kind: TokenKind, pub span: Span, pub hygiene: HygieneObject, } pub enum TokenKind { Delimited(Delimiter, Vec<TokenTree>), // String includes the commenting tokens. Comment(String, CommentKind), String(String, StringKind), Dollar, Semicolon, Eof, Word(InternedString), Punctuation(char), } pub enum Delimiter { // { } Brace, // ( ) Parenthesis, // [ ] Bracket, } pub enum CommentKind { Regular, InnerDoc, OuterDoc, } pub enum StringKind { Regular, Raw(usize), Byte, RawByte(usize), } }
HygieneInformation
は
TokenKind::Word
のみに保存でき、すべてのトークンには保存できません。 また、各トークンを個別に保存するのではなく、トークン範囲に保存することもできます。
$
と
;
を区別する必要があるかどうかはわかりません
;
:ドルはマクロ内のメタ変数を示すために使用され、セミコロンは要素を互いに分離するために使用されるため、区別するのに役立つ場合があります。 おそらく区別する必要があり
!
および
#
。マクロを呼び出すときに使用されるためです。ただし、これがどこで役立つかはわかりません。
文字列リテラルをインターンする価値があるかもしれません。 おそらく、コメントの内容は保存しないでください。それらはスパンを介して読み取ることができるためです(現在は両方を行っているなど)。
ここで非端末を補間する必要はないと思います。
また、いくつかのヘルパー関数を提供する必要があります。 ただし、注:これらのデータ構造の安定性は、時間の経過とともに保証されると予想しています。 これらの機能は署名に従ってのみ安定しますが、作業の結果に従っては安定しません。
TokenTree
または
&[TokenTree]
いずれかを受け入れます。
-
is_keyword
-
is_reserved_word
-
is_special_ident
-
is_operator
-
is_ident
-
is_path
-
metavariables
-metavariables
からメタ変数を分離します。たとえば、foo($x:ident, $y:expr)
、データ構造の一種として[("x", 2 ident), ("y", 6, expr)]
を返します。
そして、おそらくトークンツリーを構築するためのいくつかの機能。
MacroContext
MacroContext
はいくつかの役割があります。
- マクロ宣言コンテキストとそのアプリケーションコンテキストに関する情報が含まれています。
- マクロの結果の使用方法に関する情報を送信します。
-
libmacro
機能へのアクセスを提供します。これには、何らかの状態を保持する必要があります。
MacroContext
は何らかの構造になるかもしれませ
MacroContext
が、ほとんどのフィールドはプライベートになると思います。 これがタイプになる可能性があります。
コンテキスト情報
アクセス方法:
- マクロ使用スパン(注:
TokenStream
マクロ引数にも独自のスパンがあります) - マクロ自体の定義をスパンします。
- マクロの使用場所の衛生コンテキスト、およびマクロの定義の場所(これはオブジェクトを閉じることに注意してください、すべてのトークンは独自の衛生コンテキストを持ちます)
- マクロの適用場所の非拡張属性、
- マクロが生成する必要があるノードのASTビュー、
- マクロの使用場所に含まれる機能のフラグ、
- マクロが安全でないブロックで使用されているかどうかに関する情報、
- マクロ関数に使用される区切り文字。
トークンのプロパティを返す
- 生成されたコードの機能のフラグのセット、
- 生成されたコードに衛生を適用する方法の指示。
その他の機能
libmacro
に関する今後の投稿で多くを明らかにします。 最も重要な機能には、エラー、警告などが含まれます。 マクロで使用可能なトークンに基づいて、コードに関するコメントと提案を表示し、スパンに関する情報を提供する機能を含みます。
試運転
最初は、新しい手続きマクロと古い構文拡張の両方をサポートします。 両方とも不安定になります。 新しい手続きマクロの使用を推奨する非推奨の警告を発行するには、古い構文拡張を定義する必要があります。 手続き型マクロを宣言するための属性の安定化を通じて、手続き型マクロを長期的に安定させます。 次に、
libmacro
部分的に徐々に安定させます。 十分な機能が安定したら(そして、新しいシステムに内部構文拡張機能を書き直したら)、古い構文拡張機能のサポートを削除する必要があります。
代替案
IdentTT
構文拡張をサポートするようになり
IdentTT
。これは、マクロ名とティアオフ区切り文字の間の識別子を持つマクロ関数を表します。 このサポートを終了したいと思います。 ただし、一部の要素(たとえば、
my_struct! foo { ... }
)をエミュレートすると便利な場合があります。 残念ながら、このアプリケーションは修飾子(
pub my_struct! foo ...
)をサポートしていないため不十分であり、一部の作成者は、マクロ名が呼び出されたときに識別子だけでなく異なる種類のトークンを必要とします。 私の提案は、現時点ではこの機会を取り除くべきだということです。 新しい属性を追加するか(
#[macro_with_ident]
)、または
MacroContext
情報を追加することにより、後方互換性を維持しながら将来追加できます。
MacroContext
やや
MacroContext
、おそらくそれをいくつかの小さなタイプまたは構造に分割した方が良いでしょう。 ただし、これにより、マクロの作成が人間工学的でなくなる可能性があります。