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