@telorun/cel 0.0.0-stage → 0.108.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/LICENSE +17 -0
- package/README.md +281 -2
- package/dist/activation.d.ts +27 -0
- package/dist/activation.d.ts.map +1 -0
- package/dist/activation.js +24 -0
- package/dist/backend-runtime.d.ts +184 -0
- package/dist/backend-runtime.d.ts.map +1 -0
- package/dist/backend-runtime.js +425 -0
- package/dist/bounded-cache.d.ts +21 -0
- package/dist/bounded-cache.d.ts.map +1 -0
- package/dist/bounded-cache.js +42 -0
- package/dist/catalog-runtime.d.ts +59 -0
- package/dist/catalog-runtime.d.ts.map +1 -0
- package/dist/catalog-runtime.js +786 -0
- package/dist/cel-expression.d.ts +32 -0
- package/dist/cel-expression.d.ts.map +1 -0
- package/dist/cel-expression.js +29 -0
- package/dist/cel-map-value.d.ts +47 -0
- package/dist/cel-map-value.d.ts.map +1 -0
- package/dist/cel-map-value.js +85 -0
- package/dist/cel-program.d.ts +44 -0
- package/dist/cel-program.d.ts.map +1 -0
- package/dist/cel-program.js +72 -0
- package/dist/cel-type.d.ts +131 -0
- package/dist/cel-type.d.ts.map +1 -0
- package/dist/cel-type.js +293 -0
- package/dist/cel-value.d.ts +166 -0
- package/dist/cel-value.d.ts.map +1 -0
- package/dist/cel-value.js +225 -0
- package/dist/check-diagnostic.d.ts +54 -0
- package/dist/check-diagnostic.d.ts.map +1 -0
- package/dist/check-diagnostic.js +66 -0
- package/dist/checker.d.ts +86 -0
- package/dist/checker.d.ts.map +1 -0
- package/dist/checker.js +806 -0
- package/dist/closure-backend.d.ts +21 -0
- package/dist/closure-backend.d.ts.map +1 -0
- package/dist/closure-backend.js +487 -0
- package/dist/comprehension-bindings.d.ts +33 -0
- package/dist/comprehension-bindings.d.ts.map +1 -0
- package/dist/comprehension-bindings.js +50 -0
- package/dist/comprehension-runtime.d.ts +44 -0
- package/dist/comprehension-runtime.d.ts.map +1 -0
- package/dist/comprehension-runtime.js +137 -0
- package/dist/declared-chain.d.ts +35 -0
- package/dist/declared-chain.d.ts.map +1 -0
- package/dist/declared-chain.js +36 -0
- package/dist/duration-value.d.ts +70 -0
- package/dist/duration-value.d.ts.map +1 -0
- package/dist/duration-value.js +149 -0
- package/dist/emitted-module.d.ts +207 -0
- package/dist/emitted-module.d.ts.map +1 -0
- package/dist/emitted-module.js +359 -0
- package/dist/engine-version.d.ts +3 -0
- package/dist/engine-version.d.ts.map +1 -0
- package/dist/engine-version.js +8 -0
- package/dist/environment-digest.d.ts +44 -0
- package/dist/environment-digest.d.ts.map +1 -0
- package/dist/environment-digest.js +98 -0
- package/dist/environment.d.ts +291 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +474 -0
- package/dist/function-catalog.d.ts +66 -0
- package/dist/function-catalog.d.ts.map +1 -0
- package/dist/function-catalog.js +77 -0
- package/dist/function-registry.d.ts +118 -0
- package/dist/function-registry.d.ts.map +1 -0
- package/dist/function-registry.js +292 -0
- package/dist/index.d.ts +93 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +65 -0
- package/dist/integer-arithmetic.d.ts +27 -0
- package/dist/integer-arithmetic.d.ts.map +1 -0
- package/dist/integer-arithmetic.js +58 -0
- package/dist/js-emitter.d.ts +133 -0
- package/dist/js-emitter.d.ts.map +1 -0
- package/dist/js-emitter.js +568 -0
- package/dist/json-schema-type.d.ts +182 -0
- package/dist/json-schema-type.d.ts.map +1 -0
- package/dist/json-schema-type.js +487 -0
- package/dist/json-text-scan.d.ts +28 -0
- package/dist/json-text-scan.d.ts.map +1 -0
- package/dist/json-text-scan.js +159 -0
- package/dist/lexer.d.ts +103 -0
- package/dist/lexer.d.ts.map +1 -0
- package/dist/lexer.js +458 -0
- package/dist/macro-check.d.ts +41 -0
- package/dist/macro-check.d.ts.map +1 -0
- package/dist/macro-check.js +162 -0
- package/dist/macro-shape.d.ts +24 -0
- package/dist/macro-shape.d.ts.map +1 -0
- package/dist/macro-shape.js +55 -0
- package/dist/member-read.d.ts +89 -0
- package/dist/member-read.d.ts.map +1 -0
- package/dist/member-read.js +166 -0
- package/dist/namespace-resolution.d.ts +35 -0
- package/dist/namespace-resolution.d.ts.map +1 -0
- package/dist/namespace-resolution.js +160 -0
- package/dist/nominal-type.d.ts +63 -0
- package/dist/nominal-type.d.ts.map +1 -0
- package/dist/nominal-type.js +98 -0
- package/dist/nullable-access.d.ts +38 -0
- package/dist/nullable-access.d.ts.map +1 -0
- package/dist/nullable-access.js +93 -0
- package/dist/parse-limits.d.ts +26 -0
- package/dist/parse-limits.d.ts.map +1 -0
- package/dist/parse-limits.js +21 -0
- package/dist/parser.d.ts +48 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +503 -0
- package/dist/qualified-calls.d.ts +22 -0
- package/dist/qualified-calls.d.ts.map +1 -0
- package/dist/qualified-calls.js +27 -0
- package/dist/regular-expression.d.ts +48 -0
- package/dist/regular-expression.d.ts.map +1 -0
- package/dist/regular-expression.js +77 -0
- package/dist/reserved-words.d.ts +52 -0
- package/dist/reserved-words.d.ts.map +1 -0
- package/dist/reserved-words.js +77 -0
- package/dist/resolved-call.d.ts +46 -0
- package/dist/resolved-call.d.ts.map +1 -0
- package/dist/resolved-call.js +15 -0
- package/dist/root-references.d.ts +23 -0
- package/dist/root-references.d.ts.map +1 -0
- package/dist/root-references.js +120 -0
- package/dist/runtime-library.d.ts +67 -0
- package/dist/runtime-library.d.ts.map +1 -0
- package/dist/runtime-library.js +554 -0
- package/dist/serializer.d.ts +24 -0
- package/dist/serializer.d.ts.map +1 -0
- package/dist/serializer.js +256 -0
- package/dist/sha256.d.ts +20 -0
- package/dist/sha256.d.ts.map +1 -0
- package/dist/sha256.js +103 -0
- package/dist/signature.d.ts +72 -0
- package/dist/signature.d.ts.map +1 -0
- package/dist/signature.js +61 -0
- package/dist/signatures/function-catalog.json +788 -0
- package/dist/signatures/standard-library.json +229 -0
- package/dist/standard-library.d.ts +41 -0
- package/dist/standard-library.d.ts.map +1 -0
- package/dist/standard-library.js +85 -0
- package/dist/syntax-diagnostic.d.ts +61 -0
- package/dist/syntax-diagnostic.d.ts.map +1 -0
- package/dist/syntax-diagnostic.js +28 -0
- package/dist/syntax-tree.d.ts +160 -0
- package/dist/syntax-tree.d.ts.map +1 -0
- package/dist/syntax-tree.js +58 -0
- package/dist/timestamp-value.d.ts +62 -0
- package/dist/timestamp-value.d.ts.map +1 -0
- package/dist/timestamp-value.js +238 -0
- package/dist/tree-equality.d.ts +15 -0
- package/dist/tree-equality.d.ts.map +1 -0
- package/dist/tree-equality.js +105 -0
- package/dist/type-expression.d.ts +42 -0
- package/dist/type-expression.d.ts.map +1 -0
- package/dist/type-expression.js +154 -0
- package/dist/value-equality.d.ts +38 -0
- package/dist/value-equality.d.ts.map +1 -0
- package/dist/value-equality.js +196 -0
- package/dist/value-text.d.ts +25 -0
- package/dist/value-text.d.ts.map +1 -0
- package/dist/value-text.js +44 -0
- package/dist/zoned-calendar.d.ts +61 -0
- package/dist/zoned-calendar.d.ts.map +1 -0
- package/dist/zoned-calendar.js +143 -0
- package/package.json +56 -3
- package/src/activation.ts +32 -0
- package/src/backend-runtime.ts +604 -0
- package/src/bounded-cache.ts +45 -0
- package/src/catalog-runtime.ts +921 -0
- package/src/cel-expression.ts +53 -0
- package/src/cel-map-value.ts +97 -0
- package/src/cel-program.ts +103 -0
- package/src/cel-type.ts +359 -0
- package/src/cel-value.ts +361 -0
- package/src/check-diagnostic.ts +104 -0
- package/src/checker.ts +1045 -0
- package/src/closure-backend.ts +547 -0
- package/src/comprehension-bindings.ts +66 -0
- package/src/comprehension-runtime.ts +157 -0
- package/src/declared-chain.ts +45 -0
- package/src/duration-value.ts +160 -0
- package/src/emitted-module.ts +494 -0
- package/src/engine-version.ts +9 -0
- package/src/environment-digest.ts +111 -0
- package/src/environment.ts +761 -0
- package/src/function-catalog.ts +140 -0
- package/src/function-registry.ts +341 -0
- package/src/index.ts +407 -0
- package/src/integer-arithmetic.ts +64 -0
- package/src/js-emitter.ts +721 -0
- package/src/json-schema-type.ts +664 -0
- package/src/json-text-scan.ts +163 -0
- package/src/lexer.ts +562 -0
- package/src/macro-check.ts +197 -0
- package/src/macro-shape.ts +66 -0
- package/src/member-read.ts +167 -0
- package/src/namespace-resolution.ts +167 -0
- package/src/nominal-type.ts +149 -0
- package/src/nullable-access.ts +95 -0
- package/src/parse-limits.ts +36 -0
- package/src/parser.ts +554 -0
- package/src/qualified-calls.ts +39 -0
- package/src/regular-expression.ts +101 -0
- package/src/reserved-words.ts +94 -0
- package/src/resolved-call.ts +47 -0
- package/src/root-references.ts +126 -0
- package/src/runtime-library.ts +639 -0
- package/src/serializer.ts +262 -0
- package/src/sha256.ts +112 -0
- package/src/signature.ts +127 -0
- package/src/signatures/function-catalog.json +788 -0
- package/src/signatures/standard-library.json +235 -0
- package/src/standard-library.ts +149 -0
- package/src/syntax-diagnostic.ts +72 -0
- package/src/syntax-tree.ts +229 -0
- package/src/timestamp-value.ts +310 -0
- package/src/tree-equality.ts +130 -0
- package/src/type-expression.ts +182 -0
- package/src/value-equality.ts +201 -0
- package/src/value-text.ts +45 -0
- package/src/zoned-calendar.ts +178 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The closure backend: a tree compiled into nested closures, with **no `eval`, no
|
|
3
|
+
* `new Function` and no disk**.
|
|
4
|
+
*
|
|
5
|
+
* Everything an expression's shape decides is decided once, at compile time — which name
|
|
6
|
+
* is a bound slot, which select is a dotted activation read, which call is a macro — so
|
|
7
|
+
* evaluating is calling closures over a frame. Semantics are not here: every operator and
|
|
8
|
+
* function is the runtime library's (`runtime-library.ts`), every comprehension is
|
|
9
|
+
* `comprehension-runtime.ts`, every member read, name read and call-site dispatch is
|
|
10
|
+
* `backend-runtime.ts`. The emitter (`js-emitter.ts`) emits calls into exactly those, which
|
|
11
|
+
* is the only way two backends can be held to one answer.
|
|
12
|
+
*/
|
|
13
|
+
import type { CelStep, CompileTarget } from "./backend-runtime.js";
|
|
14
|
+
import type { CelNode } from "./syntax-tree.js";
|
|
15
|
+
export interface CompiledTree {
|
|
16
|
+
readonly step: CelStep;
|
|
17
|
+
/** How many binding slots a frame needs. */
|
|
18
|
+
readonly slots: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function compileTree(root: CelNode, target: CompileTarget): CompiledTree;
|
|
21
|
+
//# sourceMappingURL=closure-backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"closure-backend.d.ts","sourceRoot":"","sources":["../src/closure-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAyBH,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAiBnE,OAAO,KAAK,EAAE,OAAO,EAA8B,MAAM,kBAAkB,CAAC;AAE5E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,4CAA4C;IAC5C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAcD,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,GAAG,YAAY,CAI9E"}
|
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The closure backend: a tree compiled into nested closures, with **no `eval`, no
|
|
3
|
+
* `new Function` and no disk**.
|
|
4
|
+
*
|
|
5
|
+
* Everything an expression's shape decides is decided once, at compile time — which name
|
|
6
|
+
* is a bound slot, which select is a dotted activation read, which call is a macro — so
|
|
7
|
+
* evaluating is calling closures over a frame. Semantics are not here: every operator and
|
|
8
|
+
* function is the runtime library's (`runtime-library.ts`), every comprehension is
|
|
9
|
+
* `comprehension-runtime.ts`, every member read, name read and call-site dispatch is
|
|
10
|
+
* `backend-runtime.ts`. The emitter (`js-emitter.ts`) emits calls into exactly those, which
|
|
11
|
+
* is the only way two backends can be held to one answer.
|
|
12
|
+
*/
|
|
13
|
+
import { celMapFromEntries } from "./cel-map-value.js";
|
|
14
|
+
import { splitDeclaredChain } from "./declared-chain.js";
|
|
15
|
+
import { celError, asyncValueRefused, celNone, celSome, isCelError, isCelOptional, literalValue, } from "./cel-value.js";
|
|
16
|
+
import { celAll, celExists, celExistsOne, celFilter, celMapComprehension, } from "./comprehension-runtime.js";
|
|
17
|
+
import { namespaceMacroBinding, receiverMacroBinding } from "./comprehension-bindings.js";
|
|
18
|
+
import { isMacroCall } from "./macro-check.js";
|
|
19
|
+
import { celIterable } from "./member-read.js";
|
|
20
|
+
import { boolOperand, callSiteOf, CelCompileError, plainMemberChain, hasMember, optionalEntry, prefixCandidates, readHostValue, readName, readNameChain, readThrough, searchNameChain, } from "./backend-runtime.js";
|
|
21
|
+
import { optionalOfNonZero } from "./runtime-library.js";
|
|
22
|
+
function boundSlot(scope, name) {
|
|
23
|
+
for (let at = scope; at; at = at.outer)
|
|
24
|
+
if (at.name === name)
|
|
25
|
+
return at.slot;
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
export function compileTree(root, target) {
|
|
29
|
+
const compiler = new Compiler(target);
|
|
30
|
+
const step = compiler.compile(root, undefined);
|
|
31
|
+
return { step, slots: compiler.slots };
|
|
32
|
+
}
|
|
33
|
+
class Compiler {
|
|
34
|
+
target;
|
|
35
|
+
slots = 0;
|
|
36
|
+
constructor(target) {
|
|
37
|
+
this.target = target;
|
|
38
|
+
}
|
|
39
|
+
compile(node, scope) {
|
|
40
|
+
switch (node.kind) {
|
|
41
|
+
case "literal":
|
|
42
|
+
return constantStep(literalValue(node.literal));
|
|
43
|
+
case "ident":
|
|
44
|
+
return this.identStep(node, scope);
|
|
45
|
+
case "list":
|
|
46
|
+
return this.listStep(node, scope);
|
|
47
|
+
case "map":
|
|
48
|
+
return this.mapStep(node, scope);
|
|
49
|
+
case "select":
|
|
50
|
+
return this.selectStep(node, scope);
|
|
51
|
+
case "index":
|
|
52
|
+
return this.indexStep(node, scope);
|
|
53
|
+
case "unary":
|
|
54
|
+
return this.callStep(node.operator, "global", [this.compile(node.operand, scope)], node.range);
|
|
55
|
+
case "binary":
|
|
56
|
+
return this.binaryStep(node, scope);
|
|
57
|
+
case "conditional":
|
|
58
|
+
return this.conditionalStep(node, scope);
|
|
59
|
+
case "call":
|
|
60
|
+
case "receiverCall":
|
|
61
|
+
return this.anyCallStep(node, scope);
|
|
62
|
+
case "qcall":
|
|
63
|
+
return this.qualifiedCallStep(node, scope);
|
|
64
|
+
case "unparsed":
|
|
65
|
+
throw new CelCompileError("the expression could not be read whole, so it cannot be compiled");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// --- names --------------------------------------------------------------
|
|
69
|
+
identStep(node, scope) {
|
|
70
|
+
if (!node.absolute) {
|
|
71
|
+
const slot = boundSlot(scope, node.name);
|
|
72
|
+
if (slot !== undefined)
|
|
73
|
+
return (frame) => frame.slots[slot];
|
|
74
|
+
}
|
|
75
|
+
const { name, range } = node;
|
|
76
|
+
const { constants } = this.target;
|
|
77
|
+
return (frame) => readName(frame.activation, constants, name, range);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* A chain of plain member names rooted at a free name: one activation lookup over the
|
|
81
|
+
* longest declared prefix, the rest read through the seam. The checker resolves the
|
|
82
|
+
* same chain the same way, so a host holding `a.b.c` reads that name and one holding
|
|
83
|
+
* only `a.b` reads the map's entry.
|
|
84
|
+
*/
|
|
85
|
+
qualifiedChainStep(segments, range) {
|
|
86
|
+
const { constants, declares } = this.target;
|
|
87
|
+
// **A DECLARED chain splits at compile time**, through the same function the checker
|
|
88
|
+
// splits it with (`declared-chain.ts`), so evaluation performs exactly one activation
|
|
89
|
+
// read and then member reads — no search over prefixes, and no chance of reading a
|
|
90
|
+
// different name than the one the checker typed. A declared name the activation does not
|
|
91
|
+
// hold is `no_such_variable` for THAT name: falling back to a shorter prefix would read a
|
|
92
|
+
// value of a name the host did not mean.
|
|
93
|
+
const declaredSplit = splitDeclaredChain(segments, declares);
|
|
94
|
+
if (declaredSplit) {
|
|
95
|
+
const { name, rest } = declaredSplit;
|
|
96
|
+
return (frame) => readNameChain(frame.activation, constants, name, rest, range);
|
|
97
|
+
}
|
|
98
|
+
// Nothing declares a prefix of this chain, so there is nothing to split it on and the
|
|
99
|
+
// checker has no opinion either: the activation is searched, longest prefix first. Every
|
|
100
|
+
// conformance row that binds a dotted key reads this way.
|
|
101
|
+
const candidates = prefixCandidates(segments);
|
|
102
|
+
const written = segments.join(".");
|
|
103
|
+
return (frame) => searchNameChain(frame.activation, constants, candidates, written, range);
|
|
104
|
+
}
|
|
105
|
+
// --- aggregates ---------------------------------------------------------
|
|
106
|
+
listStep(node, scope) {
|
|
107
|
+
const elements = node.elements.map((element) => ({
|
|
108
|
+
step: this.compile(element.value, scope),
|
|
109
|
+
optional: element.optional,
|
|
110
|
+
}));
|
|
111
|
+
const { range } = node;
|
|
112
|
+
return (frame) => {
|
|
113
|
+
const out = [];
|
|
114
|
+
for (const element of elements) {
|
|
115
|
+
const value = element.step(frame);
|
|
116
|
+
if (isCelError(value))
|
|
117
|
+
return value;
|
|
118
|
+
if (!element.optional) {
|
|
119
|
+
out.push(value);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const held = optionalEntry(value, range);
|
|
123
|
+
if (isCelError(held))
|
|
124
|
+
return held;
|
|
125
|
+
// An absent optional entry leaves no element: the aggregate shrinks, which is
|
|
126
|
+
// the whole reason to write one.
|
|
127
|
+
if (held.present)
|
|
128
|
+
out.push(held.held);
|
|
129
|
+
}
|
|
130
|
+
return out;
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
mapStep(node, scope) {
|
|
134
|
+
const entries = node.entries.map((entry) => ({
|
|
135
|
+
key: this.compile(entry.key, scope),
|
|
136
|
+
value: this.compile(entry.value, scope),
|
|
137
|
+
optional: entry.optional,
|
|
138
|
+
}));
|
|
139
|
+
const { range } = node;
|
|
140
|
+
return (frame) => {
|
|
141
|
+
// Flat — key, value, key, value — so a literal of n entries costs one allocation
|
|
142
|
+
// rather than one per entry. The emitter writes the same call.
|
|
143
|
+
const flat = [];
|
|
144
|
+
for (const entry of entries) {
|
|
145
|
+
const key = entry.key(frame);
|
|
146
|
+
if (isCelError(key))
|
|
147
|
+
return key;
|
|
148
|
+
const value = entry.value(frame);
|
|
149
|
+
if (isCelError(value))
|
|
150
|
+
return value;
|
|
151
|
+
if (!entry.optional) {
|
|
152
|
+
flat.push(key, value);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const held = optionalEntry(value, range);
|
|
156
|
+
if (isCelError(held))
|
|
157
|
+
return held;
|
|
158
|
+
if (held.present)
|
|
159
|
+
flat.push(key, held.held);
|
|
160
|
+
}
|
|
161
|
+
return celMapFromEntries(flat, range);
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
// --- member reads -------------------------------------------------------
|
|
165
|
+
selectStep(node, scope) {
|
|
166
|
+
const chain = plainMemberChain(node, (name) => boundSlot(scope, name) !== undefined);
|
|
167
|
+
if (chain)
|
|
168
|
+
return this.qualifiedChainStep(chain, node.range);
|
|
169
|
+
const operand = this.compile(node.operand, scope);
|
|
170
|
+
const { field, optional, range } = node;
|
|
171
|
+
return (frame) => {
|
|
172
|
+
const held = operand(frame);
|
|
173
|
+
if (isCelError(held))
|
|
174
|
+
return held;
|
|
175
|
+
return readThrough(held, field, optional, range);
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
indexStep(node, scope) {
|
|
179
|
+
const operand = this.compile(node.operand, scope);
|
|
180
|
+
const index = this.compile(node.index, scope);
|
|
181
|
+
const { optional, range } = node;
|
|
182
|
+
return (frame) => {
|
|
183
|
+
const held = operand(frame);
|
|
184
|
+
if (isCelError(held))
|
|
185
|
+
return held;
|
|
186
|
+
const key = index(frame);
|
|
187
|
+
if (isCelError(key))
|
|
188
|
+
return key;
|
|
189
|
+
return readThrough(held, key, optional, range);
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
// --- operators ----------------------------------------------------------
|
|
193
|
+
/**
|
|
194
|
+
* `&&` and `||` carry an error-valued operand through: `false && <error>` is `false`
|
|
195
|
+
* and `true || <error>` is `true`, whichever side the error is on. That is why a CEL
|
|
196
|
+
* error is a value — an exception thrown where it was found could not be discarded
|
|
197
|
+
* here.
|
|
198
|
+
*/
|
|
199
|
+
binaryStep(node, scope) {
|
|
200
|
+
if (node.operator !== "&&" && node.operator !== "||") {
|
|
201
|
+
return this.callStep(node.operator, "global", [this.compile(node.left, scope), this.compile(node.right, scope)], node.range);
|
|
202
|
+
}
|
|
203
|
+
const left = this.compile(node.left, scope);
|
|
204
|
+
const right = this.compile(node.right, scope);
|
|
205
|
+
const { range } = node;
|
|
206
|
+
const decided = node.operator === "&&" ? false : true;
|
|
207
|
+
return (frame) => {
|
|
208
|
+
const a = boolOperand(left(frame), range);
|
|
209
|
+
if (a === decided)
|
|
210
|
+
return decided;
|
|
211
|
+
const b = boolOperand(right(frame), range);
|
|
212
|
+
if (b === decided)
|
|
213
|
+
return decided;
|
|
214
|
+
if (isCelError(a))
|
|
215
|
+
return a;
|
|
216
|
+
if (isCelError(b))
|
|
217
|
+
return b;
|
|
218
|
+
return !decided;
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
conditionalStep(node, scope) {
|
|
222
|
+
const condition = this.compile(node.condition, scope);
|
|
223
|
+
const whenTrue = this.compile(node.whenTrue, scope);
|
|
224
|
+
const whenFalse = this.compile(node.whenFalse, scope);
|
|
225
|
+
const { range } = node;
|
|
226
|
+
return (frame) => {
|
|
227
|
+
const held = boolOperand(condition(frame), range);
|
|
228
|
+
if (isCelError(held))
|
|
229
|
+
return held;
|
|
230
|
+
return held ? whenTrue(frame) : whenFalse(frame);
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
// --- calls --------------------------------------------------------------
|
|
234
|
+
anyCallStep(node, scope) {
|
|
235
|
+
// The same question the checker asks, in the same place: a macro is not a function,
|
|
236
|
+
// and it is recognised before any overload is looked for.
|
|
237
|
+
if (isMacroCall(node))
|
|
238
|
+
return this.macroStep(node, scope);
|
|
239
|
+
const form = node.kind === "call" ? "global" : "receiver";
|
|
240
|
+
const args = [
|
|
241
|
+
...(node.kind === "receiverCall" ? [this.compile(node.receiver, scope)] : []),
|
|
242
|
+
...node.args.map((argument) => this.compile(argument, scope)),
|
|
243
|
+
];
|
|
244
|
+
return this.callStep(node.name, form, args, node.range);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* One dispatch: evaluate the arguments, carry the first error out, then hand the values
|
|
248
|
+
* to the site **positionally**, which resolves the overload on their own types. The arity
|
|
249
|
+
* is the dispatch key's and known at compile time, so the step is chosen once here and no
|
|
250
|
+
* argument array is built per call — the same choice the emitter writes into its text.
|
|
251
|
+
*/
|
|
252
|
+
callStep(name, form, args, range) {
|
|
253
|
+
const site = callSiteOf(this.target, name, form, range);
|
|
254
|
+
const [first, second, third, fourth] = args;
|
|
255
|
+
switch (args.length) {
|
|
256
|
+
case 0:
|
|
257
|
+
return () => site.call0();
|
|
258
|
+
case 1:
|
|
259
|
+
return (frame) => {
|
|
260
|
+
const a = first(frame);
|
|
261
|
+
return isCelError(a) ? a : site.call1(a);
|
|
262
|
+
};
|
|
263
|
+
case 2:
|
|
264
|
+
return (frame) => {
|
|
265
|
+
const a = first(frame);
|
|
266
|
+
if (isCelError(a))
|
|
267
|
+
return a;
|
|
268
|
+
const b = second(frame);
|
|
269
|
+
return isCelError(b) ? b : site.call2(a, b);
|
|
270
|
+
};
|
|
271
|
+
case 3:
|
|
272
|
+
return (frame) => {
|
|
273
|
+
const a = first(frame);
|
|
274
|
+
if (isCelError(a))
|
|
275
|
+
return a;
|
|
276
|
+
const b = second(frame);
|
|
277
|
+
if (isCelError(b))
|
|
278
|
+
return b;
|
|
279
|
+
const c = third(frame);
|
|
280
|
+
return isCelError(c) ? c : site.call3(a, b, c);
|
|
281
|
+
};
|
|
282
|
+
case 4:
|
|
283
|
+
return (frame) => {
|
|
284
|
+
const a = first(frame);
|
|
285
|
+
if (isCelError(a))
|
|
286
|
+
return a;
|
|
287
|
+
const b = second(frame);
|
|
288
|
+
if (isCelError(b))
|
|
289
|
+
return b;
|
|
290
|
+
const c = third(frame);
|
|
291
|
+
if (isCelError(c))
|
|
292
|
+
return c;
|
|
293
|
+
const d = fourth(frame);
|
|
294
|
+
return isCelError(d) ? d : site.call4(a, b, c, d);
|
|
295
|
+
};
|
|
296
|
+
default: {
|
|
297
|
+
// A call written wider than any signature may be. Its arguments are still evaluated
|
|
298
|
+
// in order, so an error in one carries out ahead of the refusal.
|
|
299
|
+
const count = args.length;
|
|
300
|
+
return (frame) => {
|
|
301
|
+
const values = new Array(count);
|
|
302
|
+
for (let at = 0; at < count; at += 1) {
|
|
303
|
+
const held = args[at](frame);
|
|
304
|
+
if (isCelError(held))
|
|
305
|
+
return held;
|
|
306
|
+
values[at] = held;
|
|
307
|
+
}
|
|
308
|
+
return site.call(values);
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
qualifiedCallStep(node, scope) {
|
|
314
|
+
const args = node.args.map((argument) => this.compile(argument, scope));
|
|
315
|
+
const { namespace, name, range } = node;
|
|
316
|
+
return (frame) => {
|
|
317
|
+
const implementation = frame.namespaceFunction?.(namespace, name);
|
|
318
|
+
if (!implementation) {
|
|
319
|
+
return celError("unbound_function", `unbound function '${namespace}.${name}' — nothing is bound under that name here`, range);
|
|
320
|
+
}
|
|
321
|
+
const values = [];
|
|
322
|
+
for (const step of args) {
|
|
323
|
+
const value = step(frame);
|
|
324
|
+
if (isCelError(value))
|
|
325
|
+
return value;
|
|
326
|
+
values.push(value);
|
|
327
|
+
}
|
|
328
|
+
return readHostValue(implementation(values, { range }), range);
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
// --- macros -------------------------------------------------------------
|
|
332
|
+
/**
|
|
333
|
+
* Lowering a macro. There is no comprehension node to lower *to*: the macro's meaning
|
|
334
|
+
* is a call into the comprehension runtime with the body as a closure, which is the
|
|
335
|
+
* same shape the emitter produces.
|
|
336
|
+
*/
|
|
337
|
+
macroStep(node, scope) {
|
|
338
|
+
if (node.kind === "call")
|
|
339
|
+
return this.hasStep(node, scope);
|
|
340
|
+
const receiver = node.receiver;
|
|
341
|
+
if (receiver.kind === "ident" && receiver.name === "cel" && node.name === "bind") {
|
|
342
|
+
return this.bindStep(node, scope);
|
|
343
|
+
}
|
|
344
|
+
if (receiver.kind === "ident" && receiver.name === "optional") {
|
|
345
|
+
return this.optionalNamespaceStep(node, scope);
|
|
346
|
+
}
|
|
347
|
+
if ((node.name === "optMap" || node.name === "optFlatMap") && node.args.length === 2) {
|
|
348
|
+
return this.optionalBindingStep(node, scope);
|
|
349
|
+
}
|
|
350
|
+
return this.comprehensionStep(node, scope);
|
|
351
|
+
}
|
|
352
|
+
/** `has(a.b)` — presence, which a missing key answers `false` rather than erroring. */
|
|
353
|
+
hasStep(node, scope) {
|
|
354
|
+
const argument = node.args[0];
|
|
355
|
+
if (argument.kind !== "select") {
|
|
356
|
+
throw new CelCompileError("has() asks about a member, so its argument ends in a select");
|
|
357
|
+
}
|
|
358
|
+
const operand = this.compile(argument.operand, scope);
|
|
359
|
+
const { field } = argument;
|
|
360
|
+
const { range } = node;
|
|
361
|
+
return (frame) => {
|
|
362
|
+
const held = operand(frame);
|
|
363
|
+
if (isCelError(held))
|
|
364
|
+
return held;
|
|
365
|
+
return hasMember(held, field, range);
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
bindStep(node, scope) {
|
|
369
|
+
const binding = namespaceMacroBinding("cel", node.name, node.args.length);
|
|
370
|
+
if (!binding) {
|
|
371
|
+
throw new CelCompileError("cel.bind(name, value, body) takes a name, its value and a body");
|
|
372
|
+
}
|
|
373
|
+
const name = node.args[binding.variableArgument];
|
|
374
|
+
if (name.kind !== "ident")
|
|
375
|
+
throw new CelCompileError("cel.bind binds a name");
|
|
376
|
+
const value = this.compile(node.args[1], scope);
|
|
377
|
+
const slot = this.slots++;
|
|
378
|
+
const body = this.compile(node.args[2], { name: name.name, slot, outer: scope });
|
|
379
|
+
const { range } = node;
|
|
380
|
+
return (frame) => {
|
|
381
|
+
const held = value(frame);
|
|
382
|
+
if (isCelError(held))
|
|
383
|
+
return held;
|
|
384
|
+
// A name is bound to a VALUE, so a value that must be awaited is refused here rather
|
|
385
|
+
// than inside the body, where every read of the name would meet it again.
|
|
386
|
+
const refused = asyncValueRefused(held, range);
|
|
387
|
+
if (refused)
|
|
388
|
+
return refused;
|
|
389
|
+
frame.slots[slot] = held;
|
|
390
|
+
return body(frame);
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
/** `optional.of(v)`, `optional.ofNonZeroValue(v)` and `optional.none()`. */
|
|
394
|
+
optionalNamespaceStep(node, scope) {
|
|
395
|
+
if (node.name === "none")
|
|
396
|
+
return constantStep(celNone());
|
|
397
|
+
if (node.args.length !== 1) {
|
|
398
|
+
throw new CelCompileError(`optional.${node.name}(value) takes one argument`);
|
|
399
|
+
}
|
|
400
|
+
const value = this.compile(node.args[0], scope);
|
|
401
|
+
const nonZero = node.name === "ofNonZeroValue";
|
|
402
|
+
return (frame) => {
|
|
403
|
+
const held = value(frame);
|
|
404
|
+
if (isCelError(held))
|
|
405
|
+
return held;
|
|
406
|
+
return nonZero ? optionalOfNonZero(held) : celSome(held);
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
/** `opt.optMap(v, body)` wraps what the body answers; `optFlatMap` does not. */
|
|
410
|
+
optionalBindingStep(node, scope) {
|
|
411
|
+
const name = node.args[0];
|
|
412
|
+
if (name.kind !== "ident")
|
|
413
|
+
throw new CelCompileError(`${node.name} binds a name`);
|
|
414
|
+
const receiver = this.compile(node.receiver, scope);
|
|
415
|
+
const slot = this.slots++;
|
|
416
|
+
const body = this.compile(node.args[1], { name: name.name, slot, outer: scope });
|
|
417
|
+
const wrap = node.name === "optMap";
|
|
418
|
+
const { range } = node;
|
|
419
|
+
return (frame) => {
|
|
420
|
+
const held = receiver(frame);
|
|
421
|
+
if (isCelError(held))
|
|
422
|
+
return held;
|
|
423
|
+
if (!isCelOptional(held)) {
|
|
424
|
+
return celError("no_matching_overload", `${JSON.stringify(node.name)} reads an optional`, range);
|
|
425
|
+
}
|
|
426
|
+
if (!held.present)
|
|
427
|
+
return celNone();
|
|
428
|
+
// A host may hand over the optional itself, so what it HOLDS is a door of its own.
|
|
429
|
+
const refused = asyncValueRefused(held.held, range);
|
|
430
|
+
if (refused)
|
|
431
|
+
return refused;
|
|
432
|
+
frame.slots[slot] = held.held;
|
|
433
|
+
const answered = body(frame);
|
|
434
|
+
if (isCelError(answered))
|
|
435
|
+
return answered;
|
|
436
|
+
if (wrap)
|
|
437
|
+
return celSome(answered);
|
|
438
|
+
if (isCelOptional(answered))
|
|
439
|
+
return answered;
|
|
440
|
+
return celError("no_matching_overload", "optFlatMap's body answers an optional", range);
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
comprehensionStep(node, scope) {
|
|
444
|
+
const binding = receiverMacroBinding(node.name, node.args.length);
|
|
445
|
+
if (!binding)
|
|
446
|
+
throw new CelCompileError(`${node.name} is not a comprehension`);
|
|
447
|
+
const name = node.args[binding.variableArgument];
|
|
448
|
+
if (name.kind !== "ident")
|
|
449
|
+
throw new CelCompileError(`${node.name} binds a name`);
|
|
450
|
+
const receiver = this.compile(node.receiver, scope);
|
|
451
|
+
const slot = this.slots++;
|
|
452
|
+
const inner = { name: name.name, slot, outer: scope };
|
|
453
|
+
const scoped = binding.scopedArguments.map((at) => this.compile(node.args[at], inner));
|
|
454
|
+
const { range } = node;
|
|
455
|
+
const macro = node.name;
|
|
456
|
+
return (frame) => {
|
|
457
|
+
const held = receiver(frame);
|
|
458
|
+
if (isCelError(held))
|
|
459
|
+
return held;
|
|
460
|
+
const elements = celIterable(held, range);
|
|
461
|
+
if (isCelError(elements))
|
|
462
|
+
return elements;
|
|
463
|
+
const run = (step) => (element) => {
|
|
464
|
+
frame.slots[slot] = element;
|
|
465
|
+
return step(frame);
|
|
466
|
+
};
|
|
467
|
+
switch (macro) {
|
|
468
|
+
case "all":
|
|
469
|
+
return celAll(elements, run(scoped[0]), range);
|
|
470
|
+
case "exists":
|
|
471
|
+
return celExists(elements, run(scoped[0]), range);
|
|
472
|
+
case "exists_one":
|
|
473
|
+
return celExistsOne(elements, run(scoped[0]), range);
|
|
474
|
+
case "filter":
|
|
475
|
+
return celFilter(elements, run(scoped[0]), range);
|
|
476
|
+
default:
|
|
477
|
+
return scoped.length === 2
|
|
478
|
+
? celMapComprehension(elements, run(scoped[1]), run(scoped[0]), range)
|
|
479
|
+
: celMapComprehension(elements, run(scoped[0]), undefined, range);
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
// --- the pieces the steps are built from ------------------------------------
|
|
485
|
+
function constantStep(value) {
|
|
486
|
+
return () => value;
|
|
487
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which calls bind a name, and where that name is in scope.
|
|
3
|
+
*
|
|
4
|
+
* The comprehension macros and `cel.bind` introduce an identifier that is written
|
|
5
|
+
* as an ordinary argument: in `xs.map(i, i + 1)` the first `i` declares a name and
|
|
6
|
+
* the second reads it. Nothing expands these calls in the front end, so the free
|
|
7
|
+
* variables of an expression cannot be found without knowing this — otherwise `i`
|
|
8
|
+
* would be reported as a name the expression reads from its environment.
|
|
9
|
+
*
|
|
10
|
+
* It is DATA, so the checker and a backend lower exactly the forms the
|
|
11
|
+
* free-variable query already accounts for. An arity not listed here binds nothing:
|
|
12
|
+
* `xs.all(k, v, p)` is not a form the language has, and inventing a binding for it
|
|
13
|
+
* would hide the name `k` from a reader of the query.
|
|
14
|
+
*/
|
|
15
|
+
export interface ComprehensionBinding {
|
|
16
|
+
/** The argument that is the bound name. */
|
|
17
|
+
readonly variableArgument: number;
|
|
18
|
+
/** The arguments evaluated with that name in scope. */
|
|
19
|
+
readonly scopedArguments: readonly number[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Every form that binds a value into a body, as the table keys them — the receiver macros and
|
|
23
|
+
* `cel.bind`.
|
|
24
|
+
*
|
|
25
|
+
* It is here because the table is the only enumeration of them there is, and two places that
|
|
26
|
+
* list the binding forms is how one of them comes to be missing a form: a value entering a
|
|
27
|
+
* body is a door a host value comes through, and `tests/evaluate.test.ts` holds this list to a
|
|
28
|
+
* probe per form, so a tenth form cannot be added without being guarded.
|
|
29
|
+
*/
|
|
30
|
+
export declare const BINDING_FORMS: readonly string[];
|
|
31
|
+
export declare function receiverMacroBinding(name: string, arity: number): ComprehensionBinding | undefined;
|
|
32
|
+
export declare function namespaceMacroBinding(namespace: string, name: string, arity: number): ComprehensionBinding | undefined;
|
|
33
|
+
//# sourceMappingURL=comprehension-bindings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comprehension-bindings.d.ts","sourceRoot":"","sources":["../src/comprehension-bindings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,oBAAoB;IACnC,2CAA2C;IAC3C,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,uDAAuD;IACvD,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7C;AAqBD;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EAG1C,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS,CAElG;AAED,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ,oBAAoB,GAAG,SAAS,CAElC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which calls bind a name, and where that name is in scope.
|
|
3
|
+
*
|
|
4
|
+
* The comprehension macros and `cel.bind` introduce an identifier that is written
|
|
5
|
+
* as an ordinary argument: in `xs.map(i, i + 1)` the first `i` declares a name and
|
|
6
|
+
* the second reads it. Nothing expands these calls in the front end, so the free
|
|
7
|
+
* variables of an expression cannot be found without knowing this — otherwise `i`
|
|
8
|
+
* would be reported as a name the expression reads from its environment.
|
|
9
|
+
*
|
|
10
|
+
* It is DATA, so the checker and a backend lower exactly the forms the
|
|
11
|
+
* free-variable query already accounts for. An arity not listed here binds nothing:
|
|
12
|
+
* `xs.all(k, v, p)` is not a form the language has, and inventing a binding for it
|
|
13
|
+
* would hide the name `k` from a reader of the query.
|
|
14
|
+
*/
|
|
15
|
+
/** Keyed by `<name>/<arity>` for a call on a value. */
|
|
16
|
+
const RECEIVER_MACROS = new Map([
|
|
17
|
+
["all/2", { variableArgument: 0, scopedArguments: [1] }],
|
|
18
|
+
["exists/2", { variableArgument: 0, scopedArguments: [1] }],
|
|
19
|
+
["exists_one/2", { variableArgument: 0, scopedArguments: [1] }],
|
|
20
|
+
["filter/2", { variableArgument: 0, scopedArguments: [1] }],
|
|
21
|
+
["map/2", { variableArgument: 0, scopedArguments: [1] }],
|
|
22
|
+
["map/3", { variableArgument: 0, scopedArguments: [1, 2] }],
|
|
23
|
+
// The optional library's two: each binds the held value under a name, for the one
|
|
24
|
+
// expression that reads it.
|
|
25
|
+
["optMap/2", { variableArgument: 0, scopedArguments: [1] }],
|
|
26
|
+
["optFlatMap/2", { variableArgument: 0, scopedArguments: [1] }],
|
|
27
|
+
]);
|
|
28
|
+
/** Keyed by `<namespace>.<name>/<arity>` for a call on a reserved namespace. */
|
|
29
|
+
const NAMESPACE_MACROS = new Map([
|
|
30
|
+
["cel.bind/3", { variableArgument: 0, scopedArguments: [2] }],
|
|
31
|
+
]);
|
|
32
|
+
/**
|
|
33
|
+
* Every form that binds a value into a body, as the table keys them — the receiver macros and
|
|
34
|
+
* `cel.bind`.
|
|
35
|
+
*
|
|
36
|
+
* It is here because the table is the only enumeration of them there is, and two places that
|
|
37
|
+
* list the binding forms is how one of them comes to be missing a form: a value entering a
|
|
38
|
+
* body is a door a host value comes through, and `tests/evaluate.test.ts` holds this list to a
|
|
39
|
+
* probe per form, so a tenth form cannot be added without being guarded.
|
|
40
|
+
*/
|
|
41
|
+
export const BINDING_FORMS = [
|
|
42
|
+
...RECEIVER_MACROS.keys(),
|
|
43
|
+
...NAMESPACE_MACROS.keys(),
|
|
44
|
+
];
|
|
45
|
+
export function receiverMacroBinding(name, arity) {
|
|
46
|
+
return RECEIVER_MACROS.get(`${name}/${arity}`);
|
|
47
|
+
}
|
|
48
|
+
export function namespaceMacroBinding(namespace, name, arity) {
|
|
49
|
+
return NAMESPACE_MACROS.get(`${namespace}.${name}/${arity}`);
|
|
50
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a comprehension macro **does**, as a function of its range and its body.
|
|
3
|
+
*
|
|
4
|
+
* The macros are lowered by each backend — there is no comprehension node in the tree,
|
|
5
|
+
* because expanding one at read time would make the source unwritable from the tree —
|
|
6
|
+
* but what the lowering *means* lives here, once. The closure backend passes a closure
|
|
7
|
+
* as the body and the emitter will pass an emitted function, and both get the same
|
|
8
|
+
* answers, including the error rules.
|
|
9
|
+
*
|
|
10
|
+
* **An error does not win over a decided answer**, with the one exception named below: `all`
|
|
11
|
+
* is `false` as soon as one element is false even if another errors, and `exists` is `true` as
|
|
12
|
+
* soon as one is true. That is the same rule `&&` and `||` follow, and it is what makes a
|
|
13
|
+
* comprehension over partly-unreadable data usable at all.
|
|
14
|
+
*
|
|
15
|
+
* **An element entering a body is a door a host value comes through**, and it is checked for a
|
|
16
|
+
* thenable here — in the one place a comprehension's meaning lives, so both backends inherit it
|
|
17
|
+
* rather than each remembering to. It is not the leak it looks like: `xs.all(e, true)` over a
|
|
18
|
+
* host list holding a promise used to answer **`true`**, having decided something about a value
|
|
19
|
+
* it never touched, which is the worse half of the failure class. The cost is one `typeof` per
|
|
20
|
+
* element, the same check the member read already makes, on a loop that already calls a closure
|
|
21
|
+
* per element.
|
|
22
|
+
*
|
|
23
|
+
* **That refusal is TERMINAL, and it is the one error here that does not short-circuit.** It is
|
|
24
|
+
* checked where the element is BOUND, before the body runs, and it ends the comprehension
|
|
25
|
+
* whatever a later element would have decided — so `xs.all(e, false)` and `xs.exists(e, true)`
|
|
26
|
+
* refuse rather than answering `false` and `true` off the one element that is readable. The
|
|
27
|
+
* distinction is what the error is ABOUT: `no_such_key` is a fact about one datum, so a decided
|
|
28
|
+
* answer may legitimately outrank it, while a value that must be awaited says the host handed
|
|
29
|
+
* this engine something it cannot evaluate at all. Carrying that past a convenient element makes
|
|
30
|
+
* the answer depend on how many elements happen to be readable and in what order — and the rule
|
|
31
|
+
* is subtle enough that two readers in a row took the discardable refusal for a leak instead.
|
|
32
|
+
*/
|
|
33
|
+
import type { CelValue } from "./cel-value.js";
|
|
34
|
+
import type { SourceRange } from "./syntax-tree.js";
|
|
35
|
+
/** The body of a comprehension: the value its expression answers for one element. */
|
|
36
|
+
export type ComprehensionBody = (element: CelValue) => CelValue;
|
|
37
|
+
export declare function celAll(elements: readonly CelValue[], body: ComprehensionBody, range?: SourceRange): CelValue;
|
|
38
|
+
export declare function celExists(elements: readonly CelValue[], body: ComprehensionBody, range?: SourceRange): CelValue;
|
|
39
|
+
/** Exactly one element satisfies the test. An error anywhere leaves no count to trust. */
|
|
40
|
+
export declare function celExistsOne(elements: readonly CelValue[], body: ComprehensionBody, range?: SourceRange): CelValue;
|
|
41
|
+
export declare function celFilter(elements: readonly CelValue[], body: ComprehensionBody, range?: SourceRange): CelValue;
|
|
42
|
+
/** `map` in both arities: with a filter, an element the filter drops is not transformed. */
|
|
43
|
+
export declare function celMapComprehension(elements: readonly CelValue[], transform: ComprehensionBody, filter?: ComprehensionBody, range?: SourceRange): CelValue;
|
|
44
|
+
//# sourceMappingURL=comprehension-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comprehension-runtime.d.ts","sourceRoot":"","sources":["../src/comprehension-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,qFAAqF;AACrF,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,QAAQ,KAAK,QAAQ,CAAC;AAgChE,wBAAgB,MAAM,CACpB,QAAQ,EAAE,SAAS,QAAQ,EAAE,EAC7B,IAAI,EAAE,iBAAiB,EACvB,KAAK,CAAC,EAAE,WAAW,GAClB,QAAQ,CAUV;AAED,wBAAgB,SAAS,CACvB,QAAQ,EAAE,SAAS,QAAQ,EAAE,EAC7B,IAAI,EAAE,iBAAiB,EACvB,KAAK,CAAC,EAAE,WAAW,GAClB,QAAQ,CAUV;AAED,0FAA0F;AAC1F,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,SAAS,QAAQ,EAAE,EAC7B,IAAI,EAAE,iBAAiB,EACvB,KAAK,CAAC,EAAE,WAAW,GAClB,QAAQ,CAUV;AAED,wBAAgB,SAAS,CACvB,QAAQ,EAAE,SAAS,QAAQ,EAAE,EAC7B,IAAI,EAAE,iBAAiB,EACvB,KAAK,CAAC,EAAE,WAAW,GAClB,QAAQ,CAUV;AAED,4FAA4F;AAC5F,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,SAAS,QAAQ,EAAE,EAC7B,SAAS,EAAE,iBAAiB,EAC5B,MAAM,CAAC,EAAE,iBAAiB,EAC1B,KAAK,CAAC,EAAE,WAAW,GAClB,QAAQ,CAeV"}
|