@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,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The words no CEL identifier may be: cel-spec's `RESERVED` production, which its
|
|
3
|
+
* `IDENT` production excludes. All 21 of them.
|
|
4
|
+
*
|
|
5
|
+
* The set is frozen deliberately: a word added here stops a manifest that uses it as
|
|
6
|
+
* a variable or a function name from parsing, so it is widened only with a
|
|
7
|
+
* deprecation behind it.
|
|
8
|
+
*
|
|
9
|
+
* **Three of the 21 are refused by being read as something else**, and the
|
|
10
|
+
* distinction is the lexer's whole policy about words, so it lives here rather than
|
|
11
|
+
* there. `true`, `false` and `null` are literals; `in` is the membership operator.
|
|
12
|
+
* Neither reading can be reached from a name position, which is why cel-spec needs
|
|
13
|
+
* them in `RESERVED` and why an engine must not quietly accept one as a name — doing
|
|
14
|
+
* so breaks the premise every consumer of the set is written against.
|
|
15
|
+
*
|
|
16
|
+
* **A reserved word is refused as an IDENTIFIER, not as a member name.** CEL's own
|
|
17
|
+
* conformance suite reads `{'let': 1}.let` and `a.while()`; `a.in` and `a.true` are
|
|
18
|
+
* the same position, and read for the same reason. A member name is a name of a
|
|
19
|
+
* value's entry, not a name in the expression's scope.
|
|
20
|
+
*
|
|
21
|
+
* **No host's property names are here.** A member *name* is written by the author; a
|
|
22
|
+
* member *key* can come from a request (`a[request.query.k]`), so a word list cannot
|
|
23
|
+
* be what keeps a host's property out of a CEL value — it does not touch the computed
|
|
24
|
+
* form at all. That guarantee belongs to the member-read operation, which resolves
|
|
25
|
+
* every form against the value's own entries and never performs a host property read.
|
|
26
|
+
* `constructor` never being on any such list is the same point made by its absence.
|
|
27
|
+
*
|
|
28
|
+
* `cel` and `optional` are legal identifiers, reserved only against being registered
|
|
29
|
+
* as a namespace (`namespace-resolution.ts`), because the standard macros are written
|
|
30
|
+
* on them (`cel.bind`, `optional.of`).
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export const RESERVED_WORDS: readonly string[] = [
|
|
34
|
+
"as",
|
|
35
|
+
"break",
|
|
36
|
+
"const",
|
|
37
|
+
"continue",
|
|
38
|
+
"else",
|
|
39
|
+
"false",
|
|
40
|
+
"for",
|
|
41
|
+
"function",
|
|
42
|
+
"if",
|
|
43
|
+
"import",
|
|
44
|
+
"in",
|
|
45
|
+
"let",
|
|
46
|
+
"loop",
|
|
47
|
+
"namespace",
|
|
48
|
+
"null",
|
|
49
|
+
"package",
|
|
50
|
+
"return",
|
|
51
|
+
"true",
|
|
52
|
+
"var",
|
|
53
|
+
"void",
|
|
54
|
+
"while",
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
/** The reserved words that are a literal where they stand. */
|
|
58
|
+
export const LITERAL_WORDS: readonly string[] = ["true", "false", "null"];
|
|
59
|
+
|
|
60
|
+
/** The reserved words that are an operator where they stand. */
|
|
61
|
+
export const OPERATOR_WORDS: readonly string[] = ["in"];
|
|
62
|
+
|
|
63
|
+
const RESERVED = new Set(RESERVED_WORDS);
|
|
64
|
+
const LITERALS = new Set(LITERAL_WORDS);
|
|
65
|
+
const OPERATORS = new Set(OPERATOR_WORDS);
|
|
66
|
+
|
|
67
|
+
/** How a word is read where an expression begins. */
|
|
68
|
+
export type WordReading =
|
|
69
|
+
/** An ordinary identifier. */
|
|
70
|
+
| "name"
|
|
71
|
+
/** A literal value: `true`, `false`, `null`. */
|
|
72
|
+
| "literal"
|
|
73
|
+
/** An operator written as a word: `in`. */
|
|
74
|
+
| "operator"
|
|
75
|
+
/** Reserved, with no other reading — refused wherever a name is read. */
|
|
76
|
+
| "refused";
|
|
77
|
+
|
|
78
|
+
export function wordReading(text: string): WordReading {
|
|
79
|
+
if (LITERALS.has(text)) return "literal";
|
|
80
|
+
if (OPERATORS.has(text)) return "operator";
|
|
81
|
+
return RESERVED.has(text) ? "refused" : "name";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Whether the word is one cel-spec reserves, however it is read. */
|
|
85
|
+
export function isReservedWord(name: string): boolean {
|
|
86
|
+
return RESERVED.has(name);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
90
|
+
|
|
91
|
+
/** Whether the text is spelled as a CEL identifier, reserved or not. */
|
|
92
|
+
export function isIdentifierSpelling(text: string): boolean {
|
|
93
|
+
return IDENTIFIER.test(text);
|
|
94
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which function each call in an expression resolved to.
|
|
3
|
+
*
|
|
4
|
+
* A host needs this for policy the engine cannot know: whether a non-deterministic call
|
|
5
|
+
* sits in a field evaluated once at startup, whether a call reaches the host at all,
|
|
6
|
+
* which error codes a call can fail with. All three are properties of the **resolved**
|
|
7
|
+
* signature, not of the name written — two overloads of one name can differ in every
|
|
8
|
+
* one of them — so the answer has to come from the checker, which is what resolved it.
|
|
9
|
+
*
|
|
10
|
+
* **It is the checker's lowering, not the tree.** A macro is not a dispatch and is not
|
|
11
|
+
* listed: `xs.map(i, i)` becomes a comprehension and reaches no function, while the
|
|
12
|
+
* calls written inside it are listed where they are written. A list read off the tree
|
|
13
|
+
* would instead report whatever the reader's parser happened to expand.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { CallForm } from "./signature.js";
|
|
17
|
+
import type { SourceRange } from "./syntax-tree.js";
|
|
18
|
+
|
|
19
|
+
export interface ResolvedCall {
|
|
20
|
+
/** The name as written — `<namespace>.<name>` for a namespaced call. */
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly form: CallForm;
|
|
23
|
+
/** Set only for a namespaced call. */
|
|
24
|
+
readonly namespace?: string;
|
|
25
|
+
readonly arity: number;
|
|
26
|
+
readonly range: SourceRange;
|
|
27
|
+
/** The signature it resolved to, absent where nothing resolved. */
|
|
28
|
+
readonly signature?: string;
|
|
29
|
+
/** The type the call answers here, with type parameters substituted. */
|
|
30
|
+
readonly returns?: string;
|
|
31
|
+
readonly deterministic?: boolean;
|
|
32
|
+
readonly hostBacked?: boolean;
|
|
33
|
+
readonly throws?: readonly string[];
|
|
34
|
+
/**
|
|
35
|
+
* Each argument's own type, for a NAMESPACED call only.
|
|
36
|
+
*
|
|
37
|
+
* The host judges such a call against its own, richer signature grammar — a JSON Schema
|
|
38
|
+
* per parameter, an optional trailing parameter — so it needs what was actually passed.
|
|
39
|
+
* It cannot re-derive it: checking an argument's subtree on its own loses whatever the
|
|
40
|
+
* expression bound around it, so an argument inside a comprehension (`xs.map(i,
|
|
41
|
+
* Billing.total(i))`) would type as `dyn` and the host's argument check would silently
|
|
42
|
+
* stop asking. Here the types come from the same pass that typed the call.
|
|
43
|
+
*
|
|
44
|
+
* Absent for a call the registry resolved: its parameter types are the signature's.
|
|
45
|
+
*/
|
|
46
|
+
readonly argumentTypes?: readonly string[];
|
|
47
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an expression reads from its environment.
|
|
3
|
+
*
|
|
4
|
+
* The answer is the first identifier of every access chain — `request` in
|
|
5
|
+
* `request.query.limit`, `xs` in `xs[0].id` — with three exclusions, each of which
|
|
6
|
+
* would otherwise invent a dependency the expression does not have:
|
|
7
|
+
*
|
|
8
|
+
* - a name a comprehension or `cel.bind` binds, which the expression supplies itself;
|
|
9
|
+
* - the namespace of a qualified call, which names a module rather than a value;
|
|
10
|
+
* - the receiver of a call on a reserved namespace (`cel.bind(…)`, `optional.of(…)`),
|
|
11
|
+
* for the same reason.
|
|
12
|
+
*
|
|
13
|
+
* A function name is not an identifier node at all, so no call name can be mistaken
|
|
14
|
+
* for a read. Field names are not nodes either, which is why only the root of a
|
|
15
|
+
* chain is answered.
|
|
16
|
+
*
|
|
17
|
+
* The result is sorted and deduplicated: it is a set of names, and a consumer that
|
|
18
|
+
* compares two expressions' reads must not see an ordering difference.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { namespaceMacroBinding, receiverMacroBinding } from "./comprehension-bindings.js";
|
|
22
|
+
import { RESERVED_NAMESPACES } from "./namespace-resolution.js";
|
|
23
|
+
import type { CelNode } from "./syntax-tree.js";
|
|
24
|
+
|
|
25
|
+
class BoundNames {
|
|
26
|
+
private readonly depth = new Map<string, number>();
|
|
27
|
+
|
|
28
|
+
bind(name: string): void {
|
|
29
|
+
this.depth.set(name, (this.depth.get(name) ?? 0) + 1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
unbind(name: string): void {
|
|
33
|
+
const held = this.depth.get(name)!;
|
|
34
|
+
if (held === 1) this.depth.delete(name);
|
|
35
|
+
else this.depth.set(name, held - 1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
has(name: string): boolean {
|
|
39
|
+
return this.depth.has(name);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Every name the expression reads from its environment, sorted. */
|
|
44
|
+
export function rootReferences(root: CelNode): readonly string[] {
|
|
45
|
+
const found = new Set<string>();
|
|
46
|
+
collect(root, new BoundNames(), found);
|
|
47
|
+
return [...found].sort();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function collect(node: CelNode, bound: BoundNames, found: Set<string>): void {
|
|
51
|
+
switch (node.kind) {
|
|
52
|
+
case "ident":
|
|
53
|
+
// An absolute name reads past every binding by construction, so a binding of the
|
|
54
|
+
// same name says nothing about it.
|
|
55
|
+
if (node.absolute || !bound.has(node.name)) found.add(node.name);
|
|
56
|
+
return;
|
|
57
|
+
case "literal":
|
|
58
|
+
case "unparsed":
|
|
59
|
+
return;
|
|
60
|
+
case "receiverCall":
|
|
61
|
+
collectCall(node, bound, found);
|
|
62
|
+
return;
|
|
63
|
+
case "list":
|
|
64
|
+
for (const element of node.elements) collect(element.value, bound, found);
|
|
65
|
+
return;
|
|
66
|
+
case "map":
|
|
67
|
+
for (const entry of node.entries) {
|
|
68
|
+
collect(entry.key, bound, found);
|
|
69
|
+
collect(entry.value, bound, found);
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
case "select":
|
|
73
|
+
collect(node.operand, bound, found);
|
|
74
|
+
return;
|
|
75
|
+
case "index":
|
|
76
|
+
collect(node.operand, bound, found);
|
|
77
|
+
collect(node.index, bound, found);
|
|
78
|
+
return;
|
|
79
|
+
case "call":
|
|
80
|
+
case "qcall":
|
|
81
|
+
for (const argument of node.args) collect(argument, bound, found);
|
|
82
|
+
return;
|
|
83
|
+
case "unary":
|
|
84
|
+
collect(node.operand, bound, found);
|
|
85
|
+
return;
|
|
86
|
+
case "binary":
|
|
87
|
+
collect(node.left, bound, found);
|
|
88
|
+
collect(node.right, bound, found);
|
|
89
|
+
return;
|
|
90
|
+
case "conditional":
|
|
91
|
+
collect(node.condition, bound, found);
|
|
92
|
+
collect(node.whenTrue, bound, found);
|
|
93
|
+
collect(node.whenFalse, bound, found);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function collectCall(
|
|
99
|
+
node: Extract<CelNode, { kind: "receiverCall" }>,
|
|
100
|
+
bound: BoundNames,
|
|
101
|
+
found: Set<string>,
|
|
102
|
+
): void {
|
|
103
|
+
const receiver = node.receiver;
|
|
104
|
+
const onNamespace = receiver.kind === "ident" && RESERVED_NAMESPACES.includes(receiver.name);
|
|
105
|
+
const binding = onNamespace
|
|
106
|
+
? namespaceMacroBinding((receiver as { name: string }).name, node.name, node.args.length)
|
|
107
|
+
: receiverMacroBinding(node.name, node.args.length);
|
|
108
|
+
if (!onNamespace) collect(receiver, bound, found);
|
|
109
|
+
|
|
110
|
+
const variable = binding ? node.args[binding.variableArgument] : undefined;
|
|
111
|
+
if (!binding || variable?.kind !== "ident") {
|
|
112
|
+
for (const argument of node.args) collect(argument, bound, found);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
for (const [at, argument] of node.args.entries()) {
|
|
117
|
+
if (at === binding.variableArgument || binding.scopedArguments.includes(at)) continue;
|
|
118
|
+
collect(argument, bound, found);
|
|
119
|
+
}
|
|
120
|
+
bound.bind(variable.name);
|
|
121
|
+
for (const at of binding.scopedArguments) {
|
|
122
|
+
const argument = node.args[at];
|
|
123
|
+
if (argument) collect(argument, bound, found);
|
|
124
|
+
}
|
|
125
|
+
bound.unbind(variable.name);
|
|
126
|
+
}
|