@omgbase/oqx 0.18.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +177 -64
  3. package/dist/ast.d.ts +28 -5
  4. package/dist/ast.d.ts.map +1 -1
  5. package/dist/ast.js +1 -1
  6. package/dist/ast.js.map +1 -1
  7. package/dist/build.d.ts +11 -2
  8. package/dist/build.d.ts.map +1 -1
  9. package/dist/build.js +10 -3
  10. package/dist/build.js.map +1 -1
  11. package/dist/context.d.ts +19 -5
  12. package/dist/context.d.ts.map +1 -1
  13. package/dist/context.js +6 -9
  14. package/dist/context.js.map +1 -1
  15. package/dist/engine.d.ts +12 -1
  16. package/dist/engine.d.ts.map +1 -1
  17. package/dist/engine.js +263 -84
  18. package/dist/engine.js.map +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +2 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/lexer.d.ts +3 -3
  23. package/dist/lexer.d.ts.map +1 -1
  24. package/dist/lexer.js +79 -2
  25. package/dist/lexer.js.map +1 -1
  26. package/dist/optimize/analysis.d.ts +3 -2
  27. package/dist/optimize/analysis.d.ts.map +1 -1
  28. package/dist/optimize/analysis.js +18 -15
  29. package/dist/optimize/analysis.js.map +1 -1
  30. package/dist/parser.d.ts.map +1 -1
  31. package/dist/parser.js +250 -118
  32. package/dist/parser.js.map +1 -1
  33. package/dist/print.d.ts.map +1 -1
  34. package/dist/print.js +47 -29
  35. package/dist/print.js.map +1 -1
  36. package/dist/resolve.d.ts.map +1 -1
  37. package/dist/resolve.js +15 -8
  38. package/dist/resolve.js.map +1 -1
  39. package/dist/version.d.ts +1 -1
  40. package/dist/version.js +1 -1
  41. package/dist/walk.d.ts +5 -3
  42. package/dist/walk.d.ts.map +1 -1
  43. package/dist/walk.js +34 -11
  44. package/dist/walk.js.map +1 -1
  45. package/package.json +1 -1
  46. package/src/ast.ts +33 -6
  47. package/src/build.ts +12 -8
  48. package/src/context.ts +22 -11
  49. package/src/engine.ts +258 -91
  50. package/src/index.ts +2 -1
  51. package/src/lexer.ts +76 -6
  52. package/src/optimize/analysis.ts +17 -12
  53. package/src/parser.ts +227 -101
  54. package/src/print.ts +44 -16
  55. package/src/resolve.ts +15 -7
  56. package/src/version.ts +1 -1
  57. package/src/walk.ts +39 -14
package/CHANGELOG.md CHANGED
@@ -4,6 +4,36 @@ All notable changes to `@omgbase/oqx` are recorded here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
5
5
  [Semantic Versioning](https://semver.org/) (pre-1.0: a minor bump may break).
6
6
 
7
+ ## [0.21.0] - 2026-10-13
8
+
9
+ ### Minor
10
+ - **OQX 0.21 — template literals** (`spec/oqx` 0.20 → 0.21; new suite `template-literals` (50 cases) + 2 in `errors-eval`: 1258 → 1310 fixtures). `` `text ${expr} text` `` is a backtick-delimited **string value** with `${…}` interpolations, usable wherever a value stands — a `select` item (`` label: `${title} (${era})` ``), a `where` operand, an `order by` key, a function argument or method receiver, the haystack of `in`, a nested block's item reading the enclosing row (`` jobs collect { label: `${^title}: ${employer}` values } ``). Semantics (SEMANTICS §7b): the text and each interpolated value's **string form** (§7: a number without a fraction when integer-valued, `true`/`false` as the words) concatenated; **absent propagates** — any absent interpolation makes the whole literal absent, as `+` does; **a list, a record or a range cannot be interpolated** — an eval error `cannot interpolate … it is a list / a record / a range` (where `+` leaves such operands unspecified, a template literal refuses them); every interpolation is evaluated, in order, before the result is decided. Lexically (GRAMMAR §1, §4): the text decodes with the **same escapes as a string** (`\n` `\t` `\r` `\0`, `\<c>` for any other `<c>` — so `` \` `` keeps a backtick, `\$` a dollar that opens nothing, `\\` a backslash); **newlines are kept verbatim** (a literal may span lines); a `$` not followed by `{`, braces outside an interpolation and quotes are text; an interpolation holds one full expression with **balanced braces**, so it may hold a `{ … }` block (`` `${jobs single { employer values where primary }}` ``) or another template literal, to any depth, read at the enclosing scope depth (`^`, `N^` as usual). Errors: `unterminated template literal at N` and `` unterminated ${…} interpolation … `` are lex errors; `` empty interpolation `${}` `` (offset of the `${`) and `expected '}' to close the `${…}` interpolation` are parse errors; a template literal is a literal — not a receiver, not a bracket receiver, and an unaliased item needs an alias. **Bindings**: a tagged-template fragment that ends inside a backtick literal is the lex error `unterminated template literal … a ${…} binding cannot stand inside a backtick literal` (fragments lex independently). **AST** (AST.md): new node `template { parts: (string | Expr)[] }` with `parts` strictly alternating decoded text strings and expressions (odd length, text first and last: `` `${a}` `` is `["", a, ""]`); the text parts are plain strings, not nodes — `visit`/`transform` skip them; `CHILDREN.template = [parts]`; `isExpr` true; `build.template(parts)`; a `template` node spans its backticks, each interpolated expression itself. **Printer**: a template literal prints as one, its text escaped (`\\`, `` \` ``, `\$` before a `{`, the control escapes) and each interpolation as `${…}`; round-trip law holds over every fixture. Lexer: new token types `tmpl_start` / `tmpl_text` / `tmpl_interp_start` / `tmpl_interp_end` / `tmpl_end`, a mode stack per fragment; the backtick is no longer an `unexpected character`. Planners: the SQLite adapter leaves a template residual (not translatable); `exprRaiseFree` is false for a literal with an interpolation (the data decides whether it raises). `LANGUAGE_VERSION` 0.21.
11
+ - `@omgbase/oqx-syntax`: `LANGUAGE_VERSION` 0.21; new token classes `template` (`string.template.oqx`) and `interpolation` (`punctuation.section.interpolation.oqx`); every grammar enters a template state at the backtick, re-enters the language at `${` and tracks the braces of the interpolation (TextMate begin/end rules `template` / `template-interpolation` / `template-block`, Monarch states `template` / `templateInterpolation` / `templateBlock`, a mode stack in the CodeMirror stream state, a Prism `template` token whose interpolations re-enter the grammar through `rest`, braces balanced to three levels); the backtick left `ILLEGAL_CHARACTERS`; `TEMPLATE` / `TEMPLATE_LITERAL` exported; backtick auto-closing pairs; assets regenerated; parity over every fixture.
12
+
13
+ ## [0.20.0] - 2026-10-11
14
+
15
+ ### Minor
16
+ - OQX 0.20 (`spec/oqx` 0.19 → 0.20; 1188 → 1258 fixtures, three new suites `tuples`, `identity`, `cardinality`):
17
+
18
+ - **`follow` loses its options block.** A brace after *any* destination opens that destination's block (`follow children { depth 2 }`, `follow a, children { id != "root" }`, `follow entries(kids) { where … }`), with the 0.17 body rules. Two clauses belong to a destination block alone, after `where` in the fixed order: `depth <1..8>` (this destination expands a frontier row only while its `$depth < n`) and `frontier <expr>` (an occurrence reached through this destination for which it holds is a frontier; read with `^` the walk's enclosing scope; seeds are never frontiers). The former `where` option is the block's `where`; `by` is gone. When several destinations reach a row in one step the first governs the occurrence. **The body that carries the `follow` may carry `frontier <expr>` too** (Brendan, 2026-10-11): it applies to the SEEDS — a seed for which it holds (read in the seed's own scope, `^` the enclosing scope, like `where`) is `$stop == "frontier"` and is not expanded, while it stays in the result — so every block that feeds rows into the walk says which of its rows are frontiers, and the frontier relevant to the current `$it` is defined separately from each destination's. The clause order of every body is `select from where frontier follow order by limit offset` (a destination block's adds `depth` after `frontier`); a body-level `frontier` without a `follow` is the parse error `` `frontier` needs a `follow` ``, a body-level `depth` an error naming the destination block; inside a destination block `frontier` is always the destination's. AST: `follow` keeps only `destinations`/`distinct`; `subquery` gains `frontier`/`depth`, `query` gains `frontier`; `print` writes `follow x collect { where … depth 2 }` and a body's `frontier` between `where` and `follow`.
19
+ - **Tuples.** `(e1, e2, …)` with two or more elements is a value and a receiver: the elements concatenated with one level of flattening (a collection contributes its items, a row itself; an absent element nothing, a `null` is kept; duplicates kept). `size((a, b))`, `"x" in (tags, aliases)`, `(mom, dad, siblings) { name values }`, `follow (before, after) { depth 2 }` (one destination over the union). New AST node `tuple { items }`.
20
+ - **Identity is `$key`.** One meaning — the key a row is known by — for `follow` cycle detection, the per-step union, `follow distinct`, `distinct` over unprojected rows, `is`, and reading `$key` on any row. The default is the host's identity through a new `DataContext` seam (TS: optional `identityOf(row)`, replacing the required `identity`, which stays as a deprecated alias for one minor; Rust: `identity` now has a structural default), else the row's structural value; an entry's identity is its key. The language never names an `id` property: the "`id` if present" rule is removed, and the in-memory `DefaultContext` names no identity. A block may project `$key: <expr>` to give the rows it yields that identity (`select name, $key: email from people follow next { $key: email }`); in a destination block `$key:` is not a column. Caveat: identities compare as values across the whole walk, so destinations that can reach the same rows should project the same `$key`.
21
+ - **Result shape by receiver cardinality.** In row position an array is a collection and a non-array object is one row: a `collect` over it — `doc { title }`, `title from doc`, a nested `d: dst { title }` — yields the one record (or absent when filtered out; `doc { status == "active" }` is a conditional reach-through), not a one-element list; `values` the one value; `first`/`single` that object. A scalar in row position is an eval error (`` `s` is a string ("abc"), not a collection or an object ``) instead of a one-row collection — `list(s)` says it explicitly. A `from` re-projection or a `follow` makes the result plural. Hosts say which of their objects are collections through the new optional `DataContext.isSingle` (TS; default: a non-iterable object is one row) / `is_single` (Rust; default `true`). TS `OqxResult` for `collect` gains `single: boolean` (`unwrap` yields the record or `null`); Rust gains `OqxResult::Record(Option<Value>)`.
22
+ - **Sugar closures.** `x { … }` ≡ `x collect { … }` wherever a value is accepted, function arguments included (`size(children { is checked })`); `x distinct { … }` ≡ `x collect distinct { … }`.
23
+
24
+ omgbase binding (surface stays 3.0; the dialect follows the language):
25
+
26
+ - `DataContext.identityOf` (TS) / `identity` (Rust) answers a store row's id for every kind (`doc_id`, `block_id`, `node_id`, `edge_id`), so `$key` on any hit is its id and walks dedup by it as before; the binding no longer relies on the removed `id`-property rule.
27
+ - The statically single relations `doc`, `block` and `dst` hand out an absent-single marker when they have no row, so `dst { title }` on a link row is one record and, on an external or dangling link, absent — not `[]`; as a value the marker is absent (`dst == null`, `has(dst)`, `dst.title` unchanged). The Rust context's `is_single` marks the lazy root scans as collections.
28
+ - The `graph` tool compiles to `follow distinct <dir>_links collect { from <dst|doc> depth <n> }`; `query_syntax`, the `query` tool description, both help cards, `docs/query-language.md`, `docs/graph-and-query.md`, `docs/mcp-api.md` and the OQX tutorial describe the 0.20 forms (body-level `frontier` included); the planners treat a destination block's `frontier`/`depth` as its own, never push a tuple down, and a body with `frontier` plans like one with a `follow` — in memory (pinned on both sides). `spec/surface` `query-follow::body-frontier-keeps-seeds-unexpanded` walks the processes with the unverified one kept but not expanded.
29
+ - `spec/surface` fixtures using the old options block are rewritten to the destination-block forms (`… collect { from dst depth 2 }`; `by type` → `{ $key: type from dst }`) and regenerated; `spec/cli` and `interop.json` regenerated with them.
30
+ - `@omgbase/oqx-syntax`: `LANGUAGE_VERSION` 0.20; `FOLLOW_OPTIONS` → `DESTINATION_CLAUSES` (`frontier`, `depth`; `by` is no longer a word of the language), assets regenerated.
31
+
32
+ ## [0.19.0] - 2026-10-10
33
+
34
+ ### Minor
35
+ - OQX language 0.19 — outer-reference `follow` destinations take the implicit `collect`. A `follow` destination whose receiver is headed by an outer reference (`^docs`, `^^root.rel`, `0^docs`, `^$it.docs`, with any member chain) can never be a plain destination (plain destinations are relations of the current row), so a `{` right after it now opens a **destination block** with the implicit `collect`: `follow refs(before), ^docs { where ^$path in list(after) }` walks `before` references forward and, backward, every document whose `after` names the frontier row — exactly `^docs collect { … }` with the 0.17 body rules (where-first, projections). Until 0.18 that query failed with the outer-reference error, because the 0.17 carve-out reserved a brace after the last destination for the options block. Any other receiver (a bare relation, a call like `refs(before)` or `entries(kids)`, a binding) keeps that reading: a brace after it is the options block when it is the last destination (`follow children { depth 2 }`, `follow a, children { depth 2 }`) and the "a destination block needs its consumer" error when the brace holds a predicate. An options block may still follow a trailing destination block (`follow ^docs { where … } { depth 2 }`). A plain `follow ^children` (no brace, no consumer) is still an error; its message now names both block spellings: `` `follow` takes a relation of the current row (`follow <relation>`); an outer reference walks a root collection only as a destination block — `follow ^docs { where … }` or `follow ^docs collect { … }` ``. Fixtures: 25 new cases across `follow`, `sugar`, `errors-parse` and `ast` (1188 total); the 0.17 sugar case "an outer-reference destination with a brace is the outer-reference error" is replaced by its positive form. `LANGUAGE_VERSION` is 0.19 in both engines and in `@omgbase/oqx-syntax`; the `query_syntax` reference (`@omgbase/core`, `omgbase-surface`) describes the new rule.
36
+
7
37
  ## [0.18.0] - 2026-10-10
8
38
 
9
39
  ### Minor
package/README.md CHANGED
@@ -34,7 +34,7 @@ const company = "Globocorp";
34
34
  const employees = oqx`
35
35
  name, id, title
36
36
  from ${people}
37
- where jobs exists { where employer == ${company} && !end_date }
37
+ where jobs exists { where employer == ${company} and not end_date }
38
38
  `;
39
39
  // → [{ name: "Bob", id: 124, title: "Engineer" }, …] (current Globocorp employees)
40
40
  ```
@@ -56,8 +56,8 @@ once, and every one is optional except `from` at the top level:
56
56
  ```
57
57
  [ [select] projection ] name, id, title: label (or: select distinct …, … values)
58
58
  from <collection> from ${people}
59
- [ where <predicate> ] where age >= 18 && jobs exists { where !end }
60
- [ follow <dest>, … ] follow children { depth 4 } (or: follow before, after; follow ^people collect { … })
59
+ [ where <predicate> ] where age >= 18 and jobs exists { where not end }
60
+ [ follow <dest>, … ] follow children { depth 4 } (or: follow before, after; follow ^people { where … })
61
61
  [ order by <expr> … ] order by age desc, name
62
62
  [ limit N ] limit 10
63
63
  [ offset N ] offset 20
@@ -200,19 +200,22 @@ const flags = { beta: { on: true }, legacy: { on: false } };
200
200
  oqx`$key values from entries(${flags}) where on`; // ["beta"] (bare `on` reads the value)
201
201
 
202
202
  oqx`name, on: entries(prefs) collect { $key values where $it } from ${users}`; // as a nested receiver
203
- oqx`name from ${users} where entries(prefs) exists { where $key == "dark" && $it }`;
203
+ oqx`name from ${users} where entries(prefs) exists { where $key == "dark" and $it }`;
204
204
  ```
205
205
 
206
206
  `entries(array)` yields numeric index keys, a `Map` yields its entries, and
207
- absence/scalars yield nothing. `$key` exists **only** in an entry scope — an
208
- ordinary row or array element has no implicit index; `entries(arr)` is how you
207
+ absence/scalars yield nothing. An entry's `$key` is its key — and since
208
+ language 0.20 `$key` is the **identity** every row is known by (§9): an
209
+ ordinary row's `$key` is the host's identity, structurally the row itself for
210
+ plain data; an array element has no implicit index — `entries(arr)` is how you
209
211
  ask for one. As a plain value (not a source) `entries(x)` is an array of
210
212
  `{ key, value }` records.
211
213
 
212
214
  ### 3. Predicates (where)
213
215
 
214
216
  `where` filters rows. The predicate language has comparisons (`== != < <= > >=`),
215
- boolean operators (`&& || !`) with grouping `( )`, membership (`in`), arithmetic
217
+ boolean operators — the words `and`, `or`, `not`; the symbols `&& || !` are
218
+ accepted synonyms — with grouping `( )`, membership (`in`), arithmetic
216
219
  (`+ - * / %`), and bare truthiness. The `where` keyword is always written — a
217
220
  leading expression without it is a projection, and a bare comparison in that
218
221
  position is an error that points you at `where`.
@@ -319,7 +322,7 @@ from a row nested one level deeper. A receiver may start with `^` too, which is
319
322
  how a nested consumer runs over a named root or an enclosing row's relation:
320
323
 
321
324
  ```js
322
- execute("name, peers: ^people collect { name where city == ^city && name != ^name } from people", { people });
325
+ execute("name, peers: ^people collect { name where city == ^city and name != ^name } from people", { people });
323
326
  ```
324
327
 
325
328
  **Correlated subqueries.** Because inner and outer rows often share names, the
@@ -334,7 +337,7 @@ const family = [
334
337
  ];
335
338
  oqx`
336
339
  name,
337
- siblings: ${family} collect { name where parent == ^parent && name != ^name }
340
+ siblings: ${family} collect { name where parent == ^parent and name != ^name }
338
341
  from ${family}
339
342
  `;
340
343
  // [{ name: "Ada", siblings: [{ name: "Ben" }] },
@@ -357,7 +360,7 @@ Methods on a value: `contains`, `startsWith`, `endsWith`, `matches` (regex),
357
360
  ```js
358
361
  oqx`name from ${people} where title.startsWith("Eng")`; // → Bob
359
362
  oqx`name from ${people} where title.lower() == "director"`; // → Alice
360
- oqx`name from ${people} where has(age) && !has(nickname)`; // present vs absent
363
+ oqx`name from ${people} where has(age) and not has(nickname)`; // present vs absent
361
364
  oqx`name from ${people} where tags.contains("admin")`; // array membership
362
365
  ```
363
366
 
@@ -591,8 +594,10 @@ Reached rows expose recursion **intrinsics** in `select` / `order by`:
591
594
  graph beyond — a boundary or the depth cap), `$ordinal` (a deterministic 1..N
592
595
  rank over the walk, ordered by depth then path), and `$stop`
593
596
  (`"interior"` / `"leaf"` / `"frontier"` / `"depth"` / `"cycle"`, precedence
594
- cycle > frontier > depth > leaf > interior — only `interior` rows expand). Options
595
- go in a trailing block:
597
+ cycle > frontier > depth > leaf > interior — only `interior` rows expand). A
598
+ brace after a destination opens that destination's **block** (language 0.20),
599
+ whose body has two clauses of its own after `where`: `depth <n>` and
600
+ `frontier <pred>`:
596
601
 
597
602
  ```js
598
603
  oqx`id, stop: $stop from ${tree} follow children { depth 2 } order by $ordinal`;
@@ -604,10 +609,10 @@ a nested block (`kids: children collect { … }`) a bare `$depth` is the nested
604
609
  row's own `$depth` property (absent unless the data carries one), and the
605
610
  occurrence's depth is `^$depth`. The same holds everywhere the metadata is not
606
611
  present — outside a `follow`, `$depth`/`$ordinal`/`$stop`/`$leaf`/`$frontier`
607
- are ordinary property reads (a row `{ "$depth": 3 }` projects `3`), and `$key`
608
- is likewise an ordinary property read outside an `entries()` scope. Where the
609
- metadata exists it wins over a same-named property (language 0.13; before, the
610
- six names read absent everywhere they were not metadata).
612
+ are ordinary property reads (a row `{ "$depth": 3 }` projects `3`). `$key` is
613
+ the exception: it is the row's identity everywhere (below). Where the metadata
614
+ exists it wins over a same-named property (language 0.13; before, the six names
615
+ read absent everywhere they were not metadata).
611
616
 
612
617
  The walk is **per-path**: a node reached by N distinct paths yields N
613
618
  occurrences, and revisiting an identity already on the current path is admitted
@@ -616,42 +621,134 @@ runaway. `follow distinct` collapses occurrences to reached nodes (the minimal
616
621
  `(depth, path)` per identity).
617
622
 
618
623
  **Destinations** (language 0.14) are comma-separated; the walk is their union.
619
- A **plain destination** is a relation of the current row (`children`, `doc.out`
620
- — an outer reference `^rel` or a literal is a parse error). A **destination
621
- block** is a receiver followed by `collect`, `first` or `single` (optionally
622
- `distinct`) and a block — the same directive you would write in `select`,
623
- re-evaluated per frontier row with `^` bound to that row. Its receiver *may* be
624
- an outer reference, so a walk can compute successors from a named root:
624
+ A **plain destination** is a relation of the current row (`children`, `doc.out`,
625
+ `entries(kids)`, a tuple `(before, after)` — one destination over the union of
626
+ its elements; an outer reference `^rel` on its own or a literal is a parse
627
+ error). A **destination block** is a receiver followed by a block — `children {
628
+ depth 2 }`, `^people { where manager == ^id }`, `(before, after) { depth 2 }`,
629
+ or with its consumer, `^chain first distinct { … }` — the same directive you
630
+ would write in `select`, re-evaluated per frontier row with `^` bound to that
631
+ row. Its receiver *may* be an outer reference, so a walk can compute successors
632
+ from a named root. Since language 0.20 **a brace after any destination opens
633
+ that destination's block** (there is no options block any more):
625
634
 
626
635
  ```js
627
636
  const people = [{ id: 1, manager: null }, { id: 2, manager: 1 }, { id: 3, manager: 1 }, { id: 4, manager: 2 }];
628
- oqx`id values from ${people} where id == 1 follow ^people collect { where manager == ^id } order by $ordinal`;
637
+ oqx`id values from ${people} where id == 1 follow ^people { where manager == ^id } order by $ordinal`;
629
638
  // [1, 2, 3, 4] — the reporting tree under person 1 (`^people` names the root from a top-level walk)
639
+ oqx`id values from ${people} where id == 1 follow ^people { where manager == ^id depth 2 } order by $ordinal`;
640
+ // [1, 2, 3] — `depth` is a clause of the destination block, after its `where`
630
641
 
631
642
  const chain = [{ id: "a" }, { id: "b", prev: "a" }, { id: "c", prev: "b" }];
632
- oqx`id values from ${chain} where id == "b" follow distinct ^chain collect { where id == ^prev }, ^chain collect { where prev == ^id } order by $ordinal`;
643
+ oqx`id values from ${chain} where id == "b" follow distinct ^chain { where id == ^prev }, ^chain { where prev == ^id } order by $ordinal`;
633
644
  // ["b", "a", "c"] — both directions of a chain; a block may order, bound and `distinct` its rows
634
645
  ```
635
646
 
636
- A row's successors are, per destination in order, that destination's rows,
637
- filtered by the follow `where`, then **unioned by identity within the step**: a
638
- node reached by two destinations (or twice by one relation) in the same step is
639
- a single successor. `exists`/`none`/`count` are not destinations (they are
647
+ A row's successors are, per destination in order, that destination's rows
648
+ (shaped by the block's projection, if any), then **unioned by identity within
649
+ the step**: a node reached by two destinations (or twice by one relation) in the
650
+ same step is a single successor, and the destination that reached it first
651
+ governs it. `exists`/`none`/`count` are not destinations (they are
640
652
  where-position tests).
641
653
 
642
- The block accepts: `where <succ>` (which successors keep participating),
643
- `frontier <pred>` (cut a relation that could continue, whichever destination
644
- reached it), `depth <n>` (1–8), and `by <expr>` (the identity used for cycle
645
- detection + `distinct` — default `.id` or the object reference). The follow
646
- `where` is **correlated**: it reads the candidate successor's scope whose parent
647
- is the frontier row's — a bare name is the candidate's own property, `^name` is
648
- the row being expanded, `^^name` the walk's enclosing scope (language 0.14;
649
- before, `^` skipped the frontier row). So `follow doc.in { where
650
- before.contains(^$path) }` steps only into rows that name the row they were
651
- reached from. `frontier` and `by` are not correlated: they read the occurrence
652
- with `^` the enclosing scope, as the body does. Give `follow` a stable identity
653
- (`.id` or `by`) when your relation returns fresh objects rather than shared
654
- references.
654
+ A destination block's body is an ordinary block — `where <succ>` (which
655
+ successors keep participating), a projection, `order by`, `limit`/`offset`,
656
+ `distinct` — plus two clauses after `where`: `frontier <pred>` (an occurrence
657
+ this destination reached for which it holds is a frontier — cut, not expanded)
658
+ and `depth <n>` (1–8: this destination expands a frontier row only while its
659
+ `$depth < n`; a destination block's clause alone). The block's `where` is **correlated**: it reads
660
+ the candidate successor's scope whose parent is the frontier row's — a bare name
661
+ is the candidate's own property, `^name` is the row being expanded, `^^name` the
662
+ walk's enclosing scope (language 0.14; before, `^` skipped the frontier row). So
663
+ `follow doc.in { where before.contains(^$path) }` steps only into rows that
664
+ name the row they were reached from. `frontier` is not correlated: it reads the
665
+ occurrence with `^` the enclosing scope, as the body does; seeds are reached
666
+ through no destination and are never frontiers by a destination's rule. **The
667
+ body that carries the `follow` may write `frontier <pred>` too**, after its
668
+ `where` and before `follow`: it applies to the seeds — a matching seed stays in
669
+ the result with `$stop == "frontier"` and is not expanded (`id from tree frontier
670
+ !active follow children { depth 2 }`); read in the seed's scope like the body's
671
+ `where`. Each destination's `frontier` is its own; without a `follow`, a
672
+ body-level `frontier` is a parse error.
673
+
674
+ **Identity is `$key`** (language 0.20). Cycle detection, the per-step union,
675
+ `follow distinct`, `distinct` over unprojected rows and `is` all compare one
676
+ thing — the identity a row is known by — and reading `$key` on any row returns
677
+ it. The default is the host's (`DataContext.identityOf`), else the row's
678
+ **structural value**; `DefaultContext` names none, so over plain objects two
679
+ objects with the same `id` are two nodes. A block may project `$key: <expr>` to
680
+ give the rows it yields that identity — the body's `select` names the seeds, a
681
+ destination block its successors (where `$key:` is not a column; the rows keep
682
+ their shape):
683
+
684
+ ```js
685
+ const n1 = { id: 1 }, n2 = { id: 2, next: [n1] }; n1.next = [n2]; // a cyclic graph of fresh references
686
+ oqx`id, stop: $stop, $key: id from ${[n1]} follow next { $key: id } order by $ordinal`;
687
+ // [{ id: 1, stop: "interior", $key: 1 }, { id: 2, stop: "interior", $key: 2 }, { id: 1, stop: "cycle", $key: 1 }]
688
+ oqx`name, $key: email from ${people} follow next { $key: email }`; // known by email at every step
689
+ ```
690
+
691
+ Identities are compared as values across the whole walk, so destinations that
692
+ can reach the same rows should project the same `$key`, or a cycle through both
693
+ may run to the depth cap. Give a cyclic graph of fresh objects a `$key` (or a
694
+ context with `identityOf`) rather than rely on the structural default.
695
+
696
+ ### 9b. Cardinality and tuples (0.20)
697
+
698
+ In row position an **array** is a collection and a non-array **object** is one
699
+ row: a block over it yields **one record** (or absent, when the row is filtered
700
+ out) rather than a one-element list, `values` the one value, `first`/`single`
701
+ that object — `doc { title }` is `{ title }`, `doc { status == "active" }` is the
702
+ object or absent (a conditional reach-through), `title from doc` the same record.
703
+ A scalar in row position is an eval error (`list(s)` says "one element"
704
+ explicitly); a `from` re-projection or a `follow` is plural. A host says which
705
+ of its objects are collections through `DataContext.isSingle` (a lazy handle);
706
+ by default a non-iterable object is one row.
707
+
708
+ ```js
709
+ oqx`${settings} { theme }`; // { theme: "dark" } (one object → one record)
710
+ oqx`${[settings]} { theme }`; // [{ theme: "dark" }] (a collection → a list)
711
+ oqx`from list(${settings})`; // [settings] (the explicit one-element collection)
712
+ ```
713
+
714
+ A **tuple** `(e1, e2, …)` — two or more expressions in parentheses — is a value
715
+ and a receiver: its elements concatenated with one level of flattening (a
716
+ collection contributes its items, a row itself; an absent element nothing, a
717
+ `null` is kept; duplicates kept). One parenthesized expression is grouping.
718
+
719
+ ```js
720
+ oqx`n: size((tags, aliases)) from ${people}`; // one count over both lists
721
+ oqx`name from ${people} where "x" in (tags, aliases)`; // membership in the union
722
+ oqx`n: (mom, dad, siblings) { name values } from ${people}`; // one block over the union
723
+ oqx`id from ${g} follow (before, after) { depth 2 }`; // one destination over the union
724
+ ```
725
+
726
+ ### 9c. Template literals (0.21)
727
+
728
+ `` `text ${expr} text` `` is a string value: the text and each interpolated
729
+ value's **string form** (numbers without a fraction when integer-valued,
730
+ booleans as the words) concatenated. It stands wherever a value does — a
731
+ `select` item, a `where` operand, an `order by` key, a function argument, a
732
+ method receiver. The text decodes with the same escapes as a string (`` \` ``
733
+ keeps a backtick, `\$` a dollar that opens nothing); newlines are kept, so a
734
+ literal may span lines. An interpolation holds any expression with balanced
735
+ braces — a `{ … }` block or another template literal included.
736
+
737
+ ```js
738
+ oqx`label: \`\${title} (\${era})\` from ${docs}`; // "Alpha (3)"
739
+ oqx`x: \`\${jobs single { employer values where primary }}\` from ${docs}`; // the block's one value
740
+ oqx`title from ${docs} where \`\${title}!\` == "Alpha!"`;
741
+ oqx`x: jobs collect { label: \`\${^title}: \${employer}\` values } from ${docs}`; // `^` reads the enclosing row
742
+ ```
743
+
744
+ Two rules keep it honest: **absent propagates** — any absent interpolation makes
745
+ the whole literal absent (as `+` does), never `"undefined"` — and **a list, a
746
+ record or a range cannot be interpolated**: that is an eval error (`cannot
747
+ interpolate …`), not a silent `[object Object]`. Inside a JavaScript tagged
748
+ template write the backticks and `${` escaped as above (`` \` ``, `\${`), or
749
+ pass the whole query as a string: a `${…}` *binding* can stand beside a template
750
+ literal but never inside one (a fragment that ends inside the literal is an
751
+ unterminated-literal error).
655
752
 
656
753
  ### 10. Sugar (0.17)
657
754
 
@@ -661,11 +758,15 @@ optimizer and any pushdown planner see it), so nothing here has semantics of its
661
758
  own except the postfix `!` and the infix `is`:
662
759
 
663
760
  ```js
664
- // A receiver block without a consumer is `collect`; a block whose LEADING
665
- // expression is syntactically a predicate drops `where`.
761
+ // A receiver block without a consumer is `collect` — wherever a value stands
762
+ // (language 0.20): a select item, a where leaf, a follow destination, a call
763
+ // argument; `x distinct { … }` is `x collect distinct { … }`. A block whose
764
+ // LEADING expression is syntactically a predicate drops `where`.
666
765
  oqx`name, current: jobs { !end_date } from ${people}`
667
766
  // ≡ name, current: jobs collect { where !end_date } from ${people}
668
767
  oqx`${people} { age > 40 }` // ≡ ${people} collect { where age > 40 }
768
+ oqx`n: size(jobs { !end_date }) from ${people}` // a block as a call argument
769
+ oqx`${people} distinct { city }` // ≡ ${people} collect distinct { city }
669
770
  oqx`${people} count { jobs exists { } }` // a consumer test is predicate-shaped too
670
771
  oqx`${people} first { name }` // a bare name still projects — unchanged
671
772
  oqx`${people} first { is active }` // the bare-field filter: `is x` ≡ `!!x`, `not x` ≡ `!x`
@@ -678,15 +779,15 @@ oqx`name, first_job: jobs[0], primary: jobs[pay > 2]! from ${people}`
678
779
  // jobs[pay > 2]! ≡ jobs single { where pay > 2 }! (zero or many matches ⇒ an eval error)
679
780
 
680
781
  // Postfix `!` — required: the value, or an eval error naming the expression
681
- // (and the row's id). Never a filter, never a coercion: 0! and ""! are values.
782
+ // (and the row's `$key` when scalar). Never a filter, never a coercion: 0! and ""! are values.
682
783
  oqx`select id!, title from ${docs}` // insist on an identity before a write
683
784
  // Tightest precedence: `refs(c)[0]!.name` requires the lookup, `refs(c)[0].name!` the name.
684
785
 
685
- // `is` / `is not` compare IDENTITY (a row's id when present, else structurally):
786
+ // `is` / `is not` compare IDENTITY (`$key`: the host's, else structural — §9):
686
787
  oqx`name from ${people} where manager is null` // absent (same as == null)
687
788
  oqx`${people} count { ^people exists { $it is ^$it } }` // rows compare; `==` leaves that unspecified
688
- // `and` / `or` are exact synonyms of `&&` / `||`: `title or path` coalesces.
689
- // `print` writes the words (language 0.18); `!` stays `!`.
789
+ // `and` / `or` / `not` are the canonical connectives; `&&` / `||` / `!` are exact
790
+ // synonyms (`title or path` coalesces). `print` writes the words (language 0.18); `!` stays `!`.
690
791
 
691
792
  // A select item may use the items to its left (inlined like a `where` alias):
692
793
  oqx`boss: ^people[id == ^manager], bossName: boss.name from ${people}`
@@ -694,7 +795,12 @@ oqx`boss: ^people[id == ^manager], bossName: boss.name from ${people}`
694
795
 
695
796
  Reserved words, never a bare field name: `from where select is not and or` and
696
797
  the literal words `true false null`. The top level is unchanged — `age > 15
697
- from ${people}` stays an error, because `where` would precede `from`.
798
+ from ${people}` stays an error, because `where` would precede `from`. One
799
+ carve-out: after a `follow` destination that is a relation of the current row
800
+ (a bare name, a call, a binding) a brace is still the **options** block (`follow
801
+ children { depth 2 }`), never a collect; only over an outer-reference receiver,
802
+ which cannot be a plain destination, is the brace a destination block with the
803
+ implicit `collect` (`follow ^people { where … }`, language 0.19).
698
804
 
699
805
  ### Cheat-sheet
700
806
 
@@ -702,11 +808,11 @@ from ${people}` stays an error, because `where` would precede `from`.
702
808
  select … from … where … follow … order by … limit N offset N the fixed clause order (each at most once)
703
809
  name, alias: expr, nested: rel collect { … } projection (`select` may be dropped only here, in first position)
704
810
  from ${source} source collection (required at the top level)
705
- where a == b && rel exists { where … } || !c predicate tree + nested ops (`and`/`or` ≡ `&&`/`||`)
811
+ where a == b and rel exists { where … } or not c predicate tree + nested ops (`&&`/`||`/`!` are accepted synonyms)
706
812
  rel { pay > 2 } / rel { employer } a block without a consumer is collect; a leading predicate is where-first, a bare name projects
707
813
  rel[p] / rel[p]! / rel[0] / rel[0]! first match / exactly one (required) / positional / required positional
708
814
  x! required: the value, or an error when absent (tightest; `a!.b` vs `a.b!`)
709
- is x / not x / x is y / x is not y truthiness (≡ !!x / !x) / identity (a row's id, else structural; absent ≡ null)
815
+ is x / not x / x is y / x is not y truthiness (≡ !!x / !x) / identity ($key: the host's, else structural; absent ≡ null)
710
816
  where alias / a: expr, b: a `where` and later items may use this body's select aliases (resolved before evaluation; an alias shadows a field)
711
817
  where rel none { … } zero rows (≡ !rel exists { … }; "all" = none over the complement)
712
818
  where x in lo..hi / lo...hi / ..hi / lo.. range membership (incl. / excl. / open-ended)
@@ -721,9 +827,13 @@ from entries(obj) … $key / $it a record's properties as a co
721
827
  entries(rel) exists { … } a free-function call may be a receiver
722
828
  order by expr desc, expr2 ordering
723
829
  limit n / offset n bound the row set (after where/order/distinct, before the consumer)
724
- follow rel { where … frontier … depth n by … } recursion ($depth/$stop/$leaf/$frontier); `^name` in the where is the frontier row
725
- follow before, after several destinations: the union of their rows (per step, by identity)
726
- follow ^people collect { where manager == ^id } a destination block: successors computed per frontier row (`^` = that row)
830
+ follow rel { where … frontier … depth n } recursion ($depth/$stop/$leaf/$frontier/$key); the brace is the destination's block, `^name` in its where the frontier row
831
+ follow before, after / follow (before, after) several destinations: the union of their rows (per step, by identity) / one tuple destination
832
+ follow ^people { where manager == ^id } a destination block: successors computed per frontier row (`^` = that row; `collect` implicit, 0.20)
833
+ $key / $key: expr the identity a row is known by (the host's, else structural) / give the rows a block yields that identity
834
+ (a, b) / rel distinct { … } a tuple: the elements, one level flat (a value and a receiver) / collect distinct (0.20)
835
+ `${title} (${era})` a template literal: text + string forms; absent anywhere → absent; a list/record → error (0.21)
836
+ obj { title } one object → one record (or absent); a collection → a list; a scalar → an error (0.20)
727
837
  ${source} <collect|exists|none|count|first|single> { … } whole-query consumer
728
838
  ```
729
839
 
@@ -836,9 +946,12 @@ than approximate it. The conformance suite verifies this.
836
946
 
837
947
  The engine never touches host objects directly; it asks a `DataContext` to
838
948
  resolve named roots, read properties/relations, coerce results to rows, and
839
- compute identity (and, optionally, to supply a `rootObject()` — the root
949
+ optionally to name a row's identity (`identityOf` — `$key`; the default is the
950
+ row's structural value), to say which of its objects are collections rather
951
+ than one row (`isSingle`; by default a non-iterable object is one row, so a
952
+ block over it is one record), and to supply a `rootObject()` — the root
840
953
  scope's row, what `$it` is at the root and `^$it` from a top-level row; without
841
- one the root row is absent). Implement it to query an ORM graph, a remote API,
954
+ one the root row is absent. Implement it to query an ORM graph, a remote API,
842
955
  or lazily loaded relations — the query *semantics* stay in OQX. Name resolution is simple
843
956
  for a context: a bare `field`, a `.field` segment, and a `^field` outer
844
957
  reference each become one `get(row, key)` against exactly the row of the scope
@@ -851,7 +964,7 @@ const graph = {
851
964
  root: (name) => name === "tree" ? [nodes.get(1)] : undefined,
852
965
  get: (row, key) => key === "children" ? row.childIds.map(id => nodes.get(id)) : row[key],
853
966
  toRows: (v) => v == null ? [] : Array.isArray(v) ? v : [v],
854
- identity: (row) => row.id, // for follow dedup
967
+ identityOf: (row) => row.id, // `$key`: for follow dedup, distinct, `is` (optional; default: structural)
855
968
  };
856
969
  run(parse("id, depth: $depth from tree follow children"), { context: graph });
857
970
  ```
@@ -884,7 +997,7 @@ const ctx = {
884
997
  return builtins.callMethod(name, recv, args);
885
998
  },
886
999
  };
887
- run(parse("id from tree where age(born) > 18 && title.slug() == 'x'"), { context: ctx });
1000
+ run(parse("id from tree where age(born) > 18 and title.slug() == 'x'"), { context: ctx });
888
1001
  ```
889
1002
 
890
1003
  The simplest route is to `extends DefaultContext` and `super.callFunction(...)`
@@ -910,7 +1023,7 @@ import { parse, PlannedEngine } from "@omgbase/oqx";
910
1023
  import { SqliteTable } from "@omgbase/oqx/sqlite";
911
1024
 
912
1025
  const planner = new SqliteTable(db, "emp", { columns: ["id", "name", "dept", "level"] });
913
- new PlannedEngine(planner).run(parse('name from emp where dept == "eng" && level >= 5'), []);
1026
+ new PlannedEngine(planner).run(parse('name from emp where dept == "eng" and level >= 5'), []);
914
1027
  // → `dept`/`level` pushed to SQL; anything untranslatable finishes in-memory
915
1028
  ```
916
1029
 
@@ -941,7 +1054,7 @@ results and on errors.
941
1054
 
942
1055
  **What is optimized**
943
1056
 
944
- - **Correlated equality → hash probe.** A top-level `&&` conjunct of the block's
1057
+ - **Correlated equality → hash probe.** A top-level `and` conjunct of the block's
945
1058
  `where` of the form `local == outer` (either side) — `local` a bare identifier
946
1059
  or member chain on the block's row (`customer_id`, `meta.id`, `$it`),
947
1060
  `outer` anything that reads nothing from that row (`^customer_id`, `^^x.id`,
@@ -963,7 +1076,7 @@ results and on errors.
963
1076
  by`) are answered from the bucket's size after the bound without entering a
964
1077
  row.
965
1078
 
966
- **The sound rule.** `&&` is strictly left to right and short-circuiting (§13),
1079
+ **The sound rule.** `and` is strictly left to right and short-circuiting (§13),
967
1080
  so a probe may skip a row only if everything the scan would have evaluated for
968
1081
  it could neither raise nor bind: an equality is hoisted only when **every
969
1082
  conjunct to its left is raise-free** — no function or method call, no `single`,
@@ -973,7 +1086,7 @@ too. A conjunct that could raise *right* of the equality is simply evaluated
973
1086
  over the bucket, where the scan would have evaluated it on the same rows. A
974
1087
  block whose `where` lifts anywhere is never probed (a lift could change what
975
1088
  the outer side reads between rows). Otherwise the block scans as before, so
976
- `where nope(x) && id == ^id` still raises `unknown function`.
1089
+ `where nope(x) and id == ^id` still raises `unknown function`.
977
1090
 
978
1091
  Property reads are treated as total (§23: evaluation is otherwise total). A
979
1092
  `DataContext.get` that throws is caught where the engine relies on reads it
@@ -1082,7 +1195,7 @@ adapter lives on the `@omgbase/oqx/sqlite` subpath.
1082
1195
  (the optimizer rules for nested blocks, default `DEFAULT_RULES`; `[]` is the
1083
1196
  naive scan) and `trace` (a `(event: TraceEvent) => void` sink for index /
1084
1197
  probe / memo / cardinality / fallback events). See [Performance](#performance-relational-patterns).
1085
- - `DataContext` — the tier-2 interface: `root`, `get`, `toRows`, `identity`,
1198
+ - `DataContext` — the tier-2 interface: `root`, `get`, `toRows`, `identityOf`, `isSingle`,
1086
1199
  plus optional `callFunction` / `callMethod` (above), an optional
1087
1200
  `indexFor(collection, path)` returning a `RowIndex` for a pre-built equality
1088
1201
  index (`lookup` → positions; optionally `lookupRows` → rows, probed without
@@ -1095,7 +1208,7 @@ adapter lives on the `@omgbase/oqx/sqlite` subpath.
1095
1208
  "native")`; `BUILTIN_METHODS.matches` is always the baseline.
1096
1209
  - `CallResult` — `{ handled: boolean; value?: unknown }`, returned by those hooks.
1097
1210
  - `DefaultContext` — `new DefaultContext(roots?, options?)`; plain-object
1098
- access, `.id` identity, and the builtin function/method tables. `options`:
1211
+ access, structural identity (`$key` is the row itself unless projected), and the builtin function/method tables. `options`:
1099
1212
  `{ regexDialect?: RegexDialect }` (see the regex baseline, §4).
1100
1213
  - `compileRegex(pattern, flags?, dialect?)` — the `matches()` compiler: a
1101
1214
  `RegExp`, or an `OqxError` for a bad pattern/flag. `RegexDialect` /
@@ -1142,7 +1255,7 @@ adapter lives on the `@omgbase/oqx/sqlite` subpath.
1142
1255
  import { parse, run, IndexedCollection, PlannedEngine } from "@omgbase/oqx";
1143
1256
  const planner = new IndexedCollection("people", people, ["city", "title"]);
1144
1257
  const engine = new PlannedEngine(planner, planner.context());
1145
- run(parse('name from people where city == "NYC" && age > 30'), { engine });
1258
+ run(parse('name from people where city == "NYC" and age > 30'), { engine });
1146
1259
  // city probe from the index; `age > 30` finished in-memory over the candidates
1147
1260
  ```
1148
1261
 
package/dist/ast.d.ts CHANGED
@@ -15,6 +15,9 @@ export type BinaryOp = RelOp | IdentityOp | "+" | "-" | "*" | "/" | "%";
15
15
  export type UnaryOp = "!" | "-";
16
16
  /** The operator of a `logical` expression. */
17
17
  export type LogicalOp = "&&" | "||";
18
+ /** One part of a template literal (since 0.21): a decoded text run, or an
19
+ * interpolated expression. The strings are not nodes (no `kind`, no span). */
20
+ export type TemplatePart = string | Expr;
18
21
  /** Scalar value/predicate expression, evaluated against a row scope + bindings. */
19
22
  export type Expr = {
20
23
  kind: "lit";
@@ -76,6 +79,14 @@ export type Expr = {
76
79
  lo: Expr | null;
77
80
  hi: Expr | null;
78
81
  exclusiveEnd: boolean;
82
+ } | {
83
+ kind: "tuple";
84
+ span: Span;
85
+ items: Expr[];
86
+ } | {
87
+ kind: "template";
88
+ span: Span;
89
+ parts: TemplatePart[];
79
90
  } | OpNode;
80
91
  /** One `order by` term. */
81
92
  export interface OrderSpec {
@@ -123,16 +134,13 @@ export interface OpNode {
123
134
  * destination block — a select-position directive (`collect`/`first`/`single`) re-evaluated
124
135
  * per frontier row, inside which `^` is that row. Discriminate on `kind === "op"`. */
125
136
  export type FollowDestination = Expr | OpNode;
126
- /** The recursive `follow` clause. */
137
+ /** The recursive `follow` clause (since 0.20 just the destinations: a
138
+ * destination's `where`, `frontier` and `depth` live in its own block). */
127
139
  export interface Follow {
128
140
  kind: "follow";
129
141
  span: Span;
130
142
  destinations: FollowDestination[];
131
143
  distinct: boolean;
132
- where: Expr | null;
133
- frontier: Expr | null;
134
- depth: number | null;
135
- by: Expr | null;
136
144
  }
137
145
  /** A nested (sub)query body — the `{ … }` of a directive. */
138
146
  export interface Subquery {
@@ -143,6 +151,16 @@ export interface Subquery {
143
151
  select: SelectItem[];
144
152
  orderBy: OrderSpec[] | null;
145
153
  follow: Follow | null;
154
+ /** `frontier P` / `depth n` (since 0.20). In a `follow` destination block
155
+ * `frontier` marks the occurrences reached through this destination for
156
+ * which `P` holds (not expanded; `P` read one level out, under the walk's
157
+ * enclosing scope), and `depth n` makes the destination expand a frontier
158
+ * row only while that row's `$depth < n` (1..8; `null` = the hard cap 8).
159
+ * In any other block `frontier` is legal only with a `follow` and marks the
160
+ * walk's SEEDS, read in the seed's scope like `where` (as on `Query`);
161
+ * `depth` is destination-only. Both `null` when absent. */
162
+ frontier: Expr | null;
163
+ depth: number | null;
146
164
  /** `values` — scalar projection mode: the (single) projected expression is the
147
165
  * row's result itself rather than being wrapped in a `{ name: value }` record,
148
166
  * so `name values` yields `["Bob", …]` and `$it values` yields the rows. */
@@ -186,6 +204,11 @@ export interface Query {
186
204
  orderBy: OrderSpec[] | null;
187
205
  consumer: Consumer;
188
206
  follow: Follow | null;
207
+ /** `frontier P` (since 0.20) — legal only when the body has a `follow`: a
208
+ * seed for which `P` holds (read in the seed's own scope, `^` the enclosing
209
+ * scope, like `where`) is a frontier (`$stop == "frontier"`, not expanded).
210
+ * `null` without one. */
211
+ frontier: Expr | null;
189
212
  /** `distinct` — dedup the result rows by their projected value (see OpNode). */
190
213
  distinct: boolean;
191
214
  /** `values` — scalar projection mode (see Subquery). */
package/dist/ast.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"AAaA;;kFAEkF;AAClF,MAAM,MAAM,IAAI,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;AAEhD;kFACkF;AAClF,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEpF,sEAAsE;AACtE,MAAM,MAAM,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC;AAE1D,0DAA0D;AAC1D,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,QAAQ,CAAC;AAEzC,4FAA4F;AAC5F,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,UAAU,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAExE,4CAA4C;AAC5C,MAAM,MAAM,OAAO,GAAG,GAAG,GAAG,GAAG,CAAC;AAEhC,8CAA8C;AAC9C,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC;AAEpC,mFAAmF;AACnF,MAAM,MAAM,IAAI,GACZ;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAA;CAAE,GACpE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,EAAE,CAAA;CAAE,GAC3E;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,GAEtD;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,IAAI,CAAA;CAAE,GACrE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,IAAI,CAAA;CAAE,GACvE;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,IAAI,CAAA;CAAE,GAInD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,IAAI,GAAG,IAAI,CAAC;IAAC,EAAE,EAAE,IAAI,GAAG,IAAI,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,GAItF,MAAM,CAAC;AAEX,2BAA2B;AAC3B,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,OAAO,CAAC;CACf;AAED;uDACuD;AACvD,MAAM,MAAM,UAAU,GAKlB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACrE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9D;2CAC2C;AAC3C,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACf;AAED;0EAC0E;AAC1E,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,IAAI,CAAC;IACf,EAAE,EAAE,QAAQ,CAAC;IACb,GAAG,EAAE,QAAQ,CAAC;IACd,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B;;;sFAGkF;IAClF,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;sFAEsF;AACtF,MAAM,MAAM,iBAAiB,GAAG,IAAI,GAAG,MAAM,CAAC;AAE9C,qCAAqC;AACrC,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,IAAI,CAAC;IACX,YAAY,EAAE,iBAAiB,EAAE,CAAC;IAClC,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,EAAE,EAAE,IAAI,GAAG,IAAI,CAAC;CACjB;AAED,6DAA6D;AAC7D,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,IAAI,EAAE,CAAC;IACb,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;;gFAE4E;IAC5E,MAAM,EAAE,OAAO,CAAC;IAChB;;;;;gDAK4C;IAC5C,KAAK,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,MAAM,EAAE,IAAI,GAAG,IAAI,CAAC;CACrB;AAED;yEACyE;AACzE,MAAM,MAAM,KAAK,GACb;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,KAAK,EAAE,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,KAAK,EAAE,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,KAAK,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,GAC1C,MAAM,CAAC;AAEX,6BAA6B;AAC7B,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,IAAI,CAAC;IACb,IAAI,EAAE,IAAI,EAAE,CAAC;IACb,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,gFAAgF;IAChF,QAAQ,EAAE,OAAO,CAAC;IAClB,wDAAwD;IACxD,MAAM,EAAE,OAAO,CAAC;IAChB,0EAA0E;IAC1E,KAAK,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,MAAM,EAAE,IAAI,GAAG,IAAI,CAAC;CACrB;AAED,iFAAiF;AACjF,MAAM,MAAM,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,KAAK,GAAG,IAAI,CAAC;AAEjG,oCAAoC;AACpC,MAAM,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAOtC;wDACwD;AACxD,wBAAgB,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,IAAI,CAElD;AAED;4BAC4B;AAC5B,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC"}
1
+ {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"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"}
package/dist/ast.js CHANGED
@@ -12,7 +12,7 @@
12
12
  // empty span `[0, 0]` (see `build.ts`).
13
13
  /** The expression kinds (`Expr["kind"]`), for `isExpr`. */
14
14
  const EXPR_KINDS = new Set([
15
- "lit", "ident", "outer", "binding", "member", "call", "unary", "required", "binary", "logical", "in", "range",
15
+ "lit", "ident", "outer", "binding", "member", "call", "unary", "required", "binary", "logical", "in", "range", "tuple", "template",
16
16
  ]);
17
17
  /** Whether a node is a scalar `Expr` (an `op` in expression position is not: it
18
18
  * is an expression only by position, see AST.md §1). */
package/dist/ast.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ast.js","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,8EAA8E;AAC9E,gFAAgF;AAChF,+EAA+E;AAC/E,2EAA2E;AAC3E,gFAAgF;AAChF,wDAAwD;AACxD,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAChF,4EAA4E;AAC5E,wCAAwC;AAqKxC,2DAA2D;AAC3D,MAAM,UAAU,GAAwB,IAAI,GAAG,CAAC;IAC9C,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO;CAC9G,CAAC,CAAC;AAEH;wDACwD;AACxD,MAAM,UAAU,MAAM,CAAC,IAAa;IAClC,OAAO,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC"}
1
+ {"version":3,"file":"ast.js","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,8EAA8E;AAC9E,gFAAgF;AAChF,+EAA+E;AAC/E,2EAA2E;AAC3E,gFAAgF;AAChF,wDAAwD;AACxD,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAChF,4EAA4E;AAC5E,wCAAwC;AAgMxC,2DAA2D;AAC3D,MAAM,UAAU,GAAwB,IAAI,GAAG,CAAC;IAC9C,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU;CACnI,CAAC,CAAC;AAEH;wDACwD;AACxD,MAAM,UAAU,MAAM,CAAC,IAAa;IAClC,OAAO,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC"}