template

package module
v0.8.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 4, 2026 License: MIT Imports: 20 Imported by: 0

README

template

Go Version

A Go template engine with Django-style control flow, Liquid-style filters, and optional layout-aware HTML rendering.

For development guidelines and project conventions, see CLAUDE.md.

Features

  • One entry point: Configure a single Engine for source-string parsing or loader-backed rendering.
  • Two output modes: FormatText for raw text, FormatHTML for automatic escaping of {{ expr }} output.
  • Layout on demand: WithLayout() enables include, extends, block, raw, and safe.
  • Composable loaders: NewMemoryLoader, NewDirLoader, NewFSLoader, and NewChainLoader for tests, embedded assets, and override layers.
  • Sandboxed disk reads: NewDirLoader uses os.Root so template lookups cannot escape the configured directory.
  • Typed diagnostics: *RenderError carries the failing template name, line, column, and a sentinel cause for errors.Is / errors.As.
  • Engine-local extensions: Register filters and tags on a single engine without touching global state.

Installation

go get github.com/kaptinlin/template

Requires Go 1.26+.

Quick Start

package main

import (
	"fmt"
	"log"

	"github.com/kaptinlin/template"
)

func main() {
	engine := template.New()
	tmpl, err := engine.ParseString("Hello, {{ name | upcase }}!")
	if err != nil {
		log.Fatal(err)
	}

	out, err := tmpl.Render(template.Data{"name": "alice"})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(out) // Hello, ALICE!
}

Core Concepts

Task API Notes
Parse a source string Engine.ParseString + Template.Render / Template.RenderTo Best for one-off templates
Render named templates WithLoader(...) + Engine.Render / Engine.RenderTo Compiles and caches by loader-resolved name
Choose output semantics WithFormat(FormatText) / WithFormat(FormatHTML) FormatHTML auto-escapes {{ expr }} output
Enable layout syntax WithLayout() Turns on include, extends, block, raw, and safe
Provide shared defaults WithDefaults(Data) Render-time keys override engine defaults
Extend behavior RegisterFilter, ReplaceFilter, RegisterTag, ReplaceTag Scoped to the engine instance

Loader-Backed Rendering

Use a loader when templates live outside the source string:

loader := template.NewMemoryLoader(map[string]string{
	"base.html": `<h1>{{ page.title }}</h1>{% block content %}{% endblock %}`,
	"page.html": `{% extends "base.html" %}{% block content %}{{ page.content }}{% endblock %}`,
})

engine := template.New(
	template.WithLoader(loader),
	template.WithFormat(template.FormatHTML),
	template.WithLayout(),
)

out, err := engine.Render("page.html", template.Data{
	"page": map[string]any{
		"title":   "Hello <world>",
		"content": "<p>escaped</p>",
	},
})
if err != nil {
	log.Fatal(err)
}

fmt.Println(out)
// <h1>Hello &lt;world&gt;</h1>&lt;p&gt;escaped&lt;/p&gt;

For runnable programs, see the examples below.

Errors and Diagnostics

Parse-time failures return *ParseError. Render-time failures return *RenderError, which carries the failing template name, 1-based line and column, and the underlying sentinel.

out, err := engine.Render("page.html", data)
if err != nil {
	var re *template.RenderError
	if errors.As(err, &re) {
		log.Printf("%s:%d:%d: %v", re.Template, re.Line, re.Col, re.Cause)
	}
	if errors.Is(err, template.ErrFilterNotFound) {
		// branch on the sentinel category
	}
	return err
}

Sentinels live in errors.go and are matched with errors.Is. The human-readable RenderError.Error() format is not part of the contract; read the fields for stable output.

Extension Points

Need API
Custom filter Engine.RegisterFilter, Engine.ReplaceFilter, Engine.MustRegisterFilter
Custom tag Engine.RegisterTag, Engine.ReplaceTag, Engine.MustRegisterTag
Custom loader Implement Loader and pass it through WithLoader(...)
Direct runtime control Template.Execute with RenderContext
Trusted HTML SafeString or `

Examples

Runnable examples live in examples/:

Example Path What it shows
Basic usage examples/usage Parse and render a source string through Engine
Custom filters examples/custom_filters Register an engine-local filter
Custom tags examples/custom_tags Register an engine-local tag parser
HTML layouts examples/layout WithLayout(), FormatHTML, includes, extends, and block.super
Text generation examples/multifile_text FormatText with loader-backed multi-file output
Secret redaction examples/secret_redaction Redact rendered output at the writer or filter boundary

Run any example with:

go run ./examples/<name>

Documentation

Development

task test           # Run all tests with race detection
task test-coverage  # Generate coverage.out and coverage.html
task bench          # Run benchmarks
task fmt            # Run configured Go formatters
task vet            # Run go vet ./...
task lint           # Run golangci-lint and go mod tidy checks
task verify         # Run deps, fmt, vet, lint, and test

Contributing

Contributions are welcome. See CONTRIBUTING.md for design boundaries, documentation expectations, and pull request guidance.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Overview

Package template provides a lightweight template engine with Django/Jinja-style syntax.

Package template provides a Go template engine built around a single core concept: Engine.

Use New with WithLoader, WithFormat, WithLayout, and WithFeatures to define how templates are loaded, what output semantics they use, and which optional language features are enabled.

loader, _ := template.NewDirLoader("./templates")
engine := template.New(
	template.WithLoader(loader),
	template.WithFormat(template.FormatHTML),
	template.WithLayout(),
	template.WithDefaults(template.Data{"site": siteData}),
)
_ = engine.RenderTo("layouts/blog.html", os.Stdout, template.Data{"page": pageData})

Core design rules:

Supported syntax summary:

  • Variable interpolation: {{ variable }}
  • Filters: {{ variable | filter:arg }}
  • Control structures: {% if %}, {% for %}
  • Comments: {# ... #}
  • (FeatureLayout only) {% include "x" [with k=v] [only] [if_exists] %}
  • (FeatureLayout only) {% extends "parent" %} + {% block name %}...{% endblock %}
  • (FeatureLayout only) {{ block.super }}
  • (FeatureLayout only) {% raw %}...{% endraw %}
  • (FeatureLayout only) {{ x | safe }} and (FormatHTML only) auto-escape of {{ x }}

For detailed examples, see the examples/ directory.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrTemplateNotFound      = errors.New("template not found")
	ErrInvalidTemplateName   = errors.New("invalid template name")
	ErrIncludeDepthExceeded  = errors.New("include depth exceeded")
	ErrExtendsDepthExceeded  = errors.New("extends depth exceeded")
	ErrCircularExtends       = errors.New("circular extends detected")
	ErrExtendsNotFirst       = errors.New("extends must be the first tag")
	ErrExtendsPathNotLiteral = errors.New("extends path must be a string literal")
	ErrBlockRedefined        = errors.New("block redefined in same template")
	ErrUnclosedRaw           = errors.New("unclosed raw block")
	ErrIncludePathNotString  = errors.New("include path did not evaluate to string")
)

ErrTemplateNotFound indicates a loader could not locate the named template. ErrInvalidTemplateName indicates the template name failed fs.ValidPath validation (contains "..", is absolute, contains NUL, backslash, or similar). ErrIncludeDepthExceeded indicates include nesting exceeded the hard limit. ErrExtendsDepthExceeded indicates the extends chain exceeded the hard limit. ErrCircularExtends indicates a cycle was detected in the extends chain. ErrExtendsNotFirst indicates an extends tag appeared after other content. ErrExtendsPathNotLiteral indicates an extends path was an expression, not a string literal. ErrBlockRedefined indicates the same block name appeared twice in one template. ErrUnclosedRaw indicates a raw block was not terminated with endraw. ErrIncludePathNotString indicates a dynamic include path evaluated to a non-string value.

View Source
var (
	ErrContextKeyNotFound     = errors.New("key not found in context")
	ErrContextInvalidKeyType  = errors.New("invalid key type for navigation")
	ErrContextIndexOutOfRange = errors.New("index out of range in context")
)

ErrContextKeyNotFound indicates a key was not found in the render data. ErrContextInvalidKeyType indicates an invalid key type during context navigation. ErrContextIndexOutOfRange indicates an index out of range during context navigation.

View Source
var (
	ErrFilterNotFound               = errors.New("filter not found")
	ErrFilterExecutionFailed        = errors.New("filter execution failed")
	ErrFilterInputInvalid           = errors.New("filter input is invalid")
	ErrFilterArgsInvalid            = errors.New("filter arguments are invalid")
	ErrFilterInputEmpty             = errors.New("filter input is empty")
	ErrFilterInputNotSlice          = errors.New("filter input is not a slice")
	ErrFilterInputNotNumeric        = errors.New("filter input is not numeric")
	ErrFilterInputInvalidTimeFormat = errors.New("filter input has an invalid time format")
	ErrFilterInputUnsupportedType   = errors.New("filter input is of an unsupported type")
	ErrInsufficientArgs             = errors.New("insufficient arguments provided")
	ErrInvalidFilterName            = errors.New("invalid filter name")
	ErrUnknownFilterArgumentType    = errors.New("unknown argument type")
	ErrExpectedFilterName           = errors.New("expected filter name after '|'")
)

ErrFilterNotFound indicates a referenced filter does not exist. ErrFilterExecutionFailed indicates a filter failed during execution. ErrFilterInputInvalid indicates the filter received invalid input. ErrFilterArgsInvalid indicates the filter received invalid arguments. ErrFilterInputEmpty indicates the filter received empty input. ErrFilterInputNotSlice indicates the filter expected a slice input. ErrFilterInputNotNumeric indicates the filter expected numeric input. ErrFilterInputInvalidTimeFormat indicates the filter received an invalid time format. ErrFilterInputUnsupportedType indicates the filter received an unsupported input type. ErrInsufficientArgs indicates insufficient arguments were provided to a filter. ErrInvalidFilterName indicates an invalid filter name was used. ErrUnknownFilterArgumentType indicates an unknown argument type was passed to a filter. ErrExpectedFilterName indicates a filter name was expected after the pipe operator.

View Source
var (
	ErrUnexpectedCharacter = errors.New("unexpected character")
	ErrUnterminatedString  = errors.New("unterminated string literal")
)

ErrUnexpectedCharacter indicates the lexer encountered an unexpected character. ErrUnterminatedString indicates a string literal was not properly closed.

View Source
var (
	ErrInvalidNumber   = errors.New("invalid number")
	ErrExpectedRParen  = errors.New("expected ')'")
	ErrUnexpectedToken = errors.New("unexpected token")
	ErrUnknownNodeType = errors.New("unknown node type")
	ErrIntegerOverflow = errors.New("unsigned integer value exceeds maximum int64 value")
)

ErrInvalidNumber indicates the parser encountered an invalid numeric literal. ErrExpectedRParen indicates a closing parenthesis was expected but not found. ErrUnexpectedToken indicates the parser encountered an unexpected token. ErrUnknownNodeType indicates an unknown AST node type was encountered. ErrIntegerOverflow indicates an unsigned integer value exceeds the maximum int64 value.

View Source
var (
	ErrUnsupportedType     = errors.New("unsupported type")
	ErrUnsupportedOperator = errors.New("unsupported operator")
	ErrUnsupportedUnaryOp  = errors.New("unsupported unary operator")
)

ErrUnsupportedType indicates an unsupported type was encountered. ErrUnsupportedOperator indicates an unsupported operator was used. ErrUnsupportedUnaryOp indicates an unsupported unary operator was used.

View Source
var (
	ErrUndefinedVariable     = errors.New("undefined variable")
	ErrUndefinedProperty     = errors.New("undefined property")
	ErrNonStructProperty     = errors.New("cannot access property of non-struct value")
	ErrCannotAccessProperty  = errors.New("cannot access property")
	ErrNonObjectProperty     = errors.New("cannot access property of non-object")
	ErrInvalidVariableAccess = errors.New("invalid variable access")
)

ErrUndefinedVariable indicates a referenced variable is not defined. ErrUndefinedProperty indicates a referenced property is not defined. ErrNonStructProperty indicates a property access was attempted on a non-struct value. ErrCannotAccessProperty indicates a property cannot be accessed. ErrNonObjectProperty indicates a property access was attempted on a non-object value. ErrInvalidVariableAccess indicates an invalid variable access pattern.

View Source
var (
	ErrCannotAddTypes       = errors.New("cannot add values of these types")
	ErrCannotSubtractTypes  = errors.New("cannot subtract values of these types")
	ErrCannotMultiplyTypes  = errors.New("cannot multiply values of these types")
	ErrCannotDivideTypes    = errors.New("cannot divide values of these types")
	ErrCannotModuloTypes    = errors.New("cannot modulo values of these types")
	ErrDivisionByZero       = errors.New("division by zero")
	ErrModuloByZero         = errors.New("modulo by zero")
	ErrCannotConvertToBool  = errors.New("cannot convert type to boolean")
	ErrCannotNegate         = errors.New("cannot negate value")
	ErrCannotApplyUnaryPlus = errors.New("cannot apply unary plus")
	ErrCannotCompareTypes   = errors.New("cannot compare values of these types")
)

ErrCannotAddTypes indicates addition is not supported for the given types. ErrCannotSubtractTypes indicates subtraction is not supported for the given types. ErrCannotMultiplyTypes indicates multiplication is not supported for the given types. ErrCannotDivideTypes indicates division is not supported for the given types. ErrCannotModuloTypes indicates modulo is not supported for the given types. ErrDivisionByZero indicates a division by zero was attempted. ErrModuloByZero indicates a modulo by zero was attempted. ErrCannotConvertToBool indicates a value cannot be converted to boolean. ErrCannotNegate indicates a value cannot be negated. ErrCannotApplyUnaryPlus indicates unary plus cannot be applied to the value. ErrCannotCompareTypes indicates comparison is not supported for the given types.

View Source
var (
	ErrInvalidIndexType      = errors.New("invalid index type")
	ErrInvalidArrayIndex     = errors.New("invalid array index")
	ErrIndexOutOfRange       = errors.New("index out of range")
	ErrCannotIndexNil        = errors.New("cannot index nil")
	ErrTypeNotIndexable      = errors.New("type is not indexable")
	ErrCannotGetKeyFromNil   = errors.New("cannot get key from nil")
	ErrTypeNotMap            = errors.New("type is not a map")
	ErrCannotGetFieldFromNil = errors.New("cannot get field from nil")
	ErrStructHasNoField      = errors.New("struct has no field")
	ErrTypeHasNoField        = errors.New("type has no field")
	ErrUnsupportedArrayType  = errors.New("unsupported array type")
)

ErrInvalidIndexType indicates an invalid index type was used. ErrInvalidArrayIndex indicates an invalid array index was used. ErrIndexOutOfRange indicates an index is out of range. ErrCannotIndexNil indicates an indexing operation was attempted on nil. ErrTypeNotIndexable indicates the type does not support indexing. ErrCannotGetKeyFromNil indicates a key lookup was attempted on nil. ErrTypeNotMap indicates the type is not a map. ErrCannotGetFieldFromNil indicates a field access was attempted on nil. ErrStructHasNoField indicates the struct does not have the requested field. ErrTypeHasNoField indicates the type does not have the requested field. ErrUnsupportedArrayType indicates an unsupported array type was encountered.

View Source
var (
	ErrUnsupportedCollectionType = errors.New("unsupported collection type for for loop")
	ErrTypeNotIterable           = errors.New("type is not iterable")
	ErrTypeHasNoLength           = errors.New("type has no length")
)

ErrUnsupportedCollectionType indicates the collection type is not supported in a for loop. ErrTypeNotIterable indicates the type does not support iteration. ErrTypeHasNoLength indicates the type does not support the length operation.

View Source
var (
	ErrCannotConvertNilToInt   = errors.New("cannot convert nil to int")
	ErrCannotConvertToInt      = errors.New("cannot convert value to int")
	ErrCannotConvertNilToFloat = errors.New("cannot convert nil to float")
	ErrCannotConvertToFloat    = errors.New("cannot convert value to float")
	ErrExpectedSliceOrArray    = errors.New("expected slice or array")
)

ErrCannotConvertNilToInt indicates nil cannot be converted to int. ErrCannotConvertToInt indicates the value cannot be converted to int. ErrCannotConvertNilToFloat indicates nil cannot be converted to float. ErrCannotConvertToFloat indicates the value cannot be converted to float.

View Source
var (
	ErrBreakOutsideLoop    = errors.New("break statement outside of loop")
	ErrContinueOutsideLoop = errors.New("continue statement outside of loop")
)

ErrBreakOutsideLoop indicates a break statement was used outside of a loop. ErrContinueOutsideLoop indicates a continue statement was used outside of a loop.

View Source
var (
	ErrTagAlreadyRegistered = errors.New("tag already registered")

	ErrMultipleElseStatements         = errors.New("multiple 'else' statements found in if block, use 'elif' for additional conditions")
	ErrUnexpectedTokensAfterCondition = errors.New("unexpected tokens after condition")
	ErrElseNoArgs                     = errors.New("else does not take arguments")
	ErrEndifNoArgs                    = errors.New("endif does not take arguments")
	ErrElifAfterElse                  = errors.New("elif cannot appear after else")

	ErrExpectedVariable                = errors.New("expected variable name")
	ErrExpectedSecondVariable          = errors.New("expected second variable name after comma")
	ErrExpectedInKeyword               = errors.New("expected 'in' keyword")
	ErrUnexpectedTokensAfterCollection = errors.New("unexpected tokens after collection")
	ErrEndforNoArgs                    = errors.New("endfor does not take arguments")

	ErrBreakNoArgs    = errors.New("break does not take arguments")
	ErrContinueNoArgs = errors.New("continue does not take arguments")
)

ErrTagAlreadyRegistered indicates a tag with the same name is already registered.

ErrMultipleElseStatements indicates multiple else clauses were found in an if block. ErrUnexpectedTokensAfterCondition indicates unexpected tokens after a condition expression. ErrElseNoArgs indicates the else tag received unexpected arguments. ErrEndifNoArgs indicates the endif tag received unexpected arguments. ErrElifAfterElse indicates an elif clause appeared after an else clause.

ErrExpectedVariable indicates a variable name was expected but not found. ErrExpectedSecondVariable indicates a second variable name was expected after a comma. ErrExpectedInKeyword indicates the "in" keyword was expected but not found. ErrUnexpectedTokensAfterCollection indicates unexpected tokens after a collection expression. ErrEndforNoArgs indicates the endfor tag received unexpected arguments.

ErrBreakNoArgs indicates the break tag received unexpected arguments. ErrContinueNoArgs indicates the continue tag received unexpected arguments.

Functions

func IsKeyword added in v0.4.0

func IsKeyword(ident string) bool

IsKeyword reports whether ident is a reserved keyword.

func IsSymbol added in v0.4.0

func IsSymbol(s string) bool

IsSymbol reports whether s is a valid operator or punctuation symbol.

func ValidateName added in v0.6.0

func ValidateName(name string) error

ValidateName checks that name is safe to use as a template path. It rejects:

  • anything fs.ValidPath rejects (empty element, "..", absolute, trailing /)
  • backslash (Windows path separator; forces forward-slash discipline)
  • NUL byte (path injection)

Loader implementations must call this on every name they receive.

Types

type BinaryOpNode added in v0.4.0

type BinaryOpNode struct {
	Operator string
	Left     Expression
	Right    Expression
	Line     int
	Col      int
}

BinaryOpNode represents a binary operation.

func NewBinaryOpNode added in v0.4.0

func NewBinaryOpNode(operator string, left, right Expression, line, col int) *BinaryOpNode

NewBinaryOpNode returns a new BinaryOpNode.

func (*BinaryOpNode) Evaluate added in v0.4.0

func (n *BinaryOpNode) Evaluate(ctx *RenderContext) (*Value, error)

Evaluate computes the binary operation result.

func (*BinaryOpNode) Position added in v0.4.0

func (n *BinaryOpNode) Position() (int, int)

Position returns the position of the BinaryOpNode.

func (*BinaryOpNode) String added in v0.4.0

func (n *BinaryOpNode) String() string

String returns a debug representation of the BinaryOpNode.

type BlockNode added in v0.6.0

type BlockNode struct {
	Name string
	Body []Node
	Line int
	Col  int
}

BlockNode is a forward declaration stub filled in when {% block %} is implemented. Defined here so Template.blocks can reference it without import cycles.

func (*BlockNode) Execute added in v0.6.0

func (n *BlockNode) Execute(ctx *RenderContext, w io.Writer) error

Execute renders this block, resolving overrides across the extends chain. If the current template is not part of a chain (or this block is inside an included partial), the block simply renders its own body inline.

The {{ block.super }} expression is supported by injecting a "block" variable into the render context whose "super" field is the pre-rendered parent block body (already a SafeString so it survives HTML auto-escape untouched).

func (*BlockNode) Position added in v0.6.0

func (n *BlockNode) Position() (int, int)

Position returns the source position of the block.

func (*BlockNode) String added in v0.6.0

func (n *BlockNode) String() string

String returns a debug representation.

type BreakError added in v0.4.0

type BreakError struct{}

BreakError signals loop termination.

func (*BreakError) Error added in v0.4.0

func (e *BreakError) Error() string

Error implements the error interface.

type BreakNode added in v0.4.0

type BreakNode struct {
	Line int
	Col  int
}

BreakNode represents a {% break %} statement.

func (*BreakNode) Execute added in v0.4.0

func (n *BreakNode) Execute(_ *RenderContext, _ io.Writer) error

Execute signals loop termination via BreakError.

func (*BreakNode) Position added in v0.4.0

func (n *BreakNode) Position() (int, int)

Position returns the position of the BreakNode.

func (*BreakNode) String added in v0.4.0

func (n *BreakNode) String() string

String returns a debug representation of the BreakNode.

type ContinueError added in v0.4.0

type ContinueError struct{}

ContinueError signals loop continuation.

func (*ContinueError) Error added in v0.4.0

func (e *ContinueError) Error() string

Error implements the error interface.

type ContinueNode added in v0.4.0

type ContinueNode struct {
	Line int
	Col  int
}

ContinueNode represents a {% continue %} statement.

func (*ContinueNode) Execute added in v0.4.0

func (n *ContinueNode) Execute(_ *RenderContext, _ io.Writer) error

Execute signals loop continuation via ContinueError.

func (*ContinueNode) Position added in v0.4.0

func (n *ContinueNode) Position() (int, int)

Position returns the position of the ContinueNode.

func (*ContinueNode) String added in v0.4.0

func (n *ContinueNode) String() string

String returns a debug representation of the ContinueNode.

type Data added in v0.7.0

type Data map[string]any

Data stores template variables as a string-keyed map. Values can be of any type. Dot-notation (e.g., "user.name") is supported for nested access.

func NewData added in v0.7.0

func NewData() Data

NewData creates and returns a new empty Data.

func (Data) Get added in v0.7.0

func (c Data) Get(key string) (any, error)

Get retrieves a value from the Data by key. Dot-separated keys (e.g., "user.profile.name") navigate nested structures. Array indices are supported (e.g., "items.0").

Get returns ErrContextKeyNotFound, ErrContextIndexOutOfRange, or ErrContextInvalidKeyType on failure.

func (Data) Set added in v0.7.0

func (c Data) Set(key string, value any)

Set inserts a value into the Data with the specified key. Dot-notation (e.g., "user.address.city") creates nested map structures. Top-level keys preserve original data types; nested keys use map[string]any. Empty keys are silently ignored.

type DataBuilder added in v0.7.0

type DataBuilder struct {
	// contains filtered or unexported fields
}

DataBuilder provides a fluent API for building a Data with error collection.

func NewDataBuilder added in v0.7.0

func NewDataBuilder() *DataBuilder

NewDataBuilder creates a new DataBuilder for fluent Data construction.

ctx, err := NewDataBuilder().
    KeyValue("name", "John").
    Struct(user).
    Build()

func (*DataBuilder) Build added in v0.7.0

func (cb *DataBuilder) Build() (Data, error)

Build returns the constructed Data and any collected errors. Errors from DataBuilder.KeyValue or DataBuilder.Struct operations are joined into a single error.

func (*DataBuilder) KeyValue added in v0.7.0

func (cb *DataBuilder) KeyValue(key string, value any) *DataBuilder

KeyValue sets a key-value pair and returns the builder for chaining.

builder := NewDataBuilder().
    KeyValue("name", "John").
    KeyValue("age", 30)

func (*DataBuilder) Struct added in v0.7.0

func (cb *DataBuilder) Struct(v any) *DataBuilder

Struct expands struct fields into the Data using JSON serialization. Fields are flattened to top-level keys based on their json tags. Nested structs are preserved as nested maps accessible via dot notation. If serialization fails, the error is collected and returned by DataBuilder.Build.

type Engine added in v0.7.0

type Engine struct {
	// contains filtered or unexported fields
}

Engine is the entry point for the loader-backed template system.

An Engine holds a loader, compile cache, rendering format, feature flags, and per-engine tag/filter registries. Engines are safe for concurrent use: compiled templates are cached and treated as read-only after compilation.

func New added in v0.7.0

func New(opts ...EngineOption) *Engine

New constructs an Engine with the provided options.

func (*Engine) Clone added in v0.7.0

func (e *Engine) Clone(opts ...EngineOption) *Engine

Clone copies engine configuration into a fresh Engine with an empty cache.

func (*Engine) Filters added in v0.7.0

func (e *Engine) Filters() *Registry

Filters returns the engine-local filter registry layer.

func (*Engine) HasFeature added in v0.7.0

func (e *Engine) HasFeature(feature Feature) bool

HasFeature reports whether the engine has an optional feature enabled.

func (*Engine) Load added in v0.7.0

func (e *Engine) Load(name string) (*Template, error)

Load resolves, compiles, and caches the named template.

func (*Engine) MustRegisterFilter added in v0.7.0

func (e *Engine) MustRegisterFilter(name string, fn FilterFunc)

MustRegisterFilter adds or replaces an engine-local filter and panics on nil.

func (*Engine) MustRegisterTag added in v0.7.0

func (e *Engine) MustRegisterTag(name string, parser TagParser)

MustRegisterTag registers an engine-local tag parser and panics on duplicate registration or nil parser.

func (*Engine) ParseString added in v0.7.0

func (e *Engine) ParseString(source string) (*Template, error)

ParseString compiles a template source string in the context of this engine.

Example
package main

import (
	"fmt"
	"log"

	"github.com/kaptinlin/template"
)

func main() {
	engine := template.New()
	tmpl, err := engine.ParseString("Hello, {{ name|upcase }}!")
	if err != nil {
		log.Fatal(err)
	}

	out, err := tmpl.Render(template.Data{"name": "alice"})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(out)
}
Output:
Hello, ALICE!

func (*Engine) RegisterFilter added in v0.7.0

func (e *Engine) RegisterFilter(name string, fn FilterFunc)

RegisterFilter adds or replaces an engine-local filter.

func (*Engine) RegisterTag added in v0.7.0

func (e *Engine) RegisterTag(name string, parser TagParser) error

RegisterTag adds an engine-local tag parser. Duplicate names return ErrTagAlreadyRegistered.

func (*Engine) Render added in v0.7.0

func (e *Engine) Render(name string, data Data) (string, error)

Render loads the named template and returns its output as a string.

Example
package main

import (
	"fmt"
	"log"

	"github.com/kaptinlin/template"
)

func main() {
	loader := template.NewMemoryLoader(map[string]string{
		"base.html": `<h1>{{ page.title }}</h1>{% block content %}{% endblock %}`,
		"page.html": `{% extends "base.html" %}{% block content %}{{ page.content }} {{ page.safe | safe }}{% endblock %}`,
	})

	engine := template.New(
		template.WithLoader(loader),
		template.WithFormat(template.FormatHTML),
		template.WithLayout(),
	)

	out, err := engine.Render("page.html", template.Data{
		"page": map[string]any{
			"title":   "Hello <world>",
			"content": "<p>escaped</p>",
			"safe":    template.SafeString("<p>trusted</p>"),
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(out)
}
Output:
<h1>Hello &lt;world&gt;</h1>&lt;p&gt;escaped&lt;/p&gt; <p>trusted</p>

func (*Engine) RenderTo added in v0.7.0

func (e *Engine) RenderTo(name string, w io.Writer, data Data) error

RenderTo loads the named template and writes its output to w. data is merged with any defaults configured via WithDefaults; data keys take precedence over defaults.

func (*Engine) ReplaceFilter added in v0.7.0

func (e *Engine) ReplaceFilter(name string, fn FilterFunc)

ReplaceFilter overwrites an engine-local filter.

func (*Engine) ReplaceTag added in v0.7.0

func (e *Engine) ReplaceTag(name string, parser TagParser)

ReplaceTag overwrites an engine-local tag parser.

func (*Engine) Reset added in v0.7.0

func (e *Engine) Reset()

Reset clears the template cache.

func (*Engine) Tags added in v0.7.0

func (e *Engine) Tags() *TagRegistry

Tags returns the engine-local tag registry layer.

type EngineOption added in v0.7.0

type EngineOption func(*Engine)

EngineOption configures an Engine at construction time.

func WithDefaults added in v0.7.0

func WithDefaults(g Data) EngineOption

WithDefaults injects variables available to every render call. Render-time ctx keys override defaults on conflict.

func WithFeatures added in v0.7.0

func WithFeatures(features ...Feature) EngineOption

WithFeatures enables optional language features.

func WithFilters added in v0.7.0

func WithFilters(r *Registry) EngineOption

WithFilters replaces the engine-local filter registry layer.

func WithFormat added in v0.7.0

func WithFormat(format Format) EngineOption

WithFormat configures how output is rendered.

func WithLayout added in v0.7.0

func WithLayout() EngineOption

WithLayout enables layout features such as include, extends, block, raw, and safe-aware engine behavior.

func WithLoader added in v0.7.0

func WithLoader(loader Loader) EngineOption

WithLoader configures the Engine loader used by Load and Render.

func WithTags added in v0.7.0

func WithTags(r *TagRegistry) EngineOption

WithTags replaces the engine-local tag registry layer.

type ExprParser added in v0.4.0

type ExprParser struct {
	// contains filtered or unexported fields
}

ExprParser parses expressions from a token stream. It handles operator precedence, filters, property access, etc.

func NewExprParser added in v0.4.0

func NewExprParser(tokens []*Token) *ExprParser

NewExprParser creates a new expression parser.

func (*ExprParser) ParseExpression added in v0.4.0

func (p *ExprParser) ParseExpression() (Expression, error)

ParseExpression parses a complete expression. This is the entry point for expression parsing.

type Expression added in v0.4.0

type Expression interface {
	Node
	Evaluate(ctx *RenderContext) (*Value, error)
}

Expression is the interface for all expression nodes. Expressions are evaluated to produce values.

type ExtendsNode added in v0.6.0

type ExtendsNode struct {
	Line int
	Col  int
}

ExtendsNode is a marker for {% extends "parent" %}. It produces no output directly; inheritance is handled by Template.Execute when it detects a non-nil parent field.

func (*ExtendsNode) Execute added in v0.6.0

func (n *ExtendsNode) Execute(_ *RenderContext, _ io.Writer) error

Execute is a no-op. The parent relationship is established at parse time and consumed by Template.Execute.

func (*ExtendsNode) Position added in v0.6.0

func (n *ExtendsNode) Position() (int, int)

Position returns the source position of the extends tag.

func (*ExtendsNode) String added in v0.6.0

func (n *ExtendsNode) String() string

String returns a debug representation.

type Feature added in v0.7.0

type Feature uint8

Feature is an optional language capability that can be enabled per engine.

const (
	// FeatureLayout enables include, extends, block, raw, and safe-aware engine behavior.
	FeatureLayout Feature = 1 << iota
)

type FilterFunc

type FilterFunc func(value any, args ...any) (any, error)

FilterFunc represents the signature of functions that can be applied as filters.

type FilterNode added in v0.4.0

type FilterNode struct {
	Expr Expression
	Name string
	Args []Expression
	Line int
	Col  int
}

FilterNode represents a filter application.

func NewFilterNode added in v0.4.0

func NewFilterNode(expr Expression, name string, args []Expression, line, col int) *FilterNode

NewFilterNode returns a new FilterNode.

func (*FilterNode) Evaluate added in v0.4.0

func (n *FilterNode) Evaluate(ctx *RenderContext) (*Value, error)

Evaluate applies the named filter to the expression value.

Filter lookup consults the per-engine filter registry first, falling back to the built-in registry. This gives each engine access to its own feature-gated filters and format-specific overrides.

func (*FilterNode) Position added in v0.4.0

func (n *FilterNode) Position() (int, int)

Position returns the position of the FilterNode.

func (*FilterNode) String added in v0.4.0

func (n *FilterNode) String() string

String returns a debug representation of the FilterNode.

type ForNode added in v0.4.0

type ForNode struct {
	Vars       []string
	Collection Expression
	Body       []Node
	Line       int
	Col        int
}

ForNode represents a for loop.

func (*ForNode) Execute added in v0.4.0

func (n *ForNode) Execute(ctx *RenderContext, w io.Writer) error

Execute evaluates the iterable and executes the loop body for each element.

func (*ForNode) Position added in v0.4.0

func (n *ForNode) Position() (int, int)

Position returns the position of the ForNode.

func (*ForNode) String added in v0.4.0

func (n *ForNode) String() string

String returns a debug representation of the ForNode.

type Format added in v0.7.0

type Format uint8

Format controls output semantics during rendering.

const (
	// FormatText renders output without automatic escaping.
	FormatText Format = iota
	// FormatHTML automatically HTML-escapes {{ expr }} output unless it is safe.
	FormatHTML
)

type IfBranch added in v0.4.0

type IfBranch struct {
	Condition Expression
	Body      []Node
}

IfBranch represents a single if or elif branch.

type IfNode added in v0.4.0

type IfNode struct {
	Branches []IfBranch
	ElseBody []Node
	Line     int
	Col      int
}

IfNode represents an if-elif-else conditional block.

func (*IfNode) Execute added in v0.4.0

func (n *IfNode) Execute(ctx *RenderContext, w io.Writer) error

Execute runs the first truthy branch, or the else block if no branch matches.

func (*IfNode) Position added in v0.4.0

func (n *IfNode) Position() (int, int)

Position returns the position of the IfNode.

func (*IfNode) String added in v0.4.0

func (n *IfNode) String() string

String returns a debug representation of the IfNode.

type IncludeNode added in v0.6.0

type IncludeNode struct {
	Line int
	Col  int
	// contains filtered or unexported fields
}

IncludeNode represents an {% include %} statement.

Shapes:

  • Static path (string literal), resolved at parse time: prepared != nil.
  • Parse-time circular static path: prepared == nil, staticName holds the literal for runtime lookup.
  • Dynamic path (expression): pathExpr != nil.

Options:

  • withPairs: {% include "x" with k1=expr1 k2=expr2 %}
  • only: {% include "x" only %} — fully isolates the child context, excluding parent variables AND defaults.
  • ifExists: {% include "x" if_exists %} — missing template is a no-op instead of an error.

func (*IncludeNode) Execute added in v0.6.0

func (n *IncludeNode) Execute(ctx *RenderContext, w io.Writer) error

Execute renders the included template. The parser only produces IncludeNode inside an Engine with layout enabled, so ctx.engine is guaranteed non-nil by the time we reach here.

func (*IncludeNode) Position added in v0.6.0

func (n *IncludeNode) Position() (int, int)

Position returns the source position of the include tag.

func (*IncludeNode) String added in v0.6.0

func (n *IncludeNode) String() string

String returns a debug representation.

type Lexer added in v0.2.0

type Lexer struct {
	// contains filtered or unexported fields
}

Lexer performs lexical analysis on template input.

func NewLexer added in v0.4.0

func NewLexer(input string) *Lexer

NewLexer creates a new Lexer for the given input.

func (*Lexer) Tokenize added in v0.4.0

func (l *Lexer) Tokenize() ([]*Token, error)

Tokenize performs lexical analysis and returns all tokens.

type LexerError added in v0.4.0

type LexerError struct {
	Message string
	Line    int
	Col     int
}

LexerError represents a lexical analysis error with position information.

func (*LexerError) Error added in v0.4.0

func (e *LexerError) Error() string

Error implements the error interface.

type LiteralNode added in v0.4.0

type LiteralNode struct {
	Value any
	Line  int
	Col   int
}

LiteralNode represents a literal value (string, number, boolean).

func NewLiteralNode added in v0.4.0

func NewLiteralNode(value any, line, col int) *LiteralNode

NewLiteralNode returns a new LiteralNode.

func (*LiteralNode) Evaluate added in v0.4.0

func (n *LiteralNode) Evaluate(_ *RenderContext) (*Value, error)

Evaluate returns the literal value wrapped in a Value.

func (*LiteralNode) Position added in v0.4.0

func (n *LiteralNode) Position() (int, int)

Position returns the position of the LiteralNode.

func (*LiteralNode) String added in v0.4.0

func (n *LiteralNode) String() string

String returns a debug representation of the LiteralNode.

type Loader added in v0.6.0

type Loader interface {
	Open(name string) (source string, resolved string, err error)
}

Loader locates and loads template source code by name.

Implementations must validate the name with ValidateName (which adds backslash and NUL rejection on top of fs.ValidPath) and return ErrInvalidTemplateName for any path that fails the check. Unknown names must return ErrTemplateNotFound.

The returned resolved name is used as the cache key and should be stable and unique within a loader (e.g., include a layer prefix when chained).

func NewChainLoader added in v0.6.0

func NewChainLoader(loaders ...Loader) Loader

NewChainLoader returns a Loader that queries the given loaders in order and returns the first one that has the requested template.

Chain loaders are typically used to implement override layers like user > theme > builtin. Each hit's resolved name is prefixed with the layer index so the same name in different layers produces distinct cache keys in an Engine.

func NewDirLoader added in v0.6.0

func NewDirLoader(dir string) (Loader, error)

NewDirLoader returns a Loader that reads templates from the given local directory, sandboxed by os.Root. Symbolic links cannot escape the root: following any link whose target lies outside dir results in an error.

This is the default, recommended way to load templates from disk.

For development workflows that deliberately require symlink following (theme dev, monorepo sharing), use NewFSLoader with os.DirFS and accept responsibility for the relaxed sandbox.

func NewFSLoader added in v0.6.0

func NewFSLoader(fsys fs.FS) Loader

NewFSLoader wraps any fs.FS as a Loader. Intended for already- sandboxed filesystems such as embed.FS, testing/fstest.MapFS, and archive/zip.Reader.

Warning: if you pass a non-sandboxed fs.FS (for example os.DirFS pointing at a real directory) the library cannot prevent symbolic links from escaping. Prefer NewDirLoader for local directories unless you deliberately need this escape hatch.

func NewMemoryLoader added in v0.6.0

func NewMemoryLoader(files map[string]string) Loader

NewMemoryLoader returns a Loader that serves templates from an in-memory map. Intended for tests and small pre-registered sets.

type LoopContext added in v0.2.8

type LoopContext struct {
	Index      int
	Counter    int
	Revindex   int
	Revcounter int
	First      bool
	Last       bool
	Length     int
	Parent     *LoopContext
}

LoopContext represents loop metadata for templates.

type Node

type Node interface {
	// Position returns the line and column where this node starts.
	Position() (line, col int)

	// String returns a string representation of the node for debugging.
	String() string
}

Node is the interface that all AST nodes must implement. Each node represents a part of the template syntax tree.

type OutputNode added in v0.4.0

type OutputNode struct {
	Expr Expression
	Line int
	Col  int
}

OutputNode represents a variable output {{ ... }}.

func NewOutputNode added in v0.4.0

func NewOutputNode(expr Expression, line, col int) *OutputNode

NewOutputNode returns a new OutputNode.

func (*OutputNode) Execute added in v0.4.0

func (n *OutputNode) Execute(ctx *RenderContext, w io.Writer) error

Execute evaluates the expression and writes its string value.

When the render context has autoescape enabled (FormatHTML engine rendering), the output is HTML-escaped UNLESS the underlying Go value is a SafeString. In the non-autoescape path (text engine or standalone Compile), SafeString is treated as a plain string.

func (*OutputNode) Position added in v0.4.0

func (n *OutputNode) Position() (int, int)

Position returns the position of the OutputNode.

func (*OutputNode) String added in v0.4.0

func (n *OutputNode) String() string

String returns a debug representation of the OutputNode.

type ParseError added in v0.4.0

type ParseError struct {
	Message string
	Line    int
	Col     int
}

ParseError represents a parsing error with source location.

func (*ParseError) Error added in v0.4.0

func (e *ParseError) Error() string

Error returns a human-readable error message with source location.

type Parser

type Parser struct {
	// contains filtered or unexported fields
}

Parser consumes tokens and builds an AST.

func NewParser

func NewParser(tokens []*Token) *Parser

NewParser creates a new Parser.

func (*Parser) Advance added in v0.4.0

func (p *Parser) Advance()

Advance moves to the next token.

func (*Parser) Current added in v0.4.0

func (p *Parser) Current() *Token

Current returns the current token without advancing the parser.

func (*Parser) Engine added in v0.7.0

func (p *Parser) Engine() *Engine

Engine returns the template engine associated with this parser, if any. Tag parsers can use this to load referenced templates at parse time.

func (*Parser) Error added in v0.4.0

func (p *Parser) Error(msg string) error

Error creates a parse error at the current token position.

func (*Parser) Errorf added in v0.4.0

func (p *Parser) Errorf(format string, args ...any) error

Errorf creates a formatted parse error at the current token position.

func (*Parser) ExpectIdentifier added in v0.4.0

func (p *Parser) ExpectIdentifier() (*Token, error)

ExpectIdentifier expects and consumes an identifier token.

func (*Parser) Match added in v0.4.0

func (p *Parser) Match(tokenType TokenType, value string) *Token

Match consumes and returns the current token if type and value match. It returns nil when there is no match.

func (*Parser) Parse

func (p *Parser) Parse() ([]Statement, error)

Parse parses the entire template and returns AST statement nodes.

func (*Parser) ParseExpression added in v0.4.0

func (p *Parser) ParseExpression() (Expression, error)

ParseExpression parses an expression from the current token position.

func (*Parser) ParseUntil added in v0.4.0

func (p *Parser) ParseUntil(endTags ...string) ([]Statement, string, error)

ParseUntil parses nodes until one of the given end tags is encountered.

Returns the parsed nodes, the matched end-tag name, and any error.

func (*Parser) ParseUntilWithArgs added in v0.4.0

func (p *Parser) ParseUntilWithArgs(endTags ...string) ([]Statement, string, *Parser, error)

ParseUntilWithArgs parses nodes until one of the given end tags is encountered, and also returns a parser for the end-tag arguments.

func (*Parser) Remaining added in v0.4.0

func (p *Parser) Remaining() int

Remaining returns the number of unconsumed tokens.

type PropertyAccessNode added in v0.4.0

type PropertyAccessNode struct {
	Object   Expression
	Property string
	Line     int
	Col      int
}

PropertyAccessNode represents property/attribute access.

func NewPropertyAccessNode added in v0.4.0

func NewPropertyAccessNode(object Expression, property string, line, col int) *PropertyAccessNode

NewPropertyAccessNode returns a new PropertyAccessNode.

func (*PropertyAccessNode) Evaluate added in v0.4.0

func (n *PropertyAccessNode) Evaluate(ctx *RenderContext) (*Value, error)

Evaluate returns the property value from the evaluated object.

func (*PropertyAccessNode) Position added in v0.4.0

func (n *PropertyAccessNode) Position() (int, int)

Position returns the position of the PropertyAccessNode.

func (*PropertyAccessNode) String added in v0.4.0

func (n *PropertyAccessNode) String() string

String returns a debug representation of the PropertyAccessNode.

type Registry added in v0.4.0

type Registry struct {
	// contains filtered or unexported fields
}

Registry is a concurrency-safe collection of named filter functions. Use NewRegistry to create an instance, or use the package-level functions that operate on the default registry.

Registry supports an optional parent: when a lookup misses in this registry, the parent is consulted. This allows an Engine to layer its own private filters (like safe and the HTML-aware escape) over the global registry without copying entries.

func NewRegistry added in v0.4.0

func NewRegistry() *Registry

NewRegistry creates an empty filter registry.

func (*Registry) Clone added in v0.7.0

func (r *Registry) Clone() *Registry

Clone returns a shallow copy of the registry and its direct entries. The parent registry reference is preserved.

func (*Registry) Filter added in v0.4.0

func (r *Registry) Filter(name string) (FilterFunc, bool)

Filter returns the filter registered under name and a boolean indicating whether it was found. If not found locally and a parent registry is set, the parent is consulted.

func (*Registry) Has added in v0.4.0

func (r *Registry) Has(name string) bool

Has reports whether a filter with the given name is registered (including in ancestor registries).

func (*Registry) List added in v0.4.0

func (r *Registry) List() []string

List returns the names of all registered filters in sorted order.

func (*Registry) MustRegister added in v0.7.0

func (r *Registry) MustRegister(name string, fn FilterFunc)

MustRegister is an explicit panic-on-failure registration helper.

For Registry this currently matches Registry.Replace, and exists to make bootstrap code read consistently with TagRegistry.MustRegister.

func (*Registry) Register added in v0.4.0

func (r *Registry) Register(name string, fn FilterFunc)

Register adds or replaces a filter function under the given name.

Register preserves the original Registry behavior for compatibility. For new code, prefer Registry.Replace when overwrite semantics are intentional, or introduce a higher-level guard before calling Register.

func (*Registry) Replace added in v0.7.0

func (r *Registry) Replace(name string, fn FilterFunc)

Replace stores fn under name, overwriting any direct existing entry.

Replace panics if fn is nil.

func (*Registry) Unregister added in v0.4.0

func (r *Registry) Unregister(name string)

Unregister removes the filter registered under name. It is a no-op if no such filter exists.

type RenderContext added in v0.7.0

type RenderContext struct {
	Data   Data // render input data
	Locals Data // local bindings (e.g., loop counters)
	// contains filtered or unexported fields
}

RenderContext holds the execution state for template rendering, separating render input data (Data) from local bindings (Locals).

func NewChildContext added in v0.4.0

func NewChildContext(parent *RenderContext) *RenderContext

NewChildContext creates a child RenderContext that shares the parent's Data, copies the Locals for isolated scope, and preserves runtime rendering state.

func NewIsolatedChildContext added in v0.7.0

func NewIsolatedChildContext(parent *RenderContext) *RenderContext

NewIsolatedChildContext creates a child RenderContext with fresh Data/Locals while preserving runtime rendering state.

func NewRenderContext added in v0.7.0

func NewRenderContext(data Data) *RenderContext

NewRenderContext creates a new RenderContext from user data.

func (*RenderContext) Get added in v0.7.0

func (ec *RenderContext) Get(name string) (any, bool)

Get retrieves a variable, checking Locals first, then Data.

func (*RenderContext) Set added in v0.7.0

func (ec *RenderContext) Set(name string, value any)

Set stores a variable in the local bindings.

type RenderError added in v0.8.0

type RenderError struct {
	// Template is the loader-resolved template name where the failure
	// originated; empty for templates parsed via Engine.ParseString.
	Template string

	// Line is the 1-based line where the failing node starts; 0 if unknown.
	Line int

	// Col is the 1-based column where the failing node starts; 0 if unknown.
	Col int

	// Cause is the underlying error. It is always non-nil for a
	// well-formed RenderError and is suitable for errors.Is / errors.As
	// against the sentinels in errors.go.
	Cause error
}

RenderError carries machine-readable context for a render-time failure.

It always wraps an underlying sentinel from errors.go (directly or through fmt.Errorf wrapping). Callers identify the failure mode with errors.Is and recover position information with errors.As:

var re *template.RenderError
if errors.As(err, &re) {
    log.Printf("%s:%d:%d: %v", re.Template, re.Line, re.Col, re.Cause)
}
if errors.Is(err, template.ErrFilterNotFound) {
    // ...
}

The Error() string is human-readable and may evolve; the Template, Line, Col, and Cause fields are part of the public contract and will not change shape across minor versions.

func (*RenderError) Error added in v0.8.0

func (e *RenderError) Error() string

Error returns a human-readable representation. The format is not part of the public contract; consume Template, Line, Col, and Cause directly for stable output.

func (*RenderError) Unwrap added in v0.8.0

func (e *RenderError) Unwrap() error

Unwrap exposes the underlying cause for errors.Is / errors.As.

type SafeString added in v0.6.0

type SafeString string

SafeString marks a string value as pre-escaped or otherwise trusted HTML content. When a template rendered by an engine with FormatHTML outputs a SafeString, the auto-escaper bypasses escaping. In text-format engine renders or [Compile]-path templates, SafeString behaves identically to a plain string.

Producing a SafeString from untrusted input is a security bug: the contents are emitted verbatim into HTML.

type Statement added in v0.4.0

type Statement interface {
	Node
	Execute(ctx *RenderContext, writer io.Writer) error
}

Statement is the interface for all statement nodes. Statements are executed and produce output or side effects.

type SubscriptNode added in v0.4.0

type SubscriptNode struct {
	Object Expression
	Index  Expression
	Line   int
	Col    int
}

SubscriptNode represents subscript/index access.

func NewSubscriptNode added in v0.4.0

func NewSubscriptNode(object, index Expression, line, col int) *SubscriptNode

NewSubscriptNode returns a new SubscriptNode.

func (*SubscriptNode) Evaluate added in v0.4.0

func (n *SubscriptNode) Evaluate(ctx *RenderContext) (*Value, error)

Evaluate returns the indexed or keyed value.

func (*SubscriptNode) Position added in v0.4.0

func (n *SubscriptNode) Position() (int, int)

Position returns the position of the SubscriptNode.

func (*SubscriptNode) String added in v0.4.0

func (n *SubscriptNode) String() string

String returns a debug representation of the SubscriptNode.

type TagParser added in v0.4.0

type TagParser func(doc *Parser, start *Token, arguments *Parser) (Statement, error)

TagParser is the parse function signature for template tags.

Parameters:

  • doc: The document-level parser used to parse nested tag bodies. Example: an if tag parses content between {% if %} and {% endif %}.
  • start: The tag-name token (for example "if", "for"), including source position information used in error reporting.
  • arguments: A dedicated parser for tag arguments. Example: in {% if x > 5 %}, this parser sees "x > 5".

Returns:

  • Statement: The parsed statement node.
  • error: Any parse error encountered.

type TagRegistry added in v0.6.0

type TagRegistry struct {
	// contains filtered or unexported fields
}

TagRegistry stores tag parsers. An optional parent registry is consulted when a lookup misses, enabling an Engine to layer its own private tags over the global registry without copying entries.

TagRegistry is safe for concurrent use.

func NewTagRegistry added in v0.6.0

func NewTagRegistry() *TagRegistry

NewTagRegistry creates an empty TagRegistry.

func (*TagRegistry) Clone added in v0.7.0

func (r *TagRegistry) Clone() *TagRegistry

Clone returns a shallow copy of the registry and its direct entries. The parent registry reference is preserved.

func (*TagRegistry) Get added in v0.6.0

func (r *TagRegistry) Get(name string) (TagParser, bool)

Get looks up a tag parser by name. If not found and a parent registry is set, the parent is consulted.

func (*TagRegistry) Has added in v0.6.0

func (r *TagRegistry) Has(name string) bool

Has reports whether a tag is registered (including in ancestors).

func (*TagRegistry) List added in v0.6.0

func (r *TagRegistry) List() []string

List returns a sorted list of tag names registered directly in this registry (excluding parents).

func (*TagRegistry) MustRegister added in v0.7.0

func (r *TagRegistry) MustRegister(name string, parser TagParser)

MustRegister registers parser under name and panics on duplicate registration or nil parser.

func (*TagRegistry) Register added in v0.6.0

func (r *TagRegistry) Register(name string, parser TagParser) error

Register adds a tag parser. Duplicate names return ErrTagAlreadyRegistered.

func (*TagRegistry) Replace added in v0.7.0

func (r *TagRegistry) Replace(name string, parser TagParser)

Replace stores parser under name, overwriting any direct existing entry.

Replace panics if parser is nil.

func (*TagRegistry) Unregister added in v0.6.0

func (r *TagRegistry) Unregister(name string)

Unregister removes a tag from this registry (does not touch parents).

type Template

type Template struct {
	// contains filtered or unexported fields
}

Template represents a compiled template ready for execution. A Template is immutable after compilation.

Templates parsed without a loader-backed engine have no resolved name or engine reference. Templates loaded via Engine.Load carry an engine reference and a resolved name for caching, dependency tracking, and multi-file rendering.

func NewTemplate

func NewTemplate(root []Statement) *Template

NewTemplate creates a new Template from parsed AST nodes.

Most callers should use Engine.ParseString or Engine.Load, which handle lexing and parsing automatically.

func (*Template) Execute

func (t *Template) Execute(ctx *RenderContext, w io.Writer) error

Execute writes the template output to w using the given render context.

For most use cases, Template.Render is simpler. Use Execute when you need control over the output destination or render context.

When the template extends a parent (via {% extends %}), Execute walks up to the root of the extends chain and runs that template's body. The current template is recorded as ctx.currentLeaf so BlockNode.Execute can resolve overrides across the chain. For templates without a parent the loop is a no-op and the template runs its own body.

func (*Template) Render added in v0.4.0

func (t *Template) Render(data Data) (string, error)

Render executes the template with data and returns the output as a string.

Render is a convenience wrapper around Template.RenderTo for the common case where a string result is needed.

func (*Template) RenderTo added in v0.7.0

func (t *Template) RenderTo(w io.Writer, data Data) error

RenderTo writes the template output to w using plain render data.

Use RenderTo for the common writer-based path. Reach for Template.Execute only when you need direct control over RenderContext.

type TextNode added in v0.4.0

type TextNode struct {
	Text string
	Line int
	Col  int
}

TextNode represents plain text outside of template tags.

func NewTextNode added in v0.4.0

func NewTextNode(text string, line, col int) *TextNode

NewTextNode returns a new TextNode.

func (*TextNode) Execute added in v0.4.0

func (n *TextNode) Execute(_ *RenderContext, w io.Writer) error

Execute writes the raw text to the output.

func (*TextNode) Position added in v0.4.0

func (n *TextNode) Position() (int, int)

Position returns the position of the TextNode.

func (*TextNode) String added in v0.4.0

func (n *TextNode) String() string

String returns a debug representation of the TextNode.

type Token added in v0.2.0

type Token struct {
	Type  TokenType
	Value string
	Line  int // 1-based
	Col   int // 1-based
}

Token represents a single lexical token.

func (*Token) String added in v0.4.0

func (t *Token) String() string

String returns a human-readable representation of the token.

type TokenType added in v0.2.0

type TokenType int

TokenType represents the type of a token.

const (
	// TokenError indicates a lexical error.
	TokenError TokenType = iota

	// TokenEOF indicates the end of input.
	TokenEOF

	// TokenText represents plain text outside of template tags.
	TokenText

	// TokenVarBegin represents the {{ variable tag opener.
	TokenVarBegin

	// TokenVarEnd represents the }} variable tag closer.
	TokenVarEnd

	// TokenTagBegin represents the {% block tag opener.
	TokenTagBegin

	// TokenTagEnd represents the %} block tag closer.
	TokenTagEnd

	// TokenIdentifier represents an identifier such as a variable name or keyword.
	TokenIdentifier

	// TokenString represents a quoted string literal.
	TokenString

	// TokenNumber represents an integer or floating-point literal.
	TokenNumber

	// TokenSymbol represents an operator or punctuation character.
	TokenSymbol
)

func (TokenType) String added in v0.4.0

func (t TokenType) String() string

String returns a string representation of the token type.

type UnaryOpNode added in v0.4.0

type UnaryOpNode struct {
	Operator string
	Operand  Expression
	Line     int
	Col      int
}

UnaryOpNode represents a unary operation.

func NewUnaryOpNode added in v0.4.0

func NewUnaryOpNode(operator string, operand Expression, line, col int) *UnaryOpNode

NewUnaryOpNode returns a new UnaryOpNode.

func (*UnaryOpNode) Evaluate added in v0.4.0

func (n *UnaryOpNode) Evaluate(ctx *RenderContext) (*Value, error)

Evaluate computes the unary operation result.

func (*UnaryOpNode) Position added in v0.4.0

func (n *UnaryOpNode) Position() (int, int)

Position returns the position of the UnaryOpNode.

func (*UnaryOpNode) String added in v0.4.0

func (n *UnaryOpNode) String() string

String returns a debug representation of the UnaryOpNode.

type Value added in v0.2.0

type Value struct {
	// contains filtered or unexported fields
}

Value wraps a Go value for template execution, providing type checking, conversion, and comparison operations.

func NewValue added in v0.2.0

func NewValue(v any) *Value

NewValue creates a Value wrapping v.

func (*Value) Bool added in v0.2.0

func (v *Value) Bool() bool

Bool reports whether the value is truthy.

func (*Value) Compare added in v0.4.0

func (v *Value) Compare(other *Value) (int, error)

Compare compares v with other. It returns -1 if v < other, 0 if v == other, 1 if v > other.

func (*Value) Equals added in v0.4.0

func (v *Value) Equals(other *Value) bool

Equals reports whether v and other represent the same value.

func (*Value) Field added in v0.4.0

func (v *Value) Field(name string) (*Value, error)

Field returns the value of a struct field or map key by name. For structs, it searches by JSON tag first, then by exported field name.

func (*Value) Float added in v0.2.0

func (v *Value) Float() (float64, error)

Float returns the value as float64, converting if possible.

func (*Value) Index added in v0.4.0

func (v *Value) Index(i int) (*Value, error)

Index returns the element at index i (for slices, arrays, strings).

func (*Value) Int added in v0.2.0

func (v *Value) Int() (int64, error)

Int returns the value as int64, converting if possible.

func (*Value) Interface added in v0.4.0

func (v *Value) Interface() any

Interface returns the underlying Go value.

func (*Value) IsNil added in v0.4.0

func (v *Value) IsNil() bool

IsNil reports whether the value is nil.

func (*Value) IsTrue added in v0.4.0

func (v *Value) IsTrue() bool

IsTrue reports whether the value is truthy in a template context. False values: nil, false, 0, "", empty slice/map/array.

func (*Value) Iterate added in v0.4.0

func (v *Value) Iterate(fn func(idx, count int, key, val *Value) bool) error

Iterate calls fn for each element in a collection (slice, array, map, string). fn receives the iteration index, total count, key, and value. Returning false from fn stops iteration early.

func (*Value) Key added in v0.4.0

func (v *Value) Key(key any) (*Value, error)

Key returns the map value for the given key.

func (*Value) Len added in v0.4.0

func (v *Value) Len() (int, error)

Len returns the length of the value (string, slice, map, or array).

func (*Value) String added in v0.4.0

func (v *Value) String() string

String returns the string representation of the value.

type VariableNode added in v0.2.0

type VariableNode struct {
	Name string
	Line int
	Col  int
}

VariableNode represents a variable reference.

func NewVariableNode added in v0.4.0

func NewVariableNode(name string, line, col int) *VariableNode

NewVariableNode returns a new VariableNode.

func (*VariableNode) Evaluate added in v0.2.0

func (n *VariableNode) Evaluate(ctx *RenderContext) (*Value, error)

Evaluate resolves the variable in the current render context.

func (*VariableNode) Position added in v0.4.0

func (n *VariableNode) Position() (int, int)

Position returns the position of the VariableNode.

func (*VariableNode) String added in v0.4.0

func (n *VariableNode) String() string

String returns a debug representation of the VariableNode.

Directories

Path Synopsis
examples
custom_filters command
Package main demonstrates registering custom filters.
Package main demonstrates registering custom filters.
custom_tags command
Package main demonstrates registering a custom tag on an Engine.
Package main demonstrates registering a custom tag on an Engine.
layout command
Package main demonstrates multi-file HTML templating with layout inheritance, includes, block.super, and HTML auto-escape.
Package main demonstrates multi-file HTML templating with layout inheritance, includes, block.super, and HTML auto-escape.
multifile_text command
Package main demonstrates multi-file text generation with Engine.
Package main demonstrates multi-file text generation with Engine.
secret_redaction command
Package main demonstrates redacting secrets from rendered output.
Package main demonstrates redacting secrets from rendered output.
usage command
Package main demonstrates typical template usage.
Package main demonstrates typical template usage.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL