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