@omgbase/oqx 0.6.0 → 0.8.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.
- package/README.md +88 -15
- package/dist/adapters/sqlite.js +2 -2
- package/dist/adapters/sqlite.js.map +1 -1
- package/dist/ast.d.ts +6 -0
- package/dist/ast.d.ts.map +1 -1
- package/dist/context.d.ts +4 -5
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +0 -3
- package/dist/context.js.map +1 -1
- package/dist/engine.d.ts +2 -2
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +59 -49
- package/dist/engine.js.map +1 -1
- package/dist/parser.js +84 -32
- package/dist/parser.js.map +1 -1
- package/dist/plan.d.ts +2 -1
- package/dist/plan.d.ts.map +1 -1
- package/dist/plan.js +9 -1
- package/dist/plan.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/sqlite.ts +2 -2
- package/src/ast.ts +9 -3
- package/src/context.ts +4 -8
- package/src/engine.ts +64 -45
- package/src/parser.ts +76 -25
- package/src/plan.ts +9 -1
package/README.md
CHANGED
|
@@ -101,8 +101,51 @@ oqx`label: name, decade: age / 10 from ${people} where name == "Bob"`;
|
|
|
101
101
|
// [{ label: "Bob", decade: 4.1 }]
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
+
An item that is not a plain navigation — a call, arithmetic, a comparison — has
|
|
105
|
+
no natural key, so it must be aliased (`n: size(jobs)`), unless the projection is
|
|
106
|
+
in `values` mode (next).
|
|
107
|
+
|
|
104
108
|
The `select` keyword is optional and works in any position — `select name from …`
|
|
105
|
-
is identical to `name from
|
|
109
|
+
is identical to `name from …` (and hosts `select distinct`, §6).
|
|
110
|
+
|
|
111
|
+
**`values` — scalar projection.** Ordinarily every row projects to a record. Add
|
|
112
|
+
`values` after a projection of exactly **one** item to get the value itself:
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
oqx`name values from ${people}`; // ["Bob", "Alice", "Carol"]
|
|
116
|
+
oqx`name.upper() values from ${people} where age < 30`; // ["CAROL"] (no alias needed)
|
|
117
|
+
oqx`${people} first { name values where age > 50 }`; // "Alice"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`values` is a result-shape mode, not a consumer: it works in the top-level
|
|
121
|
+
projection and inside any `collect` / `first` / `single` block, and composes with
|
|
122
|
+
`distinct` (`select distinct employer values from ${jobs}` is the distinct set of
|
|
123
|
+
employers as strings, not `{ employer }` records). An alias, if present, is
|
|
124
|
+
ignored; a lift (`^name:`) cannot be combined with it.
|
|
125
|
+
|
|
126
|
+
**`$value` — the current item.** Every scope has a current value; `$value` is
|
|
127
|
+
that exact value, whatever its type (an object row or a plain scalar). Bare names
|
|
128
|
+
still navigate it (`name` ≡ `$value.name`), so `$value` matters exactly where
|
|
129
|
+
there is nothing to navigate: collections of numbers or strings, or handing the
|
|
130
|
+
whole row somewhere. Together with `values` this makes scalar collections
|
|
131
|
+
first-class:
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
const scores = [10, 60, 70, 45];
|
|
135
|
+
oqx`$value values from ${scores} where $value > 50`; // [60, 70]
|
|
136
|
+
oqx`$value values from ${scores} order by $value desc`; // [70, 60, 45, 10]
|
|
137
|
+
|
|
138
|
+
const players = [{ name: "Ann", scores: [10, 60, 70] }, { name: "Ben", scores: [45] }];
|
|
139
|
+
oqx`name, big: scores collect { $value values where $value > 50 } from ${players}`;
|
|
140
|
+
// [{ name: "Ann", big: [60, 70] }, { name: "Ben", big: [] }]
|
|
141
|
+
|
|
142
|
+
oqx`employee: $value from ${people} where name == "Bob"`; // [{ employee: <Bob> }]
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Inside a nested block `$value` is the inner item; the enclosing row is `^$value`
|
|
146
|
+
(§3). At the root scope (before any row) it is absent. Plain objects are **not**
|
|
147
|
+
iterable through `$value` — a record stays a record; converting one to a
|
|
148
|
+
collection of entries is a separate, explicit step (`entries()`, planned).
|
|
106
149
|
|
|
107
150
|
### 3. Predicates (where)
|
|
108
151
|
|
|
@@ -149,25 +192,44 @@ non-range string yields an absent range, so `x in range(bad)` is just false.
|
|
|
149
192
|
**Interpolations are always values, never syntax.** `where name == ${x}` compares
|
|
150
193
|
against the value of `x`; a string in `x` can't inject operators or identifiers.
|
|
151
194
|
|
|
152
|
-
**
|
|
153
|
-
|
|
195
|
+
**Scoping: bare names are local, `^` reaches out.** A bare identifier resolves
|
|
196
|
+
against the **current row only**. If the row lacks that property the value is
|
|
197
|
+
absent — it never falls through to an enclosing row. To correlate with an
|
|
198
|
+
enclosing scope you say so explicitly with `^name` ("exactly one scope out";
|
|
199
|
+
`^^name` for two, and so on):
|
|
154
200
|
|
|
155
201
|
```js
|
|
156
202
|
const accounts = [
|
|
157
203
|
{ owner: "x", budget: 100, orders: [{ amount: 50 }, { amount: 150 }] },
|
|
158
204
|
{ owner: "y", budget: 200, orders: [{ amount: 250 }] },
|
|
159
205
|
];
|
|
206
|
+
oqx`owner from ${accounts} where orders exists { amount > ^budget }`;
|
|
207
|
+
// [{ owner: "x" }, { owner: "y" }] — `^budget` is the enclosing account's budget
|
|
208
|
+
|
|
160
209
|
oqx`owner from ${accounts} where orders exists { amount > budget }`;
|
|
161
|
-
// [
|
|
210
|
+
// [] — a bare `budget` is the ORDER's own budget: absent, so `>` is false
|
|
162
211
|
```
|
|
163
212
|
|
|
164
|
-
|
|
213
|
+
Every reference is therefore decidable from the query text alone. Adding a
|
|
214
|
+
`budget` field to the order rows later cannot change what `^budget` means, and a
|
|
215
|
+
typo can't silently capture an outer field. Present-but-falsy values (`null`,
|
|
216
|
+
`false`, `0`, `""`) are read like any other local value — there is no "absent, so
|
|
217
|
+
look outward" rule to trip over — and `^` always reads *exactly* N scopes out
|
|
218
|
+
(one past the root is absent, not the nearest match).
|
|
165
219
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
220
|
+
A `.member` access always navigates the value on its left. Named roots (see
|
|
221
|
+
[data context](#data-context-string-queries-and-named-roots)) live on the
|
|
222
|
+
*root* scope, one out from a top-level row: `^people` from a person row, `^^people`
|
|
223
|
+
from a row nested one level deeper. A receiver may start with `^` too, which is
|
|
224
|
+
how a nested consumer runs over a named root or an enclosing row's relation:
|
|
225
|
+
|
|
226
|
+
```js
|
|
227
|
+
execute("name, peers: ^people collect { name where city == ^city && name != ^name } from people", { people });
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Correlated subqueries.** Because inner and outer rows often share names, the
|
|
231
|
+
explicit `^` is what makes correlation unambiguous — e.g. each person's
|
|
232
|
+
siblings, where both the person and the candidates have a `parent`:
|
|
171
233
|
|
|
172
234
|
```js
|
|
173
235
|
const family = [
|
|
@@ -188,7 +250,8 @@ oqx`
|
|
|
188
250
|
Here `parent` is the inner candidate's parent while `^parent` is the outer
|
|
189
251
|
person's. (`^` in an expression *reads* one scope out; the same `^` as a
|
|
190
252
|
select-item prefix — `^name: …` in §7 — *binds* one scope out. Both mean "one
|
|
191
|
-
scope out.")
|
|
253
|
+
scope out.") A value bound into a scope by a lift is read there as a bare name,
|
|
254
|
+
like a row property.
|
|
192
255
|
|
|
193
256
|
### 4. Built-in functions
|
|
194
257
|
|
|
@@ -350,6 +413,10 @@ oqx`id, stop: $stop from ${tree} follow children { depth 2 } order by $ordinal`;
|
|
|
350
413
|
// a1 is never reached; a and b report stop:"depth"
|
|
351
414
|
```
|
|
352
415
|
|
|
416
|
+
The intrinsics belong to the reached row's own scope like any other name: inside
|
|
417
|
+
a nested block (`kids: children collect { … }`) a bare `$depth` is absent, and
|
|
418
|
+
the occurrence's depth is `^$depth`.
|
|
419
|
+
|
|
353
420
|
The walk is **per-path**: a node reached by N distinct paths yields N
|
|
354
421
|
occurrences, and revisiting an identity already on the current path is admitted
|
|
355
422
|
**once** as `$stop == "cycle"` and never re-expanded, so cycles terminate without
|
|
@@ -370,8 +437,12 @@ from ${source} source collection
|
|
|
370
437
|
where a == b && rel exists { … } || !c predicate tree + nested ops
|
|
371
438
|
where x in lo..hi / lo...hi / ..hi / lo.. range membership (incl. / excl. / open-ended)
|
|
372
439
|
where x in range(field) coerce a string field to a range, then test coverage
|
|
373
|
-
|
|
440
|
+
name the CURRENT row's field only (never climbs)
|
|
441
|
+
$value the current item itself (a scalar row, or the whole object)
|
|
442
|
+
<expr> values scalar projection: the value, not a { name: value } record
|
|
443
|
+
^name / ^^name read an enclosing row's field (exactly N scopes out); ^$value = the enclosing row
|
|
374
444
|
^name: expr / ^^name: expr lift/export a value N scopes out (flatten-append)
|
|
445
|
+
^rel collect { … } / ^^root exists { … } nested consumer over an enclosing row's relation / a named root
|
|
375
446
|
order by expr desc, expr2 ordering
|
|
376
447
|
follow rel { where … frontier … depth n by … } recursion ($depth/$stop/$leaf/$frontier)
|
|
377
448
|
${source} <collect|exists|count|first|single> { … } whole-query consumer
|
|
@@ -414,7 +485,10 @@ than approximate it. The conformance suite verifies this.
|
|
|
414
485
|
The engine never touches host objects directly; it asks a `DataContext` to
|
|
415
486
|
resolve named roots, read properties/relations, coerce results to rows, and
|
|
416
487
|
compute identity. Implement it to query an ORM graph, a remote API, or lazily
|
|
417
|
-
loaded relations — the query *semantics* stay in OQX
|
|
488
|
+
loaded relations — the query *semantics* stay in OQX. Name resolution is simple
|
|
489
|
+
for a context: a bare `field`, a `.field` segment, and a `^field` outer
|
|
490
|
+
reference each become one `get(row, key)` against exactly the row of the scope
|
|
491
|
+
they name, so a computed relation only needs `get` to know about it:
|
|
418
492
|
|
|
419
493
|
```js
|
|
420
494
|
import { parse, run } from "@omgbase/oqx";
|
|
@@ -422,7 +496,6 @@ import { parse, run } from "@omgbase/oqx";
|
|
|
422
496
|
const graph = {
|
|
423
497
|
root: (name) => name === "tree" ? [nodes.get(1)] : undefined,
|
|
424
498
|
get: (row, key) => key === "children" ? row.childIds.map(id => nodes.get(id)) : row[key],
|
|
425
|
-
has: (row, key) => key === "children" || key in row, // declare computed relations
|
|
426
499
|
toRows: (v) => v == null ? [] : Array.isArray(v) ? v : [v],
|
|
427
500
|
identity: (row) => row.id, // for follow dedup
|
|
428
501
|
};
|
|
@@ -454,7 +527,7 @@ new PlannedEngine(planner).run(parse('name from emp where dept == "eng" && level
|
|
|
454
527
|
|
|
455
528
|
This is the seam an omgbase adapter uses: its existing OQX→SQL compiler (docs/
|
|
456
529
|
blocks/nodes, the relations table, `$` intrinsics, `WITH RECURSIVE` for `follow`)
|
|
457
|
-
becomes a `QueryPlanner`, while oqx
|
|
530
|
+
becomes a `QueryPlanner`, while oqx contributes the parser, IR, semantics
|
|
458
531
|
contract, and residual executor.
|
|
459
532
|
|
|
460
533
|
## Requirements
|
package/dist/adapters/sqlite.js
CHANGED
|
@@ -54,12 +54,12 @@ export class SqliteTable {
|
|
|
54
54
|
switch (e.kind) {
|
|
55
55
|
case "lit":
|
|
56
56
|
case "binding": return true;
|
|
57
|
-
case "ident": return this.columns.has(e.name);
|
|
57
|
+
case "ident": return this.columns.has(e.name); // a bare name is always the current row's column
|
|
58
58
|
case "unary": return (e.op === "!" || e.op === "-") && this.translatable(e.expr);
|
|
59
59
|
case "logical": return this.translatable(e.left) && this.translatable(e.right);
|
|
60
60
|
case "binary":
|
|
61
61
|
return (e.op in RELOP_SQL || ARITH_SQL.has(e.op)) && this.translatable(e.left) && this.translatable(e.right);
|
|
62
|
-
default: return false; // member/index/call/in → residual
|
|
62
|
+
default: return false; // member/index/call/in/outer(^) → residual
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
translate(e, params, out) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/adapters/sqlite.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/adapters/sqlite.ts"],"names":[],"mappings":"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"}
|
package/dist/ast.d.ts
CHANGED
|
@@ -102,6 +102,10 @@ export interface Subquery {
|
|
|
102
102
|
select: SelectItem[];
|
|
103
103
|
orderBy: OrderSpec[] | null;
|
|
104
104
|
follow: Follow | null;
|
|
105
|
+
/** `values` — scalar projection mode: the (single) projected expression is the
|
|
106
|
+
* row's result itself rather than being wrapped in a `{ name: value }` record,
|
|
107
|
+
* so `name values` yields `["Bob", …]` and `$value values` yields the rows. */
|
|
108
|
+
values?: boolean;
|
|
105
109
|
}
|
|
106
110
|
/** The where-clause boolean tree: OQX owns &&/||/!/grouping so consumer ops
|
|
107
111
|
* (invisible to the scalar evaluator) compose with scalar predicates. */
|
|
@@ -129,5 +133,7 @@ export interface Query {
|
|
|
129
133
|
follow: Follow | null;
|
|
130
134
|
/** `distinct` — dedup the result rows by their projected value (see OpNode). */
|
|
131
135
|
distinct?: boolean;
|
|
136
|
+
/** `values` — scalar projection mode (see Subquery). */
|
|
137
|
+
values?: boolean;
|
|
132
138
|
}
|
|
133
139
|
//# sourceMappingURL=ast.d.ts.map
|
package/dist/ast.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"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"}
|
package/dist/context.d.ts
CHANGED
|
@@ -7,11 +7,11 @@ export interface CallResult {
|
|
|
7
7
|
export interface DataContext {
|
|
8
8
|
/** Resolve a named root collection (the `from <name>` / directive receiver). */
|
|
9
9
|
root(name: string): unknown;
|
|
10
|
-
/** Read a property/relation off a row
|
|
10
|
+
/** Read a property/relation off a row: a bare identifier (`field`), a
|
|
11
|
+
* `.field` segment, or a `^field` outer reference all come through here, each
|
|
12
|
+
* against exactly the row of the scope it names. An absent property is
|
|
13
|
+
* `undefined`; the engine never looks elsewhere for it. */
|
|
11
14
|
get(row: unknown, key: string): unknown;
|
|
12
|
-
/** Whether a row has a property — decides scope-climbing for a bare identifier
|
|
13
|
-
* (a present-but-undefined field stops the climb). Defaults to a key check. */
|
|
14
|
-
has?(row: unknown, key: string): boolean;
|
|
15
15
|
/** Coerce a relation/source value into rows (may be lazy). */
|
|
16
16
|
toRows(value: unknown): Iterable<unknown>;
|
|
17
17
|
/** Identity of a row for `follow` cycle detection / dedup. */
|
|
@@ -29,7 +29,6 @@ export declare class DefaultContext implements DataContext {
|
|
|
29
29
|
constructor(roots?: Record<string, unknown>);
|
|
30
30
|
root(name: string): unknown;
|
|
31
31
|
get(row: unknown, key: string): unknown;
|
|
32
|
-
has(row: unknown, key: string): boolean;
|
|
33
32
|
toRows(value: unknown): Iterable<unknown>;
|
|
34
33
|
identity(row: unknown): unknown;
|
|
35
34
|
callFunction(name: string, args: unknown[]): CallResult;
|
package/dist/context.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAYA;kEACkE;AAClE,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,gFAAgF;IAChF,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5B
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAYA;kEACkE;AAClE,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,gFAAgF;IAChF,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5B;;;+DAG2D;IAC3D,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACxC,8DAA8D;IAC9D,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC1C,8DAA8D;IAC9D,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;IAChC,2EAA2E;IAC3E,YAAY,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC;IACzD,oEAAoE;IACpE,UAAU,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC;CACvE;AAED;;yEAEyE;AACzE,qBAAa,cAAe,YAAW,WAAW;IAChD,OAAO,CAAC,KAAK,CAA0B;gBAE3B,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;IAI/C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI3B,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAKvC,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC;IAIzC,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO;IAK/B,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,UAAU;IAKvD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,UAAU;CAIrE"}
|
package/dist/context.js
CHANGED
package/dist/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,iFAAiF;AACjF,gFAAgF;AAChF,4EAA4E;AAC5E,gFAAgF;AAChF,gFAAgF;AAChF,EAAE;AACF,gFAAgF;AAChF,2EAA2E;AAE3E,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AA2BtF;;yEAEyE;AACzE,MAAM,OAAO,cAAc;IACjB,KAAK,CAA0B;IAEvC,YAAY,QAAiC,EAAE;QAC7C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,IAAI,CAAC,IAAY;QACf,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,GAAG,CAAC,GAAY,EAAE,GAAW;QAC3B,IAAI,GAAG,IAAI,IAAI;YAAE,OAAO,SAAS,CAAC;QAClC,OAAQ,MAAM,CAAC,GAAG,CAA6B,CAAC,GAAG,CAAC,CAAC;IACvD,CAAC;IAED,
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,iFAAiF;AACjF,gFAAgF;AAChF,4EAA4E;AAC5E,gFAAgF;AAChF,gFAAgF;AAChF,EAAE;AACF,gFAAgF;AAChF,2EAA2E;AAE3E,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AA2BtF;;yEAEyE;AACzE,MAAM,OAAO,cAAc;IACjB,KAAK,CAA0B;IAEvC,YAAY,QAAiC,EAAE;QAC7C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,IAAI,CAAC,IAAY;QACf,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,GAAG,CAAC,GAAY,EAAE,GAAW;QAC3B,IAAI,GAAG,IAAI,IAAI;YAAE,OAAO,SAAS,CAAC;QAClC,OAAQ,MAAM,CAAC,GAAG,CAA6B,CAAC,GAAG,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,CAAC,KAAc;QACnB,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;IAED,QAAQ,CAAC,GAAY;QACnB,IAAI,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,IAAI,IAAI,GAAG;YAAE,OAAQ,GAA+B,CAAC,EAAE,CAAC;QACtG,OAAO,GAAG,CAAC;IACb,CAAC;IAED,YAAY,CAAC,IAAY,EAAE,IAAe;QACxC,MAAM,EAAE,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACnC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACtE,CAAC;IAED,UAAU,CAAC,IAAY,EAAE,IAAa,EAAE,IAAe;QACrD,MAAM,EAAE,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;QACjC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC5E,CAAC;CACF"}
|
package/dist/engine.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ export declare class InMemoryEngine implements Engine {
|
|
|
33
33
|
private followWalk;
|
|
34
34
|
private shape;
|
|
35
35
|
private projectRow;
|
|
36
|
+
private itemValue;
|
|
36
37
|
private evalWhere;
|
|
37
38
|
private evalWhereOp;
|
|
38
39
|
private anyMatch;
|
|
@@ -41,8 +42,7 @@ export declare class InMemoryEngine implements Engine {
|
|
|
41
42
|
private evalCollectValue;
|
|
42
43
|
private sortScopes;
|
|
43
44
|
private evalExpr;
|
|
44
|
-
private
|
|
45
|
-
private has;
|
|
45
|
+
private resolveIn;
|
|
46
46
|
private evalCall;
|
|
47
47
|
}
|
|
48
48
|
/** Convenience: run a query with plain-object roots (the default context). */
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACV,KAAK,EACN,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAOhD,yEAAyE;AACzE,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,OAAO,EAAE,CAAA;CAAE,GACxC;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GACvC;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,GAC1C;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,CAAC;AAEhD,6EAA6E;AAC7E,MAAM,WAAW,MAAM;IACrB,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,CAAC;CAC5D;AAsBD,qBAAa,cAAe,YAAW,MAAM;IAC3C,OAAO,CAAC,GAAG,CAAc;gBAEb,OAAO,GAAE,WAAkC;IAIvD,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,GAAE,SAAS,OAAO,EAAO,GAAG,SAAS;IAsC/D,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,KAAK;IAMb,OAAO,CAAC,SAAS;IAcjB,OAAO,CAAC,iBAAiB;IA2BzB,OAAO,CAAC,UAAU;IA6DlB,OAAO,CAAC,KAAK;IAcb,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,SAAS;IAcjB,OAAO,CAAC,WAAW;IA6BnB,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAE,aAAa;IAStB,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,gBAAgB;IAyBxB,OAAO,CAAC,UAAU;IAwBlB,OAAO,CAAC,QAAQ;IAuDhB,OAAO,CAAC,SAAS;IAQjB,OAAO,CAAC,QAAQ;CAYjB;AA+ED,8EAA8E;AAC9E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,GAAG,SAAS,CAG9F"}
|
package/dist/engine.js
CHANGED
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
// `first`/`single` stop early when the result is unordered; `count` never
|
|
7
7
|
// materializes rows; and within an `&&` the cheap scalar leaves are evaluated
|
|
8
8
|
// before expensive consumer-op leaves (which each drive a nested traversal).
|
|
9
|
+
//
|
|
10
|
+
// Name resolution is strictly lexical and LOCAL: a bare identifier is read from
|
|
11
|
+
// the current scope only, and an enclosing scope is reached solely through an
|
|
12
|
+
// explicit `^name` (exactly one scope out per caret). There is no implicit
|
|
13
|
+
// fall-through from an inner scope to an outer one — see `resolveIn`.
|
|
9
14
|
import { DefaultContext } from "./context.js";
|
|
10
15
|
import { OqxError } from "./errors.js";
|
|
11
16
|
import { equals, relate, arith, membership, truthy, toNumber, compareForSort, makeRange, } from "./semantics.js";
|
|
@@ -53,8 +58,8 @@ export class InMemoryEngine {
|
|
|
53
58
|
}
|
|
54
59
|
this.sortScopes(kept, query.orderBy);
|
|
55
60
|
if (query.distinct)
|
|
56
|
-
kept = this.dedupByProjection(kept, query
|
|
57
|
-
return this.shape(query.consumer, kept, query
|
|
61
|
+
kept = this.dedupByProjection(kept, query);
|
|
62
|
+
return this.shape(query.consumer, kept, query);
|
|
58
63
|
}
|
|
59
64
|
// A where match that needs no lift capture (exists/count fast paths).
|
|
60
65
|
matches(where, row, parent) {
|
|
@@ -78,19 +83,19 @@ export class InMemoryEngine {
|
|
|
78
83
|
scopes = scopes.filter((s) => this.evalWhere(post, s));
|
|
79
84
|
this.sortScopes(scopes, query.orderBy);
|
|
80
85
|
if (query.distinct)
|
|
81
|
-
scopes = this.dedupByProjection(scopes, query
|
|
82
|
-
return this.shape(query.consumer, scopes, query
|
|
86
|
+
scopes = this.dedupByProjection(scopes, query);
|
|
87
|
+
return this.shape(query.consumer, scopes, query);
|
|
83
88
|
}
|
|
84
89
|
// Dedup scopes by their PROJECTED value (`distinct`): keep the first scope per
|
|
85
90
|
// distinct projection, preserving order. An empty projection dedups by row
|
|
86
91
|
// identity (so `count distinct { }` counts distinct rows).
|
|
87
|
-
dedupByProjection(scopes,
|
|
92
|
+
dedupByProjection(scopes, proj) {
|
|
88
93
|
const seen = new Set();
|
|
89
94
|
const out = [];
|
|
90
95
|
for (const s of scopes) {
|
|
91
|
-
const key = select.length === 0
|
|
96
|
+
const key = proj.select.length === 0
|
|
92
97
|
? `i:${String(this.ctx.identity(s.row))}`
|
|
93
|
-
: `p:${stableStringify(this.projectRow(
|
|
98
|
+
: `p:${stableStringify(this.projectRow(proj, s))}`;
|
|
94
99
|
if (seen.has(key))
|
|
95
100
|
continue;
|
|
96
101
|
seen.add(key);
|
|
@@ -171,27 +176,34 @@ export class InMemoryEngine {
|
|
|
171
176
|
}));
|
|
172
177
|
}
|
|
173
178
|
// ---- consumer shaping -----------------------------------------------------
|
|
174
|
-
shape(consumer, scopes,
|
|
179
|
+
shape(consumer, scopes, proj) {
|
|
175
180
|
switch (consumer) {
|
|
176
181
|
case "exists": return { consumer, exists: scopes.length > 0 };
|
|
177
182
|
case "count": return { consumer, count: scopes.length };
|
|
178
|
-
case "collect": return { consumer, rows: scopes.map((s) => this.projectRow(
|
|
179
|
-
case "first": return { consumer, row: scopes.length > 0 ? this.projectRow(
|
|
183
|
+
case "collect": return { consumer, rows: scopes.map((s) => this.projectRow(proj, s)) };
|
|
184
|
+
case "first": return { consumer, row: scopes.length > 0 ? this.projectRow(proj, scopes[0]) : null };
|
|
180
185
|
case "single":
|
|
181
186
|
if (scopes.length > 1)
|
|
182
187
|
throw new OqxError(`single { … } matched ${scopes.length} rows; use first { … } for zero-or-one`, "eval");
|
|
183
|
-
return { consumer, row: scopes.length > 0 ? this.projectRow(
|
|
188
|
+
return { consumer, row: scopes.length > 0 ? this.projectRow(proj, scopes[0]) : null };
|
|
184
189
|
}
|
|
185
190
|
}
|
|
186
|
-
|
|
191
|
+
// The per-row result: the raw row (empty projection), the single item's value
|
|
192
|
+
// itself (`values` mode), or a `{ name: value }` record.
|
|
193
|
+
projectRow(proj, scope) {
|
|
194
|
+
const { select } = proj;
|
|
187
195
|
if (select.length === 0)
|
|
188
196
|
return scope.row;
|
|
197
|
+
if (proj.values)
|
|
198
|
+
return this.itemValue(select[0], scope);
|
|
189
199
|
const out = {};
|
|
190
|
-
for (const item of select)
|
|
191
|
-
out[item.name] =
|
|
192
|
-
}
|
|
200
|
+
for (const item of select)
|
|
201
|
+
out[item.name] = this.itemValue(item, scope);
|
|
193
202
|
return out;
|
|
194
203
|
}
|
|
204
|
+
itemValue(item, scope) {
|
|
205
|
+
return item.kind === "field" ? this.evalExpr(item.expr, scope) : this.evalCollectValue(item.op, scope);
|
|
206
|
+
}
|
|
195
207
|
// ---- where evaluation -----------------------------------------------------
|
|
196
208
|
evalWhere(w, scope) {
|
|
197
209
|
switch (w.kind) {
|
|
@@ -234,7 +246,7 @@ export class InMemoryEngine {
|
|
|
234
246
|
if (op.op === "count") {
|
|
235
247
|
let rows = this.matchRows(op, scope);
|
|
236
248
|
if (op.distinct)
|
|
237
|
-
rows = this.dedupByProjection(rows, op.sub
|
|
249
|
+
rows = this.dedupByProjection(rows, op.sub);
|
|
238
250
|
const n = rows.length;
|
|
239
251
|
return op.countCmp ? compareCount(n, op.countCmp) : n > 0;
|
|
240
252
|
}
|
|
@@ -277,14 +289,14 @@ export class InMemoryEngine {
|
|
|
277
289
|
}
|
|
278
290
|
this.sortScopes(scopes, sub.orderBy);
|
|
279
291
|
if (op.distinct)
|
|
280
|
-
scopes = this.dedupByProjection(scopes, sub
|
|
292
|
+
scopes = this.dedupByProjection(scopes, sub);
|
|
281
293
|
switch (op.op) {
|
|
282
|
-
case "collect": return scopes.map((s) => this.projectRow(sub
|
|
283
|
-
case "first": return scopes.length > 0 ? this.projectRow(sub
|
|
294
|
+
case "collect": return scopes.map((s) => this.projectRow(sub, s));
|
|
295
|
+
case "first": return scopes.length > 0 ? this.projectRow(sub, scopes[0]) : null;
|
|
284
296
|
case "single":
|
|
285
297
|
if (scopes.length > 1)
|
|
286
298
|
throw new OqxError(`single { … } for '${describeReceiver(op.receiver)}' matched ${scopes.length} rows`, "eval");
|
|
287
|
-
return scopes.length > 0 ? this.projectRow(sub
|
|
299
|
+
return scopes.length > 0 ? this.projectRow(sub, scopes[0]) : null;
|
|
288
300
|
default: throw new OqxError(`${op.op} { … } is not valid in select position`, "eval");
|
|
289
301
|
}
|
|
290
302
|
}
|
|
@@ -320,12 +332,14 @@ export class InMemoryEngine {
|
|
|
320
332
|
switch (e.kind) {
|
|
321
333
|
case "lit": return e.value;
|
|
322
334
|
case "binding": return scope.bindings[e.index];
|
|
323
|
-
case "ident": return this.
|
|
335
|
+
case "ident": return this.resolveIn(e.name, scope);
|
|
324
336
|
case "outer": {
|
|
337
|
+
// `^name` reads from EXACTLY `levels` scopes out — the target scope is
|
|
338
|
+
// resolved locally, never climbed further. Past the root it is absent.
|
|
325
339
|
let s = scope;
|
|
326
340
|
for (let i = 0; i < e.levels && s; i++)
|
|
327
341
|
s = s.parent;
|
|
328
|
-
return s ? this.
|
|
342
|
+
return s ? this.resolveIn(e.name, s) : undefined;
|
|
329
343
|
}
|
|
330
344
|
case "member": {
|
|
331
345
|
const r = this.evalExpr(e.recv, scope);
|
|
@@ -354,33 +368,29 @@ export class InMemoryEngine {
|
|
|
354
368
|
case "range": return makeRange(e.lo == null ? null : this.evalExpr(e.lo, scope), e.hi == null ? null : this.evalExpr(e.hi, scope), e.exclusiveEnd);
|
|
355
369
|
}
|
|
356
370
|
}
|
|
357
|
-
// Resolve a
|
|
358
|
-
//
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
has(row, name) {
|
|
381
|
-
if (this.ctx.has)
|
|
382
|
-
return this.ctx.has(row, name);
|
|
383
|
-
return row != null && typeof row === "object" && name in row;
|
|
371
|
+
// Resolve a name against ONE scope — never its ancestors. A scope provides,
|
|
372
|
+
// in order: `$value` (the scope's row itself — the current item, whatever its
|
|
373
|
+
// type, so scalar collections are queryable; absent at the root, which has no
|
|
374
|
+
// row); the recursion intrinsics (`$depth`, …) when it is a follow occurrence;
|
|
375
|
+
// values lifted into it by `^name:` items; then either the row's own property
|
|
376
|
+
// or, for the root scope (no row), the context's named roots.
|
|
377
|
+
//
|
|
378
|
+
// A name the scope lacks is simply absent (undefined). It does NOT fall
|
|
379
|
+
// through to an enclosing scope, so a query's meaning never depends on which
|
|
380
|
+
// properties an inner row happens to have: adding a same-named property to an
|
|
381
|
+
// inner row cannot capture an outer reference, and an outer reference is
|
|
382
|
+
// always spelled explicitly as `^name`. Present-but-falsy values (null, false,
|
|
383
|
+
// 0, "") need no special case — there is no "absent, so look outward" rule.
|
|
384
|
+
resolveIn(name, scope) {
|
|
385
|
+
if (name === "$value")
|
|
386
|
+
return scope.parent === null ? undefined : scope.row;
|
|
387
|
+
if (RECUR.has(name))
|
|
388
|
+
return scope.meta ? scope.meta[name] : undefined;
|
|
389
|
+
if (scope.lifts && name in scope.lifts)
|
|
390
|
+
return scope.lifts[name];
|
|
391
|
+
if (scope.parent === null)
|
|
392
|
+
return this.ctx.root(name);
|
|
393
|
+
return this.ctx.get(scope.row, name);
|
|
384
394
|
}
|
|
385
395
|
evalCall(e, scope) {
|
|
386
396
|
const args = e.args.map((a) => this.evalExpr(a, scope));
|