エージェントがコードを書いている最中のファイルは、たいてい一時的に壊れています。閉じ括弧がまだない、関数の途中で止まっている。その状態で「このファイルに何があるか」を読めるかどうかで、道具の使い勝手が決まります。
構文解析器に求められるものが、人が使うときとは少し違ってきます。
壊し方を決めて、比べる
Aid-Onが開発しているgramideでは、実際のコーパス(既存プロジェクトのソース一式)を四通りに壊して、広く使われている既存の構文解析器と並べて測っています。見ているのは二つです。宣言がどれだけ残ったか。そして、壊れた箇所以外を失わず、存在しないものを作り出しもしなかった率です。
Goの8,010ファイル・30,927件の破壊で、残った宣言は99.8%(既存のものは99.3%)、後者の率は91.1%(同82.9%)でした。Pythonでは96.3%と82.3%です。残存率の差は小さく、差がつくのは後者でした。
壊れた箇所の周りで、失うか、無いものを作るか。エージェントに渡す場合は後者のほうが厄介です。存在しない関数を読んだモデルは、それを前提に書き始めてしまいます。
一文字打つたびに読み直さない
TypeScriptのコンパイラ本体(3.1MB、うち1つの関数が2.9MB)で、識別子の中に1文字打ったときの再解析は中央値54マイクロ秒でした。既存のものは、差分解析が561マイクロ秒、全体の解析が58ミリ秒でした。
代償はメモリで、編集中のファイルは既存のものの約2倍(127MB対62MB)を使います。一度読むだけのファイルであれば4%以内に収まります。編集中だけ重い、という形です。
木を作らない検査
構文が通るかどうかだけ知りたい場面では、構文木を作る必要がありません。gramideの check は同じエンジンを木を組まないモードで走らせ、終了コードだけで答えます。エージェントが書き込む前の関門として使うなら、これで足ります。
symbols は標準出力にJSONを一つ書き、回復が必要なほど壊れていればJSONを出さずに異常終了します。中途半端な結果を黙って返さない作りです。
道具を選ぶときの確認点
コード解析の仕組みを比較検討される場合は、正常なファイルでの速度だけでなく、壊れたファイルでどう振る舞うかを確かめることをお勧めします。エラーで止まるのか、読める範囲を返すのか、それとも存在しない構造を返すのか。エージェントに読ませる用途では、ここが結果を大きく左右します。