littlewing 2.2.0 → 2.3.1
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 +32 -69
- package/dist/index.js +873 -744
- 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);
|
|
@@ -815,7 +848,17 @@ function concatenateArrays(a, b) {
|
|
|
815
848
|
function validateHomogeneousArray(elements) {
|
|
816
849
|
if (elements.length <= 1)
|
|
817
850
|
return;
|
|
818
|
-
const
|
|
851
|
+
const first = elements[0];
|
|
852
|
+
const firstPrimitive = typeof first;
|
|
853
|
+
if (firstPrimitive === "number" || firstPrimitive === "string" || firstPrimitive === "boolean") {
|
|
854
|
+
for (let i = 1;i < elements.length; i++) {
|
|
855
|
+
if (typeof elements[i] !== firstPrimitive) {
|
|
856
|
+
throw new TypeError(`Heterogeneous array: expected ${firstPrimitive}, got ${typeOf(elements[i])} at index ${i}`);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
const firstType = typeOf(first);
|
|
819
862
|
for (let i = 1;i < elements.length; i++) {
|
|
820
863
|
const elemType = typeOf(elements[i]);
|
|
821
864
|
if (elemType !== firstType) {
|
|
@@ -919,13 +962,29 @@ function resolveIndex(target, index) {
|
|
|
919
962
|
throw new TypeError(`Index must be an integer, got ${index}`);
|
|
920
963
|
}
|
|
921
964
|
if (typeof target === "string") {
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
965
|
+
if (index >= 0) {
|
|
966
|
+
let i2 = 0;
|
|
967
|
+
for (const cp of target) {
|
|
968
|
+
if (i2 === index)
|
|
969
|
+
return cp;
|
|
970
|
+
i2++;
|
|
971
|
+
}
|
|
972
|
+
throw new RangeError(`Index ${index} out of bounds for length ${i2}`);
|
|
973
|
+
}
|
|
974
|
+
let len2 = 0;
|
|
975
|
+
for (const _ of target)
|
|
976
|
+
len2++;
|
|
977
|
+
const resolved2 = len2 + index;
|
|
978
|
+
if (resolved2 < 0) {
|
|
926
979
|
throw new RangeError(`Index ${index} out of bounds for length ${len2}`);
|
|
927
980
|
}
|
|
928
|
-
|
|
981
|
+
let i = 0;
|
|
982
|
+
for (const cp of target) {
|
|
983
|
+
if (i === resolved2)
|
|
984
|
+
return cp;
|
|
985
|
+
i++;
|
|
986
|
+
}
|
|
987
|
+
throw new RangeError(`Index ${index} out of bounds for length ${len2}`);
|
|
929
988
|
}
|
|
930
989
|
const len = target.length;
|
|
931
990
|
const resolved = index < 0 ? len + index : index;
|
|
@@ -945,11 +1004,7 @@ function buildRange(start, end, inclusive) {
|
|
|
945
1004
|
throw new RangeError(`Range start (${start}) must not exceed end (${end})`);
|
|
946
1005
|
}
|
|
947
1006
|
const limit = inclusive ? end + 1 : end;
|
|
948
|
-
|
|
949
|
-
for (let i = start;i < limit; i++) {
|
|
950
|
-
result.push(i);
|
|
951
|
-
}
|
|
952
|
-
return result;
|
|
1007
|
+
return Array.from({ length: limit - start }, (_, i) => start + i);
|
|
953
1008
|
}
|
|
954
1009
|
function getOperatorPrecedence(operator) {
|
|
955
1010
|
switch (operator) {
|
|
@@ -979,69 +1034,75 @@ function getOperatorPrecedence(operator) {
|
|
|
979
1034
|
}
|
|
980
1035
|
function collectAllIdentifiers(node) {
|
|
981
1036
|
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
|
-
});
|
|
1037
|
+
collectIdentifiers(node, identifiers, new Set);
|
|
1043
1038
|
return identifiers;
|
|
1044
1039
|
}
|
|
1040
|
+
function collectIdentifiers(node, ids, boundVars) {
|
|
1041
|
+
switch (node.kind) {
|
|
1042
|
+
case 0 /* Program */:
|
|
1043
|
+
for (const s of node.statements)
|
|
1044
|
+
collectIdentifiers(s, ids, boundVars);
|
|
1045
|
+
break;
|
|
1046
|
+
case 2 /* Identifier */:
|
|
1047
|
+
if (!boundVars.has(node.name))
|
|
1048
|
+
ids.add(node.name);
|
|
1049
|
+
break;
|
|
1050
|
+
case 10 /* ArrayLiteral */:
|
|
1051
|
+
for (const e of node.elements)
|
|
1052
|
+
collectIdentifiers(e, ids, boundVars);
|
|
1053
|
+
break;
|
|
1054
|
+
case 3 /* BinaryOp */:
|
|
1055
|
+
collectIdentifiers(node.left, ids, boundVars);
|
|
1056
|
+
collectIdentifiers(node.right, ids, boundVars);
|
|
1057
|
+
break;
|
|
1058
|
+
case 4 /* UnaryOp */:
|
|
1059
|
+
collectIdentifiers(node.argument, ids, boundVars);
|
|
1060
|
+
break;
|
|
1061
|
+
case 5 /* FunctionCall */:
|
|
1062
|
+
for (const a of node.args)
|
|
1063
|
+
collectIdentifiers(a, ids, boundVars);
|
|
1064
|
+
break;
|
|
1065
|
+
case 6 /* Assignment */:
|
|
1066
|
+
collectIdentifiers(node.value, ids, boundVars);
|
|
1067
|
+
break;
|
|
1068
|
+
case 7 /* IfExpression */:
|
|
1069
|
+
collectIdentifiers(node.condition, ids, boundVars);
|
|
1070
|
+
collectIdentifiers(node.consequent, ids, boundVars);
|
|
1071
|
+
collectIdentifiers(node.alternate, ids, boundVars);
|
|
1072
|
+
break;
|
|
1073
|
+
case 11 /* ForExpression */: {
|
|
1074
|
+
collectIdentifiers(node.iterable, ids, boundVars);
|
|
1075
|
+
if (node.accumulator)
|
|
1076
|
+
collectIdentifiers(node.accumulator.initial, ids, boundVars);
|
|
1077
|
+
const innerBound = new Set(boundVars);
|
|
1078
|
+
innerBound.add(node.variable);
|
|
1079
|
+
if (node.accumulator)
|
|
1080
|
+
innerBound.add(node.accumulator.name);
|
|
1081
|
+
if (node.guard)
|
|
1082
|
+
collectIdentifiers(node.guard, ids, innerBound);
|
|
1083
|
+
collectIdentifiers(node.body, ids, innerBound);
|
|
1084
|
+
break;
|
|
1085
|
+
}
|
|
1086
|
+
case 12 /* IndexAccess */:
|
|
1087
|
+
collectIdentifiers(node.object, ids, boundVars);
|
|
1088
|
+
collectIdentifiers(node.index, ids, boundVars);
|
|
1089
|
+
break;
|
|
1090
|
+
case 13 /* RangeExpression */:
|
|
1091
|
+
collectIdentifiers(node.start, ids, boundVars);
|
|
1092
|
+
collectIdentifiers(node.end, ids, boundVars);
|
|
1093
|
+
break;
|
|
1094
|
+
case 14 /* PipeExpression */:
|
|
1095
|
+
collectIdentifiers(node.value, ids, boundVars);
|
|
1096
|
+
for (const a of node.args)
|
|
1097
|
+
collectIdentifiers(a, ids, boundVars);
|
|
1098
|
+
break;
|
|
1099
|
+
case 1 /* NumberLiteral */:
|
|
1100
|
+
case 8 /* StringLiteral */:
|
|
1101
|
+
case 9 /* BooleanLiteral */:
|
|
1102
|
+
case 15 /* Placeholder */:
|
|
1103
|
+
break;
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1045
1106
|
function getTokenPrecedence(kind) {
|
|
1046
1107
|
switch (kind) {
|
|
1047
1108
|
case 24 /* Eq */:
|
|
@@ -1139,90 +1200,88 @@ function generate(node) {
|
|
|
1139
1200
|
return code;
|
|
1140
1201
|
}
|
|
1141
1202
|
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;
|
|
1203
|
+
const recurse = generateNode;
|
|
1204
|
+
switch (node.kind) {
|
|
1205
|
+
case 0 /* Program */:
|
|
1206
|
+
return generateProgram(node, recurse);
|
|
1207
|
+
case 1 /* NumberLiteral */:
|
|
1208
|
+
return String(node.value);
|
|
1209
|
+
case 8 /* StringLiteral */:
|
|
1210
|
+
return `"${escapeString(node.value)}"`;
|
|
1211
|
+
case 9 /* BooleanLiteral */:
|
|
1212
|
+
return node.value ? "true" : "false";
|
|
1213
|
+
case 10 /* ArrayLiteral */:
|
|
1214
|
+
return `[${node.elements.map(recurse).join(", ")}]`;
|
|
1215
|
+
case 2 /* Identifier */:
|
|
1216
|
+
return node.name;
|
|
1217
|
+
case 3 /* BinaryOp */: {
|
|
1218
|
+
const left = recurse(node.left);
|
|
1219
|
+
const right = recurse(node.right);
|
|
1220
|
+
const opPrec = getOperatorPrecedence(node.operator);
|
|
1221
|
+
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
1222
|
const leftCode = leftNeedsParens ? `(${left})` : left;
|
|
1165
|
-
const rightNeedsParens = needsParens(
|
|
1223
|
+
const rightNeedsParens = needsParens(node.right, node.operator, false) || isPipeExpression(node.right) || isAssignment(node.right) || isRangeExpression(node.right) && opPrec >= 6;
|
|
1166
1224
|
const rightCode = rightNeedsParens ? `(${right})` : right;
|
|
1167
|
-
return `${leftCode} ${
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
const arg = recurse(
|
|
1171
|
-
const parensNeeded = isBinaryOp(
|
|
1225
|
+
return `${leftCode} ${node.operator} ${rightCode}`;
|
|
1226
|
+
}
|
|
1227
|
+
case 4 /* UnaryOp */: {
|
|
1228
|
+
const arg = recurse(node.argument);
|
|
1229
|
+
const parensNeeded = isBinaryOp(node.argument) || isAssignment(node.argument) || isPipeExpression(node.argument) || isRangeExpression(node.argument);
|
|
1172
1230
|
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(
|
|
1231
|
+
return `${node.operator}${argCode}`;
|
|
1232
|
+
}
|
|
1233
|
+
case 5 /* FunctionCall */: {
|
|
1234
|
+
const argsCode = node.args.map(recurse).join(", ");
|
|
1235
|
+
return `${node.name}(${argsCode})`;
|
|
1236
|
+
}
|
|
1237
|
+
case 6 /* Assignment */: {
|
|
1238
|
+
const value = recurse(node.value);
|
|
1239
|
+
return `${node.name} = ${value}`;
|
|
1240
|
+
}
|
|
1241
|
+
case 7 /* IfExpression */: {
|
|
1242
|
+
const condition = recurse(node.condition);
|
|
1243
|
+
const consequent = recurse(node.consequent);
|
|
1244
|
+
const alternate = recurse(node.alternate);
|
|
1187
1245
|
return `if ${condition} then ${consequent} else ${alternate}`;
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
const parts = [`for ${
|
|
1191
|
-
if (
|
|
1192
|
-
parts.push(`when ${recurse(
|
|
1246
|
+
}
|
|
1247
|
+
case 11 /* ForExpression */: {
|
|
1248
|
+
const parts = [`for ${node.variable} in ${recurse(node.iterable)}`];
|
|
1249
|
+
if (node.guard) {
|
|
1250
|
+
parts.push(`when ${recurse(node.guard)}`);
|
|
1193
1251
|
}
|
|
1194
|
-
if (
|
|
1195
|
-
parts.push(`into ${
|
|
1252
|
+
if (node.accumulator) {
|
|
1253
|
+
parts.push(`into ${node.accumulator.name} = ${recurse(node.accumulator.initial)}`);
|
|
1196
1254
|
}
|
|
1197
|
-
parts.push(`then ${recurse(
|
|
1255
|
+
parts.push(`then ${recurse(node.body)}`);
|
|
1198
1256
|
return parts.join(" ");
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
|
-
const object = recurse(
|
|
1202
|
-
const index = recurse(
|
|
1203
|
-
const
|
|
1204
|
-
const objectCode =
|
|
1257
|
+
}
|
|
1258
|
+
case 12 /* IndexAccess */: {
|
|
1259
|
+
const object = recurse(node.object);
|
|
1260
|
+
const index = recurse(node.index);
|
|
1261
|
+
const objNeedsParens = isBinaryOp(node.object) || isUnaryOp(node.object) || isAssignment(node.object) || isRangeExpression(node.object) || isPipeExpression(node.object);
|
|
1262
|
+
const objectCode = objNeedsParens ? `(${object})` : object;
|
|
1205
1263
|
return `${objectCode}[${index}]`;
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
const start = recurse(
|
|
1209
|
-
const end = recurse(
|
|
1210
|
-
const op =
|
|
1211
|
-
const startNeedsParens = isBinaryOp(
|
|
1212
|
-
const endNeedsParens = isBinaryOp(
|
|
1264
|
+
}
|
|
1265
|
+
case 13 /* RangeExpression */: {
|
|
1266
|
+
const start = recurse(node.start);
|
|
1267
|
+
const end = recurse(node.end);
|
|
1268
|
+
const op = node.inclusive ? "..=" : "..";
|
|
1269
|
+
const startNeedsParens = isBinaryOp(node.start) || isRangeExpression(node.start) || isIfExpression(node.start) || isForExpression(node.start) || isAssignment(node.start);
|
|
1270
|
+
const endNeedsParens = isBinaryOp(node.end) || isRangeExpression(node.end) || isPipeExpression(node.end) || isAssignment(node.end);
|
|
1213
1271
|
const startCode = startNeedsParens ? `(${start})` : start;
|
|
1214
1272
|
const endCode = endNeedsParens ? `(${end})` : end;
|
|
1215
1273
|
return `${startCode}${op}${endCode}`;
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
const value = recurse(
|
|
1219
|
-
const argsCode =
|
|
1220
|
-
const valueNeedsParens = isAssignment(
|
|
1274
|
+
}
|
|
1275
|
+
case 14 /* PipeExpression */: {
|
|
1276
|
+
const value = recurse(node.value);
|
|
1277
|
+
const argsCode = node.args.map(recurse).join(", ");
|
|
1278
|
+
const valueNeedsParens = isAssignment(node.value) || isIfExpression(node.value) || isForExpression(node.value);
|
|
1221
1279
|
const valueCode = valueNeedsParens ? `(${value})` : value;
|
|
1222
|
-
return `${valueCode} |> ${
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1280
|
+
return `${valueCode} |> ${node.name}(${argsCode})`;
|
|
1281
|
+
}
|
|
1282
|
+
case 15 /* Placeholder */:
|
|
1283
|
+
return "?";
|
|
1284
|
+
}
|
|
1226
1285
|
}
|
|
1227
1286
|
// src/parser.ts
|
|
1228
1287
|
var BINARY_OPERATOR_TOKENS = new Set([
|
|
@@ -1612,198 +1671,224 @@ function advance2(state) {
|
|
|
1612
1671
|
}
|
|
1613
1672
|
|
|
1614
1673
|
// 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;
|
|
1674
|
+
function run(input, context = {}) {
|
|
1675
|
+
const node = typeof input === "string" ? parse(input) : input;
|
|
1676
|
+
const variables = new Map;
|
|
1677
|
+
const externalVariables = new Set;
|
|
1678
|
+
const vars = context.variables;
|
|
1679
|
+
if (vars) {
|
|
1680
|
+
for (const key of Object.keys(vars)) {
|
|
1681
|
+
variables.set(key, vars[key]);
|
|
1682
|
+
externalVariables.add(key);
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
const value = evalNode(node);
|
|
1686
|
+
return { value, variables };
|
|
1687
|
+
function evalNode(node2) {
|
|
1688
|
+
switch (node2.kind) {
|
|
1689
|
+
case 0 /* Program */: {
|
|
1690
|
+
let result = 0;
|
|
1691
|
+
for (const statement of node2.statements) {
|
|
1692
|
+
result = evalNode(statement);
|
|
1693
|
+
}
|
|
1694
|
+
return result;
|
|
1657
1695
|
}
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1696
|
+
case 1 /* NumberLiteral */:
|
|
1697
|
+
return node2.value;
|
|
1698
|
+
case 8 /* StringLiteral */:
|
|
1699
|
+
return node2.value;
|
|
1700
|
+
case 9 /* BooleanLiteral */:
|
|
1701
|
+
return node2.value;
|
|
1702
|
+
case 10 /* ArrayLiteral */: {
|
|
1703
|
+
const elems = node2.elements;
|
|
1704
|
+
const elements = [];
|
|
1705
|
+
for (let i = 0;i < elems.length; i++) {
|
|
1706
|
+
elements.push(evalNode(elems[i]));
|
|
1707
|
+
}
|
|
1708
|
+
validateHomogeneousArray(elements);
|
|
1709
|
+
return elements;
|
|
1667
1710
|
}
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1711
|
+
case 2 /* Identifier */: {
|
|
1712
|
+
const value2 = variables.get(node2.name);
|
|
1713
|
+
if (value2 === undefined) {
|
|
1714
|
+
throw new Error(`Undefined variable: ${node2.name}`);
|
|
1715
|
+
}
|
|
1716
|
+
return value2;
|
|
1671
1717
|
}
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1718
|
+
case 3 /* BinaryOp */: {
|
|
1719
|
+
if (node2.operator === "&&") {
|
|
1720
|
+
const left = evalNode(node2.left);
|
|
1721
|
+
assertBoolean(left, "Operator '&&'", "left");
|
|
1722
|
+
if (!left)
|
|
1723
|
+
return false;
|
|
1724
|
+
const right = evalNode(node2.right);
|
|
1725
|
+
assertBoolean(right, "Operator '&&'", "right");
|
|
1726
|
+
return right;
|
|
1727
|
+
}
|
|
1728
|
+
if (node2.operator === "||") {
|
|
1729
|
+
const left = evalNode(node2.left);
|
|
1730
|
+
assertBoolean(left, "Operator '||'", "left");
|
|
1731
|
+
if (left)
|
|
1732
|
+
return true;
|
|
1733
|
+
const right = evalNode(node2.right);
|
|
1734
|
+
assertBoolean(right, "Operator '||'", "right");
|
|
1735
|
+
return right;
|
|
1736
|
+
}
|
|
1737
|
+
return evaluateBinaryOperation(node2.operator, evalNode(node2.left), evalNode(node2.right));
|
|
1678
1738
|
}
|
|
1679
|
-
|
|
1680
|
-
|
|
1739
|
+
case 4 /* UnaryOp */: {
|
|
1740
|
+
const arg = evalNode(node2.argument);
|
|
1741
|
+
if (node2.operator === "-") {
|
|
1742
|
+
assertNumber(arg, "Operator '-' (unary)");
|
|
1743
|
+
return -arg;
|
|
1744
|
+
}
|
|
1745
|
+
if (node2.operator === "!") {
|
|
1746
|
+
assertBoolean(arg, "Operator '!'");
|
|
1747
|
+
return !arg;
|
|
1748
|
+
}
|
|
1749
|
+
throw new Error(`Unknown unary operator: ${String(node2.operator)}`);
|
|
1681
1750
|
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1751
|
+
case 5 /* FunctionCall */: {
|
|
1752
|
+
const fn = context.functions?.[node2.name];
|
|
1753
|
+
if (fn === undefined) {
|
|
1754
|
+
throw new Error(`Undefined function: ${node2.name}`);
|
|
1755
|
+
}
|
|
1756
|
+
if (typeof fn !== "function") {
|
|
1757
|
+
throw new Error(`${node2.name} is not a function`);
|
|
1758
|
+
}
|
|
1759
|
+
const nodeArgs = node2.args;
|
|
1760
|
+
const evaluatedArgs = [];
|
|
1761
|
+
for (let i = 0;i < nodeArgs.length; i++) {
|
|
1762
|
+
evaluatedArgs.push(evalNode(nodeArgs[i]));
|
|
1691
1763
|
}
|
|
1764
|
+
return fn(...evaluatedArgs);
|
|
1692
1765
|
}
|
|
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);
|
|
1766
|
+
case 6 /* Assignment */: {
|
|
1767
|
+
const value2 = evalNode(node2.value);
|
|
1768
|
+
if (externalVariables.has(node2.name)) {
|
|
1769
|
+
const externalValue = variables.get(node2.name);
|
|
1770
|
+
if (externalValue !== undefined) {
|
|
1771
|
+
return externalValue;
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
variables.set(node2.name, value2);
|
|
1775
|
+
return value2;
|
|
1706
1776
|
}
|
|
1707
|
-
|
|
1708
|
-
|
|
1777
|
+
case 7 /* IfExpression */: {
|
|
1778
|
+
const condition = evalNode(node2.condition);
|
|
1779
|
+
assertBoolean(condition, "If condition");
|
|
1780
|
+
return condition ? evalNode(node2.consequent) : evalNode(node2.alternate);
|
|
1709
1781
|
}
|
|
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}`);
|
|
1782
|
+
case 12 /* IndexAccess */: {
|
|
1783
|
+
const object = evalNode(node2.object);
|
|
1784
|
+
const index = evalNode(node2.index);
|
|
1785
|
+
if (Array.isArray(object)) {
|
|
1786
|
+
return resolveIndex(object, index);
|
|
1787
|
+
}
|
|
1788
|
+
if (typeof object === "string") {
|
|
1789
|
+
return resolveIndex(object, index);
|
|
1790
|
+
}
|
|
1791
|
+
throw new TypeError(`Index access expected array or string, got ${typeOf(object)}`);
|
|
1724
1792
|
}
|
|
1725
|
-
|
|
1726
|
-
|
|
1793
|
+
case 13 /* RangeExpression */: {
|
|
1794
|
+
const start = evalNode(node2.start);
|
|
1795
|
+
const end = evalNode(node2.end);
|
|
1796
|
+
assertNumber(start, "Range start");
|
|
1797
|
+
assertNumber(end, "Range end");
|
|
1798
|
+
return buildRange(start, end, node2.inclusive);
|
|
1727
1799
|
}
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1800
|
+
case 14 /* PipeExpression */: {
|
|
1801
|
+
const pipedValue = evalNode(node2.value);
|
|
1802
|
+
const fn = context.functions?.[node2.name];
|
|
1803
|
+
if (fn === undefined) {
|
|
1804
|
+
throw new Error(`Undefined function: ${node2.name}`);
|
|
1805
|
+
}
|
|
1806
|
+
if (typeof fn !== "function") {
|
|
1807
|
+
throw new Error(`${node2.name} is not a function`);
|
|
1808
|
+
}
|
|
1809
|
+
const pipeArgs = node2.args;
|
|
1810
|
+
const evaluatedArgs = [];
|
|
1811
|
+
for (let i = 0;i < pipeArgs.length; i++) {
|
|
1812
|
+
const arg = pipeArgs[i];
|
|
1813
|
+
evaluatedArgs.push(arg.kind === 15 /* Placeholder */ ? pipedValue : evalNode(arg));
|
|
1814
|
+
}
|
|
1815
|
+
return fn(...evaluatedArgs);
|
|
1743
1816
|
}
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1817
|
+
case 15 /* Placeholder */:
|
|
1818
|
+
throw new Error("Placeholder outside pipe expression");
|
|
1819
|
+
case 11 /* ForExpression */: {
|
|
1820
|
+
const iterable = evalNode(node2.iterable);
|
|
1821
|
+
let iterTarget;
|
|
1822
|
+
if (Array.isArray(iterable)) {
|
|
1823
|
+
iterTarget = iterable;
|
|
1824
|
+
} else if (typeof iterable === "string") {
|
|
1825
|
+
iterTarget = iterable;
|
|
1749
1826
|
} else {
|
|
1750
|
-
|
|
1827
|
+
throw new TypeError(`For expression expected array or string, got ${typeOf(iterable)}`);
|
|
1751
1828
|
}
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1829
|
+
const previousValue = variables.get(node2.variable);
|
|
1830
|
+
const hadPreviousValue = variables.has(node2.variable);
|
|
1831
|
+
if (node2.accumulator) {
|
|
1832
|
+
let acc = evalNode(node2.accumulator.initial);
|
|
1833
|
+
const prevAcc = variables.get(node2.accumulator.name);
|
|
1834
|
+
const hadPrevAcc = variables.has(node2.accumulator.name);
|
|
1835
|
+
for (const item of iterTarget) {
|
|
1836
|
+
variables.set(node2.variable, item);
|
|
1837
|
+
if (node2.guard) {
|
|
1838
|
+
const guardValue = evalNode(node2.guard);
|
|
1839
|
+
assertBoolean(guardValue, "For guard");
|
|
1840
|
+
if (!guardValue)
|
|
1841
|
+
continue;
|
|
1842
|
+
}
|
|
1843
|
+
variables.set(node2.accumulator.name, acc);
|
|
1844
|
+
acc = evalNode(node2.body);
|
|
1845
|
+
}
|
|
1846
|
+
if (hadPrevAcc) {
|
|
1847
|
+
variables.set(node2.accumulator.name, prevAcc);
|
|
1848
|
+
} else {
|
|
1849
|
+
variables.delete(node2.accumulator.name);
|
|
1850
|
+
}
|
|
1851
|
+
if (hadPreviousValue) {
|
|
1852
|
+
variables.set(node2.variable, previousValue);
|
|
1853
|
+
} else {
|
|
1854
|
+
variables.delete(node2.variable);
|
|
1855
|
+
}
|
|
1856
|
+
return acc;
|
|
1857
|
+
}
|
|
1858
|
+
const result = [];
|
|
1859
|
+
for (const item of iterTarget) {
|
|
1860
|
+
variables.set(node2.variable, item);
|
|
1861
|
+
if (node2.guard) {
|
|
1862
|
+
const guardValue = evalNode(node2.guard);
|
|
1761
1863
|
assertBoolean(guardValue, "For guard");
|
|
1762
1864
|
if (!guardValue)
|
|
1763
1865
|
continue;
|
|
1764
1866
|
}
|
|
1765
|
-
|
|
1766
|
-
acc = recurse(n.body);
|
|
1867
|
+
result.push(evalNode(node2.body));
|
|
1767
1868
|
}
|
|
1768
|
-
if (
|
|
1769
|
-
variables.set(
|
|
1869
|
+
if (hadPreviousValue) {
|
|
1870
|
+
variables.set(node2.variable, previousValue);
|
|
1770
1871
|
} else {
|
|
1771
|
-
variables.delete(
|
|
1772
|
-
}
|
|
1773
|
-
restoreLoopVar();
|
|
1774
|
-
return acc;
|
|
1775
|
-
}
|
|
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;
|
|
1872
|
+
variables.delete(node2.variable);
|
|
1784
1873
|
}
|
|
1785
|
-
result
|
|
1874
|
+
validateHomogeneousArray(result);
|
|
1875
|
+
return result;
|
|
1786
1876
|
}
|
|
1787
|
-
restoreLoopVar();
|
|
1788
|
-
validateHomogeneousArray(result);
|
|
1789
|
-
return result;
|
|
1790
1877
|
}
|
|
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 };
|
|
1878
|
+
}
|
|
1800
1879
|
}
|
|
1801
1880
|
function evaluate(input, context = {}) {
|
|
1802
1881
|
return run(input, context).value;
|
|
1803
1882
|
}
|
|
1804
1883
|
function evaluateScope(input, context = {}) {
|
|
1805
|
-
|
|
1806
|
-
|
|
1884
|
+
return evaluateWithScope(input, context).scope;
|
|
1885
|
+
}
|
|
1886
|
+
function evaluateWithScope(input, context = {}) {
|
|
1887
|
+
const { value, variables } = run(input, context);
|
|
1888
|
+
return {
|
|
1889
|
+
value,
|
|
1890
|
+
scope: Object.fromEntries(variables)
|
|
1891
|
+
};
|
|
1807
1892
|
}
|
|
1808
1893
|
// src/optimizer.ts
|
|
1809
1894
|
function preserveComments(original, replacement) {
|
|
@@ -1869,24 +1954,34 @@ function isLiteral(node) {
|
|
|
1869
1954
|
return isNumberLiteral(node) || isStringLiteral(node) || isBooleanLiteral(node);
|
|
1870
1955
|
}
|
|
1871
1956
|
function mightHaveSideEffects(node) {
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1957
|
+
switch (node.kind) {
|
|
1958
|
+
case 0 /* Program */:
|
|
1959
|
+
return node.statements.some(mightHaveSideEffects);
|
|
1960
|
+
case 10 /* ArrayLiteral */:
|
|
1961
|
+
return node.elements.some(mightHaveSideEffects);
|
|
1962
|
+
case 3 /* BinaryOp */:
|
|
1963
|
+
return mightHaveSideEffects(node.left) || mightHaveSideEffects(node.right);
|
|
1964
|
+
case 4 /* UnaryOp */:
|
|
1965
|
+
return mightHaveSideEffects(node.argument);
|
|
1966
|
+
case 7 /* IfExpression */:
|
|
1967
|
+
return mightHaveSideEffects(node.condition) || mightHaveSideEffects(node.consequent) || mightHaveSideEffects(node.alternate);
|
|
1968
|
+
case 11 /* ForExpression */:
|
|
1969
|
+
return mightHaveSideEffects(node.iterable) || node.guard !== null && mightHaveSideEffects(node.guard) || node.accumulator !== null && mightHaveSideEffects(node.accumulator.initial) || mightHaveSideEffects(node.body);
|
|
1970
|
+
case 12 /* IndexAccess */:
|
|
1971
|
+
return mightHaveSideEffects(node.object) || mightHaveSideEffects(node.index);
|
|
1972
|
+
case 13 /* RangeExpression */:
|
|
1973
|
+
return mightHaveSideEffects(node.start) || mightHaveSideEffects(node.end);
|
|
1974
|
+
case 5 /* FunctionCall */:
|
|
1975
|
+
case 14 /* PipeExpression */:
|
|
1976
|
+
case 6 /* Assignment */:
|
|
1977
|
+
return true;
|
|
1978
|
+
case 1 /* NumberLiteral */:
|
|
1979
|
+
case 8 /* StringLiteral */:
|
|
1980
|
+
case 9 /* BooleanLiteral */:
|
|
1981
|
+
case 2 /* Identifier */:
|
|
1982
|
+
case 15 /* Placeholder */:
|
|
1983
|
+
return false;
|
|
1984
|
+
}
|
|
1890
1985
|
}
|
|
1891
1986
|
function propagateConstants(program2, externalVariables) {
|
|
1892
1987
|
const statements = program2.statements;
|
|
@@ -1917,10 +2012,9 @@ function propagateConstants(program2, externalVariables) {
|
|
|
1917
2012
|
return null;
|
|
1918
2013
|
const referencedIds = new Set;
|
|
1919
2014
|
for (const stmt of statements) {
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
collectReferencedIdentifiers(stmt, referencedIds);
|
|
2015
|
+
const identifiers = collectAllIdentifiers(isAssignment(stmt) ? stmt.value : stmt);
|
|
2016
|
+
for (const id of identifiers) {
|
|
2017
|
+
referencedIds.add(id);
|
|
1924
2018
|
}
|
|
1925
2019
|
}
|
|
1926
2020
|
let hasSubstitution = false;
|
|
@@ -1938,221 +2032,230 @@ function propagateConstants(program2, externalVariables) {
|
|
|
1938
2032
|
}
|
|
1939
2033
|
return result;
|
|
1940
2034
|
}
|
|
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
2035
|
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
|
-
|
|
2036
|
+
switch (node.kind) {
|
|
2037
|
+
case 0 /* Program */:
|
|
2038
|
+
for (const s of node.statements)
|
|
2039
|
+
collectForLoopVars(s, vars);
|
|
2040
|
+
break;
|
|
2041
|
+
case 10 /* ArrayLiteral */:
|
|
2042
|
+
for (const e of node.elements)
|
|
2043
|
+
collectForLoopVars(e, vars);
|
|
2044
|
+
break;
|
|
2045
|
+
case 3 /* BinaryOp */:
|
|
2046
|
+
collectForLoopVars(node.left, vars);
|
|
2047
|
+
collectForLoopVars(node.right, vars);
|
|
2048
|
+
break;
|
|
2049
|
+
case 4 /* UnaryOp */:
|
|
2050
|
+
collectForLoopVars(node.argument, vars);
|
|
2051
|
+
break;
|
|
2052
|
+
case 5 /* FunctionCall */:
|
|
2053
|
+
for (const a of node.args)
|
|
2054
|
+
collectForLoopVars(a, vars);
|
|
2055
|
+
break;
|
|
2056
|
+
case 6 /* Assignment */:
|
|
2057
|
+
collectForLoopVars(node.value, vars);
|
|
2058
|
+
break;
|
|
2059
|
+
case 7 /* IfExpression */:
|
|
2060
|
+
collectForLoopVars(node.condition, vars);
|
|
2061
|
+
collectForLoopVars(node.consequent, vars);
|
|
2062
|
+
collectForLoopVars(node.alternate, vars);
|
|
2063
|
+
break;
|
|
2064
|
+
case 11 /* ForExpression */:
|
|
2065
|
+
vars.add(node.variable);
|
|
2066
|
+
if (node.accumulator)
|
|
2067
|
+
vars.add(node.accumulator.name);
|
|
2068
|
+
collectForLoopVars(node.iterable, vars);
|
|
2069
|
+
if (node.guard)
|
|
2070
|
+
collectForLoopVars(node.guard, vars);
|
|
2071
|
+
if (node.accumulator)
|
|
2072
|
+
collectForLoopVars(node.accumulator.initial, vars);
|
|
2073
|
+
collectForLoopVars(node.body, vars);
|
|
2074
|
+
break;
|
|
2075
|
+
case 12 /* IndexAccess */:
|
|
2076
|
+
collectForLoopVars(node.object, vars);
|
|
2077
|
+
collectForLoopVars(node.index, vars);
|
|
2078
|
+
break;
|
|
2079
|
+
case 13 /* RangeExpression */:
|
|
2080
|
+
collectForLoopVars(node.start, vars);
|
|
2081
|
+
collectForLoopVars(node.end, vars);
|
|
2082
|
+
break;
|
|
2083
|
+
case 14 /* PipeExpression */:
|
|
2084
|
+
collectForLoopVars(node.value, vars);
|
|
2085
|
+
for (const a of node.args)
|
|
2086
|
+
collectForLoopVars(a, vars);
|
|
2087
|
+
break;
|
|
2088
|
+
case 1 /* NumberLiteral */:
|
|
2089
|
+
case 8 /* StringLiteral */:
|
|
2090
|
+
case 9 /* BooleanLiteral */:
|
|
2091
|
+
case 2 /* Identifier */:
|
|
2092
|
+
case 15 /* Placeholder */:
|
|
2093
|
+
break;
|
|
2094
|
+
}
|
|
2059
2095
|
}
|
|
2060
2096
|
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
|
-
|
|
2097
|
+
switch (node.kind) {
|
|
2098
|
+
case 0 /* Program */:
|
|
2099
|
+
for (const s of node.statements)
|
|
2100
|
+
countAssignments(s, counts);
|
|
2101
|
+
break;
|
|
2102
|
+
case 6 /* Assignment */:
|
|
2103
|
+
counts.set(node.name, (counts.get(node.name) ?? 0) + 1);
|
|
2104
|
+
countAssignments(node.value, counts);
|
|
2105
|
+
break;
|
|
2106
|
+
case 10 /* ArrayLiteral */:
|
|
2107
|
+
for (const e of node.elements)
|
|
2108
|
+
countAssignments(e, counts);
|
|
2109
|
+
break;
|
|
2110
|
+
case 3 /* BinaryOp */:
|
|
2111
|
+
countAssignments(node.left, counts);
|
|
2112
|
+
countAssignments(node.right, counts);
|
|
2113
|
+
break;
|
|
2114
|
+
case 4 /* UnaryOp */:
|
|
2115
|
+
countAssignments(node.argument, counts);
|
|
2116
|
+
break;
|
|
2117
|
+
case 5 /* FunctionCall */:
|
|
2118
|
+
for (const a of node.args)
|
|
2119
|
+
countAssignments(a, counts);
|
|
2120
|
+
break;
|
|
2121
|
+
case 7 /* IfExpression */:
|
|
2122
|
+
countAssignments(node.condition, counts);
|
|
2123
|
+
countAssignments(node.consequent, counts);
|
|
2124
|
+
countAssignments(node.alternate, counts);
|
|
2125
|
+
break;
|
|
2126
|
+
case 11 /* ForExpression */:
|
|
2127
|
+
countAssignments(node.iterable, counts);
|
|
2128
|
+
if (node.guard)
|
|
2129
|
+
countAssignments(node.guard, counts);
|
|
2130
|
+
if (node.accumulator)
|
|
2131
|
+
countAssignments(node.accumulator.initial, counts);
|
|
2132
|
+
countAssignments(node.body, counts);
|
|
2133
|
+
break;
|
|
2134
|
+
case 12 /* IndexAccess */:
|
|
2135
|
+
countAssignments(node.object, counts);
|
|
2136
|
+
countAssignments(node.index, counts);
|
|
2137
|
+
break;
|
|
2138
|
+
case 13 /* RangeExpression */:
|
|
2139
|
+
countAssignments(node.start, counts);
|
|
2140
|
+
countAssignments(node.end, counts);
|
|
2141
|
+
break;
|
|
2142
|
+
case 14 /* PipeExpression */:
|
|
2143
|
+
countAssignments(node.value, counts);
|
|
2144
|
+
for (const a of node.args)
|
|
2145
|
+
countAssignments(a, counts);
|
|
2146
|
+
break;
|
|
2147
|
+
case 1 /* NumberLiteral */:
|
|
2148
|
+
case 8 /* StringLiteral */:
|
|
2149
|
+
case 9 /* BooleanLiteral */:
|
|
2150
|
+
case 2 /* Identifier */:
|
|
2151
|
+
case 15 /* Placeholder */:
|
|
2152
|
+
break;
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
function unchangedArray(original, mapped) {
|
|
2156
|
+
for (let i = 0;i < original.length; i++) {
|
|
2157
|
+
if (original[i] !== mapped[i])
|
|
2158
|
+
return false;
|
|
2159
|
+
}
|
|
2160
|
+
return true;
|
|
2117
2161
|
}
|
|
2118
2162
|
function substituteIdentifiers(node, knownValues) {
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2163
|
+
const recurse = (n) => substituteIdentifiers(n, knownValues);
|
|
2164
|
+
switch (node.kind) {
|
|
2165
|
+
case 0 /* Program */: {
|
|
2166
|
+
const stmts = node.statements.map(recurse);
|
|
2167
|
+
if (unchangedArray(node.statements, stmts))
|
|
2168
|
+
return node;
|
|
2169
|
+
const result = program(stmts);
|
|
2170
|
+
if (node.trailingComments && node.trailingComments.length > 0) {
|
|
2171
|
+
return { ...result, trailingComments: node.trailingComments };
|
|
2124
2172
|
}
|
|
2125
2173
|
return result;
|
|
2126
|
-
}
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2174
|
+
}
|
|
2175
|
+
case 1 /* NumberLiteral */:
|
|
2176
|
+
case 8 /* StringLiteral */:
|
|
2177
|
+
case 9 /* BooleanLiteral */:
|
|
2178
|
+
case 15 /* Placeholder */:
|
|
2179
|
+
return node;
|
|
2180
|
+
case 10 /* ArrayLiteral */: {
|
|
2181
|
+
const elements = node.elements.map(recurse);
|
|
2182
|
+
if (unchangedArray(node.elements, elements))
|
|
2183
|
+
return node;
|
|
2184
|
+
return preserveComments(node, array(elements));
|
|
2185
|
+
}
|
|
2186
|
+
case 2 /* Identifier */: {
|
|
2187
|
+
const replacement = knownValues.get(node.name);
|
|
2188
|
+
return replacement ? preserveComments(node, replacement) : node;
|
|
2189
|
+
}
|
|
2190
|
+
case 3 /* BinaryOp */: {
|
|
2191
|
+
const left = recurse(node.left);
|
|
2192
|
+
const right = recurse(node.right);
|
|
2193
|
+
if (left === node.left && right === node.right)
|
|
2194
|
+
return node;
|
|
2195
|
+
return preserveComments(node, binaryOp(left, node.operator, right));
|
|
2196
|
+
}
|
|
2197
|
+
case 4 /* UnaryOp */: {
|
|
2198
|
+
const argument = recurse(node.argument);
|
|
2199
|
+
if (argument === node.argument)
|
|
2200
|
+
return node;
|
|
2201
|
+
return preserveComments(node, unaryOp(node.operator, argument));
|
|
2202
|
+
}
|
|
2203
|
+
case 5 /* FunctionCall */: {
|
|
2204
|
+
const args = node.args.map(recurse);
|
|
2205
|
+
if (unchangedArray(node.args, args))
|
|
2206
|
+
return node;
|
|
2207
|
+
return preserveComments(node, functionCall(node.name, args));
|
|
2208
|
+
}
|
|
2209
|
+
case 6 /* Assignment */: {
|
|
2210
|
+
const value = recurse(node.value);
|
|
2211
|
+
if (value === node.value)
|
|
2212
|
+
return node;
|
|
2213
|
+
return preserveComments(node, assign(node.name, value));
|
|
2214
|
+
}
|
|
2215
|
+
case 7 /* IfExpression */: {
|
|
2216
|
+
const condition = recurse(node.condition);
|
|
2217
|
+
const consequent = recurse(node.consequent);
|
|
2218
|
+
const alternate = recurse(node.alternate);
|
|
2219
|
+
if (condition === node.condition && consequent === node.consequent && alternate === node.alternate)
|
|
2220
|
+
return node;
|
|
2221
|
+
return preserveComments(node, ifExpr(condition, consequent, alternate));
|
|
2222
|
+
}
|
|
2223
|
+
case 11 /* ForExpression */: {
|
|
2141
2224
|
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
|
-
|
|
2225
|
+
innerKnown.delete(node.variable);
|
|
2226
|
+
if (node.accumulator)
|
|
2227
|
+
innerKnown.delete(node.accumulator.name);
|
|
2228
|
+
const iterable = recurse(node.iterable);
|
|
2229
|
+
const guard = node.guard ? substituteIdentifiers(node.guard, innerKnown) : null;
|
|
2230
|
+
const initial = node.accumulator ? recurse(node.accumulator.initial) : null;
|
|
2231
|
+
const body = substituteIdentifiers(node.body, innerKnown);
|
|
2232
|
+
if (iterable === node.iterable && guard === node.guard && (node.accumulator === null || initial === node.accumulator.initial) && body === node.body)
|
|
2233
|
+
return node;
|
|
2234
|
+
const accumulator = node.accumulator ? { name: node.accumulator.name, initial } : null;
|
|
2235
|
+
return preserveComments(node, forExpr(node.variable, iterable, guard, accumulator, body));
|
|
2236
|
+
}
|
|
2237
|
+
case 12 /* IndexAccess */: {
|
|
2238
|
+
const object = recurse(node.object);
|
|
2239
|
+
const index = recurse(node.index);
|
|
2240
|
+
if (object === node.object && index === node.index)
|
|
2241
|
+
return node;
|
|
2242
|
+
return preserveComments(node, indexAccess(object, index));
|
|
2243
|
+
}
|
|
2244
|
+
case 13 /* RangeExpression */: {
|
|
2245
|
+
const start = recurse(node.start);
|
|
2246
|
+
const end = recurse(node.end);
|
|
2247
|
+
if (start === node.start && end === node.end)
|
|
2248
|
+
return node;
|
|
2249
|
+
return preserveComments(node, rangeExpr(start, end, node.inclusive));
|
|
2250
|
+
}
|
|
2251
|
+
case 14 /* PipeExpression */: {
|
|
2252
|
+
const value = recurse(node.value);
|
|
2253
|
+
const args = node.args.map(recurse);
|
|
2254
|
+
if (value === node.value && unchangedArray(node.args, args))
|
|
2255
|
+
return node;
|
|
2256
|
+
return preserveComments(node, pipeExpr(value, node.name, args));
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2156
2259
|
}
|
|
2157
2260
|
function optimize(node, externalVariables) {
|
|
2158
2261
|
let propagated = fold(node);
|
|
@@ -2179,142 +2282,168 @@ function optimize(node, externalVariables) {
|
|
|
2179
2282
|
return propagated;
|
|
2180
2283
|
}
|
|
2181
2284
|
function fold(node) {
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2285
|
+
const recurse = fold;
|
|
2286
|
+
switch (node.kind) {
|
|
2287
|
+
case 1 /* NumberLiteral */:
|
|
2288
|
+
case 8 /* StringLiteral */:
|
|
2289
|
+
case 9 /* BooleanLiteral */:
|
|
2290
|
+
case 2 /* Identifier */:
|
|
2291
|
+
case 15 /* Placeholder */:
|
|
2292
|
+
return node;
|
|
2293
|
+
case 0 /* Program */: {
|
|
2294
|
+
const optimizedStatements = node.statements.map(recurse);
|
|
2295
|
+
if (unchangedArray(node.statements, optimizedStatements))
|
|
2296
|
+
return node;
|
|
2297
|
+
const result = program(optimizedStatements);
|
|
2298
|
+
if (node.trailingComments && node.trailingComments.length > 0) {
|
|
2299
|
+
return { ...result, trailingComments: node.trailingComments };
|
|
2300
|
+
}
|
|
2301
|
+
return result;
|
|
2302
|
+
}
|
|
2303
|
+
case 10 /* ArrayLiteral */: {
|
|
2304
|
+
const elements = node.elements.map(recurse);
|
|
2305
|
+
if (unchangedArray(node.elements, elements))
|
|
2306
|
+
return node;
|
|
2307
|
+
return preserveComments(node, array(elements));
|
|
2308
|
+
}
|
|
2309
|
+
case 3 /* BinaryOp */: {
|
|
2310
|
+
const left = recurse(node.left);
|
|
2311
|
+
const right = recurse(node.right);
|
|
2194
2312
|
if (isNumberLiteral(left) && isNumberLiteral(right)) {
|
|
2195
|
-
const result = evaluateBinaryOperation(
|
|
2313
|
+
const result = evaluateBinaryOperation(node.operator, left.value, right.value);
|
|
2196
2314
|
if (typeof result === "number")
|
|
2197
|
-
return preserveComments(
|
|
2315
|
+
return preserveComments(node, number(result));
|
|
2198
2316
|
if (typeof result === "boolean")
|
|
2199
|
-
return preserveComments(
|
|
2317
|
+
return preserveComments(node, boolean(result));
|
|
2200
2318
|
}
|
|
2201
2319
|
if (isStringLiteral(left) && isStringLiteral(right)) {
|
|
2202
|
-
if (
|
|
2203
|
-
return preserveComments(
|
|
2204
|
-
if (
|
|
2205
|
-
const result = evaluateBinaryOperation(
|
|
2206
|
-
return preserveComments(
|
|
2320
|
+
if (node.operator === "+")
|
|
2321
|
+
return preserveComments(node, string(left.value + right.value));
|
|
2322
|
+
if (node.operator === "<" || node.operator === ">" || node.operator === "<=" || node.operator === ">=") {
|
|
2323
|
+
const result = evaluateBinaryOperation(node.operator, left.value, right.value);
|
|
2324
|
+
return preserveComments(node, boolean(result));
|
|
2207
2325
|
}
|
|
2208
|
-
if (
|
|
2209
|
-
return preserveComments(
|
|
2210
|
-
if (
|
|
2211
|
-
return preserveComments(
|
|
2326
|
+
if (node.operator === "==")
|
|
2327
|
+
return preserveComments(node, boolean(left.value === right.value));
|
|
2328
|
+
if (node.operator === "!=")
|
|
2329
|
+
return preserveComments(node, boolean(left.value !== right.value));
|
|
2212
2330
|
}
|
|
2213
2331
|
if (isBooleanLiteral(left) && isBooleanLiteral(right)) {
|
|
2214
|
-
if (
|
|
2215
|
-
return preserveComments(
|
|
2216
|
-
if (
|
|
2217
|
-
return preserveComments(
|
|
2218
|
-
if (
|
|
2219
|
-
return preserveComments(
|
|
2220
|
-
if (
|
|
2221
|
-
return preserveComments(
|
|
2332
|
+
if (node.operator === "&&")
|
|
2333
|
+
return preserveComments(node, boolean(left.value && right.value));
|
|
2334
|
+
if (node.operator === "||")
|
|
2335
|
+
return preserveComments(node, boolean(left.value || right.value));
|
|
2336
|
+
if (node.operator === "==")
|
|
2337
|
+
return preserveComments(node, boolean(left.value === right.value));
|
|
2338
|
+
if (node.operator === "!=")
|
|
2339
|
+
return preserveComments(node, boolean(left.value !== right.value));
|
|
2222
2340
|
}
|
|
2223
2341
|
if (isLiteral(left) && isLiteral(right)) {
|
|
2224
2342
|
if (left.kind !== right.kind) {
|
|
2225
|
-
if (
|
|
2226
|
-
return preserveComments(
|
|
2227
|
-
if (
|
|
2228
|
-
return preserveComments(
|
|
2343
|
+
if (node.operator === "==")
|
|
2344
|
+
return preserveComments(node, boolean(false));
|
|
2345
|
+
if (node.operator === "!=")
|
|
2346
|
+
return preserveComments(node, boolean(true));
|
|
2229
2347
|
}
|
|
2230
2348
|
}
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2349
|
+
if (left === node.left && right === node.right)
|
|
2350
|
+
return node;
|
|
2351
|
+
return preserveComments(node, binaryOp(left, node.operator, right));
|
|
2352
|
+
}
|
|
2353
|
+
case 4 /* UnaryOp */: {
|
|
2354
|
+
const argument = recurse(node.argument);
|
|
2355
|
+
if (node.operator === "-" && isNumberLiteral(argument)) {
|
|
2356
|
+
return preserveComments(node, number(-argument.value));
|
|
2237
2357
|
}
|
|
2238
|
-
if (
|
|
2239
|
-
return preserveComments(
|
|
2358
|
+
if (node.operator === "!" && isBooleanLiteral(argument)) {
|
|
2359
|
+
return preserveComments(node, boolean(!argument.value));
|
|
2240
2360
|
}
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2361
|
+
if (argument === node.argument)
|
|
2362
|
+
return node;
|
|
2363
|
+
return preserveComments(node, unaryOp(node.operator, argument));
|
|
2364
|
+
}
|
|
2365
|
+
case 5 /* FunctionCall */: {
|
|
2366
|
+
const optimizedArgs = node.args.map(recurse);
|
|
2367
|
+
if (unchangedArray(node.args, optimizedArgs))
|
|
2368
|
+
return node;
|
|
2369
|
+
return preserveComments(node, functionCall(node.name, optimizedArgs));
|
|
2370
|
+
}
|
|
2371
|
+
case 6 /* Assignment */: {
|
|
2372
|
+
const value = recurse(node.value);
|
|
2373
|
+
if (value === node.value)
|
|
2374
|
+
return node;
|
|
2375
|
+
return preserveComments(node, assign(node.name, value));
|
|
2376
|
+
}
|
|
2377
|
+
case 7 /* IfExpression */: {
|
|
2378
|
+
const condition = recurse(node.condition);
|
|
2252
2379
|
if (isBooleanLiteral(condition)) {
|
|
2253
|
-
const result = condition.value ? recurse(
|
|
2254
|
-
return preserveComments(
|
|
2380
|
+
const result = condition.value ? recurse(node.consequent) : recurse(node.alternate);
|
|
2381
|
+
return preserveComments(node, result);
|
|
2255
2382
|
}
|
|
2256
|
-
const consequent = recurse(
|
|
2257
|
-
const alternate = recurse(
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
const
|
|
2264
|
-
const
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
const
|
|
2383
|
+
const consequent = recurse(node.consequent);
|
|
2384
|
+
const alternate = recurse(node.alternate);
|
|
2385
|
+
if (condition === node.condition && consequent === node.consequent && alternate === node.alternate)
|
|
2386
|
+
return node;
|
|
2387
|
+
return preserveComments(node, ifExpr(condition, consequent, alternate));
|
|
2388
|
+
}
|
|
2389
|
+
case 11 /* ForExpression */: {
|
|
2390
|
+
const iterable = recurse(node.iterable);
|
|
2391
|
+
const guard = node.guard ? recurse(node.guard) : null;
|
|
2392
|
+
const initial = node.accumulator ? recurse(node.accumulator.initial) : null;
|
|
2393
|
+
const body = recurse(node.body);
|
|
2394
|
+
if (iterable === node.iterable && guard === node.guard && (node.accumulator === null || initial === node.accumulator.initial) && body === node.body)
|
|
2395
|
+
return node;
|
|
2396
|
+
const accumulator = node.accumulator ? { name: node.accumulator.name, initial } : null;
|
|
2397
|
+
return preserveComments(node, forExpr(node.variable, iterable, guard, accumulator, body));
|
|
2398
|
+
}
|
|
2399
|
+
case 12 /* IndexAccess */: {
|
|
2400
|
+
const object = recurse(node.object);
|
|
2401
|
+
const index = recurse(node.index);
|
|
2270
2402
|
if (isArrayLiteral(object) && isNumberLiteral(index)) {
|
|
2271
2403
|
const idx = index.value;
|
|
2272
2404
|
if (Number.isInteger(idx)) {
|
|
2273
2405
|
const len = object.elements.length;
|
|
2274
2406
|
const resolved = idx < 0 ? len + idx : idx;
|
|
2275
2407
|
if (resolved >= 0 && resolved < len) {
|
|
2276
|
-
return preserveComments(
|
|
2408
|
+
return preserveComments(node, object.elements[resolved]);
|
|
2277
2409
|
}
|
|
2278
2410
|
}
|
|
2279
2411
|
}
|
|
2280
2412
|
if (isStringLiteral(object) && isNumberLiteral(index)) {
|
|
2281
2413
|
try {
|
|
2282
2414
|
const char = resolveIndex(object.value, index.value);
|
|
2283
|
-
return preserveComments(
|
|
2415
|
+
return preserveComments(node, string(char));
|
|
2284
2416
|
} catch {}
|
|
2285
2417
|
}
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2418
|
+
if (object === node.object && index === node.index)
|
|
2419
|
+
return node;
|
|
2420
|
+
return preserveComments(node, indexAccess(object, index));
|
|
2421
|
+
}
|
|
2422
|
+
case 13 /* RangeExpression */: {
|
|
2423
|
+
const start = recurse(node.start);
|
|
2424
|
+
const end = recurse(node.end);
|
|
2291
2425
|
if (isNumberLiteral(start) && isNumberLiteral(end)) {
|
|
2292
2426
|
try {
|
|
2293
|
-
const limit =
|
|
2427
|
+
const limit = node.inclusive ? end.value + 1 : end.value;
|
|
2294
2428
|
const count = limit - start.value;
|
|
2295
2429
|
if (count >= 0 && count <= 1e4) {
|
|
2296
|
-
const values = buildRange(start.value, end.value,
|
|
2297
|
-
return preserveComments(
|
|
2430
|
+
const values = buildRange(start.value, end.value, node.inclusive);
|
|
2431
|
+
return preserveComments(node, array(values.map(number)));
|
|
2298
2432
|
}
|
|
2299
2433
|
} catch {}
|
|
2300
2434
|
}
|
|
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;
|
|
2435
|
+
if (start === node.start && end === node.end)
|
|
2436
|
+
return node;
|
|
2437
|
+
return preserveComments(node, rangeExpr(start, end, node.inclusive));
|
|
2316
2438
|
}
|
|
2317
|
-
|
|
2439
|
+
case 14 /* PipeExpression */: {
|
|
2440
|
+
const value = recurse(node.value);
|
|
2441
|
+
const optimizedArgs = node.args.map(recurse);
|
|
2442
|
+
if (value === node.value && unchangedArray(node.args, optimizedArgs))
|
|
2443
|
+
return node;
|
|
2444
|
+
return preserveComments(node, pipeExpr(value, node.name, optimizedArgs));
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2318
2447
|
}
|
|
2319
2448
|
// src/stdlib/array.ts
|
|
2320
2449
|
var exports_array = {};
|
|
@@ -3045,7 +3174,6 @@ var defaultContext = {
|
|
|
3045
3174
|
}
|
|
3046
3175
|
};
|
|
3047
3176
|
export {
|
|
3048
|
-
visitPartial,
|
|
3049
3177
|
visit,
|
|
3050
3178
|
typeOf,
|
|
3051
3179
|
toLineColumn,
|
|
@@ -3073,6 +3201,7 @@ export {
|
|
|
3073
3201
|
generate,
|
|
3074
3202
|
extractInputVariables,
|
|
3075
3203
|
extractAssignedVariables,
|
|
3204
|
+
evaluateWithScope,
|
|
3076
3205
|
evaluateScope,
|
|
3077
3206
|
evaluate,
|
|
3078
3207
|
defaultContext,
|