@elaraai/east 1.0.84 → 1.0.85
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/dist/src/analyze.d.ts.map +1 -1
- package/dist/src/analyze.js +48 -0
- package/dist/src/analyze.js.map +1 -1
- package/dist/src/ast_to_ir.coerce_to.spec.js +4 -4
- package/dist/src/ast_to_ir.coerce_to.spec.js.map +1 -1
- package/dist/src/ast_to_ir.d.ts +6 -1
- package/dist/src/ast_to_ir.d.ts.map +1 -1
- package/dist/src/ast_to_ir.js +20 -7
- package/dist/src/ast_to_ir.js.map +1 -1
- package/dist/src/builtins.d.ts +1 -1
- package/dist/src/builtins.d.ts.map +1 -1
- package/dist/src/builtins.js +52 -0
- package/dist/src/builtins.js.map +1 -1
- package/dist/src/codegen/canonical.d.ts +37 -0
- package/dist/src/codegen/canonical.d.ts.map +1 -0
- package/dist/src/codegen/canonical.js +208 -0
- package/dist/src/codegen/canonical.js.map +1 -0
- package/dist/src/codegen/codegen.spec.js +132 -201
- package/dist/src/codegen/codegen.spec.js.map +1 -1
- package/dist/src/codegen/printer.d.ts.map +1 -1
- package/dist/src/codegen/printer.js +142 -24
- package/dist/src/codegen/printer.js.map +1 -1
- package/dist/src/codegen/spellings.d.ts +4 -1
- package/dist/src/codegen/spellings.d.ts.map +1 -1
- package/dist/src/codegen/spellings.js +5 -1
- package/dist/src/codegen/spellings.js.map +1 -1
- package/dist/src/compile/builtins/index.d.ts.map +1 -1
- package/dist/src/compile/builtins/index.js +2 -0
- package/dist/src/compile/builtins/index.js.map +1 -1
- package/dist/src/compile/builtins/primitives.d.ts.map +1 -1
- package/dist/src/compile/builtins/primitives.js +2 -1
- package/dist/src/compile/builtins/primitives.js.map +1 -1
- package/dist/src/compile/builtins/query.d.ts +10 -0
- package/dist/src/compile/builtins/query.d.ts.map +1 -0
- package/dist/src/compile/builtins/query.js +5 -0
- package/dist/src/compile/builtins/query.js.map +1 -0
- package/dist/src/expr/block.d.ts.map +1 -1
- package/dist/src/expr/block.js +2 -1
- package/dist/src/expr/block.js.map +1 -1
- package/dist/src/expr/index.d.ts +75 -47
- package/dist/src/expr/index.d.ts.map +1 -1
- package/dist/src/expr/index.js +31 -0
- package/dist/src/expr/index.js.map +1 -1
- package/dist/src/expr/libs/datetime.d.ts +20 -20
- package/dist/src/expr/libs/datetime.d.ts.map +1 -1
- package/dist/src/expr/libs/datetime.js +208 -167
- package/dist/src/expr/libs/datetime.js.map +1 -1
- package/dist/src/expr/libs/float.d.ts +15 -15
- package/dist/src/expr/libs/float.d.ts.map +1 -1
- package/dist/src/expr/libs/float.js +298 -267
- package/dist/src/expr/libs/float.js.map +1 -1
- package/dist/src/expr/libs/integer.d.ts +12 -12
- package/dist/src/expr/libs/integer.d.ts.map +1 -1
- package/dist/src/expr/libs/integer.js +219 -194
- package/dist/src/expr/libs/integer.js.map +1 -1
- package/dist/src/expr/libs/lazy.d.ts +28 -0
- package/dist/src/expr/libs/lazy.d.ts.map +1 -0
- package/dist/src/expr/libs/lazy.js +55 -0
- package/dist/src/expr/libs/lazy.js.map +1 -0
- package/dist/src/expr/libs/lazy.spec.d.ts +6 -0
- package/dist/src/expr/libs/lazy.spec.d.ts.map +1 -0
- package/dist/src/expr/libs/lazy.spec.js +102 -0
- package/dist/src/expr/libs/lazy.spec.js.map +1 -0
- package/dist/src/expr/query.d.ts +60 -0
- package/dist/src/expr/query.d.ts.map +1 -0
- package/dist/src/expr/query.js +84 -0
- package/dist/src/expr/query.js.map +1 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/internal.d.ts +4 -0
- package/dist/src/internal.d.ts.map +1 -1
- package/dist/src/internal.js +9 -0
- package/dist/src/internal.js.map +1 -1
- package/dist/src/location.d.ts +22 -0
- package/dist/src/location.d.ts.map +1 -1
- package/dist/src/location.js +31 -0
- package/dist/src/location.js.map +1 -1
- package/dist/src/naming.d.ts +5 -3
- package/dist/src/naming.d.ts.map +1 -1
- package/dist/src/naming.js.map +1 -1
- package/dist/src/portable.spec.d.ts +6 -0
- package/dist/src/portable.spec.d.ts.map +1 -0
- package/dist/src/portable.spec.js +181 -0
- package/dist/src/portable.spec.js.map +1 -0
- package/dist/src/query/evaluate.d.ts +98 -0
- package/dist/src/query/evaluate.d.ts.map +1 -0
- package/dist/src/query/evaluate.js +193 -0
- package/dist/src/query/evaluate.js.map +1 -0
- package/dist/src/query/index.d.ts +17 -0
- package/dist/src/query/index.d.ts.map +1 -0
- package/dist/src/query/index.js +17 -0
- package/dist/src/query/index.js.map +1 -0
- package/dist/src/query/jq/catalog.d.ts +174 -0
- package/dist/src/query/jq/catalog.d.ts.map +1 -0
- package/dist/src/query/jq/catalog.js +1740 -0
- package/dist/src/query/jq/catalog.js.map +1 -0
- package/dist/src/query/jq/check.d.ts +210 -0
- package/dist/src/query/jq/check.d.ts.map +1 -0
- package/dist/src/query/jq/check.js +2261 -0
- package/dist/src/query/jq/check.js.map +1 -0
- package/dist/src/query/jq/complete.d.ts +89 -0
- package/dist/src/query/jq/complete.d.ts.map +1 -0
- package/dist/src/query/jq/complete.js +320 -0
- package/dist/src/query/jq/complete.js.map +1 -0
- package/dist/src/query/jq/describe.d.ts +53 -0
- package/dist/src/query/jq/describe.d.ts.map +1 -0
- package/dist/src/query/jq/describe.js +149 -0
- package/dist/src/query/jq/describe.js.map +1 -0
- package/dist/src/query/jq/lex.d.ts +118 -0
- package/dist/src/query/jq/lex.d.ts.map +1 -0
- package/dist/src/query/jq/lex.js +371 -0
- package/dist/src/query/jq/lex.js.map +1 -0
- package/dist/src/query/jq/literals.d.ts +49 -0
- package/dist/src/query/jq/literals.d.ts.map +1 -0
- package/dist/src/query/jq/literals.js +78 -0
- package/dist/src/query/jq/literals.js.map +1 -0
- package/dist/src/query/jq/messages.d.ts +126 -0
- package/dist/src/query/jq/messages.d.ts.map +1 -0
- package/dist/src/query/jq/messages.js +172 -0
- package/dist/src/query/jq/messages.js.map +1 -0
- package/dist/src/query/jq/parse.d.ts +70 -0
- package/dist/src/query/jq/parse.d.ts.map +1 -0
- package/dist/src/query/jq/parse.js +1013 -0
- package/dist/src/query/jq/parse.js.map +1 -0
- package/dist/src/query/jq/print.d.ts +59 -0
- package/dist/src/query/jq/print.d.ts.map +1 -0
- package/dist/src/query/jq/print.js +587 -0
- package/dist/src/query/jq/print.js.map +1 -0
- package/dist/src/query/jq/shapes.d.ts +345 -0
- package/dist/src/query/jq/shapes.d.ts.map +1 -0
- package/dist/src/query/jq/shapes.js +584 -0
- package/dist/src/query/jq/shapes.js.map +1 -0
- package/dist/src/query/jq/spans.d.ts +133 -0
- package/dist/src/query/jq/spans.d.ts.map +1 -0
- package/dist/src/query/jq/spans.js +195 -0
- package/dist/src/query/jq/spans.js.map +1 -0
- package/dist/src/query/jq/split.d.ts +334 -0
- package/dist/src/query/jq/split.d.ts.map +1 -0
- package/dist/src/query/jq/split.js +2108 -0
- package/dist/src/query/jq/split.js.map +1 -0
- package/dist/src/query/jq/strftime.d.ts +20 -0
- package/dist/src/query/jq/strftime.d.ts.map +1 -0
- package/dist/src/query/jq/strftime.js +65 -0
- package/dist/src/query/jq/strftime.js.map +1 -0
- package/dist/src/query/jq/summary.d.ts +139 -0
- package/dist/src/query/jq/summary.d.ts.map +1 -0
- package/dist/src/query/jq/summary.js +222 -0
- package/dist/src/query/jq/summary.js.map +1 -0
- package/dist/src/query/jq/translate-builtins.d.ts +21 -0
- package/dist/src/query/jq/translate-builtins.d.ts.map +1 -0
- package/dist/src/query/jq/translate-builtins.js +1873 -0
- package/dist/src/query/jq/translate-builtins.js.map +1 -0
- package/dist/src/query/jq/translate.d.ts +627 -0
- package/dist/src/query/jq/translate.d.ts.map +1 -0
- package/dist/src/query/jq/translate.js +3364 -0
- package/dist/src/query/jq/translate.js.map +1 -0
- package/dist/src/query/types.d.ts +505 -0
- package/dist/src/query/types.d.ts.map +1 -0
- package/dist/src/query/types.js +231 -0
- package/dist/src/query/types.js.map +1 -0
- package/dist/src/runner_exec.d.ts +134 -0
- package/dist/src/runner_exec.d.ts.map +1 -0
- package/dist/src/runner_exec.js +371 -0
- package/dist/src/runner_exec.js.map +1 -0
- package/dist/src/runner_exec.spec.d.ts +6 -0
- package/dist/src/runner_exec.spec.d.ts.map +1 -0
- package/dist/src/runner_exec.spec.js +198 -0
- package/dist/src/runner_exec.spec.js.map +1 -0
- package/dist/src/runner_io.d.ts +370 -0
- package/dist/src/runner_io.d.ts.map +1 -0
- package/dist/src/runner_io.js +563 -0
- package/dist/src/runner_io.js.map +1 -0
- package/dist/src/runner_io.spec.d.ts +6 -0
- package/dist/src/runner_io.spec.d.ts.map +1 -0
- package/dist/src/runner_io.spec.js +224 -0
- package/dist/src/runner_io.spec.js.map +1 -0
- package/dist/src/runner_merge.d.ts +95 -0
- package/dist/src/runner_merge.d.ts.map +1 -0
- package/dist/src/runner_merge.js +238 -0
- package/dist/src/runner_merge.js.map +1 -0
- package/dist/src/runner_protocol.d.ts +11 -4
- package/dist/src/runner_protocol.d.ts.map +1 -1
- package/dist/src/runner_protocol.js +11 -4
- package/dist/src/runner_protocol.js.map +1 -1
- package/dist/src/serialization/beast2/index.d.ts +1 -1
- package/dist/src/serialization/beast2/index.d.ts.map +1 -1
- package/dist/src/serialization/beast2/index.js.map +1 -1
- package/dist/src/serialization/beast2/v5/frames.d.ts +10 -6
- package/dist/src/serialization/beast2/v5/frames.d.ts.map +1 -1
- package/dist/src/serialization/beast2/v5/frames.js +10 -6
- package/dist/src/serialization/beast2/v5/frames.js.map +1 -1
- package/dist/src/serialization/beast2/v5/frames.spec.d.ts +6 -0
- package/dist/src/serialization/beast2/v5/frames.spec.d.ts.map +1 -0
- package/dist/src/serialization/beast2/v5/frames.spec.js +42 -0
- package/dist/src/serialization/beast2/v5/frames.spec.js.map +1 -0
- package/dist/src/serialization/beast2/v5/index.spec.js +108 -0
- package/dist/src/serialization/beast2/v5/index.spec.js.map +1 -1
- package/dist/src/serialization/beast2/v5/lazy.d.ts +12 -7
- package/dist/src/serialization/beast2/v5/lazy.d.ts.map +1 -1
- package/dist/src/serialization/beast2/v5/lazy.js +16 -6
- package/dist/src/serialization/beast2/v5/lazy.js.map +1 -1
- package/dist/src/serialization/beast2/v5/lazy.spec.js +159 -18
- package/dist/src/serialization/beast2/v5/lazy.spec.js.map +1 -1
- package/dist/src/serialization/beast2/v5/stream.d.ts +110 -21
- package/dist/src/serialization/beast2/v5/stream.d.ts.map +1 -1
- package/dist/src/serialization/beast2/v5/stream.js +153 -42
- package/dist/src/serialization/beast2/v5/stream.js.map +1 -1
- package/dist/src/serialization/beast2/v5/weight.d.ts +36 -0
- package/dist/src/serialization/beast2/v5/weight.d.ts.map +1 -0
- package/dist/src/serialization/beast2/v5/weight.js +238 -0
- package/dist/src/serialization/beast2/v5/weight.js.map +1 -0
- package/dist/src/serialization/crc32.d.ts +38 -0
- package/dist/src/serialization/crc32.d.ts.map +1 -0
- package/dist/src/serialization/crc32.js +70 -0
- package/dist/src/serialization/crc32.js.map +1 -0
- package/dist/src/serialization/crc32.spec.d.ts +6 -0
- package/dist/src/serialization/crc32.spec.d.ts.map +1 -0
- package/dist/src/serialization/crc32.spec.js +92 -0
- package/dist/src/serialization/crc32.spec.js.map +1 -0
- package/dist/src/serialization/east-inferred.spec.js +4 -0
- package/dist/src/serialization/east-inferred.spec.js.map +1 -1
- package/dist/src/serialization/east.d.ts.map +1 -1
- package/dist/src/serialization/east.js +134 -110
- package/dist/src/serialization/east.js.map +1 -1
- package/dist/src/serialization/east.spec.js +185 -4
- package/dist/src/serialization/east.spec.js.map +1 -1
- package/dist/src/serialization/east_text.d.ts +25 -0
- package/dist/src/serialization/east_text.d.ts.map +1 -0
- package/dist/src/serialization/east_text.js +29 -0
- package/dist/src/serialization/east_text.js.map +1 -0
- package/dist/src/serialization/index.d.ts +1 -1
- package/dist/src/serialization/index.d.ts.map +1 -1
- package/dist/src/types.d.ts +1 -1
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +7 -6
- package/dist/src/types.js.map +1 -1
- package/package.json +6 -3
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* IR up to what the cross-runtime contract lets two builders differ on — the
|
|
7
|
+
* erasure `east-c ir normalize` makes, made in TypeScript.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
import { isVariant } from "../containers/variant.js";
|
|
12
|
+
/**
|
|
13
|
+
* The canonical form of an IR value: loc_ids zeroed, variables and labels
|
|
14
|
+
* renamed in the order they are bound and met, recursive type ids renumbered,
|
|
15
|
+
* bigints as strings. Two IR values are the same program when their
|
|
16
|
+
* canonical forms are {@link canonicalEqual}.
|
|
17
|
+
*
|
|
18
|
+
* @param node - an IR value (a node, or any value holding nodes)
|
|
19
|
+
* @returns its canonical form: plain arrays, objects and leaves
|
|
20
|
+
*
|
|
21
|
+
* @remarks
|
|
22
|
+
* Variables are renamed per BINDING, resolved lexically — a parameter, a
|
|
23
|
+
* Let, a loop, match or catch variable each mint a canonical name for the
|
|
24
|
+
* body they scope — not per name: sibling bodies reuse a name (three
|
|
25
|
+
* callbacks each naming their element `x`), and two programs that bind the
|
|
26
|
+
* same variable under different names are the same program. A variable no
|
|
27
|
+
* binding in the value reaches is `free:<name>`.
|
|
28
|
+
*
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
31
|
+
export function canonicalIR(node) {
|
|
32
|
+
const labels = new Map();
|
|
33
|
+
const recursive = new Map();
|
|
34
|
+
let variables = 0;
|
|
35
|
+
const rename = (table, name, prefix) => {
|
|
36
|
+
let hit = table.get(name);
|
|
37
|
+
if (hit === undefined) {
|
|
38
|
+
hit = `${prefix}${table.size}`;
|
|
39
|
+
table.set(name, hit);
|
|
40
|
+
}
|
|
41
|
+
return hit;
|
|
42
|
+
};
|
|
43
|
+
/** Binds `variable` (a Variable node) in `scope`: its canonical name, minted in binding order. */
|
|
44
|
+
const bind = (variable, scope) => {
|
|
45
|
+
const name = `v${variables}`;
|
|
46
|
+
variables += 1;
|
|
47
|
+
scope.set(variable.value.name, name);
|
|
48
|
+
return variableNode(variable, name);
|
|
49
|
+
};
|
|
50
|
+
const variableNode = (v, name) => ["Variable", { type: walk(v.value.type, "type"), name, mutable: v.value.mutable, captured: v.value.captured }];
|
|
51
|
+
const lookup = (scopes, name) => {
|
|
52
|
+
for (let i = scopes.length - 1; i >= 0; i--) {
|
|
53
|
+
const hit = scopes[i].get(name);
|
|
54
|
+
if (hit !== undefined)
|
|
55
|
+
return hit;
|
|
56
|
+
}
|
|
57
|
+
return `free:${name}`;
|
|
58
|
+
};
|
|
59
|
+
// the scope chain is threaded through the walk: a body opens a scope, a Let extends the current one
|
|
60
|
+
let chain = [new Map()];
|
|
61
|
+
const inScope = (f) => {
|
|
62
|
+
chain = [...chain, new Map()];
|
|
63
|
+
try {
|
|
64
|
+
return f();
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
chain = chain.slice(0, -1);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const walk = (v, context) => {
|
|
71
|
+
if (v === null || v === undefined)
|
|
72
|
+
return null;
|
|
73
|
+
if (typeof v === "bigint")
|
|
74
|
+
return v.toString();
|
|
75
|
+
if (v instanceof Date)
|
|
76
|
+
return v.toISOString();
|
|
77
|
+
if (v instanceof Uint8Array)
|
|
78
|
+
return Array.from(v);
|
|
79
|
+
if (Array.isArray(v))
|
|
80
|
+
return v.map(x => walk(x, context));
|
|
81
|
+
if (isVariant(v)) {
|
|
82
|
+
const tag = v.type;
|
|
83
|
+
const p = v.value;
|
|
84
|
+
if (context === "type") {
|
|
85
|
+
if (tag === "Recursive") {
|
|
86
|
+
const payload = v.value;
|
|
87
|
+
if (payload.type === "ref")
|
|
88
|
+
return ["Recursive", ["ref", rename(recursive, String(payload.value), "r")]];
|
|
89
|
+
return ["Recursive", ["wrapper", { id: rename(recursive, String(payload.value.id), "r"), inner: walk(payload.value.inner, "type") }]];
|
|
90
|
+
}
|
|
91
|
+
return [tag, walk(v.value, context)];
|
|
92
|
+
}
|
|
93
|
+
switch (tag) {
|
|
94
|
+
case "Variable":
|
|
95
|
+
return variableNode(v, lookup(chain, p.name));
|
|
96
|
+
case "Function":
|
|
97
|
+
case "AsyncFunction":
|
|
98
|
+
return inScope(() => [tag, {
|
|
99
|
+
type: walk(p.type, "type"),
|
|
100
|
+
parameters: p.parameters.map(q => bind(q, chain[chain.length - 1])),
|
|
101
|
+
body: walk(p.body, context),
|
|
102
|
+
captures: walk(p.captures, context),
|
|
103
|
+
}]);
|
|
104
|
+
case "Let": {
|
|
105
|
+
const value = walk(p.value, context);
|
|
106
|
+
return [tag, { type: walk(p.type, "type"), value, variable: bind(p.variable, chain[chain.length - 1]) }];
|
|
107
|
+
}
|
|
108
|
+
case "ForArray":
|
|
109
|
+
case "ForDict":
|
|
110
|
+
case "ForSet": {
|
|
111
|
+
const source = tag === "ForArray" ? "array" : tag === "ForDict" ? "dict" : "set";
|
|
112
|
+
const coll = walk(p[source], context);
|
|
113
|
+
return inScope(() => {
|
|
114
|
+
const scope = chain[chain.length - 1];
|
|
115
|
+
const out = { type: walk(p.type, "type"), [source]: coll, label: { name: rename(labels, p.label.name, "L") } };
|
|
116
|
+
if (tag !== "ForSet")
|
|
117
|
+
out["value"] = bind(p.value, scope);
|
|
118
|
+
out["key"] = bind(p.key, scope);
|
|
119
|
+
out["body"] = walk(p.body, context);
|
|
120
|
+
return [tag, out];
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
case "Match": {
|
|
124
|
+
const subject = walk(p.variant, context);
|
|
125
|
+
const cases = p.cases.map(c => inScope(() => ({
|
|
126
|
+
case: c.case, variable: bind(c.variable, chain[chain.length - 1]), body: walk(c.body, context),
|
|
127
|
+
})));
|
|
128
|
+
return [tag, { type: walk(p.type, "type"), variant: subject, cases }];
|
|
129
|
+
}
|
|
130
|
+
case "TryCatch": {
|
|
131
|
+
const tryBody = inScope(() => walk(p.try_body, context));
|
|
132
|
+
const caught = inScope(() => {
|
|
133
|
+
const scope = chain[chain.length - 1];
|
|
134
|
+
const message = bind(p.message, scope);
|
|
135
|
+
const stack = bind(p.stack, scope);
|
|
136
|
+
return { message, stack, catch_body: walk(p.catch_body, context) };
|
|
137
|
+
});
|
|
138
|
+
const finallyBody = inScope(() => walk(p.finally_body, context));
|
|
139
|
+
return [tag, { type: walk(p.type, "type"), try_body: tryBody, ...caught, finally_body: finallyBody }];
|
|
140
|
+
}
|
|
141
|
+
case "Block":
|
|
142
|
+
return inScope(() => [tag, { type: walk(p.type, "type"), statements: walk(p.statements, context) }]);
|
|
143
|
+
case "While":
|
|
144
|
+
return [tag, { type: walk(p.type, "type"), predicate: walk(p.predicate, context), label: { name: rename(labels, p.label.name, "L") }, body: inScope(() => walk(p.body, context)) }];
|
|
145
|
+
case "IfElse":
|
|
146
|
+
return [tag, {
|
|
147
|
+
type: walk(p.type, "type"),
|
|
148
|
+
ifs: p.ifs.map(branch => ({ predicate: walk(branch.predicate, context), body: inScope(() => walk(branch.body, context)) })),
|
|
149
|
+
else_body: inScope(() => walk(p.else_body, context)),
|
|
150
|
+
}];
|
|
151
|
+
default:
|
|
152
|
+
return [tag, walk(v.value, context)];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (typeof v === "object") {
|
|
156
|
+
const out = {};
|
|
157
|
+
for (const key of Object.keys(v).sort()) {
|
|
158
|
+
if (key === "loc_id") {
|
|
159
|
+
out[key] = "0";
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (key === "label" && typeof v[key] === "object" && v[key] !== null && "name" in v[key]) {
|
|
163
|
+
out[key] = { name: rename(labels, v[key].name, "L") };
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
out[key] = walk(v[key], key === "type" || key === "type_parameters" ? "type" : context);
|
|
167
|
+
}
|
|
168
|
+
return out;
|
|
169
|
+
}
|
|
170
|
+
return v;
|
|
171
|
+
};
|
|
172
|
+
return walk(node, null);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* The first place two canonical forms differ, or `null` when they are the
|
|
176
|
+
* same: a leaf compares by `Object.is`, so -0.0 is not 0.0 and NaN is NaN.
|
|
177
|
+
*
|
|
178
|
+
* @param a - a canonical form ({@link canonicalIR})
|
|
179
|
+
* @param b - another
|
|
180
|
+
* @param path - where `a` and `b` sit, for the report
|
|
181
|
+
* @returns the path of the first difference and the two values there, or `null`
|
|
182
|
+
*
|
|
183
|
+
* @internal
|
|
184
|
+
*/
|
|
185
|
+
export function canonicalDifference(a, b, path = "ir") {
|
|
186
|
+
if (Object.is(a, b))
|
|
187
|
+
return null;
|
|
188
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
189
|
+
if (a.length !== b.length)
|
|
190
|
+
return `${path}.length: ${a.length} vs ${b.length}`;
|
|
191
|
+
for (let i = 0; i < a.length; i++) {
|
|
192
|
+
const d = canonicalDifference(a[i], b[i], `${path}[${i}]`);
|
|
193
|
+
if (d !== null)
|
|
194
|
+
return d;
|
|
195
|
+
}
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
if (a !== null && b !== null && typeof a === "object" && typeof b === "object" && !Array.isArray(a) && !Array.isArray(b)) {
|
|
199
|
+
for (const k of [...new Set([...Object.keys(a), ...Object.keys(b)])].sort()) {
|
|
200
|
+
const d = canonicalDifference(a[k], b[k], `${path}.${k}`);
|
|
201
|
+
if (d !== null)
|
|
202
|
+
return d;
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
return `${path}: ${String(a)} vs ${String(b)}`;
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=canonical.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical.js","sourceRoot":"","sources":["../../../src/codegen/canonical.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,11 +9,12 @@
|
|
|
9
9
|
* (loc_ids, canonical variable and label names, recursive type ids).
|
|
10
10
|
*
|
|
11
11
|
* Three legs: hand-written functions covering every node kind and spelling
|
|
12
|
-
* family; every exported `*.examples.ts` example (
|
|
13
|
-
* `
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* family; every exported `*.examples.ts` example (`EAST_EXAMPLES_IR_DIR`,
|
|
13
|
+
* `make export-examples`), which also executes and must compute its declared
|
|
14
|
+
* `returns`; every compliance-corpus program (`EAST_TEST_IR_DIR`, `make
|
|
15
|
+
* test-export`). The root `paths.mk` sets both to this checkout's `tmp/`. The
|
|
16
|
+
* two exported legs skip when a variable is unset or nothing is exported
|
|
17
|
+
* there — the three-way sweep in east-py runs them under CI's
|
|
17
18
|
* `EAST_CONFORMANCE_REQUIRED=1`.
|
|
18
19
|
*/
|
|
19
20
|
import { describe, test, after } from "node:test";
|
|
@@ -22,7 +23,8 @@ import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync,
|
|
|
22
23
|
import { tmpdir } from "node:os";
|
|
23
24
|
import { dirname, join } from "node:path";
|
|
24
25
|
import { pathToFileURL } from "node:url";
|
|
25
|
-
import { East, Expr, variant, ref, some, none, ArrayType, BlobType, DictType, FloatType, FunctionType, IntegerType, NullType, OptionType, RecursiveType, SetType, StringType, StructType, VariantType, VectorType, IRType, EastTypeType, fromJSONFor, equalFor, isVariant, isPlatformDeclaration, toSource, RAW_ONLY, } from "../index.js";
|
|
26
|
+
import { East, Expr, variant, ref, some, none, SortedSet, SortedMap, compareFor, ArrayType, BlobType, BooleanType, DateTimeType, DictType, FloatType, FunctionType, IntegerType, NullType, OptionType, QueryCallType, RecursiveType, SetType, StringType, StructType, VariantType, VectorType, IRType, EastTypeType, fromJSONFor, equalFor, isVariant, isPlatformDeclaration, toSource, RAW_ONLY, } from "../index.js";
|
|
27
|
+
import { canonicalIR as canonical, canonicalDifference as firstDifference } from "./canonical.js";
|
|
26
28
|
// ── the harness ─────────────────────────────────────────────────────────────
|
|
27
29
|
const INDEX_URL = new URL("../index.js", import.meta.url).href;
|
|
28
30
|
const TMP = mkdtempSync(join(tmpdir(), "east-codegen-"));
|
|
@@ -38,186 +40,6 @@ async function rebuild(source) {
|
|
|
38
40
|
const mod = await import(pathToFileURL(path).href);
|
|
39
41
|
return mod.main;
|
|
40
42
|
}
|
|
41
|
-
/**
|
|
42
|
-
* The normalized shape of an IR value: loc_ids zeroed, variables and labels
|
|
43
|
-
* renamed in first-occurrence order, recursive type ids renumbered, bigints
|
|
44
|
-
* as strings — what `east-c ir normalize` erases, erased here in TypeScript.
|
|
45
|
-
*
|
|
46
|
-
* Variables are renamed per BINDING, resolved lexically — a parameter, a
|
|
47
|
-
* Let, a loop or match or catch variable each mint a canonical name for the
|
|
48
|
-
* body they scope — not per name: since #639 sibling bodies reuse a name
|
|
49
|
-
* (three callbacks each naming their element `x`), and two programs that
|
|
50
|
-
* bind the same variable under different names (a printed module drops an
|
|
51
|
-
* empty `.catch`, whose default variables the rebuild names afresh) are the
|
|
52
|
-
* same program.
|
|
53
|
-
*/
|
|
54
|
-
function canonical(node) {
|
|
55
|
-
const labels = new Map();
|
|
56
|
-
const recursive = new Map();
|
|
57
|
-
let variables = 0;
|
|
58
|
-
const rename = (table, name, prefix) => {
|
|
59
|
-
let hit = table.get(name);
|
|
60
|
-
if (hit === undefined) {
|
|
61
|
-
hit = `${prefix}${table.size}`;
|
|
62
|
-
table.set(name, hit);
|
|
63
|
-
}
|
|
64
|
-
return hit;
|
|
65
|
-
};
|
|
66
|
-
/** Binds `variable` (a Variable node) in `scope`: its canonical name, minted in binding order. */
|
|
67
|
-
const bind = (variable, scope) => {
|
|
68
|
-
const name = `v${variables}`;
|
|
69
|
-
variables += 1;
|
|
70
|
-
scope.set(variable.value.name, name);
|
|
71
|
-
return variableNode(variable, name);
|
|
72
|
-
};
|
|
73
|
-
const variableNode = (v, name) => ["Variable", { type: walk(v.value.type, "type", new Map()), name, mutable: v.value.mutable, captured: v.value.captured }];
|
|
74
|
-
const lookup = (scopes, name) => {
|
|
75
|
-
for (let i = scopes.length - 1; i >= 0; i--) {
|
|
76
|
-
const hit = scopes[i].get(name);
|
|
77
|
-
if (hit !== undefined)
|
|
78
|
-
return hit;
|
|
79
|
-
}
|
|
80
|
-
return `free:${name}`;
|
|
81
|
-
};
|
|
82
|
-
// the scope chain is threaded through the walk: a body opens a scope, a Let extends the current one
|
|
83
|
-
let chain = [new Map()];
|
|
84
|
-
const inScope = (f) => {
|
|
85
|
-
chain = [...chain, new Map()];
|
|
86
|
-
try {
|
|
87
|
-
return f();
|
|
88
|
-
}
|
|
89
|
-
finally {
|
|
90
|
-
chain = chain.slice(0, -1);
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
const walk = (v, context, _scope) => {
|
|
94
|
-
if (v === null || v === undefined)
|
|
95
|
-
return null;
|
|
96
|
-
if (typeof v === "bigint")
|
|
97
|
-
return v.toString();
|
|
98
|
-
if (v instanceof Date)
|
|
99
|
-
return v.toISOString();
|
|
100
|
-
if (v instanceof Uint8Array)
|
|
101
|
-
return Array.from(v);
|
|
102
|
-
if (Array.isArray(v))
|
|
103
|
-
return v.map(x => walk(x, context));
|
|
104
|
-
if (isVariant(v)) {
|
|
105
|
-
const tag = v.type;
|
|
106
|
-
const p = v.value;
|
|
107
|
-
if (context === "type") {
|
|
108
|
-
if (tag === "Recursive") {
|
|
109
|
-
const payload = v.value;
|
|
110
|
-
if (payload.type === "ref")
|
|
111
|
-
return ["Recursive", ["ref", rename(recursive, String(payload.value), "r")]];
|
|
112
|
-
return ["Recursive", ["wrapper", { id: rename(recursive, String(payload.value.id), "r"), inner: walk(payload.value.inner, "type") }]];
|
|
113
|
-
}
|
|
114
|
-
return [tag, walk(v.value, context)];
|
|
115
|
-
}
|
|
116
|
-
switch (tag) {
|
|
117
|
-
case "Variable":
|
|
118
|
-
return variableNode(v, lookup(chain, p.name));
|
|
119
|
-
case "Function":
|
|
120
|
-
case "AsyncFunction":
|
|
121
|
-
return inScope(() => [tag, {
|
|
122
|
-
type: walk(p.type, "type"),
|
|
123
|
-
parameters: p.parameters.map(q => bind(q, chain[chain.length - 1])),
|
|
124
|
-
body: walk(p.body, context),
|
|
125
|
-
captures: walk(p.captures, context),
|
|
126
|
-
}]);
|
|
127
|
-
case "Let": {
|
|
128
|
-
const value = walk(p.value, context);
|
|
129
|
-
return [tag, { type: walk(p.type, "type"), value, variable: bind(p.variable, chain[chain.length - 1]) }];
|
|
130
|
-
}
|
|
131
|
-
case "ForArray":
|
|
132
|
-
case "ForDict":
|
|
133
|
-
case "ForSet": {
|
|
134
|
-
const source = tag === "ForArray" ? "array" : tag === "ForDict" ? "dict" : "set";
|
|
135
|
-
const coll = walk(p[source], context);
|
|
136
|
-
return inScope(() => {
|
|
137
|
-
const scope = chain[chain.length - 1];
|
|
138
|
-
const out = { type: walk(p.type, "type"), [source]: coll, label: { name: rename(labels, p.label.name, "L") } };
|
|
139
|
-
if (tag !== "ForSet")
|
|
140
|
-
out["value"] = bind(p.value, scope);
|
|
141
|
-
out["key"] = bind(p.key, scope);
|
|
142
|
-
out["body"] = walk(p.body, context);
|
|
143
|
-
return [tag, out];
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
case "Match": {
|
|
147
|
-
const variant = walk(p.variant, context);
|
|
148
|
-
const cases = p.cases.map(c => inScope(() => ({
|
|
149
|
-
case: c.case, variable: bind(c.variable, chain[chain.length - 1]), body: walk(c.body, context),
|
|
150
|
-
})));
|
|
151
|
-
return [tag, { type: walk(p.type, "type"), variant, cases }];
|
|
152
|
-
}
|
|
153
|
-
case "TryCatch": {
|
|
154
|
-
const tryBody = inScope(() => walk(p.try_body, context));
|
|
155
|
-
const caught = inScope(() => {
|
|
156
|
-
const scope = chain[chain.length - 1];
|
|
157
|
-
const message = bind(p.message, scope);
|
|
158
|
-
const stack = bind(p.stack, scope);
|
|
159
|
-
return { message, stack, catch_body: walk(p.catch_body, context) };
|
|
160
|
-
});
|
|
161
|
-
const finallyBody = inScope(() => walk(p.finally_body, context));
|
|
162
|
-
return [tag, { type: walk(p.type, "type"), try_body: tryBody, ...caught, finally_body: finallyBody }];
|
|
163
|
-
}
|
|
164
|
-
case "Block":
|
|
165
|
-
return inScope(() => [tag, { type: walk(p.type, "type"), statements: walk(p.statements, context) }]);
|
|
166
|
-
case "While":
|
|
167
|
-
return [tag, { type: walk(p.type, "type"), predicate: walk(p.predicate, context), label: { name: rename(labels, p.label.name, "L") }, body: inScope(() => walk(p.body, context)) }];
|
|
168
|
-
case "IfElse":
|
|
169
|
-
return [tag, {
|
|
170
|
-
type: walk(p.type, "type"),
|
|
171
|
-
ifs: p.ifs.map(branch => ({ predicate: walk(branch.predicate, context), body: inScope(() => walk(branch.body, context)) })),
|
|
172
|
-
else_body: inScope(() => walk(p.else_body, context)),
|
|
173
|
-
}];
|
|
174
|
-
default:
|
|
175
|
-
return [tag, walk(v.value, context)];
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
if (typeof v === "object") {
|
|
179
|
-
const out = {};
|
|
180
|
-
for (const key of Object.keys(v).sort()) {
|
|
181
|
-
if (key === "loc_id") {
|
|
182
|
-
out[key] = "0";
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
if (key === "label" && typeof v[key] === "object" && "name" in v[key]) {
|
|
186
|
-
out[key] = { name: rename(labels, v[key].name, "L") };
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
out[key] = walk(v[key], key === "type" || key === "type_parameters" ? "type" : context);
|
|
190
|
-
}
|
|
191
|
-
return out;
|
|
192
|
-
}
|
|
193
|
-
return v;
|
|
194
|
-
};
|
|
195
|
-
return walk(node, null);
|
|
196
|
-
}
|
|
197
|
-
/** The first path at which two canonical forms differ, or `null`. */
|
|
198
|
-
function firstDifference(a, b, path = "ir") {
|
|
199
|
-
if (Object.is(a, b))
|
|
200
|
-
return null;
|
|
201
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
202
|
-
if (a.length !== b.length)
|
|
203
|
-
return `${path}.length: ${a.length} vs ${b.length}`;
|
|
204
|
-
for (let i = 0; i < a.length; i++) {
|
|
205
|
-
const d = firstDifference(a[i], b[i], `${path}[${i}]`);
|
|
206
|
-
if (d !== null)
|
|
207
|
-
return d;
|
|
208
|
-
}
|
|
209
|
-
return null;
|
|
210
|
-
}
|
|
211
|
-
if (a !== null && b !== null && typeof a === "object" && typeof b === "object") {
|
|
212
|
-
for (const k of [...new Set([...Object.keys(a), ...Object.keys(b)])].sort()) {
|
|
213
|
-
const d = firstDifference(a[k], b[k], `${path}.${k}`);
|
|
214
|
-
if (d !== null)
|
|
215
|
-
return d;
|
|
216
|
-
}
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
return `${path}: ${String(a)} vs ${String(b)}`;
|
|
220
|
-
}
|
|
221
43
|
/** A host value for a message: bigints, Maps and Sets made printable. */
|
|
222
44
|
function show(v) {
|
|
223
45
|
return JSON.stringify(v, (_k, x) => typeof x === "bigint" ? `${x}n` : x instanceof Map ? [...x] : x instanceof Set ? [...x] : x);
|
|
@@ -595,6 +417,36 @@ describe("codegen: toSource round trips the builder surface", () => {
|
|
|
595
417
|
const main = await roundTrip(fn, "regex+csv");
|
|
596
418
|
assert.equal(main.toIR().compile([])("aab foo", [{ a: 1n }]), fn.toIR().compile([])("aab foo", [{ a: 1n }]));
|
|
597
419
|
});
|
|
420
|
+
test("a regex JavaScript would re-spell prints raw, and rebuilds as it was", async () => {
|
|
421
|
+
// `new RegExp("").source` is `(?:)`, a `/` reads back `\/`, and flags come back in JavaScript's order
|
|
422
|
+
const fn = East.function([StringType], StringType, ($, s) => {
|
|
423
|
+
const empty = $.const(East.builtin("RegexContains", [], [s, "", ""], BooleanType));
|
|
424
|
+
const slash = $.const(East.builtin("RegexIndexOf", [], [s, "a/b", ""], IntegerType));
|
|
425
|
+
const order = $.const(East.builtin("RegexContains", [], [s, "x", "mi"], BooleanType));
|
|
426
|
+
return East.str `${empty} ${slash} ${order}`;
|
|
427
|
+
});
|
|
428
|
+
const source = toSource(fn, { importFrom: INDEX_URL, width: Infinity });
|
|
429
|
+
assert.doesNotMatch(source, /new RegExp/, source);
|
|
430
|
+
const main = await roundTrip(fn, "regex re-spelled");
|
|
431
|
+
assert.equal(main.toIR().compile([])("xa/b"), fn.toIR().compile([])("xa/b"));
|
|
432
|
+
});
|
|
433
|
+
test("an option's none over a computed null, and a Float key of -0.0, print as they are and rebuild", async () => {
|
|
434
|
+
const Opt = OptionType(IntegerType);
|
|
435
|
+
const fn = East.function([Opt], IntegerType, ($, o) => {
|
|
436
|
+
// `none` spells the null literal only: a payload computed some other way stays
|
|
437
|
+
const same = $.const(o.match({ none: ($, n) => East.value(variant("none", n), Opt), some: ($, x) => East.value(some(x), Opt) }));
|
|
438
|
+
// JavaScript's Set and Map fold -0 into 0; East keeps -0.0 apart from 0.0
|
|
439
|
+
const zeros = $.const(new SortedSet([-0, 0, 1.5], compareFor(FloatType)), SetType(FloatType));
|
|
440
|
+
const keyed = $.const(new SortedMap([[-0, 1n], [0, 2n]], compareFor(FloatType)), DictType(FloatType, IntegerType));
|
|
441
|
+
return zeros.size().add(keyed.size()).add(same.match({ none: ($) => 0n, some: ($, x) => x }));
|
|
442
|
+
});
|
|
443
|
+
const source = toSource(fn, { importFrom: INDEX_URL, width: Infinity });
|
|
444
|
+
assert.match(source, /variant\("none", n\)/, source);
|
|
445
|
+
assert.match(source, /new SortedSet\(\[-0, 0, 1\.5\], compareFor\(FloatType\)\)/, source);
|
|
446
|
+
assert.match(source, /new SortedMap\(\[\[-0, 1n\], \[0, 2n\]\], compareFor\(FloatType\)\)/, source);
|
|
447
|
+
const main = await roundTrip(fn, "none payload and -0.0 keys");
|
|
448
|
+
assert.equal(main.toIR().compile([])(some(4n)), 9n); // three floats, two keys, and the 4
|
|
449
|
+
});
|
|
598
450
|
test("another builder's fresh names print as one v_N sequence, and the printed module prints to itself", async () => {
|
|
599
451
|
const fn = East.function([ArrayType(IntegerType)], IntegerType, ($, xs) => xs.map(($, x) => x.multiply(2n)).reduce(($, acc, x) => acc.add(x), 0n));
|
|
600
452
|
// python spells a slot the body did not name `__nN` (TypeScript: `_N`),
|
|
@@ -611,6 +463,24 @@ describe("codegen: toSource round trips the builder surface", () => {
|
|
|
611
463
|
assert.equal(toSource(main.toIR().ir, { importFrom: INDEX_URL, width: Infinity }), wide, "print → build → print is the identity");
|
|
612
464
|
assert.equal(toSource(main.toIR().ir, { importFrom: INDEX_URL }), toSource(ir, { importFrom: INDEX_URL }), "… at the default width too");
|
|
613
465
|
});
|
|
466
|
+
test("a variable named as a name the module imports prints renamed, and rebuilds", async () => {
|
|
467
|
+
const fn = East.function([OptionType(IntegerType)], IntegerType, ($, o) => {
|
|
468
|
+
const fallback = $.const(East.value(none, OptionType(IntegerType)));
|
|
469
|
+
const n = $.const(o.match({ none: ($) => 0n, some: ($, x) => x }));
|
|
470
|
+
return n.add(fallback.match({ none: ($) => 1n, some: ($, x) => x }));
|
|
471
|
+
});
|
|
472
|
+
// the bindings named as the module's own imports (a translated query names a variable `none`)
|
|
473
|
+
const imported = (v) => Array.isArray(v) ? v.map(imported)
|
|
474
|
+
: isVariant(v) ? variant(v.type, imported(v.value))
|
|
475
|
+
: v !== null && typeof v === "object" && !(v instanceof Date) && !(v instanceof Uint8Array)
|
|
476
|
+
? Object.fromEntries(Object.entries(v).map(([k, x]) => [k, k === "name" && typeof x === "string" ? (x === "fallback" ? "none" : x === "n" ? "IntegerType" : x) : imported(x)]))
|
|
477
|
+
: v;
|
|
478
|
+
const ir = imported(fn.toIR().ir);
|
|
479
|
+
const source = toSource(ir, { importFrom: INDEX_URL, width: Infinity });
|
|
480
|
+
assert.doesNotMatch(source, /const none = |const IntegerType = /, source);
|
|
481
|
+
const main = await roundTrip(ir, "import-named variables");
|
|
482
|
+
assert.equal(main.toIR().compile([])(some(4n)), 5n);
|
|
483
|
+
});
|
|
614
484
|
test("layout: a wide literal, its type and an argument list break one entry per line, as prettier lays them out", async () => {
|
|
615
485
|
const Ops = StructType({ add: FunctionType([IntegerType, IntegerType], IntegerType), multiply: FunctionType([IntegerType, IntegerType], IntegerType) });
|
|
616
486
|
const fn = East.function([], IntegerType, ($) => {
|
|
@@ -689,6 +559,46 @@ describe("codegen: toSource round trips the builder surface", () => {
|
|
|
689
559
|
const main = await roundTrip(fn, "layout hug+chain");
|
|
690
560
|
assert.equal(main.toIR().compile([])([1n, 2n, 3n], "n"), fn.toIR().compile([])([1n, 2n, 3n], "n"));
|
|
691
561
|
});
|
|
562
|
+
test("a query prints as the East.jq it was, with named inputs as an object, and rebuilds (#927, #1041)", async () => {
|
|
563
|
+
const Order = StructType({ id: IntegerType, total: FloatType });
|
|
564
|
+
const fn = East.function([ArrayType(Order)], ArrayType(IntegerType), ($, orders) => East.jq(orders, "[.[] | select(.total > 1000) | .id]", ArrayType(IntegerType)));
|
|
565
|
+
const source = toSource(fn, { importFrom: INDEX_URL, width: Infinity });
|
|
566
|
+
assert.match(source, /\(\$, orders\) => East\.jq\(orders, "\[\.\[\] \| select\(\.total > 1000\) \| \.id\]", ArrayType\(IntegerType\)\)/, source);
|
|
567
|
+
assert.doesNotMatch(source, /east_jq|Query/, source);
|
|
568
|
+
const main = await roundTrip(fn, "jq one input");
|
|
569
|
+
assert.deepEqual(main.toIR().compile([])([{ id: 1n, total: 250.0 }, { id: 2n, total: 1200.0 }]), [2n]);
|
|
570
|
+
// Every named input is an argument of the call, one the query does not read too.
|
|
571
|
+
const named = East.function([ArrayType(Order), IntegerType, StringType], FloatType, ($, orders, min, note) => {
|
|
572
|
+
const total = $.const(East.jq({ orders, min, note }, ".min as $m | [.orders[] | select(.id >= $m) | .total] | add", FloatType));
|
|
573
|
+
return total.add(1.0);
|
|
574
|
+
});
|
|
575
|
+
const printed = toSource(named, { importFrom: INDEX_URL, width: Infinity });
|
|
576
|
+
assert.match(printed, /const total = \$\.const\(East\.jq\(\{ orders: orders, min: min, note: note \}, "[^"]*", FloatType\)\);/, printed);
|
|
577
|
+
await roundTrip(named, "jq named inputs");
|
|
578
|
+
});
|
|
579
|
+
test("a query's program prints as written: an ISO date compared with a DateTime stays the text it was (#1041)", async () => {
|
|
580
|
+
const Order = StructType({ id: IntegerType, at: DateTimeType });
|
|
581
|
+
const fn = East.function([ArrayType(Order)], ArrayType(IntegerType), ($, orders) => East.jq(orders, "[.[] | select(.at >= \"2026-01-01\") | .id]", ArrayType(IntegerType)));
|
|
582
|
+
const source = toSource(fn, { importFrom: INDEX_URL, width: Infinity });
|
|
583
|
+
assert.match(source, /East\.jq\(orders, "\[\.\[\] \| select\(\.at >= \\"2026-01-01\\"\) \| \.id\]", ArrayType\(IntegerType\)\)/, source);
|
|
584
|
+
await roundTrip(fn, "jq as written");
|
|
585
|
+
});
|
|
586
|
+
test("a query inside a callback prints as East.jq there (#927)", async () => {
|
|
587
|
+
const fn = East.function([ArrayType(ArrayType(IntegerType))], ArrayType(IntegerType), ($, rows) => rows.map(($, row) => East.jq(row, "map(. * 2) | add", IntegerType)));
|
|
588
|
+
const source = toSource(fn, { importFrom: INDEX_URL, width: Infinity });
|
|
589
|
+
assert.match(source, /rows\.map\(\(\$, row, _\d+\) => East\.jq\(row, "map\(\. \* 2\) \| add", IntegerType\)\)/, source);
|
|
590
|
+
const main = await roundTrip(fn, "jq in a callback");
|
|
591
|
+
assert.deepEqual(main.toIR().compile([])([[1n, 2n], [3n]]), [6n, 6n]);
|
|
592
|
+
});
|
|
593
|
+
test("a Query call whose query is not a constant prints raw, and rebuilds (#1041)", async () => {
|
|
594
|
+
const Numbers = ArrayType(IntegerType);
|
|
595
|
+
const F = FunctionType([Numbers], Numbers);
|
|
596
|
+
const fn = East.function([QueryCallType, Numbers], Numbers, ($, query, xs) => East.builtin("Query", [F], [query, East.function([Numbers], Numbers, ($, ys) => ys)], F)(xs));
|
|
597
|
+
const source = toSource(fn, { importFrom: INDEX_URL, width: Infinity });
|
|
598
|
+
assert.doesNotMatch(source, /East\.jq\(/, source);
|
|
599
|
+
assert.match(source, /East\.builtin\("Query"/, source);
|
|
600
|
+
await roundTrip(fn, "Query call of no constant");
|
|
601
|
+
});
|
|
692
602
|
test("a raw builtin prints through East.builtin and rebuilds", async () => {
|
|
693
603
|
const fn = East.function([ArrayType(IntegerType)], ArrayType(IntegerType), ($, xs) => {
|
|
694
604
|
return xs.getKeys([0n, 1n]);
|
|
@@ -699,9 +609,20 @@ describe("codegen: toSource round trips the builder surface", () => {
|
|
|
699
609
|
});
|
|
700
610
|
});
|
|
701
611
|
// ── the exported corpora ────────────────────────────────────────────────────
|
|
702
|
-
const EXAMPLES_DIR = process.env["EAST_EXAMPLES_IR_DIR"] ?? "/tmp/east-examples-ir";
|
|
703
|
-
const CORPUS_DIR = process.env["EAST_TEST_IR_DIR"] ?? "/tmp/east-test-ir";
|
|
704
612
|
const REQUIRED = process.env["EAST_CONFORMANCE_REQUIRED"] === "1";
|
|
613
|
+
/**
|
|
614
|
+
* Where an exported corpus is, from the variable the root `paths.mk` sets:
|
|
615
|
+
* its directory, or why the leg has nothing to read — the variable unset
|
|
616
|
+
* (the spec was not run through make) or nothing exported there.
|
|
617
|
+
*/
|
|
618
|
+
function corpusDir(variable, what) {
|
|
619
|
+
const dir = process.env[variable];
|
|
620
|
+
if (dir === undefined)
|
|
621
|
+
return { dir: null, skip: `${variable} is unset: run it through make (make -C libs/east test)` };
|
|
622
|
+
if (!existsSync(dir))
|
|
623
|
+
return { dir: null, skip: `no ${what} in ${dir}` };
|
|
624
|
+
return { dir, skip: false };
|
|
625
|
+
}
|
|
705
626
|
const decodeIR = fromJSONFor(IRType);
|
|
706
627
|
const decodeType = fromJSONFor(EastTypeType);
|
|
707
628
|
function hasPlatform(node) {
|
|
@@ -716,14 +637,13 @@ function hasPlatform(node) {
|
|
|
716
637
|
return Object.keys(node).some(k => k !== "type" && k !== "type_parameters" && hasPlatform(node[k]));
|
|
717
638
|
}
|
|
718
639
|
describe("codegen: every exported example round trips and runs", () => {
|
|
719
|
-
const
|
|
720
|
-
if (REQUIRED &&
|
|
721
|
-
throw new Error(`EAST_CONFORMANCE_REQUIRED=1 but
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
: [];
|
|
640
|
+
const examples = corpusDir("EAST_EXAMPLES_IR_DIR", "exported examples");
|
|
641
|
+
if (REQUIRED && examples.skip)
|
|
642
|
+
throw new Error(`EAST_CONFORMANCE_REQUIRED=1 but ${examples.skip}`);
|
|
643
|
+
const root = examples.dir;
|
|
644
|
+
const files = root === null ? [] : readdirSync(root).flatMap(suite => readdirSync(join(root, suite)).filter(f => f.endsWith(".json")).map(f => join(root, suite, f)));
|
|
725
645
|
const rawSeen = new Set();
|
|
726
|
-
test(`round trip (${files.length} examples)`, { skip:
|
|
646
|
+
test(`round trip (${files.length} examples)`, { skip: examples.skip }, async () => {
|
|
727
647
|
const labelOf = (file) => `${file.split("/").at(-2)}/${file.split("/").at(-1).slice(0, -5)}`;
|
|
728
648
|
await sweep(files, labelOf, async (file) => {
|
|
729
649
|
const record = JSON.parse(readFileSync(file, "utf-8"));
|
|
@@ -748,14 +668,25 @@ describe("codegen: every exported example round trips and runs", () => {
|
|
|
748
668
|
});
|
|
749
669
|
});
|
|
750
670
|
describe("codegen: every compliance-corpus program round trips", () => {
|
|
751
|
-
const
|
|
752
|
-
if (REQUIRED &&
|
|
753
|
-
throw new Error(`EAST_CONFORMANCE_REQUIRED=1 but
|
|
754
|
-
const
|
|
755
|
-
|
|
671
|
+
const corpus = corpusDir("EAST_TEST_IR_DIR", "exported IR corpus");
|
|
672
|
+
if (REQUIRED && corpus.skip)
|
|
673
|
+
throw new Error(`EAST_CONFORMANCE_REQUIRED=1 but ${corpus.skip}`);
|
|
674
|
+
const root = corpus.dir;
|
|
675
|
+
// the top level, and every `query-*` suite directory beside it (#924, #987), as every runner reads them:
|
|
676
|
+
// their queries print as the East.jq that built them (#927)
|
|
677
|
+
const suites = root === null
|
|
678
|
+
? []
|
|
679
|
+
: readdirSync(root, { withFileTypes: true }).filter(d => d.isDirectory() && d.name.startsWith("query-")).map(d => d.name).sort();
|
|
680
|
+
const files = root === null
|
|
681
|
+
? []
|
|
682
|
+
: [
|
|
683
|
+
...readdirSync(root).filter(f => f.endsWith(".json")).sort(),
|
|
684
|
+
...suites.flatMap(dir => readdirSync(join(root, dir)).filter(f => f.endsWith(".json")).sort().map(f => join(dir, f))),
|
|
685
|
+
];
|
|
686
|
+
test(`round trip (${files.length} programs)`, { skip: corpus.skip }, async () => {
|
|
756
687
|
const labelOf = (file) => `corpus/${file.slice(0, -5)}`;
|
|
757
688
|
await sweep(files, labelOf, async (file) => {
|
|
758
|
-
const record = JSON.parse(readFileSync(join(
|
|
689
|
+
const record = JSON.parse(readFileSync(join(root, file), "utf-8"));
|
|
759
690
|
await roundTrip(decodeIR(record.ir), labelOf(file));
|
|
760
691
|
});
|
|
761
692
|
});
|