Rustの手続き型マクロフレームワーク

翻訳者から


手続き型マクロは、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ノードにすることができます(つまり、 ModifierDecorator構文拡張を一度に置き換えます)。
2番目のTokenStream有効なASTノードに解析される一方で、最初のTokenStream解析される場合とそうでない場合があります。
手続き型マクロは、返されたTokenStreamマクロ呼び出しのコンテキストで解析されることを保証する必要があります。

libmacro


Libmacroは、標準言語パッケージに追加される新しいライブラリです。 主に手続き型マクロによって使用されることが想定されています。 その内容は、他のライブラリコンテナと同じ安定化ルールに従います(つまり、すべての機能が不安定として導入され、その後、有用性が証明されると安定します)。 libsyntaxライブラリは残りますが、コンパイラー実装の詳細になります。手続き型マクロは使用しないでください。また、安定としてマークされません(つまり、安定マクロは使用しないでください)。
その考えは、 libmacroがかなり低レベルのインターフェースを提供するということです。 より高いレベルのライブラリを備えたコンテナがエコシステムに登場することを期待しています。 特に、 libmacroはASTの概念はありません。 より広いエコシステムのコンテナは、ASTを提供するだけでなく、AST内のトークンを解析し、ASTを構築する機能も期待されています。
Libmacroには、トークンの構造( libsyntaxから再エクスポートlibsyntax )とマクロに渡されるMacroContextが含まれます。 Libmacroには次の機能が含まれます。

この機能のほとんどは、 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), } } 
HygieneInformationTokenKind::Wordのみに保存でき、すべてのトークンには保存できません。 また、各トークンを個別に保存するのではなく、トークン範囲に保存することもできます。
$;を区別する必要があるかどうかはわかりません; :ドルはマクロ内のメタ変数を示すために使用され、セミコロンは要素を互いに分離するために使用されるため、区別するのに役立つ場合があります。 おそらく区別する必要があり! および# 。マクロを呼び出すときに使用されるためです。ただし、これがどこで役立つかはわかりません。
文字列リテラルをインターンする価値があるかもしれません。 おそらく、コメントの内容は保存しないでください。それらはスパンを介して読み取ることができるためです(現在は両方を行っているなど)。
ここで非端末を補間する必要はないと思います。
また、いくつかのヘルパー関数を提供する必要があります。 ただし、注:これらのデータ構造の安定性は、時間の経過とともに保証されると予想しています。 これらの機能は署名に従ってのみ安定しますが、作業の結果に従っては安定しません。 TokenTreeまたは&[TokenTree]いずれかを受け入れます。

そして、おそらくトークンツリーを構築するためのいくつかの機能。

MacroContext


MacroContextはいくつかの役割があります。

MacroContextは何らかの構造になるかもしれませMacroContextが、ほとんどのフィールドはプライベートになると思います。 これがタイプになる可能性があります。

コンテキスト情報

アクセス方法:


トークンのプロパティを返す



その他の機能

libmacroに関する今後の投稿で多くを明らかにします。 最も重要な機能には、エラー、警告などが含まれます。 マクロで使用可能なトークンに基づいて、コードに関するコメントと提案を表示し、スパンに関する情報を提供する機能を含みます。

試運転


最初は、新しい手続きマクロと古い構文拡張の両方をサポートします。 両方とも不安定になります。 新しい手続きマクロの使用を推奨する非推奨の警告を発行するには、古い構文拡張を定義する必要があります。 手続き型マクロを宣言するための属性の安定化を通じて、手続き型マクロを長期的に安定させます。 次に、 libmacro部分的に徐々に安定させます。 十分な機能が安定したら(そして、新しいシステムに内部構文拡張機能を書き直したら)、古い構文拡張機能のサポートを削除する必要があります。

代替案


IdentTT構文拡張をサポートするようになりIdentTT 。これは、マクロ名とティアオフ区切り文字の間の識別子を持つマクロ関数を表します。 このサポートを終了したいと思います。 ただし、一部の要素(たとえば、 my_struct! foo { ... } )をエミュレートすると便利な場合があります。 残念ながら、このアプリケーションは修飾子( pub my_struct! foo ... )をサポートしていないため不十分であり、一部の作成者は、マクロ名が呼び出されたときに識別子だけでなく異なる種類のトークンを必要とします。 私の提案は、現時点ではこの機会を取り除くべきだということです。 新しい属性を追加するか( #[macro_with_ident] )、またはMacroContext情報を追加することにより、後方互換性を維持しながら将来追加できます。
MacroContextややMacroContext 、おそらくそれをいくつかの小さなタイプまたは構造に分割した方が良いでしょう。 ただし、これにより、マクロの作成が人間工学的でなくなる可能性があります。

Source: https://habr.com/ru/post/J274225/


All Articles