@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.
@@ -97,15 +97,11 @@ var TOKEN_TYPES = Object.freeze({
97
97
  Text: "Text",
98
98
  // The text between Jinja statements or expressions
99
99
  NumericLiteral: "NumericLiteral",
100
- // e.g., 123
101
- BooleanLiteral: "BooleanLiteral",
102
- // true or false
103
- NullLiteral: "NullLiteral",
104
- // none
100
+ // e.g., 123, 1.0
105
101
  StringLiteral: "StringLiteral",
106
102
  // 'string'
107
103
  Identifier: "Identifier",
108
- // Variables, functions, etc.
104
+ // Variables, functions, statements, booleans, etc.
109
105
  Equals: "Equals",
110
106
  // =
111
107
  OpenParen: "OpenParen",
@@ -139,63 +135,15 @@ var TOKEN_TYPES = Object.freeze({
139
135
  CallOperator: "CallOperator",
140
136
  // ()
141
137
  AdditiveBinaryOperator: "AdditiveBinaryOperator",
142
- // + -
138
+ // + - ~
143
139
  MultiplicativeBinaryOperator: "MultiplicativeBinaryOperator",
144
140
  // * / %
145
141
  ComparisonBinaryOperator: "ComparisonBinaryOperator",
146
142
  // < > <= >= == !=
147
143
  UnaryOperator: "UnaryOperator",
148
144
  // ! - +
149
- // Keywords
150
- Set: "Set",
151
- If: "If",
152
- For: "For",
153
- In: "In",
154
- Is: "Is",
155
- NotIn: "NotIn",
156
- Else: "Else",
157
- EndSet: "EndSet",
158
- EndIf: "EndIf",
159
- ElseIf: "ElseIf",
160
- EndFor: "EndFor",
161
- And: "And",
162
- Or: "Or",
163
- Not: "UnaryOperator",
164
- Macro: "Macro",
165
- EndMacro: "EndMacro",
166
- Break: "Break",
167
- Continue: "Continue"
168
- });
169
- var KEYWORDS = Object.freeze({
170
- set: TOKEN_TYPES.Set,
171
- for: TOKEN_TYPES.For,
172
- in: TOKEN_TYPES.In,
173
- is: TOKEN_TYPES.Is,
174
- if: TOKEN_TYPES.If,
175
- else: TOKEN_TYPES.Else,
176
- endset: TOKEN_TYPES.EndSet,
177
- endif: TOKEN_TYPES.EndIf,
178
- elif: TOKEN_TYPES.ElseIf,
179
- endfor: TOKEN_TYPES.EndFor,
180
- and: TOKEN_TYPES.And,
181
- or: TOKEN_TYPES.Or,
182
- not: TOKEN_TYPES.Not,
183
- "not in": TOKEN_TYPES.NotIn,
184
- macro: TOKEN_TYPES.Macro,
185
- endmacro: TOKEN_TYPES.EndMacro,
186
- break: TOKEN_TYPES.Break,
187
- continue: TOKEN_TYPES.Continue,
188
- // Literals
189
- true: TOKEN_TYPES.BooleanLiteral,
190
- false: TOKEN_TYPES.BooleanLiteral,
191
- none: TOKEN_TYPES.NullLiteral,
192
- // NOTE: According to the Jinja docs: The special constants true, false, and none are indeed lowercase.
193
- // Because that caused confusion in the past, (True used to expand to an undefined variable that was considered false),
194
- // all three can now also be written in title case (True, False, and None). However, for consistency, (all Jinja identifiers are lowercase)
195
- // you should use the lowercase versions.
196
- True: TOKEN_TYPES.BooleanLiteral,
197
- False: TOKEN_TYPES.BooleanLiteral,
198
- None: TOKEN_TYPES.NullLiteral
145
+ Comment: "Comment"
146
+ // {# ... #}
199
147
  });
200
148
  var Token = class {
201
149
  /**
@@ -241,6 +189,7 @@ var ORDERED_MAPPING_TABLE = [
241
189
  // Arithmetic operators
242
190
  ["+", TOKEN_TYPES.AdditiveBinaryOperator],
243
191
  ["-", TOKEN_TYPES.AdditiveBinaryOperator],
192
+ ["~", TOKEN_TYPES.AdditiveBinaryOperator],
244
193
  ["*", TOKEN_TYPES.MultiplicativeBinaryOperator],
245
194
  ["/", TOKEN_TYPES.MultiplicativeBinaryOperator],
246
195
  ["%", TOKEN_TYPES.MultiplicativeBinaryOperator],
@@ -271,19 +220,19 @@ function preprocess(template, options = {}) {
271
220
  if (template.endsWith("\n")) {
272
221
  template = template.slice(0, -1);
273
222
  }
274
- template = template.replace(/{#.*?#}/gs, "{##}");
275
223
  if (options.lstrip_blocks) {
276
- template = template.replace(/^[ \t]*({[#%])/gm, "$1");
224
+ template = template.replace(/^[ \t]*({[#%-])/gm, "$1");
277
225
  }
278
226
  if (options.trim_blocks) {
279
- template = template.replace(/([#%]})\n/g, "$1");
227
+ template = template.replace(/([#%-]})\n/g, "$1");
280
228
  }
281
- return template.replace(/{##}/g, "").replace(/-%}\s*/g, "%}").replace(/\s*{%-/g, "{%").replace(/-}}\s*/g, "}}").replace(/\s*{{-/g, "{{");
229
+ 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, "");
282
230
  }
283
231
  function tokenize(source, options = {}) {
284
232
  const tokens = [];
285
233
  const src = preprocess(source, options);
286
234
  let cursorPosition = 0;
235
+ let curlyBracketDepth = 0;
287
236
  const consumeWhile = (predicate) => {
288
237
  let str = "";
289
238
  while (predicate(src[cursorPosition])) {
@@ -308,10 +257,10 @@ function tokenize(source, options = {}) {
308
257
  main:
309
258
  while (cursorPosition < src.length) {
310
259
  const lastTokenType = tokens.at(-1)?.type;
311
- if (lastTokenType === void 0 || lastTokenType === TOKEN_TYPES.CloseStatement || lastTokenType === TOKEN_TYPES.CloseExpression) {
260
+ if (lastTokenType === void 0 || lastTokenType === TOKEN_TYPES.CloseStatement || lastTokenType === TOKEN_TYPES.CloseExpression || lastTokenType === TOKEN_TYPES.Comment) {
312
261
  let text = "";
313
262
  while (cursorPosition < src.length && // Keep going until we hit the next Jinja statement or expression
314
- !(src[cursorPosition] === "{" && (src[cursorPosition + 1] === "%" || src[cursorPosition + 1] === "{"))) {
263
+ !(src[cursorPosition] === "{" && (src[cursorPosition + 1] === "%" || src[cursorPosition + 1] === "{" || src[cursorPosition + 1] === "#"))) {
315
264
  text += src[cursorPosition++];
316
265
  }
317
266
  if (text.length > 0) {
@@ -319,6 +268,19 @@ function tokenize(source, options = {}) {
319
268
  continue;
320
269
  }
321
270
  }
271
+ if (src[cursorPosition] === "{" && src[cursorPosition + 1] === "#") {
272
+ cursorPosition += 2;
273
+ let comment = "";
274
+ while (src[cursorPosition] !== "#" || src[cursorPosition + 1] !== "}") {
275
+ if (cursorPosition + 2 >= src.length) {
276
+ throw new SyntaxError("Missing end of comment tag");
277
+ }
278
+ comment += src[cursorPosition++];
279
+ }
280
+ tokens.push(new Token(comment, TOKEN_TYPES.Comment));
281
+ cursorPosition += 2;
282
+ continue;
283
+ }
322
284
  consumeWhile((char2) => /\s/.test(char2));
323
285
  const char = src[cursorPosition];
324
286
  if (char === "-" || char === "+") {
@@ -329,8 +291,6 @@ function tokenize(source, options = {}) {
329
291
  switch (lastTokenType2) {
330
292
  case TOKEN_TYPES.Identifier:
331
293
  case TOKEN_TYPES.NumericLiteral:
332
- case TOKEN_TYPES.BooleanLiteral:
333
- case TOKEN_TYPES.NullLiteral:
334
294
  case TOKEN_TYPES.StringLiteral:
335
295
  case TOKEN_TYPES.CloseParen:
336
296
  case TOKEN_TYPES.CloseSquareBracket:
@@ -345,11 +305,21 @@ function tokenize(source, options = {}) {
345
305
  }
346
306
  }
347
307
  }
348
- for (const [char2, token] of ORDERED_MAPPING_TABLE) {
349
- const slice2 = src.slice(cursorPosition, cursorPosition + char2.length);
350
- if (slice2 === char2) {
351
- tokens.push(new Token(char2, token));
352
- cursorPosition += char2.length;
308
+ for (const [seq, type] of ORDERED_MAPPING_TABLE) {
309
+ if (seq === "}}" && curlyBracketDepth > 0) {
310
+ continue;
311
+ }
312
+ const slice2 = src.slice(cursorPosition, cursorPosition + seq.length);
313
+ if (slice2 === seq) {
314
+ tokens.push(new Token(seq, type));
315
+ if (type === TOKEN_TYPES.OpenExpression) {
316
+ curlyBracketDepth = 0;
317
+ } else if (type === TOKEN_TYPES.OpenCurlyBracket) {
318
+ ++curlyBracketDepth;
319
+ } else if (type === TOKEN_TYPES.CloseCurlyBracket) {
320
+ --curlyBracketDepth;
321
+ }
322
+ cursorPosition += seq.length;
353
323
  continue main;
354
324
  }
355
325
  }
@@ -361,19 +331,18 @@ function tokenize(source, options = {}) {
361
331
  continue;
362
332
  }
363
333
  if (isInteger(char)) {
364
- const num = consumeWhile(isInteger);
334
+ let num = consumeWhile(isInteger);
335
+ if (src[cursorPosition] === "." && isInteger(src[cursorPosition + 1])) {
336
+ ++cursorPosition;
337
+ const frac = consumeWhile(isInteger);
338
+ num = `${num}.${frac}`;
339
+ }
365
340
  tokens.push(new Token(num, TOKEN_TYPES.NumericLiteral));
366
341
  continue;
367
342
  }
368
343
  if (isWord(char)) {
369
344
  const word = consumeWhile(isWord);
370
- const type = Object.hasOwn(KEYWORDS, word) ? KEYWORDS[word] : TOKEN_TYPES.Identifier;
371
- if (type === TOKEN_TYPES.In && tokens.at(-1)?.type === TOKEN_TYPES.Not) {
372
- tokens.pop();
373
- tokens.push(new Token("not in", TOKEN_TYPES.NotIn));
374
- } else {
375
- tokens.push(new Token(word, type));
376
- }
345
+ tokens.push(new Token(word, TOKEN_TYPES.Identifier));
377
346
  continue;
378
347
  }
379
348
  throw new SyntaxError(`Unexpected character: ${char}`);
@@ -435,6 +404,13 @@ var Macro = class extends Statement {
435
404
  }
436
405
  type = "Macro";
437
406
  };
407
+ var Comment = class extends Statement {
408
+ constructor(value) {
409
+ super();
410
+ this.value = value;
411
+ }
412
+ type = "Comment";
413
+ };
438
414
  var Expression = class extends Statement {
439
415
  type = "Expression";
440
416
  };
@@ -472,18 +448,15 @@ var Literal = class extends Expression {
472
448
  }
473
449
  type = "Literal";
474
450
  };
475
- var NumericLiteral = class extends Literal {
476
- type = "NumericLiteral";
451
+ var IntegerLiteral = class extends Literal {
452
+ type = "IntegerLiteral";
453
+ };
454
+ var FloatLiteral = class extends Literal {
455
+ type = "FloatLiteral";
477
456
  };
478
457
  var StringLiteral = class extends Literal {
479
458
  type = "StringLiteral";
480
459
  };
481
- var BooleanLiteral = class extends Literal {
482
- type = "BooleanLiteral";
483
- };
484
- var NullLiteral = class extends Literal {
485
- type = "NullLiteral";
486
- };
487
460
  var ArrayLiteral = class extends Literal {
488
461
  type = "ArrayLiteral";
489
462
  };
@@ -510,10 +483,18 @@ var FilterExpression = class extends Expression {
510
483
  }
511
484
  type = "FilterExpression";
512
485
  };
486
+ var FilterStatement = class extends Statement {
487
+ constructor(filter, body) {
488
+ super();
489
+ this.filter = filter;
490
+ this.body = body;
491
+ }
492
+ type = "FilterStatement";
493
+ };
513
494
  var SelectExpression = class extends Expression {
514
- constructor(iterable, test) {
495
+ constructor(lhs, test) {
515
496
  super();
516
- this.iterable = iterable;
497
+ this.lhs = lhs;
517
498
  this.test = test;
518
499
  }
519
500
  type = "SelectExpression";
@@ -552,6 +533,31 @@ var KeywordArgumentExpression = class extends Expression {
552
533
  }
553
534
  type = "KeywordArgumentExpression";
554
535
  };
536
+ var SpreadExpression = class extends Expression {
537
+ constructor(argument) {
538
+ super();
539
+ this.argument = argument;
540
+ }
541
+ type = "SpreadExpression";
542
+ };
543
+ var CallStatement = class extends Statement {
544
+ constructor(call, callerArgs, body) {
545
+ super();
546
+ this.call = call;
547
+ this.callerArgs = callerArgs;
548
+ this.body = body;
549
+ }
550
+ type = "CallStatement";
551
+ };
552
+ var Ternary = class extends Expression {
553
+ constructor(condition, trueExpr, falseExpr) {
554
+ super();
555
+ this.condition = condition;
556
+ this.trueExpr = trueExpr;
557
+ this.falseExpr = falseExpr;
558
+ }
559
+ type = "Ternary";
560
+ };
555
561
 
556
562
  // src/parser.ts
557
563
  function parse(tokens) {
@@ -564,8 +570,16 @@ function parse(tokens) {
564
570
  }
565
571
  return prev;
566
572
  }
573
+ function expectIdentifier(name) {
574
+ if (!isIdentifier(name)) {
575
+ throw new SyntaxError(`Expected ${name}`);
576
+ }
577
+ ++current;
578
+ }
567
579
  function parseAny() {
568
580
  switch (tokens[current].type) {
581
+ case TOKEN_TYPES.Comment:
582
+ return new Comment(tokens[current++].value);
569
583
  case TOKEN_TYPES.Text:
570
584
  return parseText();
571
585
  case TOKEN_TYPES.OpenStatement:
@@ -576,57 +590,103 @@ function parse(tokens) {
576
590
  throw new SyntaxError(`Unexpected token type: ${tokens[current].type}`);
577
591
  }
578
592
  }
579
- function not(...types) {
580
- return current + types.length <= tokens.length && types.some((type, i) => type !== tokens[current + i].type);
581
- }
582
593
  function is(...types) {
583
594
  return current + types.length <= tokens.length && types.every((type, i) => type === tokens[current + i].type);
584
595
  }
596
+ function isStatement(...names) {
597
+ return tokens[current]?.type === TOKEN_TYPES.OpenStatement && tokens[current + 1]?.type === TOKEN_TYPES.Identifier && names.includes(tokens[current + 1]?.value);
598
+ }
599
+ function isIdentifier(...names) {
600
+ return current + names.length <= tokens.length && names.every((name, i) => tokens[current + i].type === "Identifier" && name === tokens[current + i].value);
601
+ }
585
602
  function parseText() {
586
603
  return new StringLiteral(expect(TOKEN_TYPES.Text, "Expected text token").value);
587
604
  }
588
605
  function parseJinjaStatement() {
589
606
  expect(TOKEN_TYPES.OpenStatement, "Expected opening statement token");
607
+ if (tokens[current].type !== TOKEN_TYPES.Identifier) {
608
+ throw new SyntaxError(`Unknown statement, got ${tokens[current].type}`);
609
+ }
610
+ const name = tokens[current].value;
590
611
  let result;
591
- switch (tokens[current].type) {
592
- case TOKEN_TYPES.Set:
612
+ switch (name) {
613
+ case "set":
593
614
  ++current;
594
615
  result = parseSetStatement();
595
- expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
596
616
  break;
597
- case TOKEN_TYPES.If:
617
+ case "if":
598
618
  ++current;
599
619
  result = parseIfStatement();
600
620
  expect(TOKEN_TYPES.OpenStatement, "Expected {% token");
601
- expect(TOKEN_TYPES.EndIf, "Expected endif token");
621
+ expectIdentifier("endif");
602
622
  expect(TOKEN_TYPES.CloseStatement, "Expected %} token");
603
623
  break;
604
- case TOKEN_TYPES.Macro:
624
+ case "macro":
605
625
  ++current;
606
626
  result = parseMacroStatement();
607
627
  expect(TOKEN_TYPES.OpenStatement, "Expected {% token");
608
- expect(TOKEN_TYPES.EndMacro, "Expected endmacro token");
628
+ expectIdentifier("endmacro");
609
629
  expect(TOKEN_TYPES.CloseStatement, "Expected %} token");
610
630
  break;
611
- case TOKEN_TYPES.For:
631
+ case "for":
612
632
  ++current;
613
633
  result = parseForStatement();
614
634
  expect(TOKEN_TYPES.OpenStatement, "Expected {% token");
615
- expect(TOKEN_TYPES.EndFor, "Expected endfor token");
635
+ expectIdentifier("endfor");
616
636
  expect(TOKEN_TYPES.CloseStatement, "Expected %} token");
617
637
  break;
618
- case TOKEN_TYPES.Break:
638
+ case "call": {
639
+ ++current;
640
+ let callerArgs = null;
641
+ if (is(TOKEN_TYPES.OpenParen)) {
642
+ callerArgs = parseArgs();
643
+ }
644
+ const callee = parsePrimaryExpression();
645
+ if (callee.type !== "Identifier") {
646
+ throw new SyntaxError(`Expected identifier following call statement`);
647
+ }
648
+ const callArgs = parseArgs();
649
+ expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
650
+ const body = [];
651
+ while (!isStatement("endcall")) {
652
+ body.push(parseAny());
653
+ }
654
+ expect(TOKEN_TYPES.OpenStatement, "Expected '{%'");
655
+ expectIdentifier("endcall");
656
+ expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
657
+ const callExpr = new CallExpression(callee, callArgs);
658
+ result = new CallStatement(callExpr, callerArgs, body);
659
+ break;
660
+ }
661
+ case "break":
619
662
  ++current;
620
663
  expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
621
664
  result = new Break();
622
665
  break;
623
- case TOKEN_TYPES.Continue:
666
+ case "continue":
624
667
  ++current;
625
668
  expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
626
669
  result = new Continue();
627
670
  break;
671
+ case "filter": {
672
+ ++current;
673
+ let filterNode = parsePrimaryExpression();
674
+ if (filterNode instanceof Identifier && is(TOKEN_TYPES.OpenParen)) {
675
+ filterNode = parseCallExpression(filterNode);
676
+ }
677
+ expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
678
+ const filterBody = [];
679
+ while (!isStatement("endfilter")) {
680
+ filterBody.push(parseAny());
681
+ }
682
+ expect(TOKEN_TYPES.OpenStatement, "Expected '{%'");
683
+ expectIdentifier("endfilter");
684
+ expect(TOKEN_TYPES.CloseStatement, "Expected '%}'");
685
+ result = new FilterStatement(filterNode, filterBody);
686
+ break;
687
+ }
628
688
  default:
629
- throw new SyntaxError(`Unknown statement type: ${tokens[current].type}`);
689
+ throw new SyntaxError(`Unknown statement type: ${name}`);
630
690
  }
631
691
  return result;
632
692
  }
@@ -637,42 +697,42 @@ function parse(tokens) {
637
697
  return result;
638
698
  }
639
699
  function parseSetStatement() {
640
- const left = parseExpression();
700
+ const left = parseExpressionSequence();
701
+ let value = null;
702
+ const body = [];
641
703
  if (is(TOKEN_TYPES.Equals)) {
642
704
  ++current;
643
- const value = parseExpression();
644
- return new SetStatement(left, value, []);
705
+ value = parseExpressionSequence();
645
706
  } else {
646
- const body = [];
647
707
  expect(TOKEN_TYPES.CloseStatement, "Expected %} token");
648
- while (!(tokens[current]?.type === TOKEN_TYPES.OpenStatement && tokens[current + 1]?.type === TOKEN_TYPES.EndSet)) {
649
- const another = parseAny();
650
- body.push(another);
708
+ while (!isStatement("endset")) {
709
+ body.push(parseAny());
651
710
  }
652
711
  expect(TOKEN_TYPES.OpenStatement, "Expected {% token");
653
- expect(TOKEN_TYPES.EndSet, "Expected endset token");
654
- return new SetStatement(left, null, body);
712
+ expectIdentifier("endset");
655
713
  }
714
+ expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
715
+ return new SetStatement(left, value, body);
656
716
  }
657
717
  function parseIfStatement() {
658
718
  const test = parseExpression();
659
719
  expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
660
720
  const body = [];
661
721
  const alternate = [];
662
- 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))) {
722
+ while (!isStatement("elif", "else", "endif")) {
663
723
  body.push(parseAny());
664
724
  }
665
- if (tokens[current]?.type === TOKEN_TYPES.OpenStatement && tokens[current + 1]?.type !== TOKEN_TYPES.EndIf) {
725
+ if (isStatement("elif")) {
666
726
  ++current;
667
- if (is(TOKEN_TYPES.ElseIf)) {
668
- expect(TOKEN_TYPES.ElseIf, "Expected elseif token");
669
- alternate.push(parseIfStatement());
670
- } else {
671
- expect(TOKEN_TYPES.Else, "Expected else token");
672
- expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
673
- while (!(tokens[current]?.type === TOKEN_TYPES.OpenStatement && tokens[current + 1]?.type === TOKEN_TYPES.EndIf)) {
674
- alternate.push(parseAny());
675
- }
727
+ ++current;
728
+ const result = parseIfStatement();
729
+ alternate.push(result);
730
+ } else if (isStatement("else")) {
731
+ ++current;
732
+ ++current;
733
+ expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
734
+ while (!isStatement("endif")) {
735
+ alternate.push(parseAny());
676
736
  }
677
737
  }
678
738
  return new If(test, body, alternate);
@@ -685,7 +745,7 @@ function parse(tokens) {
685
745
  const args = parseArgs();
686
746
  expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
687
747
  const body = [];
688
- while (not(TOKEN_TYPES.OpenStatement, TOKEN_TYPES.EndMacro)) {
748
+ while (!isStatement("endmacro")) {
689
749
  body.push(parseAny());
690
750
  }
691
751
  return new Macro(name, args, body);
@@ -708,19 +768,22 @@ function parse(tokens) {
708
768
  if (!(loopVariable instanceof Identifier || loopVariable instanceof TupleLiteral)) {
709
769
  throw new SyntaxError(`Expected identifier/tuple for the loop variable, got ${loopVariable.type} instead`);
710
770
  }
711
- expect(TOKEN_TYPES.In, "Expected `in` keyword following loop variable");
771
+ if (!isIdentifier("in")) {
772
+ throw new SyntaxError("Expected `in` keyword following loop variable");
773
+ }
774
+ ++current;
712
775
  const iterable = parseExpression();
713
776
  expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
714
777
  const body = [];
715
- while (not(TOKEN_TYPES.OpenStatement, TOKEN_TYPES.EndFor) && not(TOKEN_TYPES.OpenStatement, TOKEN_TYPES.Else)) {
778
+ while (!isStatement("endfor", "else")) {
716
779
  body.push(parseAny());
717
780
  }
718
781
  const alternative = [];
719
- if (is(TOKEN_TYPES.OpenStatement, TOKEN_TYPES.Else)) {
782
+ if (isStatement("else")) {
720
783
  ++current;
721
784
  ++current;
722
785
  expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
723
- while (not(TOKEN_TYPES.OpenStatement, TOKEN_TYPES.EndFor)) {
786
+ while (!isStatement("endfor")) {
724
787
  alternative.push(parseAny());
725
788
  }
726
789
  }
@@ -731,22 +794,22 @@ function parse(tokens) {
731
794
  }
732
795
  function parseIfExpression() {
733
796
  const a = parseLogicalOrExpression();
734
- if (is(TOKEN_TYPES.If)) {
797
+ if (isIdentifier("if")) {
735
798
  ++current;
736
- const predicate = parseLogicalOrExpression();
737
- if (is(TOKEN_TYPES.Else)) {
799
+ const test = parseLogicalOrExpression();
800
+ if (isIdentifier("else")) {
738
801
  ++current;
739
- const b = parseLogicalOrExpression();
740
- return new If(predicate, [a], [b]);
802
+ const falseExpr = parseIfExpression();
803
+ return new Ternary(test, a, falseExpr);
741
804
  } else {
742
- return new SelectExpression(a, predicate);
805
+ return new SelectExpression(a, test);
743
806
  }
744
807
  }
745
808
  return a;
746
809
  }
747
810
  function parseLogicalOrExpression() {
748
811
  let left = parseLogicalAndExpression();
749
- while (is(TOKEN_TYPES.Or)) {
812
+ while (isIdentifier("or")) {
750
813
  const operator = tokens[current];
751
814
  ++current;
752
815
  const right = parseLogicalAndExpression();
@@ -756,7 +819,7 @@ function parse(tokens) {
756
819
  }
757
820
  function parseLogicalAndExpression() {
758
821
  let left = parseLogicalNegationExpression();
759
- while (is(TOKEN_TYPES.And)) {
822
+ while (isIdentifier("and")) {
760
823
  const operator = tokens[current];
761
824
  ++current;
762
825
  const right = parseLogicalNegationExpression();
@@ -766,7 +829,7 @@ function parse(tokens) {
766
829
  }
767
830
  function parseLogicalNegationExpression() {
768
831
  let right;
769
- while (is(TOKEN_TYPES.Not)) {
832
+ while (isIdentifier("not")) {
770
833
  const operator = tokens[current];
771
834
  ++current;
772
835
  const arg = parseLogicalNegationExpression();
@@ -776,9 +839,18 @@ function parse(tokens) {
776
839
  }
777
840
  function parseComparisonExpression() {
778
841
  let left = parseAdditiveExpression();
779
- while (is(TOKEN_TYPES.ComparisonBinaryOperator) || is(TOKEN_TYPES.In) || is(TOKEN_TYPES.NotIn)) {
780
- const operator = tokens[current];
781
- ++current;
842
+ while (true) {
843
+ let operator;
844
+ if (isIdentifier("not", "in")) {
845
+ operator = new Token("not in", TOKEN_TYPES.Identifier);
846
+ current += 2;
847
+ } else if (isIdentifier("in")) {
848
+ operator = tokens[current++];
849
+ } else if (is(TOKEN_TYPES.ComparisonBinaryOperator)) {
850
+ operator = tokens[current++];
851
+ } else {
852
+ break;
853
+ }
782
854
  const right = parseAdditiveExpression();
783
855
  left = new BinaryExpression(operator, left, right);
784
856
  }
@@ -818,14 +890,21 @@ function parse(tokens) {
818
890
  function parseArgumentsList() {
819
891
  const args = [];
820
892
  while (!is(TOKEN_TYPES.CloseParen)) {
821
- let argument = parseExpression();
822
- if (is(TOKEN_TYPES.Equals)) {
893
+ let argument;
894
+ if (tokens[current].type === TOKEN_TYPES.MultiplicativeBinaryOperator && tokens[current].value === "*") {
823
895
  ++current;
824
- if (!(argument instanceof Identifier)) {
825
- throw new SyntaxError(`Expected identifier for keyword argument`);
896
+ const expr = parseExpression();
897
+ argument = new SpreadExpression(expr);
898
+ } else {
899
+ argument = parseExpression();
900
+ if (is(TOKEN_TYPES.Equals)) {
901
+ ++current;
902
+ if (!(argument instanceof Identifier)) {
903
+ throw new SyntaxError(`Expected identifier for keyword argument`);
904
+ }
905
+ const value = parseExpression();
906
+ argument = new KeywordArgumentExpression(argument, value);
826
907
  }
827
- const value = parseExpression();
828
- argument = new KeywordArgumentExpression(argument, value);
829
908
  }
830
909
  args.push(argument);
831
910
  if (is(TOKEN_TYPES.Comma)) {
@@ -866,7 +945,7 @@ function parse(tokens) {
866
945
  const operator = tokens[current];
867
946
  ++current;
868
947
  let property;
869
- const computed = operator.type !== TOKEN_TYPES.Dot;
948
+ const computed = operator.type === TOKEN_TYPES.OpenSquareBracket;
870
949
  if (computed) {
871
950
  property = parseMemberExpressionArgumentsList();
872
951
  expect(TOKEN_TYPES.CloseSquareBracket, "Expected closing square bracket");
@@ -883,8 +962,7 @@ function parse(tokens) {
883
962
  function parseMultiplicativeExpression() {
884
963
  let left = parseTestExpression();
885
964
  while (is(TOKEN_TYPES.MultiplicativeBinaryOperator)) {
886
- const operator = tokens[current];
887
- ++current;
965
+ const operator = tokens[current++];
888
966
  const right = parseTestExpression();
889
967
  left = new BinaryExpression(operator, left, right);
890
968
  }
@@ -892,18 +970,13 @@ function parse(tokens) {
892
970
  }
893
971
  function parseTestExpression() {
894
972
  let operand = parseFilterExpression();
895
- while (is(TOKEN_TYPES.Is)) {
973
+ while (isIdentifier("is")) {
896
974
  ++current;
897
- const negate = is(TOKEN_TYPES.Not);
975
+ const negate = isIdentifier("not");
898
976
  if (negate) {
899
977
  ++current;
900
978
  }
901
- let filter = parsePrimaryExpression();
902
- if (filter instanceof BooleanLiteral) {
903
- filter = new Identifier(filter.value.toString());
904
- } else if (filter instanceof NullLiteral) {
905
- filter = new Identifier("none");
906
- }
979
+ const filter = parsePrimaryExpression();
907
980
  if (!(filter instanceof Identifier)) {
908
981
  throw new SyntaxError(`Expected identifier for the test`);
909
982
  }
@@ -927,34 +1000,27 @@ function parse(tokens) {
927
1000
  return operand;
928
1001
  }
929
1002
  function parsePrimaryExpression() {
930
- const token = tokens[current];
1003
+ const token = tokens[current++];
931
1004
  switch (token.type) {
932
- case TOKEN_TYPES.NumericLiteral:
933
- ++current;
934
- return new NumericLiteral(Number(token.value));
935
- case TOKEN_TYPES.StringLiteral:
936
- ++current;
937
- return new StringLiteral(token.value);
938
- case TOKEN_TYPES.BooleanLiteral:
939
- ++current;
940
- return new BooleanLiteral(token.value.toLowerCase() === "true");
941
- case TOKEN_TYPES.NullLiteral:
942
- ++current;
943
- return new NullLiteral(null);
1005
+ case TOKEN_TYPES.NumericLiteral: {
1006
+ const num = token.value;
1007
+ return num.includes(".") ? new FloatLiteral(Number(num)) : new IntegerLiteral(Number(num));
1008
+ }
1009
+ case TOKEN_TYPES.StringLiteral: {
1010
+ let value = token.value;
1011
+ while (is(TOKEN_TYPES.StringLiteral)) {
1012
+ value += tokens[current++].value;
1013
+ }
1014
+ return new StringLiteral(value);
1015
+ }
944
1016
  case TOKEN_TYPES.Identifier:
945
- ++current;
946
1017
  return new Identifier(token.value);
947
1018
  case TOKEN_TYPES.OpenParen: {
948
- ++current;
949
1019
  const expression = parseExpressionSequence();
950
- if (tokens[current].type !== TOKEN_TYPES.CloseParen) {
951
- throw new SyntaxError(`Expected closing parenthesis, got ${tokens[current].type} instead`);
952
- }
953
- ++current;
1020
+ expect(TOKEN_TYPES.CloseParen, "Expected closing parenthesis, got ${tokens[current].type} instead.");
954
1021
  return expression;
955
1022
  }
956
1023
  case TOKEN_TYPES.OpenSquareBracket: {
957
- ++current;
958
1024
  const values = [];
959
1025
  while (!is(TOKEN_TYPES.CloseSquareBracket)) {
960
1026
  values.push(parseExpression());
@@ -966,7 +1032,6 @@ function parse(tokens) {
966
1032
  return new ArrayLiteral(values);
967
1033
  }
968
1034
  case TOKEN_TYPES.OpenCurlyBracket: {
969
- ++current;
970
1035
  const values = /* @__PURE__ */ new Map();
971
1036
  while (!is(TOKEN_TYPES.CloseCurlyBracket)) {
972
1037
  const key = parseExpression();
@@ -1020,6 +1085,52 @@ function slice(array, start, stop, step = 1) {
1020
1085
  function titleCase(value) {
1021
1086
  return value.replace(/\b\w/g, (c) => c.toUpperCase());
1022
1087
  }
1088
+ function strftime_now(format2) {
1089
+ return strftime(/* @__PURE__ */ new Date(), format2);
1090
+ }
1091
+ function strftime(date, format2) {
1092
+ const monthFormatterLong = new Intl.DateTimeFormat(void 0, { month: "long" });
1093
+ const monthFormatterShort = new Intl.DateTimeFormat(void 0, { month: "short" });
1094
+ const pad2 = (n) => n < 10 ? "0" + n : n.toString();
1095
+ return format2.replace(/%[YmdbBHM%]/g, (token) => {
1096
+ switch (token) {
1097
+ case "%Y":
1098
+ return date.getFullYear().toString();
1099
+ case "%m":
1100
+ return pad2(date.getMonth() + 1);
1101
+ case "%d":
1102
+ return pad2(date.getDate());
1103
+ case "%b":
1104
+ return monthFormatterShort.format(date);
1105
+ case "%B":
1106
+ return monthFormatterLong.format(date);
1107
+ case "%H":
1108
+ return pad2(date.getHours());
1109
+ case "%M":
1110
+ return pad2(date.getMinutes());
1111
+ case "%%":
1112
+ return "%";
1113
+ default:
1114
+ return token;
1115
+ }
1116
+ });
1117
+ }
1118
+ function escapeRegExp(s) {
1119
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1120
+ }
1121
+ function replace(str, oldvalue, newvalue, count) {
1122
+ if (count === 0)
1123
+ return str;
1124
+ let remaining = count == null || count < 0 ? Infinity : count;
1125
+ const pattern = oldvalue.length === 0 ? new RegExp("(?=)", "gu") : new RegExp(escapeRegExp(oldvalue), "gu");
1126
+ return str.replaceAll(pattern, (match) => {
1127
+ if (remaining > 0) {
1128
+ --remaining;
1129
+ return newvalue;
1130
+ }
1131
+ return match;
1132
+ });
1133
+ }
1023
1134
 
1024
1135
  // src/runtime.ts
1025
1136
  var BreakControl = class extends Error {
@@ -1047,9 +1158,18 @@ var RuntimeValue = class {
1047
1158
  __bool__() {
1048
1159
  return new BooleanValue(!!this.value);
1049
1160
  }
1161
+ toString() {
1162
+ return String(this.value);
1163
+ }
1164
+ };
1165
+ var IntegerValue = class extends RuntimeValue {
1166
+ type = "IntegerValue";
1050
1167
  };
1051
- var NumericValue = class extends RuntimeValue {
1052
- type = "NumericValue";
1168
+ var FloatValue = class extends RuntimeValue {
1169
+ type = "FloatValue";
1170
+ toString() {
1171
+ return this.value % 1 === 0 ? this.value.toFixed(1) : this.value.toString();
1172
+ }
1053
1173
  };
1054
1174
  var StringValue = class extends RuntimeValue {
1055
1175
  type = "StringValue";
@@ -1078,7 +1198,13 @@ var StringValue = class extends RuntimeValue {
1078
1198
  return new StringValue(titleCase(this.value));
1079
1199
  })
1080
1200
  ],
1081
- ["length", new NumericValue(this.value.length)],
1201
+ [
1202
+ "capitalize",
1203
+ new FunctionValue(() => {
1204
+ return new StringValue(this.value.charAt(0).toUpperCase() + this.value.slice(1));
1205
+ })
1206
+ ],
1207
+ ["length", new IntegerValue(this.value.length)],
1082
1208
  [
1083
1209
  "rstrip",
1084
1210
  new FunctionValue(() => {
@@ -1097,11 +1223,21 @@ var StringValue = class extends RuntimeValue {
1097
1223
  if (args.length === 0) {
1098
1224
  throw new Error("startswith() requires at least one argument");
1099
1225
  }
1100
- const prefix = args[0];
1101
- if (!(prefix instanceof StringValue)) {
1102
- throw new Error("startswith() argument must be a string");
1226
+ const pattern = args[0];
1227
+ if (pattern instanceof StringValue) {
1228
+ return new BooleanValue(this.value.startsWith(pattern.value));
1229
+ } else if (pattern instanceof ArrayValue) {
1230
+ for (const item of pattern.value) {
1231
+ if (!(item instanceof StringValue)) {
1232
+ throw new Error("startswith() tuple elements must be strings");
1233
+ }
1234
+ if (this.value.startsWith(item.value)) {
1235
+ return new BooleanValue(true);
1236
+ }
1237
+ }
1238
+ return new BooleanValue(false);
1103
1239
  }
1104
- return new BooleanValue(this.value.startsWith(prefix.value));
1240
+ throw new Error("startswith() argument must be a string or tuple of strings");
1105
1241
  })
1106
1242
  ],
1107
1243
  [
@@ -1110,11 +1246,21 @@ var StringValue = class extends RuntimeValue {
1110
1246
  if (args.length === 0) {
1111
1247
  throw new Error("endswith() requires at least one argument");
1112
1248
  }
1113
- const suffix = args[0];
1114
- if (!(suffix instanceof StringValue)) {
1115
- throw new Error("endswith() argument must be a string");
1249
+ const pattern = args[0];
1250
+ if (pattern instanceof StringValue) {
1251
+ return new BooleanValue(this.value.endsWith(pattern.value));
1252
+ } else if (pattern instanceof ArrayValue) {
1253
+ for (const item of pattern.value) {
1254
+ if (!(item instanceof StringValue)) {
1255
+ throw new Error("endswith() tuple elements must be strings");
1256
+ }
1257
+ if (this.value.endsWith(item.value)) {
1258
+ return new BooleanValue(true);
1259
+ }
1260
+ }
1261
+ return new BooleanValue(false);
1116
1262
  }
1117
- return new BooleanValue(this.value.endsWith(suffix.value));
1263
+ throw new Error("endswith() argument must be a string or tuple of strings");
1118
1264
  })
1119
1265
  ],
1120
1266
  [
@@ -1126,8 +1272,8 @@ var StringValue = class extends RuntimeValue {
1126
1272
  if (!(sep instanceof StringValue || sep instanceof NullValue)) {
1127
1273
  throw new Error("sep argument must be a string or null");
1128
1274
  }
1129
- const maxsplit = args[1] ?? new NumericValue(-1);
1130
- if (!(maxsplit instanceof NumericValue)) {
1275
+ const maxsplit = args[1] ?? new IntegerValue(-1);
1276
+ if (!(maxsplit instanceof IntegerValue)) {
1131
1277
  throw new Error("maxsplit argument must be a number");
1132
1278
  }
1133
1279
  let result = [];
@@ -1151,6 +1297,33 @@ var StringValue = class extends RuntimeValue {
1151
1297
  }
1152
1298
  return new ArrayValue(result.map((part) => new StringValue(part)));
1153
1299
  })
1300
+ ],
1301
+ [
1302
+ "replace",
1303
+ new FunctionValue((args) => {
1304
+ if (args.length < 2) {
1305
+ throw new Error("replace() requires at least two arguments");
1306
+ }
1307
+ const oldValue = args[0];
1308
+ const newValue = args[1];
1309
+ if (!(oldValue instanceof StringValue && newValue instanceof StringValue)) {
1310
+ throw new Error("replace() arguments must be strings");
1311
+ }
1312
+ let count;
1313
+ if (args.length > 2) {
1314
+ if (args[2].type === "KeywordArgumentsValue") {
1315
+ count = args[2].value.get("count") ?? new NullValue();
1316
+ } else {
1317
+ count = args[2];
1318
+ }
1319
+ } else {
1320
+ count = new NullValue();
1321
+ }
1322
+ if (!(count instanceof IntegerValue || count instanceof NullValue)) {
1323
+ throw new Error("replace() count argument must be a number or null");
1324
+ }
1325
+ return new StringValue(replace(this.value, oldValue.value, newValue.value, count.value));
1326
+ })
1154
1327
  ]
1155
1328
  ]);
1156
1329
  };
@@ -1180,22 +1353,28 @@ var ObjectValue = class extends RuntimeValue {
1180
1353
  return this.value.get(key.value) ?? defaultValue ?? new NullValue();
1181
1354
  })
1182
1355
  ],
1183
- [
1184
- "items",
1185
- new FunctionValue(() => {
1186
- return new ArrayValue(
1187
- Array.from(this.value.entries()).map(([key, value]) => new ArrayValue([new StringValue(key), value]))
1188
- );
1189
- })
1190
- ]
1356
+ ["items", new FunctionValue(() => this.items())],
1357
+ ["keys", new FunctionValue(() => this.keys())],
1358
+ ["values", new FunctionValue(() => this.values())]
1191
1359
  ]);
1360
+ items() {
1361
+ return new ArrayValue(
1362
+ Array.from(this.value.entries()).map(([key, value]) => new ArrayValue([new StringValue(key), value]))
1363
+ );
1364
+ }
1365
+ keys() {
1366
+ return new ArrayValue(Array.from(this.value.keys()).map((key) => new StringValue(key)));
1367
+ }
1368
+ values() {
1369
+ return new ArrayValue(Array.from(this.value.values()));
1370
+ }
1192
1371
  };
1193
1372
  var KeywordArgumentsValue = class extends ObjectValue {
1194
1373
  type = "KeywordArgumentsValue";
1195
1374
  };
1196
1375
  var ArrayValue = class extends RuntimeValue {
1197
1376
  type = "ArrayValue";
1198
- builtins = /* @__PURE__ */ new Map([["length", new NumericValue(this.value.length)]]);
1377
+ builtins = /* @__PURE__ */ new Map([["length", new IntegerValue(this.value.length)]]);
1199
1378
  /**
1200
1379
  * NOTE: necessary to override since all JavaScript arrays are considered truthy,
1201
1380
  * while only non-empty Python arrays are consider truthy.
@@ -1250,8 +1429,8 @@ var Environment = class {
1250
1429
  [
1251
1430
  "odd",
1252
1431
  (operand) => {
1253
- if (operand.type !== "NumericValue") {
1254
- throw new Error(`Cannot apply test "odd" to type: ${operand.type}`);
1432
+ if (!(operand instanceof IntegerValue)) {
1433
+ throw new Error(`cannot odd on ${operand.type}`);
1255
1434
  }
1256
1435
  return operand.value % 2 !== 0;
1257
1436
  }
@@ -1259,8 +1438,8 @@ var Environment = class {
1259
1438
  [
1260
1439
  "even",
1261
1440
  (operand) => {
1262
- if (operand.type !== "NumericValue") {
1263
- throw new Error(`Cannot apply test "even" to type: ${operand.type}`);
1441
+ if (!(operand instanceof IntegerValue)) {
1442
+ throw new Error(`cannot even on ${operand.type}`);
1264
1443
  }
1265
1444
  return operand.value % 2 === 0;
1266
1445
  }
@@ -1269,8 +1448,8 @@ var Environment = class {
1269
1448
  ["true", (operand) => operand.type === "BooleanValue" && operand.value],
1270
1449
  ["none", (operand) => operand.type === "NullValue"],
1271
1450
  ["string", (operand) => operand.type === "StringValue"],
1272
- ["number", (operand) => operand.type === "NumericValue"],
1273
- ["integer", (operand) => operand.type === "NumericValue" && Number.isInteger(operand.value)],
1451
+ ["number", (operand) => operand instanceof IntegerValue || operand instanceof FloatValue],
1452
+ ["integer", (operand) => operand instanceof IntegerValue],
1274
1453
  ["iterable", (operand) => operand.type === "ArrayValue" || operand.type === "StringValue"],
1275
1454
  ["mapping", (operand) => operand.type === "ObjectValue"],
1276
1455
  [
@@ -1341,6 +1520,19 @@ var Environment = class {
1341
1520
  }
1342
1521
  }
1343
1522
  };
1523
+ function setupGlobals(env) {
1524
+ env.set("false", false);
1525
+ env.set("true", true);
1526
+ env.set("none", null);
1527
+ env.set("raise_exception", (args) => {
1528
+ throw new Error(args);
1529
+ });
1530
+ env.set("range", range);
1531
+ env.set("strftime_now", strftime_now);
1532
+ env.set("True", true);
1533
+ env.set("False", false);
1534
+ env.set("None", null);
1535
+ }
1344
1536
  var Interpreter = class {
1345
1537
  global;
1346
1538
  constructor(env) {
@@ -1371,29 +1563,39 @@ var Interpreter = class {
1371
1563
  return new BooleanValue(left.value != right.value);
1372
1564
  }
1373
1565
  if (left instanceof UndefinedValue || right instanceof UndefinedValue) {
1374
- throw new Error("Cannot perform operation on undefined values");
1566
+ if (right instanceof UndefinedValue && ["in", "not in"].includes(node.operator.value)) {
1567
+ return new BooleanValue(node.operator.value === "not in");
1568
+ }
1569
+ throw new Error(`Cannot perform operation ${node.operator.value} on undefined values`);
1375
1570
  } else if (left instanceof NullValue || right instanceof NullValue) {
1376
1571
  throw new Error("Cannot perform operation on null values");
1377
- } else if (left instanceof NumericValue && right instanceof NumericValue) {
1572
+ } else if (node.operator.value === "~") {
1573
+ return new StringValue(left.value.toString() + right.value.toString());
1574
+ } else if ((left instanceof IntegerValue || left instanceof FloatValue) && (right instanceof IntegerValue || right instanceof FloatValue)) {
1575
+ const a = left.value, b = right.value;
1378
1576
  switch (node.operator.value) {
1379
1577
  case "+":
1380
- return new NumericValue(left.value + right.value);
1381
1578
  case "-":
1382
- return new NumericValue(left.value - right.value);
1383
- case "*":
1384
- return new NumericValue(left.value * right.value);
1579
+ case "*": {
1580
+ const res = node.operator.value === "+" ? a + b : node.operator.value === "-" ? a - b : a * b;
1581
+ const isFloat = left instanceof FloatValue || right instanceof FloatValue;
1582
+ return isFloat ? new FloatValue(res) : new IntegerValue(res);
1583
+ }
1385
1584
  case "/":
1386
- return new NumericValue(left.value / right.value);
1387
- case "%":
1388
- return new NumericValue(left.value % right.value);
1585
+ return new FloatValue(a / b);
1586
+ case "%": {
1587
+ const rem = a % b;
1588
+ const isFloat = left instanceof FloatValue || right instanceof FloatValue;
1589
+ return isFloat ? new FloatValue(rem) : new IntegerValue(rem);
1590
+ }
1389
1591
  case "<":
1390
- return new BooleanValue(left.value < right.value);
1592
+ return new BooleanValue(a < b);
1391
1593
  case ">":
1392
- return new BooleanValue(left.value > right.value);
1594
+ return new BooleanValue(a > b);
1393
1595
  case ">=":
1394
- return new BooleanValue(left.value >= right.value);
1596
+ return new BooleanValue(a >= b);
1395
1597
  case "<=":
1396
- return new BooleanValue(left.value <= right.value);
1598
+ return new BooleanValue(a <= b);
1397
1599
  }
1398
1600
  } else if (left instanceof ArrayValue && right instanceof ArrayValue) {
1399
1601
  switch (node.operator.value) {
@@ -1437,7 +1639,16 @@ var Interpreter = class {
1437
1639
  const positionalArguments = [];
1438
1640
  const keywordArguments = /* @__PURE__ */ new Map();
1439
1641
  for (const argument of args) {
1440
- if (argument.type === "KeywordArgumentExpression") {
1642
+ if (argument.type === "SpreadExpression") {
1643
+ const spreadNode = argument;
1644
+ const val = this.evaluate(spreadNode.argument, environment);
1645
+ if (!(val instanceof ArrayValue)) {
1646
+ throw new Error(`Cannot unpack non-iterable type: ${val.type}`);
1647
+ }
1648
+ for (const item of val.value) {
1649
+ positionalArguments.push(item);
1650
+ }
1651
+ } else if (argument.type === "KeywordArgumentExpression") {
1441
1652
  const kwarg = argument;
1442
1653
  keywordArguments.set(kwarg.key.value, this.evaluate(kwarg.value, environment));
1443
1654
  } else {
@@ -1449,13 +1660,9 @@ var Interpreter = class {
1449
1660
  }
1450
1661
  return [positionalArguments, keywordArguments];
1451
1662
  }
1452
- /**
1453
- * Evaluates expressions following the filter operation type.
1454
- */
1455
- evaluateFilterExpression(node, environment) {
1456
- const operand = this.evaluate(node.operand, environment);
1457
- if (node.filter.type === "Identifier") {
1458
- const filter = node.filter;
1663
+ applyFilter(operand, filterNode, environment) {
1664
+ if (filterNode.type === "Identifier") {
1665
+ const filter = filterNode;
1459
1666
  if (filter.value === "tojson") {
1460
1667
  return new StringValue(toJSON(operand));
1461
1668
  }
@@ -1468,7 +1675,7 @@ var Interpreter = class {
1468
1675
  case "last":
1469
1676
  return operand.value[operand.value.length - 1];
1470
1677
  case "length":
1471
- return new NumericValue(operand.value.length);
1678
+ return new IntegerValue(operand.value.length);
1472
1679
  case "reverse":
1473
1680
  return new ArrayValue(operand.value.reverse());
1474
1681
  case "sort":
@@ -1478,7 +1685,8 @@ var Interpreter = class {
1478
1685
  throw new Error(`Cannot compare different types: ${a.type} and ${b.type}`);
1479
1686
  }
1480
1687
  switch (a.type) {
1481
- case "NumericValue":
1688
+ case "IntegerValue":
1689
+ case "FloatValue":
1482
1690
  return a.value - b.value;
1483
1691
  case "StringValue":
1484
1692
  return a.value.localeCompare(b.value);
@@ -1491,21 +1699,40 @@ var Interpreter = class {
1491
1699
  return new StringValue(operand.value.map((x) => x.value).join(""));
1492
1700
  case "string":
1493
1701
  return new StringValue(toJSON(operand));
1702
+ case "unique": {
1703
+ const seen = /* @__PURE__ */ new Set();
1704
+ const output = [];
1705
+ for (const item of operand.value) {
1706
+ if (!seen.has(item.value)) {
1707
+ seen.add(item.value);
1708
+ output.push(item);
1709
+ }
1710
+ }
1711
+ return new ArrayValue(output);
1712
+ }
1494
1713
  default:
1495
1714
  throw new Error(`Unknown ArrayValue filter: ${filter.value}`);
1496
1715
  }
1497
1716
  } else if (operand instanceof StringValue) {
1498
1717
  switch (filter.value) {
1499
1718
  case "length":
1500
- return new NumericValue(operand.value.length);
1501
1719
  case "upper":
1502
- return new StringValue(operand.value.toUpperCase());
1503
1720
  case "lower":
1504
- return new StringValue(operand.value.toLowerCase());
1505
1721
  case "title":
1506
- return new StringValue(titleCase(operand.value));
1507
- case "capitalize":
1508
- return new StringValue(operand.value.charAt(0).toUpperCase() + operand.value.slice(1));
1722
+ case "capitalize": {
1723
+ const builtin = operand.builtins.get(filter.value);
1724
+ if (builtin instanceof FunctionValue) {
1725
+ return builtin.value(
1726
+ /* no arguments */
1727
+ [],
1728
+ environment
1729
+ );
1730
+ } else if (builtin instanceof IntegerValue) {
1731
+ return builtin;
1732
+ } else {
1733
+ throw new Error(`Unknown StringValue filter: ${filter.value}`);
1734
+ }
1735
+ }
1509
1736
  case "trim":
1510
1737
  return new StringValue(operand.value.trim());
1511
1738
  case "indent":
@@ -1520,13 +1747,25 @@ var Interpreter = class {
1520
1747
  case "join":
1521
1748
  case "string":
1522
1749
  return operand;
1750
+ case "int": {
1751
+ const val = parseInt(operand.value, 10);
1752
+ return new IntegerValue(isNaN(val) ? 0 : val);
1753
+ }
1754
+ case "float": {
1755
+ const val = parseFloat(operand.value);
1756
+ return new FloatValue(isNaN(val) ? 0 : val);
1757
+ }
1523
1758
  default:
1524
1759
  throw new Error(`Unknown StringValue filter: ${filter.value}`);
1525
1760
  }
1526
- } else if (operand instanceof NumericValue) {
1761
+ } else if (operand instanceof IntegerValue || operand instanceof FloatValue) {
1527
1762
  switch (filter.value) {
1528
1763
  case "abs":
1529
- return new NumericValue(Math.abs(operand.value));
1764
+ return operand instanceof IntegerValue ? new IntegerValue(Math.abs(operand.value)) : new FloatValue(Math.abs(operand.value));
1765
+ case "int":
1766
+ return new IntegerValue(Math.floor(operand.value));
1767
+ case "float":
1768
+ return new FloatValue(operand.value);
1530
1769
  default:
1531
1770
  throw new Error(`Unknown NumericValue filter: ${filter.value}`);
1532
1771
  }
@@ -1537,14 +1776,27 @@ var Interpreter = class {
1537
1776
  Array.from(operand.value.entries()).map(([key, value]) => new ArrayValue([new StringValue(key), value]))
1538
1777
  );
1539
1778
  case "length":
1540
- return new NumericValue(operand.value.size);
1779
+ return new IntegerValue(operand.value.size);
1541
1780
  default:
1542
1781
  throw new Error(`Unknown ObjectValue filter: ${filter.value}`);
1543
1782
  }
1783
+ } else if (operand instanceof BooleanValue) {
1784
+ switch (filter.value) {
1785
+ case "bool":
1786
+ return new BooleanValue(operand.value);
1787
+ case "int":
1788
+ return new IntegerValue(operand.value ? 1 : 0);
1789
+ case "float":
1790
+ return new FloatValue(operand.value ? 1 : 0);
1791
+ case "string":
1792
+ return new StringValue(operand.value ? "true" : "false");
1793
+ default:
1794
+ throw new Error(`Unknown BooleanValue filter: ${filter.value}`);
1795
+ }
1544
1796
  }
1545
1797
  throw new Error(`Cannot apply filter "${filter.value}" to type: ${operand.type}`);
1546
- } else if (node.filter.type === "CallExpression") {
1547
- const filter = node.filter;
1798
+ } else if (filterNode.type === "CallExpression") {
1799
+ const filter = filterNode;
1548
1800
  if (filter.callee.type !== "Identifier") {
1549
1801
  throw new Error(`Unknown filter: ${filter.callee.type}`);
1550
1802
  }
@@ -1552,7 +1804,7 @@ var Interpreter = class {
1552
1804
  if (filterName === "tojson") {
1553
1805
  const [, kwargs] = this.evaluateArguments(filter.args, environment);
1554
1806
  const indent = kwargs.get("indent") ?? new NullValue();
1555
- if (!(indent instanceof NumericValue || indent instanceof NullValue)) {
1807
+ if (!(indent instanceof IntegerValue || indent instanceof NullValue)) {
1556
1808
  throw new Error("If set, indent must be a number");
1557
1809
  }
1558
1810
  return new StringValue(toJSON(operand, indent.value));
@@ -1571,6 +1823,30 @@ var Interpreter = class {
1571
1823
  throw new Error("separator must be a string");
1572
1824
  }
1573
1825
  return new StringValue(value.join(separator.value));
1826
+ } else if (filterName === "int" || filterName === "float") {
1827
+ const [args, kwargs] = this.evaluateArguments(filter.args, environment);
1828
+ const defaultValue = args.at(0) ?? kwargs.get("default") ?? (filterName === "int" ? new IntegerValue(0) : new FloatValue(0));
1829
+ if (operand instanceof StringValue) {
1830
+ const val = filterName === "int" ? parseInt(operand.value, 10) : parseFloat(operand.value);
1831
+ return isNaN(val) ? defaultValue : filterName === "int" ? new IntegerValue(val) : new FloatValue(val);
1832
+ } else if (operand instanceof IntegerValue || operand instanceof FloatValue) {
1833
+ return operand;
1834
+ } else if (operand instanceof BooleanValue) {
1835
+ return filterName === "int" ? new IntegerValue(operand.value ? 1 : 0) : new FloatValue(operand.value ? 1 : 0);
1836
+ } else {
1837
+ throw new Error(`Cannot apply filter "${filterName}" to type: ${operand.type}`);
1838
+ }
1839
+ } else if (filterName === "default") {
1840
+ const [args, kwargs] = this.evaluateArguments(filter.args, environment);
1841
+ const defaultValue = args[0] ?? new StringValue("");
1842
+ const booleanValue = args[1] ?? kwargs.get("boolean") ?? new BooleanValue(false);
1843
+ if (!(booleanValue instanceof BooleanValue)) {
1844
+ throw new Error("`default` filter flag must be a boolean");
1845
+ }
1846
+ if (operand instanceof UndefinedValue || booleanValue.value && !operand.__bool__().value) {
1847
+ return defaultValue;
1848
+ }
1849
+ return operand;
1574
1850
  }
1575
1851
  if (operand instanceof ArrayValue) {
1576
1852
  switch (filterName) {
@@ -1626,8 +1902,8 @@ var Interpreter = class {
1626
1902
  switch (filterName) {
1627
1903
  case "indent": {
1628
1904
  const [args, kwargs] = this.evaluateArguments(filter.args, environment);
1629
- const width = args.at(0) ?? kwargs.get("width") ?? new NumericValue(4);
1630
- if (!(width instanceof NumericValue)) {
1905
+ const width = args.at(0) ?? kwargs.get("width") ?? new IntegerValue(4);
1906
+ if (!(width instanceof IntegerValue)) {
1631
1907
  throw new Error("width must be a number");
1632
1908
  }
1633
1909
  const first = args.at(1) ?? kwargs.get("first") ?? new BooleanValue(false);
@@ -1639,13 +1915,28 @@ var Interpreter = class {
1639
1915
  );
1640
1916
  return new StringValue(indented.join("\n"));
1641
1917
  }
1918
+ case "replace": {
1919
+ const replaceFn = operand.builtins.get("replace");
1920
+ if (!(replaceFn instanceof FunctionValue)) {
1921
+ throw new Error("replace filter not available");
1922
+ }
1923
+ const [args, kwargs] = this.evaluateArguments(filter.args, environment);
1924
+ return replaceFn.value([...args, new KeywordArgumentsValue(kwargs)], environment);
1925
+ }
1642
1926
  }
1643
1927
  throw new Error(`Unknown StringValue filter: ${filterName}`);
1644
1928
  } else {
1645
1929
  throw new Error(`Cannot apply filter "${filterName}" to type: ${operand.type}`);
1646
1930
  }
1647
1931
  }
1648
- throw new Error(`Unknown filter: ${node.filter.type}`);
1932
+ throw new Error(`Unknown filter: ${filterNode.type}`);
1933
+ }
1934
+ /**
1935
+ * Evaluates expressions following the filter operation type.
1936
+ */
1937
+ evaluateFilterExpression(node, environment) {
1938
+ const operand = this.evaluate(node.operand, environment);
1939
+ return this.applyFilter(operand, node.filter, environment);
1649
1940
  }
1650
1941
  /**
1651
1942
  * Evaluates expressions following the test operation type.
@@ -1659,6 +1950,16 @@ var Interpreter = class {
1659
1950
  const result = test(operand);
1660
1951
  return new BooleanValue(node.negate ? !result : result);
1661
1952
  }
1953
+ /**
1954
+ * Evaluates expressions following the select operation type.
1955
+ */
1956
+ evaluateSelectExpression(node, environment) {
1957
+ const predicate = this.evaluate(node.test, environment);
1958
+ if (!predicate.__bool__().value) {
1959
+ return new UndefinedValue();
1960
+ }
1961
+ return this.evaluate(node.lhs, environment);
1962
+ }
1662
1963
  /**
1663
1964
  * Evaluates expressions following the unary operation type.
1664
1965
  */
@@ -1671,6 +1972,10 @@ var Interpreter = class {
1671
1972
  throw new SyntaxError(`Unknown operator: ${node.operator.value}`);
1672
1973
  }
1673
1974
  }
1975
+ evaluateTernaryExpression(node, environment) {
1976
+ const cond = this.evaluate(node.condition, environment);
1977
+ return cond.__bool__().value ? this.evaluate(node.trueExpr, environment) : this.evaluate(node.falseExpr, environment);
1978
+ }
1674
1979
  evalProgram(program, environment) {
1675
1980
  return this.evaluateBlock(program.body, environment);
1676
1981
  }
@@ -1679,7 +1984,7 @@ var Interpreter = class {
1679
1984
  for (const statement of statements) {
1680
1985
  const lastEvaluated = this.evaluate(statement, environment);
1681
1986
  if (lastEvaluated.type !== "NullValue" && lastEvaluated.type !== "UndefinedValue") {
1682
- result += lastEvaluated.value;
1987
+ result += lastEvaluated.toString();
1683
1988
  }
1684
1989
  }
1685
1990
  return new StringValue(result);
@@ -1705,13 +2010,13 @@ var Interpreter = class {
1705
2010
  const start = this.evaluate(expr.start, environment);
1706
2011
  const stop = this.evaluate(expr.stop, environment);
1707
2012
  const step = this.evaluate(expr.step, environment);
1708
- if (!(start instanceof NumericValue || start instanceof UndefinedValue)) {
2013
+ if (!(start instanceof IntegerValue || start instanceof UndefinedValue)) {
1709
2014
  throw new Error("Slice start must be numeric or undefined");
1710
2015
  }
1711
- if (!(stop instanceof NumericValue || stop instanceof UndefinedValue)) {
2016
+ if (!(stop instanceof IntegerValue || stop instanceof UndefinedValue)) {
1712
2017
  throw new Error("Slice stop must be numeric or undefined");
1713
2018
  }
1714
- if (!(step instanceof NumericValue || step instanceof UndefinedValue)) {
2019
+ if (!(step instanceof IntegerValue || step instanceof UndefinedValue)) {
1715
2020
  throw new Error("Slice step must be numeric or undefined");
1716
2021
  }
1717
2022
  if (object instanceof ArrayValue) {
@@ -1739,7 +2044,7 @@ var Interpreter = class {
1739
2044
  }
1740
2045
  value = object.value.get(property.value) ?? object.builtins.get(property.value);
1741
2046
  } else if (object instanceof ArrayValue || object instanceof StringValue) {
1742
- if (property instanceof NumericValue) {
2047
+ if (property instanceof IntegerValue) {
1743
2048
  value = object.value.at(property.value);
1744
2049
  if (object instanceof StringValue) {
1745
2050
  value = new StringValue(object.value.at(property.value));
@@ -1762,6 +2067,22 @@ var Interpreter = class {
1762
2067
  if (node.assignee.type === "Identifier") {
1763
2068
  const variableName = node.assignee.value;
1764
2069
  environment.setVariable(variableName, rhs);
2070
+ } else if (node.assignee.type === "TupleLiteral") {
2071
+ const tuple = node.assignee;
2072
+ if (!(rhs instanceof ArrayValue)) {
2073
+ throw new Error(`Cannot unpack non-iterable type in set: ${rhs.type}`);
2074
+ }
2075
+ const arr = rhs.value;
2076
+ if (arr.length !== tuple.value.length) {
2077
+ throw new Error(`Too ${tuple.value.length > arr.length ? "few" : "many"} items to unpack in set`);
2078
+ }
2079
+ for (let i = 0; i < tuple.value.length; ++i) {
2080
+ const elem = tuple.value[i];
2081
+ if (elem.type !== "Identifier") {
2082
+ throw new Error(`Cannot unpack to non-identifier in set: ${elem.type}`);
2083
+ }
2084
+ environment.setVariable(elem.value, arr[i]);
2085
+ }
1765
2086
  } else if (node.assignee.type === "MemberExpression") {
1766
2087
  const member = node.assignee;
1767
2088
  const object = this.evaluate(member.object, environment);
@@ -1786,13 +2107,16 @@ var Interpreter = class {
1786
2107
  let test, iterable;
1787
2108
  if (node.iterable.type === "SelectExpression") {
1788
2109
  const select = node.iterable;
1789
- iterable = this.evaluate(select.iterable, scope);
2110
+ iterable = this.evaluate(select.lhs, scope);
1790
2111
  test = select.test;
1791
2112
  } else {
1792
2113
  iterable = this.evaluate(node.iterable, scope);
1793
2114
  }
1794
- if (!(iterable instanceof ArrayValue)) {
1795
- throw new Error(`Expected iterable type in for loop: got ${iterable.type}`);
2115
+ if (!(iterable instanceof ArrayValue || iterable instanceof ObjectValue)) {
2116
+ throw new Error(`Expected iterable or object type in for loop: got ${iterable.type}`);
2117
+ }
2118
+ if (iterable instanceof ObjectValue) {
2119
+ iterable = iterable.keys();
1796
2120
  }
1797
2121
  const items = [];
1798
2122
  const scopeUpdateFunctions = [];
@@ -1836,13 +2160,13 @@ var Interpreter = class {
1836
2160
  let noIteration = true;
1837
2161
  for (let i = 0; i < items.length; ++i) {
1838
2162
  const loop = /* @__PURE__ */ new Map([
1839
- ["index", new NumericValue(i + 1)],
1840
- ["index0", new NumericValue(i)],
1841
- ["revindex", new NumericValue(items.length - i)],
1842
- ["revindex0", new NumericValue(items.length - i - 1)],
2163
+ ["index", new IntegerValue(i + 1)],
2164
+ ["index0", new IntegerValue(i)],
2165
+ ["revindex", new IntegerValue(items.length - i)],
2166
+ ["revindex0", new IntegerValue(items.length - i - 1)],
1843
2167
  ["first", new BooleanValue(i === 0)],
1844
2168
  ["last", new BooleanValue(i === items.length - 1)],
1845
- ["length", new NumericValue(items.length)],
2169
+ ["length", new IntegerValue(items.length)],
1846
2170
  ["previtem", i > 0 ? items[i - 1] : new UndefinedValue()],
1847
2171
  ["nextitem", i < items.length - 1 ? items[i + 1] : new UndefinedValue()]
1848
2172
  ]);
@@ -1905,8 +2229,36 @@ var Interpreter = class {
1905
2229
  );
1906
2230
  return new NullValue();
1907
2231
  }
2232
+ evaluateCallStatement(node, environment) {
2233
+ const callerFn = new FunctionValue((callerArgs, callerEnv) => {
2234
+ const callBlockEnv = new Environment(callerEnv);
2235
+ if (node.callerArgs) {
2236
+ for (let i = 0; i < node.callerArgs.length; ++i) {
2237
+ const param = node.callerArgs[i];
2238
+ if (param.type !== "Identifier") {
2239
+ throw new Error(`Caller parameter must be an identifier, got ${param.type}`);
2240
+ }
2241
+ callBlockEnv.setVariable(param.value, callerArgs[i] ?? new UndefinedValue());
2242
+ }
2243
+ }
2244
+ return this.evaluateBlock(node.body, callBlockEnv);
2245
+ });
2246
+ const [macroArgs, macroKwargs] = this.evaluateArguments(node.call.args, environment);
2247
+ macroArgs.push(new KeywordArgumentsValue(macroKwargs));
2248
+ const fn = this.evaluate(node.call.callee, environment);
2249
+ if (fn.type !== "FunctionValue") {
2250
+ throw new Error(`Cannot call something that is not a function: got ${fn.type}`);
2251
+ }
2252
+ const newEnv = new Environment(environment);
2253
+ newEnv.setVariable("caller", callerFn);
2254
+ return fn.value(macroArgs, newEnv);
2255
+ }
2256
+ evaluateFilterStatement(node, environment) {
2257
+ const rendered = this.evaluateBlock(node.body, environment);
2258
+ return this.applyFilter(rendered, node.filter, environment);
2259
+ }
1908
2260
  evaluate(statement, environment) {
1909
- if (statement === void 0)
2261
+ if (!statement)
1910
2262
  return new UndefinedValue();
1911
2263
  switch (statement.type) {
1912
2264
  case "Program":
@@ -1919,18 +2271,18 @@ var Interpreter = class {
1919
2271
  return this.evaluateFor(statement, environment);
1920
2272
  case "Macro":
1921
2273
  return this.evaluateMacro(statement, environment);
2274
+ case "CallStatement":
2275
+ return this.evaluateCallStatement(statement, environment);
1922
2276
  case "Break":
1923
2277
  throw new BreakControl();
1924
2278
  case "Continue":
1925
2279
  throw new ContinueControl();
1926
- case "NumericLiteral":
1927
- return new NumericValue(Number(statement.value));
2280
+ case "IntegerLiteral":
2281
+ return new IntegerValue(statement.value);
2282
+ case "FloatLiteral":
2283
+ return new FloatValue(statement.value);
1928
2284
  case "StringLiteral":
1929
2285
  return new StringValue(statement.value);
1930
- case "BooleanLiteral":
1931
- return new BooleanValue(statement.value);
1932
- case "NullLiteral":
1933
- return new NullValue(statement.value);
1934
2286
  case "ArrayLiteral":
1935
2287
  return new ArrayValue(statement.value.map((x) => this.evaluate(x, environment)));
1936
2288
  case "TupleLiteral":
@@ -1958,8 +2310,16 @@ var Interpreter = class {
1958
2310
  return this.evaluateBinaryExpression(statement, environment);
1959
2311
  case "FilterExpression":
1960
2312
  return this.evaluateFilterExpression(statement, environment);
2313
+ case "FilterStatement":
2314
+ return this.evaluateFilterStatement(statement, environment);
1961
2315
  case "TestExpression":
1962
2316
  return this.evaluateTestExpression(statement, environment);
2317
+ case "SelectExpression":
2318
+ return this.evaluateSelectExpression(statement, environment);
2319
+ case "Ternary":
2320
+ return this.evaluateTernaryExpression(statement, environment);
2321
+ case "Comment":
2322
+ return new NullValue();
1963
2323
  default:
1964
2324
  throw new SyntaxError(`Unknown node type: ${statement.type}`);
1965
2325
  }
@@ -1968,7 +2328,7 @@ var Interpreter = class {
1968
2328
  function convertToRuntimeValues(input) {
1969
2329
  switch (typeof input) {
1970
2330
  case "number":
1971
- return new NumericValue(input);
2331
+ return Number.isInteger(input) ? new IntegerValue(input) : new FloatValue(input);
1972
2332
  case "string":
1973
2333
  return new StringValue(input);
1974
2334
  case "boolean":
@@ -2000,7 +2360,8 @@ function toJSON(input, indent, depth) {
2000
2360
  case "NullValue":
2001
2361
  case "UndefinedValue":
2002
2362
  return "null";
2003
- case "NumericValue":
2363
+ case "IntegerValue":
2364
+ case "FloatValue":
2004
2365
  case "StringValue":
2005
2366
  case "BooleanValue":
2006
2367
  return JSON.stringify(input.value);
@@ -2029,11 +2390,23 @@ function toJSON(input, indent, depth) {
2029
2390
  var NEWLINE = "\n";
2030
2391
  var OPEN_STATEMENT = "{%- ";
2031
2392
  var CLOSE_STATEMENT = " -%}";
2032
- var OPERATOR_PRECEDENCE = {
2033
- MultiplicativeBinaryOperator: 2,
2034
- AdditiveBinaryOperator: 1,
2035
- ComparisonBinaryOperator: 0
2036
- };
2393
+ function getBinaryOperatorPrecedence(expr) {
2394
+ switch (expr.operator.type) {
2395
+ case "MultiplicativeBinaryOperator":
2396
+ return 4;
2397
+ case "AdditiveBinaryOperator":
2398
+ return 3;
2399
+ case "ComparisonBinaryOperator":
2400
+ return 2;
2401
+ case "Identifier":
2402
+ if (expr.operator.value === "and")
2403
+ return 1;
2404
+ if (expr.operator.value === "in" || expr.operator.value === "not in")
2405
+ return 2;
2406
+ return 0;
2407
+ }
2408
+ return 0;
2409
+ }
2037
2410
  function format(program, indent = " ") {
2038
2411
  const indentStr = typeof indent === "number" ? " ".repeat(indent) : indent;
2039
2412
  const body = formatStatements(program.body, 0, indentStr);
@@ -2062,6 +2435,12 @@ function formatStatement(node, depth, indentStr) {
2062
2435
  return pad + createStatement("break");
2063
2436
  case "Continue":
2064
2437
  return pad + createStatement("continue");
2438
+ case "CallStatement":
2439
+ return formatCallStatement(node, depth, indentStr);
2440
+ case "FilterStatement":
2441
+ return formatFilterStatement(node, depth, indentStr);
2442
+ case "Comment":
2443
+ return pad + "{# " + node.value + " #}";
2065
2444
  default:
2066
2445
  return pad + "{{- " + formatExpression(node) + " -}}";
2067
2446
  }
@@ -2079,7 +2458,7 @@ function formatIf(node, depth, indentStr) {
2079
2458
  }
2080
2459
  }
2081
2460
  let out = pad + createStatement("if", formatExpression(clauses[0].test)) + NEWLINE + formatStatements(clauses[0].body, depth + 1, indentStr);
2082
- for (let i = 1; i < clauses.length; i++) {
2461
+ for (let i = 1; i < clauses.length; ++i) {
2083
2462
  out += NEWLINE + pad + createStatement("elif", formatExpression(clauses[i].test)) + NEWLINE + formatStatements(clauses[i].body, depth + 1, indentStr);
2084
2463
  }
2085
2464
  if (current && current.alternate.length > 0) {
@@ -2093,7 +2472,7 @@ function formatFor(node, depth, indentStr) {
2093
2472
  let formattedIterable = "";
2094
2473
  if (node.iterable.type === "SelectExpression") {
2095
2474
  const n = node.iterable;
2096
- formattedIterable = `${formatExpression(n.iterable)} if ${formatExpression(n.test)}`;
2475
+ formattedIterable = `${formatExpression(n.lhs)} if ${formatExpression(n.test)}`;
2097
2476
  } else {
2098
2477
  formattedIterable = formatExpression(node.iterable);
2099
2478
  }
@@ -2119,20 +2498,40 @@ function formatMacro(node, depth, indentStr) {
2119
2498
  const args = node.args.map(formatExpression).join(", ");
2120
2499
  return pad + createStatement("macro", `${node.name.value}(${args})`) + NEWLINE + formatStatements(node.body, depth + 1, indentStr) + NEWLINE + pad + createStatement("endmacro");
2121
2500
  }
2501
+ function formatCallStatement(node, depth, indentStr) {
2502
+ const pad = indentStr.repeat(depth);
2503
+ const params = node.callerArgs && node.callerArgs.length > 0 ? `(${node.callerArgs.map(formatExpression).join(", ")})` : "";
2504
+ const callExpr = formatExpression(node.call);
2505
+ let out = pad + createStatement(`call${params}`, callExpr) + NEWLINE;
2506
+ out += formatStatements(node.body, depth + 1, indentStr) + NEWLINE;
2507
+ out += pad + createStatement("endcall");
2508
+ return out;
2509
+ }
2510
+ function formatFilterStatement(node, depth, indentStr) {
2511
+ const pad = indentStr.repeat(depth);
2512
+ const spec = node.filter.type === "Identifier" ? node.filter.value : formatExpression(node.filter);
2513
+ let out = pad + createStatement("filter", spec) + NEWLINE;
2514
+ out += formatStatements(node.body, depth + 1, indentStr) + NEWLINE;
2515
+ out += pad + createStatement("endfilter");
2516
+ return out;
2517
+ }
2122
2518
  function formatExpression(node, parentPrec = -1) {
2123
2519
  switch (node.type) {
2520
+ case "SpreadExpression": {
2521
+ const n = node;
2522
+ return `*${formatExpression(n.argument)}`;
2523
+ }
2124
2524
  case "Identifier":
2125
2525
  return node.value;
2126
- case "NullLiteral":
2127
- return "none";
2128
- case "NumericLiteral":
2129
- case "BooleanLiteral":
2526
+ case "IntegerLiteral":
2527
+ return `${node.value}`;
2528
+ case "FloatLiteral":
2130
2529
  return `${node.value}`;
2131
2530
  case "StringLiteral":
2132
2531
  return JSON.stringify(node.value);
2133
2532
  case "BinaryExpression": {
2134
2533
  const n = node;
2135
- const thisPrecedence = OPERATOR_PRECEDENCE[n.operator.type] ?? 0;
2534
+ const thisPrecedence = getBinaryOperatorPrecedence(n);
2136
2535
  const left = formatExpression(n.left, thisPrecedence);
2137
2536
  const right = formatExpression(n.right, thisPrecedence + 1);
2138
2537
  const expr = `${left} ${n.operator.value} ${right}`;
@@ -2143,20 +2542,28 @@ function formatExpression(node, parentPrec = -1) {
2143
2542
  const val = n.operator.value + (n.operator.value === "not" ? " " : "") + formatExpression(n.argument, Infinity);
2144
2543
  return val;
2145
2544
  }
2146
- case "LogicalNegationExpression":
2147
- return `not ${formatExpression(node.argument, Infinity)}`;
2148
2545
  case "CallExpression": {
2149
2546
  const n = node;
2150
- const args = n.args.map((a) => formatExpression(a, -1)).join(", ");
2151
- return `${formatExpression(n.callee, -1)}(${args})`;
2547
+ const args = n.args.map(formatExpression).join(", ");
2548
+ return `${formatExpression(n.callee)}(${args})`;
2152
2549
  }
2153
2550
  case "MemberExpression": {
2154
2551
  const n = node;
2155
- let obj = formatExpression(n.object, -1);
2156
- if (n.object.type !== "Identifier") {
2552
+ let obj = formatExpression(n.object);
2553
+ if (![
2554
+ "Identifier",
2555
+ "MemberExpression",
2556
+ "CallExpression",
2557
+ "StringLiteral",
2558
+ "IntegerLiteral",
2559
+ "FloatLiteral",
2560
+ "ArrayLiteral",
2561
+ "TupleLiteral",
2562
+ "ObjectLiteral"
2563
+ ].includes(n.object.type)) {
2157
2564
  obj = `(${obj})`;
2158
2565
  }
2159
- let prop = formatExpression(n.property, -1);
2566
+ let prop = formatExpression(n.property);
2160
2567
  if (!n.computed && n.property.type !== "Identifier") {
2161
2568
  prop = `(${prop})`;
2162
2569
  }
@@ -2166,47 +2573,47 @@ function formatExpression(node, parentPrec = -1) {
2166
2573
  const n = node;
2167
2574
  const operand = formatExpression(n.operand, Infinity);
2168
2575
  if (n.filter.type === "CallExpression") {
2169
- return `${operand} | ${formatExpression(n.filter, -1)}`;
2576
+ return `${operand} | ${formatExpression(n.filter)}`;
2170
2577
  }
2171
2578
  return `${operand} | ${n.filter.value}`;
2172
2579
  }
2173
2580
  case "SelectExpression": {
2174
2581
  const n = node;
2175
- return `${formatExpression(n.iterable, -1)} | select(${formatExpression(n.test, -1)})`;
2582
+ return `${formatExpression(n.lhs)} if ${formatExpression(n.test)}`;
2176
2583
  }
2177
2584
  case "TestExpression": {
2178
2585
  const n = node;
2179
- return `${formatExpression(n.operand, -1)} is${n.negate ? " not" : ""} ${n.test.value}`;
2586
+ return `${formatExpression(n.operand)} is${n.negate ? " not" : ""} ${n.test.value}`;
2180
2587
  }
2181
2588
  case "ArrayLiteral":
2182
2589
  case "TupleLiteral": {
2183
- const elems = node.value.map((e) => formatExpression(e, -1));
2590
+ const elems = node.value.map(formatExpression);
2184
2591
  const brackets = node.type === "ArrayLiteral" ? "[]" : "()";
2185
2592
  return `${brackets[0]}${elems.join(", ")}${brackets[1]}`;
2186
2593
  }
2187
2594
  case "ObjectLiteral": {
2188
2595
  const entries = Array.from(node.value.entries()).map(
2189
- ([k, v]) => `${formatExpression(k, -1)}: ${formatExpression(v, -1)}`
2596
+ ([k, v]) => `${formatExpression(k)}: ${formatExpression(v)}`
2190
2597
  );
2191
- return `{ ${entries.join(", ")} }`;
2598
+ return `{${entries.join(", ")}}`;
2192
2599
  }
2193
2600
  case "SliceExpression": {
2194
2601
  const n = node;
2195
- const s = n.start ? formatExpression(n.start, -1) : "";
2196
- const t = n.stop ? formatExpression(n.stop, -1) : "";
2197
- const st = n.step ? `:${formatExpression(n.step, -1)}` : "";
2602
+ const s = n.start ? formatExpression(n.start) : "";
2603
+ const t = n.stop ? formatExpression(n.stop) : "";
2604
+ const st = n.step ? `:${formatExpression(n.step)}` : "";
2198
2605
  return `${s}:${t}${st}`;
2199
2606
  }
2200
2607
  case "KeywordArgumentExpression": {
2201
2608
  const n = node;
2202
- return `${n.key.value}=${formatExpression(n.value, -1)}`;
2609
+ return `${n.key.value}=${formatExpression(n.value)}`;
2203
2610
  }
2204
- case "If": {
2611
+ case "Ternary": {
2205
2612
  const n = node;
2206
- const test = formatExpression(n.test, -1);
2207
- const body = formatExpression(n.body[0], 0);
2208
- const alternate = formatExpression(n.alternate[0], -1);
2209
- return `${body} if ${test} else ${alternate}`;
2613
+ const expr = `${formatExpression(n.trueExpr)} if ${formatExpression(n.condition, 0)} else ${formatExpression(
2614
+ n.falseExpr
2615
+ )}`;
2616
+ return parentPrec > -1 ? `(${expr})` : expr;
2210
2617
  }
2211
2618
  default:
2212
2619
  throw new Error(`Unknown expression type: ${node.type}`);
@@ -2228,12 +2635,7 @@ var Template = class {
2228
2635
  }
2229
2636
  render(items) {
2230
2637
  const env = new Environment();
2231
- env.set("false", false);
2232
- env.set("true", true);
2233
- env.set("raise_exception", (args) => {
2234
- throw new Error(args);
2235
- });
2236
- env.set("range", range);
2638
+ setupGlobals(env);
2237
2639
  if (items) {
2238
2640
  for (const [key, value] of Object.entries(items)) {
2239
2641
  env.set(key, value);
@@ -4367,7 +4769,7 @@ __webpack_require__.r(__webpack_exports__);
4367
4769
 
4368
4770
 
4369
4771
 
4370
- const VERSION = '3.6.2';
4772
+ const VERSION = '3.6.3';
4371
4773
 
4372
4774
  // Check if various APIs are available (depends on environment)
4373
4775
  const IS_BROWSER_ENV = typeof window !== "undefined" && typeof window.document !== "undefined";