Standalone Mustache template parser and expander.
Supports a subset of Mustache spec:
- Simple variables:
{{name}} - Dot notation:
{{user.name}} - Current element:
{{.}} - Comments:
{{! comment }} - Sections (lists):
{{#items}}...{{/items}} - Sections (maps/context push):
{{#user}}...{{/user}} - Inverted sections:
{{^items}}...{{/items}} - Standalone tag whitespace control
No dependencies on other PtcRunner modules.
Examples
iex> {:ok, ast} = PtcRunner.Mustache.parse("Hello {{name}}")
iex> PtcRunner.Mustache.expand(ast, %{name: "Alice"})
{:ok, "Hello Alice"}
iex> PtcRunner.Mustache.render("{{#items}}- {{name}}\n{{/items}}", %{items: [%{name: "A"}, %{name: "B"}]})
{:ok, "- A\n- B\n"}
Summary
Types
Abstract syntax tree
AST node types
Location in the template
Options for expand/3 and render/3
Variable info for extraction
Functions
Expand AST with context map.
Extract all variables from AST for validation.
Parse template string into AST.
Convenience: parse and expand in one call.
Types
@type ast() :: [ast_node()]
Abstract syntax tree
@type ast_node() :: {:text, String.t()} | {:variable, path :: [String.t()], loc :: location()} | {:current, loc :: location()} | {:comment, String.t(), loc :: location()} | {:section, name :: String.t(), inner :: ast(), loc :: location()} | {:inverted_section, name :: String.t(), inner :: ast(), loc :: location()}
AST node types
@type location() :: %{line: pos_integer(), col: pos_integer()}
Location in the template
@type option() :: {:max_depth, pos_integer()}
Options for expand/3 and render/3
@type variable_info() :: %{ type: :simple | :section | :inverted_section, path: [String.t()], fields: [variable_info()] | nil, loc: location() }
Variable info for extraction
Functions
Expand AST with context map.
Returns {:ok, string} on success or {:error, reason} on expansion errors.
Options
:max_depth- Maximum recursion depth (default: 20)
Examples
iex> {:ok, ast} = PtcRunner.Mustache.parse("Hello {{name}}")
iex> PtcRunner.Mustache.expand(ast, %{name: "World"})
{:ok, "Hello World"}
iex> {:ok, ast} = PtcRunner.Mustache.parse("{{.}}")
iex> {:error, {:dot_on_map, _, _}} = PtcRunner.Mustache.expand(ast, %{}, [])
{:error, {:dot_on_map, %{line: 1, col: 1}, "{{.}} requires scalar value, got map on line 1, col 1. Use {{.field}} or pre-format the data."}}
@spec extract_variables(ast()) :: [variable_info()]
Extract all variables from AST for validation.
Returns a list of variable info maps including location and type.
Examples
iex> {:ok, ast} = PtcRunner.Mustache.parse("{{name}} {{user.email}}")
iex> PtcRunner.Mustache.extract_variables(ast)
[
%{type: :simple, path: ["name"], fields: nil, loc: %{line: 1, col: 1}},
%{type: :simple, path: ["user", "email"], fields: nil, loc: %{line: 1, col: 10}}
]
Parse template string into AST.
Returns {:ok, ast} on success or {:error, message} on parse errors.
Examples
iex> PtcRunner.Mustache.parse("Hello {{name}}")
{:ok, [{:text, "Hello "}, {:variable, ["name"], %{line: 1, col: 7}}]}
iex> PtcRunner.Mustache.parse("{{#items}}{{/items}}")
{:ok, [{:section, "items", [], %{line: 1, col: 1}}]}
iex> PtcRunner.Mustache.parse("{{#items}}")
{:error, "unclosed section 'items' opened at line 1, col 1"}
Convenience: parse and expand in one call.
Examples
iex> PtcRunner.Mustache.render("Hello {{name}}", %{name: "Alice"})
{:ok, "Hello Alice"}
iex> PtcRunner.Mustache.render("{{#items}}{{name}} {{/items}}", %{items: [%{name: "A"}, %{name: "B"}]})
{:ok, "A B "}