2626| ` !field ` | Negated field (assert absent) |
2727| ` ? ` ` * ` ` + ` | Quantifiers (0-1, 0+, 1+) |
2828| ` ?? ` ` *? ` ` +? ` | Non-greedy variants |
29- | ` . ` | Anchor (adjacency) |
29+ | ` . ` | Anchor (adjacency, see below) |
3030| ` {...} ` | Sequence (siblings in order) |
3131| ` [...] ` | Alternation (first match wins) |
3232| ` Name = ... ` | Named definition (entrypoint) |
3636
3737- Captures are flat by default: nesting in pattern ≠ nesting in output
3838- ` {...} @x ` or ` [...] @x ` creates a nested scope
39- - Quantifier on captured pattern → array: ` (x)* @a ` → ` a: T[] `
39+ - Scalar list (no internal captures): ` (x)* @a ` → ` a: T[] `
40+ - Row list (with internal captures): ` {(x) @x}* @rows ` → ` rows: { x: T }[] `
41+ - ** Strict dimensionality** : ` * ` /` + ` with internal captures requires row capture
4042
4143## Alternations
4244
@@ -68,6 +70,18 @@ Labeled (tagged union):
6870Nested = (call function: [(id) @name (Nested) @inner])
6971```
7072
73+ ## Anchor Strictness
74+
75+ The ` . ` anchor adapts to what it's anchoring:
76+
77+ | Pattern | Behavior |
78+ | ----------- | ------------------------------------------- |
79+ | ` (a) . (b) ` | Skip trivia, no named nodes between |
80+ | ` "x" . (b) ` | Strict—nothing between (anonymous involved) |
81+ | ` (a) . "x" ` | Strict—nothing between (anonymous involved) |
82+
83+ Rule: anchor is as strict as its strictest operand.
84+
7185## Anti-patterns
7286
7387```
@@ -81,21 +95,21 @@ Nested = (call function: [(id) @name (Nested) @inner])
8195(id) @x (#eq? @x "foo")
8296```
8397
84- ## Type System Gotchas
98+ ## Type System Rules
8599
86- ** Columnar output ** : Quantifiers produce parallel arrays, not list of objects :
100+ ** Strict dimensionality ** : Quantifiers with internal captures require explicit row capture :
87101
88102```
89- {(A) @a (B) @b}* → { a: Node[], b: Node[] } // NOT [{a,b}, {a,b}]
103+ {(a) @a (b) @b}* ; ERROR: internal captures, no row capture
104+ {(a) @a (b) @b}* @rows ; OK: rows: { a: Node, b: Node }[]
105+ (func (id) @name)* ; ERROR: internal capture without row
106+ {(func (id) @name) @f}* @funcs ; OK: funcs: { f: Node, name: Node }[]
90107```
91108
92- For list of objects, wrap in sequence: ` ({(A) @a (B) @b} @row)* `
93-
94- ** Row integrity** : Can't mix ` * ` /` + ` with ` 1 ` /` ? ` in same quantified scope:
109+ ** Optional bubbling** : ` ? ` does NOT require row capture (no dimensionality added):
95110
96111```
97- {(A)* @a (B) @b}* ; ERROR: @a desync, @b sync
98- {(A)? @a (B) @b}* ; OK: both synchronized (? emits null)
112+ {(a) @a (b) @b}? ; OK: a?: Node, b?: Node (bubbles to parent)
99113```
100114
101115** Recursion rules** :
@@ -110,9 +124,7 @@ A = (foo (B)) B = (bar (A)) ; OK: descends each step
110124
111125## ⚠️ Sequence Syntax (Tree-sitter vs Plotnik)
112126
113- Tree-sitter: ` ((a) (b)) ` — Plotnik: ` {(a) (b)} ` . The #1 syntax mistake.
114-
115- ` ((a) (b)) ` in Plotnik means "node ` (a) ` with child ` (b) ` ", NOT a sequence.
127+ Tree-sitter: ` ((a) (b)) ` — Plotnik: ` {(a) (b)} ` . The #1 syntax error.
116128
117129# Architecture Decision Records (ADRs)
118130
@@ -122,10 +134,6 @@ Tree-sitter: `((a) (b))` — Plotnik: `{(a) (b)}`. The #1 syntax mistake.
122134 - _ (no ADRs yet)_
123135- ** Template** :
124136
125- [ ADR-0001] ( docs/adr/ADR-0001-query-parser.md ) | [ ADR-0002] ( docs/adr/ADR-0002-diagnostics-system.md ) | [ ADR-0004] ( docs/adr/ADR-0004-query-ir-binary-format.md ) | [ ADR-0005] ( docs/adr/ADR-0005-transition-graph-format.md ) | [ ADR-0006] ( docs/adr/ADR-0006-dynamic-query-execution.md ) | [ ADR-0007] ( docs/adr/ADR-0007-type-metadata-format.md ) | [ ADR-0008] ( docs/adr/ADR-0008-tree-navigation.md ) | [ ADR-0009] ( docs/adr/ADR-0009-type-system.md ) | [ ADR-0010] ( docs/adr/ADR-0010-type-system-v2.md ) | [ ADR-0012] ( docs/adr/ADR-0012-variable-length-ir.md )
126-
127- ## Template
128-
129137``` markdown
130138# ADR-XXXX: Title
131139
0 commit comments