@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.
Files changed (223) hide show
  1. package/LICENSE +17 -0
  2. package/README.md +281 -2
  3. package/dist/activation.d.ts +27 -0
  4. package/dist/activation.d.ts.map +1 -0
  5. package/dist/activation.js +24 -0
  6. package/dist/backend-runtime.d.ts +184 -0
  7. package/dist/backend-runtime.d.ts.map +1 -0
  8. package/dist/backend-runtime.js +425 -0
  9. package/dist/bounded-cache.d.ts +21 -0
  10. package/dist/bounded-cache.d.ts.map +1 -0
  11. package/dist/bounded-cache.js +42 -0
  12. package/dist/catalog-runtime.d.ts +59 -0
  13. package/dist/catalog-runtime.d.ts.map +1 -0
  14. package/dist/catalog-runtime.js +786 -0
  15. package/dist/cel-expression.d.ts +32 -0
  16. package/dist/cel-expression.d.ts.map +1 -0
  17. package/dist/cel-expression.js +29 -0
  18. package/dist/cel-map-value.d.ts +47 -0
  19. package/dist/cel-map-value.d.ts.map +1 -0
  20. package/dist/cel-map-value.js +85 -0
  21. package/dist/cel-program.d.ts +44 -0
  22. package/dist/cel-program.d.ts.map +1 -0
  23. package/dist/cel-program.js +72 -0
  24. package/dist/cel-type.d.ts +131 -0
  25. package/dist/cel-type.d.ts.map +1 -0
  26. package/dist/cel-type.js +293 -0
  27. package/dist/cel-value.d.ts +166 -0
  28. package/dist/cel-value.d.ts.map +1 -0
  29. package/dist/cel-value.js +225 -0
  30. package/dist/check-diagnostic.d.ts +54 -0
  31. package/dist/check-diagnostic.d.ts.map +1 -0
  32. package/dist/check-diagnostic.js +66 -0
  33. package/dist/checker.d.ts +86 -0
  34. package/dist/checker.d.ts.map +1 -0
  35. package/dist/checker.js +806 -0
  36. package/dist/closure-backend.d.ts +21 -0
  37. package/dist/closure-backend.d.ts.map +1 -0
  38. package/dist/closure-backend.js +487 -0
  39. package/dist/comprehension-bindings.d.ts +33 -0
  40. package/dist/comprehension-bindings.d.ts.map +1 -0
  41. package/dist/comprehension-bindings.js +50 -0
  42. package/dist/comprehension-runtime.d.ts +44 -0
  43. package/dist/comprehension-runtime.d.ts.map +1 -0
  44. package/dist/comprehension-runtime.js +137 -0
  45. package/dist/declared-chain.d.ts +35 -0
  46. package/dist/declared-chain.d.ts.map +1 -0
  47. package/dist/declared-chain.js +36 -0
  48. package/dist/duration-value.d.ts +70 -0
  49. package/dist/duration-value.d.ts.map +1 -0
  50. package/dist/duration-value.js +149 -0
  51. package/dist/emitted-module.d.ts +207 -0
  52. package/dist/emitted-module.d.ts.map +1 -0
  53. package/dist/emitted-module.js +359 -0
  54. package/dist/engine-version.d.ts +3 -0
  55. package/dist/engine-version.d.ts.map +1 -0
  56. package/dist/engine-version.js +8 -0
  57. package/dist/environment-digest.d.ts +44 -0
  58. package/dist/environment-digest.d.ts.map +1 -0
  59. package/dist/environment-digest.js +98 -0
  60. package/dist/environment.d.ts +291 -0
  61. package/dist/environment.d.ts.map +1 -0
  62. package/dist/environment.js +474 -0
  63. package/dist/function-catalog.d.ts +66 -0
  64. package/dist/function-catalog.d.ts.map +1 -0
  65. package/dist/function-catalog.js +77 -0
  66. package/dist/function-registry.d.ts +118 -0
  67. package/dist/function-registry.d.ts.map +1 -0
  68. package/dist/function-registry.js +292 -0
  69. package/dist/index.d.ts +93 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/dist/index.js +65 -0
  72. package/dist/integer-arithmetic.d.ts +27 -0
  73. package/dist/integer-arithmetic.d.ts.map +1 -0
  74. package/dist/integer-arithmetic.js +58 -0
  75. package/dist/js-emitter.d.ts +133 -0
  76. package/dist/js-emitter.d.ts.map +1 -0
  77. package/dist/js-emitter.js +568 -0
  78. package/dist/json-schema-type.d.ts +182 -0
  79. package/dist/json-schema-type.d.ts.map +1 -0
  80. package/dist/json-schema-type.js +487 -0
  81. package/dist/json-text-scan.d.ts +28 -0
  82. package/dist/json-text-scan.d.ts.map +1 -0
  83. package/dist/json-text-scan.js +159 -0
  84. package/dist/lexer.d.ts +103 -0
  85. package/dist/lexer.d.ts.map +1 -0
  86. package/dist/lexer.js +458 -0
  87. package/dist/macro-check.d.ts +41 -0
  88. package/dist/macro-check.d.ts.map +1 -0
  89. package/dist/macro-check.js +162 -0
  90. package/dist/macro-shape.d.ts +24 -0
  91. package/dist/macro-shape.d.ts.map +1 -0
  92. package/dist/macro-shape.js +55 -0
  93. package/dist/member-read.d.ts +89 -0
  94. package/dist/member-read.d.ts.map +1 -0
  95. package/dist/member-read.js +166 -0
  96. package/dist/namespace-resolution.d.ts +35 -0
  97. package/dist/namespace-resolution.d.ts.map +1 -0
  98. package/dist/namespace-resolution.js +160 -0
  99. package/dist/nominal-type.d.ts +63 -0
  100. package/dist/nominal-type.d.ts.map +1 -0
  101. package/dist/nominal-type.js +98 -0
  102. package/dist/nullable-access.d.ts +38 -0
  103. package/dist/nullable-access.d.ts.map +1 -0
  104. package/dist/nullable-access.js +93 -0
  105. package/dist/parse-limits.d.ts +26 -0
  106. package/dist/parse-limits.d.ts.map +1 -0
  107. package/dist/parse-limits.js +21 -0
  108. package/dist/parser.d.ts +48 -0
  109. package/dist/parser.d.ts.map +1 -0
  110. package/dist/parser.js +503 -0
  111. package/dist/qualified-calls.d.ts +22 -0
  112. package/dist/qualified-calls.d.ts.map +1 -0
  113. package/dist/qualified-calls.js +27 -0
  114. package/dist/regular-expression.d.ts +48 -0
  115. package/dist/regular-expression.d.ts.map +1 -0
  116. package/dist/regular-expression.js +77 -0
  117. package/dist/reserved-words.d.ts +52 -0
  118. package/dist/reserved-words.d.ts.map +1 -0
  119. package/dist/reserved-words.js +77 -0
  120. package/dist/resolved-call.d.ts +46 -0
  121. package/dist/resolved-call.d.ts.map +1 -0
  122. package/dist/resolved-call.js +15 -0
  123. package/dist/root-references.d.ts +23 -0
  124. package/dist/root-references.d.ts.map +1 -0
  125. package/dist/root-references.js +120 -0
  126. package/dist/runtime-library.d.ts +67 -0
  127. package/dist/runtime-library.d.ts.map +1 -0
  128. package/dist/runtime-library.js +554 -0
  129. package/dist/serializer.d.ts +24 -0
  130. package/dist/serializer.d.ts.map +1 -0
  131. package/dist/serializer.js +256 -0
  132. package/dist/sha256.d.ts +20 -0
  133. package/dist/sha256.d.ts.map +1 -0
  134. package/dist/sha256.js +103 -0
  135. package/dist/signature.d.ts +72 -0
  136. package/dist/signature.d.ts.map +1 -0
  137. package/dist/signature.js +61 -0
  138. package/dist/signatures/function-catalog.json +788 -0
  139. package/dist/signatures/standard-library.json +229 -0
  140. package/dist/standard-library.d.ts +41 -0
  141. package/dist/standard-library.d.ts.map +1 -0
  142. package/dist/standard-library.js +85 -0
  143. package/dist/syntax-diagnostic.d.ts +61 -0
  144. package/dist/syntax-diagnostic.d.ts.map +1 -0
  145. package/dist/syntax-diagnostic.js +28 -0
  146. package/dist/syntax-tree.d.ts +160 -0
  147. package/dist/syntax-tree.d.ts.map +1 -0
  148. package/dist/syntax-tree.js +58 -0
  149. package/dist/timestamp-value.d.ts +62 -0
  150. package/dist/timestamp-value.d.ts.map +1 -0
  151. package/dist/timestamp-value.js +238 -0
  152. package/dist/tree-equality.d.ts +15 -0
  153. package/dist/tree-equality.d.ts.map +1 -0
  154. package/dist/tree-equality.js +105 -0
  155. package/dist/type-expression.d.ts +42 -0
  156. package/dist/type-expression.d.ts.map +1 -0
  157. package/dist/type-expression.js +154 -0
  158. package/dist/value-equality.d.ts +38 -0
  159. package/dist/value-equality.d.ts.map +1 -0
  160. package/dist/value-equality.js +196 -0
  161. package/dist/value-text.d.ts +25 -0
  162. package/dist/value-text.d.ts.map +1 -0
  163. package/dist/value-text.js +44 -0
  164. package/dist/zoned-calendar.d.ts +61 -0
  165. package/dist/zoned-calendar.d.ts.map +1 -0
  166. package/dist/zoned-calendar.js +143 -0
  167. package/package.json +56 -3
  168. package/src/activation.ts +32 -0
  169. package/src/backend-runtime.ts +604 -0
  170. package/src/bounded-cache.ts +45 -0
  171. package/src/catalog-runtime.ts +921 -0
  172. package/src/cel-expression.ts +53 -0
  173. package/src/cel-map-value.ts +97 -0
  174. package/src/cel-program.ts +103 -0
  175. package/src/cel-type.ts +359 -0
  176. package/src/cel-value.ts +361 -0
  177. package/src/check-diagnostic.ts +104 -0
  178. package/src/checker.ts +1045 -0
  179. package/src/closure-backend.ts +547 -0
  180. package/src/comprehension-bindings.ts +66 -0
  181. package/src/comprehension-runtime.ts +157 -0
  182. package/src/declared-chain.ts +45 -0
  183. package/src/duration-value.ts +160 -0
  184. package/src/emitted-module.ts +494 -0
  185. package/src/engine-version.ts +9 -0
  186. package/src/environment-digest.ts +111 -0
  187. package/src/environment.ts +761 -0
  188. package/src/function-catalog.ts +140 -0
  189. package/src/function-registry.ts +341 -0
  190. package/src/index.ts +407 -0
  191. package/src/integer-arithmetic.ts +64 -0
  192. package/src/js-emitter.ts +721 -0
  193. package/src/json-schema-type.ts +664 -0
  194. package/src/json-text-scan.ts +163 -0
  195. package/src/lexer.ts +562 -0
  196. package/src/macro-check.ts +197 -0
  197. package/src/macro-shape.ts +66 -0
  198. package/src/member-read.ts +167 -0
  199. package/src/namespace-resolution.ts +167 -0
  200. package/src/nominal-type.ts +149 -0
  201. package/src/nullable-access.ts +95 -0
  202. package/src/parse-limits.ts +36 -0
  203. package/src/parser.ts +554 -0
  204. package/src/qualified-calls.ts +39 -0
  205. package/src/regular-expression.ts +101 -0
  206. package/src/reserved-words.ts +94 -0
  207. package/src/resolved-call.ts +47 -0
  208. package/src/root-references.ts +126 -0
  209. package/src/runtime-library.ts +639 -0
  210. package/src/serializer.ts +262 -0
  211. package/src/sha256.ts +112 -0
  212. package/src/signature.ts +127 -0
  213. package/src/signatures/function-catalog.json +788 -0
  214. package/src/signatures/standard-library.json +235 -0
  215. package/src/standard-library.ts +149 -0
  216. package/src/syntax-diagnostic.ts +72 -0
  217. package/src/syntax-tree.ts +229 -0
  218. package/src/timestamp-value.ts +310 -0
  219. package/src/tree-equality.ts +130 -0
  220. package/src/type-expression.ts +182 -0
  221. package/src/value-equality.ts +201 -0
  222. package/src/value-text.ts +45 -0
  223. package/src/zoned-calendar.ts +178 -0
package/dist/parser.js ADDED
@@ -0,0 +1,503 @@
1
+ /**
2
+ * CEL's grammar, read by recursive descent into the canonical tree.
3
+ *
4
+ * **Error recovery is the point.** Parsing never throws and never discards what it
5
+ * read: the first thing it cannot read becomes one ranged diagnostic, the reader
6
+ * stops there, and the tree holds the longest prefix it understood with an
7
+ * `unparsed` node where the rest would have been. That is what lets completion and
8
+ * hover work on an expression mid-token, and nothing downstream has to re-parse a
9
+ * shortened prefix to get an answer. A member left unnamed (`request.`) is a select
10
+ * with an **empty field name**, which is the shape completion after a dot reads; an
11
+ * aggregate or a call left open keeps the elements it read. Neither can be
12
+ * serialized, and the diagnostic is what says the tree is incomplete.
13
+ *
14
+ * **Nothing is expanded.** A macro call (`has(x)`, `xs.map(i, i)`, `cel.bind(…)`) is
15
+ * an ordinary call node: expanding it here would make the serializer unable to
16
+ * write the source back, and lowering it to a comprehension is the checker's and a
17
+ * backend's business. A qualified call is not produced here either — see
18
+ * `namespace-resolution.ts`.
19
+ *
20
+ * **A unary minus directly on a numeric literal folds into the literal.** That is
21
+ * how the int64 minimum is written at all, and it keeps `-0.0` a value rather than
22
+ * a negation of zero. A minus on a parenthesized literal does not fold, so the
23
+ * serializer can write a hand-built negation back without changing its shape.
24
+ */
25
+ import { tokenize } from "./lexer.js";
26
+ import { resolveParseLimits } from "./parse-limits.js";
27
+ const RELATIONS = ["==", "!=", "<", "<=", ">", ">="];
28
+ const ADDITIVE = ["+", "-"];
29
+ const MULTIPLICATIVE = ["*", "/", "%"];
30
+ class Parser {
31
+ source;
32
+ tokens;
33
+ diagnostics;
34
+ limits;
35
+ optionalSyntax;
36
+ at = 0;
37
+ nodes = 0;
38
+ depth = 0;
39
+ stopped = false;
40
+ constructor(source, tokens, diagnostics, limits, optionalSyntax) {
41
+ this.source = source;
42
+ this.tokens = tokens;
43
+ this.diagnostics = diagnostics;
44
+ this.limits = limits;
45
+ this.optionalSyntax = optionalSyntax;
46
+ if (diagnostics.reported)
47
+ this.stopped = true;
48
+ }
49
+ parse() {
50
+ const root = this.conditional();
51
+ if (!this.stopped && this.peek().type !== "eof")
52
+ this.unexpected(this.peek());
53
+ return root;
54
+ }
55
+ // --- token access -------------------------------------------------------
56
+ peek(ahead = 0) {
57
+ return this.tokens[Math.min(this.at + ahead, this.tokens.length - 1)];
58
+ }
59
+ advance() {
60
+ const token = this.peek();
61
+ if (this.at < this.tokens.length - 1)
62
+ this.at += 1;
63
+ return token;
64
+ }
65
+ isPunct(text) {
66
+ const token = this.peek();
67
+ return token.type === "punct" && token.text === text;
68
+ }
69
+ takePunct(text) {
70
+ if (!this.isPunct(text))
71
+ return false;
72
+ this.advance();
73
+ return true;
74
+ }
75
+ expectPunct(text) {
76
+ if (this.takePunct(text))
77
+ return true;
78
+ this.unexpected(this.peek(), text);
79
+ return false;
80
+ }
81
+ // --- diagnostics and budgets -------------------------------------------
82
+ unexpected(token, expected) {
83
+ const wanted = expected === undefined ? "" : `, expected ${JSON.stringify(expected)}`;
84
+ if (token.type === "eof") {
85
+ this.diagnostics.report("unexpected_end", `the expression ends before it is complete${wanted}`, token.start, token.end);
86
+ }
87
+ else {
88
+ this.diagnostics.report("unexpected_token", `${JSON.stringify(this.source.slice(token.start, token.end))} cannot stand here${wanted}`, token.start, token.end);
89
+ }
90
+ this.stopped = true;
91
+ }
92
+ limit(what, max, start, end) {
93
+ this.diagnostics.report("limit_exceeded", `the expression has more ${what} than the limit of ${max}`, start, end);
94
+ this.stopped = true;
95
+ }
96
+ /** Counts a node against the node budget. */
97
+ keep(node) {
98
+ this.nodes += 1;
99
+ if (this.nodes > this.limits.maxNodes && !this.stopped) {
100
+ this.limit("nodes", this.limits.maxNodes, node.range[0], node.range[1]);
101
+ }
102
+ return node;
103
+ }
104
+ enter(start) {
105
+ this.depth += 1;
106
+ if (this.depth <= this.limits.maxDepth)
107
+ return true;
108
+ if (!this.stopped)
109
+ this.limit("nesting", this.limits.maxDepth, start, start);
110
+ return false;
111
+ }
112
+ leave() {
113
+ this.depth -= 1;
114
+ }
115
+ unparsed(start, end = start) {
116
+ return this.keep({ kind: "unparsed", range: [start, end] });
117
+ }
118
+ spanning(left, right) {
119
+ return [left.range[0], right.range[1]];
120
+ }
121
+ // --- the grammar -------------------------------------------------------
122
+ conditional() {
123
+ const start = this.peek().start;
124
+ if (!this.enter(start))
125
+ return this.unparsed(start);
126
+ try {
127
+ const condition = this.binary(0);
128
+ if (this.stopped || !this.isPunct("?"))
129
+ return condition;
130
+ this.advance();
131
+ const whenTrue = this.conditional();
132
+ const whenFalse = this.stopped || !this.expectPunct(":")
133
+ ? this.unparsed(whenTrue.range[1])
134
+ : this.conditional();
135
+ return this.keep({
136
+ kind: "conditional",
137
+ condition,
138
+ whenTrue,
139
+ whenFalse,
140
+ range: this.spanning(condition, whenFalse),
141
+ });
142
+ }
143
+ finally {
144
+ this.leave();
145
+ }
146
+ }
147
+ /**
148
+ * The binary levels, lowest first: `||`, `&&`, the relations, `+ -`, `* / %`.
149
+ * One loop per level, each left-associative, so a long chain costs no depth.
150
+ */
151
+ binary(level) {
152
+ if (level >= 5)
153
+ return this.unary();
154
+ let left = this.binary(level + 1);
155
+ while (!this.stopped) {
156
+ const operator = this.binaryOperatorAt(level);
157
+ if (!operator)
158
+ return left;
159
+ this.advance();
160
+ const right = this.binary(level + 1);
161
+ left = this.keep({ kind: "binary", operator, left, right, range: this.spanning(left, right) });
162
+ if (right.kind === "unparsed")
163
+ return left;
164
+ }
165
+ return left;
166
+ }
167
+ binaryOperatorAt(level) {
168
+ const token = this.peek();
169
+ if (level === 2 && token.type === "keyword" && token.text === "in")
170
+ return "in";
171
+ if (token.type !== "punct")
172
+ return undefined;
173
+ const text = token.text;
174
+ const wanted = level === 0 ? ["||"] : level === 1 ? ["&&"] : level === 2 ? RELATIONS : level === 3 ? ADDITIVE : MULTIPLICATIVE;
175
+ return wanted.includes(text) ? text : undefined;
176
+ }
177
+ unary() {
178
+ const token = this.peek();
179
+ if (token.type === "punct" && (token.text === "!" || token.text === "-")) {
180
+ const folded = token.text === "-" ? this.foldedNumber(token) : undefined;
181
+ if (folded)
182
+ return this.postfix(folded);
183
+ if (!this.enter(token.start))
184
+ return this.unparsed(token.start);
185
+ try {
186
+ this.advance();
187
+ const operand = this.unary();
188
+ return this.keep({
189
+ kind: "unary",
190
+ operator: token.text,
191
+ operand,
192
+ range: [token.start, operand.range[1]],
193
+ });
194
+ }
195
+ finally {
196
+ this.leave();
197
+ }
198
+ }
199
+ return this.postfix(this.primary());
200
+ }
201
+ /** `-` directly on an int or double literal is part of the literal. */
202
+ foldedNumber(minus) {
203
+ const number = this.peek(1);
204
+ if (number.type !== "int" && number.type !== "double")
205
+ return undefined;
206
+ this.advance();
207
+ this.advance();
208
+ const literal = number.type === "int"
209
+ ? { type: "int", value: -number.int }
210
+ : { type: "double", value: -number.double };
211
+ return this.keep({ kind: "literal", literal, range: [minus.start, number.end] });
212
+ }
213
+ postfix(operand) {
214
+ let node = operand;
215
+ while (!this.stopped) {
216
+ if (this.takePunct(".")) {
217
+ const optional = this.takePunct("?");
218
+ const name = this.peek();
219
+ const quoted = name.type === "quotedIdent";
220
+ if (!this.expectMemberName(name)) {
221
+ // The member is unnamed: the select stands with an empty field, which is
222
+ // what an editor reads to complete a member after the dot.
223
+ return this.keep({
224
+ kind: "select",
225
+ operand: node,
226
+ field: "",
227
+ fieldRange: [name.start, name.start],
228
+ optional,
229
+ quoted: false,
230
+ range: [node.range[0], name.start],
231
+ });
232
+ }
233
+ this.advance();
234
+ const fieldRange = [name.start, name.end];
235
+ if (quoted && this.isPunct("(")) {
236
+ // cel-spec admits a quoted name where a FIELD is read (`member '.' escapeIdent`)
237
+ // and nowhere else: a called function is always an identifier. Keeping that line
238
+ // is also what keeps a namespaced call to exactly one spelling.
239
+ this.unexpected(this.peek(), "a member read — a quoted name is a field, not a call");
240
+ return this.keep({
241
+ kind: "select",
242
+ operand: node,
243
+ field: name.text,
244
+ fieldRange,
245
+ optional,
246
+ quoted,
247
+ range: [node.range[0], name.end],
248
+ });
249
+ }
250
+ if (this.isPunct("(")) {
251
+ const args = this.callArguments();
252
+ node = this.keep({
253
+ kind: "receiverCall",
254
+ receiver: node,
255
+ name: name.text,
256
+ nameRange: fieldRange,
257
+ args: args.nodes,
258
+ range: [node.range[0], args.end],
259
+ });
260
+ continue;
261
+ }
262
+ node = this.keep({
263
+ kind: "select",
264
+ operand: node,
265
+ field: name.text,
266
+ fieldRange,
267
+ optional,
268
+ quoted,
269
+ range: [node.range[0], name.end],
270
+ });
271
+ continue;
272
+ }
273
+ if (this.isPunct("[")) {
274
+ this.advance();
275
+ const optional = this.takePunct("?");
276
+ const index = this.conditional();
277
+ const closed = !this.stopped && this.expectPunct("]");
278
+ const end = closed ? this.tokens[this.at - 1].end : index.range[1];
279
+ node = this.keep({ kind: "index", operand: node, index, optional, range: [node.range[0], end] });
280
+ if (!closed)
281
+ return node;
282
+ continue;
283
+ }
284
+ return node;
285
+ }
286
+ return node;
287
+ }
288
+ /**
289
+ * A field or a called function may be named by any word, however that word is read
290
+ * elsewhere — `{'let': 1}.let`, `a.in` and `a.true` are all CEL. A member names a
291
+ * value's entry, not a name in the expression's scope.
292
+ */
293
+ expectMemberName(token) {
294
+ if (token.type === "ident" || token.type === "reserved" || token.type === "keyword")
295
+ return true;
296
+ // A name between backticks, which is how a member not spelled as an identifier is read.
297
+ if (token.type === "quotedIdent")
298
+ return true;
299
+ return this.expectIdentifier(token);
300
+ }
301
+ expectIdentifier(token) {
302
+ if (token.type === "ident")
303
+ return true;
304
+ if (token.type === "reserved") {
305
+ this.diagnostics.report("reserved_identifier", `${JSON.stringify(token.text)} is a reserved word and cannot be used as a name`, token.start, token.end);
306
+ this.stopped = true;
307
+ return false;
308
+ }
309
+ this.unexpected(token, "a name");
310
+ return false;
311
+ }
312
+ primary() {
313
+ const token = this.peek();
314
+ switch (token.type) {
315
+ case "int":
316
+ this.advance();
317
+ if (token.atIntBoundary) {
318
+ // 2^63 is a magnitude, not a value: it is only the int64 minimum, which
319
+ // the fold under a unary minus has already taken.
320
+ this.diagnostics.report("invalid_integer", `${token.text} is outside the range of a 64-bit integer`, token.start, token.end);
321
+ this.stopped = true;
322
+ return this.unparsed(token.start, token.end);
323
+ }
324
+ return this.keep({
325
+ kind: "literal",
326
+ literal: { type: "int", value: token.int },
327
+ range: [token.start, token.end],
328
+ });
329
+ case "uint":
330
+ this.advance();
331
+ return this.keep({
332
+ kind: "literal",
333
+ literal: { type: "uint", value: token.int },
334
+ range: [token.start, token.end],
335
+ });
336
+ case "double":
337
+ this.advance();
338
+ return this.keep({
339
+ kind: "literal",
340
+ literal: { type: "double", value: token.double },
341
+ range: [token.start, token.end],
342
+ });
343
+ case "string":
344
+ this.advance();
345
+ return this.keep({
346
+ kind: "literal",
347
+ literal: { type: "string", value: token.string },
348
+ range: [token.start, token.end],
349
+ });
350
+ case "bytes":
351
+ this.advance();
352
+ return this.keep({
353
+ kind: "literal",
354
+ literal: { type: "bytes", value: token.bytes },
355
+ range: [token.start, token.end],
356
+ });
357
+ case "reserved":
358
+ this.expectIdentifier(token);
359
+ return this.unparsed(token.start, token.end);
360
+ case "keyword":
361
+ // An operator written as a word, where an expression must begin.
362
+ this.unexpected(token);
363
+ return this.unparsed(token.start, token.end);
364
+ case "quotedIdent":
365
+ // A quoted name reads a MEMBER; nothing else in the language is written that way.
366
+ this.unexpected(token, "a member read, as in a.`b`");
367
+ return this.unparsed(token.start, token.end);
368
+ case "ident":
369
+ return this.word(token);
370
+ case "punct":
371
+ if (token.text === "(")
372
+ return this.parenthesized(token);
373
+ if (token.text === "[")
374
+ return this.list(token);
375
+ if (token.text === "{")
376
+ return this.map(token);
377
+ if (token.text === ".")
378
+ return this.absoluteName(token);
379
+ this.unexpected(token);
380
+ return this.unparsed(token.start, token.end);
381
+ case "eof":
382
+ this.unexpected(token);
383
+ return this.unparsed(token.start, token.end);
384
+ }
385
+ }
386
+ /**
387
+ * `.y` — a name resolved against the environment alone. It is the only spelling for an
388
+ * outer name a comprehension variable of the same name would otherwise hide.
389
+ */
390
+ absoluteName(dot) {
391
+ this.advance();
392
+ const name = this.peek();
393
+ // A NAME, not a member: cel-spec's leading-dot form takes an identifier, so neither a
394
+ // reserved word nor a quoted name opens one.
395
+ if (!this.expectIdentifier(name))
396
+ return this.unparsed(dot.start, name.start);
397
+ this.advance();
398
+ return this.keep({ kind: "ident", name: name.text, absolute: true, range: [dot.start, name.end] });
399
+ }
400
+ word(token) {
401
+ this.advance();
402
+ const range = [token.start, token.end];
403
+ if (token.text === "true" || token.text === "false") {
404
+ return this.keep({ kind: "literal", literal: { type: "bool", value: token.text === "true" }, range });
405
+ }
406
+ if (token.text === "null") {
407
+ return this.keep({ kind: "literal", literal: { type: "null" }, range });
408
+ }
409
+ if (!this.isPunct("("))
410
+ return this.keep({ kind: "ident", name: token.text, absolute: false, range });
411
+ const args = this.callArguments();
412
+ return this.keep({
413
+ kind: "call",
414
+ name: token.text,
415
+ nameRange: range,
416
+ args: args.nodes,
417
+ range: [token.start, args.end],
418
+ });
419
+ }
420
+ parenthesized(open) {
421
+ this.advance();
422
+ const inner = this.conditional();
423
+ if (!this.stopped)
424
+ this.expectPunct(")");
425
+ return inner;
426
+ }
427
+ /** The arguments of a call, the open paren being the current token. */
428
+ callArguments() {
429
+ const open = this.advance();
430
+ const nodes = [];
431
+ while (!this.stopped && !this.isPunct(")")) {
432
+ const argument = this.conditional();
433
+ nodes.push(argument);
434
+ if (nodes.length > this.limits.maxCallArguments) {
435
+ this.limit("call arguments", this.limits.maxCallArguments, open.start, argument.range[1]);
436
+ break;
437
+ }
438
+ if (!this.takePunct(","))
439
+ break;
440
+ }
441
+ if (this.stopped)
442
+ return { nodes, end: nodes.at(-1)?.range[1] ?? open.end };
443
+ const close = this.peek();
444
+ if (!this.expectPunct(")"))
445
+ return { nodes, end: close.end };
446
+ return { nodes, end: close.end };
447
+ }
448
+ list(open) {
449
+ this.advance();
450
+ const elements = [];
451
+ while (!this.stopped && !this.isPunct("]")) {
452
+ const optional = this.optionalSyntax && this.takePunct("?");
453
+ const value = this.conditional();
454
+ elements.push({ value, optional });
455
+ if (elements.length > this.limits.maxListElements) {
456
+ this.limit("list elements", this.limits.maxListElements, open.start, value.range[1]);
457
+ break;
458
+ }
459
+ if (!this.takePunct(","))
460
+ break;
461
+ }
462
+ const end = this.closingEnd("]", open, elements.at(-1)?.value.range[1]);
463
+ return this.keep({ kind: "list", elements, range: [open.start, end] });
464
+ }
465
+ map(open) {
466
+ this.advance();
467
+ const entries = [];
468
+ while (!this.stopped && !this.isPunct("}")) {
469
+ const optional = this.optionalSyntax && this.takePunct("?");
470
+ const key = this.conditional();
471
+ if (this.stopped || !this.expectPunct(":")) {
472
+ entries.push({ key, value: this.unparsed(key.range[1]), optional });
473
+ break;
474
+ }
475
+ const value = this.conditional();
476
+ entries.push({ key, value, optional });
477
+ if (entries.length > this.limits.maxMapEntries) {
478
+ this.limit("map entries", this.limits.maxMapEntries, open.start, value.range[1]);
479
+ break;
480
+ }
481
+ if (!this.takePunct(","))
482
+ break;
483
+ }
484
+ const end = this.closingEnd("}", open, entries.at(-1)?.value.range[1]);
485
+ return this.keep({ kind: "map", entries, range: [open.start, end] });
486
+ }
487
+ /** Where an aggregate ends: at its closing bracket, or at what it did read. */
488
+ closingEnd(bracket, open, lastRead) {
489
+ if (!this.stopped && this.expectPunct(bracket))
490
+ return this.tokens[this.at - 1].end;
491
+ return lastRead ?? open.end;
492
+ }
493
+ }
494
+ /**
495
+ * Reads one CEL expression. Always answers a tree; `diagnostics` holds at most one
496
+ * entry, and holds one exactly when the whole source could not be read.
497
+ */
498
+ export function parseSyntax(source, options) {
499
+ const limits = resolveParseLimits(options?.limits);
500
+ const { tokens, diagnostics } = tokenize(source);
501
+ const root = new Parser(source, tokens, diagnostics, limits, options?.optionalSyntax ?? false).parse();
502
+ return { source, root, diagnostics: diagnostics.list() };
503
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Every call an expression makes on a namespace.
3
+ *
4
+ * Only a resolved tree holds one (`namespace-resolution.ts`), so the answer is
5
+ * always relative to the namespace set the tree was resolved under. A consumer uses
6
+ * it to say which functions of which other modules an expression reaches, which
7
+ * cannot be re-derived from the expression alone — the name set is not in the text.
8
+ *
9
+ * The order is source order: a call before any call written inside it.
10
+ */
11
+ import type { CelNode, SourceRange } from "./syntax-tree.js";
12
+ export interface QualifiedCall {
13
+ readonly namespace: string;
14
+ readonly name: string;
15
+ /** `<namespace>.<name>`, the spelling the call was written with. */
16
+ readonly qualifiedName: string;
17
+ readonly arity: number;
18
+ readonly range: SourceRange;
19
+ readonly nameRange: SourceRange;
20
+ }
21
+ export declare function qualifiedCalls(root: CelNode): readonly QualifiedCall[];
22
+ //# sourceMappingURL=qualified-calls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qualified-calls.d.ts","sourceRoot":"","sources":["../src/qualified-calls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG7D,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC;CACjC;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,aAAa,EAAE,CActE"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Every call an expression makes on a namespace.
3
+ *
4
+ * Only a resolved tree holds one (`namespace-resolution.ts`), so the answer is
5
+ * always relative to the namespace set the tree was resolved under. A consumer uses
6
+ * it to say which functions of which other modules an expression reaches, which
7
+ * cannot be re-derived from the expression alone — the name set is not in the text.
8
+ *
9
+ * The order is source order: a call before any call written inside it.
10
+ */
11
+ import { walkTree } from "./syntax-tree.js";
12
+ export function qualifiedCalls(root) {
13
+ const calls = [];
14
+ for (const node of walkTree(root)) {
15
+ if (node.kind !== "qcall")
16
+ continue;
17
+ calls.push({
18
+ namespace: node.namespace,
19
+ name: node.name,
20
+ qualifiedName: `${node.namespace}.${node.name}`,
21
+ arity: node.args.length,
22
+ range: node.range,
23
+ nameRange: node.nameRange,
24
+ });
25
+ }
26
+ return calls;
27
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * `matches` — and it is RE2, not the host's regular expressions.
3
+ *
4
+ * CEL's pattern language is RE2: linear-time matching with no backreferences and no
5
+ * lookaround. The host's own engine accepts patterns RE2 refuses and is exponential on
6
+ * some of them, so an expression that matches here would behave differently on another
7
+ * runtime and a hostile pattern in a manifest would be a denial of service. A pattern
8
+ * RE2 cannot parse is `invalid_regular_expression`, never a silent `false`.
9
+ *
10
+ * Compilation is memoized, bounded, because a pattern is almost always a literal
11
+ * evaluated many times. **One compile serves every caller** — the language's `matches`
12
+ * and the catalog's regex family alike — so a pattern is read one way and the cache is
13
+ * not duplicated; what differs is only how each words its refusal, which is why a refusal
14
+ * answers RE2's parse-error KIND beside the library's whole message.
15
+ */
16
+ import { RE2JS } from "re2js";
17
+ import { type CelError } from "./cel-value.js";
18
+ import type { SourceRange } from "./syntax-tree.js";
19
+ /** How many compiled patterns one process keeps. */
20
+ export declare const PATTERN_CACHE_CAPACITY = 128;
21
+ /** The flag letters a caller may map onto RE2's own flags. */
22
+ export declare const RE2_FLAG_LETTERS: Readonly<Record<string, number>>;
23
+ /**
24
+ * RE2's parse-error kinds, in RE2's own words — the closed vocabulary an invalid-pattern
25
+ * refusal ends with, so every engine words one identically. The last two are RE2's parser
26
+ * limits: a nesting height above 1000, and a parsed size above 3,355,443.
27
+ */
28
+ export declare const RE2_PATTERN_ERROR_KINDS: readonly string[];
29
+ /**
30
+ * Why RE2 refused a pattern: its own parse-error kind where it reported one of the
31
+ * closed vocabulary above, and its whole message. A caller that prints the kind decides
32
+ * for itself what an absent one is — the language's `matches` prints the message, so a
33
+ * refusal RE2 words some other way is still an answer about the pattern there.
34
+ */
35
+ export interface RE2PatternRefusal {
36
+ readonly kind?: string;
37
+ readonly message: string;
38
+ }
39
+ export type RE2Compiled = {
40
+ readonly pattern: RE2JS;
41
+ } | {
42
+ readonly refused: RE2PatternRefusal;
43
+ };
44
+ /** A compiled pattern, or why RE2 refused it. One compile and one cache for every caller. */
45
+ export declare function re2Pattern(pattern: string, flags?: number): RE2Compiled;
46
+ /** Whether the pattern matches anywhere in the text, or why the pattern is not one. */
47
+ export declare function celMatches(text: string, pattern: string, range?: SourceRange): boolean | CelError;
48
+ //# sourceMappingURL=regular-expression.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regular-expression.d.ts","sourceRoot":"","sources":["../src/regular-expression.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,KAAK,EAAwB,MAAM,OAAO,CAAC;AAEpD,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,oDAAoD;AACpD,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,8DAA8D;AAC9D,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAI7D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAepD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAIhG,6FAA6F;AAC7F,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,WAAW,CAkBlE;AAED,uFAAuF;AACvF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,GAAG,QAAQ,CAUjG"}