Skip to content

Queries โ€‹

What you'll learn โ€‹

  • The supported query syntax and how matching works.

Query syntax at a glance โ€‹

scheme
(identifier) @id            ; named node with a capture
"+" @plus                   ; anonymous token
(_) @any                    ; wildcard: any named node
(expression left: (_) @l)   ; field-constrained child
(term)?                     ; ? * + quantifiers on any node
((identifier) @x (#eq? @x "foo")) ; grouped pattern with predicate

Top-level patterns match at any depth; nested patterns require a direct parent-child relationship. Sibling patterns match in order (gaps allowed unless . anchors are used).

Complete example โ€‹

examples/query.zig over total + price * count:

zig
var query = try parser.compileQuery("(identifier) @var");
defer query.deinit();

var cursor = parser.queryCursor();
defer cursor.deinit();
try cursor.execute(treesitter.expression_language, query.patterns(), query.nodes(), query.captureNames(), &tree);
while (cursor.nextMatch()) |m| {
    for (m.captures) |cap| {
        std.debug.print("@{s}: {s} [{d}, {d}]\n", .{ cap.name, cap.node.text(), cap.node.startByte(), cap.node.endByte() });
    }
}

Expected output โ€‹

text
@var: total [0, 5]
@var: price [8, 13]
@var: count [16, 21]

How it works โ€‹

text
Source โ†’ Parser โ†’ Tree โ†’ Query โ†’ QueryCursor โ†’ Matches โ†’ Captures โ†’ Application
  1. compileQuery parses the S-expression source once into flat pattern nodes, capture names, predicates, #set! settings, and general directives. Compile errors (UnexpectedToken, InvalidPredicate, InvalidCapture for undeclared predicate captures) are reported with no partial state.
  2. execute walks the tree depth-first, tries every root pattern at every node, and records captures into reusable scratch space. Pattern symbol names resolve to ids once per execution, so hot matching compares integers.
  3. Predicates filter matches after structural matching (eq/match default to all-captured-nodes; any- variants need one).

Memory ownership โ€‹

Queries own their compiled representation; cursors own their match lists. Both inherit the parser's allocator via the convenience constructors and are reusable across trees.

API used โ€‹

Released under the MIT License.