littlewing 2.1.1 → 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,78 +338,139 @@ 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);
415
448
  }
449
+ // src/errors.ts
450
+ class ParseError extends Error {
451
+ start;
452
+ end;
453
+ name = "ParseError";
454
+ constructor(message, start, end) {
455
+ super(message);
456
+ this.start = start;
457
+ this.end = end;
458
+ }
459
+ }
460
+ function toLineColumn(source, offset) {
461
+ let line = 1;
462
+ let column = 1;
463
+ const clamped = Math.min(offset, source.length);
464
+ for (let i = 0;i < clamped; i++) {
465
+ if (source.charCodeAt(i) === 10) {
466
+ line++;
467
+ column = 1;
468
+ } else {
469
+ column++;
470
+ }
471
+ }
472
+ return { line, column };
473
+ }
416
474
  // src/lexer.ts
417
475
  var KEYWORDS = new Map([
418
476
  ["if", 28 /* If */],
@@ -594,7 +652,7 @@ function nextToken(cursor) {
594
652
  }
595
653
  return [18 /* DotDot */, start, cursor.pos];
596
654
  }
597
- throw new Error(`Unexpected character '${String.fromCharCode(ch)}' at position ${start}`);
655
+ throw new ParseError(`Unexpected character '${String.fromCharCode(ch)}'`, start, cursor.pos);
598
656
  case 44:
599
657
  advance(cursor);
600
658
  return [25 /* Comma */, start, cursor.pos];
@@ -607,7 +665,7 @@ function nextToken(cursor) {
607
665
  advance(cursor);
608
666
  return [16 /* And */, start, cursor.pos];
609
667
  }
610
- throw new Error(`Unexpected character '${String.fromCharCode(ch)}' at position ${start}`);
668
+ throw new ParseError(`Unexpected character '${String.fromCharCode(ch)}'`, start, cursor.pos);
611
669
  case 124:
612
670
  advance(cursor);
613
671
  if (peek(cursor) === 124) {
@@ -618,12 +676,13 @@ function nextToken(cursor) {
618
676
  advance(cursor);
619
677
  return [26 /* Pipe */, start, cursor.pos];
620
678
  }
621
- throw new Error(`Unexpected character '${String.fromCharCode(ch)}' at position ${start}`);
679
+ throw new ParseError(`Unexpected character '${String.fromCharCode(ch)}'`, start, cursor.pos);
622
680
  case 63:
623
681
  advance(cursor);
624
682
  return [27 /* Question */, start, cursor.pos];
625
683
  default:
626
- throw new Error(`Unexpected character '${String.fromCharCode(ch)}' at position ${start}`);
684
+ advance(cursor);
685
+ throw new ParseError(`Unexpected character '${String.fromCharCode(ch)}'`, start, cursor.pos);
627
686
  }
628
687
  }
629
688
  function lexString(cursor) {
@@ -640,7 +699,7 @@ function lexString(cursor) {
640
699
  cursor.pos++;
641
700
  }
642
701
  }
643
- throw new Error(`Unterminated string at position ${start}`);
702
+ throw new ParseError("Unterminated string", start, cursor.pos);
644
703
  }
645
704
  function lexNumber(cursor) {
646
705
  const start = cursor.pos;
@@ -789,7 +848,17 @@ function concatenateArrays(a, b) {
789
848
  function validateHomogeneousArray(elements) {
790
849
  if (elements.length <= 1)
791
850
  return;
792
- 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);
793
862
  for (let i = 1;i < elements.length; i++) {
794
863
  const elemType = typeOf(elements[i]);
795
864
  if (elemType !== firstType) {
@@ -893,13 +962,29 @@ function resolveIndex(target, index) {
893
962
  throw new TypeError(`Index must be an integer, got ${index}`);
894
963
  }
895
964
  if (typeof target === "string") {
896
- const codePoints = Array.from(target);
897
- const len2 = codePoints.length;
898
- const resolved2 = index < 0 ? len2 + index : index;
899
- 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) {
900
979
  throw new RangeError(`Index ${index} out of bounds for length ${len2}`);
901
980
  }
902
- 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}`);
903
988
  }
904
989
  const len = target.length;
905
990
  const resolved = index < 0 ? len + index : index;
@@ -919,11 +1004,7 @@ function buildRange(start, end, inclusive) {
919
1004
  throw new RangeError(`Range start (${start}) must not exceed end (${end})`);
920
1005
  }
921
1006
  const limit = inclusive ? end + 1 : end;
922
- const result = [];
923
- for (let i = start;i < limit; i++) {
924
- result.push(i);
925
- }
926
- return result;
1007
+ return Array.from({ length: limit - start }, (_, i) => start + i);
927
1008
  }
928
1009
  function getOperatorPrecedence(operator) {
929
1010
  switch (operator) {
@@ -953,69 +1034,75 @@ function getOperatorPrecedence(operator) {
953
1034
  }
954
1035
  function collectAllIdentifiers(node) {
955
1036
  const identifiers = new Set;
956
- visit(node, {
957
- Program: (n, recurse) => {
958
- for (const stmt of n.statements) {
959
- recurse(stmt);
960
- }
961
- },
962
- NumberLiteral: () => {},
963
- StringLiteral: () => {},
964
- BooleanLiteral: () => {},
965
- ArrayLiteral: (n, recurse) => {
966
- for (const elem of n.elements) {
967
- recurse(elem);
968
- }
969
- },
970
- Identifier: (n) => {
971
- identifiers.add(n.name);
972
- },
973
- BinaryOp: (n, recurse) => {
974
- recurse(n.left);
975
- recurse(n.right);
976
- },
977
- UnaryOp: (n, recurse) => {
978
- recurse(n.argument);
979
- },
980
- FunctionCall: (n, recurse) => {
981
- for (const arg of n.args) {
982
- recurse(arg);
983
- }
984
- },
985
- Assignment: (n, recurse) => {
986
- recurse(n.value);
987
- },
988
- IfExpression: (n, recurse) => {
989
- recurse(n.condition);
990
- recurse(n.consequent);
991
- recurse(n.alternate);
992
- },
993
- ForExpression: (n, recurse) => {
994
- recurse(n.iterable);
995
- if (n.guard)
996
- recurse(n.guard);
997
- if (n.accumulator)
998
- recurse(n.accumulator.initial);
999
- recurse(n.body);
1000
- },
1001
- IndexAccess: (n, recurse) => {
1002
- recurse(n.object);
1003
- recurse(n.index);
1004
- },
1005
- RangeExpression: (n, recurse) => {
1006
- recurse(n.start);
1007
- recurse(n.end);
1008
- },
1009
- PipeExpression: (n, recurse) => {
1010
- recurse(n.value);
1011
- for (const arg of n.args) {
1012
- recurse(arg);
1013
- }
1014
- },
1015
- Placeholder: () => {}
1016
- });
1037
+ collectIdentifiers(node, identifiers, new Set);
1017
1038
  return identifiers;
1018
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
+ }
1019
1106
  function getTokenPrecedence(kind) {
1020
1107
  switch (kind) {
1021
1108
  case 24 /* Eq */:
@@ -1113,90 +1200,88 @@ function generate(node) {
1113
1200
  return code;
1114
1201
  }
1115
1202
  function generateNode(node) {
1116
- return visit(node, {
1117
- Program: generateProgram,
1118
- NumberLiteral: (n) => {
1119
- return String(n.value);
1120
- },
1121
- StringLiteral: (n) => {
1122
- return `"${escapeString(n.value)}"`;
1123
- },
1124
- BooleanLiteral: (n) => {
1125
- return n.value ? "true" : "false";
1126
- },
1127
- ArrayLiteral: (n, recurse) => {
1128
- return `[${n.elements.map(recurse).join(", ")}]`;
1129
- },
1130
- Identifier: (n) => {
1131
- return n.name;
1132
- },
1133
- BinaryOp: (n, recurse) => {
1134
- const left = recurse(n.left);
1135
- const right = recurse(n.right);
1136
- const opPrec = getOperatorPrecedence(n.operator);
1137
- 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;
1138
1222
  const leftCode = leftNeedsParens ? `(${left})` : left;
1139
- 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;
1140
1224
  const rightCode = rightNeedsParens ? `(${right})` : right;
1141
- return `${leftCode} ${n.operator} ${rightCode}`;
1142
- },
1143
- UnaryOp: (n, recurse) => {
1144
- const arg = recurse(n.argument);
1145
- 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);
1146
1230
  const argCode = parensNeeded ? `(${arg})` : arg;
1147
- return `${n.operator}${argCode}`;
1148
- },
1149
- FunctionCall: (n, recurse) => {
1150
- const argsCode = n.args.map(recurse).join(", ");
1151
- return `${n.name}(${argsCode})`;
1152
- },
1153
- Assignment: (n, recurse) => {
1154
- const value = recurse(n.value);
1155
- return `${n.name} = ${value}`;
1156
- },
1157
- IfExpression: (n, recurse) => {
1158
- const condition = recurse(n.condition);
1159
- const consequent = recurse(n.consequent);
1160
- 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);
1161
1245
  return `if ${condition} then ${consequent} else ${alternate}`;
1162
- },
1163
- ForExpression: (n, recurse) => {
1164
- const parts = [`for ${n.variable} in ${recurse(n.iterable)}`];
1165
- if (n.guard) {
1166
- 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)}`);
1167
1251
  }
1168
- if (n.accumulator) {
1169
- 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)}`);
1170
1254
  }
1171
- parts.push(`then ${recurse(n.body)}`);
1255
+ parts.push(`then ${recurse(node.body)}`);
1172
1256
  return parts.join(" ");
1173
- },
1174
- IndexAccess: (n, recurse) => {
1175
- const object = recurse(n.object);
1176
- const index = recurse(n.index);
1177
- const needsParens2 = isBinaryOp(n.object) || isUnaryOp(n.object) || isAssignment(n.object) || isRangeExpression(n.object) || isPipeExpression(n.object);
1178
- 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;
1179
1263
  return `${objectCode}[${index}]`;
1180
- },
1181
- RangeExpression: (n, recurse) => {
1182
- const start = recurse(n.start);
1183
- const end = recurse(n.end);
1184
- const op = n.inclusive ? "..=" : "..";
1185
- const startNeedsParens = isBinaryOp(n.start) || isRangeExpression(n.start) || isIfExpression(n.start) || isForExpression(n.start) || isAssignment(n.start);
1186
- 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);
1187
1271
  const startCode = startNeedsParens ? `(${start})` : start;
1188
1272
  const endCode = endNeedsParens ? `(${end})` : end;
1189
1273
  return `${startCode}${op}${endCode}`;
1190
- },
1191
- PipeExpression: (n, recurse) => {
1192
- const value = recurse(n.value);
1193
- const argsCode = n.args.map(recurse).join(", ");
1194
- 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);
1195
1279
  const valueCode = valueNeedsParens ? `(${value})` : value;
1196
- return `${valueCode} |> ${n.name}(${argsCode})`;
1197
- },
1198
- Placeholder: () => "?"
1199
- });
1280
+ return `${valueCode} |> ${node.name}(${argsCode})`;
1281
+ }
1282
+ case 15 /* Placeholder */:
1283
+ return "?";
1284
+ }
1200
1285
  }
1201
1286
  // src/parser.ts
1202
1287
  var BINARY_OPERATOR_TOKENS = new Set([
@@ -1230,13 +1315,13 @@ function parse(source) {
1230
1315
  statements.push(parseExpression(state, 0));
1231
1316
  }
1232
1317
  if (statements.length === 0) {
1233
- throw new Error("Empty program");
1318
+ throw new ParseError("Empty program", 0, 0);
1234
1319
  }
1235
1320
  const annotated = attachComments(source, statements, statementOffsets, cursor.comments);
1236
1321
  if (annotated.statements.length === 1 && !annotated.programTrailing) {
1237
1322
  const stmt = annotated.statements[0];
1238
1323
  if (stmt === undefined) {
1239
- throw new Error("Unexpected empty statements array");
1324
+ throw new ParseError("Unexpected empty statements array", 0, 0);
1240
1325
  }
1241
1326
  return stmt;
1242
1327
  }
@@ -1346,7 +1431,7 @@ function parseExpression(state, minPrecedence) {
1346
1431
  advance2(state);
1347
1432
  const index = parseExpression(state, 0);
1348
1433
  if (peekKind(state) !== 23 /* RBracket */) {
1349
- throw new Error("Expected closing bracket");
1434
+ throw new ParseError("Expected closing bracket", state.currentToken[1], state.currentToken[2]);
1350
1435
  }
1351
1436
  advance2(state);
1352
1437
  left = indexAccess(left, index);
@@ -1377,7 +1462,7 @@ function parsePrefix(state) {
1377
1462
  advance2(state);
1378
1463
  const expr = parseExpression(state, 0);
1379
1464
  if (peekKind(state) !== 21 /* RParen */) {
1380
- throw new Error("Expected closing parenthesis");
1465
+ throw new ParseError("Expected closing parenthesis", state.currentToken[1], state.currentToken[2]);
1381
1466
  }
1382
1467
  advance2(state);
1383
1468
  return expr;
@@ -1393,7 +1478,7 @@ function parsePrefix(state) {
1393
1478
  }
1394
1479
  }
1395
1480
  if (peekKind(state) !== 23 /* RBracket */) {
1396
- throw new Error("Expected closing bracket");
1481
+ throw new ParseError("Expected closing bracket", state.currentToken[1], state.currentToken[2]);
1397
1482
  }
1398
1483
  advance2(state);
1399
1484
  return array(elements);
@@ -1402,12 +1487,12 @@ function parsePrefix(state) {
1402
1487
  advance2(state);
1403
1488
  const condition = parseExpression(state, 0);
1404
1489
  if (peekKind(state) !== 29 /* Then */) {
1405
- throw new Error('Expected "then" in if expression');
1490
+ throw new ParseError('Expected "then" in if expression', state.currentToken[1], state.currentToken[2]);
1406
1491
  }
1407
1492
  advance2(state);
1408
1493
  const consequent = parseExpression(state, 0);
1409
1494
  if (peekKind(state) !== 30 /* Else */) {
1410
- throw new Error('Expected "else" in if expression');
1495
+ throw new ParseError('Expected "else" in if expression', state.currentToken[1], state.currentToken[2]);
1411
1496
  }
1412
1497
  advance2(state);
1413
1498
  const alternate = parseExpression(state, 0);
@@ -1416,12 +1501,12 @@ function parsePrefix(state) {
1416
1501
  if (tokenKind === 31 /* For */) {
1417
1502
  advance2(state);
1418
1503
  if (peekKind(state) !== 1 /* Identifier */) {
1419
- throw new Error('Expected identifier after "for"');
1504
+ throw new ParseError('Expected identifier after "for"', state.currentToken[1], state.currentToken[2]);
1420
1505
  }
1421
1506
  const variableName = readText(state.cursor, state.currentToken);
1422
1507
  advance2(state);
1423
1508
  if (peekKind(state) !== 32 /* In */) {
1424
- throw new Error('Expected "in" in for expression');
1509
+ throw new ParseError('Expected "in" in for expression', state.currentToken[1], state.currentToken[2]);
1425
1510
  }
1426
1511
  advance2(state);
1427
1512
  const iterable = parseExpression(state, 0);
@@ -1434,19 +1519,19 @@ function parsePrefix(state) {
1434
1519
  if (peekKind(state) === 34 /* Into */) {
1435
1520
  advance2(state);
1436
1521
  if (peekKind(state) !== 1 /* Identifier */) {
1437
- throw new Error('Expected identifier after "into"');
1522
+ throw new ParseError('Expected identifier after "into"', state.currentToken[1], state.currentToken[2]);
1438
1523
  }
1439
1524
  const accName = readText(state.cursor, state.currentToken);
1440
1525
  advance2(state);
1441
1526
  if (peekKind(state) !== 24 /* Eq */) {
1442
- throw new Error('Expected "=" after accumulator name');
1527
+ throw new ParseError('Expected "=" after accumulator name', state.currentToken[1], state.currentToken[2]);
1443
1528
  }
1444
1529
  advance2(state);
1445
1530
  const initial = parseExpression(state, 0);
1446
1531
  accumulator = { name: accName, initial };
1447
1532
  }
1448
1533
  if (peekKind(state) !== 29 /* Then */) {
1449
- throw new Error('Expected "then" in for expression');
1534
+ throw new ParseError('Expected "then" in for expression', state.currentToken[1], state.currentToken[2]);
1450
1535
  }
1451
1536
  advance2(state);
1452
1537
  const body = parseExpression(state, 0);
@@ -1478,7 +1563,7 @@ function parsePrefix(state) {
1478
1563
  advance2(state);
1479
1564
  const args = parseFunctionArguments(state);
1480
1565
  if (peekKind(state) !== 21 /* RParen */) {
1481
- throw new Error("Expected closing parenthesis");
1566
+ throw new ParseError("Expected closing parenthesis", state.currentToken[1], state.currentToken[2]);
1482
1567
  }
1483
1568
  advance2(state);
1484
1569
  return functionCall(name, args);
@@ -1487,13 +1572,13 @@ function parsePrefix(state) {
1487
1572
  }
1488
1573
  const token = state.currentToken;
1489
1574
  const tokenText = readText(state.cursor, token);
1490
- throw new Error(`Unexpected token: ${tokenText}`);
1575
+ throw new ParseError(`Unexpected token: ${tokenText}`, token[1], token[2]);
1491
1576
  }
1492
1577
  function parseInfix(state, left, precedence) {
1493
1578
  const tokenKind = peekKind(state);
1494
1579
  if (tokenKind === 24 /* Eq */) {
1495
1580
  if (left.kind !== 2 /* Identifier */) {
1496
- throw new Error("Invalid assignment target");
1581
+ throw new ParseError("Invalid assignment target", state.currentToken[1], state.currentToken[2]);
1497
1582
  }
1498
1583
  const identName = left.name;
1499
1584
  advance2(state);
@@ -1509,17 +1594,17 @@ function parseInfix(state, left, precedence) {
1509
1594
  if (tokenKind === 26 /* Pipe */) {
1510
1595
  advance2(state);
1511
1596
  if (peekKind(state) !== 1 /* Identifier */) {
1512
- throw new Error("Expected function name after |>");
1597
+ throw new ParseError("Expected function name after |>", state.currentToken[1], state.currentToken[2]);
1513
1598
  }
1514
1599
  const name = readText(state.cursor, state.currentToken);
1515
1600
  advance2(state);
1516
1601
  if (peekKind(state) !== 20 /* LParen */) {
1517
- throw new Error("Expected ( after function name in pipe expression");
1602
+ throw new ParseError("Expected ( after function name in pipe expression", state.currentToken[1], state.currentToken[2]);
1518
1603
  }
1519
1604
  advance2(state);
1520
1605
  const args = parsePipeArguments(state);
1521
1606
  if (peekKind(state) !== 21 /* RParen */) {
1522
- throw new Error("Expected closing parenthesis");
1607
+ throw new ParseError("Expected closing parenthesis", state.currentToken[1], state.currentToken[2]);
1523
1608
  }
1524
1609
  advance2(state);
1525
1610
  let hasPlaceholder = false;
@@ -1530,7 +1615,7 @@ function parseInfix(state, left, precedence) {
1530
1615
  }
1531
1616
  }
1532
1617
  if (!hasPlaceholder) {
1533
- throw new Error("Pipe expression requires at least one ? placeholder");
1618
+ throw new ParseError("Pipe expression requires at least one ? placeholder", state.currentToken[1], state.currentToken[2]);
1534
1619
  }
1535
1620
  return pipeExpr(left, name, args);
1536
1621
  }
@@ -1541,7 +1626,7 @@ function parseInfix(state, left, precedence) {
1541
1626
  const right = parseExpression(state, isRightAssociative ? precedence : precedence + 1);
1542
1627
  return binaryOp(left, operator, right);
1543
1628
  }
1544
- throw new Error("Unexpected token in infix position");
1629
+ throw new ParseError("Unexpected token in infix position", state.currentToken[1], state.currentToken[2]);
1545
1630
  }
1546
1631
  function parseFunctionArguments(state) {
1547
1632
  if (peekKind(state) === 21 /* RParen */) {
@@ -1586,198 +1671,224 @@ function advance2(state) {
1586
1671
  }
1587
1672
 
1588
1673
  // src/interpreter.ts
1589
- function evaluateNode(node, context, variables, externalVariables) {
1590
- return visit(node, {
1591
- Program: (n, recurse) => {
1592
- let result = 0;
1593
- for (const statement of n.statements) {
1594
- result = recurse(statement);
1595
- }
1596
- return result;
1597
- },
1598
- NumberLiteral: (n) => n.value,
1599
- StringLiteral: (n) => n.value,
1600
- BooleanLiteral: (n) => n.value,
1601
- ArrayLiteral: (n, recurse) => {
1602
- const elements = n.elements.map(recurse);
1603
- validateHomogeneousArray(elements);
1604
- return elements;
1605
- },
1606
- Identifier: (n) => {
1607
- const value = variables.get(n.name);
1608
- if (value === undefined) {
1609
- throw new Error(`Undefined variable: ${n.name}`);
1610
- }
1611
- return value;
1612
- },
1613
- BinaryOp: (n, recurse) => {
1614
- if (n.operator === "&&") {
1615
- const left2 = recurse(n.left);
1616
- assertBoolean(left2, "Operator '&&'", "left");
1617
- if (!left2)
1618
- return false;
1619
- const right2 = recurse(n.right);
1620
- assertBoolean(right2, "Operator '&&'", "right");
1621
- return right2;
1622
- }
1623
- if (n.operator === "||") {
1624
- const left2 = recurse(n.left);
1625
- assertBoolean(left2, "Operator '||'", "left");
1626
- if (left2)
1627
- return true;
1628
- const right2 = recurse(n.right);
1629
- assertBoolean(right2, "Operator '||'", "right");
1630
- 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;
1631
1695
  }
1632
- const left = recurse(n.left);
1633
- const right = recurse(n.right);
1634
- return evaluateBinaryOperation(n.operator, left, right);
1635
- },
1636
- UnaryOp: (n, recurse) => {
1637
- const arg = recurse(n.argument);
1638
- if (n.operator === "-") {
1639
- assertNumber(arg, "Operator '-' (unary)");
1640
- 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;
1641
1710
  }
1642
- if (n.operator === "!") {
1643
- assertBoolean(arg, "Operator '!'");
1644
- 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;
1645
1717
  }
1646
- throw new Error(`Unknown unary operator: ${String(n.operator)}`);
1647
- },
1648
- FunctionCall: (n, recurse) => {
1649
- const fn = context.functions?.[n.name];
1650
- if (fn === undefined) {
1651
- 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));
1652
1738
  }
1653
- if (typeof fn !== "function") {
1654
- 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)}`);
1655
1750
  }
1656
- const evaluatedArgs = n.args.map(recurse);
1657
- return fn(...evaluatedArgs);
1658
- },
1659
- Assignment: (n, recurse) => {
1660
- const value = recurse(n.value);
1661
- if (externalVariables.has(n.name)) {
1662
- const externalValue = variables.get(n.name);
1663
- if (externalValue !== undefined) {
1664
- 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`);
1665
1758
  }
1759
+ const nodeArgs = node2.args;
1760
+ const evaluatedArgs = [];
1761
+ for (let i = 0;i < nodeArgs.length; i++) {
1762
+ evaluatedArgs.push(evalNode(nodeArgs[i]));
1763
+ }
1764
+ return fn(...evaluatedArgs);
1666
1765
  }
1667
- variables.set(n.name, value);
1668
- return value;
1669
- },
1670
- IfExpression: (n, recurse) => {
1671
- const condition = recurse(n.condition);
1672
- assertBoolean(condition, "If condition");
1673
- return condition ? recurse(n.consequent) : recurse(n.alternate);
1674
- },
1675
- IndexAccess: (n, recurse) => {
1676
- const object = recurse(n.object);
1677
- const index = recurse(n.index);
1678
- if (Array.isArray(object)) {
1679
- 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;
1680
1776
  }
1681
- if (typeof object === "string") {
1682
- 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);
1683
1781
  }
1684
- throw new TypeError(`Index access expected array or string, got ${typeOf(object)}`);
1685
- },
1686
- RangeExpression: (n, recurse) => {
1687
- const start = recurse(n.start);
1688
- const end = recurse(n.end);
1689
- assertNumber(start, "Range start");
1690
- assertNumber(end, "Range end");
1691
- return buildRange(start, end, n.inclusive);
1692
- },
1693
- PipeExpression: (n, recurse) => {
1694
- const pipedValue = recurse(n.value);
1695
- const fn = context.functions?.[n.name];
1696
- if (fn === undefined) {
1697
- 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)}`);
1698
1792
  }
1699
- if (typeof fn !== "function") {
1700
- 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);
1701
1799
  }
1702
- const evaluatedArgs = n.args.map((arg) => arg.kind === 15 /* Placeholder */ ? pipedValue : recurse(arg));
1703
- return fn(...evaluatedArgs);
1704
- },
1705
- Placeholder: () => {
1706
- throw new Error("Placeholder outside pipe expression");
1707
- },
1708
- ForExpression: (n, recurse) => {
1709
- const iterable = recurse(n.iterable);
1710
- let items;
1711
- if (Array.isArray(iterable)) {
1712
- items = iterable;
1713
- } else if (typeof iterable === "string") {
1714
- items = Array.from(iterable);
1715
- } else {
1716
- 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);
1717
1816
  }
1718
- const previousValue = variables.get(n.variable);
1719
- const hadPreviousValue = variables.has(n.variable);
1720
- const restoreLoopVar = () => {
1721
- if (hadPreviousValue) {
1722
- 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;
1723
1826
  } else {
1724
- variables.delete(n.variable);
1827
+ throw new TypeError(`For expression expected array or string, got ${typeOf(iterable)}`);
1725
1828
  }
1726
- };
1727
- if (n.accumulator) {
1728
- let acc = recurse(n.accumulator.initial);
1729
- const prevAcc = variables.get(n.accumulator.name);
1730
- const hadPrevAcc = variables.has(n.accumulator.name);
1731
- for (const item of items) {
1732
- variables.set(n.variable, item);
1733
- if (n.guard) {
1734
- 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);
1735
1863
  assertBoolean(guardValue, "For guard");
1736
1864
  if (!guardValue)
1737
1865
  continue;
1738
1866
  }
1739
- variables.set(n.accumulator.name, acc);
1740
- acc = recurse(n.body);
1867
+ result.push(evalNode(node2.body));
1741
1868
  }
1742
- if (hadPrevAcc) {
1743
- variables.set(n.accumulator.name, prevAcc);
1869
+ if (hadPreviousValue) {
1870
+ variables.set(node2.variable, previousValue);
1744
1871
  } else {
1745
- variables.delete(n.accumulator.name);
1746
- }
1747
- restoreLoopVar();
1748
- return acc;
1749
- }
1750
- const result = [];
1751
- for (const item of items) {
1752
- variables.set(n.variable, item);
1753
- if (n.guard) {
1754
- const guardValue = recurse(n.guard);
1755
- assertBoolean(guardValue, "For guard");
1756
- if (!guardValue)
1757
- continue;
1872
+ variables.delete(node2.variable);
1758
1873
  }
1759
- result.push(recurse(n.body));
1874
+ validateHomogeneousArray(result);
1875
+ return result;
1760
1876
  }
1761
- restoreLoopVar();
1762
- validateHomogeneousArray(result);
1763
- return result;
1764
1877
  }
1765
- });
1766
- }
1767
- function run(input, context = {}) {
1768
- const node = typeof input === "string" ? parse(input) : input;
1769
- const entries = Object.entries(context.variables || {});
1770
- const variables = new Map(entries);
1771
- const externalVariables = new Set(entries.map(([key]) => key));
1772
- const value = evaluateNode(node, context, variables, externalVariables);
1773
- return { value, variables };
1878
+ }
1774
1879
  }
1775
1880
  function evaluate(input, context = {}) {
1776
1881
  return run(input, context).value;
1777
1882
  }
1778
1883
  function evaluateScope(input, context = {}) {
1779
- const { variables } = run(input, context);
1780
- 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
+ };
1781
1892
  }
1782
1893
  // src/optimizer.ts
1783
1894
  function preserveComments(original, replacement) {
@@ -1843,24 +1954,34 @@ function isLiteral(node) {
1843
1954
  return isNumberLiteral(node) || isStringLiteral(node) || isBooleanLiteral(node);
1844
1955
  }
1845
1956
  function mightHaveSideEffects(node) {
1846
- return visit(node, {
1847
- Program: (n, recurse) => n.statements.some(recurse),
1848
- NumberLiteral: () => false,
1849
- StringLiteral: () => false,
1850
- BooleanLiteral: () => false,
1851
- Identifier: () => false,
1852
- ArrayLiteral: (n, recurse) => n.elements.some(recurse),
1853
- BinaryOp: (n, recurse) => recurse(n.left) || recurse(n.right),
1854
- UnaryOp: (n, recurse) => recurse(n.argument),
1855
- FunctionCall: () => true,
1856
- PipeExpression: () => true,
1857
- Placeholder: () => false,
1858
- Assignment: () => true,
1859
- IfExpression: (n, recurse) => recurse(n.condition) || recurse(n.consequent) || recurse(n.alternate),
1860
- ForExpression: (n, recurse) => recurse(n.iterable) || (n.guard ? recurse(n.guard) : false) || (n.accumulator ? recurse(n.accumulator.initial) : false) || recurse(n.body),
1861
- IndexAccess: (n, recurse) => recurse(n.object) || recurse(n.index),
1862
- RangeExpression: (n, recurse) => recurse(n.start) || recurse(n.end)
1863
- });
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
+ }
1864
1985
  }
1865
1986
  function propagateConstants(program2, externalVariables) {
1866
1987
  const statements = program2.statements;
@@ -1891,10 +2012,9 @@ function propagateConstants(program2, externalVariables) {
1891
2012
  return null;
1892
2013
  const referencedIds = new Set;
1893
2014
  for (const stmt of statements) {
1894
- if (isAssignment(stmt)) {
1895
- collectReferencedIdentifiers(stmt.value, referencedIds);
1896
- } else {
1897
- collectReferencedIdentifiers(stmt, referencedIds);
2015
+ const identifiers = collectAllIdentifiers(isAssignment(stmt) ? stmt.value : stmt);
2016
+ for (const id of identifiers) {
2017
+ referencedIds.add(id);
1898
2018
  }
1899
2019
  }
1900
2020
  let hasSubstitution = false;
@@ -1912,221 +2032,230 @@ function propagateConstants(program2, externalVariables) {
1912
2032
  }
1913
2033
  return result;
1914
2034
  }
1915
- function collectReferencedIdentifiers(node, ids) {
1916
- visit(node, {
1917
- Program: (n, recurse) => {
1918
- for (const s of n.statements)
1919
- recurse(s);
1920
- },
1921
- NumberLiteral: () => {},
1922
- StringLiteral: () => {},
1923
- BooleanLiteral: () => {},
1924
- ArrayLiteral: (n, recurse) => {
1925
- for (const e of n.elements)
1926
- recurse(e);
1927
- },
1928
- Identifier: (n) => {
1929
- ids.add(n.name);
1930
- },
1931
- BinaryOp: (n, recurse) => {
1932
- recurse(n.left);
1933
- recurse(n.right);
1934
- },
1935
- UnaryOp: (n, recurse) => {
1936
- recurse(n.argument);
1937
- },
1938
- FunctionCall: (n, recurse) => {
1939
- for (const a of n.args)
1940
- recurse(a);
1941
- },
1942
- Assignment: (n, recurse) => {
1943
- recurse(n.value);
1944
- },
1945
- IfExpression: (n, recurse) => {
1946
- recurse(n.condition);
1947
- recurse(n.consequent);
1948
- recurse(n.alternate);
1949
- },
1950
- ForExpression: (n, recurse) => {
1951
- recurse(n.iterable);
1952
- if (n.guard)
1953
- recurse(n.guard);
1954
- if (n.accumulator)
1955
- recurse(n.accumulator.initial);
1956
- recurse(n.body);
1957
- },
1958
- IndexAccess: (n, recurse) => {
1959
- recurse(n.object);
1960
- recurse(n.index);
1961
- },
1962
- RangeExpression: (n, recurse) => {
1963
- recurse(n.start);
1964
- recurse(n.end);
1965
- },
1966
- PipeExpression: (n, recurse) => {
1967
- recurse(n.value);
1968
- for (const a of n.args)
1969
- recurse(a);
1970
- },
1971
- Placeholder: () => {}
1972
- });
1973
- }
1974
2035
  function collectForLoopVars(node, vars) {
1975
- visit(node, {
1976
- Program: (n, recurse) => {
1977
- for (const s of n.statements)
1978
- recurse(s);
1979
- },
1980
- NumberLiteral: () => {},
1981
- StringLiteral: () => {},
1982
- BooleanLiteral: () => {},
1983
- ArrayLiteral: (n, recurse) => {
1984
- for (const e of n.elements)
1985
- recurse(e);
1986
- },
1987
- Identifier: () => {},
1988
- BinaryOp: (n, recurse) => {
1989
- recurse(n.left);
1990
- recurse(n.right);
1991
- },
1992
- UnaryOp: (n, recurse) => {
1993
- recurse(n.argument);
1994
- },
1995
- FunctionCall: (n, recurse) => {
1996
- for (const a of n.args)
1997
- recurse(a);
1998
- },
1999
- Assignment: (n, recurse) => {
2000
- recurse(n.value);
2001
- },
2002
- IfExpression: (n, recurse) => {
2003
- recurse(n.condition);
2004
- recurse(n.consequent);
2005
- recurse(n.alternate);
2006
- },
2007
- ForExpression: (n, recurse) => {
2008
- vars.add(n.variable);
2009
- if (n.accumulator)
2010
- vars.add(n.accumulator.name);
2011
- recurse(n.iterable);
2012
- if (n.guard)
2013
- recurse(n.guard);
2014
- if (n.accumulator)
2015
- recurse(n.accumulator.initial);
2016
- recurse(n.body);
2017
- },
2018
- IndexAccess: (n, recurse) => {
2019
- recurse(n.object);
2020
- recurse(n.index);
2021
- },
2022
- RangeExpression: (n, recurse) => {
2023
- recurse(n.start);
2024
- recurse(n.end);
2025
- },
2026
- PipeExpression: (n, recurse) => {
2027
- recurse(n.value);
2028
- for (const a of n.args)
2029
- recurse(a);
2030
- },
2031
- Placeholder: () => {}
2032
- });
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
+ }
2033
2095
  }
2034
2096
  function countAssignments(node, counts) {
2035
- visit(node, {
2036
- Program: (n, recurse) => {
2037
- for (const s of n.statements)
2038
- recurse(s);
2039
- },
2040
- NumberLiteral: () => {},
2041
- StringLiteral: () => {},
2042
- BooleanLiteral: () => {},
2043
- ArrayLiteral: (n, recurse) => {
2044
- for (const e of n.elements)
2045
- recurse(e);
2046
- },
2047
- Identifier: () => {},
2048
- BinaryOp: (n, recurse) => {
2049
- recurse(n.left);
2050
- recurse(n.right);
2051
- },
2052
- UnaryOp: (n, recurse) => {
2053
- recurse(n.argument);
2054
- },
2055
- FunctionCall: (n, recurse) => {
2056
- for (const a of n.args)
2057
- recurse(a);
2058
- },
2059
- Assignment: (n, recurse) => {
2060
- counts.set(n.name, (counts.get(n.name) ?? 0) + 1);
2061
- recurse(n.value);
2062
- },
2063
- IfExpression: (n, recurse) => {
2064
- recurse(n.condition);
2065
- recurse(n.consequent);
2066
- recurse(n.alternate);
2067
- },
2068
- ForExpression: (n, recurse) => {
2069
- recurse(n.iterable);
2070
- if (n.guard)
2071
- recurse(n.guard);
2072
- if (n.accumulator)
2073
- recurse(n.accumulator.initial);
2074
- recurse(n.body);
2075
- },
2076
- IndexAccess: (n, recurse) => {
2077
- recurse(n.object);
2078
- recurse(n.index);
2079
- },
2080
- RangeExpression: (n, recurse) => {
2081
- recurse(n.start);
2082
- recurse(n.end);
2083
- },
2084
- PipeExpression: (n, recurse) => {
2085
- recurse(n.value);
2086
- for (const a of n.args)
2087
- recurse(a);
2088
- },
2089
- Placeholder: () => {}
2090
- });
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;
2091
2161
  }
2092
2162
  function substituteIdentifiers(node, knownValues) {
2093
- return visit(node, {
2094
- Program: (n, recurse) => {
2095
- const result = program(n.statements.map(recurse));
2096
- if (n.trailingComments && n.trailingComments.length > 0) {
2097
- 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 };
2098
2172
  }
2099
2173
  return result;
2100
- },
2101
- NumberLiteral: (n) => n,
2102
- StringLiteral: (n) => n,
2103
- BooleanLiteral: (n) => n,
2104
- ArrayLiteral: (n, recurse) => preserveComments(n, array(n.elements.map(recurse))),
2105
- Identifier: (n) => {
2106
- const replacement = knownValues.get(n.name);
2107
- return replacement ? preserveComments(n, replacement) : n;
2108
- },
2109
- BinaryOp: (n, recurse) => preserveComments(n, binaryOp(recurse(n.left), n.operator, recurse(n.right))),
2110
- UnaryOp: (n, recurse) => preserveComments(n, unaryOp(n.operator, recurse(n.argument))),
2111
- FunctionCall: (n, recurse) => preserveComments(n, functionCall(n.name, n.args.map(recurse))),
2112
- Assignment: (n, recurse) => preserveComments(n, assign(n.name, recurse(n.value))),
2113
- IfExpression: (n, recurse) => preserveComments(n, ifExpr(recurse(n.condition), recurse(n.consequent), recurse(n.alternate))),
2114
- 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 */: {
2115
2224
  const innerKnown = new Map(knownValues);
2116
- innerKnown.delete(n.variable);
2117
- if (n.accumulator)
2118
- innerKnown.delete(n.accumulator.name);
2119
- const iterable = recurse(n.iterable);
2120
- const guard = n.guard ? substituteIdentifiers(n.guard, innerKnown) : null;
2121
- const accumulator = n.accumulator ? { name: n.accumulator.name, initial: recurse(n.accumulator.initial) } : null;
2122
- const body = substituteIdentifiers(n.body, innerKnown);
2123
- return preserveComments(n, forExpr(n.variable, iterable, guard, accumulator, body));
2124
- },
2125
- IndexAccess: (n, recurse) => preserveComments(n, indexAccess(recurse(n.object), recurse(n.index))),
2126
- RangeExpression: (n, recurse) => preserveComments(n, rangeExpr(recurse(n.start), recurse(n.end), n.inclusive)),
2127
- PipeExpression: (n, recurse) => preserveComments(n, pipeExpr(recurse(n.value), n.name, n.args.map(recurse))),
2128
- Placeholder: (n) => n
2129
- });
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
+ }
2130
2259
  }
2131
2260
  function optimize(node, externalVariables) {
2132
2261
  let propagated = fold(node);
@@ -2153,142 +2282,168 @@ function optimize(node, externalVariables) {
2153
2282
  return propagated;
2154
2283
  }
2155
2284
  function fold(node) {
2156
- return visit(node, {
2157
- NumberLiteral: (n) => n,
2158
- StringLiteral: (n) => n,
2159
- BooleanLiteral: (n) => n,
2160
- ArrayLiteral: (n, recurse) => {
2161
- const elements = n.elements.map(recurse);
2162
- return preserveComments(n, array(elements));
2163
- },
2164
- Identifier: (n) => n,
2165
- BinaryOp: (n, recurse) => {
2166
- const left = recurse(n.left);
2167
- 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);
2168
2312
  if (isNumberLiteral(left) && isNumberLiteral(right)) {
2169
- const result = evaluateBinaryOperation(n.operator, left.value, right.value);
2313
+ const result = evaluateBinaryOperation(node.operator, left.value, right.value);
2170
2314
  if (typeof result === "number")
2171
- return preserveComments(n, number(result));
2315
+ return preserveComments(node, number(result));
2172
2316
  if (typeof result === "boolean")
2173
- return preserveComments(n, boolean(result));
2317
+ return preserveComments(node, boolean(result));
2174
2318
  }
2175
2319
  if (isStringLiteral(left) && isStringLiteral(right)) {
2176
- if (n.operator === "+")
2177
- return preserveComments(n, string(left.value + right.value));
2178
- if (n.operator === "<" || n.operator === ">" || n.operator === "<=" || n.operator === ">=") {
2179
- const result = evaluateBinaryOperation(n.operator, left.value, right.value);
2180
- 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));
2181
2325
  }
2182
- if (n.operator === "==")
2183
- return preserveComments(n, boolean(left.value === right.value));
2184
- if (n.operator === "!=")
2185
- 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));
2186
2330
  }
2187
2331
  if (isBooleanLiteral(left) && isBooleanLiteral(right)) {
2188
- if (n.operator === "&&")
2189
- return preserveComments(n, boolean(left.value && right.value));
2190
- if (n.operator === "||")
2191
- return preserveComments(n, boolean(left.value || right.value));
2192
- if (n.operator === "==")
2193
- return preserveComments(n, boolean(left.value === right.value));
2194
- if (n.operator === "!=")
2195
- 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));
2196
2340
  }
2197
2341
  if (isLiteral(left) && isLiteral(right)) {
2198
2342
  if (left.kind !== right.kind) {
2199
- if (n.operator === "==")
2200
- return preserveComments(n, boolean(false));
2201
- if (n.operator === "!=")
2202
- 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));
2203
2347
  }
2204
2348
  }
2205
- return preserveComments(n, binaryOp(left, n.operator, right));
2206
- },
2207
- UnaryOp: (n, recurse) => {
2208
- const argument = recurse(n.argument);
2209
- if (n.operator === "-" && isNumberLiteral(argument)) {
2210
- 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));
2211
2357
  }
2212
- if (n.operator === "!" && isBooleanLiteral(argument)) {
2213
- return preserveComments(n, boolean(!argument.value));
2358
+ if (node.operator === "!" && isBooleanLiteral(argument)) {
2359
+ return preserveComments(node, boolean(!argument.value));
2214
2360
  }
2215
- return preserveComments(n, unaryOp(n.operator, argument));
2216
- },
2217
- FunctionCall: (n, recurse) => {
2218
- const optimizedArgs = n.args.map(recurse);
2219
- return preserveComments(n, functionCall(n.name, optimizedArgs));
2220
- },
2221
- Assignment: (n, recurse) => {
2222
- return preserveComments(n, assign(n.name, recurse(n.value)));
2223
- },
2224
- IfExpression: (n, recurse) => {
2225
- 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);
2226
2379
  if (isBooleanLiteral(condition)) {
2227
- const result = condition.value ? recurse(n.consequent) : recurse(n.alternate);
2228
- return preserveComments(n, result);
2380
+ const result = condition.value ? recurse(node.consequent) : recurse(node.alternate);
2381
+ return preserveComments(node, result);
2229
2382
  }
2230
- const consequent = recurse(n.consequent);
2231
- const alternate = recurse(n.alternate);
2232
- return preserveComments(n, ifExpr(condition, consequent, alternate));
2233
- },
2234
- ForExpression: (n, recurse) => {
2235
- const iterable = recurse(n.iterable);
2236
- const guard = n.guard ? recurse(n.guard) : null;
2237
- const accumulator = n.accumulator ? { name: n.accumulator.name, initial: recurse(n.accumulator.initial) } : null;
2238
- const body = recurse(n.body);
2239
- return preserveComments(n, forExpr(n.variable, iterable, guard, accumulator, body));
2240
- },
2241
- IndexAccess: (n, recurse) => {
2242
- const object = recurse(n.object);
2243
- 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);
2244
2402
  if (isArrayLiteral(object) && isNumberLiteral(index)) {
2245
2403
  const idx = index.value;
2246
2404
  if (Number.isInteger(idx)) {
2247
2405
  const len = object.elements.length;
2248
2406
  const resolved = idx < 0 ? len + idx : idx;
2249
2407
  if (resolved >= 0 && resolved < len) {
2250
- return preserveComments(n, object.elements[resolved]);
2408
+ return preserveComments(node, object.elements[resolved]);
2251
2409
  }
2252
2410
  }
2253
2411
  }
2254
2412
  if (isStringLiteral(object) && isNumberLiteral(index)) {
2255
2413
  try {
2256
2414
  const char = resolveIndex(object.value, index.value);
2257
- return preserveComments(n, string(char));
2415
+ return preserveComments(node, string(char));
2258
2416
  } catch {}
2259
2417
  }
2260
- return preserveComments(n, indexAccess(object, index));
2261
- },
2262
- RangeExpression: (n, recurse) => {
2263
- const start = recurse(n.start);
2264
- 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);
2265
2425
  if (isNumberLiteral(start) && isNumberLiteral(end)) {
2266
2426
  try {
2267
- const limit = n.inclusive ? end.value + 1 : end.value;
2427
+ const limit = node.inclusive ? end.value + 1 : end.value;
2268
2428
  const count = limit - start.value;
2269
2429
  if (count >= 0 && count <= 1e4) {
2270
- const values = buildRange(start.value, end.value, n.inclusive);
2271
- 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)));
2272
2432
  }
2273
2433
  } catch {}
2274
2434
  }
2275
- return preserveComments(n, rangeExpr(start, end, n.inclusive));
2276
- },
2277
- PipeExpression: (n, recurse) => {
2278
- const value = recurse(n.value);
2279
- const optimizedArgs = n.args.map(recurse);
2280
- return preserveComments(n, pipeExpr(value, n.name, optimizedArgs));
2281
- },
2282
- Placeholder: (n) => n,
2283
- Program: (n, recurse) => {
2284
- const optimizedStatements = n.statements.map(recurse);
2285
- const result = program(optimizedStatements);
2286
- if (n.trailingComments && n.trailingComments.length > 0) {
2287
- return { ...result, trailingComments: n.trailingComments };
2288
- }
2289
- return result;
2435
+ if (start === node.start && end === node.end)
2436
+ return node;
2437
+ return preserveComments(node, rangeExpr(start, end, node.inclusive));
2290
2438
  }
2291
- });
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
+ }
2292
2447
  }
2293
2448
  // src/stdlib/array.ts
2294
2449
  var exports_array = {};
@@ -2296,52 +2451,11 @@ __export(exports_array, {
2296
2451
  ARR_UNIQUE: () => ARR_UNIQUE,
2297
2452
  ARR_SUM: () => ARR_SUM,
2298
2453
  ARR_SORT: () => ARR_SORT,
2299
- ARR_SLICE: () => ARR_SLICE,
2300
- ARR_REVERSE: () => ARR_REVERSE,
2301
- ARR_PUSH: () => ARR_PUSH,
2302
2454
  ARR_MIN: () => ARR_MIN,
2303
2455
  ARR_MAX: () => ARR_MAX,
2304
- ARR_LEN: () => ARR_LEN,
2305
2456
  ARR_JOIN: () => ARR_JOIN,
2306
- ARR_FLAT: () => ARR_FLAT,
2307
- ARR_CONTAINS: () => ARR_CONTAINS
2457
+ ARR_FLAT: () => ARR_FLAT
2308
2458
  });
2309
- var ARR_LEN = (a) => {
2310
- assertArray(a, "ARR_LEN");
2311
- return a.length;
2312
- };
2313
- var ARR_PUSH = (a, value) => {
2314
- assertArray(a, "ARR_PUSH");
2315
- if (a.length > 0) {
2316
- const elemType = typeOf(a[0]);
2317
- const valType = typeOf(value);
2318
- if (elemType !== valType) {
2319
- throw new TypeError(`ARR_PUSH: cannot push ${valType} into array<${elemType}>`);
2320
- }
2321
- }
2322
- return [...a, value];
2323
- };
2324
- var ARR_SLICE = (a, start, end) => {
2325
- assertArray(a, "ARR_SLICE");
2326
- assertNumber(start, "ARR_SLICE", "start");
2327
- if (end !== undefined) {
2328
- assertNumber(end, "ARR_SLICE", "end");
2329
- return a.slice(start, end);
2330
- }
2331
- return a.slice(start);
2332
- };
2333
- var ARR_CONTAINS = (a, value) => {
2334
- assertArray(a, "ARR_CONTAINS");
2335
- for (const elem of a) {
2336
- if (deepEquals(elem, value))
2337
- return true;
2338
- }
2339
- return false;
2340
- };
2341
- var ARR_REVERSE = (a) => {
2342
- assertArray(a, "ARR_REVERSE");
2343
- return [...a].reverse();
2344
- };
2345
2459
  var ARR_SORT = (a) => {
2346
2460
  assertArray(a, "ARR_SORT");
2347
2461
  if (a.length <= 1)
@@ -2473,7 +2587,12 @@ var exports_core = {};
2473
2587
  __export(exports_core, {
2474
2588
  TYPE: () => TYPE,
2475
2589
  STR: () => STR,
2476
- NUM: () => NUM
2590
+ SLICE: () => SLICE,
2591
+ REVERSE: () => REVERSE,
2592
+ NUM: () => NUM,
2593
+ LEN: () => LEN,
2594
+ INDEX_OF: () => INDEX_OF,
2595
+ CONTAINS: () => CONTAINS
2477
2596
  });
2478
2597
  var STR = (v) => {
2479
2598
  if (typeof v === "string")
@@ -2504,31 +2623,81 @@ var NUM = (v) => {
2504
2623
  var TYPE = (v) => {
2505
2624
  return typeOf(v);
2506
2625
  };
2626
+ function assertStringOrArray(v, context) {
2627
+ if (typeof v !== "string" && !Array.isArray(v)) {
2628
+ throw new TypeError(`${context} expected string or array, got ${typeOf(v)}`);
2629
+ }
2630
+ }
2631
+ var LEN = (v) => {
2632
+ assertStringOrArray(v, "LEN");
2633
+ return v.length;
2634
+ };
2635
+ var SLICE = (v, start, end) => {
2636
+ assertStringOrArray(v, "SLICE");
2637
+ assertNumber(start, "SLICE", "start");
2638
+ if (end !== undefined) {
2639
+ assertNumber(end, "SLICE", "end");
2640
+ return v.slice(start, end);
2641
+ }
2642
+ return v.slice(start);
2643
+ };
2644
+ var CONTAINS = (v, search) => {
2645
+ assertStringOrArray(v, "CONTAINS");
2646
+ if (typeof v === "string") {
2647
+ assertString(search, "CONTAINS (search)");
2648
+ return v.includes(search);
2649
+ }
2650
+ for (const elem of v) {
2651
+ if (deepEquals(elem, search))
2652
+ return true;
2653
+ }
2654
+ return false;
2655
+ };
2656
+ var REVERSE = (v) => {
2657
+ assertStringOrArray(v, "REVERSE");
2658
+ if (typeof v === "string") {
2659
+ return Array.from(v).reverse().join("");
2660
+ }
2661
+ return [...v].reverse();
2662
+ };
2663
+ var INDEX_OF = (v, search) => {
2664
+ assertStringOrArray(v, "INDEX_OF");
2665
+ if (typeof v === "string") {
2666
+ assertString(search, "INDEX_OF (search)");
2667
+ return v.indexOf(search);
2668
+ }
2669
+ for (let i = 0;i < v.length; i++) {
2670
+ if (deepEquals(v[i], search))
2671
+ return i;
2672
+ }
2673
+ return -1;
2674
+ };
2507
2675
 
2508
2676
  // src/stdlib/datetime.ts
2509
2677
  var exports_datetime = {};
2510
2678
  __export(exports_datetime, {
2679
+ YEAR: () => YEAR,
2680
+ WEEKDAY: () => WEEKDAY,
2511
2681
  TODAY: () => TODAY,
2512
2682
  START_OF_YEAR: () => START_OF_YEAR,
2513
2683
  START_OF_WEEK: () => START_OF_WEEK,
2514
2684
  START_OF_QUARTER: () => START_OF_QUARTER,
2515
2685
  START_OF_MONTH: () => START_OF_MONTH,
2686
+ QUARTER: () => QUARTER,
2687
+ MONTH: () => MONTH,
2516
2688
  IS_WEEKEND: () => IS_WEEKEND,
2517
2689
  IS_SAME_DAY: () => IS_SAME_DAY,
2518
2690
  IS_LEAP_YEAR: () => IS_LEAP_YEAR,
2519
- GET_YEAR: () => GET_YEAR,
2520
- GET_WEEKDAY: () => GET_WEEKDAY,
2521
- GET_QUARTER: () => GET_QUARTER,
2522
- GET_MONTH: () => GET_MONTH,
2523
- GET_DAY_OF_YEAR: () => GET_DAY_OF_YEAR,
2524
- GET_DAY: () => GET_DAY,
2525
2691
  END_OF_YEAR: () => END_OF_YEAR,
2526
2692
  END_OF_MONTH: () => END_OF_MONTH,
2527
2693
  DIFFERENCE_IN_YEARS: () => DIFFERENCE_IN_YEARS,
2528
2694
  DIFFERENCE_IN_WEEKS: () => DIFFERENCE_IN_WEEKS,
2529
2695
  DIFFERENCE_IN_MONTHS: () => DIFFERENCE_IN_MONTHS,
2530
2696
  DIFFERENCE_IN_DAYS: () => DIFFERENCE_IN_DAYS,
2697
+ DAY_OF_YEAR: () => DAY_OF_YEAR,
2698
+ DAY: () => DAY,
2531
2699
  DATE: () => DATE,
2700
+ AGE: () => AGE,
2532
2701
  ADD_YEARS: () => ADD_YEARS,
2533
2702
  ADD_MONTHS: () => ADD_MONTHS,
2534
2703
  ADD_DAYS: () => ADD_DAYS
@@ -2540,28 +2709,28 @@ var DATE = (year, month, day) => {
2540
2709
  assertNumber(day, "DATE", "day");
2541
2710
  return new Temporal.PlainDate(year, month, day);
2542
2711
  };
2543
- var GET_YEAR = (date) => {
2544
- assertDateOrDateTime(date, "GET_YEAR");
2712
+ var YEAR = (date) => {
2713
+ assertDateOrDateTime(date, "YEAR");
2545
2714
  return date.year;
2546
2715
  };
2547
- var GET_MONTH = (date) => {
2548
- assertDateOrDateTime(date, "GET_MONTH");
2716
+ var MONTH = (date) => {
2717
+ assertDateOrDateTime(date, "MONTH");
2549
2718
  return date.month;
2550
2719
  };
2551
- var GET_DAY = (date) => {
2552
- assertDateOrDateTime(date, "GET_DAY");
2720
+ var DAY = (date) => {
2721
+ assertDateOrDateTime(date, "DAY");
2553
2722
  return date.day;
2554
2723
  };
2555
- var GET_WEEKDAY = (date) => {
2556
- assertDateOrDateTime(date, "GET_WEEKDAY");
2724
+ var WEEKDAY = (date) => {
2725
+ assertDateOrDateTime(date, "WEEKDAY");
2557
2726
  return date.dayOfWeek;
2558
2727
  };
2559
- var GET_DAY_OF_YEAR = (date) => {
2560
- assertDateOrDateTime(date, "GET_DAY_OF_YEAR");
2728
+ var DAY_OF_YEAR = (date) => {
2729
+ assertDateOrDateTime(date, "DAY_OF_YEAR");
2561
2730
  return date.dayOfYear;
2562
2731
  };
2563
- var GET_QUARTER = (date) => {
2564
- assertDateOrDateTime(date, "GET_QUARTER");
2732
+ var QUARTER = (date) => {
2733
+ assertDateOrDateTime(date, "QUARTER");
2565
2734
  return Math.ceil(date.month / 3);
2566
2735
  };
2567
2736
  var ADD_DAYS = (date, days) => {
@@ -2676,6 +2845,22 @@ var IS_LEAP_YEAR = (date) => {
2676
2845
  assertDateOrDateTime(date, "IS_LEAP_YEAR");
2677
2846
  return date.inLeapYear;
2678
2847
  };
2848
+ var AGE = (birthDate, ...rest) => {
2849
+ assertDateOrDateTime(birthDate, "AGE");
2850
+ const birth = birthDate instanceof Temporal.PlainDateTime ? birthDate.toPlainDate() : birthDate;
2851
+ let reference;
2852
+ if (rest.length > 0) {
2853
+ const ref = rest[0];
2854
+ assertDateOrDateTime(ref, "AGE");
2855
+ reference = ref instanceof Temporal.PlainDateTime ? ref.toPlainDate() : ref;
2856
+ } else {
2857
+ reference = Temporal.Now.plainDateISO();
2858
+ }
2859
+ if (Temporal.PlainDate.compare(birth, reference) > 0) {
2860
+ throw new RangeError("AGE requires birth date to be on or before reference date");
2861
+ }
2862
+ return birth.until(reference, { largestUnit: "year" }).years;
2863
+ };
2679
2864
 
2680
2865
  // src/stdlib/datetimefull.ts
2681
2866
  var exports_datetimefull = {};
@@ -2822,19 +3007,11 @@ __export(exports_string, {
2822
3007
  STR_TRIM: () => STR_TRIM,
2823
3008
  STR_STARTS_WITH: () => STR_STARTS_WITH,
2824
3009
  STR_SPLIT: () => STR_SPLIT,
2825
- STR_SLICE: () => STR_SLICE,
2826
3010
  STR_REPLACE: () => STR_REPLACE,
2827
3011
  STR_REPEAT: () => STR_REPEAT,
2828
3012
  STR_LOWER: () => STR_LOWER,
2829
- STR_LEN: () => STR_LEN,
2830
- STR_INDEX_OF: () => STR_INDEX_OF,
2831
- STR_ENDS_WITH: () => STR_ENDS_WITH,
2832
- STR_CONTAINS: () => STR_CONTAINS
3013
+ STR_ENDS_WITH: () => STR_ENDS_WITH
2833
3014
  });
2834
- var STR_LEN = (s) => {
2835
- assertString(s, "STR_LEN");
2836
- return s.length;
2837
- };
2838
3015
  var STR_UPPER = (s) => {
2839
3016
  assertString(s, "STR_UPPER");
2840
3017
  return s.toUpperCase();
@@ -2847,25 +3024,6 @@ var STR_TRIM = (s) => {
2847
3024
  assertString(s, "STR_TRIM");
2848
3025
  return s.trim();
2849
3026
  };
2850
- var STR_SLICE = (s, start, end) => {
2851
- assertString(s, "STR_SLICE");
2852
- assertNumber(start, "STR_SLICE", "start");
2853
- if (end !== undefined) {
2854
- assertNumber(end, "STR_SLICE", "end");
2855
- return s.slice(start, end);
2856
- }
2857
- return s.slice(start);
2858
- };
2859
- var STR_CONTAINS = (s, search) => {
2860
- assertString(s, "STR_CONTAINS");
2861
- assertString(search, "STR_CONTAINS");
2862
- return s.includes(search);
2863
- };
2864
- var STR_INDEX_OF = (s, search) => {
2865
- assertString(s, "STR_INDEX_OF");
2866
- assertString(search, "STR_INDEX_OF");
2867
- return s.indexOf(search);
2868
- };
2869
3027
  var STR_SPLIT = (s, sep) => {
2870
3028
  assertString(s, "STR_SPLIT");
2871
3029
  assertString(sep, "STR_SPLIT (separator)");
@@ -2900,12 +3058,12 @@ var STR_REPEAT = (s, count) => {
2900
3058
  var exports_time = {};
2901
3059
  __export(exports_time, {
2902
3060
  TIME: () => TIME,
3061
+ SECOND: () => SECOND,
2903
3062
  NOW_TIME: () => NOW_TIME,
3063
+ MINUTE: () => MINUTE,
3064
+ MILLISECOND: () => MILLISECOND,
2904
3065
  IS_SAME_TIME: () => IS_SAME_TIME,
2905
- GET_SECOND: () => GET_SECOND,
2906
- GET_MINUTE: () => GET_MINUTE,
2907
- GET_MILLISECOND: () => GET_MILLISECOND,
2908
- GET_HOUR: () => GET_HOUR,
3066
+ HOUR: () => HOUR,
2909
3067
  DIFFERENCE_IN_SECONDS: () => DIFFERENCE_IN_SECONDS,
2910
3068
  DIFFERENCE_IN_MINUTES: () => DIFFERENCE_IN_MINUTES,
2911
3069
  DIFFERENCE_IN_HOURS: () => DIFFERENCE_IN_HOURS,
@@ -2920,20 +3078,20 @@ var TIME = (hour, minute, second) => {
2920
3078
  return new Temporal.PlainTime(hour, minute, second);
2921
3079
  };
2922
3080
  var NOW_TIME = () => Temporal.Now.plainTimeISO();
2923
- var GET_HOUR = (t) => {
2924
- assertTimeOrDateTime(t, "GET_HOUR");
3081
+ var HOUR = (t) => {
3082
+ assertTimeOrDateTime(t, "HOUR");
2925
3083
  return t.hour;
2926
3084
  };
2927
- var GET_MINUTE = (t) => {
2928
- assertTimeOrDateTime(t, "GET_MINUTE");
3085
+ var MINUTE = (t) => {
3086
+ assertTimeOrDateTime(t, "MINUTE");
2929
3087
  return t.minute;
2930
3088
  };
2931
- var GET_SECOND = (t) => {
2932
- assertTimeOrDateTime(t, "GET_SECOND");
3089
+ var SECOND = (t) => {
3090
+ assertTimeOrDateTime(t, "SECOND");
2933
3091
  return t.second;
2934
3092
  };
2935
- var GET_MILLISECOND = (t) => {
2936
- assertTimeOrDateTime(t, "GET_MILLISECOND");
3093
+ var MILLISECOND = (t) => {
3094
+ assertTimeOrDateTime(t, "MILLISECOND");
2937
3095
  return t.millisecond;
2938
3096
  };
2939
3097
  var ADD_HOURS = (t, hours) => {
@@ -3016,9 +3174,9 @@ var defaultContext = {
3016
3174
  }
3017
3175
  };
3018
3176
  export {
3019
- visitPartial,
3020
3177
  visit,
3021
3178
  typeOf,
3179
+ toLineColumn,
3022
3180
  exports_time as time,
3023
3181
  exports_string as string,
3024
3182
  parse,
@@ -3043,6 +3201,7 @@ export {
3043
3201
  generate,
3044
3202
  extractInputVariables,
3045
3203
  extractAssignedVariables,
3204
+ evaluateWithScope,
3046
3205
  evaluateScope,
3047
3206
  evaluate,
3048
3207
  defaultContext,
@@ -3060,5 +3219,6 @@ export {
3060
3219
  assertBoolean,
3061
3220
  assertArray,
3062
3221
  exports_array as array,
3222
+ ParseError,
3063
3223
  NodeKind
3064
3224
  };