Parsing code
The stable public API for low-level syntax-tree parsing is get_parser(). It returns the package’s Parser wrapper configured for one of the bundled languages.
Language bindings expose process() for structured analysis. Use process() unless you need a raw syntax tree or a manual AST walk.
use tree_sitter_language_pack::get_parser;
let mut parser = get_parser("python")?;let tree = parser .parse("def greet(name: str) -> str:\n return f\"Hello {name}\"\n") .ok_or("failed to parse source")?;
let root = tree.root_node();println!("{}", root.to_sexp());# Ok::<(), Box<dyn std::error::Error>>(())# Print the syntax tree for a filets-pack parse main.py
# Output as JSONts-pack parse main.py --format jsonFor batch processing in Rust, reuse the parser object. Creating one parser per parse works at small scale but adds avoidable setup overhead when processing a large file set for the same language.
The syntax tree
Section titled “The syntax tree”Every parse returns a Tree with a single root node. Its kind is the top-level grammar node for the language: module for Python, program for JavaScript, source_file for Rust and Go.
let mut parser = get_parser("python")?;let tree = parser .parse("def foo(): pass\ndef bar(): pass") .ok_or("failed to parse source")?;let root = tree.root_node();
println!("{}", root.kind()); // "module"println!("{:?}", root.start_position());println!("{:?}", root.end_position());println!("{}", root.child_count()); // 2println!("{}", root.has_error()); // false# Ok::<(), Box<dyn std::error::Error>>(())Field names
Section titled “Field names”Grammars assign named fields to semantically meaningful children. A Python function_definition has name, parameters, return_type, and body. Use child_by_field_name to reach them directly:
let mut parser = get_parser("python")?;let tree = parser .parse("def add(a, b):\n return a + b") .ok_or("failed to parse source")?;let root = tree.root_node();let func = root.child(0).ok_or("missing function")?;
let name = func.child_by_field_name("name").ok_or("missing function name")?;let params = func .child_by_field_name("parameters") .ok_or("missing parameters")?;
println!("{}", name.utf8_text(b"def add(a, b):\n return a + b")?); // "add"println!("{}", params.utf8_text(b"def add(a, b):\n return a + b")?); // "(a, b)"# Ok::<(), Box<dyn std::error::Error>>(())Field names are grammar-specific. To discover them, run ts-pack parse file.py and read the labelled S-expression output. Field names appear as name:, parameters:, body: before each child.
Named vs. anonymous nodes
Section titled “Named vs. anonymous nodes”Named nodes carry semantic meaning (identifier, call_expression, string). Anonymous nodes are punctuation and keywords ((, ), def, :). Use named_children when you want semantic nodes and no punctuation tokens.
let cursor = &mut root.walk();for child in root.named_children(cursor) { println!("{}", child.kind());}Syntax errors
Section titled “Syntax errors”Tree-sitter does not raise on malformed syntax. It marks problem areas with ERROR or MISSING nodes and keeps parsing.
let mut parser = get_parser("python")?;let tree = parser .parse("def broken(") .ok_or("failed to parse source")?;
println!("{}", tree.root_node().has_error()); // truehas_error on the root is a fast way to check whether any errors exist before walking. For structured diagnostics, use process() with diagnostics enabled.
Node properties
Section titled “Node properties”| Property | Description |
|---|---|
kind() |
Grammar node type, for example function_definition |
start_position() |
(row, column), zero-indexed |
end_position() |
(row, column), zero-indexed |
start_byte() |
Byte offset in source |
end_byte() |
Byte offset in source |
child_count() |
All children including anonymous nodes |
named_child_count() |
Named children only |
utf8_text(source) |
Raw source text for this node decoded as UTF-8 |
has_error() |
True if any error nodes exist in this subtree |
is_named() |
False for anonymous nodes like ( or def |
parent() |
Enclosing node, or None for the root |
Language passthrough support
Section titled “Language passthrough support”get_language(name) (or getLanguage in Node.js) returns a language handle in the most idiomatic shape for each binding. Where the ecosystem ships a native tree-sitter library, the returned value is the real Language object from that library, ready to feed into the local Parser. Where no such library exists or the library does not accept a raw pointer constructor, the binding returns an opaque handle.
Passthrough bindings (host-native Language)
Section titled “Passthrough bindings (host-native Language)”These return the ecosystem’s native Language type, ready to use with that ecosystem’s parser:
| Binding | Returns | Use with | On unknown language |
|---|---|---|---|
| Rust | tree_sitter::Language |
tree_sitter::Parser::set_language |
Result<Language, Error> — propagate with ? |
| Python | tree_sitter.Language (PyCapsule) |
tree_sitter.Parser(language) |
Raises LanguageNotFoundError |
| Node.js | tree-sitter npm Language |
new Parser().setLanguage(lang) |
Throws an Error |
| Go | *tree_sitter.Language |
parser.SetLanguage(lang) |
Returns (nil, error) |
| Java | io.github.treesitter.jtreesitter.Language |
new Parser().setLanguage(lang) |
Throws TreeSitterLanguagePackRsException |
| C# | TreeSitter.Language |
new Parser().SetLanguage(lang) |
Throws TreeSitterLanguagePackException |
| Kotlin Android | io.github.treesitter.ktreesitter.Language |
Parser(...).setLanguage(lang) |
Throws the binding’s bridge exception |
| Swift | SwiftTreeSitter.Language |
Parser().setLanguage(lang) |
Throws TreeSitterLanguagePackError |
| Zig | ?*const tree_sitter.Language |
Parser.setLanguage(lang) |
Returns a Zig error (Error!…) |
| C FFI | const TSLanguage * (borrowed) |
ts_parser_set_language(parser, lang) |
Returns NULL (do not free the result) |
On a passthrough binding the handle is the ecosystem’s own Language, so it drops straight into existing tree-sitter code — add this package as a grammar source without touching the rest of your parsing setup. In Python, get_language() returns a real tree_sitter.Language (a PyCapsule-backed object) that the upstream tree_sitter.Parser accepts directly:
import tree_sitterfrom tree_sitter_language_pack import get_language
language = get_language("python") # a tree_sitter.Languageparser = tree_sitter.Parser(language)tree = parser.parse(b"def f(x):\n return x + 1\n")print(tree.root_node.type) # "module"This restores the pre-1.9 ability to pass the handle to a separately installed tree_sitter (or tree-sitter on npm). If you do not already depend on the ecosystem library, prefer get_parser(name) — it returns a ready-to-use parser with no second dependency.
Opaque-handle bindings
Section titled “Opaque-handle bindings”These return an opaque handle specific to this package. Use the higher-level process() function or this package’s getParser()/get_parser() method rather than reaching for the ecosystem’s tree-sitter library.
| Binding | Returns | Recommendation |
|---|---|---|
| Ruby | opaque handle | Use this package’s Parser wrapper or process() |
| PHP | opaque handle | Use this package’s extension API; talbergs/php-tree-sitter FFI not exposed |
| Elixir | opaque handle | Use this package’s NIF wrapper; ResourceArc<T> is NIF-private |
| Dart | opaque handle | flutter_rust_bridge marshals Rust types as Arc-counted opaque proxies and never exposes the raw const TSLanguage * passthrough needs, and there is no maintained Dart tree-sitter package to construct from it — use the generated parser wrapper |
| WASM | opaque handle | Use this package’s wrapper; web-tree-sitter runs in separate WASM memory |
Next steps
Section titled “Next steps”- Code intelligence — extract functions, imports, docstrings, and symbols without writing a tree walker
- Extraction queries — public API status for custom query helpers
- Languages — all 306 supported languages and their aliases