Memory Model โ
Client owns an allocator (e.g. std.heap.DebugAllocator)
Parser.init(allocator) borrows it for the parser lifetime
parseString copies source bytes into the new Tree
Tree owns its node pool, child index buffer, and source copy
tree.cursor() / parser.queryCursor() inherit the stored allocator
Every deinit releases exactly what its object owns
The handoff rule โ
The client allocator enters the library at exactly one place โ Parser.init โ and is then inherited: trees inherit the parser's allocator, cursors inherit their tree's or parser's, queries inherit the parser's via the convenience constructors. Standalone constructors take an explicit allocator for code that has no parent object to inherit from.
Ownership summary โ
| Object | Owns | Borrows |
|---|---|---|
| Parser | stacks, scratch, candidates, ranges | the allocator itself, the Language tables |
| Tree | source copy, node pool, child indices | the allocator, the Language |
| Node | nothing | its tree |
| TreeCursor | path stack | its tree |
| Query | patterns, captures, predicate args | the allocator, the Language |
| QueryCursor | match lists | the allocator |
| Range slice | nothing (caller-owned) | โ |
Language table data is always borrowed โ grammars are typically comptime-known statics, and the runtime never frees them.
