gotreesitter: A Pure-Go Tree-Sitter Runtime That Drops CGo From the Build Chain
Pure Go tree-sitter runtime. It cross-compiles to any GOOS/GOARCH target Go supports, including wasip1.
At a glance
- What is it?
- gotreesitter reimplements the tree-sitter runtime in Go, removing CGo and the C toolchain from cross-compilation and CI. It targets Go developers who need tree-sitter parsing on wasip1, Windows, or any platform Go supports.
- Who is it for?
- Adopt gotreesitter if you need tree-sitter parsing on targets where CGo fails, such as wasip1 or Windows without MinGW, or if you want race detection and fuzzing to see across the parser boundary. Do not adopt it if you depend on C runtime behavior that this Go port does not replicate, or if your grammar relies on external scanners that are not yet implemented.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 5 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 14, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The CGo Problem It Solves
Every existing Go tree-sitter binding depends on CGo, which creates a chain of build failures. Cross-compiling to wasip1 or Windows from a Linux host requires a C cross-toolchain per target, and any Windows target without MSYS2 or MinGW will not link. CI images must carry gcc and the grammar's C sources, and `go install` fails for downstream users without a C compiler. gotreesitter removes the C dependency entirely. The parser, lexer, query engine, incremental reparsing, arena allocator, external scanners, and tree cursor are all implemented in Go. The only input is a grammar blob, so the build chain becomes a pure Go compile. This matters for teams that ship static binaries or run in sandboxed CI where installing a C toolchain is not an option.
How Grammar Tables Are Loaded
gotreesitter uses the same parse-table format as the C runtime, but it does not compile C code. The `ts2go` tool extracts grammar tables from upstream `parser.c` files, compresses them into binary blobs, and deserializes them on first use. The registry ships 206 grammars, so most common languages are available without building anything. The quick start shows a direct call: `lang := grammars.GoLanguage()` followed by `parser := gotreesitter.NewParser(lang)`. There is also `grammars.DetectLanguage("main.go")` to resolve a filename to a `LangEntry`. This design means the runtime is not a wrapper around the C library; it is a reimplementation that consumes the same data. The trade-off is that any bug in the Go port's parser logic will surface independently of the C runtime, and fixes depend on this project's release cycle.
Strict Parsing for Partial Trees
The default parse methods preserve tree-sitter's partial-tree behavior. If a timeout, cancellation flag, token-source EOF, or parser safety limit stops the parse early, the returned tree records the stop reason and the error stays nil. That is useful for editors and diagnostics, where a partial tree still has value. But for batch processing or API responses, a partial tree can silently corrupt results. The strict variants handle this: `parser.ParseStrict(src)` returns an error if the parse stops early, and you can check `errors.Is(err, gotreesitter.ErrParseStoppedEarly)` to inspect the reason via `tree.ParseStopReason()`. Strict variants exist for full parse, incremental parse, token-source parse, factory parse, `ParseWith`, and `ParserPool`. The `grammars.ParseFilePooledStrict` function returns a pooled `*BoundTree` only after a complete parse, and the caller must call `Release` on the returned tree. This is a concrete design choice: you opt into failure instead of accepting a partial result silently. The documentation is clear that `Tagger.TagStrict` releases its internal tree before returning, so you cannot reuse it after an error.
Query Engine and Typed Codegen
The query engine supports the full S-expression pattern language: structural quantifiers (`?`, `*`, `+`), alternation (`[...]`), field constraints, negated fields, anchor (`!`), and all standard predicates. The README gives a direct example where `gotreesitter.NewQuery` takes a pattern and `cursor.Exec` iterates matches. The query API is not just for ad-hoc use; the `tsquery` command generates type-safe Go wrappers from `.scm` files. Running `go run ./cmd/tsquery -input queries/go_functions.scm -lang go -output go_functions_query.go -package queries` produces a struct per pattern, like `FunctionDeclarationMatch` with typed fields `Name` and `Body`. This removes the need to manually extract captures from a generic cursor. The generated code is a real productivity gain for projects with many queries, but it also means you must regenerate wrappers when the query changes, and the generated struct names are tied to the pattern structure. The README does not specify how the generated code handles predicates like `#eq?` or `#match?`, so you should test those cases before relying on them.
WebAssembly and Browser Support
The repository ships two `GOOS=js GOARCH=wasm` targets: a blob-loading runtime and a grammargen build that imports tree-sitter grammar JSON and generates tables in the browser. The runtime exposes parsing, queries, and highlighting, and structured parse and query results include both UTF-8 byte offsets and JavaScript UTF-16 code-unit offsets. That dual-offset design is important for editors that work with JavaScript strings, where UTF-16 offsets are what the DOM uses. The runtime can also retain an incrementally updated document tree and reuse it for highlights, tags, and queries. The `cmd/wasmassets` tool emits a reproducible, single-language browser bundle for either the Go or TinyGo WebAssembly compiler. This is a notable advantage over CGo bindings, which typically require a separate Emscripten build. The README points to a `wasm/README.md` for build commands and limits, but that file is not included here. You should read it before assuming the browser bundle supports all 206 grammars or all query predicates.
Code-Understanding Helpers and the Taproot DSL Harness
For hot indexing paths, the `FactProgram` API compiles a program once and reuses it across trees. `gotreesitter.NewFactProgram(lang, gotreesitter.FactAll)` extracts definitions, calls, heritage edges, and imports in one tree traversal. Individual APIs like `ExtractDefinitionSpans`, `ExtractCalls`, `ExtractHeritage`, and `ExtractImports` remain available. These cover Go, JavaScript, TypeScript/TSX, Python, Starlark, and Java, and they skip unsupported languages or ambiguous shapes. That is a practical shortcut for building an indexer, but the language coverage is limited to those six, so you cannot use it for Rust or Ruby without writing your own queries. The `taproot` package is a separate front-end for grammargen-backed DSLs. It caches generated or blob-loaded languages, parses source, and returns a `Walker` with common CST helpers. It reports syntax errors while still returning the partial root for diagnostics. The example shows `taproot.ParseFromBlob("dsl", blob, buildGrammar, src)` returning a root, walker, and error. This is a niche tool for DSL authors, and the README does not explain how to write a `buildGrammar` function, so you will need to look at the source or the skill file linked in the README.
Limitations and Alternatives
The most obvious limitation is that this is a reimplementation, not a binding. The README claims external scanners are implemented in Go, but it does not detail how they behave when the upstream C scanner uses platform-specific code. If a grammar's external scanner depends on C libraries or system calls, the Go port may not match the C runtime's behavior. The README also truncates the multi-language injection section, so the documentation for that feature is incomplete. A real alternative is the original `go-tree-sitter` binding, which uses CGo and links directly to the C runtime. That approach gives you exact parity with the C library's behavior, including any bug-for-bug compatibility, but it reintroduces the cross-compilation and CI problems that gotreesitter solves. Another alternative is using tree-sitter's own CLI to generate a parser in C and then calling it via cgo, which is the common pattern in other language bindings. If you need the absolute latest grammar features or rely on a grammar not in the 206 bundled ones, the CGo route may be safer. The choice comes down to whether you value build portability over behavioral parity.
Maintenance and License Considerations
The project is under active development, with three releases in August 2026 alone: v0.50.0, v0.50.1, and v0.51.0. That pace suggests ongoing fixes and feature additions, but it also means you should pin a version and test upgrades carefully, as the API may shift between minor versions. The license is MIT, which is permissive and allows commercial use, modification, and redistribution without copyleft obligations. The README links to an agent skill file, which indicates the maintainers expect AI-assisted development workflows, but that does not affect the runtime's behavior. The project is not archived, and the last push matches the latest release, so maintenance appears active. However, the README does not mention a contribution guide, code of conduct, or issue template, so the governance structure is unclear. You should inspect the repository's issue tracker and commit history before adopting it for a long-term project.
Editorial conclusion
Adopt gotreesitter if you need tree-sitter parsing on targets where CGo fails, such as wasip1 or Windows without MinGW, or if you want race detection and fuzzing to see across the parser boundary. Do not adopt it if you depend on C runtime behavior that this Go port does not replicate, or if your grammar relies on external scanners that are not yet implemented. Before committing, verify that the 206 bundled grammars cover your languages, test the strict parse paths for your editor or indexing workload, and confirm the query engine's predicate support matches your .scm files. The project is under active release, with v0.51.0 pushed in August 2026, so check the changelog for recent breaking changes.
Community notes