diff --git a/AGENTS.md b/AGENTS.md index 7e4136c2..2211bd0f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -76,8 +76,8 @@ The `.` anchor adapts to what it's anchoring: | Pattern | Behavior | | ----------- | ------------------------------------------- | | `(a) . (b)` | Skip trivia, no named nodes between | -| `"x" . (b)` | Strict—nothing between (anonymous involved) | -| `(a) . "x"` | Strict—nothing between (anonymous involved) | +| `"x" . (b)` | Strict — nothing between (anonymous involved) | +| `(a) . "x"` | Strict — nothing between (anonymous involved) | Rule: anchor is as strict as its strictest operand. diff --git a/crates/plotnik-core/src/interner.rs b/crates/plotnik-core/src/interner.rs index 361c83a1..87a17694 100644 --- a/crates/plotnik-core/src/interner.rs +++ b/crates/plotnik-core/src/interner.rs @@ -10,7 +10,7 @@ use std::collections::HashMap; /// A lightweight handle to an interned string. /// /// Comparing two symbols is O(1). Symbols are ordered by insertion order, -/// not lexicographically—use `Interner::resolve` if you need string ordering. +/// not lexicographically — use `Interner::resolve` if you need string ordering. #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub struct Symbol(u32); diff --git a/crates/plotnik-lib/src/analyze/type_check/unify.rs b/crates/plotnik-lib/src/analyze/type_check/unify.rs index 9d5b3ce5..e1e13f94 100644 --- a/crates/plotnik-lib/src/analyze/type_check/unify.rs +++ b/crates/plotnik-lib/src/analyze/type_check/unify.rs @@ -1,7 +1,7 @@ //! Unification logic for alternation branches. //! //! Handles merging TypeFlow from different branches of untagged alternations. -//! Tagged alternations don't unify—they produce Enum types directly. +//! Tagged alternations don't unify — they produce Enum types directly. use std::collections::BTreeMap; diff --git a/crates/plotnik-lib/src/parser/mod.rs b/crates/plotnik-lib/src/parser/mod.rs index a19b3f8b..e013f510 100644 --- a/crates/plotnik-lib/src/parser/mod.rs +++ b/crates/plotnik-lib/src/parser/mod.rs @@ -12,7 +12,7 @@ //! //! # Recovery Strategy //! -//! The parser is resilient—it always produces a tree. Recovery follows these rules: +//! The parser is resilient — it always produces a tree. Recovery follows these rules: //! //! 1. Unknown tokens get wrapped in `SyntaxKind::Error` nodes and consumed //! 2. Missing expected tokens emit a diagnostic but don't consume (parent may handle) diff --git a/docs/binary-format/04-types.md b/docs/binary-format/04-types.md index 8608f874..847ea199 100644 --- a/docs/binary-format/04-types.md +++ b/docs/binary-format/04-types.md @@ -21,7 +21,7 @@ This section defines the type system metadata used for code generation and runti | TypeScript | Binding-provided object with `startPosition`, `text`, etc. | | JSON | Unique node identifier (e.g., `"node:42"` or path-based) | -The handle provides access to node metadata (kind, span, text) without copying the source. Lifetime management is platform-specific—Rust enforces it statically, bindings may use reference counting or arena allocation. +The handle provides access to node metadata (kind, span, text) without copying the source. Lifetime management is platform-specific — Rust enforces it statically, bindings may use reference counting or arena allocation. **TypeKind (u8)**: Discriminator for `TypeDef`. diff --git a/docs/binary-format/06-transitions.md b/docs/binary-format/06-transitions.md index 04468bbb..24668a07 100644 --- a/docs/binary-format/06-transitions.md +++ b/docs/binary-format/06-transitions.md @@ -247,7 +247,7 @@ The compiler places effects based on semantic requirements: scope openers often ### 4.3. Epsilon Transitions -A Match instruction with `nav == Epsilon` is an **epsilon transition**—it succeeds unconditionally without cursor movement or node checking. The VM skips navigation and node matching entirely, only executing effects and proceeding to successors. This enables: +A Match instruction with `nav == Epsilon` is an **epsilon transition** — it succeeds unconditionally without cursor movement or node checking. The VM skips navigation and node matching entirely, only executing effects and proceeding to successors. This enables: - **Branching at EOF**: `(a)?` must succeed when no node exists to match. - **Pure control flow**: Decision points for quantifiers. diff --git a/docs/binary-format/07-dump-format.md b/docs/binary-format/07-dump-format.md index 8a3c9e8e..6bb58d4a 100644 --- a/docs/binary-format/07-dump-format.md +++ b/docs/binary-format/07-dump-format.md @@ -23,7 +23,7 @@ They require all three conditions: - `node_field == None` (no field constraint) A step with `nav == Stay` but with a type constraint (e.g., `(identifier)`) is NOT -epsilon—it matches at the current cursor position. +epsilon — it matches at the current cursor position. **Capture effect consolidation**: Scalar capture effects (`Node`, `Text`, `Set`) are placed directly on match instructions rather than in separate epsilon steps. Structural diff --git a/docs/binary-format/08-trace-format.md b/docs/binary-format/08-trace-format.md index dee6badb..43f84bb0 100644 --- a/docs/binary-format/08-trace-format.md +++ b/docs/binary-format/08-trace-format.md @@ -293,7 +293,7 @@ Assignment: 16 ◀ (Assignment) ◼ ``` -First branch (Literal) matches immediately—checkpoint at 28 is never used. +First branch (Literal) matches immediately — checkpoint at 28 is never used. --- @@ -323,7 +323,7 @@ Expression: ○ string hello ``` -Both branches fail. No more checkpoints—query does not match. The CLI exits with code 1. +Both branches fail. No more checkpoints — query does not match. The CLI exits with code 1. --- @@ -421,7 +421,7 @@ Assignment: ○ number 42 ``` -Type check fails at root—no navigation occurs. The CLI exits with code 1. +Type check fails at root — no navigation occurs. The CLI exits with code 1. --- diff --git a/docs/lang-reference.md b/docs/lang-reference.md index 8fae5feb..36117f38 100644 --- a/docs/lang-reference.md +++ b/docs/lang-reference.md @@ -2,7 +2,7 @@ Plotnik is a pattern-matching language for tree-sitter syntax trees. It extends [tree-sitter's query syntax](https://tree-sitter.github.io/tree-sitter/using-parsers/queries/1-syntax.html) with named expressions, recursion, and static type inference. -Predicates (`#eq?`, `#match?`) and directives (`#set!`) are intentionally unsupported—filtering logic belongs in your host language. +Predicates (`#eq?`, `#match?`) and directives (`#set!`) are intentionally unsupported — filtering logic belongs in your host language. --- @@ -51,13 +51,13 @@ Matches `a.b` even if there's a comment like `a /* x */ .b` (trivia skipped), bu (call_expression (identifier) @fn . "(") ; no trivia between name and paren ``` -When any side of the anchor is an anonymous node (literal token), the match is exact—no trivia allowed. +When any side of the anchor is an anonymous node (literal token), the match is exact — no trivia allowed. **Rule**: The anchor is as strict as its strictest operand. Anonymous nodes demand precision; named nodes tolerate trivia. ### Partial Matching -Node patterns are open—unmentioned children are ignored: +Node patterns are open — unmentioned children are ignored: ``` (binary_expression left: (identifier) @left) @@ -78,7 +78,7 @@ Sequences `{...}` advance through siblings in order, skipping non-matching nodes ) ``` -Fields participate in sequential matching—they're not independent lookups. +Fields participate in sequential matching — they're not independent lookups. --- @@ -206,11 +206,11 @@ The pattern is 4 levels deep, but the output is flat. You're extracting specific → { methods: { method: Node, name: Node }[] } ``` -This prevents association loss—each struct is a distinct object, not parallel arrays that lose per-iteration grouping. See [Type System: Strict Dimensionality](type-system.md#1-strict-dimensionality). +This prevents association loss — each struct is a distinct object, not parallel arrays that lose per-iteration grouping. See [Type System: Strict Dimensionality](type-system.md#1-strict-dimensionality). ### The Node Type -Default capture type—a reference to a tree-sitter node: +Default capture type — a reference to a tree-sitter node: ``` interface Node { @@ -297,7 +297,7 @@ Use cases: Rules: - `@_` and `@_name` match like regular captures but produce no output -- Named suppressive captures (`@_foo`) are equivalent to `@_`—the name is documentation only +- Named suppressive captures (`@_foo`) are equivalent to `@_` — the name is documentation only - Type annotations are not allowed on suppressive captures - Nesting works: `@_outer` containing `@_inner` correctly suppresses both @@ -351,7 +351,7 @@ Match named nodes (non-terminals and named terminals) by type: (binary_expression (identifier) (number)) ``` -Children can be partial—this matches any `binary_expression` with at least one `string_literal` child: +Children can be partial — this matches any `binary_expression` with at least one `string_literal` child: ``` (binary_expression (string_literal)) @@ -490,7 +490,7 @@ decorator: (decorator)* @decorators ; repeats the whole field value: [A: (x) B: (y)] @kind ; captures the field (containing the alternation) ``` -This allows repeating fields (useful for things like decorators in JavaScript). The capture still correctly produces the value's type—for alternations, you get the tagged union, not a raw node. +This allows repeating fields (useful for things like decorators in JavaScript). The capture still correctly produces the value's type — for alternations, you get the tagged union, not a raw node. ### Negated Fields @@ -502,7 +502,7 @@ Assert a field is absent with `-`: -type_parameters) ``` -Negated fields don't affect the output type—they're purely structural constraints: +Negated fields don't affect the output type — they're purely structural constraints: ```typescript { @@ -532,7 +532,7 @@ Output types: { decorators: [Node, ...Node[]] } ``` -The `+` quantifier always produces non-empty arrays—no opt-out. +The `+` quantifier always produces non-empty arrays — no opt-out. Plotnik also supports non-greedy variants: `*?`, `+?`, `??` @@ -745,7 +745,7 @@ interface Target { ## Anchors -The anchor `.` constrains sibling positions. Anchors don't affect types—they're structural constraints. +The anchor `.` constrains sibling positions. Anchors don't affect types — they're structural constraints. ### Anchor Strictness @@ -758,7 +758,7 @@ Anchor behavior depends on the node types being anchored: | `(a) . "x"` | Disallowed | Disallowed | | `"x" . "y"` | Disallowed | Disallowed | -When anchoring named nodes, trivia (comments, whitespace) is skipped but no other named nodes may appear between. When any operand is an anonymous node (literal token), the anchor enforces exact adjacency—nothing in between. +When anchoring named nodes, trivia (comments, whitespace) is skipped but no other named nodes may appear between. When any operand is an anonymous node (literal token), the anchor enforces exact adjacency — nothing in between. ### Position Anchors @@ -792,7 +792,7 @@ Here, no trivia is allowed between the function name and the opening parenthesis ### Output Types -Anchors are structural constraints only—they don't affect output types: +Anchors are structural constraints only — they don't affect output types: ```typescript { first: Node } @@ -835,7 +835,7 @@ The rules: - **Boundary anchors** (at start/end of sequence) need a parent named node to provide first/last child or adjacent sibling semantics - **Interior anchors** (between items in a sequence) are always valid because both sides are explicitly defined -- **Alternations** cannot contain anchors directly—anchors must be inside a branch expression +- **Alternations** cannot contain anchors directly — anchors must be inside a branch expression --- diff --git a/docs/runtime-engine.md b/docs/runtime-engine.md index 752374d4..e6ed3102 100644 --- a/docs/runtime-engine.md +++ b/docs/runtime-engine.md @@ -6,7 +6,7 @@ Executes compiled query graphs against Tree-sitter syntax trees. See [06-transit ```rust struct VM<'t> { - cursor: TreeCursor<'t>, // Never reset—preserves descendant_index for O(1) backtrack + cursor: TreeCursor<'t>, // Never reset — preserves descendant_index for O(1) backtrack ip: StepId, // Current step index frames: Vec, // Call stack effects: EffectLog<'t>, // Side-effect log @@ -43,7 +43,7 @@ Fetch step at `ip` → dispatch by `type_id` → execute → update `ip`. ### Epsilon Transitions -A `Match8` or `Match16–64` with `node_type: None`, `node_field: None`, and `nav: Stay` is an **epsilon transition**—it succeeds unconditionally without cursor interaction. This enables pure control-flow decisions (branching for quantifiers) even when the cursor is exhausted (EOF). +A `Match8` or `Match16–64` with `node_type: None`, `node_field: None`, and `nav: Stay` is an **epsilon transition** — it succeeds unconditionally without cursor interaction. This enables pure control-flow decisions (branching for quantifiers) even when the cursor is exhausted (EOF). Common patterns: @@ -97,7 +97,7 @@ struct Frame { } ``` -"Pop" just moves `current`—frames remain for checkpoint restoration. +"Pop" just moves `current` — frames remain for checkpoint restoration. ### Pruning @@ -110,7 +110,7 @@ arena.truncate(high_water + 1) Bounds arena to O(max_checkpoint_depth + current_call_depth). -**O(1) Invariant**: The checkpoint stack maintains `max_frame_ref`—the highest `frame_index` referenced by any active checkpoint. +**O(1) Invariant**: The checkpoint stack maintains `max_frame_ref` — the highest `frame_index` referenced by any active checkpoint. | Operation | Invariant Update | Complexity | | --------- | ---------------------------------------------------- | -------------- | @@ -185,7 +185,7 @@ The `Node` and `Text` variants carry the actual `tree_sitter::Node` so the mater ### Bytecode vs Runtime Effects -**Bytecode** (`EffectOp` in `bytecode/effects.rs`): Compact 2-byte encoding with 6-bit opcode + 10-bit payload. No embedded data—the `Node` opcode signals "capture `matched_node`" but doesn't carry it. +**Bytecode** (`EffectOp` in `bytecode/effects.rs`): Compact 2-byte encoding with 6-bit opcode + 10-bit payload. No embedded data — the `Node` opcode signals "capture `matched_node`" but doesn't carry it. **Runtime** (`RuntimeEffect`): The VM interprets bytecode effects and produces runtime effects with embedded data. When the VM executes a bytecode `Node` effect, it emits `RuntimeEffect::Node(matched_node)`. @@ -204,4 +204,4 @@ Exhaustion returns `RuntimeError`, not panic. ## Trivia Handling -Per-language trivia list used for `*Skip` navigation. A node is never skipped if it matches the current target—`(comment)` still matches comments. +Per-language trivia list used for `*Skip` navigation. A node is never skipped if it matches the current target — `(comment)` still matches comments. diff --git a/docs/tree-navigation.md b/docs/tree-navigation.md index a0c37801..f536ba9b 100644 --- a/docs/tree-navigation.md +++ b/docs/tree-navigation.md @@ -23,7 +23,7 @@ struct Checkpoint { } ``` -**Critical constraint**: The cursor must be created at the tree root and never call `reset()`. The `descendant_index` is relative to the cursor's root—`reset(node)` would invalidate all checkpoints. +**Critical constraint**: The cursor must be created at the tree root and never call `reset()`. The `descendant_index` is relative to the cursor's root — `reset(node)` would invalidate all checkpoints. ### Why TreeCursor @@ -35,10 +35,10 @@ struct Checkpoint { | `descendant_index()` | O(1) | — | | `goto_descendant()` | O(depth) | — | -The `Node` API's `next_sibling()` is O(siblings)—unacceptable for repeated backtracking. TreeCursor provides O(1) sibling traversal and 4-byte checkpoints via `descendant_index`. +The `Node` API's `next_sibling()` is O(siblings) — unacceptable for repeated backtracking. TreeCursor provides O(1) sibling traversal and 4-byte checkpoints via `descendant_index`. - Checkpoint save: O(1) -- Checkpoint restore: O(depth)—cold path only +- Checkpoint restore: O(depth) — cold path only ## Nav Encoding @@ -88,7 +88,7 @@ Each mode defines what happens when a match fails: | Mode | Constraint | | ----------------- | --------------------------------------------- | -| `Up(n)` | None—just ascend n levels | +| `Up(n)` | None — just ascend n levels | | `UpSkipTrivia(n)` | Must be at last non-trivia child, then ascend | | `UpExact(n)` | Must be at last child, then ascend | @@ -185,7 +185,7 @@ Using dump format from [07-dump-format.md](binary-format/07-dump-format.md): 05 *↑³ ◼ ``` -Multi-level `Up(n)` coalesces ascent when no intermediate anchors exist. Not yet implemented—currently emits individual `Up(1)` steps. +Multi-level `Up(n)` coalesces ascent when no intermediate anchors exist. Not yet implemented — currently emits individual `Up(1)` steps. **Mixed anchors**: `(a (b) . (c) .)` @@ -249,7 +249,7 @@ for &fid in pattern.neg_fields { } ``` -Both constraints participate in the skip policy—a mismatch triggers retry (for `*`), fail-if-non-trivia (for `~`), or immediate fail (for `.`). +Both constraints participate in the skip policy — a mismatch triggers retry (for `*`), fail-if-non-trivia (for `~`), or immediate fail (for `.`). ## Call Navigation diff --git a/docs/type-system.md b/docs/type-system.md index c2fbc13b..979a6cba 100644 --- a/docs/type-system.md +++ b/docs/type-system.md @@ -10,7 +10,7 @@ Two principles guide the type system: 1. **Flat structure**: Captures bubble up to the nearest scope boundary. -2. **Strict dimensionality**: Quantifiers (`*`, `+`) containing captures require a struct capture. The alternative—parallel arrays—loses per-iteration association between `a[i]` and `b[i]`. +2. **Strict dimensionality**: Quantifiers (`*`, `+`) containing captures require a struct capture. The alternative — parallel arrays — loses per-iteration association between `a[i]` and `b[i]`. ### Why Transparent Scoping @@ -24,7 +24,7 @@ Extracting a pattern into a definition shouldn't change output: // Extracted Func = (function name: (identifier) @name) (Func) -→ { name: Node } // Same shape—@name bubbles through +→ { name: Node } // Same shape — @name bubbles through ``` If definitions created implicit boundaries, extraction would wrap output in a new struct, breaking downstream types. @@ -105,7 +105,7 @@ The strict rule forces you to think about structure upfront. ### Optional Bubbling -The `?` quantifier does **not** add dimensionality—it produces at most one value, not a list. Therefore, optional groups without captures are allowed: +The `?` quantifier does **not** add dimensionality — it produces at most one value, not a list. Therefore, optional groups without captures are allowed: ``` { (decorator) @dec }? @@ -215,7 +215,7 @@ Within each struct, inner quantifiers apply to fields: → { items: { decs: Node[], fn: Node }[] } ``` -Each struct has its own `decs` array—no cross-struct mixing. +Each struct has its own `decs` array — no cross-struct mixing. ## 5. Type Unification in Alternations @@ -257,7 +257,7 @@ When a quantified capture appears in some branches but not others, the missing b ] // x: Node[] ``` -Untagged alternations are "I don't care which branch matched"—so distinguishing "branch didn't match" from "matched zero times" is irrelevant. The empty array is easier to consume downstream. +Untagged alternations are "I don't care which branch matched" — so distinguishing "branch didn't match" from "matched zero times" is irrelevant. The empty array is easier to consume downstream. When types start to conflict, use tagged alternations: