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/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 { kind: 9 /* BooleanLiteral */, value };
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 { kind: 15 /* Placeholder */ };
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
- visitPartial(ast, {
345
- Program: (n, recurse) => {
346
- for (const statement of n.statements) {
347
- recurse(statement);
348
- }
349
- },
350
- Assignment: (n, recurse) => {
351
- if (!seen.has(n.name)) {
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
- recurse(n.value);
356
- },
357
- IfExpression: (n, recurse) => {
358
- recurse(n.condition);
359
- recurse(n.consequent);
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
- return names;
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
- return visit(node, {
389
- Program: (n, recurse) => n.statements.some(recurse),
390
- NumberLiteral: () => false,
391
- StringLiteral: () => false,
392
- BooleanLiteral: () => false,
393
- Identifier: (n) => !boundVars.has(n.name),
394
- ArrayLiteral: (n, recurse) => n.elements.some(recurse),
395
- BinaryOp: (n, recurse) => recurse(n.left) || recurse(n.right),
396
- UnaryOp: (n, recurse) => recurse(n.argument),
397
- FunctionCall: (n, recurse) => n.args.some(recurse),
398
- Assignment: (n, recurse) => recurse(n.value),
399
- IfExpression: (n, recurse) => recurse(n.condition) || recurse(n.consequent) || recurse(n.alternate),
400
- ForExpression: (n, recurse) => {
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(n.variable);
403
- if (n.accumulator)
404
- innerBound.add(n.accumulator.name);
405
- return recurse(n.iterable) || (n.guard ? containsExternalReference(n.guard, innerBound) : false) || (n.accumulator ? recurse(n.accumulator.initial) : false) || containsExternalReference(n.body, innerBound);
406
- },
407
- IndexAccess: (n, recurse) => recurse(n.object) || recurse(n.index),
408
- RangeExpression: (n, recurse) => recurse(n.start) || recurse(n.end),
409
- PipeExpression: (n, recurse) => recurse(n.value) || n.args.some(recurse),
410
- Placeholder: () => false
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 firstType = typeOf(elements[0]);
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
- const codePoints = Array.from(target);
923
- const len2 = codePoints.length;
924
- const resolved2 = index < 0 ? len2 + index : index;
925
- if (resolved2 < 0 || resolved2 >= len2) {
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
- return codePoints[resolved2];
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
- const result = [];
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
- visit(node, {
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
- return visit(node, {
1143
- Program: generateProgram,
1144
- NumberLiteral: (n) => {
1145
- return String(n.value);
1146
- },
1147
- StringLiteral: (n) => {
1148
- return `"${escapeString(n.value)}"`;
1149
- },
1150
- BooleanLiteral: (n) => {
1151
- return n.value ? "true" : "false";
1152
- },
1153
- ArrayLiteral: (n, recurse) => {
1154
- return `[${n.elements.map(recurse).join(", ")}]`;
1155
- },
1156
- Identifier: (n) => {
1157
- return n.name;
1158
- },
1159
- BinaryOp: (n, recurse) => {
1160
- const left = recurse(n.left);
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(n.right, n.operator, false) || isPipeExpression(n.right) || isAssignment(n.right) || isRangeExpression(n.right) && opPrec >= 6;
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} ${n.operator} ${rightCode}`;
1168
- },
1169
- UnaryOp: (n, recurse) => {
1170
- const arg = recurse(n.argument);
1171
- const parensNeeded = isBinaryOp(n.argument) || isAssignment(n.argument) || isPipeExpression(n.argument) || isRangeExpression(n.argument);
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 `${n.operator}${argCode}`;
1174
- },
1175
- FunctionCall: (n, recurse) => {
1176
- const argsCode = n.args.map(recurse).join(", ");
1177
- return `${n.name}(${argsCode})`;
1178
- },
1179
- Assignment: (n, recurse) => {
1180
- const value = recurse(n.value);
1181
- return `${n.name} = ${value}`;
1182
- },
1183
- IfExpression: (n, recurse) => {
1184
- const condition = recurse(n.condition);
1185
- const consequent = recurse(n.consequent);
1186
- const alternate = recurse(n.alternate);
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
- ForExpression: (n, recurse) => {
1190
- const parts = [`for ${n.variable} in ${recurse(n.iterable)}`];
1191
- if (n.guard) {
1192
- parts.push(`when ${recurse(n.guard)}`);
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 (n.accumulator) {
1195
- parts.push(`into ${n.accumulator.name} = ${recurse(n.accumulator.initial)}`);
1252
+ if (node.accumulator) {
1253
+ parts.push(`into ${node.accumulator.name} = ${recurse(node.accumulator.initial)}`);
1196
1254
  }
1197
- parts.push(`then ${recurse(n.body)}`);
1255
+ parts.push(`then ${recurse(node.body)}`);
1198
1256
  return parts.join(" ");
1199
- },
1200
- IndexAccess: (n, recurse) => {
1201
- const object = recurse(n.object);
1202
- const index = recurse(n.index);
1203
- const needsParens2 = isBinaryOp(n.object) || isUnaryOp(n.object) || isAssignment(n.object) || isRangeExpression(n.object) || isPipeExpression(n.object);
1204
- const objectCode = needsParens2 ? `(${object})` : object;
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
- RangeExpression: (n, recurse) => {
1208
- const start = recurse(n.start);
1209
- const end = recurse(n.end);
1210
- const op = n.inclusive ? "..=" : "..";
1211
- const startNeedsParens = isBinaryOp(n.start) || isRangeExpression(n.start) || isIfExpression(n.start) || isForExpression(n.start) || isAssignment(n.start);
1212
- const endNeedsParens = isBinaryOp(n.end) || isRangeExpression(n.end) || isPipeExpression(n.end) || isAssignment(n.end);
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
- PipeExpression: (n, recurse) => {
1218
- const value = recurse(n.value);
1219
- const argsCode = n.args.map(recurse).join(", ");
1220
- const valueNeedsParens = isAssignment(n.value) || isIfExpression(n.value) || isForExpression(n.value);
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} |> ${n.name}(${argsCode})`;
1223
- },
1224
- Placeholder: () => "?"
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 evaluateNode(node, context, variables, externalVariables) {
1616
- return visit(node, {
1617
- Program: (n, recurse) => {
1618
- let result = 0;
1619
- for (const statement of n.statements) {
1620
- result = recurse(statement);
1621
- }
1622
- return result;
1623
- },
1624
- NumberLiteral: (n) => n.value,
1625
- StringLiteral: (n) => n.value,
1626
- BooleanLiteral: (n) => n.value,
1627
- ArrayLiteral: (n, recurse) => {
1628
- const elements = n.elements.map(recurse);
1629
- validateHomogeneousArray(elements);
1630
- return elements;
1631
- },
1632
- Identifier: (n) => {
1633
- const value = variables.get(n.name);
1634
- if (value === undefined) {
1635
- throw new Error(`Undefined variable: ${n.name}`);
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
- const left = recurse(n.left);
1659
- const right = recurse(n.right);
1660
- return evaluateBinaryOperation(n.operator, left, right);
1661
- },
1662
- UnaryOp: (n, recurse) => {
1663
- const arg = recurse(n.argument);
1664
- if (n.operator === "-") {
1665
- assertNumber(arg, "Operator '-' (unary)");
1666
- return -arg;
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
- if (n.operator === "!") {
1669
- assertBoolean(arg, "Operator '!'");
1670
- return !arg;
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
- throw new Error(`Unknown unary operator: ${String(n.operator)}`);
1673
- },
1674
- FunctionCall: (n, recurse) => {
1675
- const fn = context.functions?.[n.name];
1676
- if (fn === undefined) {
1677
- throw new Error(`Undefined function: ${n.name}`);
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
- if (typeof fn !== "function") {
1680
- throw new Error(`${n.name} is not a function`);
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
- const evaluatedArgs = n.args.map(recurse);
1683
- return fn(...evaluatedArgs);
1684
- },
1685
- Assignment: (n, recurse) => {
1686
- const value = recurse(n.value);
1687
- if (externalVariables.has(n.name)) {
1688
- const externalValue = variables.get(n.name);
1689
- if (externalValue !== undefined) {
1690
- return externalValue;
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
- variables.set(n.name, value);
1694
- return value;
1695
- },
1696
- IfExpression: (n, recurse) => {
1697
- const condition = recurse(n.condition);
1698
- assertBoolean(condition, "If condition");
1699
- return condition ? recurse(n.consequent) : recurse(n.alternate);
1700
- },
1701
- IndexAccess: (n, recurse) => {
1702
- const object = recurse(n.object);
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
- if (typeof object === "string") {
1708
- return resolveIndex(object, index);
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
- throw new TypeError(`Index access expected array or string, got ${typeOf(object)}`);
1711
- },
1712
- RangeExpression: (n, recurse) => {
1713
- const start = recurse(n.start);
1714
- const end = recurse(n.end);
1715
- assertNumber(start, "Range start");
1716
- assertNumber(end, "Range end");
1717
- return buildRange(start, end, n.inclusive);
1718
- },
1719
- PipeExpression: (n, recurse) => {
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
- if (typeof fn !== "function") {
1726
- throw new Error(`${n.name} is not a function`);
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
- const evaluatedArgs = n.args.map((arg) => arg.kind === 15 /* Placeholder */ ? pipedValue : recurse(arg));
1729
- return fn(...evaluatedArgs);
1730
- },
1731
- Placeholder: () => {
1732
- throw new Error("Placeholder outside pipe expression");
1733
- },
1734
- ForExpression: (n, recurse) => {
1735
- const iterable = recurse(n.iterable);
1736
- let items;
1737
- if (Array.isArray(iterable)) {
1738
- items = iterable;
1739
- } else if (typeof iterable === "string") {
1740
- items = Array.from(iterable);
1741
- } else {
1742
- throw new TypeError(`For expression expected array or string, got ${typeOf(iterable)}`);
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
- const previousValue = variables.get(n.variable);
1745
- const hadPreviousValue = variables.has(n.variable);
1746
- const restoreLoopVar = () => {
1747
- if (hadPreviousValue) {
1748
- variables.set(n.variable, previousValue);
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
- variables.delete(n.variable);
1827
+ throw new TypeError(`For expression expected array or string, got ${typeOf(iterable)}`);
1751
1828
  }
1752
- };
1753
- if (n.accumulator) {
1754
- let acc = recurse(n.accumulator.initial);
1755
- const prevAcc = variables.get(n.accumulator.name);
1756
- const hadPrevAcc = variables.has(n.accumulator.name);
1757
- for (const item of items) {
1758
- variables.set(n.variable, item);
1759
- if (n.guard) {
1760
- const guardValue = recurse(n.guard);
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
- variables.set(n.accumulator.name, acc);
1766
- acc = recurse(n.body);
1867
+ result.push(evalNode(node2.body));
1767
1868
  }
1768
- if (hadPrevAcc) {
1769
- variables.set(n.accumulator.name, prevAcc);
1869
+ if (hadPreviousValue) {
1870
+ variables.set(node2.variable, previousValue);
1770
1871
  } else {
1771
- variables.delete(n.accumulator.name);
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.push(recurse(n.body));
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
- const { variables } = run(input, context);
1806
- return Object.fromEntries(variables);
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
- return visit(node, {
1873
- Program: (n, recurse) => n.statements.some(recurse),
1874
- NumberLiteral: () => false,
1875
- StringLiteral: () => false,
1876
- BooleanLiteral: () => false,
1877
- Identifier: () => false,
1878
- ArrayLiteral: (n, recurse) => n.elements.some(recurse),
1879
- BinaryOp: (n, recurse) => recurse(n.left) || recurse(n.right),
1880
- UnaryOp: (n, recurse) => recurse(n.argument),
1881
- FunctionCall: () => true,
1882
- PipeExpression: () => true,
1883
- Placeholder: () => false,
1884
- Assignment: () => true,
1885
- IfExpression: (n, recurse) => recurse(n.condition) || recurse(n.consequent) || recurse(n.alternate),
1886
- ForExpression: (n, recurse) => recurse(n.iterable) || (n.guard ? recurse(n.guard) : false) || (n.accumulator ? recurse(n.accumulator.initial) : false) || recurse(n.body),
1887
- IndexAccess: (n, recurse) => recurse(n.object) || recurse(n.index),
1888
- RangeExpression: (n, recurse) => recurse(n.start) || recurse(n.end)
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
- if (isAssignment(stmt)) {
1921
- collectReferencedIdentifiers(stmt.value, referencedIds);
1922
- } else {
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
- visit(node, {
2002
- Program: (n, recurse) => {
2003
- for (const s of n.statements)
2004
- recurse(s);
2005
- },
2006
- NumberLiteral: () => {},
2007
- StringLiteral: () => {},
2008
- BooleanLiteral: () => {},
2009
- ArrayLiteral: (n, recurse) => {
2010
- for (const e of n.elements)
2011
- recurse(e);
2012
- },
2013
- Identifier: () => {},
2014
- BinaryOp: (n, recurse) => {
2015
- recurse(n.left);
2016
- recurse(n.right);
2017
- },
2018
- UnaryOp: (n, recurse) => {
2019
- recurse(n.argument);
2020
- },
2021
- FunctionCall: (n, recurse) => {
2022
- for (const a of n.args)
2023
- recurse(a);
2024
- },
2025
- Assignment: (n, recurse) => {
2026
- recurse(n.value);
2027
- },
2028
- IfExpression: (n, recurse) => {
2029
- recurse(n.condition);
2030
- recurse(n.consequent);
2031
- recurse(n.alternate);
2032
- },
2033
- ForExpression: (n, recurse) => {
2034
- vars.add(n.variable);
2035
- if (n.accumulator)
2036
- vars.add(n.accumulator.name);
2037
- recurse(n.iterable);
2038
- if (n.guard)
2039
- recurse(n.guard);
2040
- if (n.accumulator)
2041
- recurse(n.accumulator.initial);
2042
- recurse(n.body);
2043
- },
2044
- IndexAccess: (n, recurse) => {
2045
- recurse(n.object);
2046
- recurse(n.index);
2047
- },
2048
- RangeExpression: (n, recurse) => {
2049
- recurse(n.start);
2050
- recurse(n.end);
2051
- },
2052
- PipeExpression: (n, recurse) => {
2053
- recurse(n.value);
2054
- for (const a of n.args)
2055
- recurse(a);
2056
- },
2057
- Placeholder: () => {}
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
- visit(node, {
2062
- Program: (n, recurse) => {
2063
- for (const s of n.statements)
2064
- recurse(s);
2065
- },
2066
- NumberLiteral: () => {},
2067
- StringLiteral: () => {},
2068
- BooleanLiteral: () => {},
2069
- ArrayLiteral: (n, recurse) => {
2070
- for (const e of n.elements)
2071
- recurse(e);
2072
- },
2073
- Identifier: () => {},
2074
- BinaryOp: (n, recurse) => {
2075
- recurse(n.left);
2076
- recurse(n.right);
2077
- },
2078
- UnaryOp: (n, recurse) => {
2079
- recurse(n.argument);
2080
- },
2081
- FunctionCall: (n, recurse) => {
2082
- for (const a of n.args)
2083
- recurse(a);
2084
- },
2085
- Assignment: (n, recurse) => {
2086
- counts.set(n.name, (counts.get(n.name) ?? 0) + 1);
2087
- recurse(n.value);
2088
- },
2089
- IfExpression: (n, recurse) => {
2090
- recurse(n.condition);
2091
- recurse(n.consequent);
2092
- recurse(n.alternate);
2093
- },
2094
- ForExpression: (n, recurse) => {
2095
- recurse(n.iterable);
2096
- if (n.guard)
2097
- recurse(n.guard);
2098
- if (n.accumulator)
2099
- recurse(n.accumulator.initial);
2100
- recurse(n.body);
2101
- },
2102
- IndexAccess: (n, recurse) => {
2103
- recurse(n.object);
2104
- recurse(n.index);
2105
- },
2106
- RangeExpression: (n, recurse) => {
2107
- recurse(n.start);
2108
- recurse(n.end);
2109
- },
2110
- PipeExpression: (n, recurse) => {
2111
- recurse(n.value);
2112
- for (const a of n.args)
2113
- recurse(a);
2114
- },
2115
- Placeholder: () => {}
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
- return visit(node, {
2120
- Program: (n, recurse) => {
2121
- const result = program(n.statements.map(recurse));
2122
- if (n.trailingComments && n.trailingComments.length > 0) {
2123
- return { ...result, trailingComments: n.trailingComments };
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
- NumberLiteral: (n) => n,
2128
- StringLiteral: (n) => n,
2129
- BooleanLiteral: (n) => n,
2130
- ArrayLiteral: (n, recurse) => preserveComments(n, array(n.elements.map(recurse))),
2131
- Identifier: (n) => {
2132
- const replacement = knownValues.get(n.name);
2133
- return replacement ? preserveComments(n, replacement) : n;
2134
- },
2135
- BinaryOp: (n, recurse) => preserveComments(n, binaryOp(recurse(n.left), n.operator, recurse(n.right))),
2136
- UnaryOp: (n, recurse) => preserveComments(n, unaryOp(n.operator, recurse(n.argument))),
2137
- FunctionCall: (n, recurse) => preserveComments(n, functionCall(n.name, n.args.map(recurse))),
2138
- Assignment: (n, recurse) => preserveComments(n, assign(n.name, recurse(n.value))),
2139
- IfExpression: (n, recurse) => preserveComments(n, ifExpr(recurse(n.condition), recurse(n.consequent), recurse(n.alternate))),
2140
- ForExpression: (n, recurse) => {
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(n.variable);
2143
- if (n.accumulator)
2144
- innerKnown.delete(n.accumulator.name);
2145
- const iterable = recurse(n.iterable);
2146
- const guard = n.guard ? substituteIdentifiers(n.guard, innerKnown) : null;
2147
- const accumulator = n.accumulator ? { name: n.accumulator.name, initial: recurse(n.accumulator.initial) } : null;
2148
- const body = substituteIdentifiers(n.body, innerKnown);
2149
- return preserveComments(n, forExpr(n.variable, iterable, guard, accumulator, body));
2150
- },
2151
- IndexAccess: (n, recurse) => preserveComments(n, indexAccess(recurse(n.object), recurse(n.index))),
2152
- RangeExpression: (n, recurse) => preserveComments(n, rangeExpr(recurse(n.start), recurse(n.end), n.inclusive)),
2153
- PipeExpression: (n, recurse) => preserveComments(n, pipeExpr(recurse(n.value), n.name, n.args.map(recurse))),
2154
- Placeholder: (n) => n
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
- return visit(node, {
2183
- NumberLiteral: (n) => n,
2184
- StringLiteral: (n) => n,
2185
- BooleanLiteral: (n) => n,
2186
- ArrayLiteral: (n, recurse) => {
2187
- const elements = n.elements.map(recurse);
2188
- return preserveComments(n, array(elements));
2189
- },
2190
- Identifier: (n) => n,
2191
- BinaryOp: (n, recurse) => {
2192
- const left = recurse(n.left);
2193
- const right = recurse(n.right);
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(n.operator, left.value, right.value);
2313
+ const result = evaluateBinaryOperation(node.operator, left.value, right.value);
2196
2314
  if (typeof result === "number")
2197
- return preserveComments(n, number(result));
2315
+ return preserveComments(node, number(result));
2198
2316
  if (typeof result === "boolean")
2199
- return preserveComments(n, boolean(result));
2317
+ return preserveComments(node, boolean(result));
2200
2318
  }
2201
2319
  if (isStringLiteral(left) && isStringLiteral(right)) {
2202
- if (n.operator === "+")
2203
- return preserveComments(n, string(left.value + right.value));
2204
- if (n.operator === "<" || n.operator === ">" || n.operator === "<=" || n.operator === ">=") {
2205
- const result = evaluateBinaryOperation(n.operator, left.value, right.value);
2206
- return preserveComments(n, boolean(result));
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 (n.operator === "==")
2209
- return preserveComments(n, boolean(left.value === right.value));
2210
- if (n.operator === "!=")
2211
- return preserveComments(n, boolean(left.value !== right.value));
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 (n.operator === "&&")
2215
- return preserveComments(n, boolean(left.value && right.value));
2216
- if (n.operator === "||")
2217
- return preserveComments(n, boolean(left.value || right.value));
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));
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 (n.operator === "==")
2226
- return preserveComments(n, boolean(false));
2227
- if (n.operator === "!=")
2228
- return preserveComments(n, boolean(true));
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
- return preserveComments(n, binaryOp(left, n.operator, right));
2232
- },
2233
- UnaryOp: (n, recurse) => {
2234
- const argument = recurse(n.argument);
2235
- if (n.operator === "-" && isNumberLiteral(argument)) {
2236
- return preserveComments(n, number(-argument.value));
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 (n.operator === "!" && isBooleanLiteral(argument)) {
2239
- return preserveComments(n, boolean(!argument.value));
2358
+ if (node.operator === "!" && isBooleanLiteral(argument)) {
2359
+ return preserveComments(node, boolean(!argument.value));
2240
2360
  }
2241
- return preserveComments(n, unaryOp(n.operator, argument));
2242
- },
2243
- FunctionCall: (n, recurse) => {
2244
- const optimizedArgs = n.args.map(recurse);
2245
- return preserveComments(n, functionCall(n.name, optimizedArgs));
2246
- },
2247
- Assignment: (n, recurse) => {
2248
- return preserveComments(n, assign(n.name, recurse(n.value)));
2249
- },
2250
- IfExpression: (n, recurse) => {
2251
- const condition = recurse(n.condition);
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(n.consequent) : recurse(n.alternate);
2254
- return preserveComments(n, result);
2380
+ const result = condition.value ? recurse(node.consequent) : recurse(node.alternate);
2381
+ return preserveComments(node, result);
2255
2382
  }
2256
- const consequent = recurse(n.consequent);
2257
- const alternate = recurse(n.alternate);
2258
- return preserveComments(n, ifExpr(condition, consequent, alternate));
2259
- },
2260
- ForExpression: (n, recurse) => {
2261
- const iterable = recurse(n.iterable);
2262
- const guard = n.guard ? recurse(n.guard) : null;
2263
- const accumulator = n.accumulator ? { name: n.accumulator.name, initial: recurse(n.accumulator.initial) } : null;
2264
- const body = recurse(n.body);
2265
- return preserveComments(n, forExpr(n.variable, iterable, guard, accumulator, body));
2266
- },
2267
- IndexAccess: (n, recurse) => {
2268
- const object = recurse(n.object);
2269
- const index = recurse(n.index);
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(n, object.elements[resolved]);
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(n, string(char));
2415
+ return preserveComments(node, string(char));
2284
2416
  } catch {}
2285
2417
  }
2286
- return preserveComments(n, indexAccess(object, index));
2287
- },
2288
- RangeExpression: (n, recurse) => {
2289
- const start = recurse(n.start);
2290
- const end = recurse(n.end);
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 = n.inclusive ? end.value + 1 : end.value;
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, n.inclusive);
2297
- return preserveComments(n, array(values.map(number)));
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
- return preserveComments(n, rangeExpr(start, end, n.inclusive));
2302
- },
2303
- PipeExpression: (n, recurse) => {
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,