Skip to content

refactor: major template engine refactor (AST-based)#27

Open
Farrael wants to merge 10 commits intoElliesaur:mainfrom
Farrael:feature/template_ast
Open

refactor: major template engine refactor (AST-based)#27
Farrael wants to merge 10 commits intoElliesaur:mainfrom
Farrael:feature/template_ast

Conversation

@Farrael
Copy link
Contributor

@Farrael Farrael commented Feb 7, 2026

Summary

This PR introduces a complete rewrite of how templates are parsed and processed.
Templates are now compiled into an AST, producing:

  • a list of template nodes
  • a list of component definitions

These structures are cached to improve performance when updating values, without having to re-parse or rebuild the template (as long as the template itself or the component set doesn’t change).

What changed

AST-based processing

  • Templates are parsed once into an AST
  • Nodes and components are cached
  • Updating variables do not invalidates the AST
  • Re-parsing only happens when the template or components actually change

Components as real elements

  • Components are now handled like HTML elements
  • They support children
    • $children is exposed as a string inside component templates

Example:

<div style="background: red">
    {{ $children }}
</div>

Improved variable system

  • Item properties and zero-argument methods are supported and evaluated as requested
  • Suppliers are cached
  • Functions are supported (see below)

Functions in variables

  • Variables can now be functions
  • They receive the current variable stack as the first argument
  • They are not cached
  • This allows accessing context dynamically and executing logic when needed
  • See processor tests and callFunctionWithArguments for examples.

Each improvements

  • each blocks can optionally declare a loop variable name:
{{#each $items item}}
  • Loop item properties can still be shortened:
{{$key}} instead of {{$item.key}}

…but only if it does not shadow an existing variable. (using the same name as a declared one)

Breaking changes

Variable declaration

  • All variables must now be explicitly declared using $.

❌ Old:

{{#if variable ...}}

✅ New:

{{#if $variable ...}}

Component syntax change

  • Components are no longer declared using template syntax.

❌ Old:

{{@special:foo=bar}}

✅ New:

<special foo="bar" />

Children support

  • Components can now receive children content, accessed via:
{{$children}}

Tip

$children is a string already parsed and resolved

Variable resolution rules

  • Variables are only resolved for:
    • declared variable (block, user, ...)
    • context variable (using interface like before)
    • item properties
    • methods without arguments
  • Functions are explicitly opt-in (not cached) and context-aware

Tests

  • A large number of tests were added to cover:
    • AST generation
    • caching behavior
    • variable resolution
    • components and children
    • function calls and context handling

Example

processor.setVariable("number", 12.847);

processor.registerComponent("statCard", """
    <div style="background-color: #2a2a3e; padding: 10; anchor-width: 120; anchor-height: 60;">
        <p style="color: #888888; font-size: 11;">{{$label}}</p>
        <p style="color: #ffffff; font-size: 18; font-weight: bold;">{{$value}}</p>
    </div>
""");

processor.setTemplate("""
    <statCard label="Blocks Placed" value={{$number}} />
""");

Notes

  • It would be good to test this branch on real-world use cases and report any issues we find before merging.
  • On discord kelpy also work on a rework of components and parsing
  • Named slot should be added to components

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant