A type-safe query language for Tree-sitter.
Powered by the arborium grammar collection.
Tree-sitter gives you the syntax tree. Extracting structured data from it still means writing imperative navigation code, null checks, and maintaining type definitions by hand. Plotnik makes extraction declarative: write a pattern, get typed data. The query is the type definition.
- Static type inference from query structure
- Named expressions for composition and reuse
- Recursion for nested structures
- Tagged unions (discriminated unions)
- TypeScript type generation
- CLI:
execfor matches,inferfor types,ast/trace/dumpfor debug - Full validation against grammar (reject queries that can never match)
- Compile-time queries via proc-macro
- WASM
- LSP, editor extensions
cargo install plotnikBy default, 15 common languages are included. To add specific languages:
cargo install plotnik --features lang-ruby,lang-elixirOr with all 80+ languages:
cargo install plotnik --features all-languagesExtract function signatures from Rust. Type references itself to handle nested generics like Option<Vec<String>>.
query.ptk:
Type = [
Simple: [(type_identifier) (primitive_type)] @name :: string
Generic: (generic_type
type: (type_identifier) @name :: string
type_arguments: (type_arguments (Type)* @args))
]
Func = (function_item
name: (identifier) @name :: string
parameters: (parameters
(parameter
pattern: (identifier) @param :: string
type: (Type) @type
)* @params))
Funcs = (source_file (Func)* @funcs)lib.rs:
fn get(key: Option<Vec<String>>) {}
fn set(key: String, val: i32) {}Plotnik infers TypeScript types from the query structure. Type is recursive: args: Type[].
❯ plotnik infer query.ptk --lang rust
export type Type =
| { $tag: "Simple"; $data: { name: string } }
| { $tag: "Generic"; $data: { name: string; args: Type[] } };
export interface Func {
name: string;
params: { param: string; type: Type }[];
}
export interface Funcs {
funcs: Func[];
}Run the query against lib.rs to extract structured JSON:
❯ plotnik exec query.ptk lib.rs
{
"funcs": [
{
"name": "get",
"params": [{
"param": "key",
"type": {
"$tag": "Generic",
"$data": {
"name": "Option",
"args": [{
"$tag": "Generic",
"$data": {
"name": "Vec",
"args": [{ "$tag": "Simple", "$data": { "name": "String" } }]
}
}]
}
}
}]
},
{
"name": "set",
"params": [
{ "param": "key", "type": { "$tag": "Simple", "$data": { "name": "String" } } },
{ "param": "val", "type": { "$tag": "Simple", "$data": { "name": "i32" } } }
]
}
]
}Pattern matching over syntax trees is powerful, but tree-sitter queries produce flat capture lists. You still need to assemble the results, handle missing captures, and define types by hand. Plotnik closes this gap: the query describes structure, the engine guarantees it.
Max Brunsfeld created Tree-sitter; Amaan Qureshi and other contributors maintain the parser ecosystem that makes this project possible.
This project is licensed under the Apache License (Version 2.0).
