littlewing 0.5.3 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -15,8 +15,8 @@ var exports_ast = {};
15
15
  __export(exports_ast, {
16
16
  unaryOp: () => unaryOp,
17
17
  subtract: () => subtract,
18
+ program: () => program,
18
19
  number: () => number,
19
- nullishAssign: () => nullishAssign,
20
20
  notEquals: () => notEquals,
21
21
  negate: () => negate,
22
22
  multiply: () => multiply,
@@ -37,6 +37,12 @@ __export(exports_ast, {
37
37
  assign: () => assign,
38
38
  add: () => add
39
39
  });
40
+ function program(statements) {
41
+ return {
42
+ type: "Program",
43
+ statements
44
+ };
45
+ }
40
46
  function number(value) {
41
47
  return {
42
48
  type: "NumberLiteral",
@@ -78,13 +84,6 @@ function assign(name, value) {
78
84
  value
79
85
  };
80
86
  }
81
- function nullishAssign(name, value) {
82
- return {
83
- type: "NullishAssignment",
84
- name,
85
- value
86
- };
87
- }
88
87
  function conditional(condition, consequent, alternate) {
89
88
  return {
90
89
  type: "ConditionalExpression",
@@ -163,7 +162,6 @@ var TokenType;
163
162
  TokenType2["COMMA"] = "COMMA";
164
163
  TokenType2["QUESTION"] = "QUESTION";
165
164
  TokenType2["COLON"] = "COLON";
166
- TokenType2["NULLISH_ASSIGN"] = "NULLISH_ASSIGN";
167
165
  TokenType2["EOF"] = "EOF";
168
166
  })(TokenType ||= {});
169
167
  function isNumberLiteral(node) {
@@ -190,9 +188,6 @@ function isProgram(node) {
190
188
  function isConditionalExpression(node) {
191
189
  return node.type === "ConditionalExpression";
192
190
  }
193
- function isNullishAssignment(node) {
194
- return node.type === "NullishAssignment";
195
- }
196
191
 
197
192
  // src/utils.ts
198
193
  function evaluateBinaryOperation(operator, left, right) {
@@ -264,7 +259,6 @@ function getOperatorPrecedence(operator) {
264
259
  function getTokenPrecedence(type) {
265
260
  switch (type) {
266
261
  case "EQUALS" /* EQUALS */:
267
- case "NULLISH_ASSIGN" /* NULLISH_ASSIGN */:
268
262
  return 1;
269
263
  case "QUESTION" /* QUESTION */:
270
264
  return 2;
@@ -310,8 +304,6 @@ class CodeGenerator {
310
304
  return this.generateFunctionCall(node);
311
305
  if (isAssignment(node))
312
306
  return this.generateAssignment(node);
313
- if (isNullishAssignment(node))
314
- return this.generateNullishAssignment(node);
315
307
  if (isConditionalExpression(node))
316
308
  return this.generateConditionalExpression(node);
317
309
  throw new Error(`Unknown node type`);
@@ -348,10 +340,6 @@ class CodeGenerator {
348
340
  const value = this.generate(node.value);
349
341
  return `${node.name} = ${value}`;
350
342
  }
351
- generateNullishAssignment(node) {
352
- const value = this.generate(node.value);
353
- return `${node.name} ??= ${value}`;
354
- }
355
343
  generateConditionalExpression(node) {
356
344
  const condition = this.generate(node.condition);
357
345
  const consequent = this.generate(node.consequent);
@@ -385,45 +373,197 @@ function generate(node) {
385
373
  const generator = new CodeGenerator;
386
374
  return generator.generate(node);
387
375
  }
376
+ // src/date-utils.ts
377
+ var exports_date_utils = {};
378
+ __export(exports_date_utils, {
379
+ TO_UNIX_SECONDS: () => TO_UNIX_SECONDS,
380
+ START_OF_YEAR: () => START_OF_YEAR,
381
+ START_OF_WEEK: () => START_OF_WEEK,
382
+ START_OF_QUARTER: () => START_OF_QUARTER,
383
+ START_OF_MONTH: () => START_OF_MONTH,
384
+ START_OF_DAY: () => START_OF_DAY,
385
+ NOW: () => NOW,
386
+ IS_WEEKEND: () => IS_WEEKEND,
387
+ IS_SAME_DAY: () => IS_SAME_DAY,
388
+ IS_LEAP_YEAR: () => IS_LEAP_YEAR,
389
+ IS_BEFORE: () => IS_BEFORE,
390
+ IS_AFTER: () => IS_AFTER,
391
+ GET_YEAR: () => GET_YEAR,
392
+ GET_WEEKDAY: () => GET_WEEKDAY,
393
+ GET_SECOND: () => GET_SECOND,
394
+ GET_QUARTER: () => GET_QUARTER,
395
+ GET_MONTH: () => GET_MONTH,
396
+ GET_MINUTE: () => GET_MINUTE,
397
+ GET_MILLISECOND: () => GET_MILLISECOND,
398
+ GET_HOUR: () => GET_HOUR,
399
+ GET_DAY_OF_YEAR: () => GET_DAY_OF_YEAR,
400
+ GET_DAY: () => GET_DAY,
401
+ FROM_YEARS: () => FROM_YEARS,
402
+ FROM_WEEKS: () => FROM_WEEKS,
403
+ FROM_UNIX_SECONDS: () => FROM_UNIX_SECONDS,
404
+ FROM_SECONDS: () => FROM_SECONDS,
405
+ FROM_MONTHS: () => FROM_MONTHS,
406
+ FROM_MINUTES: () => FROM_MINUTES,
407
+ FROM_HOURS: () => FROM_HOURS,
408
+ FROM_DAYS: () => FROM_DAYS,
409
+ END_OF_YEAR: () => END_OF_YEAR,
410
+ END_OF_MONTH: () => END_OF_MONTH,
411
+ END_OF_DAY: () => END_OF_DAY,
412
+ DIFFERENCE_IN_WEEKS: () => DIFFERENCE_IN_WEEKS,
413
+ DIFFERENCE_IN_SECONDS: () => DIFFERENCE_IN_SECONDS,
414
+ DIFFERENCE_IN_MINUTES: () => DIFFERENCE_IN_MINUTES,
415
+ DIFFERENCE_IN_HOURS: () => DIFFERENCE_IN_HOURS,
416
+ DIFFERENCE_IN_DAYS: () => DIFFERENCE_IN_DAYS,
417
+ DATE: () => DATE,
418
+ ADD_YEARS: () => ADD_YEARS,
419
+ ADD_MONTHS: () => ADD_MONTHS,
420
+ ADD_DAYS: () => ADD_DAYS
421
+ });
422
+ var NOW = () => Date.now();
423
+ var DATE = (year, month = 1, day = 1, hour = 0, minute = 0, second = 0) => new Date(year, month - 1, day, hour, minute, second).getTime();
424
+ var FROM_SECONDS = (s) => s * 1000;
425
+ var FROM_MINUTES = (m) => m * 60 * 1000;
426
+ var FROM_HOURS = (h) => h * 60 * 60 * 1000;
427
+ var FROM_DAYS = (d) => d * 24 * 60 * 60 * 1000;
428
+ var FROM_WEEKS = (w) => w * 7 * 24 * 60 * 60 * 1000;
429
+ var FROM_MONTHS = (months) => months * 30 * 24 * 60 * 60 * 1000;
430
+ var FROM_YEARS = (years) => years * 365 * 24 * 60 * 60 * 1000;
431
+ var GET_YEAR = (timestamp) => new Date(timestamp).getFullYear();
432
+ var GET_MONTH = (timestamp) => new Date(timestamp).getMonth() + 1;
433
+ var GET_DAY = (timestamp) => new Date(timestamp).getDate();
434
+ var GET_HOUR = (timestamp) => new Date(timestamp).getHours();
435
+ var GET_MINUTE = (timestamp) => new Date(timestamp).getMinutes();
436
+ var GET_SECOND = (timestamp) => new Date(timestamp).getSeconds();
437
+ var GET_MILLISECOND = (timestamp) => new Date(timestamp).getMilliseconds();
438
+ var GET_WEEKDAY = (timestamp) => new Date(timestamp).getDay();
439
+ var GET_DAY_OF_YEAR = (timestamp) => {
440
+ const date = new Date(timestamp);
441
+ const start = new Date(date.getFullYear(), 0, 0);
442
+ const diff = date.getTime() - start.getTime();
443
+ const oneDay = 1000 * 60 * 60 * 24;
444
+ return Math.floor(diff / oneDay);
445
+ };
446
+ var GET_QUARTER = (timestamp) => {
447
+ const month = new Date(timestamp).getMonth();
448
+ return Math.floor(month / 3) + 1;
449
+ };
450
+ var DIFFERENCE_IN_SECONDS = (ts1, ts2) => Math.abs(ts1 - ts2) / 1000;
451
+ var DIFFERENCE_IN_MINUTES = (ts1, ts2) => Math.abs(ts1 - ts2) / (60 * 1000);
452
+ var DIFFERENCE_IN_HOURS = (ts1, ts2) => Math.abs(ts1 - ts2) / (60 * 60 * 1000);
453
+ var DIFFERENCE_IN_DAYS = (ts1, ts2) => Math.abs(ts1 - ts2) / (24 * 60 * 60 * 1000);
454
+ var DIFFERENCE_IN_WEEKS = (ts1, ts2) => Math.abs(ts1 - ts2) / (7 * 24 * 60 * 60 * 1000);
455
+ var START_OF_DAY = (timestamp) => {
456
+ const date = new Date(timestamp);
457
+ date.setHours(0, 0, 0, 0);
458
+ return date.getTime();
459
+ };
460
+ var END_OF_DAY = (timestamp) => {
461
+ const date = new Date(timestamp);
462
+ date.setHours(23, 59, 59, 999);
463
+ return date.getTime();
464
+ };
465
+ var START_OF_WEEK = (timestamp) => {
466
+ const date = new Date(timestamp);
467
+ const day = date.getDay();
468
+ const diff = date.getDate() - day;
469
+ date.setDate(diff);
470
+ date.setHours(0, 0, 0, 0);
471
+ return date.getTime();
472
+ };
473
+ var START_OF_MONTH = (timestamp) => {
474
+ const date = new Date(timestamp);
475
+ date.setDate(1);
476
+ date.setHours(0, 0, 0, 0);
477
+ return date.getTime();
478
+ };
479
+ var END_OF_MONTH = (timestamp) => {
480
+ const date = new Date(timestamp);
481
+ date.setMonth(date.getMonth() + 1, 0);
482
+ date.setHours(23, 59, 59, 999);
483
+ return date.getTime();
484
+ };
485
+ var START_OF_YEAR = (timestamp) => {
486
+ const date = new Date(timestamp);
487
+ date.setMonth(0, 1);
488
+ date.setHours(0, 0, 0, 0);
489
+ return date.getTime();
490
+ };
491
+ var END_OF_YEAR = (timestamp) => {
492
+ const date = new Date(timestamp);
493
+ date.setMonth(11, 31);
494
+ date.setHours(23, 59, 59, 999);
495
+ return date.getTime();
496
+ };
497
+ var ADD_DAYS = (timestamp, days) => {
498
+ const date = new Date(timestamp);
499
+ date.setDate(date.getDate() + days);
500
+ return date.getTime();
501
+ };
502
+ var ADD_MONTHS = (timestamp, months) => {
503
+ const date = new Date(timestamp);
504
+ date.setMonth(date.getMonth() + months);
505
+ return date.getTime();
506
+ };
507
+ var ADD_YEARS = (timestamp, years) => {
508
+ const date = new Date(timestamp);
509
+ date.setFullYear(date.getFullYear() + years);
510
+ return date.getTime();
511
+ };
512
+ var IS_BEFORE = (ts1, ts2) => {
513
+ return ts1 < ts2 ? 1 : 0;
514
+ };
515
+ var IS_AFTER = (ts1, ts2) => {
516
+ return ts1 > ts2 ? 1 : 0;
517
+ };
518
+ var IS_SAME_DAY = (ts1, ts2) => {
519
+ const date1 = new Date(ts1);
520
+ const date2 = new Date(ts2);
521
+ return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate() ? 1 : 0;
522
+ };
523
+ var IS_WEEKEND = (timestamp) => {
524
+ const day = new Date(timestamp).getDay();
525
+ return day === 0 || day === 6 ? 1 : 0;
526
+ };
527
+ var IS_LEAP_YEAR = (timestamp) => {
528
+ const year = new Date(timestamp).getFullYear();
529
+ return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0 ? 1 : 0;
530
+ };
531
+ var START_OF_QUARTER = (timestamp) => {
532
+ const date = new Date(timestamp);
533
+ const quarter = Math.floor(date.getMonth() / 3);
534
+ date.setMonth(quarter * 3, 1);
535
+ date.setHours(0, 0, 0, 0);
536
+ return date.getTime();
537
+ };
538
+ var TO_UNIX_SECONDS = (timestamp) => Math.floor(timestamp / 1000);
539
+ var FROM_UNIX_SECONDS = (seconds) => seconds * 1000;
388
540
  // src/defaults.ts
389
541
  var defaultContext = {
390
542
  functions: {
391
- abs: Math.abs,
392
- ceil: Math.ceil,
393
- floor: Math.floor,
394
- round: Math.round,
395
- sqrt: Math.sqrt,
396
- min: Math.min,
397
- max: Math.max,
398
- sin: Math.sin,
399
- cos: Math.cos,
400
- tan: Math.tan,
401
- log: Math.log,
402
- log10: Math.log10,
403
- exp: Math.exp,
404
- now: () => Date.now(),
405
- timestamp: (year, month, day, hour = 0, minute = 0, second = 0) => new Date(year, month - 1, day, hour, minute, second).getTime(),
406
- milliseconds: (ms) => ms,
407
- seconds: (s) => s * 1000,
408
- minutes: (m) => m * 60 * 1000,
409
- hours: (h) => h * 60 * 60 * 1000,
410
- days: (d) => d * 24 * 60 * 60 * 1000,
411
- weeks: (w) => w * 7 * 24 * 60 * 60 * 1000,
412
- year: (timestamp) => new Date(timestamp).getFullYear(),
413
- month: (timestamp) => new Date(timestamp).getMonth() + 1,
414
- day: (timestamp) => new Date(timestamp).getDate(),
415
- hour: (timestamp) => new Date(timestamp).getHours(),
416
- minute: (timestamp) => new Date(timestamp).getMinutes(),
417
- second: (timestamp) => new Date(timestamp).getSeconds(),
418
- weekday: (timestamp) => new Date(timestamp).getDay()
543
+ ABS: Math.abs,
544
+ CEIL: Math.ceil,
545
+ FLOOR: Math.floor,
546
+ ROUND: Math.round,
547
+ SQRT: Math.sqrt,
548
+ MIN: Math.min,
549
+ MAX: Math.max,
550
+ SIN: Math.sin,
551
+ COS: Math.cos,
552
+ TAN: Math.tan,
553
+ LOG: Math.log,
554
+ LOG10: Math.log10,
555
+ EXP: Math.exp,
556
+ ...exports_date_utils
419
557
  }
420
558
  };
421
559
  // src/lexer.ts
422
560
  class Lexer {
423
561
  source;
424
562
  position = 0;
563
+ length;
425
564
  constructor(source) {
426
565
  this.source = source;
566
+ this.length = source.length;
427
567
  }
428
568
  tokenize() {
429
569
  const tokens = [];
@@ -438,16 +578,22 @@ class Lexer {
438
578
  }
439
579
  nextToken() {
440
580
  this.skipWhitespaceAndComments();
441
- if (this.position >= this.source.length) {
581
+ if (this.position >= this.length) {
582
+ return { type: "EOF" /* EOF */, value: "", position: this.position };
583
+ }
584
+ const char = this.source[this.position];
585
+ if (char === undefined) {
442
586
  return { type: "EOF" /* EOF */, value: "", position: this.position };
443
587
  }
444
- const char = this.getCharAt(this.position);
445
588
  const start = this.position;
446
589
  if (this.isDigit(char)) {
447
590
  return this.readNumber();
448
591
  }
449
- if (char === "." && this.isDigit(this.peek())) {
450
- return this.readNumber();
592
+ if (char === ".") {
593
+ const nextChar = this.source[this.position + 1];
594
+ if (nextChar !== undefined && this.isDigit(nextChar)) {
595
+ return this.readNumber();
596
+ }
451
597
  }
452
598
  if (this.isLetter(char) || char === "_") {
453
599
  return this.readIdentifier();
@@ -478,41 +624,33 @@ class Lexer {
478
624
  this.position++;
479
625
  return { type: "RPAREN" /* RPAREN */, value: ")", position: start };
480
626
  case "=":
481
- if (this.peek() === "=") {
627
+ if (this.source[this.position + 1] === "=") {
482
628
  this.position += 2;
483
629
  return { type: "DOUBLE_EQUALS" /* DOUBLE_EQUALS */, value: "==", position: start };
484
630
  }
485
631
  this.position++;
486
632
  return { type: "EQUALS" /* EQUALS */, value: "=", position: start };
487
633
  case "!":
488
- if (this.peek() === "=") {
634
+ if (this.source[this.position + 1] === "=") {
489
635
  this.position += 2;
490
636
  return { type: "NOT_EQUALS" /* NOT_EQUALS */, value: "!=", position: start };
491
637
  }
492
638
  throw new Error(`Unexpected character '${char}' at position ${start}`);
493
639
  case "<":
494
- if (this.peek() === "=") {
640
+ if (this.source[this.position + 1] === "=") {
495
641
  this.position += 2;
496
642
  return { type: "LESS_EQUAL" /* LESS_EQUAL */, value: "<=", position: start };
497
643
  }
498
644
  this.position++;
499
645
  return { type: "LESS_THAN" /* LESS_THAN */, value: "<", position: start };
500
646
  case ">":
501
- if (this.peek() === "=") {
647
+ if (this.source[this.position + 1] === "=") {
502
648
  this.position += 2;
503
649
  return { type: "GREATER_EQUAL" /* GREATER_EQUAL */, value: ">=", position: start };
504
650
  }
505
651
  this.position++;
506
652
  return { type: "GREATER_THAN" /* GREATER_THAN */, value: ">", position: start };
507
653
  case "?":
508
- if (this.peek() === "?" && this.peekAhead(2) === "=") {
509
- this.position += 3;
510
- return {
511
- type: "NULLISH_ASSIGN" /* NULLISH_ASSIGN */,
512
- value: "??=",
513
- position: start
514
- };
515
- }
516
654
  this.position++;
517
655
  return { type: "QUESTION" /* QUESTION */, value: "?", position: start };
518
656
  case ":":
@@ -525,13 +663,13 @@ class Lexer {
525
663
  this.position++;
526
664
  return this.nextToken();
527
665
  case "&":
528
- if (this.peek() === "&") {
666
+ if (this.source[this.position + 1] === "&") {
529
667
  this.position += 2;
530
668
  return { type: "LOGICAL_AND" /* LOGICAL_AND */, value: "&&", position: start };
531
669
  }
532
670
  throw new Error(`Unexpected character '${char}' at position ${start}`);
533
671
  case "|":
534
- if (this.peek() === "|") {
672
+ if (this.source[this.position + 1] === "|") {
535
673
  this.position += 2;
536
674
  return { type: "LOGICAL_OR" /* LOGICAL_OR */, value: "||", position: start };
537
675
  }
@@ -541,14 +679,15 @@ class Lexer {
541
679
  }
542
680
  }
543
681
  skipWhitespaceAndComments() {
544
- while (this.position < this.source.length) {
545
- const char = this.getCharAt(this.position);
682
+ while (this.position < this.length) {
683
+ const char = this.source[this.position];
546
684
  if (this.isWhitespace(char)) {
547
685
  this.position++;
548
686
  continue;
549
687
  }
550
- if (char === "/" && this.peek() === "/") {
551
- while (this.position < this.source.length && this.getCharAt(this.position) !== `
688
+ if (char === "/" && this.source[this.position + 1] === "/") {
689
+ this.position += 2;
690
+ while (this.position < this.length && this.source[this.position] !== `
552
691
  `) {
553
692
  this.position++;
554
693
  }
@@ -561,12 +700,12 @@ class Lexer {
561
700
  const start = this.position;
562
701
  let hasDecimal = false;
563
702
  let hasExponent = false;
564
- if (this.getCharAt(this.position) === ".") {
703
+ if (this.source[this.position] === ".") {
565
704
  hasDecimal = true;
566
705
  this.position++;
567
706
  }
568
- while (this.position < this.source.length) {
569
- const char = this.getCharAt(this.position);
707
+ while (this.position < this.length) {
708
+ const char = this.source[this.position];
570
709
  if (this.isDigit(char)) {
571
710
  this.position++;
572
711
  } else if (char === "." && !hasDecimal && !hasExponent) {
@@ -575,14 +714,15 @@ class Lexer {
575
714
  } else if ((char === "e" || char === "E") && !hasExponent) {
576
715
  hasExponent = true;
577
716
  this.position++;
578
- const nextChar = this.getCharAt(this.position);
717
+ const nextChar = this.source[this.position];
579
718
  if (nextChar === "+" || nextChar === "-") {
580
719
  this.position++;
581
720
  }
582
- if (!this.isDigit(this.getCharAt(this.position))) {
721
+ const digitChar = this.source[this.position];
722
+ if (digitChar === undefined || !this.isDigit(digitChar)) {
583
723
  throw new Error(`Invalid number: expected digit after exponent at position ${this.position}`);
584
724
  }
585
- while (this.position < this.source.length && this.isDigit(this.getCharAt(this.position))) {
725
+ while (this.position < this.length && this.isDigit(this.source[this.position])) {
586
726
  this.position++;
587
727
  }
588
728
  break;
@@ -595,8 +735,8 @@ class Lexer {
595
735
  }
596
736
  readIdentifier() {
597
737
  const start = this.position;
598
- while (this.position < this.source.length) {
599
- const char = this.getCharAt(this.position);
738
+ while (this.position < this.length) {
739
+ const char = this.source[this.position];
600
740
  if (this.isLetter(char) || this.isDigit(char) || char === "_") {
601
741
  this.position++;
602
742
  } else {
@@ -606,24 +746,15 @@ class Lexer {
606
746
  const name = this.source.slice(start, this.position);
607
747
  return { type: "IDENTIFIER" /* IDENTIFIER */, value: name, position: start };
608
748
  }
609
- getCharAt(pos) {
610
- return pos < this.source.length ? this.source[pos] || "" : "";
611
- }
612
- peek() {
613
- return this.getCharAt(this.position + 1);
614
- }
615
- peekAhead(n) {
616
- return this.getCharAt(this.position + n);
617
- }
618
749
  isDigit(char) {
619
- return char >= "0" && char <= "9";
750
+ return char !== undefined && char >= "0" && char <= "9";
620
751
  }
621
752
  isLetter(char) {
622
- return char >= "a" && char <= "z" || char >= "A" && char <= "Z";
753
+ return char !== undefined && (char >= "a" && char <= "z" || char >= "A" && char <= "Z");
623
754
  }
624
755
  isWhitespace(char) {
625
- return char === " " || char === "\t" || char === `
626
- ` || char === "\r";
756
+ return char !== undefined && (char === " " || char === "\t" || char === `
757
+ ` || char === "\r");
627
758
  }
628
759
  }
629
760
 
@@ -660,16 +791,13 @@ class Parser {
660
791
  throw new Error("Empty program");
661
792
  }
662
793
  if (statements.length === 1) {
663
- const stmt = statements[0];
664
- if (stmt === undefined) {
665
- throw new Error("Unexpected undefined statement");
794
+ const singleStatement = statements[0];
795
+ if (singleStatement === undefined) {
796
+ throw new Error("Unexpected empty statements array");
666
797
  }
667
- return stmt;
798
+ return singleStatement;
668
799
  }
669
- return {
670
- type: "Program",
671
- statements
672
- };
800
+ return program(statements);
673
801
  }
674
802
  parseExpression(minPrecedence) {
675
803
  let left = this.parsePrefix();
@@ -686,23 +814,7 @@ class Parser {
686
814
  const identName = left.name;
687
815
  this.advance();
688
816
  const value = this.parseExpression(precedence);
689
- left = {
690
- type: "Assignment",
691
- name: identName,
692
- value
693
- };
694
- } else if (token.type === "NULLISH_ASSIGN" /* NULLISH_ASSIGN */) {
695
- if (left.type !== "Identifier") {
696
- throw new Error("Invalid assignment target");
697
- }
698
- const identName = left.name;
699
- this.advance();
700
- const value = this.parseExpression(precedence);
701
- left = {
702
- type: "NullishAssignment",
703
- name: identName,
704
- value
705
- };
817
+ left = assign(identName, value);
706
818
  } else if (token.type === "QUESTION" /* QUESTION */) {
707
819
  this.advance();
708
820
  const consequent = this.parseExpression(0);
@@ -711,22 +823,12 @@ class Parser {
711
823
  }
712
824
  this.advance();
713
825
  const alternate = this.parseExpression(precedence);
714
- left = {
715
- type: "ConditionalExpression",
716
- condition: left,
717
- consequent,
718
- alternate
719
- };
826
+ left = conditional(left, consequent, alternate);
720
827
  } else if (this.isBinaryOperator(token.type)) {
721
828
  const operator = token.value;
722
829
  this.advance();
723
830
  const right = this.parseExpression(precedence + 1);
724
- left = {
725
- type: "BinaryOp",
726
- left,
727
- operator,
728
- right
729
- };
831
+ left = binaryOp(left, operator, right);
730
832
  } else {
731
833
  break;
732
834
  }
@@ -738,11 +840,7 @@ class Parser {
738
840
  if (token.type === "MINUS" /* MINUS */) {
739
841
  this.advance();
740
842
  const argument = this.parseExpression(this.getUnaryPrecedence());
741
- return {
742
- type: "UnaryOp",
743
- operator: "-",
744
- argument
745
- };
843
+ return unaryOp(argument);
746
844
  }
747
845
  if (token.type === "LPAREN" /* LPAREN */) {
748
846
  this.advance();
@@ -755,10 +853,7 @@ class Parser {
755
853
  }
756
854
  if (token.type === "NUMBER" /* NUMBER */) {
757
855
  this.advance();
758
- return {
759
- type: "NumberLiteral",
760
- value: token.value
761
- };
856
+ return number(token.value);
762
857
  }
763
858
  if (token.type === "IDENTIFIER" /* IDENTIFIER */) {
764
859
  const name = token.value;
@@ -770,16 +865,9 @@ class Parser {
770
865
  throw new Error("Expected closing parenthesis");
771
866
  }
772
867
  this.advance();
773
- return {
774
- type: "FunctionCall",
775
- name,
776
- arguments: args
777
- };
868
+ return functionCall(name, args);
778
869
  }
779
- return {
780
- type: "Identifier",
781
- name
782
- };
870
+ return identifier(name);
783
871
  }
784
872
  throw new Error(`Unexpected token: ${token.value}`);
785
873
  }
@@ -826,9 +914,11 @@ function parseSource(source) {
826
914
  class Executor {
827
915
  context;
828
916
  variables;
917
+ externalVariables;
829
918
  constructor(context = {}) {
830
919
  this.context = context;
831
920
  this.variables = new Map(Object.entries(context.variables || {}));
921
+ this.externalVariables = new Set(Object.keys(context.variables || {}));
832
922
  }
833
923
  execute(node) {
834
924
  if (isProgram(node))
@@ -845,8 +935,6 @@ class Executor {
845
935
  return this.executeFunctionCall(node);
846
936
  if (isAssignment(node))
847
937
  return this.executeAssignment(node);
848
- if (isNullishAssignment(node))
849
- return this.executeNullishAssignment(node);
850
938
  if (isConditionalExpression(node))
851
939
  return this.executeConditionalExpression(node);
852
940
  throw new Error(`Unknown node type`);
@@ -892,13 +980,11 @@ class Executor {
892
980
  return fn(...args);
893
981
  }
894
982
  executeAssignment(node) {
895
- const value = this.execute(node.value);
896
- this.variables.set(node.name, value);
897
- return value;
898
- }
899
- executeNullishAssignment(node) {
900
- if (this.variables.has(node.name)) {
901
- return this.variables.get(node.name);
983
+ if (this.externalVariables.has(node.name)) {
984
+ const externalValue = this.variables.get(node.name);
985
+ if (externalValue !== undefined) {
986
+ return externalValue;
987
+ }
902
988
  }
903
989
  const value = this.execute(node.value);
904
990
  this.variables.set(node.name, value);
@@ -916,420 +1002,47 @@ function execute(source, context) {
916
1002
  }
917
1003
  // src/optimizer.ts
918
1004
  function optimize(node) {
919
- if (!isProgram(node)) {
920
- return basicOptimize(node);
921
- }
922
- const analysis = analyzeProgram(node);
923
- const { propagated, allConstants } = propagateConstantsOptimal(node, analysis);
924
- const optimized = eliminateDeadCodeOptimal(propagated, analysis, allConstants);
925
- return optimized;
926
- }
927
- function analyzeProgram(node) {
928
- if (!isProgram(node)) {
929
- return {
930
- constants: new Map,
931
- tainted: new Set,
932
- dependencies: new Map,
933
- liveVariables: new Set,
934
- assignmentIndices: new Map,
935
- evaluationOrder: []
936
- };
937
- }
938
- const constants = new Map;
939
- const tainted = new Set;
940
- const dependencies = new Map;
941
- const assignmentIndices = new Map;
942
- const assignmentCounts = new Map;
943
- for (let i = 0;i < node.statements.length; i++) {
944
- const stmt = node.statements[i];
945
- if (!stmt)
946
- continue;
947
- if (isAssignment(stmt) || isNullishAssignment(stmt)) {
948
- const varName = stmt.name;
949
- const count = assignmentCounts.get(varName) || 0;
950
- assignmentCounts.set(varName, count + 1);
951
- assignmentIndices.set(varName, i);
952
- const deps = new Set;
953
- const hasFunctionCall = collectDependencies(stmt.value, deps);
954
- dependencies.set(varName, deps);
955
- if (isNullishAssignment(stmt)) {
956
- tainted.add(varName);
957
- } else {
958
- if (count === 0 && isNumberLiteral(stmt.value)) {
959
- constants.set(varName, stmt.value.value);
960
- }
961
- if (hasFunctionCall) {
962
- tainted.add(varName);
963
- }
964
- }
965
- }
966
- }
967
- for (const [varName, count] of assignmentCounts) {
968
- if (count > 1) {
969
- constants.delete(varName);
970
- tainted.add(varName);
971
- }
972
- }
973
- let taintChanged = true;
974
- while (taintChanged) {
975
- taintChanged = false;
976
- for (const [varName, deps] of dependencies) {
977
- if (tainted.has(varName))
978
- continue;
979
- for (const dep of deps) {
980
- if (tainted.has(dep)) {
981
- tainted.add(varName);
982
- constants.delete(varName);
983
- taintChanged = true;
984
- break;
985
- }
986
- }
987
- }
988
- }
989
- const liveVariables = new Set;
990
- const lastStmt = node.statements[node.statements.length - 1];
991
- if (lastStmt) {
992
- if (isAssignment(lastStmt)) {
993
- const deps = new Set;
994
- collectDependencies(lastStmt.value, deps);
995
- for (const dep of deps) {
996
- liveVariables.add(dep);
997
- }
998
- } else {
999
- const deps = new Set;
1000
- collectDependencies(lastStmt, deps);
1001
- for (const dep of deps) {
1002
- liveVariables.add(dep);
1003
- }
1004
- }
1005
- }
1006
- let liveChanged = true;
1007
- while (liveChanged) {
1008
- liveChanged = false;
1009
- for (const [varName, deps] of dependencies) {
1010
- if (liveVariables.has(varName)) {
1011
- for (const dep of deps) {
1012
- if (!liveVariables.has(dep)) {
1013
- liveVariables.add(dep);
1014
- liveChanged = true;
1015
- }
1016
- }
1017
- }
1018
- }
1019
- }
1020
- const evaluationOrder = topologicalSort(dependencies, liveVariables);
1021
- return {
1022
- constants,
1023
- tainted,
1024
- dependencies,
1025
- liveVariables,
1026
- assignmentIndices,
1027
- evaluationOrder
1028
- };
1029
- }
1030
- function collectDependencies(node, deps) {
1031
- if (isIdentifier(node)) {
1032
- deps.add(node.name);
1033
- return false;
1034
- }
1035
1005
  if (isNumberLiteral(node)) {
1036
- return false;
1037
- }
1038
- if (isAssignment(node) || isNullishAssignment(node)) {
1039
- return collectDependencies(node.value, deps);
1040
- }
1041
- if (isBinaryOp(node)) {
1042
- const leftHasCall = collectDependencies(node.left, deps);
1043
- const rightHasCall = collectDependencies(node.right, deps);
1044
- return leftHasCall || rightHasCall;
1045
- }
1046
- if (isUnaryOp(node)) {
1047
- return collectDependencies(node.argument, deps);
1048
- }
1049
- if (isFunctionCall(node)) {
1050
- for (const arg of node.arguments) {
1051
- collectDependencies(arg, deps);
1052
- }
1053
- return true;
1054
- }
1055
- if (isConditionalExpression(node)) {
1056
- const condHasCall = collectDependencies(node.condition, deps);
1057
- const consHasCall = collectDependencies(node.consequent, deps);
1058
- const altHasCall = collectDependencies(node.alternate, deps);
1059
- return condHasCall || consHasCall || altHasCall;
1060
- }
1061
- if (isProgram(node)) {
1062
- let hasCall = false;
1063
- for (const stmt of node.statements) {
1064
- hasCall = collectDependencies(stmt, deps) || hasCall;
1065
- }
1066
- return hasCall;
1067
- }
1068
- return false;
1069
- }
1070
- function topologicalSort(dependencies, liveVariables) {
1071
- const result = [];
1072
- const visited = new Set;
1073
- const visiting = new Set;
1074
- function visit(varName) {
1075
- if (visited.has(varName))
1076
- return;
1077
- if (visiting.has(varName)) {
1078
- return;
1079
- }
1080
- visiting.add(varName);
1081
- const deps = dependencies.get(varName);
1082
- if (deps) {
1083
- for (const dep of deps) {
1084
- if (liveVariables.has(dep)) {
1085
- visit(dep);
1086
- }
1087
- }
1088
- }
1089
- visiting.delete(varName);
1090
- visited.add(varName);
1091
- result.push(varName);
1092
- }
1093
- for (const varName of liveVariables) {
1094
- visit(varName);
1095
- }
1096
- return result;
1097
- }
1098
- function propagateConstantsOptimal(node, analysis) {
1099
- if (!isProgram(node)) {
1100
- return { propagated: node, allConstants: new Map };
1101
- }
1102
- const allConstants = new Map(analysis.constants);
1103
- for (const varName of analysis.evaluationOrder) {
1104
- if (allConstants.has(varName))
1105
- continue;
1106
- if (analysis.tainted.has(varName))
1107
- continue;
1108
- const deps = analysis.dependencies.get(varName);
1109
- if (!deps)
1110
- continue;
1111
- let allDepsConstant = true;
1112
- for (const dep of deps) {
1113
- if (!allConstants.has(dep)) {
1114
- allDepsConstant = false;
1115
- break;
1116
- }
1117
- }
1118
- if (allDepsConstant) {
1119
- const assignmentIdx = analysis.assignmentIndices.get(varName);
1120
- if (assignmentIdx !== undefined) {
1121
- const stmt = node.statements[assignmentIdx];
1122
- if (stmt && isAssignment(stmt)) {
1123
- const evaluated = evaluateWithConstants(stmt.value, allConstants);
1124
- if (isNumberLiteral(evaluated)) {
1125
- allConstants.set(varName, evaluated.value);
1126
- }
1127
- }
1128
- }
1129
- }
1130
- }
1131
- const statements = node.statements.map((stmt) => replaceWithConstants(stmt, allConstants));
1132
- return {
1133
- propagated: {
1134
- type: "Program",
1135
- statements
1136
- },
1137
- allConstants
1138
- };
1139
- }
1140
- function evaluateWithConstants(node, constants) {
1141
- if (isIdentifier(node)) {
1142
- const value = constants.get(node.name);
1143
- if (value !== undefined) {
1144
- return number(value);
1145
- }
1146
1006
  return node;
1147
1007
  }
1148
- if (isNumberLiteral(node)) {
1008
+ if (node.type === "Identifier") {
1149
1009
  return node;
1150
1010
  }
1151
1011
  if (isBinaryOp(node)) {
1152
- const left = evaluateWithConstants(node.left, constants);
1153
- const right = evaluateWithConstants(node.right, constants);
1012
+ const left = optimize(node.left);
1013
+ const right = optimize(node.right);
1154
1014
  if (isNumberLiteral(left) && isNumberLiteral(right)) {
1155
1015
  const result = evaluateBinaryOperation(node.operator, left.value, right.value);
1156
1016
  return number(result);
1157
1017
  }
1158
- return {
1159
- ...node,
1160
- left,
1161
- right
1162
- };
1018
+ return binaryOp(left, node.operator, right);
1163
1019
  }
1164
1020
  if (isUnaryOp(node)) {
1165
- const argument = evaluateWithConstants(node.argument, constants);
1021
+ const argument = optimize(node.argument);
1166
1022
  if (isNumberLiteral(argument)) {
1167
1023
  return number(-argument.value);
1168
1024
  }
1169
- return {
1170
- ...node,
1171
- argument
1172
- };
1025
+ return unaryOp(argument);
1173
1026
  }
1174
1027
  if (isFunctionCall(node)) {
1175
- return {
1176
- ...node,
1177
- arguments: node.arguments.map((arg) => evaluateWithConstants(arg, constants))
1178
- };
1179
- }
1180
- if (isConditionalExpression(node)) {
1181
- const condition = evaluateWithConstants(node.condition, constants);
1182
- const consequent = evaluateWithConstants(node.consequent, constants);
1183
- const alternate = evaluateWithConstants(node.alternate, constants);
1184
- if (isNumberLiteral(condition)) {
1185
- return condition.value !== 0 ? consequent : alternate;
1186
- }
1187
- return {
1188
- ...node,
1189
- condition,
1190
- consequent,
1191
- alternate
1192
- };
1028
+ const optimizedArgs = node.arguments.map((arg) => optimize(arg));
1029
+ return functionCall(node.name, optimizedArgs);
1193
1030
  }
1194
1031
  if (isAssignment(node)) {
1195
- return {
1196
- ...node,
1197
- value: evaluateWithConstants(node.value, constants)
1198
- };
1199
- }
1200
- if (isNullishAssignment(node)) {
1201
- return {
1202
- ...node,
1203
- value: evaluateWithConstants(node.value, constants)
1204
- };
1205
- }
1206
- return node;
1207
- }
1208
- function replaceWithConstants(node, constants) {
1209
- return evaluateWithConstants(node, constants);
1210
- }
1211
- function eliminateDeadCodeOptimal(node, analysis, allConstants) {
1212
- if (!isProgram(node))
1213
- return node;
1214
- const lastStmt = node.statements[node.statements.length - 1];
1215
- if (lastStmt) {
1216
- const evaluated = evaluateWithConstants(lastStmt, allConstants);
1217
- if (isNumberLiteral(evaluated)) {
1218
- return evaluated;
1219
- }
1220
- }
1221
- const filteredStatements = [];
1222
- const variablesInUse = new Set;
1223
- for (const stmt of node.statements) {
1224
- collectDependencies(stmt, variablesInUse);
1225
- }
1226
- for (let i = 0;i < node.statements.length; i++) {
1227
- const stmt = node.statements[i];
1228
- if (!stmt)
1229
- continue;
1230
- if (i === node.statements.length - 1) {
1231
- filteredStatements.push(stmt);
1232
- continue;
1233
- }
1234
- if (isAssignment(stmt) || isNullishAssignment(stmt)) {
1235
- if (variablesInUse.has(stmt.name)) {
1236
- filteredStatements.push(stmt);
1237
- }
1238
- } else {
1239
- filteredStatements.push(stmt);
1240
- }
1241
- }
1242
- if (filteredStatements.length === 1) {
1243
- const singleStmt = filteredStatements[0];
1244
- if (!singleStmt) {
1245
- return node;
1246
- }
1247
- if (isNumberLiteral(singleStmt)) {
1248
- return singleStmt;
1249
- }
1250
- if (isAssignment(singleStmt) && isNumberLiteral(singleStmt.value)) {
1251
- if (!analysis.liveVariables.has(singleStmt.name)) {
1252
- return singleStmt.value;
1253
- }
1254
- }
1255
- const evaluated = evaluateWithConstants(singleStmt, allConstants);
1256
- if (isNumberLiteral(evaluated)) {
1257
- return evaluated;
1258
- }
1259
- }
1260
- if (filteredStatements.length === 0) {
1261
- const lastStmt2 = node.statements[node.statements.length - 1];
1262
- if (lastStmt2 && isAssignment(lastStmt2) && isNumberLiteral(lastStmt2.value)) {
1263
- return lastStmt2.value;
1264
- }
1265
- return node;
1266
- }
1267
- return {
1268
- type: "Program",
1269
- statements: filteredStatements
1270
- };
1271
- }
1272
- function basicOptimize(node) {
1273
- if (isAssignment(node)) {
1274
- return {
1275
- ...node,
1276
- value: basicOptimize(node.value)
1277
- };
1278
- }
1279
- if (isNullishAssignment(node)) {
1280
- return {
1281
- ...node,
1282
- value: basicOptimize(node.value)
1283
- };
1284
- }
1285
- if (isBinaryOp(node)) {
1286
- const left = basicOptimize(node.left);
1287
- const right = basicOptimize(node.right);
1288
- if (isNumberLiteral(left) && isNumberLiteral(right)) {
1289
- const result = evaluateBinaryOperation(node.operator, left.value, right.value);
1290
- return number(result);
1291
- }
1292
- return {
1293
- ...node,
1294
- left,
1295
- right
1296
- };
1297
- }
1298
- if (isUnaryOp(node)) {
1299
- const argument = basicOptimize(node.argument);
1300
- if (isNumberLiteral(argument)) {
1301
- return number(-argument.value);
1302
- }
1303
- return {
1304
- ...node,
1305
- argument
1306
- };
1307
- }
1308
- if (isFunctionCall(node)) {
1309
- return {
1310
- ...node,
1311
- arguments: node.arguments.map((arg) => basicOptimize(arg))
1312
- };
1032
+ return assign(node.name, optimize(node.value));
1313
1033
  }
1314
1034
  if (isConditionalExpression(node)) {
1315
- const condition = basicOptimize(node.condition);
1316
- const consequent = basicOptimize(node.consequent);
1317
- const alternate = basicOptimize(node.alternate);
1035
+ const condition = optimize(node.condition);
1318
1036
  if (isNumberLiteral(condition)) {
1319
- return condition.value !== 0 ? consequent : alternate;
1037
+ return condition.value !== 0 ? optimize(node.consequent) : optimize(node.alternate);
1320
1038
  }
1321
- return {
1322
- ...node,
1323
- condition,
1324
- consequent,
1325
- alternate
1326
- };
1039
+ const consequent = optimize(node.consequent);
1040
+ const alternate = optimize(node.alternate);
1041
+ return conditional(condition, consequent, alternate);
1327
1042
  }
1328
1043
  if (isProgram(node)) {
1329
- return {
1330
- ...node,
1331
- statements: node.statements.map((stmt) => basicOptimize(stmt))
1332
- };
1044
+ const optimizedStatements = node.statements.map((stmt) => optimize(stmt));
1045
+ return program(optimizedStatements);
1333
1046
  }
1334
1047
  return node;
1335
1048
  }
@@ -1339,7 +1052,6 @@ export {
1339
1052
  isUnaryOp,
1340
1053
  isProgram,
1341
1054
  isNumberLiteral,
1342
- isNullishAssignment,
1343
1055
  isIdentifier,
1344
1056
  isFunctionCall,
1345
1057
  isConditionalExpression,
@@ -1348,6 +1060,7 @@ export {
1348
1060
  generate,
1349
1061
  execute,
1350
1062
  defaultContext,
1063
+ exports_date_utils as dateUtils,
1351
1064
  exports_ast as ast,
1352
1065
  TokenType,
1353
1066
  Parser,