@huggingface/transformers 3.6.2 → 3.6.3

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.
@@ -111,15 +111,11 @@ var TOKEN_TYPES = Object.freeze({
111
111
  Text: "Text",
112
112
  // The text between Jinja statements or expressions
113
113
  NumericLiteral: "NumericLiteral",
114
- // e.g., 123
115
- BooleanLiteral: "BooleanLiteral",
116
- // true or false
117
- NullLiteral: "NullLiteral",
118
- // none
114
+ // e.g., 123, 1.0
119
115
  StringLiteral: "StringLiteral",
120
116
  // 'string'
121
117
  Identifier: "Identifier",
122
- // Variables, functions, etc.
118
+ // Variables, functions, statements, booleans, etc.
123
119
  Equals: "Equals",
124
120
  // =
125
121
  OpenParen: "OpenParen",
@@ -153,63 +149,15 @@ var TOKEN_TYPES = Object.freeze({
153
149
  CallOperator: "CallOperator",
154
150
  // ()
155
151
  AdditiveBinaryOperator: "AdditiveBinaryOperator",
156
- // + -
152
+ // + - ~
157
153
  MultiplicativeBinaryOperator: "MultiplicativeBinaryOperator",
158
154
  // * / %
159
155
  ComparisonBinaryOperator: "ComparisonBinaryOperator",
160
156
  // < > <= >= == !=
161
157
  UnaryOperator: "UnaryOperator",
162
158
  // ! - +
163
- // Keywords
164
- Set: "Set",
165
- If: "If",
166
- For: "For",
167
- In: "In",
168
- Is: "Is",
169
- NotIn: "NotIn",
170
- Else: "Else",
171
- EndSet: "EndSet",
172
- EndIf: "EndIf",
173
- ElseIf: "ElseIf",
174
- EndFor: "EndFor",
175
- And: "And",
176
- Or: "Or",
177
- Not: "UnaryOperator",
178
- Macro: "Macro",
179
- EndMacro: "EndMacro",
180
- Break: "Break",
181
- Continue: "Continue"
182
- });
183
- var KEYWORDS = Object.freeze({
184
- set: TOKEN_TYPES.Set,
185
- for: TOKEN_TYPES.For,
186
- in: TOKEN_TYPES.In,
187
- is: TOKEN_TYPES.Is,
188
- if: TOKEN_TYPES.If,
189
- else: TOKEN_TYPES.Else,
190
- endset: TOKEN_TYPES.EndSet,
191
- endif: TOKEN_TYPES.EndIf,
192
- elif: TOKEN_TYPES.ElseIf,
193
- endfor: TOKEN_TYPES.EndFor,
194
- and: TOKEN_TYPES.And,
195
- or: TOKEN_TYPES.Or,
196
- not: TOKEN_TYPES.Not,
197
- "not in": TOKEN_TYPES.NotIn,
198
- macro: TOKEN_TYPES.Macro,
199
- endmacro: TOKEN_TYPES.EndMacro,
200
- break: TOKEN_TYPES.Break,
201
- continue: TOKEN_TYPES.Continue,
202
- // Literals
203
- true: TOKEN_TYPES.BooleanLiteral,
204
- false: TOKEN_TYPES.BooleanLiteral,
205
- none: TOKEN_TYPES.NullLiteral,
206
- // NOTE: According to the Jinja docs: The special constants true, false, and none are indeed lowercase.
207
- // Because that caused confusion in the past, (True used to expand to an undefined variable that was considered false),
208
- // all three can now also be written in title case (True, False, and None). However, for consistency, (all Jinja identifiers are lowercase)
209
- // you should use the lowercase versions.
210
- True: TOKEN_TYPES.BooleanLiteral,
211
- False: TOKEN_TYPES.BooleanLiteral,
212
- None: TOKEN_TYPES.NullLiteral
159
+ Comment: "Comment"
160
+ // {# ... #}
213
161
  });
214
162
  var Token = class {
215
163
  /**
@@ -255,6 +203,7 @@ var ORDERED_MAPPING_TABLE = [
255
203
  // Arithmetic operators
256
204
  ["+", TOKEN_TYPES.AdditiveBinaryOperator],
257
205
  ["-", TOKEN_TYPES.AdditiveBinaryOperator],
206
+ ["~", TOKEN_TYPES.AdditiveBinaryOperator],
258
207
  ["*", TOKEN_TYPES.MultiplicativeBinaryOperator],
259
208
  ["/", TOKEN_TYPES.MultiplicativeBinaryOperator],
260
209
  ["%", TOKEN_TYPES.MultiplicativeBinaryOperator],
@@ -285,19 +234,19 @@ function preprocess(template, options = {}) {
285
234
  if (template.endsWith("\n")) {
286
235
  template = template.slice(0, -1);
287
236
  }
288
- template = template.replace(/{#.*?#}/gs, "{##}");
289
237
  if (options.lstrip_blocks) {
290
- template = template.replace(/^[ \t]*({[#%])/gm, "$1");
238
+ template = template.replace(/^[ \t]*({[#%-])/gm, "$1");
291
239
  }
292
240
  if (options.trim_blocks) {
293
- template = template.replace(/([#%]})\n/g, "$1");
241
+ template = template.replace(/([#%-]})\n/g, "$1");
294
242
  }
295
- return template.replace(/{##}/g, "").replace(/-%}\s*/g, "%}").replace(/\s*{%-/g, "{%").replace(/-}}\s*/g, "}}").replace(/\s*{{-/g, "{{");
243
+ return template.replace(/-%}\s*/g, "%}").replace(/\s*{%-/g, "{%").replace(/-}}\s*/g, "}}").replace(/\s*{{-/g, "{{").replace(/-#}\s*/g, "#}").replace(/\s*{#-/g, "{#").replace(/{%\s*(end)?generation\s*%}/gs, "");
296
244
  }
297
245
  function tokenize(source, options = {}) {
298
246
  const tokens = [];
299
247
  const src = preprocess(source, options);
300
248
  let cursorPosition = 0;
249
+ let curlyBracketDepth = 0;
301
250
  const consumeWhile = (predicate) => {
302
251
  let str = "";
303
252
  while (predicate(src[cursorPosition])) {
@@ -322,10 +271,10 @@ function tokenize(source, options = {}) {
322
271
  main:
323
272
  while (cursorPosition < src.length) {
324
273
  const lastTokenType = tokens.at(-1)?.type;
325
- if (lastTokenType === void 0 || lastTokenType === TOKEN_TYPES.CloseStatement || lastTokenType === TOKEN_TYPES.CloseExpression) {
274
+ if (lastTokenType === void 0 || lastTokenType === TOKEN_TYPES.CloseStatement || lastTokenType === TOKEN_TYPES.CloseExpression || lastTokenType === TOKEN_TYPES.Comment) {
326
275
  let text = "";
327
276
  while (cursorPosition < src.length && // Keep going until we hit the next Jinja statement or expression
328
- !(src[cursorPosition] === "{" && (src[cursorPosition + 1] === "%" || src[cursorPosition + 1] === "{"))) {
277
+ !(src[cursorPosition] === "{" && (src[cursorPosition + 1] === "%" || src[cursorPosition + 1] === "{" || src[cursorPosition + 1] === "#"))) {
329
278
  text += src[cursorPosition++];
330
279
  }
331
280
  if (text.length > 0) {
@@ -333,6 +282,19 @@ function tokenize(source, options = {}) {
333
282
  continue;
334
283
  }
335
284
  }
285
+ if (src[cursorPosition] === "{" && src[cursorPosition + 1] === "#") {
286
+ cursorPosition += 2;
287
+ let comment = "";
288
+ while (src[cursorPosition] !== "#" || src[cursorPosition + 1] !== "}") {
289
+ if (cursorPosition + 2 >= src.length) {
290
+ throw new SyntaxError("Missing end of comment tag");
291
+ }
292
+ comment += src[cursorPosition++];
293
+ }
294
+ tokens.push(new Token(comment, TOKEN_TYPES.Comment));
295
+ cursorPosition += 2;
296
+ continue;
297
+ }
336
298
  consumeWhile((char2) => /\s/.test(char2));
337
299
  const char = src[cursorPosition];
338
300
  if (char === "-" || char === "+") {
@@ -343,8 +305,6 @@ function tokenize(source, options = {}) {
343
305
  switch (lastTokenType2) {
344
306
  case TOKEN_TYPES.Identifier:
345
307
  case TOKEN_TYPES.NumericLiteral:
346
- case TOKEN_TYPES.BooleanLiteral:
347
- case TOKEN_TYPES.NullLiteral:
348
308
  case TOKEN_TYPES.StringLiteral:
349
309
  case TOKEN_TYPES.CloseParen:
350
310
  case TOKEN_TYPES.CloseSquareBracket:
@@ -359,11 +319,21 @@ function tokenize(source, options = {}) {
359
319
  }
360
320
  }
361
321
  }
362
- for (const [char2, token] of ORDERED_MAPPING_TABLE) {
363
- const slice2 = src.slice(cursorPosition, cursorPosition + char2.length);
364
- if (slice2 === char2) {
365
- tokens.push(new Token(char2, token));
366
- cursorPosition += char2.length;
322
+ for (const [seq, type] of ORDERED_MAPPING_TABLE) {
323
+ if (seq === "}}" && curlyBracketDepth > 0) {
324
+ continue;
325
+ }
326
+ const slice2 = src.slice(cursorPosition, cursorPosition + seq.length);
327
+ if (slice2 === seq) {
328
+ tokens.push(new Token(seq, type));
329
+ if (type === TOKEN_TYPES.OpenExpression) {
330
+ curlyBracketDepth = 0;
331
+ } else if (type === TOKEN_TYPES.OpenCurlyBracket) {
332
+ ++curlyBracketDepth;
333
+ } else if (type === TOKEN_TYPES.CloseCurlyBracket) {
334
+ --curlyBracketDepth;
335
+ }
336
+ cursorPosition += seq.length;
367
337
  continue main;
368
338
  }
369
339
  }
@@ -375,19 +345,18 @@ function tokenize(source, options = {}) {
375
345
  continue;
376
346
  }
377
347
  if (isInteger(char)) {
378
- const num = consumeWhile(isInteger);
348
+ let num = consumeWhile(isInteger);
349
+ if (src[cursorPosition] === "." && isInteger(src[cursorPosition + 1])) {
350
+ ++cursorPosition;
351
+ const frac = consumeWhile(isInteger);
352
+ num = `${num}.${frac}`;
353
+ }
379
354
  tokens.push(new Token(num, TOKEN_TYPES.NumericLiteral));
380
355
  continue;
381
356
  }
382
357
  if (isWord(char)) {
383
358
  const word = consumeWhile(isWord);
384
- const type = Object.hasOwn(KEYWORDS, word) ? KEYWORDS[word] : TOKEN_TYPES.Identifier;
385
- if (type === TOKEN_TYPES.In && tokens.at(-1)?.type === TOKEN_TYPES.Not) {
386
- tokens.pop();
387
- tokens.push(new Token("not in", TOKEN_TYPES.NotIn));
388
- } else {
389
- tokens.push(new Token(word, type));
390
- }
359
+ tokens.push(new Token(word, TOKEN_TYPES.Identifier));
391
360
  continue;
392
361
  }
393
362
  throw new SyntaxError(`Unexpected character: ${char}`);
@@ -449,6 +418,13 @@ var Macro = class extends Statement {
449
418
  }
450
419
  type = "Macro";
451
420
  };
421
+ var Comment = class extends Statement {
422
+ constructor(value) {
423
+ super();
424
+ this.value = value;
425
+ }
426
+ type = "Comment";
427
+ };
452
428
  var Expression = class extends Statement {
453
429
  type = "Expression";
454
430
  };
@@ -486,18 +462,15 @@ var Literal = class extends Expression {
486
462
  }
487
463
  type = "Literal";
488
464
  };
489
- var NumericLiteral = class extends Literal {
490
- type = "NumericLiteral";
465
+ var IntegerLiteral = class extends Literal {
466
+ type = "IntegerLiteral";
467
+ };
468
+ var FloatLiteral = class extends Literal {
469
+ type = "FloatLiteral";
491
470
  };
492
471
  var StringLiteral = class extends Literal {
493
472
  type = "StringLiteral";
494
473
  };
495
- var BooleanLiteral = class extends Literal {
496
- type = "BooleanLiteral";
497
- };
498
- var NullLiteral = class extends Literal {
499
- type = "NullLiteral";
500
- };
501
474
  var ArrayLiteral = class extends Literal {
502
475
  type = "ArrayLiteral";
503
476
  };
@@ -524,10 +497,18 @@ var FilterExpression = class extends Expression {
524
497
  }
525
498
  type = "FilterExpression";
526
499
  };
500
+ var FilterStatement = class extends Statement {
501
+ constructor(filter, body) {
502
+ super();
503
+ this.filter = filter;
504
+ this.body = body;
505
+ }
506
+ type = "FilterStatement";
507
+ };
527
508
  var SelectExpression = class extends Expression {
528
- constructor(iterable, test) {
509
+ constructor(lhs, test) {
529
510
  super();
530
- this.iterable = iterable;
511
+ this.lhs = lhs;
531
512
  this.test = test;
532
513
  }
533
514
  type = "SelectExpression";
@@ -566,6 +547,31 @@ var KeywordArgumentExpression = class extends Expression {
566
547
  }
567
548
  type = "KeywordArgumentExpression";
568
549
  };
550
+ var SpreadExpression = class extends Expression {
551
+ constructor(argument) {
552
+ super();
553
+ this.argument = argument;
554
+ }
555
+ type = "SpreadExpression";
556
+ };
557
+ var CallStatement = class extends Statement {
558
+ constructor(call, callerArgs, body) {
559
+ super();
560
+ this.call = call;
561
+ this.callerArgs = callerArgs;
562
+ this.body = body;
563
+ }
564
+ type = "CallStatement";
565
+ };
566
+ var Ternary = class extends Expression {
567
+ constructor(condition, trueExpr, falseExpr) {
568
+ super();
569
+ this.condition = condition;
570
+ this.trueExpr = trueExpr;
571
+ this.falseExpr = falseExpr;
572
+ }
573
+ type = "Ternary";
574
+ };
569
575
 
570
576
  // src/parser.ts
571
577
  function parse(tokens) {
@@ -578,8 +584,16 @@ function parse(tokens) {
578
584
  }
579
585
  return prev;
580
586
  }
587
+ function expectIdentifier(name) {
588
+ if (!isIdentifier(name)) {
589
+ throw new SyntaxError(`Expected ${name}`);
590
+ }
591
+ ++current;
592
+ }
581
593
  function parseAny() {
582
594
  switch (tokens[current].type) {
595
+ case TOKEN_TYPES.Comment:
596
+ return new Comment(tokens[current++].value);
583
597
  case TOKEN_TYPES.Text:
584
598
  return parseText();
585
599
  case TOKEN_TYPES.OpenStatement:
@@ -590,57 +604,103 @@ function parse(tokens) {
590
604
  throw new SyntaxError(`Unexpected token type: ${tokens[current].type}`);
591
605
  }
592
606
  }
593
- function not(...types) {
594
- return current + types.length <= tokens.length && types.some((type, i) => type !== tokens[current + i].type);
595
- }
596
607
  function is(...types) {
597
608
  return current + types.length <= tokens.length && types.every((type, i) => type === tokens[current + i].type);
598
609
  }
610
+ function isStatement(...names) {
611
+ return tokens[current]?.type === TOKEN_TYPES.OpenStatement && tokens[current + 1]?.type === TOKEN_TYPES.Identifier && names.includes(tokens[current + 1]?.value);
612
+ }
613
+ function isIdentifier(...names) {
614
+ return current + names.length <= tokens.length && names.every((name, i) => tokens[current + i].type === "Identifier" && name === tokens[current + i].value);
615
+ }
599
616
  function parseText() {
600
617
  return new StringLiteral(expect(TOKEN_TYPES.Text, "Expected text token").value);
601
618
  }
602
619
  function parseJinjaStatement() {
603
620
  expect(TOKEN_TYPES.OpenStatement, "Expected opening statement token");
621
+ if (tokens[current].type !== TOKEN_TYPES.Identifier) {
622
+ throw new SyntaxError(`Unknown statement, got ${tokens[current].type}`);
623
+ }
624
+ const name = tokens[current].value;
604
625
  let result;
605
- switch (tokens[current].type) {
606
- case TOKEN_TYPES.Set:
626
+ switch (name) {
627
+ case "set":
607
628
  ++current;
608
629
  result = parseSetStatement();
609
- expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
610
630
  break;
611
- case TOKEN_TYPES.If:
631
+ case "if":
612
632
  ++current;
613
633
  result = parseIfStatement();
614
634
  expect(TOKEN_TYPES.OpenStatement, "Expected {% token");
615
- expect(TOKEN_TYPES.EndIf, "Expected endif token");
635
+ expectIdentifier("endif");
616
636
  expect(TOKEN_TYPES.CloseStatement, "Expected %} token");
617
637
  break;
618
- case TOKEN_TYPES.Macro:
638
+ case "macro":
619
639
  ++current;
620
640
  result = parseMacroStatement();
621
641
  expect(TOKEN_TYPES.OpenStatement, "Expected {% token");
622
- expect(TOKEN_TYPES.EndMacro, "Expected endmacro token");
642
+ expectIdentifier("endmacro");
623
643
  expect(TOKEN_TYPES.CloseStatement, "Expected %} token");
624
644
  break;
625
- case TOKEN_TYPES.For:
645
+ case "for":
626
646
  ++current;
627
647
  result = parseForStatement();
628
648
  expect(TOKEN_TYPES.OpenStatement, "Expected {% token");
629
- expect(TOKEN_TYPES.EndFor, "Expected endfor token");
649
+ expectIdentifier("endfor");
630
650
  expect(TOKEN_TYPES.CloseStatement, "Expected %} token");
631
651
  break;
632
- case TOKEN_TYPES.Break:
652
+ case "call": {
653
+ ++current;
654
+ let callerArgs = null;
655
+ if (is(TOKEN_TYPES.OpenParen)) {
656
+ callerArgs = parseArgs();
657
+ }
658
+ const callee = parsePrimaryExpression();
659
+ if (callee.type !== "Identifier") {
660
+ throw new SyntaxError(`Expected identifier following call statement`);
661
+ }
662
+ const callArgs = parseArgs();
663
+ expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
664
+ const body = [];
665
+ while (!isStatement("endcall")) {
666
+ body.push(parseAny());
667
+ }
668
+ expect(TOKEN_TYPES.OpenStatement, "Expected '{%'");
669
+ expectIdentifier("endcall");
670
+ expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
671
+ const callExpr = new CallExpression(callee, callArgs);
672
+ result = new CallStatement(callExpr, callerArgs, body);
673
+ break;
674
+ }
675
+ case "break":
633
676
  ++current;
634
677
  expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
635
678
  result = new Break();
636
679
  break;
637
- case TOKEN_TYPES.Continue:
680
+ case "continue":
638
681
  ++current;
639
682
  expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
640
683
  result = new Continue();
641
684
  break;
685
+ case "filter": {
686
+ ++current;
687
+ let filterNode = parsePrimaryExpression();
688
+ if (filterNode instanceof Identifier && is(TOKEN_TYPES.OpenParen)) {
689
+ filterNode = parseCallExpression(filterNode);
690
+ }
691
+ expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
692
+ const filterBody = [];
693
+ while (!isStatement("endfilter")) {
694
+ filterBody.push(parseAny());
695
+ }
696
+ expect(TOKEN_TYPES.OpenStatement, "Expected '{%'");
697
+ expectIdentifier("endfilter");
698
+ expect(TOKEN_TYPES.CloseStatement, "Expected '%}'");
699
+ result = new FilterStatement(filterNode, filterBody);
700
+ break;
701
+ }
642
702
  default:
643
- throw new SyntaxError(`Unknown statement type: ${tokens[current].type}`);
703
+ throw new SyntaxError(`Unknown statement type: ${name}`);
644
704
  }
645
705
  return result;
646
706
  }
@@ -651,42 +711,42 @@ function parse(tokens) {
651
711
  return result;
652
712
  }
653
713
  function parseSetStatement() {
654
- const left = parseExpression();
714
+ const left = parseExpressionSequence();
715
+ let value = null;
716
+ const body = [];
655
717
  if (is(TOKEN_TYPES.Equals)) {
656
718
  ++current;
657
- const value = parseExpression();
658
- return new SetStatement(left, value, []);
719
+ value = parseExpressionSequence();
659
720
  } else {
660
- const body = [];
661
721
  expect(TOKEN_TYPES.CloseStatement, "Expected %} token");
662
- while (!(tokens[current]?.type === TOKEN_TYPES.OpenStatement && tokens[current + 1]?.type === TOKEN_TYPES.EndSet)) {
663
- const another = parseAny();
664
- body.push(another);
722
+ while (!isStatement("endset")) {
723
+ body.push(parseAny());
665
724
  }
666
725
  expect(TOKEN_TYPES.OpenStatement, "Expected {% token");
667
- expect(TOKEN_TYPES.EndSet, "Expected endset token");
668
- return new SetStatement(left, null, body);
726
+ expectIdentifier("endset");
669
727
  }
728
+ expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
729
+ return new SetStatement(left, value, body);
670
730
  }
671
731
  function parseIfStatement() {
672
732
  const test = parseExpression();
673
733
  expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
674
734
  const body = [];
675
735
  const alternate = [];
676
- while (!(tokens[current]?.type === TOKEN_TYPES.OpenStatement && (tokens[current + 1]?.type === TOKEN_TYPES.ElseIf || tokens[current + 1]?.type === TOKEN_TYPES.Else || tokens[current + 1]?.type === TOKEN_TYPES.EndIf))) {
736
+ while (!isStatement("elif", "else", "endif")) {
677
737
  body.push(parseAny());
678
738
  }
679
- if (tokens[current]?.type === TOKEN_TYPES.OpenStatement && tokens[current + 1]?.type !== TOKEN_TYPES.EndIf) {
739
+ if (isStatement("elif")) {
680
740
  ++current;
681
- if (is(TOKEN_TYPES.ElseIf)) {
682
- expect(TOKEN_TYPES.ElseIf, "Expected elseif token");
683
- alternate.push(parseIfStatement());
684
- } else {
685
- expect(TOKEN_TYPES.Else, "Expected else token");
686
- expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
687
- while (!(tokens[current]?.type === TOKEN_TYPES.OpenStatement && tokens[current + 1]?.type === TOKEN_TYPES.EndIf)) {
688
- alternate.push(parseAny());
689
- }
741
+ ++current;
742
+ const result = parseIfStatement();
743
+ alternate.push(result);
744
+ } else if (isStatement("else")) {
745
+ ++current;
746
+ ++current;
747
+ expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
748
+ while (!isStatement("endif")) {
749
+ alternate.push(parseAny());
690
750
  }
691
751
  }
692
752
  return new If(test, body, alternate);
@@ -699,7 +759,7 @@ function parse(tokens) {
699
759
  const args = parseArgs();
700
760
  expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
701
761
  const body = [];
702
- while (not(TOKEN_TYPES.OpenStatement, TOKEN_TYPES.EndMacro)) {
762
+ while (!isStatement("endmacro")) {
703
763
  body.push(parseAny());
704
764
  }
705
765
  return new Macro(name, args, body);
@@ -722,19 +782,22 @@ function parse(tokens) {
722
782
  if (!(loopVariable instanceof Identifier || loopVariable instanceof TupleLiteral)) {
723
783
  throw new SyntaxError(`Expected identifier/tuple for the loop variable, got ${loopVariable.type} instead`);
724
784
  }
725
- expect(TOKEN_TYPES.In, "Expected `in` keyword following loop variable");
785
+ if (!isIdentifier("in")) {
786
+ throw new SyntaxError("Expected `in` keyword following loop variable");
787
+ }
788
+ ++current;
726
789
  const iterable = parseExpression();
727
790
  expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
728
791
  const body = [];
729
- while (not(TOKEN_TYPES.OpenStatement, TOKEN_TYPES.EndFor) && not(TOKEN_TYPES.OpenStatement, TOKEN_TYPES.Else)) {
792
+ while (!isStatement("endfor", "else")) {
730
793
  body.push(parseAny());
731
794
  }
732
795
  const alternative = [];
733
- if (is(TOKEN_TYPES.OpenStatement, TOKEN_TYPES.Else)) {
796
+ if (isStatement("else")) {
734
797
  ++current;
735
798
  ++current;
736
799
  expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
737
- while (not(TOKEN_TYPES.OpenStatement, TOKEN_TYPES.EndFor)) {
800
+ while (!isStatement("endfor")) {
738
801
  alternative.push(parseAny());
739
802
  }
740
803
  }
@@ -745,22 +808,22 @@ function parse(tokens) {
745
808
  }
746
809
  function parseIfExpression() {
747
810
  const a = parseLogicalOrExpression();
748
- if (is(TOKEN_TYPES.If)) {
811
+ if (isIdentifier("if")) {
749
812
  ++current;
750
- const predicate = parseLogicalOrExpression();
751
- if (is(TOKEN_TYPES.Else)) {
813
+ const test = parseLogicalOrExpression();
814
+ if (isIdentifier("else")) {
752
815
  ++current;
753
- const b = parseLogicalOrExpression();
754
- return new If(predicate, [a], [b]);
816
+ const falseExpr = parseIfExpression();
817
+ return new Ternary(test, a, falseExpr);
755
818
  } else {
756
- return new SelectExpression(a, predicate);
819
+ return new SelectExpression(a, test);
757
820
  }
758
821
  }
759
822
  return a;
760
823
  }
761
824
  function parseLogicalOrExpression() {
762
825
  let left = parseLogicalAndExpression();
763
- while (is(TOKEN_TYPES.Or)) {
826
+ while (isIdentifier("or")) {
764
827
  const operator = tokens[current];
765
828
  ++current;
766
829
  const right = parseLogicalAndExpression();
@@ -770,7 +833,7 @@ function parse(tokens) {
770
833
  }
771
834
  function parseLogicalAndExpression() {
772
835
  let left = parseLogicalNegationExpression();
773
- while (is(TOKEN_TYPES.And)) {
836
+ while (isIdentifier("and")) {
774
837
  const operator = tokens[current];
775
838
  ++current;
776
839
  const right = parseLogicalNegationExpression();
@@ -780,7 +843,7 @@ function parse(tokens) {
780
843
  }
781
844
  function parseLogicalNegationExpression() {
782
845
  let right;
783
- while (is(TOKEN_TYPES.Not)) {
846
+ while (isIdentifier("not")) {
784
847
  const operator = tokens[current];
785
848
  ++current;
786
849
  const arg = parseLogicalNegationExpression();
@@ -790,9 +853,18 @@ function parse(tokens) {
790
853
  }
791
854
  function parseComparisonExpression() {
792
855
  let left = parseAdditiveExpression();
793
- while (is(TOKEN_TYPES.ComparisonBinaryOperator) || is(TOKEN_TYPES.In) || is(TOKEN_TYPES.NotIn)) {
794
- const operator = tokens[current];
795
- ++current;
856
+ while (true) {
857
+ let operator;
858
+ if (isIdentifier("not", "in")) {
859
+ operator = new Token("not in", TOKEN_TYPES.Identifier);
860
+ current += 2;
861
+ } else if (isIdentifier("in")) {
862
+ operator = tokens[current++];
863
+ } else if (is(TOKEN_TYPES.ComparisonBinaryOperator)) {
864
+ operator = tokens[current++];
865
+ } else {
866
+ break;
867
+ }
796
868
  const right = parseAdditiveExpression();
797
869
  left = new BinaryExpression(operator, left, right);
798
870
  }
@@ -832,14 +904,21 @@ function parse(tokens) {
832
904
  function parseArgumentsList() {
833
905
  const args = [];
834
906
  while (!is(TOKEN_TYPES.CloseParen)) {
835
- let argument = parseExpression();
836
- if (is(TOKEN_TYPES.Equals)) {
907
+ let argument;
908
+ if (tokens[current].type === TOKEN_TYPES.MultiplicativeBinaryOperator && tokens[current].value === "*") {
837
909
  ++current;
838
- if (!(argument instanceof Identifier)) {
839
- throw new SyntaxError(`Expected identifier for keyword argument`);
910
+ const expr = parseExpression();
911
+ argument = new SpreadExpression(expr);
912
+ } else {
913
+ argument = parseExpression();
914
+ if (is(TOKEN_TYPES.Equals)) {
915
+ ++current;
916
+ if (!(argument instanceof Identifier)) {
917
+ throw new SyntaxError(`Expected identifier for keyword argument`);
918
+ }
919
+ const value = parseExpression();
920
+ argument = new KeywordArgumentExpression(argument, value);
840
921
  }
841
- const value = parseExpression();
842
- argument = new KeywordArgumentExpression(argument, value);
843
922
  }
844
923
  args.push(argument);
845
924
  if (is(TOKEN_TYPES.Comma)) {
@@ -880,7 +959,7 @@ function parse(tokens) {
880
959
  const operator = tokens[current];
881
960
  ++current;
882
961
  let property;
883
- const computed = operator.type !== TOKEN_TYPES.Dot;
962
+ const computed = operator.type === TOKEN_TYPES.OpenSquareBracket;
884
963
  if (computed) {
885
964
  property = parseMemberExpressionArgumentsList();
886
965
  expect(TOKEN_TYPES.CloseSquareBracket, "Expected closing square bracket");
@@ -897,8 +976,7 @@ function parse(tokens) {
897
976
  function parseMultiplicativeExpression() {
898
977
  let left = parseTestExpression();
899
978
  while (is(TOKEN_TYPES.MultiplicativeBinaryOperator)) {
900
- const operator = tokens[current];
901
- ++current;
979
+ const operator = tokens[current++];
902
980
  const right = parseTestExpression();
903
981
  left = new BinaryExpression(operator, left, right);
904
982
  }
@@ -906,18 +984,13 @@ function parse(tokens) {
906
984
  }
907
985
  function parseTestExpression() {
908
986
  let operand = parseFilterExpression();
909
- while (is(TOKEN_TYPES.Is)) {
987
+ while (isIdentifier("is")) {
910
988
  ++current;
911
- const negate = is(TOKEN_TYPES.Not);
989
+ const negate = isIdentifier("not");
912
990
  if (negate) {
913
991
  ++current;
914
992
  }
915
- let filter = parsePrimaryExpression();
916
- if (filter instanceof BooleanLiteral) {
917
- filter = new Identifier(filter.value.toString());
918
- } else if (filter instanceof NullLiteral) {
919
- filter = new Identifier("none");
920
- }
993
+ const filter = parsePrimaryExpression();
921
994
  if (!(filter instanceof Identifier)) {
922
995
  throw new SyntaxError(`Expected identifier for the test`);
923
996
  }
@@ -941,34 +1014,27 @@ function parse(tokens) {
941
1014
  return operand;
942
1015
  }
943
1016
  function parsePrimaryExpression() {
944
- const token = tokens[current];
1017
+ const token = tokens[current++];
945
1018
  switch (token.type) {
946
- case TOKEN_TYPES.NumericLiteral:
947
- ++current;
948
- return new NumericLiteral(Number(token.value));
949
- case TOKEN_TYPES.StringLiteral:
950
- ++current;
951
- return new StringLiteral(token.value);
952
- case TOKEN_TYPES.BooleanLiteral:
953
- ++current;
954
- return new BooleanLiteral(token.value.toLowerCase() === "true");
955
- case TOKEN_TYPES.NullLiteral:
956
- ++current;
957
- return new NullLiteral(null);
1019
+ case TOKEN_TYPES.NumericLiteral: {
1020
+ const num = token.value;
1021
+ return num.includes(".") ? new FloatLiteral(Number(num)) : new IntegerLiteral(Number(num));
1022
+ }
1023
+ case TOKEN_TYPES.StringLiteral: {
1024
+ let value = token.value;
1025
+ while (is(TOKEN_TYPES.StringLiteral)) {
1026
+ value += tokens[current++].value;
1027
+ }
1028
+ return new StringLiteral(value);
1029
+ }
958
1030
  case TOKEN_TYPES.Identifier:
959
- ++current;
960
1031
  return new Identifier(token.value);
961
1032
  case TOKEN_TYPES.OpenParen: {
962
- ++current;
963
1033
  const expression = parseExpressionSequence();
964
- if (tokens[current].type !== TOKEN_TYPES.CloseParen) {
965
- throw new SyntaxError(`Expected closing parenthesis, got ${tokens[current].type} instead`);
966
- }
967
- ++current;
1034
+ expect(TOKEN_TYPES.CloseParen, "Expected closing parenthesis, got ${tokens[current].type} instead.");
968
1035
  return expression;
969
1036
  }
970
1037
  case TOKEN_TYPES.OpenSquareBracket: {
971
- ++current;
972
1038
  const values = [];
973
1039
  while (!is(TOKEN_TYPES.CloseSquareBracket)) {
974
1040
  values.push(parseExpression());
@@ -980,7 +1046,6 @@ function parse(tokens) {
980
1046
  return new ArrayLiteral(values);
981
1047
  }
982
1048
  case TOKEN_TYPES.OpenCurlyBracket: {
983
- ++current;
984
1049
  const values = /* @__PURE__ */ new Map();
985
1050
  while (!is(TOKEN_TYPES.CloseCurlyBracket)) {
986
1051
  const key = parseExpression();
@@ -1034,6 +1099,52 @@ function slice(array, start, stop, step = 1) {
1034
1099
  function titleCase(value) {
1035
1100
  return value.replace(/\b\w/g, (c) => c.toUpperCase());
1036
1101
  }
1102
+ function strftime_now(format2) {
1103
+ return strftime(/* @__PURE__ */ new Date(), format2);
1104
+ }
1105
+ function strftime(date, format2) {
1106
+ const monthFormatterLong = new Intl.DateTimeFormat(void 0, { month: "long" });
1107
+ const monthFormatterShort = new Intl.DateTimeFormat(void 0, { month: "short" });
1108
+ const pad2 = (n) => n < 10 ? "0" + n : n.toString();
1109
+ return format2.replace(/%[YmdbBHM%]/g, (token) => {
1110
+ switch (token) {
1111
+ case "%Y":
1112
+ return date.getFullYear().toString();
1113
+ case "%m":
1114
+ return pad2(date.getMonth() + 1);
1115
+ case "%d":
1116
+ return pad2(date.getDate());
1117
+ case "%b":
1118
+ return monthFormatterShort.format(date);
1119
+ case "%B":
1120
+ return monthFormatterLong.format(date);
1121
+ case "%H":
1122
+ return pad2(date.getHours());
1123
+ case "%M":
1124
+ return pad2(date.getMinutes());
1125
+ case "%%":
1126
+ return "%";
1127
+ default:
1128
+ return token;
1129
+ }
1130
+ });
1131
+ }
1132
+ function escapeRegExp(s) {
1133
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1134
+ }
1135
+ function replace(str, oldvalue, newvalue, count) {
1136
+ if (count === 0)
1137
+ return str;
1138
+ let remaining = count == null || count < 0 ? Infinity : count;
1139
+ const pattern = oldvalue.length === 0 ? new RegExp("(?=)", "gu") : new RegExp(escapeRegExp(oldvalue), "gu");
1140
+ return str.replaceAll(pattern, (match) => {
1141
+ if (remaining > 0) {
1142
+ --remaining;
1143
+ return newvalue;
1144
+ }
1145
+ return match;
1146
+ });
1147
+ }
1037
1148
 
1038
1149
  // src/runtime.ts
1039
1150
  var BreakControl = class extends Error {
@@ -1061,9 +1172,18 @@ var RuntimeValue = class {
1061
1172
  __bool__() {
1062
1173
  return new BooleanValue(!!this.value);
1063
1174
  }
1175
+ toString() {
1176
+ return String(this.value);
1177
+ }
1178
+ };
1179
+ var IntegerValue = class extends RuntimeValue {
1180
+ type = "IntegerValue";
1064
1181
  };
1065
- var NumericValue = class extends RuntimeValue {
1066
- type = "NumericValue";
1182
+ var FloatValue = class extends RuntimeValue {
1183
+ type = "FloatValue";
1184
+ toString() {
1185
+ return this.value % 1 === 0 ? this.value.toFixed(1) : this.value.toString();
1186
+ }
1067
1187
  };
1068
1188
  var StringValue = class extends RuntimeValue {
1069
1189
  type = "StringValue";
@@ -1092,7 +1212,13 @@ var StringValue = class extends RuntimeValue {
1092
1212
  return new StringValue(titleCase(this.value));
1093
1213
  })
1094
1214
  ],
1095
- ["length", new NumericValue(this.value.length)],
1215
+ [
1216
+ "capitalize",
1217
+ new FunctionValue(() => {
1218
+ return new StringValue(this.value.charAt(0).toUpperCase() + this.value.slice(1));
1219
+ })
1220
+ ],
1221
+ ["length", new IntegerValue(this.value.length)],
1096
1222
  [
1097
1223
  "rstrip",
1098
1224
  new FunctionValue(() => {
@@ -1111,11 +1237,21 @@ var StringValue = class extends RuntimeValue {
1111
1237
  if (args.length === 0) {
1112
1238
  throw new Error("startswith() requires at least one argument");
1113
1239
  }
1114
- const prefix = args[0];
1115
- if (!(prefix instanceof StringValue)) {
1116
- throw new Error("startswith() argument must be a string");
1240
+ const pattern = args[0];
1241
+ if (pattern instanceof StringValue) {
1242
+ return new BooleanValue(this.value.startsWith(pattern.value));
1243
+ } else if (pattern instanceof ArrayValue) {
1244
+ for (const item of pattern.value) {
1245
+ if (!(item instanceof StringValue)) {
1246
+ throw new Error("startswith() tuple elements must be strings");
1247
+ }
1248
+ if (this.value.startsWith(item.value)) {
1249
+ return new BooleanValue(true);
1250
+ }
1251
+ }
1252
+ return new BooleanValue(false);
1117
1253
  }
1118
- return new BooleanValue(this.value.startsWith(prefix.value));
1254
+ throw new Error("startswith() argument must be a string or tuple of strings");
1119
1255
  })
1120
1256
  ],
1121
1257
  [
@@ -1124,11 +1260,21 @@ var StringValue = class extends RuntimeValue {
1124
1260
  if (args.length === 0) {
1125
1261
  throw new Error("endswith() requires at least one argument");
1126
1262
  }
1127
- const suffix = args[0];
1128
- if (!(suffix instanceof StringValue)) {
1129
- throw new Error("endswith() argument must be a string");
1263
+ const pattern = args[0];
1264
+ if (pattern instanceof StringValue) {
1265
+ return new BooleanValue(this.value.endsWith(pattern.value));
1266
+ } else if (pattern instanceof ArrayValue) {
1267
+ for (const item of pattern.value) {
1268
+ if (!(item instanceof StringValue)) {
1269
+ throw new Error("endswith() tuple elements must be strings");
1270
+ }
1271
+ if (this.value.endsWith(item.value)) {
1272
+ return new BooleanValue(true);
1273
+ }
1274
+ }
1275
+ return new BooleanValue(false);
1130
1276
  }
1131
- return new BooleanValue(this.value.endsWith(suffix.value));
1277
+ throw new Error("endswith() argument must be a string or tuple of strings");
1132
1278
  })
1133
1279
  ],
1134
1280
  [
@@ -1140,8 +1286,8 @@ var StringValue = class extends RuntimeValue {
1140
1286
  if (!(sep instanceof StringValue || sep instanceof NullValue)) {
1141
1287
  throw new Error("sep argument must be a string or null");
1142
1288
  }
1143
- const maxsplit = args[1] ?? new NumericValue(-1);
1144
- if (!(maxsplit instanceof NumericValue)) {
1289
+ const maxsplit = args[1] ?? new IntegerValue(-1);
1290
+ if (!(maxsplit instanceof IntegerValue)) {
1145
1291
  throw new Error("maxsplit argument must be a number");
1146
1292
  }
1147
1293
  let result = [];
@@ -1165,6 +1311,33 @@ var StringValue = class extends RuntimeValue {
1165
1311
  }
1166
1312
  return new ArrayValue(result.map((part) => new StringValue(part)));
1167
1313
  })
1314
+ ],
1315
+ [
1316
+ "replace",
1317
+ new FunctionValue((args) => {
1318
+ if (args.length < 2) {
1319
+ throw new Error("replace() requires at least two arguments");
1320
+ }
1321
+ const oldValue = args[0];
1322
+ const newValue = args[1];
1323
+ if (!(oldValue instanceof StringValue && newValue instanceof StringValue)) {
1324
+ throw new Error("replace() arguments must be strings");
1325
+ }
1326
+ let count;
1327
+ if (args.length > 2) {
1328
+ if (args[2].type === "KeywordArgumentsValue") {
1329
+ count = args[2].value.get("count") ?? new NullValue();
1330
+ } else {
1331
+ count = args[2];
1332
+ }
1333
+ } else {
1334
+ count = new NullValue();
1335
+ }
1336
+ if (!(count instanceof IntegerValue || count instanceof NullValue)) {
1337
+ throw new Error("replace() count argument must be a number or null");
1338
+ }
1339
+ return new StringValue(replace(this.value, oldValue.value, newValue.value, count.value));
1340
+ })
1168
1341
  ]
1169
1342
  ]);
1170
1343
  };
@@ -1194,22 +1367,28 @@ var ObjectValue = class extends RuntimeValue {
1194
1367
  return this.value.get(key.value) ?? defaultValue ?? new NullValue();
1195
1368
  })
1196
1369
  ],
1197
- [
1198
- "items",
1199
- new FunctionValue(() => {
1200
- return new ArrayValue(
1201
- Array.from(this.value.entries()).map(([key, value]) => new ArrayValue([new StringValue(key), value]))
1202
- );
1203
- })
1204
- ]
1370
+ ["items", new FunctionValue(() => this.items())],
1371
+ ["keys", new FunctionValue(() => this.keys())],
1372
+ ["values", new FunctionValue(() => this.values())]
1205
1373
  ]);
1374
+ items() {
1375
+ return new ArrayValue(
1376
+ Array.from(this.value.entries()).map(([key, value]) => new ArrayValue([new StringValue(key), value]))
1377
+ );
1378
+ }
1379
+ keys() {
1380
+ return new ArrayValue(Array.from(this.value.keys()).map((key) => new StringValue(key)));
1381
+ }
1382
+ values() {
1383
+ return new ArrayValue(Array.from(this.value.values()));
1384
+ }
1206
1385
  };
1207
1386
  var KeywordArgumentsValue = class extends ObjectValue {
1208
1387
  type = "KeywordArgumentsValue";
1209
1388
  };
1210
1389
  var ArrayValue = class extends RuntimeValue {
1211
1390
  type = "ArrayValue";
1212
- builtins = /* @__PURE__ */ new Map([["length", new NumericValue(this.value.length)]]);
1391
+ builtins = /* @__PURE__ */ new Map([["length", new IntegerValue(this.value.length)]]);
1213
1392
  /**
1214
1393
  * NOTE: necessary to override since all JavaScript arrays are considered truthy,
1215
1394
  * while only non-empty Python arrays are consider truthy.
@@ -1264,8 +1443,8 @@ var Environment = class {
1264
1443
  [
1265
1444
  "odd",
1266
1445
  (operand) => {
1267
- if (operand.type !== "NumericValue") {
1268
- throw new Error(`Cannot apply test "odd" to type: ${operand.type}`);
1446
+ if (!(operand instanceof IntegerValue)) {
1447
+ throw new Error(`cannot odd on ${operand.type}`);
1269
1448
  }
1270
1449
  return operand.value % 2 !== 0;
1271
1450
  }
@@ -1273,8 +1452,8 @@ var Environment = class {
1273
1452
  [
1274
1453
  "even",
1275
1454
  (operand) => {
1276
- if (operand.type !== "NumericValue") {
1277
- throw new Error(`Cannot apply test "even" to type: ${operand.type}`);
1455
+ if (!(operand instanceof IntegerValue)) {
1456
+ throw new Error(`cannot even on ${operand.type}`);
1278
1457
  }
1279
1458
  return operand.value % 2 === 0;
1280
1459
  }
@@ -1283,8 +1462,8 @@ var Environment = class {
1283
1462
  ["true", (operand) => operand.type === "BooleanValue" && operand.value],
1284
1463
  ["none", (operand) => operand.type === "NullValue"],
1285
1464
  ["string", (operand) => operand.type === "StringValue"],
1286
- ["number", (operand) => operand.type === "NumericValue"],
1287
- ["integer", (operand) => operand.type === "NumericValue" && Number.isInteger(operand.value)],
1465
+ ["number", (operand) => operand instanceof IntegerValue || operand instanceof FloatValue],
1466
+ ["integer", (operand) => operand instanceof IntegerValue],
1288
1467
  ["iterable", (operand) => operand.type === "ArrayValue" || operand.type === "StringValue"],
1289
1468
  ["mapping", (operand) => operand.type === "ObjectValue"],
1290
1469
  [
@@ -1355,6 +1534,19 @@ var Environment = class {
1355
1534
  }
1356
1535
  }
1357
1536
  };
1537
+ function setupGlobals(env) {
1538
+ env.set("false", false);
1539
+ env.set("true", true);
1540
+ env.set("none", null);
1541
+ env.set("raise_exception", (args) => {
1542
+ throw new Error(args);
1543
+ });
1544
+ env.set("range", range);
1545
+ env.set("strftime_now", strftime_now);
1546
+ env.set("True", true);
1547
+ env.set("False", false);
1548
+ env.set("None", null);
1549
+ }
1358
1550
  var Interpreter = class {
1359
1551
  global;
1360
1552
  constructor(env) {
@@ -1385,29 +1577,39 @@ var Interpreter = class {
1385
1577
  return new BooleanValue(left.value != right.value);
1386
1578
  }
1387
1579
  if (left instanceof UndefinedValue || right instanceof UndefinedValue) {
1388
- throw new Error("Cannot perform operation on undefined values");
1580
+ if (right instanceof UndefinedValue && ["in", "not in"].includes(node.operator.value)) {
1581
+ return new BooleanValue(node.operator.value === "not in");
1582
+ }
1583
+ throw new Error(`Cannot perform operation ${node.operator.value} on undefined values`);
1389
1584
  } else if (left instanceof NullValue || right instanceof NullValue) {
1390
1585
  throw new Error("Cannot perform operation on null values");
1391
- } else if (left instanceof NumericValue && right instanceof NumericValue) {
1586
+ } else if (node.operator.value === "~") {
1587
+ return new StringValue(left.value.toString() + right.value.toString());
1588
+ } else if ((left instanceof IntegerValue || left instanceof FloatValue) && (right instanceof IntegerValue || right instanceof FloatValue)) {
1589
+ const a = left.value, b = right.value;
1392
1590
  switch (node.operator.value) {
1393
1591
  case "+":
1394
- return new NumericValue(left.value + right.value);
1395
1592
  case "-":
1396
- return new NumericValue(left.value - right.value);
1397
- case "*":
1398
- return new NumericValue(left.value * right.value);
1593
+ case "*": {
1594
+ const res = node.operator.value === "+" ? a + b : node.operator.value === "-" ? a - b : a * b;
1595
+ const isFloat = left instanceof FloatValue || right instanceof FloatValue;
1596
+ return isFloat ? new FloatValue(res) : new IntegerValue(res);
1597
+ }
1399
1598
  case "/":
1400
- return new NumericValue(left.value / right.value);
1401
- case "%":
1402
- return new NumericValue(left.value % right.value);
1599
+ return new FloatValue(a / b);
1600
+ case "%": {
1601
+ const rem = a % b;
1602
+ const isFloat = left instanceof FloatValue || right instanceof FloatValue;
1603
+ return isFloat ? new FloatValue(rem) : new IntegerValue(rem);
1604
+ }
1403
1605
  case "<":
1404
- return new BooleanValue(left.value < right.value);
1606
+ return new BooleanValue(a < b);
1405
1607
  case ">":
1406
- return new BooleanValue(left.value > right.value);
1608
+ return new BooleanValue(a > b);
1407
1609
  case ">=":
1408
- return new BooleanValue(left.value >= right.value);
1610
+ return new BooleanValue(a >= b);
1409
1611
  case "<=":
1410
- return new BooleanValue(left.value <= right.value);
1612
+ return new BooleanValue(a <= b);
1411
1613
  }
1412
1614
  } else if (left instanceof ArrayValue && right instanceof ArrayValue) {
1413
1615
  switch (node.operator.value) {
@@ -1451,7 +1653,16 @@ var Interpreter = class {
1451
1653
  const positionalArguments = [];
1452
1654
  const keywordArguments = /* @__PURE__ */ new Map();
1453
1655
  for (const argument of args) {
1454
- if (argument.type === "KeywordArgumentExpression") {
1656
+ if (argument.type === "SpreadExpression") {
1657
+ const spreadNode = argument;
1658
+ const val = this.evaluate(spreadNode.argument, environment);
1659
+ if (!(val instanceof ArrayValue)) {
1660
+ throw new Error(`Cannot unpack non-iterable type: ${val.type}`);
1661
+ }
1662
+ for (const item of val.value) {
1663
+ positionalArguments.push(item);
1664
+ }
1665
+ } else if (argument.type === "KeywordArgumentExpression") {
1455
1666
  const kwarg = argument;
1456
1667
  keywordArguments.set(kwarg.key.value, this.evaluate(kwarg.value, environment));
1457
1668
  } else {
@@ -1463,13 +1674,9 @@ var Interpreter = class {
1463
1674
  }
1464
1675
  return [positionalArguments, keywordArguments];
1465
1676
  }
1466
- /**
1467
- * Evaluates expressions following the filter operation type.
1468
- */
1469
- evaluateFilterExpression(node, environment) {
1470
- const operand = this.evaluate(node.operand, environment);
1471
- if (node.filter.type === "Identifier") {
1472
- const filter = node.filter;
1677
+ applyFilter(operand, filterNode, environment) {
1678
+ if (filterNode.type === "Identifier") {
1679
+ const filter = filterNode;
1473
1680
  if (filter.value === "tojson") {
1474
1681
  return new StringValue(toJSON(operand));
1475
1682
  }
@@ -1482,7 +1689,7 @@ var Interpreter = class {
1482
1689
  case "last":
1483
1690
  return operand.value[operand.value.length - 1];
1484
1691
  case "length":
1485
- return new NumericValue(operand.value.length);
1692
+ return new IntegerValue(operand.value.length);
1486
1693
  case "reverse":
1487
1694
  return new ArrayValue(operand.value.reverse());
1488
1695
  case "sort":
@@ -1492,7 +1699,8 @@ var Interpreter = class {
1492
1699
  throw new Error(`Cannot compare different types: ${a.type} and ${b.type}`);
1493
1700
  }
1494
1701
  switch (a.type) {
1495
- case "NumericValue":
1702
+ case "IntegerValue":
1703
+ case "FloatValue":
1496
1704
  return a.value - b.value;
1497
1705
  case "StringValue":
1498
1706
  return a.value.localeCompare(b.value);
@@ -1505,21 +1713,40 @@ var Interpreter = class {
1505
1713
  return new StringValue(operand.value.map((x) => x.value).join(""));
1506
1714
  case "string":
1507
1715
  return new StringValue(toJSON(operand));
1716
+ case "unique": {
1717
+ const seen = /* @__PURE__ */ new Set();
1718
+ const output = [];
1719
+ for (const item of operand.value) {
1720
+ if (!seen.has(item.value)) {
1721
+ seen.add(item.value);
1722
+ output.push(item);
1723
+ }
1724
+ }
1725
+ return new ArrayValue(output);
1726
+ }
1508
1727
  default:
1509
1728
  throw new Error(`Unknown ArrayValue filter: ${filter.value}`);
1510
1729
  }
1511
1730
  } else if (operand instanceof StringValue) {
1512
1731
  switch (filter.value) {
1513
1732
  case "length":
1514
- return new NumericValue(operand.value.length);
1515
1733
  case "upper":
1516
- return new StringValue(operand.value.toUpperCase());
1517
1734
  case "lower":
1518
- return new StringValue(operand.value.toLowerCase());
1519
1735
  case "title":
1520
- return new StringValue(titleCase(operand.value));
1521
- case "capitalize":
1522
- return new StringValue(operand.value.charAt(0).toUpperCase() + operand.value.slice(1));
1736
+ case "capitalize": {
1737
+ const builtin = operand.builtins.get(filter.value);
1738
+ if (builtin instanceof FunctionValue) {
1739
+ return builtin.value(
1740
+ /* no arguments */
1741
+ [],
1742
+ environment
1743
+ );
1744
+ } else if (builtin instanceof IntegerValue) {
1745
+ return builtin;
1746
+ } else {
1747
+ throw new Error(`Unknown StringValue filter: ${filter.value}`);
1748
+ }
1749
+ }
1523
1750
  case "trim":
1524
1751
  return new StringValue(operand.value.trim());
1525
1752
  case "indent":
@@ -1534,13 +1761,25 @@ var Interpreter = class {
1534
1761
  case "join":
1535
1762
  case "string":
1536
1763
  return operand;
1764
+ case "int": {
1765
+ const val = parseInt(operand.value, 10);
1766
+ return new IntegerValue(isNaN(val) ? 0 : val);
1767
+ }
1768
+ case "float": {
1769
+ const val = parseFloat(operand.value);
1770
+ return new FloatValue(isNaN(val) ? 0 : val);
1771
+ }
1537
1772
  default:
1538
1773
  throw new Error(`Unknown StringValue filter: ${filter.value}`);
1539
1774
  }
1540
- } else if (operand instanceof NumericValue) {
1775
+ } else if (operand instanceof IntegerValue || operand instanceof FloatValue) {
1541
1776
  switch (filter.value) {
1542
1777
  case "abs":
1543
- return new NumericValue(Math.abs(operand.value));
1778
+ return operand instanceof IntegerValue ? new IntegerValue(Math.abs(operand.value)) : new FloatValue(Math.abs(operand.value));
1779
+ case "int":
1780
+ return new IntegerValue(Math.floor(operand.value));
1781
+ case "float":
1782
+ return new FloatValue(operand.value);
1544
1783
  default:
1545
1784
  throw new Error(`Unknown NumericValue filter: ${filter.value}`);
1546
1785
  }
@@ -1551,14 +1790,27 @@ var Interpreter = class {
1551
1790
  Array.from(operand.value.entries()).map(([key, value]) => new ArrayValue([new StringValue(key), value]))
1552
1791
  );
1553
1792
  case "length":
1554
- return new NumericValue(operand.value.size);
1793
+ return new IntegerValue(operand.value.size);
1555
1794
  default:
1556
1795
  throw new Error(`Unknown ObjectValue filter: ${filter.value}`);
1557
1796
  }
1797
+ } else if (operand instanceof BooleanValue) {
1798
+ switch (filter.value) {
1799
+ case "bool":
1800
+ return new BooleanValue(operand.value);
1801
+ case "int":
1802
+ return new IntegerValue(operand.value ? 1 : 0);
1803
+ case "float":
1804
+ return new FloatValue(operand.value ? 1 : 0);
1805
+ case "string":
1806
+ return new StringValue(operand.value ? "true" : "false");
1807
+ default:
1808
+ throw new Error(`Unknown BooleanValue filter: ${filter.value}`);
1809
+ }
1558
1810
  }
1559
1811
  throw new Error(`Cannot apply filter "${filter.value}" to type: ${operand.type}`);
1560
- } else if (node.filter.type === "CallExpression") {
1561
- const filter = node.filter;
1812
+ } else if (filterNode.type === "CallExpression") {
1813
+ const filter = filterNode;
1562
1814
  if (filter.callee.type !== "Identifier") {
1563
1815
  throw new Error(`Unknown filter: ${filter.callee.type}`);
1564
1816
  }
@@ -1566,7 +1818,7 @@ var Interpreter = class {
1566
1818
  if (filterName === "tojson") {
1567
1819
  const [, kwargs] = this.evaluateArguments(filter.args, environment);
1568
1820
  const indent = kwargs.get("indent") ?? new NullValue();
1569
- if (!(indent instanceof NumericValue || indent instanceof NullValue)) {
1821
+ if (!(indent instanceof IntegerValue || indent instanceof NullValue)) {
1570
1822
  throw new Error("If set, indent must be a number");
1571
1823
  }
1572
1824
  return new StringValue(toJSON(operand, indent.value));
@@ -1585,6 +1837,30 @@ var Interpreter = class {
1585
1837
  throw new Error("separator must be a string");
1586
1838
  }
1587
1839
  return new StringValue(value.join(separator.value));
1840
+ } else if (filterName === "int" || filterName === "float") {
1841
+ const [args, kwargs] = this.evaluateArguments(filter.args, environment);
1842
+ const defaultValue = args.at(0) ?? kwargs.get("default") ?? (filterName === "int" ? new IntegerValue(0) : new FloatValue(0));
1843
+ if (operand instanceof StringValue) {
1844
+ const val = filterName === "int" ? parseInt(operand.value, 10) : parseFloat(operand.value);
1845
+ return isNaN(val) ? defaultValue : filterName === "int" ? new IntegerValue(val) : new FloatValue(val);
1846
+ } else if (operand instanceof IntegerValue || operand instanceof FloatValue) {
1847
+ return operand;
1848
+ } else if (operand instanceof BooleanValue) {
1849
+ return filterName === "int" ? new IntegerValue(operand.value ? 1 : 0) : new FloatValue(operand.value ? 1 : 0);
1850
+ } else {
1851
+ throw new Error(`Cannot apply filter "${filterName}" to type: ${operand.type}`);
1852
+ }
1853
+ } else if (filterName === "default") {
1854
+ const [args, kwargs] = this.evaluateArguments(filter.args, environment);
1855
+ const defaultValue = args[0] ?? new StringValue("");
1856
+ const booleanValue = args[1] ?? kwargs.get("boolean") ?? new BooleanValue(false);
1857
+ if (!(booleanValue instanceof BooleanValue)) {
1858
+ throw new Error("`default` filter flag must be a boolean");
1859
+ }
1860
+ if (operand instanceof UndefinedValue || booleanValue.value && !operand.__bool__().value) {
1861
+ return defaultValue;
1862
+ }
1863
+ return operand;
1588
1864
  }
1589
1865
  if (operand instanceof ArrayValue) {
1590
1866
  switch (filterName) {
@@ -1640,8 +1916,8 @@ var Interpreter = class {
1640
1916
  switch (filterName) {
1641
1917
  case "indent": {
1642
1918
  const [args, kwargs] = this.evaluateArguments(filter.args, environment);
1643
- const width = args.at(0) ?? kwargs.get("width") ?? new NumericValue(4);
1644
- if (!(width instanceof NumericValue)) {
1919
+ const width = args.at(0) ?? kwargs.get("width") ?? new IntegerValue(4);
1920
+ if (!(width instanceof IntegerValue)) {
1645
1921
  throw new Error("width must be a number");
1646
1922
  }
1647
1923
  const first = args.at(1) ?? kwargs.get("first") ?? new BooleanValue(false);
@@ -1653,13 +1929,28 @@ var Interpreter = class {
1653
1929
  );
1654
1930
  return new StringValue(indented.join("\n"));
1655
1931
  }
1932
+ case "replace": {
1933
+ const replaceFn = operand.builtins.get("replace");
1934
+ if (!(replaceFn instanceof FunctionValue)) {
1935
+ throw new Error("replace filter not available");
1936
+ }
1937
+ const [args, kwargs] = this.evaluateArguments(filter.args, environment);
1938
+ return replaceFn.value([...args, new KeywordArgumentsValue(kwargs)], environment);
1939
+ }
1656
1940
  }
1657
1941
  throw new Error(`Unknown StringValue filter: ${filterName}`);
1658
1942
  } else {
1659
1943
  throw new Error(`Cannot apply filter "${filterName}" to type: ${operand.type}`);
1660
1944
  }
1661
1945
  }
1662
- throw new Error(`Unknown filter: ${node.filter.type}`);
1946
+ throw new Error(`Unknown filter: ${filterNode.type}`);
1947
+ }
1948
+ /**
1949
+ * Evaluates expressions following the filter operation type.
1950
+ */
1951
+ evaluateFilterExpression(node, environment) {
1952
+ const operand = this.evaluate(node.operand, environment);
1953
+ return this.applyFilter(operand, node.filter, environment);
1663
1954
  }
1664
1955
  /**
1665
1956
  * Evaluates expressions following the test operation type.
@@ -1673,6 +1964,16 @@ var Interpreter = class {
1673
1964
  const result = test(operand);
1674
1965
  return new BooleanValue(node.negate ? !result : result);
1675
1966
  }
1967
+ /**
1968
+ * Evaluates expressions following the select operation type.
1969
+ */
1970
+ evaluateSelectExpression(node, environment) {
1971
+ const predicate = this.evaluate(node.test, environment);
1972
+ if (!predicate.__bool__().value) {
1973
+ return new UndefinedValue();
1974
+ }
1975
+ return this.evaluate(node.lhs, environment);
1976
+ }
1676
1977
  /**
1677
1978
  * Evaluates expressions following the unary operation type.
1678
1979
  */
@@ -1685,6 +1986,10 @@ var Interpreter = class {
1685
1986
  throw new SyntaxError(`Unknown operator: ${node.operator.value}`);
1686
1987
  }
1687
1988
  }
1989
+ evaluateTernaryExpression(node, environment) {
1990
+ const cond = this.evaluate(node.condition, environment);
1991
+ return cond.__bool__().value ? this.evaluate(node.trueExpr, environment) : this.evaluate(node.falseExpr, environment);
1992
+ }
1688
1993
  evalProgram(program, environment) {
1689
1994
  return this.evaluateBlock(program.body, environment);
1690
1995
  }
@@ -1693,7 +1998,7 @@ var Interpreter = class {
1693
1998
  for (const statement of statements) {
1694
1999
  const lastEvaluated = this.evaluate(statement, environment);
1695
2000
  if (lastEvaluated.type !== "NullValue" && lastEvaluated.type !== "UndefinedValue") {
1696
- result += lastEvaluated.value;
2001
+ result += lastEvaluated.toString();
1697
2002
  }
1698
2003
  }
1699
2004
  return new StringValue(result);
@@ -1719,13 +2024,13 @@ var Interpreter = class {
1719
2024
  const start = this.evaluate(expr.start, environment);
1720
2025
  const stop = this.evaluate(expr.stop, environment);
1721
2026
  const step = this.evaluate(expr.step, environment);
1722
- if (!(start instanceof NumericValue || start instanceof UndefinedValue)) {
2027
+ if (!(start instanceof IntegerValue || start instanceof UndefinedValue)) {
1723
2028
  throw new Error("Slice start must be numeric or undefined");
1724
2029
  }
1725
- if (!(stop instanceof NumericValue || stop instanceof UndefinedValue)) {
2030
+ if (!(stop instanceof IntegerValue || stop instanceof UndefinedValue)) {
1726
2031
  throw new Error("Slice stop must be numeric or undefined");
1727
2032
  }
1728
- if (!(step instanceof NumericValue || step instanceof UndefinedValue)) {
2033
+ if (!(step instanceof IntegerValue || step instanceof UndefinedValue)) {
1729
2034
  throw new Error("Slice step must be numeric or undefined");
1730
2035
  }
1731
2036
  if (object instanceof ArrayValue) {
@@ -1753,7 +2058,7 @@ var Interpreter = class {
1753
2058
  }
1754
2059
  value = object.value.get(property.value) ?? object.builtins.get(property.value);
1755
2060
  } else if (object instanceof ArrayValue || object instanceof StringValue) {
1756
- if (property instanceof NumericValue) {
2061
+ if (property instanceof IntegerValue) {
1757
2062
  value = object.value.at(property.value);
1758
2063
  if (object instanceof StringValue) {
1759
2064
  value = new StringValue(object.value.at(property.value));
@@ -1776,6 +2081,22 @@ var Interpreter = class {
1776
2081
  if (node.assignee.type === "Identifier") {
1777
2082
  const variableName = node.assignee.value;
1778
2083
  environment.setVariable(variableName, rhs);
2084
+ } else if (node.assignee.type === "TupleLiteral") {
2085
+ const tuple = node.assignee;
2086
+ if (!(rhs instanceof ArrayValue)) {
2087
+ throw new Error(`Cannot unpack non-iterable type in set: ${rhs.type}`);
2088
+ }
2089
+ const arr = rhs.value;
2090
+ if (arr.length !== tuple.value.length) {
2091
+ throw new Error(`Too ${tuple.value.length > arr.length ? "few" : "many"} items to unpack in set`);
2092
+ }
2093
+ for (let i = 0; i < tuple.value.length; ++i) {
2094
+ const elem = tuple.value[i];
2095
+ if (elem.type !== "Identifier") {
2096
+ throw new Error(`Cannot unpack to non-identifier in set: ${elem.type}`);
2097
+ }
2098
+ environment.setVariable(elem.value, arr[i]);
2099
+ }
1779
2100
  } else if (node.assignee.type === "MemberExpression") {
1780
2101
  const member = node.assignee;
1781
2102
  const object = this.evaluate(member.object, environment);
@@ -1800,13 +2121,16 @@ var Interpreter = class {
1800
2121
  let test, iterable;
1801
2122
  if (node.iterable.type === "SelectExpression") {
1802
2123
  const select = node.iterable;
1803
- iterable = this.evaluate(select.iterable, scope);
2124
+ iterable = this.evaluate(select.lhs, scope);
1804
2125
  test = select.test;
1805
2126
  } else {
1806
2127
  iterable = this.evaluate(node.iterable, scope);
1807
2128
  }
1808
- if (!(iterable instanceof ArrayValue)) {
1809
- throw new Error(`Expected iterable type in for loop: got ${iterable.type}`);
2129
+ if (!(iterable instanceof ArrayValue || iterable instanceof ObjectValue)) {
2130
+ throw new Error(`Expected iterable or object type in for loop: got ${iterable.type}`);
2131
+ }
2132
+ if (iterable instanceof ObjectValue) {
2133
+ iterable = iterable.keys();
1810
2134
  }
1811
2135
  const items = [];
1812
2136
  const scopeUpdateFunctions = [];
@@ -1850,13 +2174,13 @@ var Interpreter = class {
1850
2174
  let noIteration = true;
1851
2175
  for (let i = 0; i < items.length; ++i) {
1852
2176
  const loop = /* @__PURE__ */ new Map([
1853
- ["index", new NumericValue(i + 1)],
1854
- ["index0", new NumericValue(i)],
1855
- ["revindex", new NumericValue(items.length - i)],
1856
- ["revindex0", new NumericValue(items.length - i - 1)],
2177
+ ["index", new IntegerValue(i + 1)],
2178
+ ["index0", new IntegerValue(i)],
2179
+ ["revindex", new IntegerValue(items.length - i)],
2180
+ ["revindex0", new IntegerValue(items.length - i - 1)],
1857
2181
  ["first", new BooleanValue(i === 0)],
1858
2182
  ["last", new BooleanValue(i === items.length - 1)],
1859
- ["length", new NumericValue(items.length)],
2183
+ ["length", new IntegerValue(items.length)],
1860
2184
  ["previtem", i > 0 ? items[i - 1] : new UndefinedValue()],
1861
2185
  ["nextitem", i < items.length - 1 ? items[i + 1] : new UndefinedValue()]
1862
2186
  ]);
@@ -1919,8 +2243,36 @@ var Interpreter = class {
1919
2243
  );
1920
2244
  return new NullValue();
1921
2245
  }
2246
+ evaluateCallStatement(node, environment) {
2247
+ const callerFn = new FunctionValue((callerArgs, callerEnv) => {
2248
+ const callBlockEnv = new Environment(callerEnv);
2249
+ if (node.callerArgs) {
2250
+ for (let i = 0; i < node.callerArgs.length; ++i) {
2251
+ const param = node.callerArgs[i];
2252
+ if (param.type !== "Identifier") {
2253
+ throw new Error(`Caller parameter must be an identifier, got ${param.type}`);
2254
+ }
2255
+ callBlockEnv.setVariable(param.value, callerArgs[i] ?? new UndefinedValue());
2256
+ }
2257
+ }
2258
+ return this.evaluateBlock(node.body, callBlockEnv);
2259
+ });
2260
+ const [macroArgs, macroKwargs] = this.evaluateArguments(node.call.args, environment);
2261
+ macroArgs.push(new KeywordArgumentsValue(macroKwargs));
2262
+ const fn = this.evaluate(node.call.callee, environment);
2263
+ if (fn.type !== "FunctionValue") {
2264
+ throw new Error(`Cannot call something that is not a function: got ${fn.type}`);
2265
+ }
2266
+ const newEnv = new Environment(environment);
2267
+ newEnv.setVariable("caller", callerFn);
2268
+ return fn.value(macroArgs, newEnv);
2269
+ }
2270
+ evaluateFilterStatement(node, environment) {
2271
+ const rendered = this.evaluateBlock(node.body, environment);
2272
+ return this.applyFilter(rendered, node.filter, environment);
2273
+ }
1922
2274
  evaluate(statement, environment) {
1923
- if (statement === void 0)
2275
+ if (!statement)
1924
2276
  return new UndefinedValue();
1925
2277
  switch (statement.type) {
1926
2278
  case "Program":
@@ -1933,18 +2285,18 @@ var Interpreter = class {
1933
2285
  return this.evaluateFor(statement, environment);
1934
2286
  case "Macro":
1935
2287
  return this.evaluateMacro(statement, environment);
2288
+ case "CallStatement":
2289
+ return this.evaluateCallStatement(statement, environment);
1936
2290
  case "Break":
1937
2291
  throw new BreakControl();
1938
2292
  case "Continue":
1939
2293
  throw new ContinueControl();
1940
- case "NumericLiteral":
1941
- return new NumericValue(Number(statement.value));
2294
+ case "IntegerLiteral":
2295
+ return new IntegerValue(statement.value);
2296
+ case "FloatLiteral":
2297
+ return new FloatValue(statement.value);
1942
2298
  case "StringLiteral":
1943
2299
  return new StringValue(statement.value);
1944
- case "BooleanLiteral":
1945
- return new BooleanValue(statement.value);
1946
- case "NullLiteral":
1947
- return new NullValue(statement.value);
1948
2300
  case "ArrayLiteral":
1949
2301
  return new ArrayValue(statement.value.map((x) => this.evaluate(x, environment)));
1950
2302
  case "TupleLiteral":
@@ -1972,8 +2324,16 @@ var Interpreter = class {
1972
2324
  return this.evaluateBinaryExpression(statement, environment);
1973
2325
  case "FilterExpression":
1974
2326
  return this.evaluateFilterExpression(statement, environment);
2327
+ case "FilterStatement":
2328
+ return this.evaluateFilterStatement(statement, environment);
1975
2329
  case "TestExpression":
1976
2330
  return this.evaluateTestExpression(statement, environment);
2331
+ case "SelectExpression":
2332
+ return this.evaluateSelectExpression(statement, environment);
2333
+ case "Ternary":
2334
+ return this.evaluateTernaryExpression(statement, environment);
2335
+ case "Comment":
2336
+ return new NullValue();
1977
2337
  default:
1978
2338
  throw new SyntaxError(`Unknown node type: ${statement.type}`);
1979
2339
  }
@@ -1982,7 +2342,7 @@ var Interpreter = class {
1982
2342
  function convertToRuntimeValues(input) {
1983
2343
  switch (typeof input) {
1984
2344
  case "number":
1985
- return new NumericValue(input);
2345
+ return Number.isInteger(input) ? new IntegerValue(input) : new FloatValue(input);
1986
2346
  case "string":
1987
2347
  return new StringValue(input);
1988
2348
  case "boolean":
@@ -2014,7 +2374,8 @@ function toJSON(input, indent, depth) {
2014
2374
  case "NullValue":
2015
2375
  case "UndefinedValue":
2016
2376
  return "null";
2017
- case "NumericValue":
2377
+ case "IntegerValue":
2378
+ case "FloatValue":
2018
2379
  case "StringValue":
2019
2380
  case "BooleanValue":
2020
2381
  return JSON.stringify(input.value);
@@ -2043,11 +2404,23 @@ function toJSON(input, indent, depth) {
2043
2404
  var NEWLINE = "\n";
2044
2405
  var OPEN_STATEMENT = "{%- ";
2045
2406
  var CLOSE_STATEMENT = " -%}";
2046
- var OPERATOR_PRECEDENCE = {
2047
- MultiplicativeBinaryOperator: 2,
2048
- AdditiveBinaryOperator: 1,
2049
- ComparisonBinaryOperator: 0
2050
- };
2407
+ function getBinaryOperatorPrecedence(expr) {
2408
+ switch (expr.operator.type) {
2409
+ case "MultiplicativeBinaryOperator":
2410
+ return 4;
2411
+ case "AdditiveBinaryOperator":
2412
+ return 3;
2413
+ case "ComparisonBinaryOperator":
2414
+ return 2;
2415
+ case "Identifier":
2416
+ if (expr.operator.value === "and")
2417
+ return 1;
2418
+ if (expr.operator.value === "in" || expr.operator.value === "not in")
2419
+ return 2;
2420
+ return 0;
2421
+ }
2422
+ return 0;
2423
+ }
2051
2424
  function format(program, indent = " ") {
2052
2425
  const indentStr = typeof indent === "number" ? " ".repeat(indent) : indent;
2053
2426
  const body = formatStatements(program.body, 0, indentStr);
@@ -2076,6 +2449,12 @@ function formatStatement(node, depth, indentStr) {
2076
2449
  return pad + createStatement("break");
2077
2450
  case "Continue":
2078
2451
  return pad + createStatement("continue");
2452
+ case "CallStatement":
2453
+ return formatCallStatement(node, depth, indentStr);
2454
+ case "FilterStatement":
2455
+ return formatFilterStatement(node, depth, indentStr);
2456
+ case "Comment":
2457
+ return pad + "{# " + node.value + " #}";
2079
2458
  default:
2080
2459
  return pad + "{{- " + formatExpression(node) + " -}}";
2081
2460
  }
@@ -2093,7 +2472,7 @@ function formatIf(node, depth, indentStr) {
2093
2472
  }
2094
2473
  }
2095
2474
  let out = pad + createStatement("if", formatExpression(clauses[0].test)) + NEWLINE + formatStatements(clauses[0].body, depth + 1, indentStr);
2096
- for (let i = 1; i < clauses.length; i++) {
2475
+ for (let i = 1; i < clauses.length; ++i) {
2097
2476
  out += NEWLINE + pad + createStatement("elif", formatExpression(clauses[i].test)) + NEWLINE + formatStatements(clauses[i].body, depth + 1, indentStr);
2098
2477
  }
2099
2478
  if (current && current.alternate.length > 0) {
@@ -2107,7 +2486,7 @@ function formatFor(node, depth, indentStr) {
2107
2486
  let formattedIterable = "";
2108
2487
  if (node.iterable.type === "SelectExpression") {
2109
2488
  const n = node.iterable;
2110
- formattedIterable = `${formatExpression(n.iterable)} if ${formatExpression(n.test)}`;
2489
+ formattedIterable = `${formatExpression(n.lhs)} if ${formatExpression(n.test)}`;
2111
2490
  } else {
2112
2491
  formattedIterable = formatExpression(node.iterable);
2113
2492
  }
@@ -2133,20 +2512,40 @@ function formatMacro(node, depth, indentStr) {
2133
2512
  const args = node.args.map(formatExpression).join(", ");
2134
2513
  return pad + createStatement("macro", `${node.name.value}(${args})`) + NEWLINE + formatStatements(node.body, depth + 1, indentStr) + NEWLINE + pad + createStatement("endmacro");
2135
2514
  }
2515
+ function formatCallStatement(node, depth, indentStr) {
2516
+ const pad = indentStr.repeat(depth);
2517
+ const params = node.callerArgs && node.callerArgs.length > 0 ? `(${node.callerArgs.map(formatExpression).join(", ")})` : "";
2518
+ const callExpr = formatExpression(node.call);
2519
+ let out = pad + createStatement(`call${params}`, callExpr) + NEWLINE;
2520
+ out += formatStatements(node.body, depth + 1, indentStr) + NEWLINE;
2521
+ out += pad + createStatement("endcall");
2522
+ return out;
2523
+ }
2524
+ function formatFilterStatement(node, depth, indentStr) {
2525
+ const pad = indentStr.repeat(depth);
2526
+ const spec = node.filter.type === "Identifier" ? node.filter.value : formatExpression(node.filter);
2527
+ let out = pad + createStatement("filter", spec) + NEWLINE;
2528
+ out += formatStatements(node.body, depth + 1, indentStr) + NEWLINE;
2529
+ out += pad + createStatement("endfilter");
2530
+ return out;
2531
+ }
2136
2532
  function formatExpression(node, parentPrec = -1) {
2137
2533
  switch (node.type) {
2534
+ case "SpreadExpression": {
2535
+ const n = node;
2536
+ return `*${formatExpression(n.argument)}`;
2537
+ }
2138
2538
  case "Identifier":
2139
2539
  return node.value;
2140
- case "NullLiteral":
2141
- return "none";
2142
- case "NumericLiteral":
2143
- case "BooleanLiteral":
2540
+ case "IntegerLiteral":
2541
+ return `${node.value}`;
2542
+ case "FloatLiteral":
2144
2543
  return `${node.value}`;
2145
2544
  case "StringLiteral":
2146
2545
  return JSON.stringify(node.value);
2147
2546
  case "BinaryExpression": {
2148
2547
  const n = node;
2149
- const thisPrecedence = OPERATOR_PRECEDENCE[n.operator.type] ?? 0;
2548
+ const thisPrecedence = getBinaryOperatorPrecedence(n);
2150
2549
  const left = formatExpression(n.left, thisPrecedence);
2151
2550
  const right = formatExpression(n.right, thisPrecedence + 1);
2152
2551
  const expr = `${left} ${n.operator.value} ${right}`;
@@ -2157,20 +2556,28 @@ function formatExpression(node, parentPrec = -1) {
2157
2556
  const val = n.operator.value + (n.operator.value === "not" ? " " : "") + formatExpression(n.argument, Infinity);
2158
2557
  return val;
2159
2558
  }
2160
- case "LogicalNegationExpression":
2161
- return `not ${formatExpression(node.argument, Infinity)}`;
2162
2559
  case "CallExpression": {
2163
2560
  const n = node;
2164
- const args = n.args.map((a) => formatExpression(a, -1)).join(", ");
2165
- return `${formatExpression(n.callee, -1)}(${args})`;
2561
+ const args = n.args.map(formatExpression).join(", ");
2562
+ return `${formatExpression(n.callee)}(${args})`;
2166
2563
  }
2167
2564
  case "MemberExpression": {
2168
2565
  const n = node;
2169
- let obj = formatExpression(n.object, -1);
2170
- if (n.object.type !== "Identifier") {
2566
+ let obj = formatExpression(n.object);
2567
+ if (![
2568
+ "Identifier",
2569
+ "MemberExpression",
2570
+ "CallExpression",
2571
+ "StringLiteral",
2572
+ "IntegerLiteral",
2573
+ "FloatLiteral",
2574
+ "ArrayLiteral",
2575
+ "TupleLiteral",
2576
+ "ObjectLiteral"
2577
+ ].includes(n.object.type)) {
2171
2578
  obj = `(${obj})`;
2172
2579
  }
2173
- let prop = formatExpression(n.property, -1);
2580
+ let prop = formatExpression(n.property);
2174
2581
  if (!n.computed && n.property.type !== "Identifier") {
2175
2582
  prop = `(${prop})`;
2176
2583
  }
@@ -2180,47 +2587,47 @@ function formatExpression(node, parentPrec = -1) {
2180
2587
  const n = node;
2181
2588
  const operand = formatExpression(n.operand, Infinity);
2182
2589
  if (n.filter.type === "CallExpression") {
2183
- return `${operand} | ${formatExpression(n.filter, -1)}`;
2590
+ return `${operand} | ${formatExpression(n.filter)}`;
2184
2591
  }
2185
2592
  return `${operand} | ${n.filter.value}`;
2186
2593
  }
2187
2594
  case "SelectExpression": {
2188
2595
  const n = node;
2189
- return `${formatExpression(n.iterable, -1)} | select(${formatExpression(n.test, -1)})`;
2596
+ return `${formatExpression(n.lhs)} if ${formatExpression(n.test)}`;
2190
2597
  }
2191
2598
  case "TestExpression": {
2192
2599
  const n = node;
2193
- return `${formatExpression(n.operand, -1)} is${n.negate ? " not" : ""} ${n.test.value}`;
2600
+ return `${formatExpression(n.operand)} is${n.negate ? " not" : ""} ${n.test.value}`;
2194
2601
  }
2195
2602
  case "ArrayLiteral":
2196
2603
  case "TupleLiteral": {
2197
- const elems = node.value.map((e) => formatExpression(e, -1));
2604
+ const elems = node.value.map(formatExpression);
2198
2605
  const brackets = node.type === "ArrayLiteral" ? "[]" : "()";
2199
2606
  return `${brackets[0]}${elems.join(", ")}${brackets[1]}`;
2200
2607
  }
2201
2608
  case "ObjectLiteral": {
2202
2609
  const entries = Array.from(node.value.entries()).map(
2203
- ([k, v]) => `${formatExpression(k, -1)}: ${formatExpression(v, -1)}`
2610
+ ([k, v]) => `${formatExpression(k)}: ${formatExpression(v)}`
2204
2611
  );
2205
- return `{ ${entries.join(", ")} }`;
2612
+ return `{${entries.join(", ")}}`;
2206
2613
  }
2207
2614
  case "SliceExpression": {
2208
2615
  const n = node;
2209
- const s = n.start ? formatExpression(n.start, -1) : "";
2210
- const t = n.stop ? formatExpression(n.stop, -1) : "";
2211
- const st = n.step ? `:${formatExpression(n.step, -1)}` : "";
2616
+ const s = n.start ? formatExpression(n.start) : "";
2617
+ const t = n.stop ? formatExpression(n.stop) : "";
2618
+ const st = n.step ? `:${formatExpression(n.step)}` : "";
2212
2619
  return `${s}:${t}${st}`;
2213
2620
  }
2214
2621
  case "KeywordArgumentExpression": {
2215
2622
  const n = node;
2216
- return `${n.key.value}=${formatExpression(n.value, -1)}`;
2623
+ return `${n.key.value}=${formatExpression(n.value)}`;
2217
2624
  }
2218
- case "If": {
2625
+ case "Ternary": {
2219
2626
  const n = node;
2220
- const test = formatExpression(n.test, -1);
2221
- const body = formatExpression(n.body[0], 0);
2222
- const alternate = formatExpression(n.alternate[0], -1);
2223
- return `${body} if ${test} else ${alternate}`;
2627
+ const expr = `${formatExpression(n.trueExpr)} if ${formatExpression(n.condition, 0)} else ${formatExpression(
2628
+ n.falseExpr
2629
+ )}`;
2630
+ return parentPrec > -1 ? `(${expr})` : expr;
2224
2631
  }
2225
2632
  default:
2226
2633
  throw new Error(`Unknown expression type: ${node.type}`);
@@ -2242,12 +2649,7 @@ var Template = class {
2242
2649
  }
2243
2650
  render(items) {
2244
2651
  const env = new Environment();
2245
- env.set("false", false);
2246
- env.set("true", true);
2247
- env.set("raise_exception", (args) => {
2248
- throw new Error(args);
2249
- });
2250
- env.set("range", range);
2652
+ setupGlobals(env);
2251
2653
  if (items) {
2252
2654
  for (const [key, value] of Object.entries(items)) {
2253
2655
  env.set(key, value);
@@ -4374,7 +4776,7 @@ __webpack_require__.r(__webpack_exports__);
4374
4776
 
4375
4777
 
4376
4778
 
4377
- const VERSION = '3.6.2';
4779
+ const VERSION = '3.6.3';
4378
4780
 
4379
4781
  // Check if various APIs are available (depends on environment)
4380
4782
  const IS_BROWSER_ENV = typeof window !== "undefined" && typeof window.document !== "undefined";