Adding New Language Support
Relevant source files
The following files were used as context for generating this wiki page:
Arbor is designed as a multi-language Code Property Graph (CPG) engine that leverages Tree-sitter for high-performance code analysis. Adding support for a new language involves integrating a Tree-sitter grammar and implementing a parser that extracts meaningful code entities—such as functions, classes, and variables—and their semantic relationships like calls and imports.
The system utilizes two primary parsing pathways: a legacy trait-based approach for complex logic and a high-performance query-based approach via ArborParser (v2). The latter is preferred for modern integrations as it caches compiled queries and simplifies symbol extraction across large codebases.
Sources: CONTRIBUTING.md:126-130, crates/arbor-core/src/parser_v2.rs:1-8, crates/arbor-core/README.md
Integration Workflow
The process of adding a new language follows a structured sequence from dependency management to registry configuration.
1. Add Tree-sitter Dependency
The first step is adding the specific Tree-sitter grammar to the core engine's configuration. This allows the Rust environment to interface with the language's AST structure.
Sources: CONTRIBUTING.md:132-136, crates/arbor-core/Cargo.toml:13-22
2. Implement the Parser
Developers must define how symbols and relationships are extracted. This is currently done in two ways:
- Legacy Path: Implementing the
LanguageParsertrait, which requires defining thelanguage(),extensions(), andextract_nodes()methods. - V2 Query Path: Compiling Tree-sitter queries within
ArborParserto identify nodes (e.g.,function_definition,class_declaration) and relations (e.g.,call_expression,import_statement).
Sources: CONTRIBUTING.md:141-164, crates/arbor-core/src/parser_v2.rs:88-125
3. Registration and Testing
The new parser must be registered in the language registry (typically crates/arbor-core/src/languages/mod.rs) so the detect_language utility can map file extensions to the correct implementation.
Sources: CONTRIBUTING.md:166-172, crates/arbor-core/src/parser.rs:89-92
Architecture and Data Flow
The parsing engine acts as the "eyes" of Arbor, transforming raw source code into a structured ParseResult which contains symbols (CodeNode) and relations (SymbolRelation).
The diagram shows the transition from raw source code to the final graph integration via automated language detection and Tree-sitter query execution.
Sources: crates/arbor-core/src/parser.rs:32-52, crates/arbor-core/src/parser_v2.rs:136-170
Parsing Implementation Details
Query-Based Extraction (v2)
Modern language support in Arbor uses pre-compiled S-expression queries. These queries target specific nodes in the AST to extract metadata like names and visibility.
| Component | Description | Relevance |
|---|---|---|
symbols Query | Extracts functions, classes, structs, and interfaces. | Core node generation |
imports Query | Identifies module dependencies. | Global symbol resolution |
calls Query | Identifies function and method invocations. | Edge generation |
CompiledQueries | Struct holding the three queries above per language. | Performance caching |
Sources: crates/arbor-core/src/parser_v2.rs:68-80, crates/arbor-core/src/parser_v2.rs:307-320
Symbol Relations
Relationships are categorized into specific types to facilitate impact analysis.
This class diagram illustrates the structure of relationship data extracted during the parsing phase.
Sources: crates/arbor-core/src/parser_v2.rs:20-45
Implementation Example: Rust Support
The following snippet demonstrates how Rust-specific queries are defined within the V2 parser to capture standard symbols and calls.
// crates/arbor-core/src/parser_v2.rs:341-356
fn compile_rust_queries() -> Result<CompiledQueries> {
let language = tree_sitter_rust::language();
let symbols_query = r#"
(function_item name: (identifier) @name) @function_def
(struct_item name: (type_identifier) @name) @struct_def
(enum_item name: (type_identifier) @name) @enum_def
(trait_item name: (type_identifier) @name) @trait_def
"#;
let calls_query = r#"
(call_expression function: (identifier) @callee)
(call_expression function: (field_expression field: (field_identifier) @callee))
"#;
// ...
}
Sources: crates/arbor-core/src/parser_v2.rs:341-365
Validation and Metadata
When adding a new language, the parser must correctly populate CodeNode fields to ensure the visualizer and CLI provide accurate feedback.
- Qualified Name: Formatted as
filename:symbol_namefor uniqueness. - Signatures: Extracted from the source to show the first line of a definition (e.g.,
pub fn init()). - Location Data: Includes start/end lines, columns, and byte offsets for IDE navigation.
Sources: crates/arbor-core/src/parser_v2.rs:239-268, crates/arbor-core/src/node.rs
Summary
Adding language support is a critical task for expanding the "Logic Forest." By integrating new Tree-sitter grammars and defining query-based extraction logic, developers enable Arbor to perform cross-file symbol resolution and impact analysis on diverse ecosystems. The shift toward the ArborParser v2 framework ensures that new languages benefit from optimized query caching and consistent relationship mapping.
Sources: PR_DESCRIPTION.md:31-33, README.md:Supported Languages