@kalada/syntax 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -0
- package/dist/index.cjs +2027 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +231 -0
- package/dist/index.d.ts +231 -0
- package/dist/index.js +2017 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2017 @@
|
|
|
1
|
+
import { KaladaV1, compileKaladaV1Program } from '@kalada/core';
|
|
2
|
+
|
|
3
|
+
// src/freeze.ts
|
|
4
|
+
function freezeRange(start, end) {
|
|
5
|
+
return Object.freeze({ start, end });
|
|
6
|
+
}
|
|
7
|
+
function deepFreeze(value) {
|
|
8
|
+
if (typeof value !== "object" || value === null || Object.isFrozen(value)) return value;
|
|
9
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
10
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
11
|
+
if (descriptor && "value" in descriptor) deepFreeze(descriptor.value);
|
|
12
|
+
}
|
|
13
|
+
return Object.freeze(value);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// src/diagnostics.ts
|
|
17
|
+
var KALADA_SYNTAX_DIAGNOSTIC_MESSAGES = Object.freeze({
|
|
18
|
+
KALADA_SYNTAX_INVALID_INPUT: "Kalada syntax input is invalid.",
|
|
19
|
+
KALADA_SYNTAX_LIMIT_EXCEEDED: "Kalada syntax input exceeds a configured limit.",
|
|
20
|
+
KALADA_SYNTAX_UNEXPECTED_TOKEN: "Kalada syntax contains an unexpected token.",
|
|
21
|
+
KALADA_SYNTAX_EXPECTED_EXPRESSION: "Kalada syntax expected an expression.",
|
|
22
|
+
KALADA_SYNTAX_EXPECTED_FIELD: "Kalada syntax expected a field name.",
|
|
23
|
+
KALADA_SYNTAX_EXPECTED_RIGHT_PARENTHESIS: "Kalada syntax expected a closing parenthesis.",
|
|
24
|
+
KALADA_SYNTAX_EXPECTED_COLON: "Kalada syntax expected a colon.",
|
|
25
|
+
KALADA_SYNTAX_INVALID_NUMBER: "Kalada syntax contains an invalid JSON number.",
|
|
26
|
+
KALADA_SYNTAX_INVALID_STRING: "Kalada syntax contains an invalid JSON string.",
|
|
27
|
+
KALADA_SYNTAX_UNSUPPORTED_FORM: "Kalada syntax form is not supported.",
|
|
28
|
+
KALADA_SYNTAX_UNSUPPORTED_OPERATOR: "Kalada syntax operator is not supported.",
|
|
29
|
+
KALADA_SYNTAX_RELATIONAL_CHAIN: "Kalada relational and membership operators cannot be chained.",
|
|
30
|
+
KALADA_SYNTAX_COALESCE_LOGICAL_MIX: "Kalada null coalescing cannot be mixed with && or || without parentheses.",
|
|
31
|
+
KALADA_SYNTAX_UNKNOWN_REFERENCE: "Kalada reference is not present in the lowering environment.",
|
|
32
|
+
KALADA_SYNTAX_WRITE_INELIGIBLE: "Expression is not a static direct write location.",
|
|
33
|
+
KALADA_SYNTAX_WRITE_BINDING_INVALID: "Write location binding is missing or invalid.",
|
|
34
|
+
KALADA_SYNTAX_WRITE_PROPERTY_INVALID: "Write location property is missing or invalid."
|
|
35
|
+
});
|
|
36
|
+
var DiagnosticSink = class {
|
|
37
|
+
diagnostics;
|
|
38
|
+
limits;
|
|
39
|
+
limitIndex;
|
|
40
|
+
constructor(limits, initial = []) {
|
|
41
|
+
this.limits = limits;
|
|
42
|
+
this.diagnostics = [...initial];
|
|
43
|
+
const limitIndex = initial.findIndex((item) => item.code === "KALADA_SYNTAX_LIMIT_EXCEEDED");
|
|
44
|
+
this.limitIndex = limitIndex < 0 ? null : limitIndex;
|
|
45
|
+
}
|
|
46
|
+
add(phase, code, range, path = []) {
|
|
47
|
+
if (this.diagnostics.length >= this.limits.maxDiagnostics) {
|
|
48
|
+
if (this.limitIndex === null) this.limit(phase, range);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
this.diagnostics.push(
|
|
52
|
+
diagnostic(phase, code, KALADA_SYNTAX_DIAGNOSTIC_MESSAGES[code], range, path)
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
limit(phase, range) {
|
|
56
|
+
const current = this.limitIndex === null ? void 0 : this.diagnostics[this.limitIndex];
|
|
57
|
+
if (current && current.range.start <= range.start) return;
|
|
58
|
+
const problem = diagnostic(
|
|
59
|
+
phase,
|
|
60
|
+
"KALADA_SYNTAX_LIMIT_EXCEEDED",
|
|
61
|
+
KALADA_SYNTAX_DIAGNOSTIC_MESSAGES.KALADA_SYNTAX_LIMIT_EXCEEDED,
|
|
62
|
+
range,
|
|
63
|
+
[]
|
|
64
|
+
);
|
|
65
|
+
if (this.limitIndex !== null) {
|
|
66
|
+
this.diagnostics.splice(this.limitIndex, 1, problem);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (this.diagnostics.length >= this.limits.maxDiagnostics) {
|
|
70
|
+
this.diagnostics.splice(-1, 1, problem);
|
|
71
|
+
this.limitIndex = this.diagnostics.length - 1;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
this.diagnostics.push(problem);
|
|
75
|
+
this.limitIndex = this.diagnostics.length - 1;
|
|
76
|
+
}
|
|
77
|
+
hasLexicalLimitAt(start) {
|
|
78
|
+
return this.diagnostics.some(
|
|
79
|
+
(item) => item.phase === "lex" && item.code === "KALADA_SYNTAX_LIMIT_EXCEEDED" && item.range.start === start
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
function diagnostic(phase, code, message, range, path) {
|
|
84
|
+
return deepFreeze({
|
|
85
|
+
phase,
|
|
86
|
+
code,
|
|
87
|
+
message,
|
|
88
|
+
range: freezeRange(range.start, range.end),
|
|
89
|
+
path: [...path]
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/limits.ts
|
|
94
|
+
var DEFAULT_KALADA_SYNTAX_LIMITS = Object.freeze({
|
|
95
|
+
maxSourceLength: 1e5,
|
|
96
|
+
maxTokens: 2e4,
|
|
97
|
+
maxCstDepth: 64,
|
|
98
|
+
maxCstNodes: 1e4,
|
|
99
|
+
maxDiagnostics: 100,
|
|
100
|
+
maxRecoveryTokens: 1e3,
|
|
101
|
+
maxIdentifierLength: 1e3,
|
|
102
|
+
maxDecodedStringLength: 1e4,
|
|
103
|
+
maxStaticTypeDepth: 64
|
|
104
|
+
});
|
|
105
|
+
var MAXIMUM_KALADA_SYNTAX_LIMITS = Object.freeze({
|
|
106
|
+
maxSourceLength: 1e6,
|
|
107
|
+
maxTokens: 2e5,
|
|
108
|
+
maxCstDepth: 256,
|
|
109
|
+
maxCstNodes: 1e5,
|
|
110
|
+
maxDiagnostics: 1e3,
|
|
111
|
+
maxRecoveryTokens: 1e4,
|
|
112
|
+
maxIdentifierLength: 1e5,
|
|
113
|
+
maxDecodedStringLength: 1e6,
|
|
114
|
+
maxStaticTypeDepth: 256
|
|
115
|
+
});
|
|
116
|
+
function resolveSyntaxLimits(options) {
|
|
117
|
+
if (options === void 0) return DEFAULT_KALADA_SYNTAX_LIMITS;
|
|
118
|
+
if (!hasOnlyKeys(options, /* @__PURE__ */ new Set(["limits"]))) return null;
|
|
119
|
+
const limitsProperty = ownDataProperty(options, "limits");
|
|
120
|
+
if (!limitsProperty.ok) return null;
|
|
121
|
+
if (!limitsProperty.present) return DEFAULT_KALADA_SYNTAX_LIMITS;
|
|
122
|
+
const limits = limitsProperty.value;
|
|
123
|
+
if (!isPlainRecord(limits)) return null;
|
|
124
|
+
const output = { ...DEFAULT_KALADA_SYNTAX_LIMITS };
|
|
125
|
+
for (const key of Object.keys(DEFAULT_KALADA_SYNTAX_LIMITS)) {
|
|
126
|
+
const property2 = ownDataProperty(limits, key);
|
|
127
|
+
if (!property2.ok) return null;
|
|
128
|
+
if (!property2.present || property2.value === void 0) continue;
|
|
129
|
+
const value = property2.value;
|
|
130
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1 || value > MAXIMUM_KALADA_SYNTAX_LIMITS[key])
|
|
131
|
+
return null;
|
|
132
|
+
output[key] = value;
|
|
133
|
+
}
|
|
134
|
+
return Object.freeze(output);
|
|
135
|
+
}
|
|
136
|
+
function ownDataProperty(input, key) {
|
|
137
|
+
if (typeof input !== "object" && typeof input !== "function" || input === null)
|
|
138
|
+
return { ok: false, present: false };
|
|
139
|
+
try {
|
|
140
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, key);
|
|
141
|
+
if (!descriptor) return { ok: true, present: false };
|
|
142
|
+
return "value" in descriptor ? { ok: true, present: true, value: descriptor.value } : { ok: false, present: true };
|
|
143
|
+
} catch {
|
|
144
|
+
return { ok: false, present: false };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function isPlainRecord(input) {
|
|
148
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) return false;
|
|
149
|
+
try {
|
|
150
|
+
const keys = Reflect.ownKeys(input);
|
|
151
|
+
const allowed = new Set(Reflect.ownKeys(DEFAULT_KALADA_SYNTAX_LIMITS));
|
|
152
|
+
return keys.every((key) => typeof key === "string" && allowed.has(key));
|
|
153
|
+
} catch {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function hasOnlyKeys(input, allowed) {
|
|
158
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) return false;
|
|
159
|
+
try {
|
|
160
|
+
return Reflect.ownKeys(input).every((key) => typeof key === "string" && allowed.has(key));
|
|
161
|
+
} catch {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/lex-literals.ts
|
|
167
|
+
function token(kind, text, start, end) {
|
|
168
|
+
return Object.freeze({ kind, text, range: freezeRange(start, end) });
|
|
169
|
+
}
|
|
170
|
+
function limitedToken(source, start, end, sink, guest) {
|
|
171
|
+
const limitEnd = guest ? end : source.length;
|
|
172
|
+
sink.limit("lex", freezeRange(start, limitEnd));
|
|
173
|
+
if (guest) guest.limited = true;
|
|
174
|
+
return token("invalid", source.slice(start, limitEnd), start, limitEnd);
|
|
175
|
+
}
|
|
176
|
+
function scanNumber(source, start, sink) {
|
|
177
|
+
let end = scanDigits(source, start);
|
|
178
|
+
if (source[end] === "." && isDigit(source[end + 1])) {
|
|
179
|
+
end = scanDigits(source, end + 1);
|
|
180
|
+
}
|
|
181
|
+
if (source[end] === "e" || source[end] === "E") {
|
|
182
|
+
end += 1;
|
|
183
|
+
if (source[end] === "+" || source[end] === "-") end += 1;
|
|
184
|
+
end = scanDigits(source, end);
|
|
185
|
+
}
|
|
186
|
+
let text = source.slice(start, end);
|
|
187
|
+
let valid = validNumber(text);
|
|
188
|
+
if (source[end] === "." && !(valid && isIdentifierStart(source[end + 1]))) {
|
|
189
|
+
end += 1;
|
|
190
|
+
text = source.slice(start, end);
|
|
191
|
+
valid = false;
|
|
192
|
+
}
|
|
193
|
+
if (!valid) sink.add("lex", "KALADA_SYNTAX_INVALID_NUMBER", freezeRange(start, end));
|
|
194
|
+
return token(valid ? "number" : "invalid", text, start, end);
|
|
195
|
+
}
|
|
196
|
+
function scanDigits(source, start) {
|
|
197
|
+
let end = start;
|
|
198
|
+
while (end < source.length && isDigit(source[end])) end += 1;
|
|
199
|
+
return end;
|
|
200
|
+
}
|
|
201
|
+
function validNumber(text) {
|
|
202
|
+
return /^(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/u.test(text) && Number.isFinite(Number(text));
|
|
203
|
+
}
|
|
204
|
+
function scanString(source, start, limits, sink, guest) {
|
|
205
|
+
let end = start + 1;
|
|
206
|
+
let escaped = false;
|
|
207
|
+
while (end < source.length) {
|
|
208
|
+
const character = source[end];
|
|
209
|
+
end += 1;
|
|
210
|
+
if (!escaped && character === '"') break;
|
|
211
|
+
escaped = !escaped && character === "\\";
|
|
212
|
+
if (character !== "\\") escaped = false;
|
|
213
|
+
}
|
|
214
|
+
const text = source.slice(start, end);
|
|
215
|
+
let value;
|
|
216
|
+
try {
|
|
217
|
+
value = JSON.parse(text);
|
|
218
|
+
} catch {
|
|
219
|
+
value = null;
|
|
220
|
+
}
|
|
221
|
+
if (typeof value === "string" && value.length > limits.maxDecodedStringLength) {
|
|
222
|
+
return limitedToken(source, start, end, sink, guest);
|
|
223
|
+
}
|
|
224
|
+
const valid = typeof value === "string";
|
|
225
|
+
if (!valid) sink.add("lex", "KALADA_SYNTAX_INVALID_STRING", freezeRange(start, end));
|
|
226
|
+
return token(valid ? "string" : "invalid", text, start, end);
|
|
227
|
+
}
|
|
228
|
+
function isIdentifierStart(value) {
|
|
229
|
+
return value?.length === 1 && /[A-Za-z_]/u.test(value);
|
|
230
|
+
}
|
|
231
|
+
function isDigit(value) {
|
|
232
|
+
return /[0-9]/u.test(value);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/lexer.ts
|
|
236
|
+
var VALID_OPERATORS = [
|
|
237
|
+
"??",
|
|
238
|
+
"&&",
|
|
239
|
+
"||",
|
|
240
|
+
"==",
|
|
241
|
+
"!=",
|
|
242
|
+
"<=",
|
|
243
|
+
">=",
|
|
244
|
+
"+",
|
|
245
|
+
"-",
|
|
246
|
+
"*",
|
|
247
|
+
"/",
|
|
248
|
+
"%",
|
|
249
|
+
"!",
|
|
250
|
+
"<",
|
|
251
|
+
">"
|
|
252
|
+
];
|
|
253
|
+
var UNSUPPORTED_OPERATORS = [
|
|
254
|
+
"===",
|
|
255
|
+
"!==",
|
|
256
|
+
"=>",
|
|
257
|
+
"**",
|
|
258
|
+
"++",
|
|
259
|
+
"--",
|
|
260
|
+
"<<",
|
|
261
|
+
">>",
|
|
262
|
+
"^",
|
|
263
|
+
"&",
|
|
264
|
+
"|",
|
|
265
|
+
"=",
|
|
266
|
+
"~"
|
|
267
|
+
];
|
|
268
|
+
function lex(source, limits) {
|
|
269
|
+
const sink = new DiagnosticSink(limits);
|
|
270
|
+
if (source.length > limits.maxSourceLength) {
|
|
271
|
+
sink.limit("lex", freezeRange(0, source.length));
|
|
272
|
+
return result(
|
|
273
|
+
[token("invalid", source, 0, source.length), token("eof", "", source.length, source.length)],
|
|
274
|
+
sink
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
const tokens = [];
|
|
278
|
+
let offset = 0;
|
|
279
|
+
while (offset < source.length) {
|
|
280
|
+
if (tokens.length >= limits.maxTokens) {
|
|
281
|
+
sink.limit("lex", freezeRange(offset, source.length));
|
|
282
|
+
tokens.push(token("invalid", source.slice(offset), offset, source.length));
|
|
283
|
+
offset = source.length;
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
const next = scanToken(source, offset, limits, sink);
|
|
287
|
+
tokens.push(next);
|
|
288
|
+
offset = next.range.end;
|
|
289
|
+
}
|
|
290
|
+
tokens.push(token("eof", "", source.length, source.length));
|
|
291
|
+
return result(tokens, sink);
|
|
292
|
+
}
|
|
293
|
+
function lexGuest(source, start, limits) {
|
|
294
|
+
const window = source.slice(start, start + limits.maxSourceLength + 1);
|
|
295
|
+
const scanned = scanGuest(window, limits);
|
|
296
|
+
return deepFreeze({
|
|
297
|
+
stop: scanned.stop === null ? null : start + scanned.stop,
|
|
298
|
+
unmatchedParentheses: scanned.unmatchedParentheses,
|
|
299
|
+
unsupportedComment: scanned.unsupportedComment,
|
|
300
|
+
unsupportedQuote: scanned.unsupportedQuote,
|
|
301
|
+
tokens: scanned.tokens.map(
|
|
302
|
+
(item) => token(item.kind, item.text, start + item.range.start, start + item.range.end)
|
|
303
|
+
),
|
|
304
|
+
diagnostics: scanned.diagnostics.map(
|
|
305
|
+
(item) => diagnostic(
|
|
306
|
+
item.phase,
|
|
307
|
+
item.code,
|
|
308
|
+
item.message,
|
|
309
|
+
freezeRange(start + item.range.start, start + item.range.end),
|
|
310
|
+
item.path
|
|
311
|
+
)
|
|
312
|
+
)
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
function scanGuest(source, limits) {
|
|
316
|
+
const sink = new DiagnosticSink(limits);
|
|
317
|
+
const tokens = [];
|
|
318
|
+
let offset = 0;
|
|
319
|
+
let depth = 0;
|
|
320
|
+
let unmatchedParentheses = false;
|
|
321
|
+
let unsupportedComment = false;
|
|
322
|
+
let unsupportedQuote = false;
|
|
323
|
+
let stop = null;
|
|
324
|
+
while (offset < source.length) {
|
|
325
|
+
if (source[offset] === "}") {
|
|
326
|
+
stop = offset;
|
|
327
|
+
unmatchedParentheses ||= depth !== 0;
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
if (offset >= limits.maxSourceLength || tokens.length >= limits.maxTokens) {
|
|
331
|
+
sink.limit("lex", freezeRange(offset, offset));
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
const opener = ambiguousGuestOpener(source, offset);
|
|
335
|
+
if (opener !== null) {
|
|
336
|
+
sink.add("lex", "KALADA_SYNTAX_UNSUPPORTED_FORM", freezeRange(offset, offset));
|
|
337
|
+
stop = offset;
|
|
338
|
+
unsupportedComment = opener === "comment";
|
|
339
|
+
unsupportedQuote = opener === "quote";
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
const guest = { limited: false };
|
|
343
|
+
const next = scanToken(source, offset, limits, sink, guest);
|
|
344
|
+
tokens.push(next);
|
|
345
|
+
offset = next.range.end;
|
|
346
|
+
if (guest.limited) break;
|
|
347
|
+
depth += parenthesisDelta(next);
|
|
348
|
+
unmatchedParentheses ||= depth < 0;
|
|
349
|
+
if (offset > limits.maxSourceLength)
|
|
350
|
+
sink.limit("lex", freezeRange(limits.maxSourceLength, offset));
|
|
351
|
+
}
|
|
352
|
+
if (stop === null && source.length > limits.maxSourceLength) {
|
|
353
|
+
sink.limit("lex", freezeRange(limits.maxSourceLength, limits.maxSourceLength));
|
|
354
|
+
}
|
|
355
|
+
return finishGuest(tokens, sink, offset, {
|
|
356
|
+
stop,
|
|
357
|
+
unmatchedParentheses,
|
|
358
|
+
unsupportedComment,
|
|
359
|
+
unsupportedQuote
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
function finishGuest(tokens, sink, offset, state) {
|
|
363
|
+
tokens.push(token("eof", "", offset, offset));
|
|
364
|
+
return deepFreeze({
|
|
365
|
+
tokens,
|
|
366
|
+
diagnostics: sink.diagnostics,
|
|
367
|
+
...state
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
function ambiguousGuestOpener(source, offset) {
|
|
371
|
+
if (source.startsWith("//", offset) || source.startsWith("/*", offset)) return "comment";
|
|
372
|
+
if (source[offset] === "'" || source[offset] === "`") return "quote";
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
function parenthesisDelta(found) {
|
|
376
|
+
if (found.kind === "left-parenthesis") return 1;
|
|
377
|
+
if (found.kind === "right-parenthesis") return -1;
|
|
378
|
+
return 0;
|
|
379
|
+
}
|
|
380
|
+
function scanToken(source, start, limits, sink, guest) {
|
|
381
|
+
const character = source[start];
|
|
382
|
+
if (isWhitespace(character)) return scanWhile(source, start, isWhitespace, "whitespace");
|
|
383
|
+
if (isIdentifierStart2(character)) return scanIdentifier(source, start, limits, sink, guest);
|
|
384
|
+
if (isDigit(character)) return scanNumber(source, start, sink);
|
|
385
|
+
if (character === '"') return scanString(source, start, limits, sink, guest);
|
|
386
|
+
return scanSymbol(source, start, sink);
|
|
387
|
+
}
|
|
388
|
+
function scanSymbol(source, start, sink) {
|
|
389
|
+
const character = source[start];
|
|
390
|
+
if (character === "/" && source[start + 1] === "/" || character === "/" && source[start + 1] === "*") {
|
|
391
|
+
return scanComment(source, start, sink);
|
|
392
|
+
}
|
|
393
|
+
if (character === "'" || character === "`") return scanQuotedUnsupported(source, start, sink);
|
|
394
|
+
if (source.startsWith("?.", start)) return token("optional-dot", "?.", start, start + 2);
|
|
395
|
+
const unsupported = matchAt(source, start, UNSUPPORTED_OPERATORS);
|
|
396
|
+
const operator = matchAt(source, start, VALID_OPERATORS);
|
|
397
|
+
if (unsupported && (!operator || unsupported.length > operator.length)) {
|
|
398
|
+
return unsupportedToken(source, start, unsupported.length, sink, "operator");
|
|
399
|
+
}
|
|
400
|
+
if (operator) return token("operator", operator, start, start + operator.length);
|
|
401
|
+
if (unsupported) return unsupportedToken(source, start, unsupported.length, sink, "operator");
|
|
402
|
+
const navigation = scanNavigation(source, start);
|
|
403
|
+
if (navigation) return navigation;
|
|
404
|
+
if ("[]{};,".includes(character)) return unsupportedToken(source, start, 1, sink, "form");
|
|
405
|
+
sink.add("lex", "KALADA_SYNTAX_UNEXPECTED_TOKEN", freezeRange(start, start + 1));
|
|
406
|
+
return token("invalid", character, start, start + 1);
|
|
407
|
+
}
|
|
408
|
+
function scanIdentifier(source, start, limits, sink, guest) {
|
|
409
|
+
const found = scanWhile(source, start, isIdentifierContinue, "identifier");
|
|
410
|
+
if (found.text.length > limits.maxIdentifierLength) {
|
|
411
|
+
return limitedToken(source, start, found.range.end, sink, guest);
|
|
412
|
+
}
|
|
413
|
+
const keyword = ["true", "false", "null", "in", "xor"].find(
|
|
414
|
+
(item) => item === found.text
|
|
415
|
+
);
|
|
416
|
+
return keyword ? token(keyword, found.text, found.range.start, found.range.end) : found;
|
|
417
|
+
}
|
|
418
|
+
function scanComment(source, start, sink) {
|
|
419
|
+
const line = source.startsWith("//", start);
|
|
420
|
+
let end = start + 2;
|
|
421
|
+
if (line) while (end < source.length && source[end] !== "\n" && source[end] !== "\r") end += 1;
|
|
422
|
+
else {
|
|
423
|
+
const close = source.indexOf("*/", end);
|
|
424
|
+
end = close < 0 ? source.length : close + 2;
|
|
425
|
+
}
|
|
426
|
+
return unsupportedToken(source, start, end - start, sink, "form");
|
|
427
|
+
}
|
|
428
|
+
function scanQuotedUnsupported(source, start, sink) {
|
|
429
|
+
const quote = source[start];
|
|
430
|
+
let end = start + 1;
|
|
431
|
+
while (end < source.length && source[end] !== quote && source[end] !== "\n" && source[end] !== "\r")
|
|
432
|
+
end += 1;
|
|
433
|
+
if (source[end] === quote) end += 1;
|
|
434
|
+
return unsupportedToken(source, start, end - start, sink, "form");
|
|
435
|
+
}
|
|
436
|
+
function scanNavigation(source, start) {
|
|
437
|
+
if (source.startsWith("?.", start)) return token("optional-dot", "?.", start, start + 2);
|
|
438
|
+
const kinds = {
|
|
439
|
+
"(": "left-parenthesis",
|
|
440
|
+
")": "right-parenthesis",
|
|
441
|
+
".": "dot",
|
|
442
|
+
"?": "question",
|
|
443
|
+
":": "colon"
|
|
444
|
+
};
|
|
445
|
+
const text = source[start];
|
|
446
|
+
return kinds[text] ? token(kinds[text], text, start, start + 1) : null;
|
|
447
|
+
}
|
|
448
|
+
function unsupportedToken(source, start, length, sink, type) {
|
|
449
|
+
const end = start + length;
|
|
450
|
+
sink.add(
|
|
451
|
+
"lex",
|
|
452
|
+
type === "form" ? "KALADA_SYNTAX_UNSUPPORTED_FORM" : "KALADA_SYNTAX_UNSUPPORTED_OPERATOR",
|
|
453
|
+
freezeRange(start, end)
|
|
454
|
+
);
|
|
455
|
+
return token("unsupported", source.slice(start, end), start, end);
|
|
456
|
+
}
|
|
457
|
+
function scanWhile(source, start, predicate, kind) {
|
|
458
|
+
let end = start + 1;
|
|
459
|
+
while (end < source.length && predicate(source[end])) end += 1;
|
|
460
|
+
return token(kind, source.slice(start, end), start, end);
|
|
461
|
+
}
|
|
462
|
+
function matchAt(source, start, values) {
|
|
463
|
+
return [...values].sort((left, right) => right.length - left.length).find((value) => source.startsWith(value, start)) ?? null;
|
|
464
|
+
}
|
|
465
|
+
function result(tokens, sink) {
|
|
466
|
+
return deepFreeze({ tokens, diagnostics: sink.diagnostics });
|
|
467
|
+
}
|
|
468
|
+
function isWhitespace(value) {
|
|
469
|
+
return value === " " || value === " " || value === "\r" || value === "\n";
|
|
470
|
+
}
|
|
471
|
+
function isIdentifierStart2(value) {
|
|
472
|
+
return value?.length === 1 && /[A-Za-z_]/u.test(value);
|
|
473
|
+
}
|
|
474
|
+
function isIdentifierContinue(value) {
|
|
475
|
+
return value?.length === 1 && /[A-Za-z0-9_]/u.test(value);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// src/operators.ts
|
|
479
|
+
var tiers = Object.freeze({
|
|
480
|
+
multiplicative: Object.freeze(["*", "/", "%"]),
|
|
481
|
+
additive: Object.freeze(["+", "-"]),
|
|
482
|
+
relational: Object.freeze(["<", "<=", ">", ">=", "in"]),
|
|
483
|
+
equality: Object.freeze(["==", "!="]),
|
|
484
|
+
logicalAnd: Object.freeze(["&&"]),
|
|
485
|
+
logicalXor: Object.freeze(["xor"]),
|
|
486
|
+
logicalOr: Object.freeze(["||"]),
|
|
487
|
+
coalesce: Object.freeze(["??"])
|
|
488
|
+
});
|
|
489
|
+
var BINARY_OPERATOR_TIERS = Object.freeze({
|
|
490
|
+
multiplicative: new Set(tiers.multiplicative),
|
|
491
|
+
additive: new Set(tiers.additive),
|
|
492
|
+
relational: new Set(tiers.relational),
|
|
493
|
+
equality: new Set(tiers.equality),
|
|
494
|
+
logicalAnd: new Set(tiers.logicalAnd),
|
|
495
|
+
logicalXor: new Set(tiers.logicalXor),
|
|
496
|
+
logicalOr: new Set(tiers.logicalOr),
|
|
497
|
+
coalesce: new Set(tiers.coalesce)
|
|
498
|
+
});
|
|
499
|
+
var BINARY_OPERATORS = Object.freeze([
|
|
500
|
+
...tiers.multiplicative,
|
|
501
|
+
...tiers.additive,
|
|
502
|
+
...tiers.relational,
|
|
503
|
+
...tiers.equality,
|
|
504
|
+
...tiers.logicalAnd,
|
|
505
|
+
...tiers.logicalXor,
|
|
506
|
+
...tiers.logicalOr,
|
|
507
|
+
...tiers.coalesce
|
|
508
|
+
]);
|
|
509
|
+
function isAdditiveOperator(operator) {
|
|
510
|
+
return BINARY_OPERATOR_TIERS.additive.has(operator);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// src/parser-budget.ts
|
|
514
|
+
var ParserLimit = class extends Error {
|
|
515
|
+
node;
|
|
516
|
+
constructor(node) {
|
|
517
|
+
super("KALADA_SYNTAX_LIMIT_EXCEEDED");
|
|
518
|
+
this.node = node;
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
var ParserBudget = class {
|
|
522
|
+
nodes = 0;
|
|
523
|
+
recovered = 0;
|
|
524
|
+
pendingDepth = 0;
|
|
525
|
+
stopped = false;
|
|
526
|
+
limits;
|
|
527
|
+
sink;
|
|
528
|
+
sourceEnd;
|
|
529
|
+
constructor(limits, sink, sourceEnd) {
|
|
530
|
+
this.limits = limits;
|
|
531
|
+
this.sink = sink;
|
|
532
|
+
this.sourceEnd = sourceEnd;
|
|
533
|
+
}
|
|
534
|
+
create(node) {
|
|
535
|
+
if (this.nodes >= this.limits.maxCstNodes) this.stop(node.range.start);
|
|
536
|
+
this.nodes += 1;
|
|
537
|
+
return Object.freeze(node);
|
|
538
|
+
}
|
|
539
|
+
finishDepth(depth, start) {
|
|
540
|
+
if (this.pendingDepth + depth > this.limits.maxCstDepth) this.stop(start);
|
|
541
|
+
}
|
|
542
|
+
recover(range) {
|
|
543
|
+
if (this.recovered >= this.limits.maxRecoveryTokens) this.stop(range.start);
|
|
544
|
+
this.recovered += 1;
|
|
545
|
+
}
|
|
546
|
+
exhaust(start) {
|
|
547
|
+
return this.stop(start);
|
|
548
|
+
}
|
|
549
|
+
withWrapper(start, parse) {
|
|
550
|
+
if (this.pendingDepth + 1 >= this.limits.maxCstDepth) this.stop(start);
|
|
551
|
+
this.pendingDepth += 1;
|
|
552
|
+
try {
|
|
553
|
+
return parse();
|
|
554
|
+
} finally {
|
|
555
|
+
this.pendingDepth -= 1;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
withConditional(conditionDepth, start, parse) {
|
|
559
|
+
if (this.pendingDepth + conditionDepth + 1 > this.limits.maxCstDepth) this.stop(start);
|
|
560
|
+
return this.withWrapper(start, parse);
|
|
561
|
+
}
|
|
562
|
+
withinWrapper(start, parse) {
|
|
563
|
+
if (this.pendingDepth >= this.limits.maxCstDepth) this.stop(start);
|
|
564
|
+
return parse();
|
|
565
|
+
}
|
|
566
|
+
stop(start) {
|
|
567
|
+
const range = freezeRange(start, this.sourceEnd);
|
|
568
|
+
if (!this.stopped) {
|
|
569
|
+
this.stopped = true;
|
|
570
|
+
this.sink.limit("parse", range);
|
|
571
|
+
}
|
|
572
|
+
throw new ParserLimit(Object.freeze({ kind: "error", token: null, range }));
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
// src/parser-types.ts
|
|
577
|
+
function maxParsedDepth(...values) {
|
|
578
|
+
return Math.max(...values.map((value) => value.depth));
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// src/parser.ts
|
|
582
|
+
var OPERATORS = BINARY_OPERATOR_TIERS;
|
|
583
|
+
var Parser = class {
|
|
584
|
+
index = 0;
|
|
585
|
+
sink;
|
|
586
|
+
tokens;
|
|
587
|
+
budget;
|
|
588
|
+
constructor(tokens, limits, diagnostics) {
|
|
589
|
+
this.tokens = tokens;
|
|
590
|
+
this.sink = new DiagnosticSink(limits, diagnostics);
|
|
591
|
+
this.budget = new ParserBudget(limits, this.sink, tokens[tokens.length - 1]?.range.end ?? 0);
|
|
592
|
+
}
|
|
593
|
+
parse() {
|
|
594
|
+
try {
|
|
595
|
+
const expression = this.parseConditional().node;
|
|
596
|
+
while (!this.at("eof")) {
|
|
597
|
+
const current = this.current();
|
|
598
|
+
this.budget.recover(current.range);
|
|
599
|
+
const alreadyDiagnosed = current.kind === "unsupported" || current.kind === "invalid";
|
|
600
|
+
if (current.kind === "left-parenthesis")
|
|
601
|
+
this.sink.add("parse", "KALADA_SYNTAX_UNSUPPORTED_FORM", current.range);
|
|
602
|
+
if (current.kind !== "left-parenthesis" && !alreadyDiagnosed) {
|
|
603
|
+
this.sink.add("parse", "KALADA_SYNTAX_UNEXPECTED_TOKEN", current.range);
|
|
604
|
+
}
|
|
605
|
+
this.consume();
|
|
606
|
+
}
|
|
607
|
+
return expression;
|
|
608
|
+
} catch (error) {
|
|
609
|
+
if (error instanceof ParserLimit) {
|
|
610
|
+
this.index = this.tokens.length - 1;
|
|
611
|
+
return error.node;
|
|
612
|
+
}
|
|
613
|
+
throw error;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
parseConditional() {
|
|
617
|
+
const condition = this.parseCoalesce();
|
|
618
|
+
if (!this.at("question")) return condition;
|
|
619
|
+
const branches = this.parseConditionalBranches(condition.depth);
|
|
620
|
+
const node = this.create({
|
|
621
|
+
kind: "conditional",
|
|
622
|
+
condition: condition.node,
|
|
623
|
+
questionToken: branches.questionToken,
|
|
624
|
+
// biome-ignore lint/suspicious/noThenProperty: The CST field mirrors ternary syntax.
|
|
625
|
+
then: branches.then.node,
|
|
626
|
+
colonToken: branches.colonToken,
|
|
627
|
+
else: branches.otherwise.node,
|
|
628
|
+
range: freezeRange(condition.node.range.start, branches.otherwise.node.range.end)
|
|
629
|
+
});
|
|
630
|
+
return this.parsed(
|
|
631
|
+
node,
|
|
632
|
+
false,
|
|
633
|
+
false,
|
|
634
|
+
maxParsedDepth(condition, branches.then, branches.otherwise) + 1
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
parseConditionalBranches(conditionDepth) {
|
|
638
|
+
return this.budget.withConditional(conditionDepth, this.current().range.start, () => {
|
|
639
|
+
const questionToken = this.consume();
|
|
640
|
+
const then = this.budget.withinWrapper(
|
|
641
|
+
this.current().range.start,
|
|
642
|
+
() => this.parseConditional()
|
|
643
|
+
);
|
|
644
|
+
let colonToken = null;
|
|
645
|
+
if (this.at("colon")) colonToken = this.consume();
|
|
646
|
+
else this.sink.add("parse", "KALADA_SYNTAX_EXPECTED_COLON", this.current().range);
|
|
647
|
+
const otherwise = this.budget.withinWrapper(
|
|
648
|
+
this.current().range.start,
|
|
649
|
+
() => this.parseConditional()
|
|
650
|
+
);
|
|
651
|
+
return { questionToken, then, colonToken, otherwise };
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
parseCoalesce() {
|
|
655
|
+
let left = this.parseLogicalOr();
|
|
656
|
+
while (this.currentOperator(OPERATORS.coalesce)) {
|
|
657
|
+
const operatorToken = this.consume();
|
|
658
|
+
const right = this.parseLogicalOr();
|
|
659
|
+
if (left.logical || right.logical) {
|
|
660
|
+
this.sink.add(
|
|
661
|
+
"parse",
|
|
662
|
+
"KALADA_SYNTAX_COALESCE_LOGICAL_MIX",
|
|
663
|
+
this.token(operatorToken).range
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
left = this.binary(left, right, "??", operatorToken, true, left.logical || right.logical);
|
|
667
|
+
}
|
|
668
|
+
return left;
|
|
669
|
+
}
|
|
670
|
+
parseLogicalOr() {
|
|
671
|
+
return this.parseBinaryTier(() => this.parseLogicalXor(), OPERATORS.logicalOr, true);
|
|
672
|
+
}
|
|
673
|
+
parseLogicalXor() {
|
|
674
|
+
return this.parseBinaryTier(() => this.parseLogicalAnd(), OPERATORS.logicalXor, false);
|
|
675
|
+
}
|
|
676
|
+
parseLogicalAnd() {
|
|
677
|
+
return this.parseBinaryTier(() => this.parseEquality(), OPERATORS.logicalAnd, true);
|
|
678
|
+
}
|
|
679
|
+
parseEquality() {
|
|
680
|
+
return this.parseBinaryTier(() => this.parseRelational(), OPERATORS.equality, false);
|
|
681
|
+
}
|
|
682
|
+
parseRelational() {
|
|
683
|
+
let left = this.parseAdditive();
|
|
684
|
+
let seen = false;
|
|
685
|
+
while (this.currentOperator(OPERATORS.relational)) {
|
|
686
|
+
const operatorToken = this.consume();
|
|
687
|
+
if (seen) {
|
|
688
|
+
this.sink.add("parse", "KALADA_SYNTAX_RELATIONAL_CHAIN", this.token(operatorToken).range);
|
|
689
|
+
}
|
|
690
|
+
seen = true;
|
|
691
|
+
const operator = this.token(operatorToken).text;
|
|
692
|
+
left = this.binary(
|
|
693
|
+
left,
|
|
694
|
+
this.parseAdditive(),
|
|
695
|
+
operator,
|
|
696
|
+
operatorToken,
|
|
697
|
+
left.coalesce,
|
|
698
|
+
left.logical
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
return left;
|
|
702
|
+
}
|
|
703
|
+
parseAdditive() {
|
|
704
|
+
return this.parseBinaryTier(() => this.parseMultiplicative(), OPERATORS.additive, false);
|
|
705
|
+
}
|
|
706
|
+
parseMultiplicative() {
|
|
707
|
+
return this.parseBinaryTier(() => this.parseUnary(), OPERATORS.multiplicative, false);
|
|
708
|
+
}
|
|
709
|
+
parseBinaryTier(next, operators, logical) {
|
|
710
|
+
let left = next();
|
|
711
|
+
while (this.currentOperator(operators)) {
|
|
712
|
+
const operatorToken = this.consume();
|
|
713
|
+
const operator = this.token(operatorToken).text;
|
|
714
|
+
const right = next();
|
|
715
|
+
if (logical && (left.coalesce || right.coalesce)) {
|
|
716
|
+
this.sink.add(
|
|
717
|
+
"parse",
|
|
718
|
+
"KALADA_SYNTAX_COALESCE_LOGICAL_MIX",
|
|
719
|
+
this.token(operatorToken).range
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
left = this.binary(
|
|
723
|
+
left,
|
|
724
|
+
right,
|
|
725
|
+
operator,
|
|
726
|
+
operatorToken,
|
|
727
|
+
left.coalesce || right.coalesce,
|
|
728
|
+
logical || left.logical || right.logical
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
return left;
|
|
732
|
+
}
|
|
733
|
+
parseUnary() {
|
|
734
|
+
const token2 = this.current();
|
|
735
|
+
if (token2.kind !== "operator" || !["!", "+", "-"].includes(token2.text))
|
|
736
|
+
return this.parsePostfix();
|
|
737
|
+
const operatorToken = this.consume();
|
|
738
|
+
const operand = this.budget.withWrapper(this.current().range.start, () => this.parseUnary());
|
|
739
|
+
const node = this.create({
|
|
740
|
+
kind: "unary",
|
|
741
|
+
operator: token2.text,
|
|
742
|
+
operatorToken,
|
|
743
|
+
operand: operand.node,
|
|
744
|
+
range: freezeRange(token2.range.start, operand.node.range.end)
|
|
745
|
+
});
|
|
746
|
+
return this.parsed(node, operand.coalesce, operand.logical, operand.depth + 1);
|
|
747
|
+
}
|
|
748
|
+
parsePostfix() {
|
|
749
|
+
let target2 = this.parsePrimary();
|
|
750
|
+
while (this.at("dot") || this.at("optional-dot")) {
|
|
751
|
+
const operatorToken = this.consume();
|
|
752
|
+
const operator = this.token(operatorToken);
|
|
753
|
+
const fieldToken = this.fieldToken();
|
|
754
|
+
const field = fieldToken === null ? null : this.token(fieldToken).text;
|
|
755
|
+
const end = fieldToken === null ? operator.range.end : this.token(fieldToken).range.end;
|
|
756
|
+
const node = this.create({
|
|
757
|
+
kind: "field-access",
|
|
758
|
+
target: target2.node,
|
|
759
|
+
optional: operator.kind === "optional-dot",
|
|
760
|
+
operatorToken,
|
|
761
|
+
fieldToken,
|
|
762
|
+
field,
|
|
763
|
+
range: freezeRange(target2.node.range.start, end)
|
|
764
|
+
});
|
|
765
|
+
target2 = this.parsed(node, target2.coalesce, target2.logical, target2.depth + 1);
|
|
766
|
+
}
|
|
767
|
+
return target2;
|
|
768
|
+
}
|
|
769
|
+
parsePrimary() {
|
|
770
|
+
const current = this.current();
|
|
771
|
+
if (current.kind === "identifier") return this.reference();
|
|
772
|
+
if (["number", "string", "true", "false", "null"].includes(current.kind)) return this.literal();
|
|
773
|
+
if (current.kind === "left-parenthesis") return this.group();
|
|
774
|
+
if (current.kind === "invalid" || current.kind === "unsupported") {
|
|
775
|
+
return this.parsed(this.error(this.recoverAdvance()), false, false, 1);
|
|
776
|
+
}
|
|
777
|
+
this.sink.add("parse", "KALADA_SYNTAX_EXPECTED_EXPRESSION", current.range);
|
|
778
|
+
const synchronizer = current.kind === "eof" || current.kind === "right-parenthesis" || current.kind === "colon" || this.isBinary(current);
|
|
779
|
+
const token2 = synchronizer ? null : this.recoverAdvance();
|
|
780
|
+
return this.parsed(this.error(token2), false, false, 1);
|
|
781
|
+
}
|
|
782
|
+
group() {
|
|
783
|
+
const openToken = this.consume();
|
|
784
|
+
const expression = this.budget.withWrapper(
|
|
785
|
+
this.current().range.start,
|
|
786
|
+
() => this.parseConditional()
|
|
787
|
+
);
|
|
788
|
+
let closeToken = null;
|
|
789
|
+
if (this.at("right-parenthesis")) closeToken = this.consume();
|
|
790
|
+
else this.sink.add("parse", "KALADA_SYNTAX_EXPECTED_RIGHT_PARENTHESIS", this.current().range);
|
|
791
|
+
const end = closeToken === null ? expression.node.range.end : this.token(closeToken).range.end;
|
|
792
|
+
const node = this.create({
|
|
793
|
+
kind: "group",
|
|
794
|
+
openToken,
|
|
795
|
+
expression: expression.node,
|
|
796
|
+
closeToken,
|
|
797
|
+
range: freezeRange(this.token(openToken).range.start, end)
|
|
798
|
+
});
|
|
799
|
+
return this.parsed(node, false, false, expression.depth + 1);
|
|
800
|
+
}
|
|
801
|
+
reference() {
|
|
802
|
+
const index = this.consume();
|
|
803
|
+
const found = this.token(index);
|
|
804
|
+
const node = this.create({
|
|
805
|
+
kind: "reference",
|
|
806
|
+
token: index,
|
|
807
|
+
name: found.text,
|
|
808
|
+
range: found.range
|
|
809
|
+
});
|
|
810
|
+
return this.parsed(node, false, false, 1);
|
|
811
|
+
}
|
|
812
|
+
literal() {
|
|
813
|
+
const index = this.consume();
|
|
814
|
+
const found = this.token(index);
|
|
815
|
+
const value = found.kind === "number" ? Number(found.text) : found.kind === "string" ? JSON.parse(found.text) : found.kind === "true" ? true : found.kind === "false" ? false : null;
|
|
816
|
+
const literalKind = found.kind === "true" || found.kind === "false" ? "boolean" : found.kind;
|
|
817
|
+
const node = this.create({
|
|
818
|
+
kind: "literal",
|
|
819
|
+
literalKind,
|
|
820
|
+
token: index,
|
|
821
|
+
value,
|
|
822
|
+
range: found.range
|
|
823
|
+
});
|
|
824
|
+
return this.parsed(node, false, false, 1);
|
|
825
|
+
}
|
|
826
|
+
fieldToken() {
|
|
827
|
+
const current = this.current();
|
|
828
|
+
if (["identifier", "true", "false", "null", "in", "xor"].includes(current.kind))
|
|
829
|
+
return this.consume();
|
|
830
|
+
this.sink.add("parse", "KALADA_SYNTAX_EXPECTED_FIELD", current.range);
|
|
831
|
+
return null;
|
|
832
|
+
}
|
|
833
|
+
binary(left, right, operator, operatorToken, coalesce2, logical) {
|
|
834
|
+
const node = this.create({
|
|
835
|
+
kind: "binary",
|
|
836
|
+
operator,
|
|
837
|
+
operatorToken,
|
|
838
|
+
left: left.node,
|
|
839
|
+
right: right.node,
|
|
840
|
+
range: freezeRange(left.node.range.start, right.node.range.end)
|
|
841
|
+
});
|
|
842
|
+
return this.parsed(
|
|
843
|
+
node,
|
|
844
|
+
coalesce2 || operator === "??",
|
|
845
|
+
logical,
|
|
846
|
+
Math.max(left.depth, right.depth) + 1
|
|
847
|
+
);
|
|
848
|
+
}
|
|
849
|
+
parsed(node, coalesce2, logical, depth) {
|
|
850
|
+
this.budget.finishDepth(depth, node.range.start);
|
|
851
|
+
return { node, coalesce: coalesce2, logical, depth };
|
|
852
|
+
}
|
|
853
|
+
create(node) {
|
|
854
|
+
return this.budget.create(node);
|
|
855
|
+
}
|
|
856
|
+
error(index) {
|
|
857
|
+
const range = index === null ? this.current().range : this.token(index).range;
|
|
858
|
+
return this.create({ kind: "error", token: index, range });
|
|
859
|
+
}
|
|
860
|
+
recoverAdvance() {
|
|
861
|
+
if (this.at("eof")) return null;
|
|
862
|
+
this.budget.recover(this.current().range);
|
|
863
|
+
const found = this.consume();
|
|
864
|
+
return found;
|
|
865
|
+
}
|
|
866
|
+
currentOperator(operators) {
|
|
867
|
+
const current = this.current();
|
|
868
|
+
return (current.kind === "operator" || current.kind === "in" || current.kind === "xor") && operators.has(current.text);
|
|
869
|
+
}
|
|
870
|
+
isBinary(value) {
|
|
871
|
+
return value.kind === "in" || value.kind === "xor" || value.kind === "operator" && !["!"].includes(value.text);
|
|
872
|
+
}
|
|
873
|
+
at(kind) {
|
|
874
|
+
return this.current().kind === kind;
|
|
875
|
+
}
|
|
876
|
+
current() {
|
|
877
|
+
this.skipTrivia();
|
|
878
|
+
const found = this.token(this.index);
|
|
879
|
+
if (found.kind === "invalid" && this.sink.hasLexicalLimitAt(found.range.start))
|
|
880
|
+
this.budget.exhaust(found.range.start);
|
|
881
|
+
return found;
|
|
882
|
+
}
|
|
883
|
+
token(index) {
|
|
884
|
+
const found = this.tokens[index] ?? this.tokens[this.tokens.length - 1];
|
|
885
|
+
if (found === void 0) throw new Error("Parser token invariant failed.");
|
|
886
|
+
return found;
|
|
887
|
+
}
|
|
888
|
+
consume() {
|
|
889
|
+
this.skipTrivia();
|
|
890
|
+
const found = this.index;
|
|
891
|
+
if (this.tokens[found]?.kind !== "eof") this.index += 1;
|
|
892
|
+
return found;
|
|
893
|
+
}
|
|
894
|
+
skipTrivia() {
|
|
895
|
+
while (this.tokens[this.index]?.kind === "whitespace") this.index += 1;
|
|
896
|
+
}
|
|
897
|
+
};
|
|
898
|
+
|
|
899
|
+
// src/parse.ts
|
|
900
|
+
var parseLimits = /* @__PURE__ */ new WeakMap();
|
|
901
|
+
function parseKaladaV1Expression(source, options) {
|
|
902
|
+
if (typeof source !== "string") return invalidParseResult();
|
|
903
|
+
let limits;
|
|
904
|
+
try {
|
|
905
|
+
limits = resolveSyntaxLimits(options);
|
|
906
|
+
} catch {
|
|
907
|
+
limits = null;
|
|
908
|
+
}
|
|
909
|
+
if (limits === null) return invalidParseResult(source);
|
|
910
|
+
const scanned = lex(source, limits);
|
|
911
|
+
return parseTokens(source, scanned.tokens, limits, scanned.diagnostics);
|
|
912
|
+
}
|
|
913
|
+
function parseTokens(source, tokens, limits, diagnostics) {
|
|
914
|
+
const parser = new Parser(tokens, limits, diagnostics);
|
|
915
|
+
const document = {
|
|
916
|
+
source,
|
|
917
|
+
tokens,
|
|
918
|
+
expression: parser.parse()
|
|
919
|
+
};
|
|
920
|
+
const result2 = deepFreeze({ document, diagnostics: parser.sink.diagnostics });
|
|
921
|
+
parseLimits.set(result2, limits);
|
|
922
|
+
return result2;
|
|
923
|
+
}
|
|
924
|
+
function invalidParseResult(source = "") {
|
|
925
|
+
const range = freezeRange(0, source.length);
|
|
926
|
+
const eof = Object.freeze({
|
|
927
|
+
kind: "eof",
|
|
928
|
+
text: "",
|
|
929
|
+
range: freezeRange(source.length, source.length)
|
|
930
|
+
});
|
|
931
|
+
const result2 = deepFreeze({
|
|
932
|
+
document: {
|
|
933
|
+
source,
|
|
934
|
+
tokens: [eof],
|
|
935
|
+
expression: { kind: "error", token: null, range }
|
|
936
|
+
},
|
|
937
|
+
diagnostics: [
|
|
938
|
+
diagnostic(
|
|
939
|
+
"parse",
|
|
940
|
+
"KALADA_SYNTAX_INVALID_INPUT",
|
|
941
|
+
"Kalada syntax input is invalid.",
|
|
942
|
+
range,
|
|
943
|
+
[]
|
|
944
|
+
)
|
|
945
|
+
]
|
|
946
|
+
});
|
|
947
|
+
parseLimits.set(result2, DEFAULT_KALADA_SYNTAX_LIMITS);
|
|
948
|
+
return result2;
|
|
949
|
+
}
|
|
950
|
+
function syntaxLimitsFor(parsed) {
|
|
951
|
+
return parseLimits.get(parsed) ?? null;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// src/static-types.ts
|
|
955
|
+
function projectStaticType(type) {
|
|
956
|
+
if (type === "dynamic") return type;
|
|
957
|
+
if (!("shape" in type)) return type;
|
|
958
|
+
const value = projectStaticType(type.value);
|
|
959
|
+
return value === "dynamic" ? "dynamic" : deepFreeze({ kind: "option-type", value });
|
|
960
|
+
}
|
|
961
|
+
var primitive = (name) => Object.freeze({ kind: "primitive-type", name });
|
|
962
|
+
function literalType(value) {
|
|
963
|
+
if (value === null) return primitive("null");
|
|
964
|
+
return primitive(typeof value);
|
|
965
|
+
}
|
|
966
|
+
function readStaticType(input, maxDepth) {
|
|
967
|
+
if (input === "dynamic") return "dynamic";
|
|
968
|
+
return readType(input, 1, maxDepth);
|
|
969
|
+
}
|
|
970
|
+
function readType(input, depth, maximum) {
|
|
971
|
+
if (depth > maximum) return null;
|
|
972
|
+
const record2 = ownRecord(input);
|
|
973
|
+
if (record2 === null || typeof record2.kind !== "string") return null;
|
|
974
|
+
if (record2.kind === "primitive-type") return readExactPrimitive(record2);
|
|
975
|
+
if (record2.kind === "array-type" || record2.kind === "option-type")
|
|
976
|
+
return readCollection(record2, depth, maximum);
|
|
977
|
+
if (record2.kind === "result-type") return readExactResult(record2, depth, maximum);
|
|
978
|
+
if (record2.kind === "function-type") return readExactFunction(record2, depth, maximum);
|
|
979
|
+
return null;
|
|
980
|
+
}
|
|
981
|
+
function readExactPrimitive(record2) {
|
|
982
|
+
return exactKeys(record2, ["kind", "name"]) ? readPrimitive(record2) : null;
|
|
983
|
+
}
|
|
984
|
+
function readCollection(record2, depth, maximum) {
|
|
985
|
+
const array = record2.kind === "array-type";
|
|
986
|
+
const field = array ? "element" : "value";
|
|
987
|
+
if (!exactKeys(record2, ["kind", field])) return null;
|
|
988
|
+
const value = readType(record2[field], depth + 1, maximum);
|
|
989
|
+
return value ? deepFreeze({ kind: record2.kind, [field]: value }) : null;
|
|
990
|
+
}
|
|
991
|
+
function readExactResult(record2, depth, maximum) {
|
|
992
|
+
return exactKeys(record2, ["kind", "ok", "error"]) ? readResult(record2, depth, maximum) : null;
|
|
993
|
+
}
|
|
994
|
+
function readExactFunction(record2, depth, maximum) {
|
|
995
|
+
return exactKeys(record2, ["kind", "parameters", "returns"]) ? readFunction(record2, depth, maximum) : null;
|
|
996
|
+
}
|
|
997
|
+
function readPrimitive(record2) {
|
|
998
|
+
const names = ["null", "boolean", "number", "string", "json", "Instant", "Duration"];
|
|
999
|
+
if (typeof record2.name !== "string" || !names.includes(record2.name)) return null;
|
|
1000
|
+
return primitive(record2.name);
|
|
1001
|
+
}
|
|
1002
|
+
function readResult(record2, depth, maximum) {
|
|
1003
|
+
const ok = readType(record2.ok, depth + 1, maximum);
|
|
1004
|
+
const error = readType(record2.error, depth + 1, maximum);
|
|
1005
|
+
return ok && error ? deepFreeze({ kind: "result-type", ok, error }) : null;
|
|
1006
|
+
}
|
|
1007
|
+
function readFunction(record2, depth, maximum) {
|
|
1008
|
+
if (!Array.isArray(record2.parameters)) return null;
|
|
1009
|
+
const parameters = [];
|
|
1010
|
+
for (let index = 0; index < record2.parameters.length; index++) {
|
|
1011
|
+
const found = readType(record2.parameters[index], depth + 1, maximum);
|
|
1012
|
+
if (found === null) return null;
|
|
1013
|
+
parameters.push(found);
|
|
1014
|
+
}
|
|
1015
|
+
const returns = readType(record2.returns, depth + 1, maximum);
|
|
1016
|
+
return returns ? deepFreeze({ kind: "function-type", parameters, returns }) : null;
|
|
1017
|
+
}
|
|
1018
|
+
function ownRecord(input) {
|
|
1019
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) return null;
|
|
1020
|
+
const output = /* @__PURE__ */ Object.create(null);
|
|
1021
|
+
try {
|
|
1022
|
+
for (const key of Reflect.ownKeys(input)) {
|
|
1023
|
+
if (typeof key !== "string") return null;
|
|
1024
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, key);
|
|
1025
|
+
if (!descriptor || !("value" in descriptor)) return null;
|
|
1026
|
+
output[key] = descriptor.value;
|
|
1027
|
+
}
|
|
1028
|
+
} catch {
|
|
1029
|
+
return null;
|
|
1030
|
+
}
|
|
1031
|
+
return output;
|
|
1032
|
+
}
|
|
1033
|
+
function exactKeys(record2, expected) {
|
|
1034
|
+
const keys = Object.keys(record2);
|
|
1035
|
+
return keys.length === expected.length && expected.every((key) => keys.includes(key));
|
|
1036
|
+
}
|
|
1037
|
+
function typeIs(type, name) {
|
|
1038
|
+
return type !== "dynamic" && !("shape" in type) && type.kind === "primitive-type" && type.name === name;
|
|
1039
|
+
}
|
|
1040
|
+
function isCallable(type) {
|
|
1041
|
+
return type !== "dynamic" && !("shape" in type) && type.kind === "function-type";
|
|
1042
|
+
}
|
|
1043
|
+
function isOption(type) {
|
|
1044
|
+
return type !== "dynamic" && ("shape" in type && type.shape === "option" || !("shape" in type) && type.kind === "option-type");
|
|
1045
|
+
}
|
|
1046
|
+
function equalTypes(left, right) {
|
|
1047
|
+
return left === right || left !== "dynamic" && right !== "dynamic" && JSON.stringify(left) === JSON.stringify(right);
|
|
1048
|
+
}
|
|
1049
|
+
function joinTypes(left, right) {
|
|
1050
|
+
if (equalTypes(left, right)) return left;
|
|
1051
|
+
if (left === "dynamic" || right === "dynamic") return "dynamic";
|
|
1052
|
+
if (!("shape" in left) && !("shape" in right) && isJsonCompatible(left) && isJsonCompatible(right))
|
|
1053
|
+
return primitive("json");
|
|
1054
|
+
return null;
|
|
1055
|
+
}
|
|
1056
|
+
function isJsonCompatible(type) {
|
|
1057
|
+
if (type.kind === "array-type") return isJsonCompatible(type.element);
|
|
1058
|
+
return type.kind === "primitive-type" && ["null", "boolean", "number", "string", "json"].includes(type.name);
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
// src/direct-location.ts
|
|
1062
|
+
function failure(code, range) {
|
|
1063
|
+
return deepFreeze({
|
|
1064
|
+
ok: false,
|
|
1065
|
+
diagnostics: [diagnostic("lower", code, KALADA_SYNTAX_DIAGNOSTIC_MESSAGES[code], range, [])]
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
function safeKey(value) {
|
|
1069
|
+
return typeof value === "string" && value.length > 0 && !["__proto__", "prototype", "constructor"].includes(value);
|
|
1070
|
+
}
|
|
1071
|
+
function record(input, keys, optional = []) {
|
|
1072
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) return false;
|
|
1073
|
+
try {
|
|
1074
|
+
const found = Reflect.ownKeys(input);
|
|
1075
|
+
return found.every(
|
|
1076
|
+
(key) => typeof key === "string" && (keys.includes(key) || optional.includes(key))
|
|
1077
|
+
) && keys.every((key) => ownDataProperty(input, key).present && ownDataProperty(input, key).ok) && found.every((key) => ownDataProperty(input, key).ok);
|
|
1078
|
+
} catch {
|
|
1079
|
+
return false;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
function target(input, max) {
|
|
1083
|
+
if (!record(input, ["namespace", "segments"], ["scope"])) return null;
|
|
1084
|
+
const namespace = ownDataProperty(input, "namespace").value;
|
|
1085
|
+
const scope = ownDataProperty(input, "scope");
|
|
1086
|
+
const segments = ownDataProperty(input, "segments").value;
|
|
1087
|
+
if (namespace !== "data" || scope.present && !safeKey(scope.value)) return null;
|
|
1088
|
+
const copy = copySegments(segments, max, scope.present);
|
|
1089
|
+
if (copy === null) return null;
|
|
1090
|
+
return scope.present ? { namespace: "data", scope: scope.value, segments: copy } : { namespace: "data", segments: copy };
|
|
1091
|
+
}
|
|
1092
|
+
function copySegments(segments, max, scoped) {
|
|
1093
|
+
if (!Array.isArray(segments)) return null;
|
|
1094
|
+
const copy = [];
|
|
1095
|
+
try {
|
|
1096
|
+
if (Object.getPrototypeOf(segments) !== Array.prototype) return null;
|
|
1097
|
+
const keys = Reflect.ownKeys(segments);
|
|
1098
|
+
const length = arrayLength(segments, keys);
|
|
1099
|
+
if (length > max || !scoped && length === 0) return null;
|
|
1100
|
+
for (let i = 0; i < length; i++) {
|
|
1101
|
+
const part = ownDataProperty(segments, String(i));
|
|
1102
|
+
if (!part.ok || !part.present || !safeKey(part.value)) return null;
|
|
1103
|
+
copy.push(part.value);
|
|
1104
|
+
}
|
|
1105
|
+
if (!keys.every((key) => typeof key === "string" && arrayKey(key, length))) return null;
|
|
1106
|
+
} catch {
|
|
1107
|
+
return null;
|
|
1108
|
+
}
|
|
1109
|
+
return copy;
|
|
1110
|
+
}
|
|
1111
|
+
function snapshotType(input) {
|
|
1112
|
+
const ancestors = /* @__PURE__ */ new Set();
|
|
1113
|
+
const budget = { remaining: 512 };
|
|
1114
|
+
try {
|
|
1115
|
+
return copyType(input, ancestors, budget, 0);
|
|
1116
|
+
} catch {
|
|
1117
|
+
return null;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
function copyType(input, ancestors, budget, depth) {
|
|
1121
|
+
if (input === null || typeof input === "string" || typeof input === "boolean") return input;
|
|
1122
|
+
if (typeof input === "number" && Number.isFinite(input)) return input;
|
|
1123
|
+
if (typeof input !== "object") throw Error("non-data type leaf");
|
|
1124
|
+
if (depth >= 256 || ancestors.has(input)) throw Error("invalid type graph");
|
|
1125
|
+
ancestors.add(input);
|
|
1126
|
+
try {
|
|
1127
|
+
const array = Array.isArray(input);
|
|
1128
|
+
const prototype = Object.getPrototypeOf(input);
|
|
1129
|
+
if (prototype !== null && prototype !== (array ? Array.prototype : Object.prototype))
|
|
1130
|
+
throw Error("exotic type prototype");
|
|
1131
|
+
const keys = Reflect.ownKeys(input);
|
|
1132
|
+
budget.remaining -= keys.length + 1;
|
|
1133
|
+
if (budget.remaining < 0) throw Error("type graph too large");
|
|
1134
|
+
const output = array ? [] : /* @__PURE__ */ Object.create(null);
|
|
1135
|
+
copyTypeFields(input, output, keys, array, ancestors, budget, depth);
|
|
1136
|
+
if (array) Object.setPrototypeOf(output, null);
|
|
1137
|
+
return Object.freeze(output);
|
|
1138
|
+
} finally {
|
|
1139
|
+
ancestors.delete(input);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
function copyTypeFields(input, output, keys, array, ancestors, budget, depth) {
|
|
1143
|
+
const length = array ? arrayLength(input, keys) : 0;
|
|
1144
|
+
for (const key of keys) {
|
|
1145
|
+
if (typeof key !== "string" || array && !arrayKey(key, length))
|
|
1146
|
+
throw Error("invalid type key");
|
|
1147
|
+
if (array && key === "length") continue;
|
|
1148
|
+
copyTypeField(input, output, key, ancestors, budget, depth);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
function arrayLength(input, keys) {
|
|
1152
|
+
const length = ownDataProperty(input, "length").value;
|
|
1153
|
+
if (typeof length !== "number" || !Number.isSafeInteger(length) || length > 512)
|
|
1154
|
+
throw Error("invalid type array");
|
|
1155
|
+
if (keys.length !== length + 1) throw Error("sparse type array");
|
|
1156
|
+
return length;
|
|
1157
|
+
}
|
|
1158
|
+
function copyTypeField(input, output, key, ancestors, budget, depth) {
|
|
1159
|
+
const part = ownDataProperty(input, key);
|
|
1160
|
+
if (!part.ok || !part.present) throw Error("invalid type descriptor");
|
|
1161
|
+
Object.defineProperty(output, key, {
|
|
1162
|
+
value: copyType(part.value, ancestors, budget, depth + 1),
|
|
1163
|
+
enumerable: true,
|
|
1164
|
+
configurable: true
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
function arrayKey(key, length) {
|
|
1168
|
+
if (key === "length") return true;
|
|
1169
|
+
const index = Number(key);
|
|
1170
|
+
return Number.isInteger(index) && index >= 0 && index < length && String(index) === key;
|
|
1171
|
+
}
|
|
1172
|
+
function property(input, binding, max) {
|
|
1173
|
+
if (!record(input, binding ? ["target", "type", "writable"] : ["type", "writable"], ["properties"]))
|
|
1174
|
+
return null;
|
|
1175
|
+
if (ownDataProperty(input, "writable").value !== true) return null;
|
|
1176
|
+
const snapshot = snapshotType(ownDataProperty(input, "type").value);
|
|
1177
|
+
if (snapshot === null) return null;
|
|
1178
|
+
const type = readStaticType(snapshot, max);
|
|
1179
|
+
if (type === null || type === "dynamic" || "shape" in type) return null;
|
|
1180
|
+
const properties = ownDataProperty(input, "properties");
|
|
1181
|
+
if (properties.present && (type.kind !== "primitive-type" || type.name !== "json" || typeof properties.value !== "object" || properties.value === null || Array.isArray(properties.value)))
|
|
1182
|
+
return null;
|
|
1183
|
+
return { type, properties: properties.value };
|
|
1184
|
+
}
|
|
1185
|
+
function chain(node, max) {
|
|
1186
|
+
const fields = [];
|
|
1187
|
+
const range = node.range;
|
|
1188
|
+
let current = node;
|
|
1189
|
+
for (let depth = 0; depth <= max; depth++) {
|
|
1190
|
+
if (current.kind === "group" && current.closeToken !== null) {
|
|
1191
|
+
current = current.expression;
|
|
1192
|
+
continue;
|
|
1193
|
+
}
|
|
1194
|
+
if (current.kind === "reference")
|
|
1195
|
+
return { reference: current.name, fields: fields.reverse(), range };
|
|
1196
|
+
if (current.kind !== "field-access" || current.optional || current.fieldToken === null || !safeKey(current.field))
|
|
1197
|
+
return null;
|
|
1198
|
+
fields.push({ name: current.field, range: current.range });
|
|
1199
|
+
current = current.target;
|
|
1200
|
+
}
|
|
1201
|
+
return null;
|
|
1202
|
+
}
|
|
1203
|
+
function checkKaladaV1DirectLocation(source, options) {
|
|
1204
|
+
try {
|
|
1205
|
+
return checkSource(source, options);
|
|
1206
|
+
} catch {
|
|
1207
|
+
return failure(
|
|
1208
|
+
"KALADA_SYNTAX_WRITE_BINDING_INVALID",
|
|
1209
|
+
freezeRange(0, typeof source === "string" ? source.length : 0)
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
function checkSource(source, options) {
|
|
1214
|
+
const limits = ownDataProperty(options, "limits");
|
|
1215
|
+
const bindings = ownDataProperty(options, "bindings");
|
|
1216
|
+
if (!record(options, ["bindings"], ["limits"]) || !bindings.ok || !bindings.present || !limits.ok)
|
|
1217
|
+
return failure(
|
|
1218
|
+
"KALADA_SYNTAX_WRITE_BINDING_INVALID",
|
|
1219
|
+
freezeRange(0, typeof source === "string" ? source.length : 0)
|
|
1220
|
+
);
|
|
1221
|
+
const parsed = parseKaladaV1Expression(
|
|
1222
|
+
source,
|
|
1223
|
+
limits.present ? { limits: limits.value } : void 0
|
|
1224
|
+
);
|
|
1225
|
+
if (parsed.diagnostics.length) return deepFreeze({ ok: false, diagnostics: parsed.diagnostics });
|
|
1226
|
+
const range = parsed.document.expression.range;
|
|
1227
|
+
const found = chain(parsed.document.expression, 256);
|
|
1228
|
+
if (!found) return failure("KALADA_SYNTAX_WRITE_INELIGIBLE", range);
|
|
1229
|
+
return resolveLocation(found, bindings.value);
|
|
1230
|
+
}
|
|
1231
|
+
function resolveLocation(found, bindings) {
|
|
1232
|
+
const range = found.range;
|
|
1233
|
+
const binding = ownDataProperty(bindings, found.reference);
|
|
1234
|
+
if (!binding.ok || !binding.present) return failure("KALADA_SYNTAX_WRITE_BINDING_INVALID", range);
|
|
1235
|
+
let entry = property(binding.value, true, 256);
|
|
1236
|
+
const base = target(ownDataProperty(binding.value, "target").value, 256);
|
|
1237
|
+
if (!entry || !base || !safeKey(found.reference))
|
|
1238
|
+
return failure("KALADA_SYNTAX_WRITE_BINDING_INVALID", range);
|
|
1239
|
+
const segments = [...base.segments];
|
|
1240
|
+
for (const field of found.fields) {
|
|
1241
|
+
const next = ownDataProperty(entry.properties, field.name);
|
|
1242
|
+
if (!next.ok || !next.present)
|
|
1243
|
+
return failure("KALADA_SYNTAX_WRITE_PROPERTY_INVALID", field.range);
|
|
1244
|
+
entry = property(next.value, false, 256);
|
|
1245
|
+
if (!entry) return failure("KALADA_SYNTAX_WRITE_PROPERTY_INVALID", field.range);
|
|
1246
|
+
segments.push(field.name);
|
|
1247
|
+
}
|
|
1248
|
+
return deepFreeze({
|
|
1249
|
+
ok: true,
|
|
1250
|
+
location: {
|
|
1251
|
+
target: { ...base, segments },
|
|
1252
|
+
type: entry.type,
|
|
1253
|
+
range: freezeRange(range.start, range.end)
|
|
1254
|
+
}
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
// src/format.ts
|
|
1259
|
+
function formatKaladaV1Expression(source, options) {
|
|
1260
|
+
const parsed = parseKaladaV1Expression(source, options);
|
|
1261
|
+
if (parsed.diagnostics.length > 0) {
|
|
1262
|
+
return deepFreeze({ ok: false, diagnostics: parsed.diagnostics });
|
|
1263
|
+
}
|
|
1264
|
+
return Object.freeze({ ok: true, text: formatNode(parsed.document.expression, parsed.document) });
|
|
1265
|
+
}
|
|
1266
|
+
function formatNode(node, document) {
|
|
1267
|
+
if (node.kind === "literal" || node.kind === "reference") {
|
|
1268
|
+
return document.tokens[node.token]?.text ?? "";
|
|
1269
|
+
}
|
|
1270
|
+
if (node.kind === "group") return formatGroup(node, document);
|
|
1271
|
+
if (node.kind === "field-access") {
|
|
1272
|
+
const operator = document.tokens[node.operatorToken]?.text ?? "";
|
|
1273
|
+
const field = node.fieldToken === null ? "" : document.tokens[node.fieldToken]?.text ?? "";
|
|
1274
|
+
return `${formatNode(node.target, document)}${operator}${field}`;
|
|
1275
|
+
}
|
|
1276
|
+
if (node.kind === "unary") return formatUnary(node, document);
|
|
1277
|
+
if (node.kind === "binary") {
|
|
1278
|
+
return `${formatNode(node.left, document)} ${node.operator} ${formatNode(node.right, document)}`;
|
|
1279
|
+
}
|
|
1280
|
+
if (node.kind === "conditional") {
|
|
1281
|
+
return `${formatNode(node.condition, document)} ? ${formatNode(node.then, document)} : ${formatNode(node.else, document)}`;
|
|
1282
|
+
}
|
|
1283
|
+
return "";
|
|
1284
|
+
}
|
|
1285
|
+
function formatUnary(node, document) {
|
|
1286
|
+
const sameSign = (node.operator === "+" || node.operator === "-") && node.operand.kind === "unary" && node.operand.operator === node.operator;
|
|
1287
|
+
return `${node.operator}${sameSign ? " " : ""}${formatNode(node.operand, document)}`;
|
|
1288
|
+
}
|
|
1289
|
+
function formatGroup(node, document) {
|
|
1290
|
+
return `(${formatNode(node.expression, document)})`;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
// src/guest-boundary.ts
|
|
1294
|
+
function parseGuestExpressionPrefix(source, start, options) {
|
|
1295
|
+
let limits;
|
|
1296
|
+
try {
|
|
1297
|
+
limits = resolveSyntaxLimits(options);
|
|
1298
|
+
} catch {
|
|
1299
|
+
limits = null;
|
|
1300
|
+
}
|
|
1301
|
+
if (typeof source !== "string" || !Number.isSafeInteger(start) || start < 0 || start > source.length || limits === null) {
|
|
1302
|
+
return deepFreeze({
|
|
1303
|
+
ok: false,
|
|
1304
|
+
range: freezeRange(0, 0),
|
|
1305
|
+
stop: 0,
|
|
1306
|
+
reason: "invalid-start",
|
|
1307
|
+
parsed: null,
|
|
1308
|
+
diagnostics: []
|
|
1309
|
+
});
|
|
1310
|
+
}
|
|
1311
|
+
const scanned = lexGuest(source, start, limits);
|
|
1312
|
+
const stop = scanned.stop ?? scanned.tokens[scanned.tokens.length - 1]?.range.start ?? start;
|
|
1313
|
+
const sink = new DiagnosticSink(limits, scanned.diagnostics);
|
|
1314
|
+
const limited = sink.diagnostics.some((item) => item.code === "KALADA_SYNTAX_LIMIT_EXCEEDED");
|
|
1315
|
+
const reason = guestStopReason(scanned, limited);
|
|
1316
|
+
if (reason === "eof")
|
|
1317
|
+
sink.add("parse", "KALADA_SYNTAX_UNEXPECTED_TOKEN", freezeRange(stop, stop));
|
|
1318
|
+
const parsed = parseTokens(source, scanned.tokens, limits, sink.diagnostics);
|
|
1319
|
+
return deepFreeze({
|
|
1320
|
+
ok: reason === "outer-brace" && parsed.diagnostics.length === 0,
|
|
1321
|
+
range: freezeRange(start, stop),
|
|
1322
|
+
stop,
|
|
1323
|
+
reason,
|
|
1324
|
+
parsed,
|
|
1325
|
+
diagnostics: parsed.diagnostics
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
function guestStopReason(scanned, limited) {
|
|
1329
|
+
if (scanned.stop === null) return limited ? "limit" : "eof";
|
|
1330
|
+
if (scanned.unsupportedComment) return "unsupported-comment";
|
|
1331
|
+
if (scanned.unsupportedQuote) return "unsupported-quote";
|
|
1332
|
+
return scanned.unmatchedParentheses ? "unmatched-parentheses" : "outer-brace";
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
// src/dispatch.ts
|
|
1336
|
+
var DispatchFailure = class extends Error {
|
|
1337
|
+
code;
|
|
1338
|
+
operand;
|
|
1339
|
+
constructor(code, operand) {
|
|
1340
|
+
super(code);
|
|
1341
|
+
this.code = code;
|
|
1342
|
+
this.operand = operand;
|
|
1343
|
+
}
|
|
1344
|
+
};
|
|
1345
|
+
function dispatchBinary(operator, left, right) {
|
|
1346
|
+
if (BINARY_OPERATOR_TIERS.equality.has(operator)) return equality(left, right);
|
|
1347
|
+
if (BINARY_OPERATOR_TIERS.relational.has(operator) && operator !== "in")
|
|
1348
|
+
return ordering(left, right);
|
|
1349
|
+
if (operator === "in") return membership(left, right);
|
|
1350
|
+
if (isAdditiveOperator(operator)) return additive(operator, left, right);
|
|
1351
|
+
if (BINARY_OPERATOR_TIERS.multiplicative.has(operator)) return numeric(left, right);
|
|
1352
|
+
if (operator === "&&" || operator === "||") return booleanLogical(left, right);
|
|
1353
|
+
if (operator === "xor") return booleanXor(left, right);
|
|
1354
|
+
return coalesce(left, right);
|
|
1355
|
+
}
|
|
1356
|
+
function dispatchUnary(operator, operand) {
|
|
1357
|
+
requireType(operand, operator === "!" ? "boolean" : "number", "left");
|
|
1358
|
+
return {
|
|
1359
|
+
family: operator === "!" ? "boolean-logical" : "numeric-binary",
|
|
1360
|
+
type: primitive(operator === "!" ? "boolean" : "number")
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
function dispatchField(target2, optional) {
|
|
1364
|
+
if (!optional) {
|
|
1365
|
+
if (target2 !== "dynamic" && !typeIs(target2, "json"))
|
|
1366
|
+
fail("KALADA_FIELD_TYPE_MISMATCH", "target");
|
|
1367
|
+
return "dynamic";
|
|
1368
|
+
}
|
|
1369
|
+
if (target2 === "dynamic") return option("dynamic");
|
|
1370
|
+
if (typeIs(target2, "null")) return option(primitive("null"));
|
|
1371
|
+
if (typeIs(target2, "json")) return option("dynamic");
|
|
1372
|
+
if (isOption(target2) && validOptionalPayload(target2.value))
|
|
1373
|
+
return option(typeIs(target2.value, "null") ? primitive("null") : "dynamic");
|
|
1374
|
+
return fail("KALADA_FIELD_TYPE_MISMATCH", "target");
|
|
1375
|
+
}
|
|
1376
|
+
function dispatchConditional(condition, thenType, elseType) {
|
|
1377
|
+
requireType(condition, "boolean", "condition");
|
|
1378
|
+
const joined = joinTypes(thenType, elseType);
|
|
1379
|
+
return joined ?? fail("KALADA_OPERATOR_TYPE", "else");
|
|
1380
|
+
}
|
|
1381
|
+
function equality(left, right) {
|
|
1382
|
+
if (isCallable(left)) fail("KALADA_OPERATOR_TYPE", "left");
|
|
1383
|
+
if (isCallable(right)) fail("KALADA_OPERATOR_TYPE", "right");
|
|
1384
|
+
return { family: "equality", type: primitive("boolean") };
|
|
1385
|
+
}
|
|
1386
|
+
function ordering(left, right) {
|
|
1387
|
+
const leftDomain = orderedDomain(left);
|
|
1388
|
+
const rightDomain = orderedDomain(right);
|
|
1389
|
+
if (leftDomain === "unsupported") fail("KALADA_OPERATOR_TYPE", "left");
|
|
1390
|
+
if (rightDomain === "unsupported") fail("KALADA_OPERATOR_TYPE", "right");
|
|
1391
|
+
if (leftDomain === "dynamic" && rightDomain === "dynamic")
|
|
1392
|
+
fail("KALADA_OPERATOR_AMBIGUOUS", "operator");
|
|
1393
|
+
const domain = leftDomain === "dynamic" ? rightDomain : leftDomain;
|
|
1394
|
+
if (rightDomain !== "dynamic" && leftDomain !== "dynamic" && leftDomain !== rightDomain)
|
|
1395
|
+
fail("KALADA_OPERATOR_TYPE", "right");
|
|
1396
|
+
const family = domain === "number" ? "ordered-number" : domain === "string" ? "ordered-string" : "temporal-comparison";
|
|
1397
|
+
return { family, type: primitive("boolean") };
|
|
1398
|
+
}
|
|
1399
|
+
function membership(left, right) {
|
|
1400
|
+
if (isCallable(left)) fail("KALADA_OPERATOR_TYPE", "needle");
|
|
1401
|
+
if (right !== "dynamic" && ("shape" in right || right.kind !== "array-type"))
|
|
1402
|
+
fail("KALADA_OPERATOR_TYPE", "array");
|
|
1403
|
+
return { family: "membership", type: primitive("boolean") };
|
|
1404
|
+
}
|
|
1405
|
+
function additive(operator, left, right) {
|
|
1406
|
+
if (left === "dynamic" && right === "dynamic") fail("KALADA_OPERATOR_AMBIGUOUS", "operator");
|
|
1407
|
+
if (typeIs(left, "number") || left === "dynamic" && typeIs(right, "number"))
|
|
1408
|
+
return numeric(left, right);
|
|
1409
|
+
if (typeIs(left, "number")) return numeric(left, right);
|
|
1410
|
+
if (isTemporalCandidate(left, right, operator)) return temporalResult(operator, left, right);
|
|
1411
|
+
if (left !== "dynamic" && !isTemporal(left)) fail("KALADA_OPERATOR_TYPE", "left");
|
|
1412
|
+
return fail("KALADA_OPERATOR_TYPE", "right");
|
|
1413
|
+
}
|
|
1414
|
+
function numeric(left, right) {
|
|
1415
|
+
requireType(left, "number", "left");
|
|
1416
|
+
requireType(right, "number", "right");
|
|
1417
|
+
return { family: "numeric-binary", type: primitive("number") };
|
|
1418
|
+
}
|
|
1419
|
+
function temporalResult(operator, left, right) {
|
|
1420
|
+
let type = "dynamic";
|
|
1421
|
+
if (operator === "+" && typeIs(left, "Instant")) type = primitive("Instant");
|
|
1422
|
+
if (operator === "+" && typeIs(left, "Duration")) type = primitive("Duration");
|
|
1423
|
+
if (operator === "-" && typeIs(left, "Duration")) type = primitive("Duration");
|
|
1424
|
+
if (operator === "-" && typeIs(left, "Instant") && typeIs(right, "Duration"))
|
|
1425
|
+
type = primitive("Instant");
|
|
1426
|
+
if (operator === "-" && typeIs(right, "Instant")) type = primitive("Duration");
|
|
1427
|
+
return { family: "temporal-arithmetic", type };
|
|
1428
|
+
}
|
|
1429
|
+
function booleanLogical(left, right) {
|
|
1430
|
+
requireType(left, "boolean", "left");
|
|
1431
|
+
requireType(right, "boolean", "right");
|
|
1432
|
+
return { family: "boolean-logical", type: primitive("boolean") };
|
|
1433
|
+
}
|
|
1434
|
+
function booleanXor(left, right) {
|
|
1435
|
+
const result2 = booleanLogical(left, right);
|
|
1436
|
+
return { ...result2, family: "boolean-xor" };
|
|
1437
|
+
}
|
|
1438
|
+
function coalesce(left, right) {
|
|
1439
|
+
const payload = left === "dynamic" ? "dynamic" : isOption(left) ? left.value : fail("KALADA_OPTION_REQUIRED", "option");
|
|
1440
|
+
const joined = joinTypes(payload, right);
|
|
1441
|
+
if (joined === null) fail("KALADA_OPERATOR_TYPE", "fallback");
|
|
1442
|
+
return { family: "option-coalesce", type: joined };
|
|
1443
|
+
}
|
|
1444
|
+
function orderedDomain(type) {
|
|
1445
|
+
if (type === "dynamic") return "dynamic";
|
|
1446
|
+
if ("shape" in type) return "unsupported";
|
|
1447
|
+
if (type.kind !== "primitive-type") return "unsupported";
|
|
1448
|
+
return ["number", "string", "Instant", "Duration"].includes(type.name) ? type.name : "unsupported";
|
|
1449
|
+
}
|
|
1450
|
+
function isTemporalCandidate(left, right, operator) {
|
|
1451
|
+
if (operator === "+")
|
|
1452
|
+
return typeIs(left, "Instant") && (typeIs(right, "Duration") || right === "dynamic") || typeIs(left, "Duration") && (typeIs(right, "Duration") || right === "dynamic") || left === "dynamic" && typeIs(right, "Duration");
|
|
1453
|
+
return typeIs(left, "Instant") && (isTemporal(right) || right === "dynamic") || typeIs(left, "Duration") && (typeIs(right, "Duration") || right === "dynamic") || left === "dynamic" && isTemporal(right);
|
|
1454
|
+
}
|
|
1455
|
+
function isTemporal(type) {
|
|
1456
|
+
return typeIs(type, "Instant") || typeIs(type, "Duration");
|
|
1457
|
+
}
|
|
1458
|
+
function validOptionalPayload(type) {
|
|
1459
|
+
return type === "dynamic" || typeIs(type, "json") || typeIs(type, "null");
|
|
1460
|
+
}
|
|
1461
|
+
function option(value) {
|
|
1462
|
+
return Object.freeze({ shape: "option", value });
|
|
1463
|
+
}
|
|
1464
|
+
function requireType(type, name, operand) {
|
|
1465
|
+
if (type !== "dynamic" && !typeIs(type, name)) fail("KALADA_OPERATOR_TYPE", operand);
|
|
1466
|
+
}
|
|
1467
|
+
function fail(code, operand) {
|
|
1468
|
+
throw new DispatchFailure(code, operand);
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
// src/reference-environment.ts
|
|
1472
|
+
function readLowerConfiguration(options, limits) {
|
|
1473
|
+
if (options === void 0) return { environment: null, coreOptions: {} };
|
|
1474
|
+
if (!hasOnlyKeys(options, /* @__PURE__ */ new Set(["references", "coreOptions"]))) return null;
|
|
1475
|
+
const referencesProperty = ownDataProperty(options, "references");
|
|
1476
|
+
const coreOptionsProperty = ownDataProperty(options, "coreOptions");
|
|
1477
|
+
if (!referencesProperty.ok || !coreOptionsProperty.ok) return null;
|
|
1478
|
+
const references = referencesProperty.value;
|
|
1479
|
+
const coreOptions = coreOptionsProperty.value;
|
|
1480
|
+
if (coreOptionsProperty.present && (typeof coreOptions !== "object" || coreOptions === null))
|
|
1481
|
+
return null;
|
|
1482
|
+
const environment = referencesProperty.present ? readEnvironment(references, limits) : null;
|
|
1483
|
+
if (referencesProperty.present && environment === null) return null;
|
|
1484
|
+
return { environment, coreOptions: coreOptions ?? {} };
|
|
1485
|
+
}
|
|
1486
|
+
function readEnvironment(input, limits) {
|
|
1487
|
+
try {
|
|
1488
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) return null;
|
|
1489
|
+
const output = /* @__PURE__ */ new Map();
|
|
1490
|
+
for (const key of Reflect.ownKeys(input)) {
|
|
1491
|
+
if (typeof key !== "string") return null;
|
|
1492
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, key);
|
|
1493
|
+
if (!descriptor || !("value" in descriptor)) return null;
|
|
1494
|
+
const binding = readBinding(descriptor.value, limits);
|
|
1495
|
+
if (binding === null) return null;
|
|
1496
|
+
output.set(key, binding);
|
|
1497
|
+
}
|
|
1498
|
+
return output;
|
|
1499
|
+
} catch {
|
|
1500
|
+
return null;
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
function readBinding(input, limits) {
|
|
1504
|
+
if (!hasOnlyKeys(input, /* @__PURE__ */ new Set(["reference", "type"]))) return null;
|
|
1505
|
+
const reference = dataProperty(input, "reference");
|
|
1506
|
+
const rawType = dataProperty(input, "type");
|
|
1507
|
+
if (!reference.found || !rawType.found) return null;
|
|
1508
|
+
const type = readStaticType(rawType.value, limits.maxStaticTypeDepth);
|
|
1509
|
+
if (type === null) return null;
|
|
1510
|
+
return Object.freeze({ reference: reference.value, type });
|
|
1511
|
+
}
|
|
1512
|
+
function dataProperty(input, key) {
|
|
1513
|
+
try {
|
|
1514
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, key);
|
|
1515
|
+
return descriptor && "value" in descriptor ? { found: true, value: descriptor.value } : { found: false };
|
|
1516
|
+
} catch {
|
|
1517
|
+
return { found: false };
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
// src/semantic-inference.ts
|
|
1522
|
+
function analyzeSemantics(parsed, configuration) {
|
|
1523
|
+
const state = {
|
|
1524
|
+
configuration,
|
|
1525
|
+
inferred: /* @__PURE__ */ new Map(),
|
|
1526
|
+
diagnostics: [...parsed.diagnostics],
|
|
1527
|
+
incomplete: parsed.diagnostics.length > 0
|
|
1528
|
+
};
|
|
1529
|
+
const ordered = postorder(parsed.document.expression);
|
|
1530
|
+
for (const node of ordered) state.inferred.set(node, inferNode(node, state));
|
|
1531
|
+
return {
|
|
1532
|
+
ordered,
|
|
1533
|
+
inferred: state.inferred,
|
|
1534
|
+
diagnostics: state.diagnostics,
|
|
1535
|
+
incomplete: state.incomplete
|
|
1536
|
+
};
|
|
1537
|
+
}
|
|
1538
|
+
function inferNode(node, state) {
|
|
1539
|
+
if (node.kind === "literal") return complete(literalType(node.value));
|
|
1540
|
+
if (node.kind === "reference") return inferReference(node, state);
|
|
1541
|
+
if (node.kind === "error") return incomplete(state);
|
|
1542
|
+
try {
|
|
1543
|
+
return inferComposite(node, state);
|
|
1544
|
+
} catch (error) {
|
|
1545
|
+
return failed(node, error, state);
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
function inferComposite(node, state) {
|
|
1549
|
+
if (node.kind === "group") {
|
|
1550
|
+
const child = childInference(node.expression, state);
|
|
1551
|
+
return node.closeToken === null || !child.complete ? incomplete(state) : child;
|
|
1552
|
+
}
|
|
1553
|
+
if (node.kind === "field-access") return inferField(node, state);
|
|
1554
|
+
if (node.kind === "unary") return inferUnary(node, state);
|
|
1555
|
+
if (node.kind === "binary") return inferBinary(node, state);
|
|
1556
|
+
return inferConditional(node, state);
|
|
1557
|
+
}
|
|
1558
|
+
function inferField(node, state) {
|
|
1559
|
+
const target2 = childInference(node.target, state);
|
|
1560
|
+
if (!target2.complete || node.field === null || node.fieldToken === null) return incomplete(state);
|
|
1561
|
+
return complete(dispatchField(target2.type, node.optional));
|
|
1562
|
+
}
|
|
1563
|
+
function inferUnary(node, state) {
|
|
1564
|
+
const operand = childInference(node.operand, state);
|
|
1565
|
+
if (!operand.complete) return incomplete(state);
|
|
1566
|
+
const dispatch = dispatchUnary(node.operator, operand.type);
|
|
1567
|
+
return complete(dispatch.type, dispatch);
|
|
1568
|
+
}
|
|
1569
|
+
function inferBinary(node, state) {
|
|
1570
|
+
const left = childInference(node.left, state);
|
|
1571
|
+
const right = childInference(node.right, state);
|
|
1572
|
+
if (!left.complete || !right.complete) return incomplete(state);
|
|
1573
|
+
const dispatch = dispatchBinary(node.operator, left.type, right.type);
|
|
1574
|
+
return complete(dispatch.type, dispatch);
|
|
1575
|
+
}
|
|
1576
|
+
function inferConditional(node, state) {
|
|
1577
|
+
const condition = childInference(node.condition, state);
|
|
1578
|
+
const then = childInference(node.then, state);
|
|
1579
|
+
const otherwise = childInference(node.else, state);
|
|
1580
|
+
if (node.colonToken === null || !condition.complete || !then.complete || !otherwise.complete) {
|
|
1581
|
+
return incomplete(state);
|
|
1582
|
+
}
|
|
1583
|
+
return complete(dispatchConditional(condition.type, then.type, otherwise.type));
|
|
1584
|
+
}
|
|
1585
|
+
function inferReference(node, state) {
|
|
1586
|
+
const environment = state.configuration.environment;
|
|
1587
|
+
const binding = environment?.get(node.name);
|
|
1588
|
+
if (!environment || binding) return complete(binding?.type ?? "dynamic");
|
|
1589
|
+
state.incomplete = true;
|
|
1590
|
+
state.diagnostics.push(
|
|
1591
|
+
diagnostic(
|
|
1592
|
+
"lower",
|
|
1593
|
+
"KALADA_SYNTAX_UNKNOWN_REFERENCE",
|
|
1594
|
+
"Kalada reference is not present in the lowering environment.",
|
|
1595
|
+
node.range,
|
|
1596
|
+
[]
|
|
1597
|
+
)
|
|
1598
|
+
);
|
|
1599
|
+
return { type: "dynamic", complete: false };
|
|
1600
|
+
}
|
|
1601
|
+
function failed(node, error, state) {
|
|
1602
|
+
state.incomplete = true;
|
|
1603
|
+
const failure2 = error instanceof DispatchFailure ? error : void 0;
|
|
1604
|
+
state.diagnostics.push(
|
|
1605
|
+
diagnostic(
|
|
1606
|
+
"lower",
|
|
1607
|
+
failure2?.code ?? "KALADA_SYNTAX_INVALID_INPUT",
|
|
1608
|
+
"Kalada semantic analysis could not determine this expression.",
|
|
1609
|
+
node.range,
|
|
1610
|
+
[]
|
|
1611
|
+
)
|
|
1612
|
+
);
|
|
1613
|
+
return { type: "dynamic", complete: false, ...failure2 ? { failure: failure2 } : {} };
|
|
1614
|
+
}
|
|
1615
|
+
function childInference(node, state) {
|
|
1616
|
+
return state.inferred.get(node) ?? incomplete(state);
|
|
1617
|
+
}
|
|
1618
|
+
function complete(type, dispatch) {
|
|
1619
|
+
return { type, complete: true, ...dispatch ? { dispatch } : {} };
|
|
1620
|
+
}
|
|
1621
|
+
function incomplete(state) {
|
|
1622
|
+
state.incomplete = true;
|
|
1623
|
+
return { type: "dynamic", complete: false };
|
|
1624
|
+
}
|
|
1625
|
+
function postorder(root) {
|
|
1626
|
+
const output = [];
|
|
1627
|
+
const stack = [{ node: root, visited: false }];
|
|
1628
|
+
while (stack.length > 0) {
|
|
1629
|
+
const entry = stack.pop();
|
|
1630
|
+
if (!entry) continue;
|
|
1631
|
+
if (entry.visited) output.push(entry.node);
|
|
1632
|
+
else scheduleChildren(stack, entry.node);
|
|
1633
|
+
}
|
|
1634
|
+
return output;
|
|
1635
|
+
}
|
|
1636
|
+
function scheduleChildren(stack, node) {
|
|
1637
|
+
stack.push({ node, visited: true });
|
|
1638
|
+
const children = nodeChildren(node);
|
|
1639
|
+
for (let index = children.length - 1; index >= 0; index -= 1) {
|
|
1640
|
+
const child = children[index];
|
|
1641
|
+
if (child) stack.push({ node: child, visited: false });
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
function nodeChildren(node) {
|
|
1645
|
+
if (node.kind === "group") return [node.expression];
|
|
1646
|
+
if (node.kind === "field-access") return [node.target];
|
|
1647
|
+
if (node.kind === "unary") return [node.operand];
|
|
1648
|
+
if (node.kind === "binary") return [node.left, node.right];
|
|
1649
|
+
if (node.kind === "conditional") return [node.condition, node.then, node.else];
|
|
1650
|
+
return [];
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
// src/source-map.ts
|
|
1654
|
+
var ROLE_ORDER = Object.freeze({
|
|
1655
|
+
node: 0,
|
|
1656
|
+
operator: 1,
|
|
1657
|
+
literal: 2,
|
|
1658
|
+
reference: 3,
|
|
1659
|
+
field: 4,
|
|
1660
|
+
group: 5
|
|
1661
|
+
});
|
|
1662
|
+
function addMap(entries, path, role, range) {
|
|
1663
|
+
entries.push({ path: [...path], role, range: freezeRange(range.start, range.end) });
|
|
1664
|
+
}
|
|
1665
|
+
function widenNode(entries, path, range) {
|
|
1666
|
+
const index = entries.findIndex((entry) => entry.role === "node" && equalPath(entry.path, path));
|
|
1667
|
+
if (index < 0) return;
|
|
1668
|
+
const found = entries[index];
|
|
1669
|
+
entries[index] = { ...found, range: freezeRange(range.start, range.end) };
|
|
1670
|
+
}
|
|
1671
|
+
function finishMap(entries) {
|
|
1672
|
+
entries.sort(compareEntries);
|
|
1673
|
+
return deepFreeze(entries);
|
|
1674
|
+
}
|
|
1675
|
+
function rangeForPath(entries, path, fallback) {
|
|
1676
|
+
const exact = entries.filter((entry) => equalPath(entry.path, path)).sort(preferNarrow)[0];
|
|
1677
|
+
if (exact) return exact.range;
|
|
1678
|
+
for (let length = path.length - 1; length >= 1; length -= 1) {
|
|
1679
|
+
const ancestor = path.slice(0, length);
|
|
1680
|
+
const found = entries.find((entry) => entry.role === "node" && equalPath(entry.path, ancestor));
|
|
1681
|
+
if (found) return found.range;
|
|
1682
|
+
}
|
|
1683
|
+
return fallback;
|
|
1684
|
+
}
|
|
1685
|
+
function compareEntries(left, right) {
|
|
1686
|
+
return left.range.start - right.range.start || left.range.end - right.range.end || ROLE_ORDER[left.role] - ROLE_ORDER[right.role] || pathKey(left.path).localeCompare(pathKey(right.path));
|
|
1687
|
+
}
|
|
1688
|
+
function preferNarrow(left, right) {
|
|
1689
|
+
const leftLength = left.range.end - left.range.start;
|
|
1690
|
+
const rightLength = right.range.end - right.range.start;
|
|
1691
|
+
return leftLength - rightLength || ROLE_ORDER[left.role] - ROLE_ORDER[right.role];
|
|
1692
|
+
}
|
|
1693
|
+
function equalPath(left, right) {
|
|
1694
|
+
return left.length === right.length && left.every((item, index) => item === right[index]);
|
|
1695
|
+
}
|
|
1696
|
+
function pathKey(path) {
|
|
1697
|
+
return JSON.stringify(path);
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
// src/lower.ts
|
|
1701
|
+
var CORE_MESSAGES = Object.freeze({
|
|
1702
|
+
KALADA_OPERATOR_TYPE: "Kalada operator received an incompatible value.",
|
|
1703
|
+
KALADA_OPERATOR_AMBIGUOUS: "Kalada operator domain is ambiguous.",
|
|
1704
|
+
KALADA_OPTION_REQUIRED: "Kalada option coalesce requires an Option value.",
|
|
1705
|
+
KALADA_FIELD_TYPE_MISMATCH: "Kalada field access requires a JSON object."
|
|
1706
|
+
});
|
|
1707
|
+
var LowerFailure = class extends Error {
|
|
1708
|
+
diagnostic;
|
|
1709
|
+
constructor(diagnostic2) {
|
|
1710
|
+
super(diagnostic2.code);
|
|
1711
|
+
this.diagnostic = diagnostic2;
|
|
1712
|
+
}
|
|
1713
|
+
};
|
|
1714
|
+
function lowerKaladaV1Expression(parsed, options) {
|
|
1715
|
+
const limits = syntaxLimitsFor(parsed);
|
|
1716
|
+
if (limits === null) return invalidOutcome();
|
|
1717
|
+
if (parsed.diagnostics.length > 0)
|
|
1718
|
+
return deepFreeze({ ok: false, diagnostics: parsed.diagnostics });
|
|
1719
|
+
let configuration;
|
|
1720
|
+
try {
|
|
1721
|
+
configuration = readLowerConfiguration(options, limits);
|
|
1722
|
+
} catch {
|
|
1723
|
+
configuration = null;
|
|
1724
|
+
}
|
|
1725
|
+
if (configuration === null) return invalidOutcome(parsed.document.expression.range);
|
|
1726
|
+
const analysis = analyzeSemantics(parsed, configuration);
|
|
1727
|
+
const state = { parsed, configuration, entries: [], inferred: analysis.inferred };
|
|
1728
|
+
try {
|
|
1729
|
+
const lowered = lowerNode(parsed.document.expression, ["expression"], state);
|
|
1730
|
+
const input = KaladaV1.program(lowered.expression);
|
|
1731
|
+
const compiled = compileKaladaV1Program(input, configuration.coreOptions);
|
|
1732
|
+
if (!compiled.ok) throw coreFailure(compiled.diagnostic, state);
|
|
1733
|
+
return deepFreeze({
|
|
1734
|
+
ok: true,
|
|
1735
|
+
program: compiled.value.program,
|
|
1736
|
+
sourceMap: finishMap(state.entries),
|
|
1737
|
+
resultType: projectStaticType(lowered.type)
|
|
1738
|
+
});
|
|
1739
|
+
} catch (error) {
|
|
1740
|
+
const problem = error instanceof LowerFailure ? error.diagnostic : invalidDiagnostic(parsed.document.expression.range);
|
|
1741
|
+
return deepFreeze({ ok: false, diagnostics: [problem] });
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
function lowerNode(node, path, state) {
|
|
1745
|
+
if (node.kind === "literal") return lowerLiteral(node, path, state);
|
|
1746
|
+
if (node.kind === "reference") return lowerReference(node, path, state);
|
|
1747
|
+
if (node.kind === "group") return lowerGroup(node, path, state);
|
|
1748
|
+
if (node.kind === "field-access") return lowerField(node, path, state);
|
|
1749
|
+
if (node.kind === "unary") return lowerUnary(node, path, state);
|
|
1750
|
+
if (node.kind === "binary") return lowerBinary(node, path, state);
|
|
1751
|
+
if (node.kind === "conditional") return lowerConditional(node, path, state);
|
|
1752
|
+
throw new LowerFailure(invalidDiagnostic(node.range));
|
|
1753
|
+
}
|
|
1754
|
+
function lowerLiteral(node, path, state) {
|
|
1755
|
+
addMap(state.entries, path, "node", node.range);
|
|
1756
|
+
addMap(state.entries, [...path, "value"], "literal", node.range);
|
|
1757
|
+
return { expression: KaladaV1.literal(node.value), type: inferenceFor(node, state).type };
|
|
1758
|
+
}
|
|
1759
|
+
function lowerReference(node, path, state) {
|
|
1760
|
+
const binding = state.configuration.environment?.get(node.name);
|
|
1761
|
+
if (state.configuration.environment && !binding) {
|
|
1762
|
+
throw syntaxFailure("KALADA_SYNTAX_UNKNOWN_REFERENCE", node.range, [...path, "ref"]);
|
|
1763
|
+
}
|
|
1764
|
+
addMap(state.entries, path, "node", node.range);
|
|
1765
|
+
addMap(state.entries, [...path, "ref"], "reference", node.range);
|
|
1766
|
+
const type = inferenceFor(node, state).type;
|
|
1767
|
+
return binding ? { expression: KaladaV1.ref(binding.reference), type } : { expression: KaladaV1.ref(node.name), type };
|
|
1768
|
+
}
|
|
1769
|
+
function lowerGroup(node, path, state) {
|
|
1770
|
+
const child = lowerNode(node.expression, path, state);
|
|
1771
|
+
addMap(state.entries, path, "group", node.range);
|
|
1772
|
+
widenNode(state.entries, path, node.range);
|
|
1773
|
+
return child;
|
|
1774
|
+
}
|
|
1775
|
+
function lowerField(node, path, state) {
|
|
1776
|
+
const target2 = lowerNode(node.target, [...path, "target"], state);
|
|
1777
|
+
const operatorRange = sourceRange(state, node.operatorToken);
|
|
1778
|
+
addMap(state.entries, path, "operator", operatorRange);
|
|
1779
|
+
const fieldRange = node.fieldToken === null ? operatorRange : sourceRange(state, node.fieldToken);
|
|
1780
|
+
addMap(state.entries, [...path, "field"], "field", fieldRange);
|
|
1781
|
+
const inferred = inferenceFor(node, state);
|
|
1782
|
+
if (inferred.failure) throw dispatchFailure(inferred.failure, path, state, node.range);
|
|
1783
|
+
if (!inferred.complete) throw new LowerFailure(invalidDiagnostic(node.range));
|
|
1784
|
+
const expression = node.optional ? KaladaV1.optionalFieldAccess(target2.expression, node.field) : KaladaV1.fieldAccess(target2.expression, node.field);
|
|
1785
|
+
addMap(state.entries, path, "node", node.range);
|
|
1786
|
+
return { expression, type: inferred.type };
|
|
1787
|
+
}
|
|
1788
|
+
function lowerUnary(node, path, state) {
|
|
1789
|
+
const operand = lowerNode(node.operand, [...path, "operand"], state);
|
|
1790
|
+
const operatorPath = node.operator === "!" ? path : [...path, "operator"];
|
|
1791
|
+
addMap(state.entries, operatorPath, "operator", sourceRange(state, node.operatorToken));
|
|
1792
|
+
const inferred = inferenceFor(node, state);
|
|
1793
|
+
if (inferred.failure) throw dispatchFailure(inferred.failure, path, state, node.range, "operand");
|
|
1794
|
+
const result2 = requireDispatch(inferred, node.range);
|
|
1795
|
+
const expression = node.operator === "!" ? KaladaV1.booleanNot(operand.expression) : KaladaV1.numericUnary(node.operator === "+" ? "plus" : "negate", operand.expression);
|
|
1796
|
+
addMap(state.entries, path, "node", node.range);
|
|
1797
|
+
return { expression, type: result2.type };
|
|
1798
|
+
}
|
|
1799
|
+
function lowerBinary(node, path, state) {
|
|
1800
|
+
const [leftKey, rightKey] = binaryKeys(node.operator);
|
|
1801
|
+
const left = lowerNode(node.left, [...path, leftKey], state);
|
|
1802
|
+
const right = lowerNode(node.right, [...path, rightKey], state);
|
|
1803
|
+
const operatorPath = operatorHasField(node.operator) ? [...path, "operator"] : path;
|
|
1804
|
+
addMap(state.entries, operatorPath, "operator", sourceRange(state, node.operatorToken));
|
|
1805
|
+
const inferred = inferenceFor(node, state);
|
|
1806
|
+
if (inferred.failure) throw dispatchFailure(inferred.failure, path, state, node.range);
|
|
1807
|
+
const dispatch = requireDispatch(inferred, node.range);
|
|
1808
|
+
const expression = binaryExpression(
|
|
1809
|
+
node.operator,
|
|
1810
|
+
dispatch.family,
|
|
1811
|
+
left.expression,
|
|
1812
|
+
right.expression
|
|
1813
|
+
);
|
|
1814
|
+
addMap(state.entries, path, "node", node.range);
|
|
1815
|
+
return { expression, type: dispatch.type };
|
|
1816
|
+
}
|
|
1817
|
+
function lowerConditional(node, path, state) {
|
|
1818
|
+
const condition = lowerNode(node.condition, [...path, "condition"], state);
|
|
1819
|
+
const then = lowerNode(node.then, [...path, "then"], state);
|
|
1820
|
+
const otherwise = lowerNode(node.else, [...path, "else"], state);
|
|
1821
|
+
addMap(state.entries, path, "operator", sourceRange(state, node.questionToken));
|
|
1822
|
+
if (node.colonToken !== null)
|
|
1823
|
+
addMap(state.entries, path, "operator", sourceRange(state, node.colonToken));
|
|
1824
|
+
const inferred = inferenceFor(node, state);
|
|
1825
|
+
if (inferred.failure) throw dispatchFailure(inferred.failure, path, state, node.range);
|
|
1826
|
+
if (!inferred.complete) throw new LowerFailure(invalidDiagnostic(node.range));
|
|
1827
|
+
addMap(state.entries, path, "node", node.range);
|
|
1828
|
+
return {
|
|
1829
|
+
expression: KaladaV1.conditional(condition.expression, then.expression, otherwise.expression),
|
|
1830
|
+
type: inferred.type
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
function binaryExpression(operator, family, left, right) {
|
|
1834
|
+
if (family === "equality")
|
|
1835
|
+
return KaladaV1.equality(operator === "==" ? "equal" : "not-equal", left, right);
|
|
1836
|
+
if (family === "ordered-number" || family === "ordered-string")
|
|
1837
|
+
return KaladaV1.orderedComparison(
|
|
1838
|
+
family === "ordered-number" ? "number" : "string",
|
|
1839
|
+
comparisonOperator(operator),
|
|
1840
|
+
left,
|
|
1841
|
+
right
|
|
1842
|
+
);
|
|
1843
|
+
if (family === "temporal-comparison")
|
|
1844
|
+
return KaladaV1.temporalComparison(comparisonOperator(operator), left, right);
|
|
1845
|
+
return remainingBinaryExpression(operator, family, left, right);
|
|
1846
|
+
}
|
|
1847
|
+
function remainingBinaryExpression(operator, family, left, right) {
|
|
1848
|
+
if (family === "membership") return KaladaV1.membership(left, right);
|
|
1849
|
+
if (family === "numeric-binary")
|
|
1850
|
+
return KaladaV1.numericBinary(numericOperator(operator), left, right);
|
|
1851
|
+
if (family === "temporal-arithmetic")
|
|
1852
|
+
return KaladaV1.temporalArithmetic(operator === "+" ? "add" : "subtract", left, right);
|
|
1853
|
+
if (family === "boolean-logical")
|
|
1854
|
+
return KaladaV1.booleanLogical(operator === "&&" ? "and" : "or", left, right);
|
|
1855
|
+
if (family === "boolean-xor") return KaladaV1.booleanXor(left, right);
|
|
1856
|
+
return KaladaV1.optionCoalesce(left, right);
|
|
1857
|
+
}
|
|
1858
|
+
function comparisonOperator(operator) {
|
|
1859
|
+
const values = {
|
|
1860
|
+
"<": "less-than",
|
|
1861
|
+
"<=": "less-than-or-equal",
|
|
1862
|
+
">": "greater-than",
|
|
1863
|
+
">=": "greater-than-or-equal"
|
|
1864
|
+
};
|
|
1865
|
+
return values[operator];
|
|
1866
|
+
}
|
|
1867
|
+
function numericOperator(operator) {
|
|
1868
|
+
const values = {
|
|
1869
|
+
"+": "add",
|
|
1870
|
+
"-": "subtract",
|
|
1871
|
+
"*": "multiply",
|
|
1872
|
+
"/": "divide",
|
|
1873
|
+
"%": "remainder"
|
|
1874
|
+
};
|
|
1875
|
+
return values[operator];
|
|
1876
|
+
}
|
|
1877
|
+
function binaryKeys(operator) {
|
|
1878
|
+
if (operator === "in") return ["needle", "array"];
|
|
1879
|
+
if (operator === "??") return ["option", "fallback"];
|
|
1880
|
+
return ["left", "right"];
|
|
1881
|
+
}
|
|
1882
|
+
function operatorHasField(operator) {
|
|
1883
|
+
return !["in", "xor", "??"].includes(operator);
|
|
1884
|
+
}
|
|
1885
|
+
function dispatchFailure(error, path, state, fallback, unaryOperand) {
|
|
1886
|
+
if (!(error instanceof DispatchFailure)) return new LowerFailure(invalidDiagnostic(fallback));
|
|
1887
|
+
const key = unaryOperand ?? error.operand;
|
|
1888
|
+
const diagnosticPath = key === "operator" ? [...path, "operator"] : [...path, key];
|
|
1889
|
+
const range = rangeForPath(state.entries, diagnosticPath, fallback);
|
|
1890
|
+
return new LowerFailure(
|
|
1891
|
+
diagnostic("lower", error.code, CORE_MESSAGES[error.code], range, diagnosticPath)
|
|
1892
|
+
);
|
|
1893
|
+
}
|
|
1894
|
+
function coreFailure(problem, state) {
|
|
1895
|
+
const range = rangeForPath(state.entries, problem.path, state.parsed.document.expression.range);
|
|
1896
|
+
return new LowerFailure(diagnostic("lower", problem.code, problem.message, range, problem.path));
|
|
1897
|
+
}
|
|
1898
|
+
function syntaxFailure(code, range, path) {
|
|
1899
|
+
return new LowerFailure(
|
|
1900
|
+
diagnostic(
|
|
1901
|
+
"lower",
|
|
1902
|
+
code,
|
|
1903
|
+
"Kalada reference is not present in the lowering environment.",
|
|
1904
|
+
range,
|
|
1905
|
+
path
|
|
1906
|
+
)
|
|
1907
|
+
);
|
|
1908
|
+
}
|
|
1909
|
+
function invalidDiagnostic(range = freezeRange(0, 0)) {
|
|
1910
|
+
return diagnostic(
|
|
1911
|
+
"lower",
|
|
1912
|
+
"KALADA_SYNTAX_INVALID_INPUT",
|
|
1913
|
+
"Kalada syntax input is invalid.",
|
|
1914
|
+
range,
|
|
1915
|
+
[]
|
|
1916
|
+
);
|
|
1917
|
+
}
|
|
1918
|
+
function invalidOutcome(range) {
|
|
1919
|
+
return deepFreeze({ ok: false, diagnostics: [invalidDiagnostic(range)] });
|
|
1920
|
+
}
|
|
1921
|
+
function sourceRange(state, token2) {
|
|
1922
|
+
return state.parsed.document.tokens[token2]?.range ?? state.parsed.document.expression.range;
|
|
1923
|
+
}
|
|
1924
|
+
function inferenceFor(node, state) {
|
|
1925
|
+
return state.inferred.get(node) ?? { type: "dynamic", complete: false };
|
|
1926
|
+
}
|
|
1927
|
+
function requireDispatch(inferred, range) {
|
|
1928
|
+
if (!inferred.complete || !inferred.dispatch) throw new LowerFailure(invalidDiagnostic(range));
|
|
1929
|
+
return inferred.dispatch;
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
// src/semantic-query.ts
|
|
1933
|
+
function queryKaladaV1Semantics(parsed, options) {
|
|
1934
|
+
const limits = syntaxLimitsFor(parsed);
|
|
1935
|
+
if (limits === null) return invalidQuery();
|
|
1936
|
+
const configuration = safeConfiguration(options, limits);
|
|
1937
|
+
if (configuration === null) return invalidQuery(parsed.document.expression.range);
|
|
1938
|
+
const analysis = analyzeSemantics(parsed, configuration);
|
|
1939
|
+
const nodes = analysis.ordered.map(
|
|
1940
|
+
(node) => describeNode(node, analysis.inferred.get(node) ?? unknownInference())
|
|
1941
|
+
);
|
|
1942
|
+
return deepFreeze({
|
|
1943
|
+
nodes,
|
|
1944
|
+
diagnostics: analysis.diagnostics,
|
|
1945
|
+
incomplete: analysis.incomplete
|
|
1946
|
+
});
|
|
1947
|
+
}
|
|
1948
|
+
function safeConfiguration(options, limits) {
|
|
1949
|
+
try {
|
|
1950
|
+
return readLowerConfiguration(options, limits);
|
|
1951
|
+
} catch {
|
|
1952
|
+
return null;
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
function describeNode(node, inference) {
|
|
1956
|
+
const type = projectStaticType(inference.type);
|
|
1957
|
+
return {
|
|
1958
|
+
range: node.range,
|
|
1959
|
+
type,
|
|
1960
|
+
known: inference.complete && type !== "dynamic",
|
|
1961
|
+
fieldAccess: {
|
|
1962
|
+
plain: fieldSupport(inference, false),
|
|
1963
|
+
optional: fieldSupport(inference, true)
|
|
1964
|
+
},
|
|
1965
|
+
operators: BINARY_OPERATORS.map((operator) => ({
|
|
1966
|
+
operator,
|
|
1967
|
+
support: operatorSupport(operator, inference)
|
|
1968
|
+
}))
|
|
1969
|
+
};
|
|
1970
|
+
}
|
|
1971
|
+
function fieldSupport(inference, optional) {
|
|
1972
|
+
if (!inference.complete || inference.type === "dynamic") return "conditional";
|
|
1973
|
+
try {
|
|
1974
|
+
dispatchField(inference.type, optional);
|
|
1975
|
+
return "supported";
|
|
1976
|
+
} catch {
|
|
1977
|
+
return "unsupported";
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
function operatorSupport(operator, left) {
|
|
1981
|
+
if (!left.complete) return "conditional";
|
|
1982
|
+
return dispatchSupport(operator, left.type, "dynamic", false);
|
|
1983
|
+
}
|
|
1984
|
+
function dispatchSupport(operator, left, right, completeOperands) {
|
|
1985
|
+
try {
|
|
1986
|
+
dispatchBinary(operator, left, right);
|
|
1987
|
+
if (!completeOperands || containsDynamic(left) || containsDynamic(right)) return "conditional";
|
|
1988
|
+
return "supported";
|
|
1989
|
+
} catch {
|
|
1990
|
+
return left === "dynamic" ? "conditional" : "unsupported";
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
function containsDynamic(type) {
|
|
1994
|
+
return type === "dynamic" || "shape" in type && containsDynamic(type.value);
|
|
1995
|
+
}
|
|
1996
|
+
function unknownInference() {
|
|
1997
|
+
return { type: "dynamic", complete: false };
|
|
1998
|
+
}
|
|
1999
|
+
function invalidQuery(range = Object.freeze({ start: 0, end: 0 })) {
|
|
2000
|
+
return deepFreeze({
|
|
2001
|
+
nodes: [],
|
|
2002
|
+
diagnostics: [
|
|
2003
|
+
diagnostic(
|
|
2004
|
+
"lower",
|
|
2005
|
+
"KALADA_SYNTAX_INVALID_INPUT",
|
|
2006
|
+
"Kalada syntax input is invalid.",
|
|
2007
|
+
range,
|
|
2008
|
+
[]
|
|
2009
|
+
)
|
|
2010
|
+
],
|
|
2011
|
+
incomplete: true
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
export { DEFAULT_KALADA_SYNTAX_LIMITS, KALADA_SYNTAX_DIAGNOSTIC_MESSAGES, MAXIMUM_KALADA_SYNTAX_LIMITS, checkKaladaV1DirectLocation, parseGuestExpressionPrefix as experimentalParseKaladaV1GuestExpressionPrefix, formatKaladaV1Expression, lowerKaladaV1Expression, parseKaladaV1Expression, queryKaladaV1Semantics };
|
|
2016
|
+
//# sourceMappingURL=index.js.map
|
|
2017
|
+
//# sourceMappingURL=index.js.map
|