SPIKE (spike/elixir-to-lit): ~TSR element-template parsing and lit-html
emission (docs/notes/spike-elixir-to-lit.md §template-parser).
Two lexers produce the SAME token stream, and one tree-ifier builds the
tree (parse/1):
- Hologram path — when Hologram 0.10.1 is compiled into the path, the
markup is tokenized by
Hologram.Template.Parser.parse_markup/1(a public, documented, pure function on an@moduledoc falsemodule — de-facto internal-but-callable; see the notes for the stability discussion). Its flat parsed-tag stream is converted 1:1 into the subset token stream below. - Minimal path — a hand-rolled lexer for exactly the subset (plain
elements, static/interpolated attributes,
{expr},{%if}/{%else}/{/if},{%for pat <- expr}/{/for},$event={:action[, params]},{%region :name}/{%endregion},{%super%}), used when Hologram is absent so spec-only consumers (npm/UMD packaging without a Hologram host) still compile.
Region-bearing markup ALWAYS lexes through the minimal lexer on both hosts
(brief 2d, T1-1): Hologram's parse_markup does not know {%region%}, so
lex/1 forces the minimal path whenever the markup mentions a region or the
{%super%} marker — one token stream, one tree shape, on every host.
Token stream (the shared subset):
{:start_tag, tag, attrs} | {:self_closing_tag, tag, attrs} | {:end_tag, tag}
{:text, s} | {:expr, ast}
{:if_start, ast} | :else | :if_end
{:for_start, var, coll_ast} | :for_end
{:region_start, name} | :region_end | :super
attrs :: [{name, {:static, s} | {:parts, [{:text, s} | {:expr, expr_string}]}}
| {:"$event", dom_event, binding_src}
| {:"$bool", name, {:static, s} | {:parts, [...]}}]?attr={expr} (Lit boolean attribute binding) shares the {:"$bool", …}
form across both lexers; <slot>…</slot> (non-void, fallback content) is
supported under both lexers — the Hologram path renames slot tags through
a reserved placeholder because Hologram's parser treats <slot> as void.
Tree shape:
{:element, tag, attrs, children} | {:text, s} | {:expr, ast}
{:if, ast, children, else_children} | {:for, var, coll_ast, children}
{:fragment, children}to_lit_render/2 emits a Lit render() body as nested html template
literals.
Summary
Functions
Compile the default region bodies of a template tree into method fragments
%{name: atom, js: "html..."} (the derived render() references them via
${this.__region_<name>()}). Duplicate region names raise a CompileError.
Compile one slot_template :name override body. {%super%} inside the body
lowers to super.__region_<name>() (the base default fragment at that point
in the chain).
Which lexer parse/1 will use for THIS markup (brief 2d / T1-1): region
markup always lexes :minimal — Hologram's parser does not know
{%region :name}/{%endregion}/{%super%} — everything else follows the
host (:hologram when the dep is present).
Minimal lexer for exactly the subset (used when Hologram is not loaded).
Parse markup into the subset tree. Uses Hologram's template parser when
compiled in, otherwise the minimal lexer. Returns {:ok, tree} or
{:error, message} (message names the construct).
Declared regions of a parsed tree, preorder (outer before inner), as
%{name: atom, nested: [atom]} where nested lists every region name
appearing anywhere inside the region's body.
Emit the JavaScript expression for a Lit render() body (without the
leading return). ctx is a Noizu.Tessera.Component.Expr context.
Tree-ify the shared token stream. Frames: {:elem, {tag, attrs, children}},
{:if, {ast, then, else}}, {:else, {ast, then, else}},
{:for, {var, coll, children}}. Top-level nodes accumulate in top.
Functions
@spec compile_region_methods(term(), Noizu.Tessera.Component.Expr.t()) :: [ %{name: atom(), js: String.t()} ]
Compile the default region bodies of a template tree into method fragments
%{name: atom, js: "html..."} (the derived render() references them via
${this.__region_<name>()}). Duplicate region names raise a CompileError.
@spec compile_region_override(atom(), term(), Noizu.Tessera.Component.Expr.t()) :: %{ name: atom(), js: String.t() }
Compile one slot_template :name override body. {%super%} inside the body
lowers to super.__region_<name>() (the base default fragment at that point
in the chain).
@spec lexer() :: :hologram | :minimal
Which lexer parse/1 uses right now.
@spec lexer_for(String.t()) :: :hologram | :minimal
Which lexer parse/1 will use for THIS markup (brief 2d / T1-1): region
markup always lexes :minimal — Hologram's parser does not know
{%region :name}/{%endregion}/{%super%} — everything else follows the
host (:hologram when the dep is present).
Minimal lexer for exactly the subset (used when Hologram is not loaded).
Parse markup into the subset tree. Uses Hologram's template parser when
compiled in, otherwise the minimal lexer. Returns {:ok, tree} or
{:error, message} (message names the construct).
Declared regions of a parsed tree, preorder (outer before inner), as
%{name: atom, nested: [atom]} where nested lists every region name
appearing anywhere inside the region's body.
@spec to_lit_render(term(), Noizu.Tessera.Component.Expr.t()) :: String.t()
Emit the JavaScript expression for a Lit render() body (without the
leading return). ctx is a Noizu.Tessera.Component.Expr context.
Tree-ify the shared token stream. Frames: {:elem, {tag, attrs, children}},
{:if, {ast, then, else}}, {:else, {ast, then, else}},
{:for, {var, coll, children}}. Top-level nodes accumulate in top.