Commit graph

6488 commits

Author SHA1 Message Date
Will Lillis cde84508ac fix(generate)!: error when nonexistent _file_ path is passed to
`generate_parser_in_directory`.
2026-08-20 22:11:42 -04:00
Max Brunsfeld 43623ec9bf
Upgrade to latest wasmtime, improve robustness and perf of wasm-based parsing (#5847)
* build(deps): upgrade wasmtime C API to 48.0.0

Upgrade the Rust and Zig Wasmtime dependencies and enable reference values with the null GC collector.

Wasmtime 48 requires a newer Rust toolchain, whose Clippy version identifies three item helpers that can be const. Mark them const so the workspace continues to pass Clippy with warnings denied.

* feat(benchmark): support Wasm grammars

* perf(wasm): cache language function handles

* fix(wasm): improve validation and failure cleanup

Bounds-check dylink metadata parsing, require exact import and export names, and restore memory and function-table allocation offsets when language loading fails.

* fix(wasm): copy the complete supertype map
2026-08-20 21:02:11 +00:00
Will Lillis fe3fe327e2 fix(generate): correct test assertion for inlined supertype warning 2026-08-20 02:47:10 -04:00
Will Lillis 03ae6710cd fix(ci): run all workspace tests
Without `--workspace`, all generate tests were silently skipped.
2026-08-20 02:47:10 -04:00
Amaan Qureshi 067f85e5a2 generate: hold StrIds in node-types JSON output
The JSON output types now hold ids from the string pool instead of
cloning into owned `String`s, which makes `NodeTypeJSON` `Copy` and
drops most allocations when building the output.

Co-authored-by: Will Lillis <will.lillis24@gmail.com>
2026-08-19 20:30:17 -04:00
Amaan Qureshi 5800563fe7 generate: refactor get_variable_info and generate_node_types_json
This splits both functions into small, focused helpers with no
behavioral change.
2026-08-19 20:30:17 -04:00
Amaan Qureshi 14c158bbba generate: warn when a symbol is both a supertype and inlined
Currently, a symbol listed in both `supertypes` and `inline` produces
a phantom supertype entry in `node-types.json` for a rule that can
never appear in a tree. `intern_symbols` now drops the supertype with
a warning. A hard error would break 29 published grammars, including
python, go, ruby, rust, and scala, so that that decision is deferred
to the next breaking release. Fixes #5218
2026-08-19 20:30:17 -04:00
dependabot[bot] f235c2f1c3 build(deps): bump the cargo group with 4 updates
Bumps the cargo group with 4 updates: [bstr](https://github.com/BurntSushi/bstr), [cc](https://github.com/rust-lang/cc-rs), [clap_complete_nushell](https://github.com/clap-rs/clap) and [thiserror](https://github.com/dtolnay/thiserror).


Updates `bstr` from 1.13.0 to 1.13.1
- [Commits](https://github.com/BurntSushi/bstr/compare/1.13.0...1.13.1)

Updates `cc` from 1.4.2 to 1.4.3
- [Release notes](https://github.com/rust-lang/cc-rs/releases)
- [Changelog](https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/cc-rs/compare/cc-v1.4.2...cc-v1.4.3)

Updates `clap_complete_nushell` from 4.6.1 to 4.6.2
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete_nushell-v4.6.1...clap_complete_nushell-v4.6.2)

Updates `thiserror` from 2.0.19 to 2.0.20
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/2.0.19...2.0.20)

---
updated-dependencies:
- dependency-name: bstr
  dependency-version: 1.13.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo
- dependency-name: cc
  dependency-version: 1.4.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo
- dependency-name: clap_complete_nushell
  dependency-version: 4.6.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo
- dependency-name: thiserror
  dependency-version: 2.0.20
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-18 10:39:56 +02:00
dependabot[bot] 95eaaa2130 build(deps): bump the npm group across 1 directory with 3 updates
Bumps the npm group with 3 updates in the /lib/binding_web directory: [esbuild](https://github.com/evanw/esbuild), [tsx](https://github.com/privatenumber/tsx) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `esbuild` from 0.28.1 to 0.28.2
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.28.1...v0.28.2)

Updates `tsx` from 4.23.11 to 4.23.12
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.23.11...v4.23.12)

Updates `typescript-eslint` from 8.66.0 to 8.67.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.67.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.28.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: tsx
  dependency-version: 4.23.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: typescript-eslint
  dependency-version: 8.67.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-18 10:37:31 +02:00
dependabot[bot] dff1fd868c build(deps): bump esbuild and vite in /lib/binding_web
Bumps [esbuild](https://github.com/evanw/esbuild) and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite). These dependencies needed to be updated together.

Updates `esbuild` from 0.27.7 to 0.28.1
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.27.7...v0.28.1)

Updates `vite` from 7.3.2 to 7.3.6
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.3.6/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.3.6/packages/vite)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.28.1
  dependency-type: indirect
- dependency-name: vite
  dependency-version: 7.3.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-15 10:13:38 +02:00
Max Brunsfeld 0e2af0d8d1
Allow using Tree-sitter rust lib in multi-threaded web apps compiled to wasm32-unknown-unknown (#5851)
* feat(wasm): make syntax trees sendable

* test(wasm): transfer trees across workers

* test(wasm): use JSON grammar for tree transfer

* test(wasm): edit trees across workers

* test(wasm): share dlmalloc with tree-sitter

* test(wasm): simplify worker tree exchange

* test(wasm): drive tree exchange from Rust

* test(wasm): split sendable-tree xtask

* test(wasm): generalize Rust web fixture

* test(wasm): exercise parallel Rust tree access

* fix(wasm): use Rust global allocator for C core

* test(wasm): use default Rust allocator

* feat(wasm): support external scanners in Rust web apps

* Simplify example further, add a readme

* Regenerate wasm-stdlib

* Fix wasm_stdlib check script

* Vendor the Wasm standard library subset

* Test Unicode Ruby scanner behavior in Wasm

* Make Wasm tree languages instance-aware

* Test multi-threaded use of queries in wasm32-unknown

* Refactor reference-counted language storage

* Check ABI version compat before loading rest of language

* 🎨 Remove redundant #ifdef block

* Reject unsupported Rust Wasm builds on 0.26
2026-08-14 17:05:42 -07:00
Will Lillis 21e3614b8d perf(lib): fast path for ASCII characters in ts_lexer__advance
Yields a 0-5% improvement on parse speed (very grammar dependent).
2026-08-14 18:11:27 +02:00
Will Lillis 730946cfd5 perf(lib): fast path for ASCII characters in ts_lexer__get_lookahead
Yields a ~2% improvement on parse speed.
2026-08-14 18:11:27 +02:00
Christian Clason 277f53f886 build(swift)!: drop Package.swift
Problem: `Package.swift` build script is unmaintained and leads to build
errors.

Solution: Remove `Package.swift`; downstream tools should rely on the
swift-tree-sitter bindings (or, if they want a custom bare-metal build,
handle this in their own build scripts).
2026-08-14 12:51:38 +02:00
Christian Clason f8e735212b build(deps): bump binaryen to v132 2026-08-14 09:33:39 +02:00
Tim Vermeulen f30ee2b300 fix(parser): nest error children during recovery 2026-08-14 00:51:11 -04:00
Tim Vermeulen 869638f6cf fix(lib): accumulate error costs through hidden error nodes 2026-08-14 00:51:11 -04:00
Will Lillis c279989118 fix(xtask): check for vitest rather than chai and mocha
`test::run_wasm` gated dependency installation on `node_modules/chai` and
`node_modules/mocha`. Neither is a dependency (vitest is used instead),
so the check never passed and every `cargo xtask test-wasm`
reran `npm install`.
2026-08-14 00:13:54 -04:00
Will Lillis c68605f18d fix(rust)!: tie language string lifetimes to the proper containers
`Node::kind`, `Node::grammar_name`, the field name accessors,
`TreeCursor::field_name`, and the `Language` name lookups returned `&'static
str` while pointing into storage owned by the `TSLanguage`. Releasing the last
handle to a Wasm language frees that storage.

`Node` and `TreeCursor` now return `&'tree str`, which is ok because
`ts_tree_new` takes a reference to the language via `ts_language_copy` and
holds it until `ts_tree_delete`. The `Language` lookups return strings borrowed
from `&self`.
2026-08-14 00:13:54 -04:00
Will Lillis 7be497a14e fix(web)!: report null when lookahead iterator is not positioned
`currentType` and `currentTypeId` return `null` before the first iteration
step, after exhaustion, and after a reset.

`currentType` also falls back to the language's own name table instead of the
literal 'ERROR' when `Language.types` has no entry (auxiliary symbols). Other
bindings report `end` where this one reported 'ERROR'.
2026-08-14 00:13:54 -04:00
Will Lillis 081929092f fix(rust)!: make lookahead accessors fallible and iteration fused
`current_symbol` and `current_symbol_name` return an `Option` (`None` when the
iterator is not positioned on a symbol). Change `iter_names`'s item from
`&'static str` to `&str`.
2026-08-14 00:13:54 -04:00
Will Lillis 7bf4a10995 fix(lib)!: make lookahead iterator exhaustion sticky and retain its language
Track the iterator's phase so exhaustion is sticky, and gate the symbol name on
it, so `NULL` means "not positioned on a symbol".

`ts_lookahead_iterator_new` was also the only language consuming constructor
that did not `ts_language_copy`, so an iterator outliving a wasm language read
freed memory. Retain in `_new` and `_reset`, release in `_delete`.

Previously:
- `ts_lookahead_iterator__next` left a small parse state's cursor one past its
group end, so re-advancing an exhausted iterator resumed returning `true` and
walked through the rest of `ts_small_parse_table` and off the end of it.

- `ts_lookahead_iterator_current_symbol_name` returned `NULL` only when the
exhausted symbol index happened to fall outside the names table, so a grammar
with aliases returned a real but wrong name instead.
2026-08-14 00:13:54 -04:00
Will Lillis 721b211168 fix(generate)!: reject non-ASCII byte classes in (?-u:...) patterns
`(?-u:...)` switches `regex_syntax` to matching raw bytes, but the lexer
dispatches on decoded characters, so `expand_regex` converted the byte
class with a u8 cast. This is exact for ASCII bytes, and silently
misleading for anything aboove 0x80.

This is not reachable from grammar.js (node and QuickJS both reject
`(?-u:...)`). This change is to guard against future JS runtime changes,
as well as alternative frontends to the generate crate.
2026-08-14 00:13:39 -04:00
Will Lillis 2b15649f93 fix(generate): fold case-insensitive patterns at the AST leaves
A previous fix moved case folding for `/i` patterns out of `regex_syntax` and
into `expand_regex`, so folding could drop the two non-ASCII code points
Unicode simple folding maps onto ASCII letters: the long s `ſ` (U+017F)
onto `s`, and the Kelvin sign `K` (U+212A) onto `k`. Left in, they leak
into otherwise-ASCII tokens and stop those tokens from being extracted as
keywords.

By that point, though, the HIR has already turned a negated class into a
complement, so folding it applies the fold on the wrong side of the
negation. `(?i)[^a-z]` folds a set that contains `A-Z`, which re-admits
`a-z` and leaves a class matching very nearly everything.

`regex_syntax` folds each leaf of a class expression before applying that
leaf's negation and the set algebra above it. Keep that order and change
only the fold: walk the AST, replace each leaf with its fold, and translate
with `case_insensitive(false)`.
2026-08-12 23:40:32 -04:00
Jason Boatman 18cc71a6d9 feat(lib): add state to missing nodes.
This allows them to return possible missing values from a `LookaheadIterator`.
2026-08-12 23:39:44 -04:00
Will Lillis 816a2a0c15
refactor(rust): trim unused imports (#5845)
* Importing `char` brings deprecated methods into scope in some cases.
* Importing `str` isn't necessary in many other cases.
2026-08-12 14:27:52 +02:00
Will Lillis 42f33fe2f8 fix(query): correctly set a state's skipped_quantifier flag when a
zero quantifier skip is performed.

The zero-skip branch currently sets skipped_quantifier unconditionally.
That flag is correct only when the quantified step and its skip target
are _siblings_ at the same query depth. Otherwise, setting it allows for
a "leak" and disables unrelated, "outer" anchors.
2026-08-10 23:59:11 -05:00
dependabot[bot] 5c2533bb7b build(deps): bump the cargo group with 3 updates
Bumps the cargo group with 3 updates: [cc](https://github.com/rust-lang/cc-rs), [clap](https://github.com/clap-rs/clap) and [clap_complete](https://github.com/clap-rs/clap).


Updates `cc` from 1.4.0 to 1.4.1
- [Release notes](https://github.com/rust-lang/cc-rs/releases)
- [Changelog](https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/cc-rs/compare/cc-v1.4.0...cc-v1.4.1)

Updates `clap` from 4.6.5 to 4.6.6
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.6.5...clap_complete-v4.6.6)

Updates `clap_complete` from 4.6.8 to 4.6.9
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.6.8...clap_complete-v4.6.9)

---
updated-dependencies:
- dependency-name: cc
  dependency-version: 1.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo
- dependency-name: clap
  dependency-version: 4.6.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo
- dependency-name: clap_complete
  dependency-version: 4.6.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-10 23:42:21 +02:00
dependabot[bot] 6ea9a7c031 build(deps): bump the npm group across 1 directory with 4 updates
Bumps the npm group with 4 updates in the /lib/binding_web directory: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [eslint](https://github.com/eslint/eslint), [tsx](https://github.com/privatenumber/tsx) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `@types/node` from 26.1.2 to 26.2.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `eslint` from 10.8.0 to 10.8.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v10.8.0...v10.8.1)

Updates `tsx` from 4.23.1 to 4.23.11
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.23.1...v4.23.11)

Updates `typescript-eslint` from 8.65.0 to 8.66.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.66.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: eslint
  dependency-version: 10.8.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: tsx
  dependency-version: 4.23.11
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: typescript-eslint
  dependency-version: 8.66.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-10 23:40:39 +02:00
Will Lillis 543734d286 feat(test): allow corpus tests to use cst outputs
This is already exposed for consumers via the CLI, and is a natural way
to express some test expectations over the sexp form.

Also clean up some repeated logic in the internal test code, and narrow
the cst rendering return type to `std::io::Result` rather than
`anyhow::Result`.
2026-08-09 15:48:37 -05:00
Will Lillis 5fae914f8f fix(query): correctly identify MISSING nodes in queries 2026-08-09 15:48:07 -05:00
Will Lillis 1b916ab754 ci: increase nvim-ts generate max jobs to 10 2026-08-09 12:28:50 -05:00
Will Lillis fad0a62a35 perf(generate): pool parse-table action lists
Every (state, terminal) parse-table entry stored its action list inline as a
32-byte `ParseTableEntry`, but across a grammar those lists are ~98-99% _duplicates_.
The number of distinct lists is a few thousand regardless of grammar size, while
total entries scale into the hundreds of thousands.

Store each unique action list once in a shared `ActionListPool` (a flat arena of
actions plus `(offset, len)` ranges) and replace the inline entry with a 4-byte
`ActionListId` (a pool index with the `reusable` flag packed into the high bit).

- intern_table converts the freshly built `ParseTable<ParseTableEntry>` into
  `ParseTable<ActionListId>`.
- minimize carries and operates on the 4-byte ids. The three global state
  renumberings rewrite Shift targets once at the pool level
  (`remap_terminal_references`), while the per-state unit-reduction redirects
  copy the changed list into a new slot (COW). `mark_fragile_tokens` becomes a
  free bit flip on the id.
- `canonicalize` dedups and compacts the pool once before `render`, dropping the
  dead and duplicate slots the remaps and COW leave behind. `render` assigns the
  output action-list offsets directly.

Yields ~7% wall time reduction, ~12% peak rss reduction.
2026-08-09 12:28:50 -05:00
Will Lillis dbcdd0416b perf(generate): shrink parse/lex/production ids from usize to u32
`ParseStateId`, `LexStateId`, `ProductionInfoId`, and `ReservedWordSetId`
each held a `usize`, while the runtime stores state/field ids as `u16` (so
`u32` has plenty of headroom). Shrinking them saves space for every parse-table
entry, and a number of other data structures.

Reduces wall time by 2-5%, peak rss by 9-12%.
2026-08-09 12:28:50 -05:00
Will Lillis 87d14ddfc6 generate: use the LexStateId typedef for lex state ids
Lex state ids were passed as bare usize in several spots. Route them
through the existing `LexStateId` typedef.
2026-08-09 12:28:50 -05:00
Will Lillis 142c60dca1 perf(generate): memoize auxiliary node info within each state
`get_auxiliary_node_info` scans every entry in the item set to collect the
non-auxiliary parents of a given auxiliary symbol. It was called once per
entry whose next symbol is auxiliary, and the same auxiliary symbol recurs
across many entries in a single state (a state's GOTO on a repeat symbol is
shared by every item advancing over it), so the same full scan was repeated
many times per state. Memoize the result per symbol within a single
`add_actions` call.

Reduces wall time 1-4%, rss flat.
2026-08-09 12:28:50 -05:00
Will Lillis b14413e4f4 perf(generate): inline single-action parse table entries
`ParseTableEntry` held a `Vec<ParseAction>` per entry. `ActionList` keeps up
to one action inline and spills to a Vec only for conflict entries.

Reduces walltime by >20%, peak rss by ~30%.
2026-08-09 12:28:50 -05:00
Will Lillis 2a81995058 perf(generate): memoize get_production_id
The production info depends only on the production, but was recomputed and
deduplicated via a linear deep-equality scan over production_infos for every
reduce item in every state. Cache `prod_id` -> `ProductionInfoId`.

Wall time reduction 0-3%, peak rss neutral.
2026-08-09 12:28:50 -05:00
Will Lillis d6ae19cc6d perf(generate): intern lookahead TokenSets
`ParseItemSetEntry` stores a `LookaheadSetId` into a `LookaheadSetPool` instead
of an owned `TokenSet`. Ids are canonical, so entry hash/eq/clone are integer
ops and state dedup stops walking set words. Unions and single-token inserts
are memoized by id, so the transitive closure and successor-kernel construction
stop re-materializing the same unions per state. Closure additions carry interned
ids with word-token membership precomputed.

For cpp, 3566 distinct lookahead sets back all 49,992 states. For ruby 2000 sets
for 91,991 states, and rust 646 for 16,796.

Reduces wall time by 10-15%, peak rss by 3-5%.
2026-08-09 12:28:50 -05:00
Will Lillis 6659ff57eb perf(generate): vectorize minimize's token_conflicts with precomputed bitsets
The state-merge pass's `token_conflicts` scanned every terminal entry of the
other state per non-shared token. Precompute flat, word-aligned bitsets,
`ConflictBits`, so each call is a handful of word ANDs.

Yields a 5-6% wall time reduction on large grammars, and flat to ~1%
loss on small grammers. Peak RSS unchanged.
2026-08-09 12:28:50 -05:00
Will Lillis b304ac9e56 perf(generate): stop duplicating kernel item sets in parse_state_info
Every state's kernel set was cloned into `parse_state_info` even though the
identical set already lives as the state-dedup map key at the same index
(state ids are the map's insertion order). `ParseStateInfo` now holds the
preceding-symbol sequences plus the dedup map moved out of the builder,
allowing the report path to read kernels via `get_index`. Drops the
duplicate kernel storage, allowing for a small reduction in wall time
and peak rss (1-3%).
2026-08-09 12:28:50 -05:00
Will Lillis 2ce7b409a8 perf(generate): replace coincident state lists with a without-word bitset
`CoincidentTokenIndex` kept a `Vec<ParseStateId>` per token pair whose
only user was keyword identification asking one question: "do all states
where a given pair coincides also allow the word token?" Track that
directly as a second bitset built in the same pass.

Reduces walltime by ~7% for small grammars (i.e. Go), and 20-25% for
larger grammars (i.e. Rust, cpp). Reduces peak rss by ~20% for smaller
grammars and ~40-50% for larger grammars.
2026-08-09 12:28:50 -05:00
Will Lillis 110a8becb8 perf(generate): shrink Symbol::index from usize to u32
This cuts `Symbol`'s size from 16 to 8 bytes. Results in a ~2-7% wall time
reduction, and ~7-10% peak RSS reduction.
2026-08-09 12:28:50 -05:00
Will Lillis fb2052f02d perf(generate): represent the grammar IR as a flat, pooled arena
Replace the owned `Rule`-tree grammar with a flat, pooled representation. `Rule`
nodes live in a `RulePool` arena and reference their children and params by
index (`RuleId`). Strings are interned in a `StrPool` and referred to by `StrId`.
Productions are stored as flat `ProductionStep`/`Production` slices indexed per
variable, rather than as nested vectors hanging off each `SyntaxVariable`. This
drops the per-rule heap allocation of the owned-tree form and is the groundwork
for the later table-building optimizations.

`parse_grammar` builds the pool, the prepare passes rewrite nodes in place, and
`prepare_grammar` returns a `PreparedGrammar` bundle that owns the run's `StrPool`
alongside the grammars. `build_tables`, `node_types`, and `render` borrow that
pool and resolve `StrId`s only at the output boundary.

Shows an average ~10% walltime reduction when combined with the previous
commit, with nearly all of the savings in `build_tables`. The `prepare`
stage is also ~70% faster and much more stable, but this contributes
much less to overall generate time. Improving early stages such as
`prepare` will be important for future interactive uses.
2026-08-09 12:28:50 -05:00
Will Lillis e72c8594fb perf(generate): precompute order-preserving item identity keys
Parse-item identity dominates parse-table construction. `ParseItem`'s
`Hash`, `Eq`, and `Ord` walk the entire production on every call,
comparing per-step precedence, aliases, and field names. These are
stored as strings for named precedences and for every alias and field.

Precompute, once per table build, two dense `u32` ids for every
`(production, dot)` slot by ranking the whole slot universe with the
item-content comparator, now extracted as `ItemContent`. Equal-content
slots are adjacent after sorting and the id increments only at content
boundaries, so comparing the `cmp` id reproduces the structural order
exactly (both an ordering and an equality). A second id, `eq_with_syms`,
refines `cmp` by the preceding symbols, which enter equality only for
items with `has_preceding_inherited_fields`.

`ParseItem` now carries its production's key slice, and `Hash`/`Eq`/`Ord`
become integer ops.

On the current string-based production storage this is performance-neutral
in isolation (the up-front ranking pays back the per-comparison savings).
It is the foundation for the following change to a pooled, integer-keyed
production representation, which makes both the ranking and the item
comparisons cheaper integer work.
2026-08-09 12:28:50 -05:00
Christian Clason 4f7ab22584 ci(release): add nightly builds 2026-08-08 19:09:56 +02:00
Newosko 308aee0c90 fix(lib): continue search for later named siblings in
`ts_tree_cursor_current_status`

By terminating early on `has_later_siblings`, `has_later_named_siblings`
was incorrectly reported as `false` in some cases. This led to the
execution of some queries to terminate early.

Also remove some dead branches inside `ts_tree_cursor_current_status`.

Co-authored-by: Will Lillis <will.lillis24@gmail.com>
2026-08-07 17:54:36 -04:00
Jason Boatman 003b10c280 feat(generate)!: expose OptLevel on generate_parser_for_grammar 2026-08-07 09:47:11 +02:00
Will Lillis 7511e3adaa fix(rust)!: tie query iterators to options lifetime
`QueryMatches` and `QueryCaptures` retain query cursor options while they are
being iterated. However, their types did not preserve the lifetime of the
options' progress callback, allowing the callback to be dropped while it
was still referenced by the cursor.

Add an explicit options lifetime to both iterator types and propagate it
through `matches_with_options` and `captures_with_options`. Use a static
options lifetime for iterators created without options.
2026-08-07 01:15:38 -04:00
Christian Clason 8966c69fcd build(deps): bump wasmtime-c-api to v36.0.13 2026-08-06 01:06:13 -04:00