littlewing 2.2.0 → 2.3.2
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 +69 -60
- package/dist/index.d.ts +33 -70
- package/dist/index.js +989 -761
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ var __export = (target, all) => {
|
|
|
16
16
|
// src/ast.ts
|
|
17
17
|
var exports_ast = {};
|
|
18
18
|
__export(exports_ast, {
|
|
19
|
+
visit: () => visit,
|
|
19
20
|
unaryOp: () => unaryOp,
|
|
20
21
|
subtract: () => subtract,
|
|
21
22
|
string: () => string,
|
|
@@ -86,6 +87,43 @@ var NodeKind;
|
|
|
86
87
|
NodeKind2[NodeKind2["PipeExpression"] = 14] = "PipeExpression";
|
|
87
88
|
NodeKind2[NodeKind2["Placeholder"] = 15] = "Placeholder";
|
|
88
89
|
})(NodeKind ||= {});
|
|
90
|
+
function visit(node, visitor) {
|
|
91
|
+
const recurse = (child) => visit(child, visitor);
|
|
92
|
+
switch (node.kind) {
|
|
93
|
+
case 0 /* Program */:
|
|
94
|
+
return visitor.Program(node, recurse);
|
|
95
|
+
case 1 /* NumberLiteral */:
|
|
96
|
+
return visitor.NumberLiteral(node, recurse);
|
|
97
|
+
case 8 /* StringLiteral */:
|
|
98
|
+
return visitor.StringLiteral(node, recurse);
|
|
99
|
+
case 9 /* BooleanLiteral */:
|
|
100
|
+
return visitor.BooleanLiteral(node, recurse);
|
|
101
|
+
case 10 /* ArrayLiteral */:
|
|
102
|
+
return visitor.ArrayLiteral(node, recurse);
|
|
103
|
+
case 2 /* Identifier */:
|
|
104
|
+
return visitor.Identifier(node, recurse);
|
|
105
|
+
case 3 /* BinaryOp */:
|
|
106
|
+
return visitor.BinaryOp(node, recurse);
|
|
107
|
+
case 4 /* UnaryOp */:
|
|
108
|
+
return visitor.UnaryOp(node, recurse);
|
|
109
|
+
case 5 /* FunctionCall */:
|
|
110
|
+
return visitor.FunctionCall(node, recurse);
|
|
111
|
+
case 6 /* Assignment */:
|
|
112
|
+
return visitor.Assignment(node, recurse);
|
|
113
|
+
case 7 /* IfExpression */:
|
|
114
|
+
return visitor.IfExpression(node, recurse);
|
|
115
|
+
case 11 /* ForExpression */:
|
|
116
|
+
return visitor.ForExpression(node, recurse);
|
|
117
|
+
case 12 /* IndexAccess */:
|
|
118
|
+
return visitor.IndexAccess(node, recurse);
|
|
119
|
+
case 13 /* RangeExpression */:
|
|
120
|
+
return visitor.RangeExpression(node, recurse);
|
|
121
|
+
case 14 /* PipeExpression */:
|
|
122
|
+
return visitor.PipeExpression(node, recurse);
|
|
123
|
+
case 15 /* Placeholder */:
|
|
124
|
+
return visitor.Placeholder(node, recurse);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
89
127
|
function isProgram(node) {
|
|
90
128
|
return node.kind === 0 /* Program */;
|
|
91
129
|
}
|
|
@@ -143,8 +181,10 @@ function number(value) {
|
|
|
143
181
|
function string(value) {
|
|
144
182
|
return { kind: 8 /* StringLiteral */, value };
|
|
145
183
|
}
|
|
184
|
+
var TRUE_LITERAL = { kind: 9 /* BooleanLiteral */, value: true };
|
|
185
|
+
var FALSE_LITERAL = { kind: 9 /* BooleanLiteral */, value: false };
|
|
146
186
|
function boolean(value) {
|
|
147
|
-
return
|
|
187
|
+
return value ? TRUE_LITERAL : FALSE_LITERAL;
|
|
148
188
|
}
|
|
149
189
|
function array(elements) {
|
|
150
190
|
return { kind: 10 /* ArrayLiteral */, elements };
|
|
@@ -191,8 +231,9 @@ function rangeExpr(start, end, inclusive) {
|
|
|
191
231
|
function pipeExpr(value, name, args) {
|
|
192
232
|
return { kind: 14 /* PipeExpression */, value, name, args };
|
|
193
233
|
}
|
|
234
|
+
var PLACEHOLDER_SINGLETON = { kind: 15 /* Placeholder */ };
|
|
194
235
|
function placeholder() {
|
|
195
|
-
return
|
|
236
|
+
return PLACEHOLDER_SINGLETON;
|
|
196
237
|
}
|
|
197
238
|
function add(left, right) {
|
|
198
239
|
return binaryOp(left, "+", right);
|
|
@@ -281,50 +322,6 @@ function getNodeName(node) {
|
|
|
281
322
|
}
|
|
282
323
|
}
|
|
283
324
|
|
|
284
|
-
// src/visitor.ts
|
|
285
|
-
function visit(node, visitor) {
|
|
286
|
-
return visitPartial(node, visitor, (node2) => {
|
|
287
|
-
throw new Error(`No handler for node type: ${getNodeName(node2)}`);
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
function visitPartial(node, visitor, defaultHandler) {
|
|
291
|
-
const recurse = (n) => visitPartial(n, visitor, defaultHandler);
|
|
292
|
-
switch (node.kind) {
|
|
293
|
-
case 0 /* Program */:
|
|
294
|
-
return visitor.Program ? visitor.Program(node, recurse) : defaultHandler(node, recurse);
|
|
295
|
-
case 1 /* NumberLiteral */:
|
|
296
|
-
return visitor.NumberLiteral ? visitor.NumberLiteral(node, recurse) : defaultHandler(node, recurse);
|
|
297
|
-
case 8 /* StringLiteral */:
|
|
298
|
-
return visitor.StringLiteral ? visitor.StringLiteral(node, recurse) : defaultHandler(node, recurse);
|
|
299
|
-
case 9 /* BooleanLiteral */:
|
|
300
|
-
return visitor.BooleanLiteral ? visitor.BooleanLiteral(node, recurse) : defaultHandler(node, recurse);
|
|
301
|
-
case 10 /* ArrayLiteral */:
|
|
302
|
-
return visitor.ArrayLiteral ? visitor.ArrayLiteral(node, recurse) : defaultHandler(node, recurse);
|
|
303
|
-
case 2 /* Identifier */:
|
|
304
|
-
return visitor.Identifier ? visitor.Identifier(node, recurse) : defaultHandler(node, recurse);
|
|
305
|
-
case 3 /* BinaryOp */:
|
|
306
|
-
return visitor.BinaryOp ? visitor.BinaryOp(node, recurse) : defaultHandler(node, recurse);
|
|
307
|
-
case 4 /* UnaryOp */:
|
|
308
|
-
return visitor.UnaryOp ? visitor.UnaryOp(node, recurse) : defaultHandler(node, recurse);
|
|
309
|
-
case 5 /* FunctionCall */:
|
|
310
|
-
return visitor.FunctionCall ? visitor.FunctionCall(node, recurse) : defaultHandler(node, recurse);
|
|
311
|
-
case 6 /* Assignment */:
|
|
312
|
-
return visitor.Assignment ? visitor.Assignment(node, recurse) : defaultHandler(node, recurse);
|
|
313
|
-
case 7 /* IfExpression */:
|
|
314
|
-
return visitor.IfExpression ? visitor.IfExpression(node, recurse) : defaultHandler(node, recurse);
|
|
315
|
-
case 11 /* ForExpression */:
|
|
316
|
-
return visitor.ForExpression ? visitor.ForExpression(node, recurse) : defaultHandler(node, recurse);
|
|
317
|
-
case 12 /* IndexAccess */:
|
|
318
|
-
return visitor.IndexAccess ? visitor.IndexAccess(node, recurse) : defaultHandler(node, recurse);
|
|
319
|
-
case 13 /* RangeExpression */:
|
|
320
|
-
return visitor.RangeExpression ? visitor.RangeExpression(node, recurse) : defaultHandler(node, recurse);
|
|
321
|
-
case 14 /* PipeExpression */:
|
|
322
|
-
return visitor.PipeExpression ? visitor.PipeExpression(node, recurse) : defaultHandler(node, recurse);
|
|
323
|
-
case 15 /* Placeholder */:
|
|
324
|
-
return visitor.Placeholder ? visitor.Placeholder(node, recurse) : defaultHandler(node, recurse);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
325
|
// src/analyzer.ts
|
|
329
326
|
function extractInputVariables(ast) {
|
|
330
327
|
const inputVars = new Set;
|
|
@@ -341,74 +338,110 @@ function extractInputVariables(ast) {
|
|
|
341
338
|
function extractAssignedVariables(ast) {
|
|
342
339
|
const seen = new Set;
|
|
343
340
|
const names = [];
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
seen.add(n.name);
|
|
353
|
-
names.push(n.name);
|
|
341
|
+
collectAssignedVariables(ast, seen, names);
|
|
342
|
+
return names;
|
|
343
|
+
}
|
|
344
|
+
function collectAssignedVariables(node, seen, names) {
|
|
345
|
+
switch (node.kind) {
|
|
346
|
+
case 0 /* Program */:
|
|
347
|
+
for (const statement of node.statements) {
|
|
348
|
+
collectAssignedVariables(statement, seen, names);
|
|
354
349
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
recurse(n.alternate);
|
|
361
|
-
},
|
|
362
|
-
ForExpression: (n, recurse) => {
|
|
363
|
-
recurse(n.iterable);
|
|
364
|
-
if (n.guard)
|
|
365
|
-
recurse(n.guard);
|
|
366
|
-
if (n.accumulator)
|
|
367
|
-
recurse(n.accumulator.initial);
|
|
368
|
-
recurse(n.body);
|
|
369
|
-
},
|
|
370
|
-
IndexAccess: (n, recurse) => {
|
|
371
|
-
recurse(n.object);
|
|
372
|
-
recurse(n.index);
|
|
373
|
-
},
|
|
374
|
-
RangeExpression: (n, recurse) => {
|
|
375
|
-
recurse(n.start);
|
|
376
|
-
recurse(n.end);
|
|
377
|
-
},
|
|
378
|
-
PipeExpression: (n, recurse) => {
|
|
379
|
-
recurse(n.value);
|
|
380
|
-
for (const arg of n.args) {
|
|
381
|
-
recurse(arg);
|
|
350
|
+
break;
|
|
351
|
+
case 6 /* Assignment */:
|
|
352
|
+
if (!seen.has(node.name)) {
|
|
353
|
+
seen.add(node.name);
|
|
354
|
+
names.push(node.name);
|
|
382
355
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
356
|
+
collectAssignedVariables(node.value, seen, names);
|
|
357
|
+
break;
|
|
358
|
+
case 3 /* BinaryOp */:
|
|
359
|
+
collectAssignedVariables(node.left, seen, names);
|
|
360
|
+
collectAssignedVariables(node.right, seen, names);
|
|
361
|
+
break;
|
|
362
|
+
case 4 /* UnaryOp */:
|
|
363
|
+
collectAssignedVariables(node.argument, seen, names);
|
|
364
|
+
break;
|
|
365
|
+
case 10 /* ArrayLiteral */:
|
|
366
|
+
for (const e of node.elements)
|
|
367
|
+
collectAssignedVariables(e, seen, names);
|
|
368
|
+
break;
|
|
369
|
+
case 5 /* FunctionCall */:
|
|
370
|
+
for (const a of node.args)
|
|
371
|
+
collectAssignedVariables(a, seen, names);
|
|
372
|
+
break;
|
|
373
|
+
case 7 /* IfExpression */:
|
|
374
|
+
collectAssignedVariables(node.condition, seen, names);
|
|
375
|
+
collectAssignedVariables(node.consequent, seen, names);
|
|
376
|
+
collectAssignedVariables(node.alternate, seen, names);
|
|
377
|
+
break;
|
|
378
|
+
case 11 /* ForExpression */:
|
|
379
|
+
collectAssignedVariables(node.iterable, seen, names);
|
|
380
|
+
if (node.guard)
|
|
381
|
+
collectAssignedVariables(node.guard, seen, names);
|
|
382
|
+
if (node.accumulator)
|
|
383
|
+
collectAssignedVariables(node.accumulator.initial, seen, names);
|
|
384
|
+
collectAssignedVariables(node.body, seen, names);
|
|
385
|
+
break;
|
|
386
|
+
case 12 /* IndexAccess */:
|
|
387
|
+
collectAssignedVariables(node.object, seen, names);
|
|
388
|
+
collectAssignedVariables(node.index, seen, names);
|
|
389
|
+
break;
|
|
390
|
+
case 13 /* RangeExpression */:
|
|
391
|
+
collectAssignedVariables(node.start, seen, names);
|
|
392
|
+
collectAssignedVariables(node.end, seen, names);
|
|
393
|
+
break;
|
|
394
|
+
case 14 /* PipeExpression */:
|
|
395
|
+
collectAssignedVariables(node.value, seen, names);
|
|
396
|
+
for (const arg of node.args)
|
|
397
|
+
collectAssignedVariables(arg, seen, names);
|
|
398
|
+
break;
|
|
399
|
+
case 1 /* NumberLiteral */:
|
|
400
|
+
case 8 /* StringLiteral */:
|
|
401
|
+
case 9 /* BooleanLiteral */:
|
|
402
|
+
case 2 /* Identifier */:
|
|
403
|
+
case 15 /* Placeholder */:
|
|
404
|
+
break;
|
|
405
|
+
}
|
|
386
406
|
}
|
|
387
407
|
function containsExternalReference(node, boundVars) {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
408
|
+
const recurse = (n) => containsExternalReference(n, boundVars);
|
|
409
|
+
switch (node.kind) {
|
|
410
|
+
case 0 /* Program */:
|
|
411
|
+
return node.statements.some(recurse);
|
|
412
|
+
case 2 /* Identifier */:
|
|
413
|
+
return !boundVars.has(node.name);
|
|
414
|
+
case 10 /* ArrayLiteral */:
|
|
415
|
+
return node.elements.some(recurse);
|
|
416
|
+
case 3 /* BinaryOp */:
|
|
417
|
+
return recurse(node.left) || recurse(node.right);
|
|
418
|
+
case 4 /* UnaryOp */:
|
|
419
|
+
return recurse(node.argument);
|
|
420
|
+
case 5 /* FunctionCall */:
|
|
421
|
+
return node.args.some(recurse);
|
|
422
|
+
case 6 /* Assignment */:
|
|
423
|
+
return recurse(node.value);
|
|
424
|
+
case 7 /* IfExpression */:
|
|
425
|
+
return recurse(node.condition) || recurse(node.consequent) || recurse(node.alternate);
|
|
426
|
+
case 11 /* ForExpression */: {
|
|
401
427
|
const innerBound = new Set(boundVars);
|
|
402
|
-
innerBound.add(
|
|
403
|
-
if (
|
|
404
|
-
innerBound.add(
|
|
405
|
-
return recurse(
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
428
|
+
innerBound.add(node.variable);
|
|
429
|
+
if (node.accumulator)
|
|
430
|
+
innerBound.add(node.accumulator.name);
|
|
431
|
+
return recurse(node.iterable) || (node.guard ? containsExternalReference(node.guard, innerBound) : false) || (node.accumulator ? recurse(node.accumulator.initial) : false) || containsExternalReference(node.body, innerBound);
|
|
432
|
+
}
|
|
433
|
+
case 12 /* IndexAccess */:
|
|
434
|
+
return recurse(node.object) || recurse(node.index);
|
|
435
|
+
case 13 /* RangeExpression */:
|
|
436
|
+
return recurse(node.start) || recurse(node.end);
|
|
437
|
+
case 14 /* PipeExpression */:
|
|
438
|
+
return recurse(node.value) || node.args.some(recurse);
|
|
439
|
+
case 1 /* NumberLiteral */:
|
|
440
|
+
case 8 /* StringLiteral */:
|
|
441
|
+
case 9 /* BooleanLiteral */:
|
|
442
|
+
case 15 /* Placeholder */:
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
412
445
|
}
|
|
413
446
|
function containsVariableReference(node) {
|
|
414
447
|
return containsExternalReference(node, new Set);
|
|
@@ -707,21 +740,25 @@ function lexIdentifier(cursor) {
|
|
|
707
740
|
|
|
708
741
|
// src/utils.ts
|
|
709
742
|
function typeOf(value) {
|
|
743
|
+
if (value === undefined)
|
|
744
|
+
return "undefined";
|
|
745
|
+
if (value === null)
|
|
746
|
+
return "null";
|
|
710
747
|
if (typeof value === "number")
|
|
711
748
|
return "number";
|
|
712
749
|
if (typeof value === "string")
|
|
713
750
|
return "string";
|
|
714
751
|
if (typeof value === "boolean")
|
|
715
752
|
return "boolean";
|
|
753
|
+
if (Array.isArray(value))
|
|
754
|
+
return "array";
|
|
716
755
|
if (value instanceof Temporal.PlainDateTime)
|
|
717
756
|
return "datetime";
|
|
718
757
|
if (value instanceof Temporal.PlainDate)
|
|
719
758
|
return "date";
|
|
720
759
|
if (value instanceof Temporal.PlainTime)
|
|
721
760
|
return "time";
|
|
722
|
-
|
|
723
|
-
return "array";
|
|
724
|
-
throw new Error(`Unknown runtime value type`);
|
|
761
|
+
return typeof value;
|
|
725
762
|
}
|
|
726
763
|
function deepEquals(a, b) {
|
|
727
764
|
if (typeof a === "number" && typeof b === "number")
|
|
@@ -758,6 +795,13 @@ function assertNumber(v, context, side) {
|
|
|
758
795
|
throw new TypeError(`${context}${where} expected number, got ${typeOf(v)}`);
|
|
759
796
|
}
|
|
760
797
|
}
|
|
798
|
+
function assertInteger(v, context, side) {
|
|
799
|
+
assertNumber(v, context, side);
|
|
800
|
+
if (!Number.isInteger(v)) {
|
|
801
|
+
const where = side ? ` (${side})` : "";
|
|
802
|
+
throw new TypeError(`${context}${where} expected integer, got ${v}`);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
761
805
|
function assertBoolean(v, context, side) {
|
|
762
806
|
if (typeof v !== "boolean") {
|
|
763
807
|
const where = side ? ` (${side})` : "";
|
|
@@ -815,7 +859,17 @@ function concatenateArrays(a, b) {
|
|
|
815
859
|
function validateHomogeneousArray(elements) {
|
|
816
860
|
if (elements.length <= 1)
|
|
817
861
|
return;
|
|
818
|
-
const
|
|
862
|
+
const first = elements[0];
|
|
863
|
+
const firstPrimitive = typeof first;
|
|
864
|
+
if (firstPrimitive === "number" || firstPrimitive === "string" || firstPrimitive === "boolean") {
|
|
865
|
+
for (let i = 1;i < elements.length; i++) {
|
|
866
|
+
if (typeof elements[i] !== firstPrimitive) {
|
|
867
|
+
throw new TypeError(`Heterogeneous array: expected ${firstPrimitive}, got ${typeOf(elements[i])} at index ${i}`);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
const firstType = typeOf(first);
|
|
819
873
|
for (let i = 1;i < elements.length; i++) {
|
|
820
874
|
const elemType = typeOf(elements[i]);
|
|
821
875
|
if (elemType !== firstType) {
|
|
@@ -919,13 +973,29 @@ function resolveIndex(target, index) {
|
|
|
919
973
|
throw new TypeError(`Index must be an integer, got ${index}`);
|
|
920
974
|
}
|
|
921
975
|
if (typeof target === "string") {
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
976
|
+
if (index >= 0) {
|
|
977
|
+
let i2 = 0;
|
|
978
|
+
for (const cp of target) {
|
|
979
|
+
if (i2 === index)
|
|
980
|
+
return cp;
|
|
981
|
+
i2++;
|
|
982
|
+
}
|
|
983
|
+
throw new RangeError(`Index ${index} out of bounds for length ${i2}`);
|
|
984
|
+
}
|
|
985
|
+
let len2 = 0;
|
|
986
|
+
for (const _ of target)
|
|
987
|
+
len2++;
|
|
988
|
+
const resolved2 = len2 + index;
|
|
989
|
+
if (resolved2 < 0) {
|
|
926
990
|
throw new RangeError(`Index ${index} out of bounds for length ${len2}`);
|
|
927
991
|
}
|
|
928
|
-
|
|
992
|
+
let i = 0;
|
|
993
|
+
for (const cp of target) {
|
|
994
|
+
if (i === resolved2)
|
|
995
|
+
return cp;
|
|
996
|
+
i++;
|
|
997
|
+
}
|
|
998
|
+
throw new RangeError(`Index ${index} out of bounds for length ${len2}`);
|
|
929
999
|
}
|
|
930
1000
|
const len = target.length;
|
|
931
1001
|
const resolved = index < 0 ? len + index : index;
|
|
@@ -945,11 +1015,7 @@ function buildRange(start, end, inclusive) {
|
|
|
945
1015
|
throw new RangeError(`Range start (${start}) must not exceed end (${end})`);
|
|
946
1016
|
}
|
|
947
1017
|
const limit = inclusive ? end + 1 : end;
|
|
948
|
-
|
|
949
|
-
for (let i = start;i < limit; i++) {
|
|
950
|
-
result.push(i);
|
|
951
|
-
}
|
|
952
|
-
return result;
|
|
1018
|
+
return Array.from({ length: limit - start }, (_, i) => start + i);
|
|
953
1019
|
}
|
|
954
1020
|
function getOperatorPrecedence(operator) {
|
|
955
1021
|
switch (operator) {
|
|
@@ -979,69 +1045,75 @@ function getOperatorPrecedence(operator) {
|
|
|
979
1045
|
}
|
|
980
1046
|
function collectAllIdentifiers(node) {
|
|
981
1047
|
const identifiers = new Set;
|
|
982
|
-
|
|
983
|
-
Program: (n, recurse) => {
|
|
984
|
-
for (const stmt of n.statements) {
|
|
985
|
-
recurse(stmt);
|
|
986
|
-
}
|
|
987
|
-
},
|
|
988
|
-
NumberLiteral: () => {},
|
|
989
|
-
StringLiteral: () => {},
|
|
990
|
-
BooleanLiteral: () => {},
|
|
991
|
-
ArrayLiteral: (n, recurse) => {
|
|
992
|
-
for (const elem of n.elements) {
|
|
993
|
-
recurse(elem);
|
|
994
|
-
}
|
|
995
|
-
},
|
|
996
|
-
Identifier: (n) => {
|
|
997
|
-
identifiers.add(n.name);
|
|
998
|
-
},
|
|
999
|
-
BinaryOp: (n, recurse) => {
|
|
1000
|
-
recurse(n.left);
|
|
1001
|
-
recurse(n.right);
|
|
1002
|
-
},
|
|
1003
|
-
UnaryOp: (n, recurse) => {
|
|
1004
|
-
recurse(n.argument);
|
|
1005
|
-
},
|
|
1006
|
-
FunctionCall: (n, recurse) => {
|
|
1007
|
-
for (const arg of n.args) {
|
|
1008
|
-
recurse(arg);
|
|
1009
|
-
}
|
|
1010
|
-
},
|
|
1011
|
-
Assignment: (n, recurse) => {
|
|
1012
|
-
recurse(n.value);
|
|
1013
|
-
},
|
|
1014
|
-
IfExpression: (n, recurse) => {
|
|
1015
|
-
recurse(n.condition);
|
|
1016
|
-
recurse(n.consequent);
|
|
1017
|
-
recurse(n.alternate);
|
|
1018
|
-
},
|
|
1019
|
-
ForExpression: (n, recurse) => {
|
|
1020
|
-
recurse(n.iterable);
|
|
1021
|
-
if (n.guard)
|
|
1022
|
-
recurse(n.guard);
|
|
1023
|
-
if (n.accumulator)
|
|
1024
|
-
recurse(n.accumulator.initial);
|
|
1025
|
-
recurse(n.body);
|
|
1026
|
-
},
|
|
1027
|
-
IndexAccess: (n, recurse) => {
|
|
1028
|
-
recurse(n.object);
|
|
1029
|
-
recurse(n.index);
|
|
1030
|
-
},
|
|
1031
|
-
RangeExpression: (n, recurse) => {
|
|
1032
|
-
recurse(n.start);
|
|
1033
|
-
recurse(n.end);
|
|
1034
|
-
},
|
|
1035
|
-
PipeExpression: (n, recurse) => {
|
|
1036
|
-
recurse(n.value);
|
|
1037
|
-
for (const arg of n.args) {
|
|
1038
|
-
recurse(arg);
|
|
1039
|
-
}
|
|
1040
|
-
},
|
|
1041
|
-
Placeholder: () => {}
|
|
1042
|
-
});
|
|
1048
|
+
collectIdentifiers(node, identifiers, new Set);
|
|
1043
1049
|
return identifiers;
|
|
1044
1050
|
}
|
|
1051
|
+
function collectIdentifiers(node, ids, boundVars) {
|
|
1052
|
+
switch (node.kind) {
|
|
1053
|
+
case 0 /* Program */:
|
|
1054
|
+
for (const s of node.statements)
|
|
1055
|
+
collectIdentifiers(s, ids, boundVars);
|
|
1056
|
+
break;
|
|
1057
|
+
case 2 /* Identifier */:
|
|
1058
|
+
if (!boundVars.has(node.name))
|
|
1059
|
+
ids.add(node.name);
|
|
1060
|
+
break;
|
|
1061
|
+
case 10 /* ArrayLiteral */:
|
|
1062
|
+
for (const e of node.elements)
|
|
1063
|
+
collectIdentifiers(e, ids, boundVars);
|
|
1064
|
+
break;
|
|
1065
|
+
case 3 /* BinaryOp */:
|
|
1066
|
+
collectIdentifiers(node.left, ids, boundVars);
|
|
1067
|
+
collectIdentifiers(node.right, ids, boundVars);
|
|
1068
|
+
break;
|
|
1069
|
+
case 4 /* UnaryOp */:
|
|
1070
|
+
collectIdentifiers(node.argument, ids, boundVars);
|
|
1071
|
+
break;
|
|
1072
|
+
case 5 /* FunctionCall */:
|
|
1073
|
+
for (const a of node.args)
|
|
1074
|
+
collectIdentifiers(a, ids, boundVars);
|
|
1075
|
+
break;
|
|
1076
|
+
case 6 /* Assignment */:
|
|
1077
|
+
collectIdentifiers(node.value, ids, boundVars);
|
|
1078
|
+
break;
|
|
1079
|
+
case 7 /* IfExpression */:
|
|
1080
|
+
collectIdentifiers(node.condition, ids, boundVars);
|
|
1081
|
+
collectIdentifiers(node.consequent, ids, boundVars);
|
|
1082
|
+
collectIdentifiers(node.alternate, ids, boundVars);
|
|
1083
|
+
break;
|
|
1084
|
+
case 11 /* ForExpression */: {
|
|
1085
|
+
collectIdentifiers(node.iterable, ids, boundVars);
|
|
1086
|
+
if (node.accumulator)
|
|
1087
|
+
collectIdentifiers(node.accumulator.initial, ids, boundVars);
|
|
1088
|
+
const innerBound = new Set(boundVars);
|
|
1089
|
+
innerBound.add(node.variable);
|
|
1090
|
+
if (node.accumulator)
|
|
1091
|
+
innerBound.add(node.accumulator.name);
|
|
1092
|
+
if (node.guard)
|
|
1093
|
+
collectIdentifiers(node.guard, ids, innerBound);
|
|
1094
|
+
collectIdentifiers(node.body, ids, innerBound);
|
|
1095
|
+
break;
|
|
1096
|
+
}
|
|
1097
|
+
case 12 /* IndexAccess */:
|
|
1098
|
+
collectIdentifiers(node.object, ids, boundVars);
|
|
1099
|
+
collectIdentifiers(node.index, ids, boundVars);
|
|
1100
|
+
break;
|
|
1101
|
+
case 13 /* RangeExpression */:
|
|
1102
|
+
collectIdentifiers(node.start, ids, boundVars);
|
|
1103
|
+
collectIdentifiers(node.end, ids, boundVars);
|
|
1104
|
+
break;
|
|
1105
|
+
case 14 /* PipeExpression */:
|
|
1106
|
+
collectIdentifiers(node.value, ids, boundVars);
|
|
1107
|
+
for (const a of node.args)
|
|
1108
|
+
collectIdentifiers(a, ids, boundVars);
|
|
1109
|
+
break;
|
|
1110
|
+
case 1 /* NumberLiteral */:
|
|
1111
|
+
case 8 /* StringLiteral */:
|
|
1112
|
+
case 9 /* BooleanLiteral */:
|
|
1113
|
+
case 15 /* Placeholder */:
|
|
1114
|
+
break;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1045
1117
|
function getTokenPrecedence(kind) {
|
|
1046
1118
|
switch (kind) {
|
|
1047
1119
|
case 24 /* Eq */:
|
|
@@ -1139,90 +1211,88 @@ function generate(node) {
|
|
|
1139
1211
|
return code;
|
|
1140
1212
|
}
|
|
1141
1213
|
function generateNode(node) {
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
return
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
return
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
const
|
|
1161
|
-
const right = recurse(n.right);
|
|
1162
|
-
const opPrec = getOperatorPrecedence(n.operator);
|
|
1163
|
-
const leftNeedsParens = needsParens(n.left, n.operator, true) || n.operator === "^" && isUnaryOp(n.left) || isIfExpression(n.left) || isForExpression(n.left) || isAssignment(n.left) || isRangeExpression(n.left) && opPrec >= 7;
|
|
1214
|
+
const recurse = generateNode;
|
|
1215
|
+
switch (node.kind) {
|
|
1216
|
+
case 0 /* Program */:
|
|
1217
|
+
return generateProgram(node, recurse);
|
|
1218
|
+
case 1 /* NumberLiteral */:
|
|
1219
|
+
return String(node.value);
|
|
1220
|
+
case 8 /* StringLiteral */:
|
|
1221
|
+
return `"${escapeString(node.value)}"`;
|
|
1222
|
+
case 9 /* BooleanLiteral */:
|
|
1223
|
+
return node.value ? "true" : "false";
|
|
1224
|
+
case 10 /* ArrayLiteral */:
|
|
1225
|
+
return `[${node.elements.map(recurse).join(", ")}]`;
|
|
1226
|
+
case 2 /* Identifier */:
|
|
1227
|
+
return node.name;
|
|
1228
|
+
case 3 /* BinaryOp */: {
|
|
1229
|
+
const left = recurse(node.left);
|
|
1230
|
+
const right = recurse(node.right);
|
|
1231
|
+
const opPrec = getOperatorPrecedence(node.operator);
|
|
1232
|
+
const leftNeedsParens = needsParens(node.left, node.operator, true) || node.operator === "^" && isUnaryOp(node.left) || isIfExpression(node.left) || isForExpression(node.left) || isAssignment(node.left) || isRangeExpression(node.left) && opPrec >= 7;
|
|
1164
1233
|
const leftCode = leftNeedsParens ? `(${left})` : left;
|
|
1165
|
-
const rightNeedsParens = needsParens(
|
|
1234
|
+
const rightNeedsParens = needsParens(node.right, node.operator, false) || isPipeExpression(node.right) || isAssignment(node.right) || isRangeExpression(node.right) && opPrec >= 6;
|
|
1166
1235
|
const rightCode = rightNeedsParens ? `(${right})` : right;
|
|
1167
|
-
return `${leftCode} ${
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
const arg = recurse(
|
|
1171
|
-
const parensNeeded = isBinaryOp(
|
|
1236
|
+
return `${leftCode} ${node.operator} ${rightCode}`;
|
|
1237
|
+
}
|
|
1238
|
+
case 4 /* UnaryOp */: {
|
|
1239
|
+
const arg = recurse(node.argument);
|
|
1240
|
+
const parensNeeded = isBinaryOp(node.argument) || isAssignment(node.argument) || isPipeExpression(node.argument) || isRangeExpression(node.argument);
|
|
1172
1241
|
const argCode = parensNeeded ? `(${arg})` : arg;
|
|
1173
|
-
return `${
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
const argsCode =
|
|
1177
|
-
return `${
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
const value = recurse(
|
|
1181
|
-
return `${
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
const condition = recurse(
|
|
1185
|
-
const consequent = recurse(
|
|
1186
|
-
const alternate = recurse(
|
|
1242
|
+
return `${node.operator}${argCode}`;
|
|
1243
|
+
}
|
|
1244
|
+
case 5 /* FunctionCall */: {
|
|
1245
|
+
const argsCode = node.args.map(recurse).join(", ");
|
|
1246
|
+
return `${node.name}(${argsCode})`;
|
|
1247
|
+
}
|
|
1248
|
+
case 6 /* Assignment */: {
|
|
1249
|
+
const value = recurse(node.value);
|
|
1250
|
+
return `${node.name} = ${value}`;
|
|
1251
|
+
}
|
|
1252
|
+
case 7 /* IfExpression */: {
|
|
1253
|
+
const condition = recurse(node.condition);
|
|
1254
|
+
const consequent = recurse(node.consequent);
|
|
1255
|
+
const alternate = recurse(node.alternate);
|
|
1187
1256
|
return `if ${condition} then ${consequent} else ${alternate}`;
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
const parts = [`for ${
|
|
1191
|
-
if (
|
|
1192
|
-
parts.push(`when ${recurse(
|
|
1257
|
+
}
|
|
1258
|
+
case 11 /* ForExpression */: {
|
|
1259
|
+
const parts = [`for ${node.variable} in ${recurse(node.iterable)}`];
|
|
1260
|
+
if (node.guard) {
|
|
1261
|
+
parts.push(`when ${recurse(node.guard)}`);
|
|
1193
1262
|
}
|
|
1194
|
-
if (
|
|
1195
|
-
parts.push(`into ${
|
|
1263
|
+
if (node.accumulator) {
|
|
1264
|
+
parts.push(`into ${node.accumulator.name} = ${recurse(node.accumulator.initial)}`);
|
|
1196
1265
|
}
|
|
1197
|
-
parts.push(`then ${recurse(
|
|
1266
|
+
parts.push(`then ${recurse(node.body)}`);
|
|
1198
1267
|
return parts.join(" ");
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
|
-
const object = recurse(
|
|
1202
|
-
const index = recurse(
|
|
1203
|
-
const
|
|
1204
|
-
const objectCode =
|
|
1268
|
+
}
|
|
1269
|
+
case 12 /* IndexAccess */: {
|
|
1270
|
+
const object = recurse(node.object);
|
|
1271
|
+
const index = recurse(node.index);
|
|
1272
|
+
const objNeedsParens = isBinaryOp(node.object) || isUnaryOp(node.object) || isAssignment(node.object) || isRangeExpression(node.object) || isPipeExpression(node.object);
|
|
1273
|
+
const objectCode = objNeedsParens ? `(${object})` : object;
|
|
1205
1274
|
return `${objectCode}[${index}]`;
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
const start = recurse(
|
|
1209
|
-
const end = recurse(
|
|
1210
|
-
const op =
|
|
1211
|
-
const startNeedsParens = isBinaryOp(
|
|
1212
|
-
const endNeedsParens = isBinaryOp(
|
|
1275
|
+
}
|
|
1276
|
+
case 13 /* RangeExpression */: {
|
|
1277
|
+
const start = recurse(node.start);
|
|
1278
|
+
const end = recurse(node.end);
|
|
1279
|
+
const op = node.inclusive ? "..=" : "..";
|
|
1280
|
+
const startNeedsParens = isBinaryOp(node.start) || isRangeExpression(node.start) || isIfExpression(node.start) || isForExpression(node.start) || isAssignment(node.start);
|
|
1281
|
+
const endNeedsParens = isBinaryOp(node.end) || isRangeExpression(node.end) || isPipeExpression(node.end) || isAssignment(node.end);
|
|
1213
1282
|
const startCode = startNeedsParens ? `(${start})` : start;
|
|
1214
1283
|
const endCode = endNeedsParens ? `(${end})` : end;
|
|
1215
1284
|
return `${startCode}${op}${endCode}`;
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
const value = recurse(
|
|
1219
|
-
const argsCode =
|
|
1220
|
-
const valueNeedsParens = isAssignment(
|
|
1285
|
+
}
|
|
1286
|
+
case 14 /* PipeExpression */: {
|
|
1287
|
+
const value = recurse(node.value);
|
|
1288
|
+
const argsCode = node.args.map(recurse).join(", ");
|
|
1289
|
+
const valueNeedsParens = isAssignment(node.value) || isIfExpression(node.value) || isForExpression(node.value);
|
|
1221
1290
|
const valueCode = valueNeedsParens ? `(${value})` : value;
|
|
1222
|
-
return `${valueCode} |> ${
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1291
|
+
return `${valueCode} |> ${node.name}(${argsCode})`;
|
|
1292
|
+
}
|
|
1293
|
+
case 15 /* Placeholder */:
|
|
1294
|
+
return "?";
|
|
1295
|
+
}
|
|
1226
1296
|
}
|
|
1227
1297
|
// src/parser.ts
|
|
1228
1298
|
var BINARY_OPERATOR_TOKENS = new Set([
|
|
@@ -1612,198 +1682,224 @@ function advance2(state) {
|
|
|
1612
1682
|
}
|
|
1613
1683
|
|
|
1614
1684
|
// src/interpreter.ts
|
|
1615
|
-
function
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
}
|
|
1637
|
-
return value;
|
|
1638
|
-
},
|
|
1639
|
-
BinaryOp: (n, recurse) => {
|
|
1640
|
-
if (n.operator === "&&") {
|
|
1641
|
-
const left2 = recurse(n.left);
|
|
1642
|
-
assertBoolean(left2, "Operator '&&'", "left");
|
|
1643
|
-
if (!left2)
|
|
1644
|
-
return false;
|
|
1645
|
-
const right2 = recurse(n.right);
|
|
1646
|
-
assertBoolean(right2, "Operator '&&'", "right");
|
|
1647
|
-
return right2;
|
|
1648
|
-
}
|
|
1649
|
-
if (n.operator === "||") {
|
|
1650
|
-
const left2 = recurse(n.left);
|
|
1651
|
-
assertBoolean(left2, "Operator '||'", "left");
|
|
1652
|
-
if (left2)
|
|
1653
|
-
return true;
|
|
1654
|
-
const right2 = recurse(n.right);
|
|
1655
|
-
assertBoolean(right2, "Operator '||'", "right");
|
|
1656
|
-
return right2;
|
|
1685
|
+
function run(input, context = {}) {
|
|
1686
|
+
const node = typeof input === "string" ? parse(input) : input;
|
|
1687
|
+
const variables = new Map;
|
|
1688
|
+
const externalVariables = new Set;
|
|
1689
|
+
const vars = context.variables;
|
|
1690
|
+
if (vars) {
|
|
1691
|
+
for (const key of Object.keys(vars)) {
|
|
1692
|
+
variables.set(key, vars[key]);
|
|
1693
|
+
externalVariables.add(key);
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
const value = evalNode(node);
|
|
1697
|
+
return { value, variables };
|
|
1698
|
+
function evalNode(node2) {
|
|
1699
|
+
switch (node2.kind) {
|
|
1700
|
+
case 0 /* Program */: {
|
|
1701
|
+
let result = 0;
|
|
1702
|
+
for (const statement of node2.statements) {
|
|
1703
|
+
result = evalNode(statement);
|
|
1704
|
+
}
|
|
1705
|
+
return result;
|
|
1657
1706
|
}
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1707
|
+
case 1 /* NumberLiteral */:
|
|
1708
|
+
return node2.value;
|
|
1709
|
+
case 8 /* StringLiteral */:
|
|
1710
|
+
return node2.value;
|
|
1711
|
+
case 9 /* BooleanLiteral */:
|
|
1712
|
+
return node2.value;
|
|
1713
|
+
case 10 /* ArrayLiteral */: {
|
|
1714
|
+
const elems = node2.elements;
|
|
1715
|
+
const elements = [];
|
|
1716
|
+
for (let i = 0;i < elems.length; i++) {
|
|
1717
|
+
elements.push(evalNode(elems[i]));
|
|
1718
|
+
}
|
|
1719
|
+
validateHomogeneousArray(elements);
|
|
1720
|
+
return elements;
|
|
1667
1721
|
}
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1722
|
+
case 2 /* Identifier */: {
|
|
1723
|
+
const value2 = variables.get(node2.name);
|
|
1724
|
+
if (value2 === undefined) {
|
|
1725
|
+
throw new Error(`Undefined variable: ${node2.name}`);
|
|
1726
|
+
}
|
|
1727
|
+
return value2;
|
|
1671
1728
|
}
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1729
|
+
case 3 /* BinaryOp */: {
|
|
1730
|
+
if (node2.operator === "&&") {
|
|
1731
|
+
const left = evalNode(node2.left);
|
|
1732
|
+
assertBoolean(left, "Operator '&&'", "left");
|
|
1733
|
+
if (!left)
|
|
1734
|
+
return false;
|
|
1735
|
+
const right = evalNode(node2.right);
|
|
1736
|
+
assertBoolean(right, "Operator '&&'", "right");
|
|
1737
|
+
return right;
|
|
1738
|
+
}
|
|
1739
|
+
if (node2.operator === "||") {
|
|
1740
|
+
const left = evalNode(node2.left);
|
|
1741
|
+
assertBoolean(left, "Operator '||'", "left");
|
|
1742
|
+
if (left)
|
|
1743
|
+
return true;
|
|
1744
|
+
const right = evalNode(node2.right);
|
|
1745
|
+
assertBoolean(right, "Operator '||'", "right");
|
|
1746
|
+
return right;
|
|
1747
|
+
}
|
|
1748
|
+
return evaluateBinaryOperation(node2.operator, evalNode(node2.left), evalNode(node2.right));
|
|
1678
1749
|
}
|
|
1679
|
-
|
|
1680
|
-
|
|
1750
|
+
case 4 /* UnaryOp */: {
|
|
1751
|
+
const arg = evalNode(node2.argument);
|
|
1752
|
+
if (node2.operator === "-") {
|
|
1753
|
+
assertNumber(arg, "Operator '-' (unary)");
|
|
1754
|
+
return -arg;
|
|
1755
|
+
}
|
|
1756
|
+
if (node2.operator === "!") {
|
|
1757
|
+
assertBoolean(arg, "Operator '!'");
|
|
1758
|
+
return !arg;
|
|
1759
|
+
}
|
|
1760
|
+
throw new Error(`Unknown unary operator: ${String(node2.operator)}`);
|
|
1681
1761
|
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1762
|
+
case 5 /* FunctionCall */: {
|
|
1763
|
+
const fn = context.functions?.[node2.name];
|
|
1764
|
+
if (fn === undefined) {
|
|
1765
|
+
throw new Error(`Undefined function: ${node2.name}`);
|
|
1766
|
+
}
|
|
1767
|
+
if (typeof fn !== "function") {
|
|
1768
|
+
throw new Error(`${node2.name} is not a function`);
|
|
1769
|
+
}
|
|
1770
|
+
const nodeArgs = node2.args;
|
|
1771
|
+
const evaluatedArgs = [];
|
|
1772
|
+
for (let i = 0;i < nodeArgs.length; i++) {
|
|
1773
|
+
evaluatedArgs.push(evalNode(nodeArgs[i]));
|
|
1691
1774
|
}
|
|
1775
|
+
return fn(...evaluatedArgs);
|
|
1692
1776
|
}
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
const index = recurse(n.index);
|
|
1704
|
-
if (Array.isArray(object)) {
|
|
1705
|
-
return resolveIndex(object, index);
|
|
1777
|
+
case 6 /* Assignment */: {
|
|
1778
|
+
const value2 = evalNode(node2.value);
|
|
1779
|
+
if (externalVariables.has(node2.name)) {
|
|
1780
|
+
const externalValue = variables.get(node2.name);
|
|
1781
|
+
if (externalValue !== undefined) {
|
|
1782
|
+
return externalValue;
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
variables.set(node2.name, value2);
|
|
1786
|
+
return value2;
|
|
1706
1787
|
}
|
|
1707
|
-
|
|
1708
|
-
|
|
1788
|
+
case 7 /* IfExpression */: {
|
|
1789
|
+
const condition = evalNode(node2.condition);
|
|
1790
|
+
assertBoolean(condition, "If condition");
|
|
1791
|
+
return condition ? evalNode(node2.consequent) : evalNode(node2.alternate);
|
|
1709
1792
|
}
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
const pipedValue = recurse(n.value);
|
|
1721
|
-
const fn = context.functions?.[n.name];
|
|
1722
|
-
if (fn === undefined) {
|
|
1723
|
-
throw new Error(`Undefined function: ${n.name}`);
|
|
1793
|
+
case 12 /* IndexAccess */: {
|
|
1794
|
+
const object = evalNode(node2.object);
|
|
1795
|
+
const index = evalNode(node2.index);
|
|
1796
|
+
if (Array.isArray(object)) {
|
|
1797
|
+
return resolveIndex(object, index);
|
|
1798
|
+
}
|
|
1799
|
+
if (typeof object === "string") {
|
|
1800
|
+
return resolveIndex(object, index);
|
|
1801
|
+
}
|
|
1802
|
+
throw new TypeError(`Index access expected array or string, got ${typeOf(object)}`);
|
|
1724
1803
|
}
|
|
1725
|
-
|
|
1726
|
-
|
|
1804
|
+
case 13 /* RangeExpression */: {
|
|
1805
|
+
const start = evalNode(node2.start);
|
|
1806
|
+
const end = evalNode(node2.end);
|
|
1807
|
+
assertNumber(start, "Range start");
|
|
1808
|
+
assertNumber(end, "Range end");
|
|
1809
|
+
return buildRange(start, end, node2.inclusive);
|
|
1727
1810
|
}
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1811
|
+
case 14 /* PipeExpression */: {
|
|
1812
|
+
const pipedValue = evalNode(node2.value);
|
|
1813
|
+
const fn = context.functions?.[node2.name];
|
|
1814
|
+
if (fn === undefined) {
|
|
1815
|
+
throw new Error(`Undefined function: ${node2.name}`);
|
|
1816
|
+
}
|
|
1817
|
+
if (typeof fn !== "function") {
|
|
1818
|
+
throw new Error(`${node2.name} is not a function`);
|
|
1819
|
+
}
|
|
1820
|
+
const pipeArgs = node2.args;
|
|
1821
|
+
const evaluatedArgs = [];
|
|
1822
|
+
for (let i = 0;i < pipeArgs.length; i++) {
|
|
1823
|
+
const arg = pipeArgs[i];
|
|
1824
|
+
evaluatedArgs.push(arg.kind === 15 /* Placeholder */ ? pipedValue : evalNode(arg));
|
|
1825
|
+
}
|
|
1826
|
+
return fn(...evaluatedArgs);
|
|
1743
1827
|
}
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1828
|
+
case 15 /* Placeholder */:
|
|
1829
|
+
throw new Error("Placeholder outside pipe expression");
|
|
1830
|
+
case 11 /* ForExpression */: {
|
|
1831
|
+
const iterable = evalNode(node2.iterable);
|
|
1832
|
+
let iterTarget;
|
|
1833
|
+
if (Array.isArray(iterable)) {
|
|
1834
|
+
iterTarget = iterable;
|
|
1835
|
+
} else if (typeof iterable === "string") {
|
|
1836
|
+
iterTarget = iterable;
|
|
1749
1837
|
} else {
|
|
1750
|
-
|
|
1838
|
+
throw new TypeError(`For expression expected array or string, got ${typeOf(iterable)}`);
|
|
1751
1839
|
}
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1840
|
+
const previousValue = variables.get(node2.variable);
|
|
1841
|
+
const hadPreviousValue = variables.has(node2.variable);
|
|
1842
|
+
if (node2.accumulator) {
|
|
1843
|
+
let acc = evalNode(node2.accumulator.initial);
|
|
1844
|
+
const prevAcc = variables.get(node2.accumulator.name);
|
|
1845
|
+
const hadPrevAcc = variables.has(node2.accumulator.name);
|
|
1846
|
+
for (const item of iterTarget) {
|
|
1847
|
+
variables.set(node2.variable, item);
|
|
1848
|
+
if (node2.guard) {
|
|
1849
|
+
const guardValue = evalNode(node2.guard);
|
|
1850
|
+
assertBoolean(guardValue, "For guard");
|
|
1851
|
+
if (!guardValue)
|
|
1852
|
+
continue;
|
|
1853
|
+
}
|
|
1854
|
+
variables.set(node2.accumulator.name, acc);
|
|
1855
|
+
acc = evalNode(node2.body);
|
|
1856
|
+
}
|
|
1857
|
+
if (hadPrevAcc) {
|
|
1858
|
+
variables.set(node2.accumulator.name, prevAcc);
|
|
1859
|
+
} else {
|
|
1860
|
+
variables.delete(node2.accumulator.name);
|
|
1861
|
+
}
|
|
1862
|
+
if (hadPreviousValue) {
|
|
1863
|
+
variables.set(node2.variable, previousValue);
|
|
1864
|
+
} else {
|
|
1865
|
+
variables.delete(node2.variable);
|
|
1866
|
+
}
|
|
1867
|
+
return acc;
|
|
1868
|
+
}
|
|
1869
|
+
const result = [];
|
|
1870
|
+
for (const item of iterTarget) {
|
|
1871
|
+
variables.set(node2.variable, item);
|
|
1872
|
+
if (node2.guard) {
|
|
1873
|
+
const guardValue = evalNode(node2.guard);
|
|
1761
1874
|
assertBoolean(guardValue, "For guard");
|
|
1762
1875
|
if (!guardValue)
|
|
1763
1876
|
continue;
|
|
1764
1877
|
}
|
|
1765
|
-
|
|
1766
|
-
acc = recurse(n.body);
|
|
1878
|
+
result.push(evalNode(node2.body));
|
|
1767
1879
|
}
|
|
1768
|
-
if (
|
|
1769
|
-
variables.set(
|
|
1880
|
+
if (hadPreviousValue) {
|
|
1881
|
+
variables.set(node2.variable, previousValue);
|
|
1770
1882
|
} else {
|
|
1771
|
-
variables.delete(
|
|
1883
|
+
variables.delete(node2.variable);
|
|
1772
1884
|
}
|
|
1773
|
-
|
|
1774
|
-
return
|
|
1885
|
+
validateHomogeneousArray(result);
|
|
1886
|
+
return result;
|
|
1775
1887
|
}
|
|
1776
|
-
const result = [];
|
|
1777
|
-
for (const item of items) {
|
|
1778
|
-
variables.set(n.variable, item);
|
|
1779
|
-
if (n.guard) {
|
|
1780
|
-
const guardValue = recurse(n.guard);
|
|
1781
|
-
assertBoolean(guardValue, "For guard");
|
|
1782
|
-
if (!guardValue)
|
|
1783
|
-
continue;
|
|
1784
|
-
}
|
|
1785
|
-
result.push(recurse(n.body));
|
|
1786
|
-
}
|
|
1787
|
-
restoreLoopVar();
|
|
1788
|
-
validateHomogeneousArray(result);
|
|
1789
|
-
return result;
|
|
1790
1888
|
}
|
|
1791
|
-
}
|
|
1792
|
-
}
|
|
1793
|
-
function run(input, context = {}) {
|
|
1794
|
-
const node = typeof input === "string" ? parse(input) : input;
|
|
1795
|
-
const entries = Object.entries(context.variables || {});
|
|
1796
|
-
const variables = new Map(entries);
|
|
1797
|
-
const externalVariables = new Set(entries.map(([key]) => key));
|
|
1798
|
-
const value = evaluateNode(node, context, variables, externalVariables);
|
|
1799
|
-
return { value, variables };
|
|
1889
|
+
}
|
|
1800
1890
|
}
|
|
1801
1891
|
function evaluate(input, context = {}) {
|
|
1802
1892
|
return run(input, context).value;
|
|
1803
1893
|
}
|
|
1804
1894
|
function evaluateScope(input, context = {}) {
|
|
1805
|
-
|
|
1806
|
-
|
|
1895
|
+
return evaluateWithScope(input, context).scope;
|
|
1896
|
+
}
|
|
1897
|
+
function evaluateWithScope(input, context = {}) {
|
|
1898
|
+
const { value, variables } = run(input, context);
|
|
1899
|
+
return {
|
|
1900
|
+
value,
|
|
1901
|
+
scope: Object.fromEntries(variables)
|
|
1902
|
+
};
|
|
1807
1903
|
}
|
|
1808
1904
|
// src/optimizer.ts
|
|
1809
1905
|
function preserveComments(original, replacement) {
|
|
@@ -1869,24 +1965,34 @@ function isLiteral(node) {
|
|
|
1869
1965
|
return isNumberLiteral(node) || isStringLiteral(node) || isBooleanLiteral(node);
|
|
1870
1966
|
}
|
|
1871
1967
|
function mightHaveSideEffects(node) {
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1968
|
+
switch (node.kind) {
|
|
1969
|
+
case 0 /* Program */:
|
|
1970
|
+
return node.statements.some(mightHaveSideEffects);
|
|
1971
|
+
case 10 /* ArrayLiteral */:
|
|
1972
|
+
return node.elements.some(mightHaveSideEffects);
|
|
1973
|
+
case 3 /* BinaryOp */:
|
|
1974
|
+
return mightHaveSideEffects(node.left) || mightHaveSideEffects(node.right);
|
|
1975
|
+
case 4 /* UnaryOp */:
|
|
1976
|
+
return mightHaveSideEffects(node.argument);
|
|
1977
|
+
case 7 /* IfExpression */:
|
|
1978
|
+
return mightHaveSideEffects(node.condition) || mightHaveSideEffects(node.consequent) || mightHaveSideEffects(node.alternate);
|
|
1979
|
+
case 11 /* ForExpression */:
|
|
1980
|
+
return mightHaveSideEffects(node.iterable) || node.guard !== null && mightHaveSideEffects(node.guard) || node.accumulator !== null && mightHaveSideEffects(node.accumulator.initial) || mightHaveSideEffects(node.body);
|
|
1981
|
+
case 12 /* IndexAccess */:
|
|
1982
|
+
return mightHaveSideEffects(node.object) || mightHaveSideEffects(node.index);
|
|
1983
|
+
case 13 /* RangeExpression */:
|
|
1984
|
+
return mightHaveSideEffects(node.start) || mightHaveSideEffects(node.end);
|
|
1985
|
+
case 5 /* FunctionCall */:
|
|
1986
|
+
case 14 /* PipeExpression */:
|
|
1987
|
+
case 6 /* Assignment */:
|
|
1988
|
+
return true;
|
|
1989
|
+
case 1 /* NumberLiteral */:
|
|
1990
|
+
case 8 /* StringLiteral */:
|
|
1991
|
+
case 9 /* BooleanLiteral */:
|
|
1992
|
+
case 2 /* Identifier */:
|
|
1993
|
+
case 15 /* Placeholder */:
|
|
1994
|
+
return false;
|
|
1995
|
+
}
|
|
1890
1996
|
}
|
|
1891
1997
|
function propagateConstants(program2, externalVariables) {
|
|
1892
1998
|
const statements = program2.statements;
|
|
@@ -1917,10 +2023,9 @@ function propagateConstants(program2, externalVariables) {
|
|
|
1917
2023
|
return null;
|
|
1918
2024
|
const referencedIds = new Set;
|
|
1919
2025
|
for (const stmt of statements) {
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
collectReferencedIdentifiers(stmt, referencedIds);
|
|
2026
|
+
const identifiers = collectAllIdentifiers(isAssignment(stmt) ? stmt.value : stmt);
|
|
2027
|
+
for (const id of identifiers) {
|
|
2028
|
+
referencedIds.add(id);
|
|
1924
2029
|
}
|
|
1925
2030
|
}
|
|
1926
2031
|
let hasSubstitution = false;
|
|
@@ -1938,221 +2043,230 @@ function propagateConstants(program2, externalVariables) {
|
|
|
1938
2043
|
}
|
|
1939
2044
|
return result;
|
|
1940
2045
|
}
|
|
1941
|
-
function collectReferencedIdentifiers(node, ids) {
|
|
1942
|
-
visit(node, {
|
|
1943
|
-
Program: (n, recurse) => {
|
|
1944
|
-
for (const s of n.statements)
|
|
1945
|
-
recurse(s);
|
|
1946
|
-
},
|
|
1947
|
-
NumberLiteral: () => {},
|
|
1948
|
-
StringLiteral: () => {},
|
|
1949
|
-
BooleanLiteral: () => {},
|
|
1950
|
-
ArrayLiteral: (n, recurse) => {
|
|
1951
|
-
for (const e of n.elements)
|
|
1952
|
-
recurse(e);
|
|
1953
|
-
},
|
|
1954
|
-
Identifier: (n) => {
|
|
1955
|
-
ids.add(n.name);
|
|
1956
|
-
},
|
|
1957
|
-
BinaryOp: (n, recurse) => {
|
|
1958
|
-
recurse(n.left);
|
|
1959
|
-
recurse(n.right);
|
|
1960
|
-
},
|
|
1961
|
-
UnaryOp: (n, recurse) => {
|
|
1962
|
-
recurse(n.argument);
|
|
1963
|
-
},
|
|
1964
|
-
FunctionCall: (n, recurse) => {
|
|
1965
|
-
for (const a of n.args)
|
|
1966
|
-
recurse(a);
|
|
1967
|
-
},
|
|
1968
|
-
Assignment: (n, recurse) => {
|
|
1969
|
-
recurse(n.value);
|
|
1970
|
-
},
|
|
1971
|
-
IfExpression: (n, recurse) => {
|
|
1972
|
-
recurse(n.condition);
|
|
1973
|
-
recurse(n.consequent);
|
|
1974
|
-
recurse(n.alternate);
|
|
1975
|
-
},
|
|
1976
|
-
ForExpression: (n, recurse) => {
|
|
1977
|
-
recurse(n.iterable);
|
|
1978
|
-
if (n.guard)
|
|
1979
|
-
recurse(n.guard);
|
|
1980
|
-
if (n.accumulator)
|
|
1981
|
-
recurse(n.accumulator.initial);
|
|
1982
|
-
recurse(n.body);
|
|
1983
|
-
},
|
|
1984
|
-
IndexAccess: (n, recurse) => {
|
|
1985
|
-
recurse(n.object);
|
|
1986
|
-
recurse(n.index);
|
|
1987
|
-
},
|
|
1988
|
-
RangeExpression: (n, recurse) => {
|
|
1989
|
-
recurse(n.start);
|
|
1990
|
-
recurse(n.end);
|
|
1991
|
-
},
|
|
1992
|
-
PipeExpression: (n, recurse) => {
|
|
1993
|
-
recurse(n.value);
|
|
1994
|
-
for (const a of n.args)
|
|
1995
|
-
recurse(a);
|
|
1996
|
-
},
|
|
1997
|
-
Placeholder: () => {}
|
|
1998
|
-
});
|
|
1999
|
-
}
|
|
2000
2046
|
function collectForLoopVars(node, vars) {
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
for (const s of
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2047
|
+
switch (node.kind) {
|
|
2048
|
+
case 0 /* Program */:
|
|
2049
|
+
for (const s of node.statements)
|
|
2050
|
+
collectForLoopVars(s, vars);
|
|
2051
|
+
break;
|
|
2052
|
+
case 10 /* ArrayLiteral */:
|
|
2053
|
+
for (const e of node.elements)
|
|
2054
|
+
collectForLoopVars(e, vars);
|
|
2055
|
+
break;
|
|
2056
|
+
case 3 /* BinaryOp */:
|
|
2057
|
+
collectForLoopVars(node.left, vars);
|
|
2058
|
+
collectForLoopVars(node.right, vars);
|
|
2059
|
+
break;
|
|
2060
|
+
case 4 /* UnaryOp */:
|
|
2061
|
+
collectForLoopVars(node.argument, vars);
|
|
2062
|
+
break;
|
|
2063
|
+
case 5 /* FunctionCall */:
|
|
2064
|
+
for (const a of node.args)
|
|
2065
|
+
collectForLoopVars(a, vars);
|
|
2066
|
+
break;
|
|
2067
|
+
case 6 /* Assignment */:
|
|
2068
|
+
collectForLoopVars(node.value, vars);
|
|
2069
|
+
break;
|
|
2070
|
+
case 7 /* IfExpression */:
|
|
2071
|
+
collectForLoopVars(node.condition, vars);
|
|
2072
|
+
collectForLoopVars(node.consequent, vars);
|
|
2073
|
+
collectForLoopVars(node.alternate, vars);
|
|
2074
|
+
break;
|
|
2075
|
+
case 11 /* ForExpression */:
|
|
2076
|
+
vars.add(node.variable);
|
|
2077
|
+
if (node.accumulator)
|
|
2078
|
+
vars.add(node.accumulator.name);
|
|
2079
|
+
collectForLoopVars(node.iterable, vars);
|
|
2080
|
+
if (node.guard)
|
|
2081
|
+
collectForLoopVars(node.guard, vars);
|
|
2082
|
+
if (node.accumulator)
|
|
2083
|
+
collectForLoopVars(node.accumulator.initial, vars);
|
|
2084
|
+
collectForLoopVars(node.body, vars);
|
|
2085
|
+
break;
|
|
2086
|
+
case 12 /* IndexAccess */:
|
|
2087
|
+
collectForLoopVars(node.object, vars);
|
|
2088
|
+
collectForLoopVars(node.index, vars);
|
|
2089
|
+
break;
|
|
2090
|
+
case 13 /* RangeExpression */:
|
|
2091
|
+
collectForLoopVars(node.start, vars);
|
|
2092
|
+
collectForLoopVars(node.end, vars);
|
|
2093
|
+
break;
|
|
2094
|
+
case 14 /* PipeExpression */:
|
|
2095
|
+
collectForLoopVars(node.value, vars);
|
|
2096
|
+
for (const a of node.args)
|
|
2097
|
+
collectForLoopVars(a, vars);
|
|
2098
|
+
break;
|
|
2099
|
+
case 1 /* NumberLiteral */:
|
|
2100
|
+
case 8 /* StringLiteral */:
|
|
2101
|
+
case 9 /* BooleanLiteral */:
|
|
2102
|
+
case 2 /* Identifier */:
|
|
2103
|
+
case 15 /* Placeholder */:
|
|
2104
|
+
break;
|
|
2105
|
+
}
|
|
2059
2106
|
}
|
|
2060
2107
|
function countAssignments(node, counts) {
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
for (const s of
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
for (const a of
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2108
|
+
switch (node.kind) {
|
|
2109
|
+
case 0 /* Program */:
|
|
2110
|
+
for (const s of node.statements)
|
|
2111
|
+
countAssignments(s, counts);
|
|
2112
|
+
break;
|
|
2113
|
+
case 6 /* Assignment */:
|
|
2114
|
+
counts.set(node.name, (counts.get(node.name) ?? 0) + 1);
|
|
2115
|
+
countAssignments(node.value, counts);
|
|
2116
|
+
break;
|
|
2117
|
+
case 10 /* ArrayLiteral */:
|
|
2118
|
+
for (const e of node.elements)
|
|
2119
|
+
countAssignments(e, counts);
|
|
2120
|
+
break;
|
|
2121
|
+
case 3 /* BinaryOp */:
|
|
2122
|
+
countAssignments(node.left, counts);
|
|
2123
|
+
countAssignments(node.right, counts);
|
|
2124
|
+
break;
|
|
2125
|
+
case 4 /* UnaryOp */:
|
|
2126
|
+
countAssignments(node.argument, counts);
|
|
2127
|
+
break;
|
|
2128
|
+
case 5 /* FunctionCall */:
|
|
2129
|
+
for (const a of node.args)
|
|
2130
|
+
countAssignments(a, counts);
|
|
2131
|
+
break;
|
|
2132
|
+
case 7 /* IfExpression */:
|
|
2133
|
+
countAssignments(node.condition, counts);
|
|
2134
|
+
countAssignments(node.consequent, counts);
|
|
2135
|
+
countAssignments(node.alternate, counts);
|
|
2136
|
+
break;
|
|
2137
|
+
case 11 /* ForExpression */:
|
|
2138
|
+
countAssignments(node.iterable, counts);
|
|
2139
|
+
if (node.guard)
|
|
2140
|
+
countAssignments(node.guard, counts);
|
|
2141
|
+
if (node.accumulator)
|
|
2142
|
+
countAssignments(node.accumulator.initial, counts);
|
|
2143
|
+
countAssignments(node.body, counts);
|
|
2144
|
+
break;
|
|
2145
|
+
case 12 /* IndexAccess */:
|
|
2146
|
+
countAssignments(node.object, counts);
|
|
2147
|
+
countAssignments(node.index, counts);
|
|
2148
|
+
break;
|
|
2149
|
+
case 13 /* RangeExpression */:
|
|
2150
|
+
countAssignments(node.start, counts);
|
|
2151
|
+
countAssignments(node.end, counts);
|
|
2152
|
+
break;
|
|
2153
|
+
case 14 /* PipeExpression */:
|
|
2154
|
+
countAssignments(node.value, counts);
|
|
2155
|
+
for (const a of node.args)
|
|
2156
|
+
countAssignments(a, counts);
|
|
2157
|
+
break;
|
|
2158
|
+
case 1 /* NumberLiteral */:
|
|
2159
|
+
case 8 /* StringLiteral */:
|
|
2160
|
+
case 9 /* BooleanLiteral */:
|
|
2161
|
+
case 2 /* Identifier */:
|
|
2162
|
+
case 15 /* Placeholder */:
|
|
2163
|
+
break;
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
function unchangedArray(original, mapped) {
|
|
2167
|
+
for (let i = 0;i < original.length; i++) {
|
|
2168
|
+
if (original[i] !== mapped[i])
|
|
2169
|
+
return false;
|
|
2170
|
+
}
|
|
2171
|
+
return true;
|
|
2117
2172
|
}
|
|
2118
2173
|
function substituteIdentifiers(node, knownValues) {
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2174
|
+
const recurse = (n) => substituteIdentifiers(n, knownValues);
|
|
2175
|
+
switch (node.kind) {
|
|
2176
|
+
case 0 /* Program */: {
|
|
2177
|
+
const stmts = node.statements.map(recurse);
|
|
2178
|
+
if (unchangedArray(node.statements, stmts))
|
|
2179
|
+
return node;
|
|
2180
|
+
const result = program(stmts);
|
|
2181
|
+
if (node.trailingComments && node.trailingComments.length > 0) {
|
|
2182
|
+
return { ...result, trailingComments: node.trailingComments };
|
|
2124
2183
|
}
|
|
2125
2184
|
return result;
|
|
2126
|
-
}
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2185
|
+
}
|
|
2186
|
+
case 1 /* NumberLiteral */:
|
|
2187
|
+
case 8 /* StringLiteral */:
|
|
2188
|
+
case 9 /* BooleanLiteral */:
|
|
2189
|
+
case 15 /* Placeholder */:
|
|
2190
|
+
return node;
|
|
2191
|
+
case 10 /* ArrayLiteral */: {
|
|
2192
|
+
const elements = node.elements.map(recurse);
|
|
2193
|
+
if (unchangedArray(node.elements, elements))
|
|
2194
|
+
return node;
|
|
2195
|
+
return preserveComments(node, array(elements));
|
|
2196
|
+
}
|
|
2197
|
+
case 2 /* Identifier */: {
|
|
2198
|
+
const replacement = knownValues.get(node.name);
|
|
2199
|
+
return replacement ? preserveComments(node, replacement) : node;
|
|
2200
|
+
}
|
|
2201
|
+
case 3 /* BinaryOp */: {
|
|
2202
|
+
const left = recurse(node.left);
|
|
2203
|
+
const right = recurse(node.right);
|
|
2204
|
+
if (left === node.left && right === node.right)
|
|
2205
|
+
return node;
|
|
2206
|
+
return preserveComments(node, binaryOp(left, node.operator, right));
|
|
2207
|
+
}
|
|
2208
|
+
case 4 /* UnaryOp */: {
|
|
2209
|
+
const argument = recurse(node.argument);
|
|
2210
|
+
if (argument === node.argument)
|
|
2211
|
+
return node;
|
|
2212
|
+
return preserveComments(node, unaryOp(node.operator, argument));
|
|
2213
|
+
}
|
|
2214
|
+
case 5 /* FunctionCall */: {
|
|
2215
|
+
const args = node.args.map(recurse);
|
|
2216
|
+
if (unchangedArray(node.args, args))
|
|
2217
|
+
return node;
|
|
2218
|
+
return preserveComments(node, functionCall(node.name, args));
|
|
2219
|
+
}
|
|
2220
|
+
case 6 /* Assignment */: {
|
|
2221
|
+
const value = recurse(node.value);
|
|
2222
|
+
if (value === node.value)
|
|
2223
|
+
return node;
|
|
2224
|
+
return preserveComments(node, assign(node.name, value));
|
|
2225
|
+
}
|
|
2226
|
+
case 7 /* IfExpression */: {
|
|
2227
|
+
const condition = recurse(node.condition);
|
|
2228
|
+
const consequent = recurse(node.consequent);
|
|
2229
|
+
const alternate = recurse(node.alternate);
|
|
2230
|
+
if (condition === node.condition && consequent === node.consequent && alternate === node.alternate)
|
|
2231
|
+
return node;
|
|
2232
|
+
return preserveComments(node, ifExpr(condition, consequent, alternate));
|
|
2233
|
+
}
|
|
2234
|
+
case 11 /* ForExpression */: {
|
|
2141
2235
|
const innerKnown = new Map(knownValues);
|
|
2142
|
-
innerKnown.delete(
|
|
2143
|
-
if (
|
|
2144
|
-
innerKnown.delete(
|
|
2145
|
-
const iterable = recurse(
|
|
2146
|
-
const guard =
|
|
2147
|
-
const
|
|
2148
|
-
const body = substituteIdentifiers(
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2236
|
+
innerKnown.delete(node.variable);
|
|
2237
|
+
if (node.accumulator)
|
|
2238
|
+
innerKnown.delete(node.accumulator.name);
|
|
2239
|
+
const iterable = recurse(node.iterable);
|
|
2240
|
+
const guard = node.guard ? substituteIdentifiers(node.guard, innerKnown) : null;
|
|
2241
|
+
const initial = node.accumulator ? recurse(node.accumulator.initial) : null;
|
|
2242
|
+
const body = substituteIdentifiers(node.body, innerKnown);
|
|
2243
|
+
if (iterable === node.iterable && guard === node.guard && (node.accumulator === null || initial === node.accumulator.initial) && body === node.body)
|
|
2244
|
+
return node;
|
|
2245
|
+
const accumulator = node.accumulator ? { name: node.accumulator.name, initial } : null;
|
|
2246
|
+
return preserveComments(node, forExpr(node.variable, iterable, guard, accumulator, body));
|
|
2247
|
+
}
|
|
2248
|
+
case 12 /* IndexAccess */: {
|
|
2249
|
+
const object = recurse(node.object);
|
|
2250
|
+
const index = recurse(node.index);
|
|
2251
|
+
if (object === node.object && index === node.index)
|
|
2252
|
+
return node;
|
|
2253
|
+
return preserveComments(node, indexAccess(object, index));
|
|
2254
|
+
}
|
|
2255
|
+
case 13 /* RangeExpression */: {
|
|
2256
|
+
const start = recurse(node.start);
|
|
2257
|
+
const end = recurse(node.end);
|
|
2258
|
+
if (start === node.start && end === node.end)
|
|
2259
|
+
return node;
|
|
2260
|
+
return preserveComments(node, rangeExpr(start, end, node.inclusive));
|
|
2261
|
+
}
|
|
2262
|
+
case 14 /* PipeExpression */: {
|
|
2263
|
+
const value = recurse(node.value);
|
|
2264
|
+
const args = node.args.map(recurse);
|
|
2265
|
+
if (value === node.value && unchangedArray(node.args, args))
|
|
2266
|
+
return node;
|
|
2267
|
+
return preserveComments(node, pipeExpr(value, node.name, args));
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2156
2270
|
}
|
|
2157
2271
|
function optimize(node, externalVariables) {
|
|
2158
2272
|
let propagated = fold(node);
|
|
@@ -2179,142 +2293,188 @@ function optimize(node, externalVariables) {
|
|
|
2179
2293
|
return propagated;
|
|
2180
2294
|
}
|
|
2181
2295
|
function fold(node) {
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2296
|
+
const recurse = fold;
|
|
2297
|
+
switch (node.kind) {
|
|
2298
|
+
case 1 /* NumberLiteral */:
|
|
2299
|
+
case 8 /* StringLiteral */:
|
|
2300
|
+
case 9 /* BooleanLiteral */:
|
|
2301
|
+
case 2 /* Identifier */:
|
|
2302
|
+
case 15 /* Placeholder */:
|
|
2303
|
+
return node;
|
|
2304
|
+
case 0 /* Program */: {
|
|
2305
|
+
const optimizedStatements = node.statements.map(recurse);
|
|
2306
|
+
if (unchangedArray(node.statements, optimizedStatements))
|
|
2307
|
+
return node;
|
|
2308
|
+
const result = program(optimizedStatements);
|
|
2309
|
+
if (node.trailingComments && node.trailingComments.length > 0) {
|
|
2310
|
+
return { ...result, trailingComments: node.trailingComments };
|
|
2311
|
+
}
|
|
2312
|
+
return result;
|
|
2313
|
+
}
|
|
2314
|
+
case 10 /* ArrayLiteral */: {
|
|
2315
|
+
const elements = node.elements.map(recurse);
|
|
2316
|
+
if (unchangedArray(node.elements, elements))
|
|
2317
|
+
return node;
|
|
2318
|
+
return preserveComments(node, array(elements));
|
|
2319
|
+
}
|
|
2320
|
+
case 3 /* BinaryOp */: {
|
|
2321
|
+
const left = recurse(node.left);
|
|
2322
|
+
if (node.operator === "&&") {
|
|
2323
|
+
if (isBooleanLiteral(left) && !left.value) {
|
|
2324
|
+
return preserveComments(node, boolean(false));
|
|
2325
|
+
}
|
|
2326
|
+
const right2 = recurse(node.right);
|
|
2327
|
+
if (isBooleanLiteral(left) && isBooleanLiteral(right2)) {
|
|
2328
|
+
return preserveComments(node, boolean(right2.value));
|
|
2329
|
+
}
|
|
2330
|
+
if (left === node.left && right2 === node.right)
|
|
2331
|
+
return node;
|
|
2332
|
+
return preserveComments(node, binaryOp(left, node.operator, right2));
|
|
2333
|
+
}
|
|
2334
|
+
if (node.operator === "||") {
|
|
2335
|
+
if (isBooleanLiteral(left) && left.value) {
|
|
2336
|
+
return preserveComments(node, boolean(true));
|
|
2337
|
+
}
|
|
2338
|
+
const right2 = recurse(node.right);
|
|
2339
|
+
if (isBooleanLiteral(left) && isBooleanLiteral(right2)) {
|
|
2340
|
+
return preserveComments(node, boolean(right2.value));
|
|
2341
|
+
}
|
|
2342
|
+
if (left === node.left && right2 === node.right)
|
|
2343
|
+
return node;
|
|
2344
|
+
return preserveComments(node, binaryOp(left, node.operator, right2));
|
|
2345
|
+
}
|
|
2346
|
+
const right = recurse(node.right);
|
|
2194
2347
|
if (isNumberLiteral(left) && isNumberLiteral(right)) {
|
|
2195
|
-
const result = evaluateBinaryOperation(
|
|
2348
|
+
const result = evaluateBinaryOperation(node.operator, left.value, right.value);
|
|
2196
2349
|
if (typeof result === "number")
|
|
2197
|
-
return preserveComments(
|
|
2350
|
+
return preserveComments(node, number(result));
|
|
2198
2351
|
if (typeof result === "boolean")
|
|
2199
|
-
return preserveComments(
|
|
2352
|
+
return preserveComments(node, boolean(result));
|
|
2200
2353
|
}
|
|
2201
2354
|
if (isStringLiteral(left) && isStringLiteral(right)) {
|
|
2202
|
-
if (
|
|
2203
|
-
return preserveComments(
|
|
2204
|
-
if (
|
|
2205
|
-
const result = evaluateBinaryOperation(
|
|
2206
|
-
return preserveComments(
|
|
2355
|
+
if (node.operator === "+")
|
|
2356
|
+
return preserveComments(node, string(left.value + right.value));
|
|
2357
|
+
if (node.operator === "<" || node.operator === ">" || node.operator === "<=" || node.operator === ">=") {
|
|
2358
|
+
const result = evaluateBinaryOperation(node.operator, left.value, right.value);
|
|
2359
|
+
return preserveComments(node, boolean(result));
|
|
2207
2360
|
}
|
|
2208
|
-
if (
|
|
2209
|
-
return preserveComments(
|
|
2210
|
-
if (
|
|
2211
|
-
return preserveComments(
|
|
2361
|
+
if (node.operator === "==")
|
|
2362
|
+
return preserveComments(node, boolean(left.value === right.value));
|
|
2363
|
+
if (node.operator === "!=")
|
|
2364
|
+
return preserveComments(node, boolean(left.value !== right.value));
|
|
2212
2365
|
}
|
|
2213
2366
|
if (isBooleanLiteral(left) && isBooleanLiteral(right)) {
|
|
2214
|
-
if (
|
|
2215
|
-
return preserveComments(
|
|
2216
|
-
if (
|
|
2217
|
-
return preserveComments(
|
|
2218
|
-
if (n.operator === "==")
|
|
2219
|
-
return preserveComments(n, boolean(left.value === right.value));
|
|
2220
|
-
if (n.operator === "!=")
|
|
2221
|
-
return preserveComments(n, boolean(left.value !== right.value));
|
|
2367
|
+
if (node.operator === "==")
|
|
2368
|
+
return preserveComments(node, boolean(left.value === right.value));
|
|
2369
|
+
if (node.operator === "!=")
|
|
2370
|
+
return preserveComments(node, boolean(left.value !== right.value));
|
|
2222
2371
|
}
|
|
2223
2372
|
if (isLiteral(left) && isLiteral(right)) {
|
|
2224
2373
|
if (left.kind !== right.kind) {
|
|
2225
|
-
if (
|
|
2226
|
-
return preserveComments(
|
|
2227
|
-
if (
|
|
2228
|
-
return preserveComments(
|
|
2374
|
+
if (node.operator === "==")
|
|
2375
|
+
return preserveComments(node, boolean(false));
|
|
2376
|
+
if (node.operator === "!=")
|
|
2377
|
+
return preserveComments(node, boolean(true));
|
|
2229
2378
|
}
|
|
2230
2379
|
}
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2380
|
+
if (left === node.left && right === node.right)
|
|
2381
|
+
return node;
|
|
2382
|
+
return preserveComments(node, binaryOp(left, node.operator, right));
|
|
2383
|
+
}
|
|
2384
|
+
case 4 /* UnaryOp */: {
|
|
2385
|
+
const argument = recurse(node.argument);
|
|
2386
|
+
if (node.operator === "-" && isNumberLiteral(argument)) {
|
|
2387
|
+
return preserveComments(node, number(-argument.value));
|
|
2237
2388
|
}
|
|
2238
|
-
if (
|
|
2239
|
-
return preserveComments(
|
|
2389
|
+
if (node.operator === "!" && isBooleanLiteral(argument)) {
|
|
2390
|
+
return preserveComments(node, boolean(!argument.value));
|
|
2240
2391
|
}
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2392
|
+
if (argument === node.argument)
|
|
2393
|
+
return node;
|
|
2394
|
+
return preserveComments(node, unaryOp(node.operator, argument));
|
|
2395
|
+
}
|
|
2396
|
+
case 5 /* FunctionCall */: {
|
|
2397
|
+
const optimizedArgs = node.args.map(recurse);
|
|
2398
|
+
if (unchangedArray(node.args, optimizedArgs))
|
|
2399
|
+
return node;
|
|
2400
|
+
return preserveComments(node, functionCall(node.name, optimizedArgs));
|
|
2401
|
+
}
|
|
2402
|
+
case 6 /* Assignment */: {
|
|
2403
|
+
const value = recurse(node.value);
|
|
2404
|
+
if (value === node.value)
|
|
2405
|
+
return node;
|
|
2406
|
+
return preserveComments(node, assign(node.name, value));
|
|
2407
|
+
}
|
|
2408
|
+
case 7 /* IfExpression */: {
|
|
2409
|
+
const condition = recurse(node.condition);
|
|
2252
2410
|
if (isBooleanLiteral(condition)) {
|
|
2253
|
-
const result = condition.value ? recurse(
|
|
2254
|
-
return preserveComments(
|
|
2411
|
+
const result = condition.value ? recurse(node.consequent) : recurse(node.alternate);
|
|
2412
|
+
return preserveComments(node, result);
|
|
2255
2413
|
}
|
|
2256
|
-
const consequent = recurse(
|
|
2257
|
-
const alternate = recurse(
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
const
|
|
2264
|
-
const
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
const
|
|
2414
|
+
const consequent = recurse(node.consequent);
|
|
2415
|
+
const alternate = recurse(node.alternate);
|
|
2416
|
+
if (condition === node.condition && consequent === node.consequent && alternate === node.alternate)
|
|
2417
|
+
return node;
|
|
2418
|
+
return preserveComments(node, ifExpr(condition, consequent, alternate));
|
|
2419
|
+
}
|
|
2420
|
+
case 11 /* ForExpression */: {
|
|
2421
|
+
const iterable = recurse(node.iterable);
|
|
2422
|
+
const guard = node.guard ? recurse(node.guard) : null;
|
|
2423
|
+
const initial = node.accumulator ? recurse(node.accumulator.initial) : null;
|
|
2424
|
+
const body = recurse(node.body);
|
|
2425
|
+
if (iterable === node.iterable && guard === node.guard && (node.accumulator === null || initial === node.accumulator.initial) && body === node.body)
|
|
2426
|
+
return node;
|
|
2427
|
+
const accumulator = node.accumulator ? { name: node.accumulator.name, initial } : null;
|
|
2428
|
+
return preserveComments(node, forExpr(node.variable, iterable, guard, accumulator, body));
|
|
2429
|
+
}
|
|
2430
|
+
case 12 /* IndexAccess */: {
|
|
2431
|
+
const object = recurse(node.object);
|
|
2432
|
+
const index = recurse(node.index);
|
|
2270
2433
|
if (isArrayLiteral(object) && isNumberLiteral(index)) {
|
|
2271
2434
|
const idx = index.value;
|
|
2272
2435
|
if (Number.isInteger(idx)) {
|
|
2273
2436
|
const len = object.elements.length;
|
|
2274
2437
|
const resolved = idx < 0 ? len + idx : idx;
|
|
2275
2438
|
if (resolved >= 0 && resolved < len) {
|
|
2276
|
-
return preserveComments(
|
|
2439
|
+
return preserveComments(node, object.elements[resolved]);
|
|
2277
2440
|
}
|
|
2278
2441
|
}
|
|
2279
2442
|
}
|
|
2280
2443
|
if (isStringLiteral(object) && isNumberLiteral(index)) {
|
|
2281
2444
|
try {
|
|
2282
2445
|
const char = resolveIndex(object.value, index.value);
|
|
2283
|
-
return preserveComments(
|
|
2446
|
+
return preserveComments(node, string(char));
|
|
2284
2447
|
} catch {}
|
|
2285
2448
|
}
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2449
|
+
if (object === node.object && index === node.index)
|
|
2450
|
+
return node;
|
|
2451
|
+
return preserveComments(node, indexAccess(object, index));
|
|
2452
|
+
}
|
|
2453
|
+
case 13 /* RangeExpression */: {
|
|
2454
|
+
const start = recurse(node.start);
|
|
2455
|
+
const end = recurse(node.end);
|
|
2291
2456
|
if (isNumberLiteral(start) && isNumberLiteral(end)) {
|
|
2292
2457
|
try {
|
|
2293
|
-
const limit =
|
|
2458
|
+
const limit = node.inclusive ? end.value + 1 : end.value;
|
|
2294
2459
|
const count = limit - start.value;
|
|
2295
2460
|
if (count >= 0 && count <= 1e4) {
|
|
2296
|
-
const values = buildRange(start.value, end.value,
|
|
2297
|
-
return preserveComments(
|
|
2461
|
+
const values = buildRange(start.value, end.value, node.inclusive);
|
|
2462
|
+
return preserveComments(node, array(values.map(number)));
|
|
2298
2463
|
}
|
|
2299
2464
|
} catch {}
|
|
2300
2465
|
}
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
const value = recurse(n.value);
|
|
2305
|
-
const optimizedArgs = n.args.map(recurse);
|
|
2306
|
-
return preserveComments(n, pipeExpr(value, n.name, optimizedArgs));
|
|
2307
|
-
},
|
|
2308
|
-
Placeholder: (n) => n,
|
|
2309
|
-
Program: (n, recurse) => {
|
|
2310
|
-
const optimizedStatements = n.statements.map(recurse);
|
|
2311
|
-
const result = program(optimizedStatements);
|
|
2312
|
-
if (n.trailingComments && n.trailingComments.length > 0) {
|
|
2313
|
-
return { ...result, trailingComments: n.trailingComments };
|
|
2314
|
-
}
|
|
2315
|
-
return result;
|
|
2466
|
+
if (start === node.start && end === node.end)
|
|
2467
|
+
return node;
|
|
2468
|
+
return preserveComments(node, rangeExpr(start, end, node.inclusive));
|
|
2316
2469
|
}
|
|
2317
|
-
|
|
2470
|
+
case 14 /* PipeExpression */: {
|
|
2471
|
+
const value = recurse(node.value);
|
|
2472
|
+
const optimizedArgs = node.args.map(recurse);
|
|
2473
|
+
if (value === node.value && unchangedArray(node.args, optimizedArgs))
|
|
2474
|
+
return node;
|
|
2475
|
+
return preserveComments(node, pipeExpr(value, node.name, optimizedArgs));
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2318
2478
|
}
|
|
2319
2479
|
// src/stdlib/array.ts
|
|
2320
2480
|
var exports_array = {};
|
|
@@ -2330,7 +2490,7 @@ __export(exports_array, {
|
|
|
2330
2490
|
var ARR_SORT = (a) => {
|
|
2331
2491
|
assertArray(a, "ARR_SORT");
|
|
2332
2492
|
if (a.length <= 1)
|
|
2333
|
-
return a;
|
|
2493
|
+
return [...a];
|
|
2334
2494
|
const sorted = [...a];
|
|
2335
2495
|
const elemType = typeOf(sorted[0]);
|
|
2336
2496
|
sorted.sort((x, y) => {
|
|
@@ -2499,8 +2659,63 @@ function assertStringOrArray(v, context) {
|
|
|
2499
2659
|
throw new TypeError(`${context} expected string or array, got ${typeOf(v)}`);
|
|
2500
2660
|
}
|
|
2501
2661
|
}
|
|
2662
|
+
function hasSurrogateCodeUnit(value) {
|
|
2663
|
+
for (let i = 0;i < value.length; i++) {
|
|
2664
|
+
const code = value.charCodeAt(i);
|
|
2665
|
+
if (code >= 55296 && code <= 57343)
|
|
2666
|
+
return true;
|
|
2667
|
+
}
|
|
2668
|
+
return false;
|
|
2669
|
+
}
|
|
2670
|
+
function codePointLength(value) {
|
|
2671
|
+
let length = 0;
|
|
2672
|
+
for (let i = 0;i < value.length; i++) {
|
|
2673
|
+
const code = value.charCodeAt(i);
|
|
2674
|
+
if (code >= 55296 && code <= 56319 && i + 1 < value.length) {
|
|
2675
|
+
const next = value.charCodeAt(i + 1);
|
|
2676
|
+
if (next >= 56320 && next <= 57343)
|
|
2677
|
+
i++;
|
|
2678
|
+
}
|
|
2679
|
+
length++;
|
|
2680
|
+
}
|
|
2681
|
+
return length;
|
|
2682
|
+
}
|
|
2683
|
+
function stringLength(value) {
|
|
2684
|
+
return hasSurrogateCodeUnit(value) ? codePointLength(value) : value.length;
|
|
2685
|
+
}
|
|
2686
|
+
function sliceString(value, start, end) {
|
|
2687
|
+
if (!hasSurrogateCodeUnit(value))
|
|
2688
|
+
return value.slice(start, end);
|
|
2689
|
+
return Array.from(value).slice(start, end).join("");
|
|
2690
|
+
}
|
|
2691
|
+
function indexOfString(value, search) {
|
|
2692
|
+
if (search === "")
|
|
2693
|
+
return 0;
|
|
2694
|
+
if (!hasSurrogateCodeUnit(value) && !hasSurrogateCodeUnit(search)) {
|
|
2695
|
+
return value.indexOf(search);
|
|
2696
|
+
}
|
|
2697
|
+
const haystack = Array.from(value);
|
|
2698
|
+
const needle = Array.from(search);
|
|
2699
|
+
if (needle.length > haystack.length)
|
|
2700
|
+
return -1;
|
|
2701
|
+
const limit = haystack.length - needle.length;
|
|
2702
|
+
for (let i = 0;i <= limit; i++) {
|
|
2703
|
+
let found = true;
|
|
2704
|
+
for (let j = 0;j < needle.length; j++) {
|
|
2705
|
+
if (haystack[i + j] !== needle[j]) {
|
|
2706
|
+
found = false;
|
|
2707
|
+
break;
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
if (found)
|
|
2711
|
+
return i;
|
|
2712
|
+
}
|
|
2713
|
+
return -1;
|
|
2714
|
+
}
|
|
2502
2715
|
var LEN = (v) => {
|
|
2503
2716
|
assertStringOrArray(v, "LEN");
|
|
2717
|
+
if (typeof v === "string")
|
|
2718
|
+
return stringLength(v);
|
|
2504
2719
|
return v.length;
|
|
2505
2720
|
};
|
|
2506
2721
|
var SLICE = (v, start, end) => {
|
|
@@ -2508,8 +2723,12 @@ var SLICE = (v, start, end) => {
|
|
|
2508
2723
|
assertNumber(start, "SLICE", "start");
|
|
2509
2724
|
if (end !== undefined) {
|
|
2510
2725
|
assertNumber(end, "SLICE", "end");
|
|
2726
|
+
if (typeof v === "string")
|
|
2727
|
+
return sliceString(v, start, end);
|
|
2511
2728
|
return v.slice(start, end);
|
|
2512
2729
|
}
|
|
2730
|
+
if (typeof v === "string")
|
|
2731
|
+
return sliceString(v, start);
|
|
2513
2732
|
return v.slice(start);
|
|
2514
2733
|
};
|
|
2515
2734
|
var CONTAINS = (v, search) => {
|
|
@@ -2535,7 +2754,7 @@ var INDEX_OF = (v, search) => {
|
|
|
2535
2754
|
assertStringOrArray(v, "INDEX_OF");
|
|
2536
2755
|
if (typeof v === "string") {
|
|
2537
2756
|
assertString(search, "INDEX_OF (search)");
|
|
2538
|
-
return v
|
|
2757
|
+
return indexOfString(v, search);
|
|
2539
2758
|
}
|
|
2540
2759
|
for (let i = 0;i < v.length; i++) {
|
|
2541
2760
|
if (deepEquals(v[i], search))
|
|
@@ -2575,9 +2794,9 @@ __export(exports_datetime, {
|
|
|
2575
2794
|
});
|
|
2576
2795
|
var TODAY = () => Temporal.Now.plainDateISO();
|
|
2577
2796
|
var DATE = (year, month, day) => {
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2797
|
+
assertInteger(year, "DATE", "year");
|
|
2798
|
+
assertInteger(month, "DATE", "month");
|
|
2799
|
+
assertInteger(day, "DATE", "day");
|
|
2581
2800
|
return new Temporal.PlainDate(year, month, day);
|
|
2582
2801
|
};
|
|
2583
2802
|
var YEAR = (date) => {
|
|
@@ -2745,12 +2964,12 @@ __export(exports_datetimefull, {
|
|
|
2745
2964
|
COMBINE: () => COMBINE
|
|
2746
2965
|
});
|
|
2747
2966
|
var DATETIME = (year, month, day, hour, minute, second) => {
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2967
|
+
assertInteger(year, "DATETIME", "year");
|
|
2968
|
+
assertInteger(month, "DATETIME", "month");
|
|
2969
|
+
assertInteger(day, "DATETIME", "day");
|
|
2970
|
+
assertInteger(hour, "DATETIME", "hour");
|
|
2971
|
+
assertInteger(minute, "DATETIME", "minute");
|
|
2972
|
+
assertInteger(second, "DATETIME", "second");
|
|
2754
2973
|
return new Temporal.PlainDateTime(year, month, day, hour, minute, second);
|
|
2755
2974
|
};
|
|
2756
2975
|
var NOW = () => Temporal.Now.plainDateTimeISO();
|
|
@@ -2829,12 +3048,18 @@ var SQRT = (x) => {
|
|
|
2829
3048
|
return Math.sqrt(x);
|
|
2830
3049
|
};
|
|
2831
3050
|
var MIN = (...values) => {
|
|
3051
|
+
if (values.length === 0) {
|
|
3052
|
+
throw new RangeError("MIN requires at least one argument");
|
|
3053
|
+
}
|
|
2832
3054
|
for (const value of values) {
|
|
2833
3055
|
assertNumber(value, "MIN");
|
|
2834
3056
|
}
|
|
2835
3057
|
return Math.min(...values);
|
|
2836
3058
|
};
|
|
2837
3059
|
var MAX = (...values) => {
|
|
3060
|
+
if (values.length === 0) {
|
|
3061
|
+
throw new RangeError("MAX requires at least one argument");
|
|
3062
|
+
}
|
|
2838
3063
|
for (const value of values) {
|
|
2839
3064
|
assertNumber(value, "MAX");
|
|
2840
3065
|
}
|
|
@@ -2868,6 +3093,9 @@ var CLAMP = (value, min, max) => {
|
|
|
2868
3093
|
assertNumber(value, "CLAMP", "value");
|
|
2869
3094
|
assertNumber(min, "CLAMP", "min");
|
|
2870
3095
|
assertNumber(max, "CLAMP", "max");
|
|
3096
|
+
if (min > max) {
|
|
3097
|
+
throw new RangeError(`CLAMP: min (${min}) must not exceed max (${max})`);
|
|
3098
|
+
}
|
|
2871
3099
|
return value < min ? min : value > max ? max : value;
|
|
2872
3100
|
};
|
|
2873
3101
|
|
|
@@ -2943,9 +3171,9 @@ __export(exports_time, {
|
|
|
2943
3171
|
ADD_HOURS: () => ADD_HOURS
|
|
2944
3172
|
});
|
|
2945
3173
|
var TIME = (hour, minute, second) => {
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
3174
|
+
assertInteger(hour, "TIME", "hour");
|
|
3175
|
+
assertInteger(minute, "TIME", "minute");
|
|
3176
|
+
assertInteger(second, "TIME", "second");
|
|
2949
3177
|
return new Temporal.PlainTime(hour, minute, second);
|
|
2950
3178
|
};
|
|
2951
3179
|
var NOW_TIME = () => Temporal.Now.plainTimeISO();
|
|
@@ -3045,7 +3273,6 @@ var defaultContext = {
|
|
|
3045
3273
|
}
|
|
3046
3274
|
};
|
|
3047
3275
|
export {
|
|
3048
|
-
visitPartial,
|
|
3049
3276
|
visit,
|
|
3050
3277
|
typeOf,
|
|
3051
3278
|
toLineColumn,
|
|
@@ -3073,6 +3300,7 @@ export {
|
|
|
3073
3300
|
generate,
|
|
3074
3301
|
extractInputVariables,
|
|
3075
3302
|
extractAssignedVariables,
|
|
3303
|
+
evaluateWithScope,
|
|
3076
3304
|
evaluateScope,
|
|
3077
3305
|
evaluate,
|
|
3078
3306
|
defaultContext,
|