Revision history for Template::Jinja2

0.3.0  2026-08-21T21:20:33+01:00
    - Add Python method calls on template values. Jinja2 has no method
      syntax of its own — `foo.bar()` resolves `bar` on the underlying
      Python object — and real chat templates lean on that heavily, so
      `value.method()` now dispatches Python str/dict semantics:
      strip/lstrip/rstrip (optional chars set), startswith/endswith
      (str or tuple of candidates), replace (optional count), split
      (no-arg whitespace-run split, or literal split with optional
      maxsplit), lower, upper, title, find (-1 when absent), and
      items/keys/values/get (with optional default) on dicts. Results
      chain, feed filters and tests, and bind through {% set %}.
      Dict methods iterate in sorted-key order and dict views render as
      plain lists — see the README for both divergences.
    - Fix attribute and method access on host objects. `{{ obj.attr }}`
      on a Raku object passed in as a render argument died with
      "No such method 'Template::Jinja2::AST::GetAttr.attr'" — an
      indirect method call was interpolating the AST node instead of
      the attribute name — so no Raku-object accessor was reachable at
      all. Data values (strings, dicts, lists) deliberately do not
      reach Raku's same-named methods; they go through the Python
      method layer, which is what `s.split('|')` needs.
    - Error hygiene for method calls. An unknown method now raises a
      TemplateRuntimeError naming the receiver's type, the method and
      the methods that do exist ('str' object has no method 'casefold'
      (supported: …)); bad argument counts and types, and keyword
      arguments where Python takes none, are reported the same way. A
      method on an undefined value raises UndefinedError naming the
      expression that produced it. Raku dispatch failures inside a host
      object's method are wrapped rather than escaping. "is not
      callable" messages now describe the expression (`foo.bar`)
      instead of gisting an AST node.
    - Fix the `title` filter, which implemented Python's str.title()
      rather than Jinja2's title filter: words start after whitespace
      or one of - ( { [ < only, so `{{ "it's a test" | title }}` is now
      "It's A Test" (was "It'S A Test") and `{{ "1st place" | title }}`
      is "1st Place" (was "1St Place"). It is also Unicode-aware now
      ("école" titles to "École"). Python's spelling remains available
      as the `.title()` method.
    - Support the `separators` kwarg on `tojson`, i.e. Python's
      json.dumps (item_separator, key_separator) tuple. Kimi K2's chat
      template emits its tool declarations with
      `tojson(separators=(',', ':'))`, and the whitespace is
      load-bearing: it changes tokenisation, not just looks.
    - With the above, GLM-5.2's and Kimi K2's full upstream chat
      templates render byte-identically to Python Jinja2 3.1.6 (modulo
      dict key ordering). t/24-python-methods.rakutest covers the
      method semantics case-for-case against CPython, plus a committed
      GLM-shaped fragment fixture.

0.2.0  2026-04-30T00:14:18+01:00
    - Bump Github Actions to use node 24+
    - Add raise_exception() builtin global. Throws TemplateRuntimeError
      with the supplied message; used by HuggingFace chat templates
      (Mistral, Llama-3-Instruct, etc.) to assert structural invariants
      such as user/assistant alternation and validate role/content shapes.
    - Fix string + concatenation. The BinOp '+' branch had inverted
      logic — both code paths fell through to numeric addition, so
      `{{ "[INST]" + msg + "[/INST]" }}` threw X::Str::Numeric. Real
      HF templates (Mistral, Llama-3-Instruct, Qwen) use + for string
      concat throughout, so this blocked rendering them.
    - Fix rebinding of render-arg variables via `{% set %}`. Context
      pushed the slurpy *%vars Hash directly, preserving readonly value
      containers from named-arg binding; any later `{% set foo = ... %}`
      where `foo` was a render arg died with "Cannot assign to an
      immutable value". Mistral and Llama-3-Instruct use this idiom
      for option self-defaulting (e.g. `set re = re if re is defined
      and re is not none else 'none'`).
    - With both fixes: Mistral Small 4's full upstream chat template
      now renders end-to-end (see t/09-huggingface.rakutest). This
      exercises namespace mutation for alternation tracking, sort
      (attribute=…), structured {type, text|thinking|image} content
      arrays, raise_exception, and render-arg self-defaulting.

0.1.1  2026-04-09T04:35:22+01:00
    - Fix LoopContext forward declaration causing precompilation failure on zef install
    - Remove Python reference tests from distribution

0.1.0  2026-04-09T04:09:36+01:00
    - Initial release
    - Complete Jinja2 template engine for Raku targeting compatibility with Python Jinja2 3.1
    - Grammar-based lexer with Pratt-style expression parser
    - All 15 tag types: if/elif/else, for, set, with, block, extends, include, import, from,
      macro, call, filter, do, raw, comment, break, continue, autoescape
    - Full expression syntax: arithmetic, comparison, logical, membership, identity, ternary,
      string concat (~), chained comparisons, slicing
    - 52 built-in filters including tojson, escape, upper, lower, sort, groupby, map,
      select, reject, batch, slice, truncate, filesizeformat, and more
    - 20+ built-in tests: defined, undefined, none, even, odd, divisibleby, string, number, etc.
    - Template inheritance with extends/block/super() supporting multi-level chains
    - Recursive for loops with loop() callable, depth tracking, break/continue
    - Full loop context: index, index0, revindex, first, last, length, cycle, changed,
      previtem, nextitem
    - Macros with default arguments, varargs, kwargs, caller(), recursive self-calls
    - Call blocks with parameterized caller
    - Namespace objects for cross-scope state in loops
    - Whitespace control: trim markers ({%- -%}), +/- markers, trim_blocks, lstrip_blocks
    - Custom delimiters (PHP, ERB, comment syntax, etc.)
    - Line statement prefix and line comment prefix
    - Autoescape with SafeString support for set blocks
    - Numeric literals: hex, octal, binary, scientific notation, underscores
    - Django-style numeric dot access, string literal concatenation
    - Dotted attribute resolution in filters (sort, groupby, sum, etc.)
    - Built-in globals: range(), namespace(), dict(), cycler(), joiner()
    - DictLoader, FileSystemLoader
    - keep_trailing_newline, newline_sequence configuration
    - Error validation for reserved names, constant assignment, block hyphens, macro params
    - 612 tests across 22 test files
    - Real-world validation: byte-identical output against Python Jinja2 for ChatML, Llama 3,
      Mistral, Gemma 2, Zephyr, and Cohere Command A (15K char template) formats
