「@Golo: では、あらゆる種類のコンテキストで、あらゆる種類の言語構造の間でどのように空白が発生するかを指定する機能が必要ですか?関数のレベル)?
ゴロ:その通りです:-)"
次に必要なのは、コードの各ポイントでの言語の構造へのアクセスと、各言語要素の正確な位置情報 (開始/終了行/列) です。lintingの場合、それらの組み合わせに対してテストを作成する方法が必要です。repairの場合、制約を満たすテキストを再生成する方法が必要です。明らかに、すべてを簡単に構成できるようにする必要があります。
必要な「構造」は、構文ツリーのパーサーによって生成されるものです。コンテキストは、関心のある構造の周りの構文構造です。抽象構文ツリーは必要ありません。これにより、検査/制御する位置の具体的なトークンが失われるため、完全な具体的な解析ツリーが必要になります。
パーサーは正確なソース位置には関心がありませんが、レクサー (入力ストリームを言語トークンに分割してパーサーにフィードする必要があります) は、この正確な情報を収集する立場にあります。「列の調整を構成するものとその程度」について、いくつかの複雑な問題について心配しています。いくつかの例: タブ文字: 次の 8 文字境界へのタブ? 4キャラ?事前に指定されたタブ列に? Linux では、「LF」は行番号を進め、列数を 1 にリセットします。Windows では、ペアとして「CR/LF」です。私が遭遇した他のOSシステムでは、「CR」のみです。最新のシステムでは、Unicode の改行文字でこれを行う必要があります。では、Linux の場合、CR をどのように扱うべきでしょうか? テキストにヌル文字が見つかった場合はどうですか? ^Z? その他の制御文字 (例: ^L [フォームフィード])?
キャプチャされたソース位置を含む CST に正確に解析されたソース ファイルが与えられた場合、構造が希望どおりに配置されていることを確認する必要があります。まず、構造を指定する必要があります。ループしますか?コンストラクタ?データ宣言?次に、正確な制御を行うために、列の位置に関する述語が必要です。
構文ツリーを提供するほぼすべてのツールが、そのような構造を参照する簡単な方法を提供しているとは限りません。ほとんどの場合、構文ツリーの形状を認識し、関心のあるツリー ノードを探してそれを上っていき、他の関連するツリー ノードが存在するかどうかを確認する、古典的なコンパイラのような手続き型コードを書くことに行き詰まっています。このモードに入ると、必要なツリーを認識し、手順コードをさらに記述して間隔規則を確認できます。
プログラム変換システム (PTS) は、多くの場合、「ソースからソースへ」の書き換えを提供します。これにより、言語の表面構文を使用してパターンを直接記述できます。これは、手続き的に木の周りを登るよりもはるかに便利です。ソースからソースへのパターンのペアのみを行うものもあります。単一のパターンのみを指定する機能を提供するものもあります。PT システムは、関心のある言語を解析し、特定のタスク用のカスタム チェックを追加できるようにする必要もあります。
たとえば、当社の DMS Software Reengineering Toolkit は ECMAScript を解析し、カスタムの条件とアクションを添付する機能とともに、そのようなソース パターンの仕様を提供します。例として:
domain ECMAScript;
pattern ideal_if_statement_layout(e:expression,s:statement):statement =
" if (\e)
\s" if diagnose_not_equal(column(s),parentheses_column(e));
「if then」ステートメント (「if then else」には別のパターンを使用します) への関心と、ステートメント要素の位置をチェックするカスタム列比較関数に対する制約を表します。「diagnose_not_equal」カスタム関数は、lint-complaints を生成します。引用符はメタ引用符です。これらは、基礎となる言語ではなく、パターン マッチング言語の一部です。 eとsはメタ変数であり、それぞれ任意の言語構造の式とステートメントに一致します。これらは CST に適用されているため、意図したターゲットを一致させることはできません。カスタム関数 "column" は、 s の左端のサブツリーに関連付けられた開始列情報を取得するだけです。; DMS のツリー管理 API を使用すると、これを簡単に取得できます。パターンがeの場所を示しているため、「括弧列」が必要です。「(」はeの上のツリー ノードにあるため、「(」を見つけて右端の列を抽出するには、ツリーを少し移動する必要があります。これも DMS ツリー API で簡単に実行できます。
任意の複雑なパターンを作成できます。あるパターンで条件を作成し、別のパターンの一致に依存することもできます。そのため、カスタム列抽出関数を適度に使用して、さまざまな lint チェックを作成できます。
これで得られないのは、「if」キーワードが「(」キーワードの左側にある 1 つのスペースであることのチェックです。「statement_keyword_column」などのカスタム チェックを追加して、ある程度表現することもできます。しかし、これは厄介になり始めています。
パターンのレイアウトに気付くかもしれません。それも制約として使用するといいでしょう。DMS は、これを行うための直接的な方法を提供していません。ただし、独自のパターン記述を tree として完全に読み取ることができます。それを使用して、パターンの見かけのレイアウトを抽出し、それを使用して構造レイアウトを確認できます。これには、DMS の使用にある程度の洗練が必要ですが、理論やメカニズムの欠落ではなく、汗の問題です。
個人的には、レイアウトの lint はあまり好きではありません。ファイルの形状を変更するだけでよいと思います。DMS には、レイアウトが何であれ、CST をその prettyprinting ルールによって制御されるレイアウトに変換する prettyprinting ルールがあります。現時点では、これらのルールはツリー ノードに固有であり、文法でエンコードされているため、ある程度制限されています。(文法で)書くことができます:
stmt = 'if' expression stmt ';'
<<PrettyPrinter>>: { V(H('if,expression),I(stmt[1])) }
これにより、すべての if-then ステートメントが次のように再生成されます。
if expresssion
stmt
[V は 2 つのサブボックスの「垂直ボックス」を意味します。H は「横のボックス」を意味し、I は「インデントされたボックス」を意味します]
このような prettyprinting ルールを慎重に使用すると、コードの再フォーマットをうまく行うことができます。この方法では複数のステートメントのレイアウトを制御できないため、完全ではありません。しかし、これは DMS の一部であり、実際にはかなり簡単に変更できます。
理想的な解決策は、パターン言語を使用し、パターン内のレイアウトを使用してプリティプリンティングを制御することです。これは私たちの計画ではありますが、残念ながら DMS にはまだありません。
他のPTSは上記のようにパターンをある程度表現できると思いますし、ほとんどのPTSはDMSのようにprettyprintingを指定する方法を持っています。良いニュースは、これらのツールがあなたが望むことの多くを行うということです。あまり良いニュースとは言えませんが、ツールの 1 つを手に取り、その使い方を学ぶにはかなりの労力が必要です。ロングショットで、午後はそれをカットしません。