How Understand Anything Language Extractors Process TypeScript, Python, Go, Rust, and Java Code
Understand Anything uses dedicated Tree-Sitter-based language extractors for each supported language to perform structural analysis and call-graph extraction by traversing AST nodes specific to each grammar.
The Egonex-AI/Understand-Anything repository implements a modular extraction system where language-specific plugins parse source code into abstract syntax trees. Each extractor implements the common LanguageExtractor interface defined in packages/core/src/plugins/extractors/types.ts, providing two core methods: extractStructure for building structural representations and extractCallGraph for mapping function relationships. These extractors power the knowledge graph by converting Tree-Sitter AST nodes into normalized StructuralAnalysis and CallGraphEntry objects.
The LanguageExtractor Interface and Common Pattern
All language extractors in Understand Anything follow a consistent architectural pattern defined by the LanguageExtractor interface. Each concrete implementation specifies accepted languageIds and provides two primary entry points for AST traversal.
export class XxxExtractor implements LanguageExtractor {
readonly languageIds = ["xxx"];
extractStructure(rootNode: TreeSitterNode): StructuralAnalysis {
// Walks top-level children to populate functions, classes, imports, and exports
}
extractCallGraph(rootNode: TreeSitterNode): CallGraphEntry[] {
// Depth-first traversal maintaining a functionStack to record call relationships
}
}
The extractStructure method identifies module boundaries, type definitions, and visibility markers, while extractCallGraph performs a recursive descent to build caller-callee relationships. Shared utilities from packages/core/src/plugins/extractors/base-extractor.ts handle common tasks like string unquoting and child node iteration.
TypeScript and JavaScript Extraction
The TypeScript extractor in packages/core/src/plugins/extractors/typescript-extractor.ts handles both TypeScript and JavaScript via languageIds = ["typescript", "javascript"]. It processes the Tree-Sitter grammar specific to ECMAScript-compatible languages.
Structural Analysis
The processTopLevelNode function dispatches on node types including function_declaration, class_declaration, lexical_declaration, import_statement, and export_statement. For classes, it collects method_definition nodes as methods and public_field_definition or property_definition nodes as properties.
Parameter extraction handles required_parameter, optional_parameter, rest_pattern, and rest_element nodes. Return types are extracted from type_annotation fields by stripping the leading colon. Exports are tracked via export_statement nodes, distinguishing default exports from named export_clause specifiers.
Call Graph Construction
During call-graph extraction, the extractor detects function boundaries (function_declaration, method_definition, arrow_function) and maintains a stack of current enclosing functions. When encountering a call_expression node, it records the relationship between the current function and the callee.
Python Code Analysis
The Python extractor in packages/core/src/plugins/extractors/python-extractor.ts handles Python-specific constructs like decorators and dynamic typing. Unlike languages with explicit export keywords, Python treats all top-level definitions as exports.
Handling Decorated Definitions
The extractor unwraps decorated_definition nodes to reach underlying function_definition or class_definition nodes. Parameters are extracted from identifier, typed_parameter, default_parameter, and typed_default_parameter nodes, including splat patterns (list_splat_pattern for *args and dictionary_splat_pattern for **kwargs). Return types are read from the return_type field.
Call Graph Implementation
For call-graph extraction, the extractor tracks entry into function_definition nodes and records any call node encountered during traversal. This captures both direct function calls and method invocations through attribute access.
Go Structural Analysis
The Go extractor in packages/core/src/plugins/extractors/go-extractor.ts implements Go-specific visibility rules and receiver-based method dispatch.
Export Detection and Types
Go uses capitalization for export visibility, so the extractor implements isExported to check if a name starts with an uppercase letter. For methods, the receiver field provides the receiver type through parameter_declaration and type_identifier nodes. Both functions and methods are processed, with methods attached to their receiver types via a methodsByReceiver map.
Parameter extraction iterates through parameter_declaration children, collecting identifier nodes. Return types are read from the result field, supporting single, pointer, and tuple returns.
Struct and Interface Processing
type_declaration nodes containing struct_type or interface_type become class entries. For structs, field_declaration nodes populate the properties list. For interfaces, method_elem nodes define method signatures.
Rust Module Processing
The Rust extractor in packages/core/src/plugins/extractors/rust-extractor.ts handles visibility modifiers, complex import syntax, and implementation blocks.
Visibility and Structs
Rust exports are determined by visibility_modifier nodes starting with pub. The extractor processes struct_item, enum_item, and trait_item as classes, mapping struct fields and enum variants to properties. Method extraction occurs within impl_item nodes, with methods stored in a methodsByType map for attachment to their corresponding types.
Import Handling
The use_declaration parser supports simple paths, scoped identifiers (std::collections::HashMap), grouped lists (std::io::{Read, Write}), and wildcards (std::prelude::*).
Call Expression Resolution
Call-graph extraction identifies call_expression nodes and resolves callee names handling plain identifiers, field expressions (self.validate), and scoped identifiers (Vec::new).
Java Class and Method Extraction
The Java extractor in packages/core/src/plugins/extractors/java-extractor.ts processes class-based object-oriented structures with explicit visibility modifiers.
Class and Member Analysis
The extractor handles import_declaration, class_declaration, and interface_declaration as top-level nodes. Both classes and interfaces are stored as class entries. Methods and constructors are extracted from method_declaration and constructor_declaration nodes, added to both the class's method list and the global functions list. Fields from field_declaration nodes become properties.
Visibility and Parameters
Export status is determined by checking modifiers children for the public keyword via hasModifier. Parameters are extracted from formal_parameter and spread_parameter nodes. Return types come from the type field of method declarations.
Call Graph Edges
The extractor tracks method declarations on a stack and generates call relationships from method_invocation nodes (capturing object.method patterns when present) and object_creation_expression nodes (recording new <type> callees).
Integration in the Analysis Pipeline
Language extractors fit into a four-stage pipeline orchestrated by the TreeSitterPlugin in packages/core/src/plugins/tree-sitter-plugin.ts:
- Language Detection: The plugin matches file extensions to
languageIdsarrays to select the appropriate extractor. - AST Creation: Tree-Sitter parsers generate language-specific ASTs from source files.
- Extraction: The selected extractor runs
extractStructureandextractCallGraph, producing normalized data structures. - Graph Assembly: The
PluginRegistrymerges results across files, deduplicates exports, and resolves import relationships into the final knowledge graph.
Practical Usage Examples
Direct Extractor Usage
Analyze TypeScript code directly using the extractor:
import { TypeScriptExtractor } from "./extractors/typescript-extractor.js";
import { Parser } from "web-tree-sitter";
async function analyzeTs(source: string) {
await Parser.init();
const parser = new Parser();
parser.setLanguage(await Parser.Language.load(
"node_modules/tree-sitter-typescript/tsx.wasm"
));
const tree = parser.parse(source);
const extractor = new TypeScriptExtractor();
const structure = extractor.extractStructure(tree.rootNode);
const callGraph = extractor.extractCallGraph(tree.rootNode);
console.log("Functions:", structure.functions);
console.log("Calls:", callGraph);
}
Full Pipeline Execution
Run the complete analysis pipeline across a project:
import { TreeSitterPlugin } from "./plugins/tree-sitter-plugin.js";
import { PluginRegistry } from "./plugins/registry.js";
async function scanProject(projectPath: string) {
const registry = new PluginRegistry();
const treeSitter = new TreeSitterPlugin();
await registry.register(treeSitter);
const graph = await registry.analyzeDirectory(projectPath);
console.log("Graph nodes:", graph.nodes.length);
console.log("Call edges:", graph.edges.filter(e => e.type === "call").length);
}
Summary
- LanguageExtractor Interface: All extractors implement
extractStructureandextractCallGraphmethods operating on Tree-Sitter ASTs. - TypeScript/JavaScript: Handles
export_statementandcall_expressionnodes, supporting both languages via the same extractor intypescript-extractor.ts. - Python: Treats all top-level definitions as exports and unwraps
decorated_definitionnodes to process functions and classes. - Go: Uses capitalization-based export detection and links methods to receiver types via
methodsByReceivermapping. - Rust: Checks
visibility_modifierforpubkeywords and supports complexuse_declarationimport patterns. - Java: Detects
publicmodifiers inmodifiersnodes and handlesmethod_invocationandobject_creation_expressionfor call graphs.
Frequently Asked Questions
How does Understand Anything handle language-specific visibility rules?
Each extractor implements language-specific logic: Go checks for uppercase names in go-extractor.ts, Rust looks for visibility_modifier nodes starting with pub in rust-extractor.ts, and Java checks modifiers children for the public keyword in java-extractor.ts. Python lacks explicit exports, so the extractor treats all top-level definitions as exported.
Can I use the extractors independently of the full Understand Anything pipeline?
Yes, as shown in the direct usage example. Each extractor can be instantiated and called directly with a Tree-Sitter rootNode, allowing you to obtain StructuralAnalysis and CallGraphEntry objects without running the complete PluginRegistry pipeline.
How are method calls resolved in languages with receivers like Go and Java?
The Go extractor in go-extractor.ts extracts receiver types from the receiver field and links methods to their types via a methodsByReceiver map. The Java extractor tracks method declarations on a stack and resolves method_invocation nodes to object.method callees when an object is present, while handling object_creation_expression for constructor calls.
What Tree-Sitter grammars are required for the extractors to function?
The TreeSitterPlugin registers grammars for TypeScript/JavaScript, Python, Go, Rust, and Java. Each extractor references language-specific WASM files (e.g., tree-sitter-typescript/tsx.wasm for TypeScript) that must be available in the node_modules or specified load path for the parser to generate valid ASTs.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →