How to Configure Custom Language Parsing for Unsupported Languages in Code-Review-Graph

Developers can add support for any Tree-sitter grammar without forking or modifying the source code by creating a languages.toml configuration file inside the repository's .code-review-graph directory.

The code-review-graph tool uses Tree-sitter for parsing source files, and while it ships with built-in support for popular languages, it exposes a declarative configuration system to extend coverage to any grammar available in the tree_sitter_language_pack. This guide explains how to configure custom language parsing for unsupported languages using the repository-level configuration architecture.

Architecture of Custom Language Support

The custom language system is designed to operate without code changes, leveraging a configuration-driven architecture that merges user-defined mappings with built-in parsers at runtime.

How the Loader Interacts with the Parser

When CodeParser initializes in code_review_graph/parser.py, it invokes load_custom_languages from code_review_graph/custom_languages.py to read and validate the TOML configuration. This function returns a mapping that gets merged into the internal EXTENSION_TO_LANGUAGE table, enabling the detect_language method to recognize files with custom extensions. During parsing, the generic Tree-sitter walker uses the node-type lists defined in the configuration to extract Function, Class, IMPORTS_FROM, and CALLS edges without requiring language-specific code paths.

Step-by-Step Configuration Guide

Follow these steps to enable parsing for an unsupported language using an Erlang configuration as a working example.

1. Create the Configuration File

Create a file named languages.toml inside the .code-review-graph directory at your repository root:


# .code-review-graph/languages.toml

[languages.erlang]
extensions = [".erl"]
grammar = "erlang"                     # Grammar name from tree-sitter-language-pack

function_node_types = ["function_clause"]
class_node_types    = ["record_decl"]
import_node_types   = ["import_attribute"]
call_node_types     = ["call"]
comment = "Erlang via the bundled tree-sitter-erlang grammar"

2. Define Language Metadata and Grammar

Each language block under [languages.<name>] requires:

  • extensions: A list of file suffixes that trigger this parser.
  • grammar: The exact name of the Tree-sitter grammar as distributed in tree_sitter_language_pack.
  • comment: Optional description for debugging purposes.

The system enforces collision rules where built-in languages take precedence, and validates that each extension is unique across all configured languages.

3. Map Tree-sitter Node Types to Graph Entities

Specify which Tree-sitter AST nodes should be converted to graph entities:

  • function_node_types: Nodes representing functions or methods.
  • class_node_types: Nodes representing classes, structs, or records.
  • import_node_types: Nodes representing module imports or includes.
  • call_node_types: Nodes representing function or method invocations.

These mappings allow the generic walker in code_review_graph/custom_languages.py to process the syntax tree without language-specific logic.

4. Rebuild the Graph Index

After creating the configuration, trigger a full rebuild to index the new language:


# From the repository root

uv run code-review-graph build

The incremental build system in code_review_graph/incremental.py reprocesses all matching files and persists nodes to SQLite with the custom language identifier stored in the language field.

Validation and Testing

Verify that your configuration loads correctly and integrates with the parser before running full analysis.

Verify Configuration Loading

Use the Python API to confirm that load_custom_languages returns your custom definition:

from code_review_graph.custom_languages import load_custom_languages
from pathlib import Path

repo = Path(".")
langs = load_custom_languages(
    repo,
    builtin_extensions=EXTENSION_TO_LANGUAGE,
    builtin_languages=_builtin_language_names(),
)
print(langs)  # → {'erlang': CustomLanguage(...)}

Test File Detection and Parsing

Instantiate CodeParser with the repository root to enable custom language detection, then verify parsing produces the expected node types:

from code_review_graph.parser import CodeParser
from pathlib import Path

parser = CodeParser(Path("."))  # repo_root required for custom languages

lang = parser.detect_language(Path("src/math_utils.erl"))
print(lang)  # → "erlang"

nodes, edges = parser.parse_file(Path("src/math_utils.erl"))
assert any(n.kind == "Function" and n.language == "erlang" for n in nodes)

Technical Implementation Details

Understanding the internal validation and constraints helps prevent configuration errors.

The CustomLanguages Loader Architecture

The load_custom_languages function in code_review_graph/custom_languages.py implements a defensive loading strategy that:

  • Caches configurations to avoid repeated disk I/O during batch processing.
  • Enforces a maximum limit of 20 custom languages per repository.
  • Validates TOML schema and ensures broken configurations never crash the build.
  • Resolves extension collisions by prioritizing built-in parsers over custom definitions.

Parser Integration and Extension Mapping

In code_review_graph/parser.py, the CodeParser constructor calls load_custom_languages and merges the returned dictionary into its EXTENSION_TO_LANGUAGE mapping. This design ensures that detect_language works transparently for both built-in and custom languages. The generic Tree-sitter walker then retrieves the node-type lists from the CustomLanguage object to extract definitions and relationships, enabling impact analysis, semantic search, and community detection features for the newly configured language without additional code changes.

Summary

  • Configuration-driven: Add unsupported languages by creating .code-review-graph/languages.toml without modifying source code.
  • Tree-sitter integration: Leverage any grammar from tree_sitter_language_pack by specifying the grammar name and mapping AST node types to graph entities.
  • Validation safeguards: The load_custom_languages function in code_review_graph/custom_languages.py enforces limits (max 20 languages), prevents collisions, and ensures malformed configs fail gracefully.
  • Full rebuild required: Run code-review-graph build after configuration changes to index files with the new language parser.
  • Generic processing: The walker in code_review_graph/custom_languages.py handles both built-in and custom languages identically, supporting all downstream analysis features.

Frequently Asked Questions

What Tree-sitter grammars are supported for custom languages?

Any grammar distributed in the tree_sitter_language_pack can be used. The grammar field in languages.toml must match the exact package name as defined in that collection, such as "erlang", "dart", or "kotlin".

Why does my custom language configuration not appear to work?

Ensure you have triggered a full rebuild using code-review-graph build or uv run code-review-graph build. The incremental indexing system in code_review_graph/incremental.py only picks up new language mappings during a full graph rebuild, not during incremental updates.

Is there a limit to how many custom languages I can configure?

Yes, the system enforces a maximum of 20 custom languages per repository. This limit is hardcoded in code_review_graph/custom_languages.py to prevent configuration bloat and ensure build performance remains consistent.

How does the system handle file extensions that conflict with built-in languages?

Built-in languages always take precedence over custom definitions. If you declare an extension that matches a built-in parser, the load_custom_languages function in code_review_graph/custom_languages.py will skip the custom mapping and emit a warning, ensuring standard language detection remains unaffected.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →