新しい言語のサポート
このセクションは、開発者が big-code-analysis に新しい言語のサポートを実装するのを支援するためのものです。
新しい言語を実装するには、2 つのステップが必要です。
- 文法を生成する
- 文法を
big-code-analysisに追加する
多数のメトリクスがサポートされており、それらを実装するための手引きはドキュメントの別の場所で扱われています。
文法の生成
新しい文法を追加するための前提条件として、目的の言語について、本プロジェクトで使用しているバージョンと一致する tree-sitter のバージョンが存在している必要があります。
文法は、このリポジトリ内の enums というプロジェクトによって生成されます。以下のステップでは、言語クレートから言語サポートを追加し、メトリクスを評価するために本プロジェクトで文法として使用される enum ファイルを生成します。
- 言語固有の
tree-sitterクレートをenumsクレートに追加します。このとき、依存関係がルートのbig-code-analysisのCargo.tomlで使用されているのと同じバージョンに=X.Y.Zでピン留めされていることを確認してください。たとえば Rust サポートの場合、/enums/Cargo.toml には次の行があります:tree-sitter-rust = "=0.24.2"。 - /enums/src/languages.rs で
enumクレートに言語を追記します。引き続き Rust を例にすると、その行は(Rust, tree_sitter_rust)になります。第 1 パラメーターは生成される Rust の enum の名前で、第 2 パラメーターはその言語の文法を取得するために呼び出すtree-sitterの関数です。 - /enums/src/macros.rs の
mk_get_languageマクロルール内にある match の末尾にケースを追加します。現在の慣例では、最近の文法クレートが公開するLANGUAGE定数を使用します。Rust の場合、その行はLang::Rust => tree_sitter_rust::LANGUAGE.into()です。 - 最後に、/recreate-grammars.sh スクリプトを実行します。これは
enumsクレートを実行して、新しい言語の文法を生成します。
この時点で、/src/languages/ に新しい言語の文法ファイルができているはずです。生成された enum の例としては /src/languages/language_rust.rs を参照してください。
新しい文法を big-code-analysis に追加する
- 言語固有の
tree-sitterクレートを、enumsクレートと同じ=X.Y.Zのピン留めでbig-code-analysisワークスペースに追加します。たとえば Rust サポートの場合、ルートの Cargo.toml の行はtree-sitter-rust = "=0.24.2"です。 - 次に、新しい
tree-sitter言語の名前空間を /src/languages/mod.rs に追加します。例:
#![allow(unused)] fn main() { pub mod language_rust; pub use language_rust::*; }
- 最後に、/src/langs.rs の
mk_langs!マクロの引数に言語の定義を追加します。
#![allow(unused)] fn main() { // 1) このバリアントの文法を有効にする Cargo フィーチャ名 // 2) enum の名前 // 3) 言語の説明 // 4) 表示名 // 5) 実装対象となる空の構造体名 // 6) パーサー名 // 7) Language を取得するために呼び出す tree-sitter 関数 // 8) ファイル拡張子 // 9) emacs モード // 10) ピン留めされた文法クレートのバージョン(ワークスペースの Cargo.toml にある // `=X.Y.Z` ピンを反映。両者の一致はドリフトテストで検証されます) ( "rust", Rust, "The `Rust` language", "rust", RustCode, RustParser, tree_sitter_rust, [rs], ["rust"], "0.24.2" ) }
トレイトとテストの実装
文法の配線は最初のステップにすぎません。新しい <Lang>Code 型は、AST の基盤処理と、ワークスペースが定義するすべてのメトリクストレイトも実装しなければなりません。
- /src/checker.rs の
Checker— 文法のkind_idに対する、コメント、関数、クロージャ、呼び出し、文字列リテラル、else-ifの各述語。 - /src/getter.rs の
Getter—get_space_kindに加え、Halstead の演算子/オペランド分類テーブル。 - /src/alterator.rs の
Alterator— 通常は文字列リテラルの保全のみで、ほとんどの言語ではデフォルト実装で十分です。 - 13 個すべてのメトリクストレイト:
Abc、Cognitive、Cyclomatic、Exit、Halstead、Loc、Mi、NArgs、Nom、Npa、Npm、Tokens、Wmc。まず /src/metrics/ のimplement_metric_trait!マクロ呼び出しで各トレイトをデフォルト(no-op)の本体として登録し、その後、その言語にとって意味のあるセマンティクスを持つメトリクスについては実際の実装に置き換えます。
エイリアス化された文法バリアントの監査
tree-sitter の文法は、同じ node.kind() 文字列に対応する複数の異なる kind_id を発行することがよくあります(Go の Identifier / Identifier2 / Identifier3、C# の InvocationExpression / InvocationExpression2、Elixir の QuotedContent ⋯ QuotedContent20)。エイリアス化されうるルールに触れるすべての match node.kind_id() アームは、番号付きバリアントをすべて列挙するか、代わりに文字列 node.kind() で比較しなければなりません。エイリアスを見落とすと、ノードがメトリクスから黙って脱落します。機械的な監査手順については add-lang スキルを、失敗モードについては docs/development/lessons_learned.md のレッスン 2、4、13 を参照してください。
テスト
各 src/metrics/*.rs のテストモジュールに言語ごとのテストを追加します。各メトリクスファイルにわたって、既存の Rust のカバレッジと同等になることを目指してください。すべての insta::assert_json_snapshot! 呼び出しは必ずアンカーされていなければなりません。すなわち、インラインの期待値ブロック、その直上に置いた主要な整数アクセサに対する正の assert_eq!、または説明のための // expected: コメントのいずれかを伴う必要があります。make snapshot-anchors(make pre-commit の一部として実行)が .snapshot-anchor-baseline.txt に照らしてこれを強制します。
エンドツーエンドのワークフロー
エイリアス監査、テストレイアウト、スナップショットのアンカー付け、コード品質の後処理パスを含む、明確な方針を持つエンドツーエンドのレシピについては、プロジェクトの add-lang Claude Code スキルを参照してください。これは最近の言語追加(Elixir、PHP、C#、Bash、Go)で使用された正式なワークフローです。