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.
Features
- 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
Installation
By default, 15 common languages are included. To add specific languages:
Or with all 80+ languages:
Example
Extract 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:
Plotnik infers TypeScript types from the query structure. Type is recursive: args: Type[].
| { ; }
| { ; };
Run the query against lib.rs to extract structured JSON:
{
{
}
{
{ }
{ }
}
}
Why
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.
Documentation
Acknowledgments
Max Brunsfeld created Tree-sitter; Amaan Qureshi and other contributors maintain the parser ecosystem that makes this project possible.
License
This project is licensed under the Apache License (Version 2.0).