velty

package module
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Feb 24, 2026 License: Apache-2.0 Imports: 23 Imported by: 5

README

Velty (template engine in go)

GoReportCard GoDoc

This library is compatible with Go 1.17+

Please refer to CHANGELOG.md if you encounter breaking changes.

Motivation

This library was created to facilitated seamless migration of code that uses JDK Velocity template to golang. The goal is to provide the first class template alternative for golang that is both substantially faster than JDK Velocity and go standard template HTML/Template or Text/Template See benchmark section for details.

Introduction

In order to reduce execution time, this project first produces execution plan alongside with all variables needed to execute it. One execution plan can be shared alongside many instances of scoped variables needed by executor. Scoped Variables holds both execution state and variables defined or used in the evaluation code.

    planner := velty.New()
    exec, newState, err := planner.Compile(code)
   
    state := newState() 
    exec.Exec(state)
    fmt.Printf("Result: %v", state.Buffer.String())
   
    anotherState := newState()
    exec.Exec(anotherState)
    fmt.Printf("Result: %v", anotherState.Buffer.String())

Usage

In order to create execution plan, you need to create a planner:

    planner := velty.New()

Options that you can pass while creating a Planner:

  • velty.BufferSize - initial state buffer size
  • velty.CacheSize - cache size for dynamically evaluated templates
  • velty.EscapeHTML - enables global (per Planner) HTML string escape mechanism (i.e. $Foo, if foo contains characters like <>, they will be encoded)
    planner := velty.New(velty.BufferSize(1024), valty.CacheSize(200), velty.EscapeHTML(true))

Once you have the Planner you have to define variables that will be used. Velty doesn't use a map to store state, but it recreates an internal type each time you define new variable and uses reflect.StructField.Offset to access data from the state. Velty supports two ways of defining planner variables:

  • planner.DefineVariable(variableName, variableType) - will create and add non-anonymous reflect.StructField
  • planner.EmbedVariable(variableName, variableType) - will create and add anonymous reflect.StructField

For each of the non-anonymous struct field registered with DefineVariable or EmbedVariable will be created unique Selector. Selector is used to get field value from the state.

  err = planner.DefineVariable("foo", reflect.Typeof(Foo{})) 
  //handle error if needed
  err = planner.DefineVariable("boo", Boo{}) 
  //handle error if needed
  err = planner.EmbedVariable("bar", reflect.Typeof(Bar{})) 
  //handle error if needed
  err = planner.EmbedVariable("emp", Boo{}) 
  //handle error if needed

You can pass an instance or the reflect.Type. However, there are some constraints:

  • Velty creates selector for each of the struct field. If you define i.e.:
  type Foo struct {
      Name string
      ID int
  }
  
  planner.DefineVariable("foo", Foo{})

Velty will create three selectors: foo, foo___Name, foo_ID. Structure used by the velty shouldn't have three consecutive underscores in any of the fields.

  • Velty won't create selectors for the Anonymous fields and will flatten the fields of the anonymous field.
  type Foo struct {
      Name string
      ID int
  }
  
  type Bar struct {
      Foo
  }

  planner.EmbedVariable("foo", Bar{})

Velty will create only two selectors: Name and ID because all other fields are Anonymous.

  • You can use tags to customize selector id, see: Tags
  • Velty generates selectors for the constants and name them: _T0, _T1, _T2 etc.

In the next step you can register functions. In the template you use the receiver syntax i.e. foo.Name.ToUpperCase() but in the Go, you have to register plain function, where the first argument is the value of field on which function was called.

  err = planner.RegisterFunction("ToUpperCase", strings.ToUpper) 
  //handle error if needed

You can register function in two ways:

  • planner.RegisterFunction - you can register regular functions like strings.ToUpper, and some of them are optimized using type assertion. If the function isn't optimized, it will be called via reflect.ValueOf.Call.

  • planner.RegsiterFunc - if you notice that function is not optimized, you can optimize it registering *op.Func. The simple implementation:

    customFunc := &op.Func{
		ResultType: reflect.TypeOf(""),
		Function: func(accumulator *Selector, operands []*Operand, state *est.State) unsafe.Pointer {
			if len(operands) < 2 {
				return nil
			}
			
			accumulator.SetBool(state.MemPtr, strings.HasPrefix(*(*string)(operands[0].Exec(state)), *(*string)(operands[1].Exec(state))))
			return accumulator.Pointer(state.MemPtr)
		},
	}
	
    err = planner.RegisterFunc("HasPrefix", customFunc) 
    //handle error if needed

Regular function can return no more than two non-pointer values. First is the new value, the second is an error. However errors in this case are ignored, and if any returned - the zero value will be appended to the result.

The next step is to create execution plan and new state function:

  template := `...`
  exec, newState, err := planner.Compile([]byte(template)) 
  // handle error if needed
  state := newState()
  exec.Exec(state)

New Capabilities

1) Context-aware execution and functions

States carry context.Context. Functions (or methods) that declare context.Context as the first parameter automatically receive the state context.

planner := velty.New()
_ = planner.RegisterFunction("ToUpperWithCtx", func(ctx context.Context, s string) string {
    return strings.ToUpper(s)
})

exec, newState, err := planner.Compile([]byte(`$ToUpperWithCtx("go")`))
if err != nil {
    panic(err)
}

state := newState()
_ = exec.ExecWithContext(context.Background(), state) // explicitly set execution context

If no context is provided, new states default to context.Background().

2) Parser hooks: Listener and Adjuster

You can instrument parsing and/or transform AST nodes before planning by passing Listener and Adjuster options to velty.New(...).

type myListener struct{}
func (l *myListener) OnEvent(e velty.Event) {
    // observe enter/exit events, spans, and expression context
}

type myAdjuster struct{}
func (a *myAdjuster) Adjust(node ast.Node, ctx *velty.ParserContext) (velty.Action, error) {
    return velty.Keep(), nil
}

planner := velty.New(
    velty.Listener(&myListener{}),
    velty.Adjuster(&myAdjuster{}),
)
3) Policy-based adjustment

For composable AST rewrite/validation rules, register policies in PolicyRegistry and pass them as planner options.

reg := velty.NewPolicyRegistry()
reg.Register(&velty.BasicPolicy{
    ID:     "example",
    Order:  10,
    Active: true,
    Fn: func(node ast.Node, ctx *velty.ParserContext) (velty.Action, error) {
        return velty.Keep(), nil
    },
})

planner := velty.New(velty.Policies(reg))
4) Source-to-source template transform

You can parse with spans, run adjusters, and materialize text patches using TransformTemplate.

out, err := velty.TransformTemplate(in, adjuster)
if err != nil {
    panic(err)
}
_ = out
5) Foreach context variables

Inside #foreach, Velty exposes $foreach metadata:

  • $foreach.Index (0-based index)
  • $foreach.Count (1-based counter)
  • $foreach.HasNext
  • $foreach.First
  • $foreach.Last

Example:

planner := velty.New()
_ = planner.DefineVariable("Items", []string{})

tpl := `#foreach($item in $Items)$foreach.Index:$item|count=$foreach.Count|first=$foreach.First|last=$foreach.Last|hasNext=$foreach.HasNext
#end`

exec, newState, err := planner.Compile([]byte(tpl))
if err != nil {
    panic(err)
}
state := newState()
_ = state.SetValue("Items", []string{"A", "B", "C"})
_ = exec.Exec(state)

Tags

In order to match template identifiers with the struct fields, you can use the velty tag. Supported attributes:

  • name - represents template identifier name i.e.:
  type Foo struct {
    Name string `velty:"name=fooName"`
  }

  planner.DefineVariable("foo", Foo{})
  template := `${foo.fooName}`
  • names - similar to the name but in this case you can specify more than one template identifier by separating them with |
  type Foo struct {
    Name string `velty:"name=NAME|FOO_NAME"`
  }
   
  planner.DefineVariable("foo", Foo{})
  template := `${foo.NAME}, ${foo.FOO_NAME}`
  • prefix - prefix can be used on the anonymous fields:
  type Foo struct {
      Name string `velty:"name=NAME"`
  }
    
  type Boo struct {
      Foo `velty:"prefix=FOO_"`
  }

  planner.EmbedVariable("boo", Boo{})
  template := `${FOO_NAME}`
  • - - tells Velty to don't create a selector for given field. In other words, it won't be possible to use the field in the template:
 type Foo struct {
      Name string `velty:"-"`
  }

  planner.EmbedVariable("foo", Foo{})
  template := `${foo.Name}` // throws an error during compile time

Benchmarks

Benchmarks against the text/template and Java velocity:

Bench 1: The template.

Benchmark_Exec_Velty-8   	   54585	         21127 ns/op	       0 B/op	       0 allocs/op	       4 allocs/op
Benchmark_Exec_Template-8   	    2370	        486511 ns/op	   78402 B/op	    3004 allocs/op
Benchmark_Exec_Velocity            44089                162599 ns/op

Bench 2: The template.

Benchmark_Exec_Velty       	        69561	             16867 ns/op	       0 B/op	       0 allocs/op
Benchmark_Exec_Template   	        3103	            372839 ns/op	   66791 B/op	    2543 allocs/op
Benchmark_Exec_Velocity                62277                125636 ns/op

Bench 3: The template.

Benchmark_Exec_Velty     2077510       523.2 ns/op	       0 B/op	       0 allocs/op
Benchmark_Exec_Velocity  2077510       8183  ns/op

Velty template is substantially faster than JDK Velocity and go Text/Template. On average velty is 20x faster than go Text/template and 8-15x faster than JDK Apache Velocity

Optimizations

States can be reused and can be shared with state pool. It is important to put state back to the pool.

planner := velty.New()
planExecutor, newState, err := planner.Compile(template)
poolSize := 1000
pool := velty.NewPool(poolSize, newState)

state := pool.State()
defer pool.Put(state)
result := planExecutor.Exec(state).String()

Bugs

This project does not implement full java velocity spec, but just a subset. It supports:

  • variables - i.e. ${foo.Name} $Name
  • assignment - i.e. #set($var1 = 10 + 20 * 10) #set($var2 = ${foo.Name})
  • if statements - i.e. #if(1==1) abc #elsif(2==2) def #else ghi #end
  • foreach - i.e. #foreach($name in ${foo.Names})
  • function calls - i.e. ${name.toUpper()}
  • template evaluation - i.e. #evaluate($TEMPLATE)

Contributing to Velty

Velty is an open source project and contributors are welcome!

See Todo list.

License

The source code is made available under the terms of the Apache License, Version 2, as stated in the file LICENSE.

Individual files may be made available under their own specific license, all compatible with Apache License, Version 2. Please see individual files for details.

Credits and Acknowledgements

Library Author: Kamil Larysz, Adrian Witas

Documentation

Overview

Package velty implements subset of the JDK Velocity, using the same syntax as Velocity. Implemented subset:

variables - i.e. `${foo.Name} $Name` assignment - i.e. `#set($var1 = 10 + 20 * 10) #set($var2 = ${foo.Name})` if statements - i.e. `#if(1==1) abc #elsif(2==2) def #else ghi #end` foreach - i.e. `#foreach($name in ${foo.Names})` function calls - i.e. `${name.toUpper()}` template evaluation - i.e. `#evaluate($TEMPLATE)`

Index

Constants

This section is empty.

Variables

View Source
var TimeType = reflect.TypeOf(time.Time{})

Functions

func ApplyPatches added in v0.3.0

func ApplyPatches(src []byte, patches []Patch) []byte

ApplyPatches applies a set of textual replacements to src according to Patch spans and returns a new byte slice. Spans are interpreted using the same convention as parser spans: [Start, End] (inclusive end offset).

Callers are expected to provide non-overlapping patches relative to the original src. Overlapping or out-of-range patches are ignored.

func TransformTemplate added in v0.3.0

func TransformTemplate(src []byte, adjuster NodeAdjuster, evalCfg ...EvaluateConfig) ([]byte, error)

TransformTemplate parses a template with spans, runs the supplied adjuster, and applies accumulated text patches to return transformed source.

Types

type Action added in v0.3.0

type Action struct {
	Kind         ActionKind
	Node         ast.Node // for replace
	Patches      []Patch  // optional patches
	SkipChildren bool     // skip walking children of this node
}

Action is the result returned by an adjuster.

func Keep added in v0.3.0

func Keep() Action

Helpers

func PatchSpan added in v0.3.0

func PatchSpan(s Span, repl []byte) Action

func Remove added in v0.3.0

func Remove() Action

func Replace added in v0.3.0

func Replace(n ast.Node) Action

func (Action) WithSkipChildren added in v0.3.0

func (a Action) WithSkipChildren() Action

type ActionKind added in v0.3.0

type ActionKind int

ActionKind describes the result of an adjuster.

const (
	ActionKeep ActionKind = iota
	ActionReplace
	ActionRemove
	ActionPatchOnly
)

type AdjustFunc added in v0.3.0

type AdjustFunc func(node ast.Node, ctx *ParserContext) (Action, error)

AdjustFunc is a helper to define NodeAdjuster via a function.

func (AdjustFunc) Adjust added in v0.3.0

func (f AdjustFunc) Adjust(node ast.Node, ctx *ParserContext) (Action, error)

Adjust implements NodeAdjuster for AdjustFunc.

type Adjuster added in v0.3.0

type Adjuster NodeAdjuster

Adjuster registers a NodeAdjuster to transform AST nodes. Usage: New(Adjuster(yourAdjuster))

type AdjusterChain added in v0.3.0

type AdjusterChain struct {
	Adjusters []NodeAdjuster
}

AdjusterChain applies multiple NodeAdjusters in sequence.

func NewAdjusterChain added in v0.3.0

func NewAdjusterChain(adjusters ...NodeAdjuster) *AdjusterChain

NewAdjusterChain constructs a chain of NodeAdjusters.

func (*AdjusterChain) Adjust added in v0.3.0

func (c *AdjusterChain) Adjust(node ast.Node, ctx *ParserContext) (Action, error)

Adjust runs each adjuster on the node in order, passing the parser context. It merges patches and respects removal and replace.

type BasicPolicy added in v0.3.0

type BasicPolicy struct {
	ID       string
	Order    int
	Active   bool
	Kinds    []PolicyKind // optional node kinds filter
	Contexts []string     // optional context kinds (reserved)
	Fn       func(node ast.Node, ctx *ParserContext) (Action, error)
}

BasicPolicy is a helper policy base with simple filters and a function logic.

func (*BasicPolicy) Apply added in v0.3.0

func (p *BasicPolicy) Apply(node ast.Node, ctx *ParserContext) (Action, error)

func (*BasicPolicy) Enabled added in v0.3.0

func (p *BasicPolicy) Enabled() bool

func (*BasicPolicy) Match added in v0.3.0

func (p *BasicPolicy) Match(node ast.Node, ctx *ParserContext) bool

func (*BasicPolicy) Name added in v0.3.0

func (p *BasicPolicy) Name() string

func (*BasicPolicy) Priority added in v0.3.0

func (p *BasicPolicy) Priority() int

type BufferSize

type BufferSize int

BufferSize represents initial size of the buffer

type CacheSize

type CacheSize int

CacheSize represents cache size in case of the dynamic template evaluation

type CycleDetector

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

func NewCycleDetector

func NewCycleDetector(rType reflect.Type) *CycleDetector

func (*CycleDetector) Child

func (c *CycleDetector) Child(rType reflect.Type, parentSelector *op.Selector) (next *CycleDetector, hasCycle bool)

func (*CycleDetector) Has

func (c *CycleDetector) Has(parent *CycleDetector) bool

type ErrorRecord added in v0.3.0

type ErrorRecord struct {
	Severity Severity
	Code     string
	Message  string
	File     string
	Span     Span
	Position Position
	Policy   string
	// Optional node/context info
	NodeKind string
	NodeID   int
	// SelectorChain may carry $foo.bar.baz chain segments if available
	SelectorChain []string
}

ErrorRecord captures a diagnostic with optional span/position.

type EscapeHTML

type EscapeHTML bool

EscapeHTML escapes HTML in passed variables.

type EvalCfg added in v0.3.0

type EvalCfg EvaluateConfig

Evaluate controls evaluate traversal. EvalCfg registers evaluate traversal configuration.

type EvaluateConfig added in v0.3.0

type EvaluateConfig struct {
	Mode       EvaluateMode
	DepthLimit int // 0 = unlimited
	Safety     EvaluateSafety
	Whitelist  []string // allowed selector IDs for evaluated content
	Blacklist  []string // disallowed selector IDs
}

EvaluateConfig defines behavior for #evaluate traversal.

type EvaluateMode added in v0.3.0

type EvaluateMode int

EvaluateMode controls how #evaluate is handled during traversal.

const (
	EvalOpaque EvaluateMode = iota
	EvalInspect
	EvalRewrite
)

type EvaluateSafety added in v0.3.0

type EvaluateSafety struct {
	StringLiteralsOnly bool
	MaxBytes           int // 0 = unlimited
}

EvaluateSafety defines safety constraints for handling #evaluate content.

type Event added in v0.3.0

type Event struct {
	Type    EventType
	Node    ast.Node
	Context *ParserContext
	// Optional span/position if available
	Span     Span
	Position Position
	// ExprContext describes where this node appears (control/text/etc.).
	ExprContext ExprContext
	// Occurrence is a 1-based occurrence index for variable-like symbols
	// such as selectors ($X) when applicable; otherwise 0.
	Occurrence int
}

Event carries parser event data to listeners.

type EventType added in v0.3.0

type EventType int

EventType represents the type of parser event.

const (
	// EventEnterNode indicates entering an AST node.
	EventEnterNode EventType = iota
	// EventExitNode indicates exiting an AST node.
	EventExitNode
)

type ExprContext added in v0.3.0

type ExprContext struct {
	Kind   ExprContextKind
	ArgIdx int16 // for CtxFuncArg, -1 otherwise
}

ExprContext describes the current expression context for a node.

type ExprContextKind added in v0.3.0

type ExprContextKind uint8

ExprContextKind classifies where an expression/node appears in the template.

const (
	CtxUnknown ExprContextKind = iota
	// Control flow
	CtxIfCond
	CtxIfBody
	CtxElseIfCond
	CtxElseBody
	CtxForEachCond
	CtxForEachBody
	CtxForLoopInit
	CtxForLoopCond
	CtxForLoopPost
	// Assignment
	CtxSetLHS
	CtxSetRHS
	// Expression vs text
	CtxAppendExpr
	CtxPlainText
	// Function arguments
	CtxFuncArg
	// Evaluate expression
	CtxEvaluate
)

type ForEachInfo added in v0.3.0

type ForEachInfo struct {
	Index   int  // zero-based index
	Count   int  // one-based counter
	HasNext bool // true if there is a next element
	First   bool // true if this is the first element
	Last    bool // true if this is the last element
}

ForEachInfo holds loop context similar to Apache Velocity $foreach. Fields are accessible as: $foreach.Index, $foreach.Count, $foreach.HasNext, $foreach.First, $foreach.Last

type Listener added in v0.3.0

type Listener ParserListener

Listener registers a ParserListener to receive parse events. Usage: New(Listener(yourListener))

type MultiCycleDetector

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

func (*MultiCycleDetector) Get

func (d *MultiCycleDetector) Get(c *CycleDetector, rType reflect.Type, parentSelector *op.Selector) (*CycleDetector, bool)

type NodeAdjuster added in v0.3.0

type NodeAdjuster interface {
	// Adjust applies transformations to the given node within the parser context.
	// It returns an action or error.
	Adjust(node ast.Node, ctx *ParserContext) (Action, error)
}

NodeAdjuster defines a transformation applied to AST nodes.

type Option

type Option interface{}

Option represents Planner generic option

type PanicOnError

type PanicOnError bool

PanicOnError panics and recover when first error returned.

type ParserContext added in v0.3.0

type ParserContext struct {
	// Current variable scope
	Scope *Scope

	// NodeStack tracks ancestor nodes
	NodeStack []ast.Node

	// Source and position mapping (optional)
	File   string
	Source []byte

	Reporter *Reporter
	// Evaluate handling configuration
	EvalConfig EvaluateConfig
	EvalDepth  int
	// contains filtered or unexported fields
}

ParserContext holds parser state, including scope stack and node ancestry.

func (*ParserContext) AddPatches added in v0.3.0

func (p *ParserContext) AddPatches(ps ...Patch)

AddPatches appends patches to the context accumulator.

func (*ParserContext) BumpOccurrence added in v0.3.0

func (p *ParserContext) BumpOccurrence(name string) int

BumpOccurrence increments and returns the 1-based occurrence index for name.

func (*ParserContext) CurrentExprContext added in v0.3.0

func (p *ParserContext) CurrentExprContext() ExprContext

CurrentExprContext returns the innermost expression context, if any.

func (*ParserContext) CurrentNode added in v0.3.0

func (p *ParserContext) CurrentNode() ast.Node

CurrentNode returns the most recently pushed node or nil if empty.

func (*ParserContext) EnterScope added in v0.3.0

func (p *ParserContext) EnterScope()

EnterScope pushes a new nested scope on the context.

func (*ParserContext) ExitScope added in v0.3.0

func (p *ParserContext) ExitScope()

ExitScope pops the current scope, reverting to the parent.

func (*ParserContext) GetSpan added in v0.3.0

func (p *ParserContext) GetSpan(n ast.Node) (Span, bool)

GetSpan retrieves a span for a node, if any.

func (*ParserContext) HasUnexpandRaw added in v0.3.0

func (p *ParserContext) HasUnexpandRaw(name string) bool

HasUnexpandRaw reports if a name is marked with UnexpandRaw decorator.

func (*ParserContext) InitSource added in v0.3.0

func (p *ParserContext) InitSource(file string, src []byte)

InitSource initializes source bytes and precomputes line starts.

func (*ParserContext) IsConst added in v0.3.0

func (p *ParserContext) IsConst(name string) bool

IsConst reports if a symbol is a known constant.

func (*ParserContext) IsLocal added in v0.3.0

func (p *ParserContext) IsLocal(name string) bool

IsLocal reports if a symbol is a local variable.

func (*ParserContext) IsNamespace added in v0.3.0

func (p *ParserContext) IsNamespace(name string) bool

IsNamespace reports if a name is a function namespace.

func (*ParserContext) IsParam added in v0.3.0

func (p *ParserContext) IsParam(name string) bool

IsParam reports if a symbol is a planner-defined param (root variable).

func (*ParserContext) IsStandaloneFunc added in v0.3.0

func (p *ParserContext) IsStandaloneFunc(name string) bool

IsStandaloneFunc reports if a name is a registered standalone function.

func (*ParserContext) MarkLocal added in v0.3.0

func (p *ParserContext) MarkLocal(name string)

MarkLocal records a local variable in the current scope and set.

func (*ParserContext) MarkUnexpandRaw added in v0.3.0

func (p *ParserContext) MarkUnexpandRaw(name string)

MarkUnexpandRaw marks a name as having UnexpandRaw decorator.

func (*ParserContext) Patches added in v0.3.0

func (p *ParserContext) Patches() []Patch

Patches returns accumulated patches.

func (*ParserContext) PopExprContext added in v0.3.0

func (p *ParserContext) PopExprContext()

PopExprContext removes the most recent expression context.

func (*ParserContext) PopNode added in v0.3.0

func (p *ParserContext) PopNode()

PopNode removes the most recent node from the context stack.

func (*ParserContext) PushExprContext added in v0.3.0

func (p *ParserContext) PushExprContext(ctx ExprContext)

PushExprContext adds a new expression context on the stack.

func (*ParserContext) PushNode added in v0.3.0

func (p *ParserContext) PushNode(node ast.Node)

PushNode records a node entry into the context stack.

func (*ParserContext) ResolvePosition added in v0.3.0

func (p *ParserContext) ResolvePosition(s Span) Position

ResolvePosition converts a span to line/column positions.

func (*ParserContext) SeedSymbols added in v0.3.0

func (p *ParserContext) SeedSymbols(params, namespaces, funcs, consts []string)

SeedSymbols initializes known params, namespaces, functions, and consts.

func (*ParserContext) SetSpan added in v0.3.0

func (p *ParserContext) SetSpan(n ast.Node, s Span)

SetSpan registers a span for a node.

func (*ParserContext) VarKind added in v0.3.0

func (p *ParserContext) VarKind(name string) VarKind

VarKind reports the classification of a symbol within this context.

type ParserListener added in v0.3.0

type ParserListener interface {
	// OnEvent is invoked for each parser event with full event data.
	OnEvent(e Event)
}

ParserListener defines hooks for parser events during AST construction. Implementations can perform actions when nodes are entered or exited.

type Patch added in v0.3.0

type Patch struct {
	Span        Span
	Replacement []byte
}

Patch represents a textual replacement for a span.

type PlanHooks added in v0.3.0

type PlanHooks PlannerListener

PlanHooks registers a planning listener to receive binding hooks. Usage: New(PlanHooks(yourPlannerListener))

type Planner

type Planner struct {
	*est.Control
	Type *est.Type

	*op.Functions
	// contains filtered or unexported fields
}

func New

func New(options ...Option) *Planner

func (*Planner) Compile

func (p *Planner) Compile(template []byte) (*est.Execution, func() *est.State, error)

Compile create Execution Plan and State provider for the Execution Plan.

func (*Planner) DefineVariable

func (p *Planner) DefineVariable(name string, v interface{}, names ...string) error

DefineVariable enrich the Type by adding field with given name. val can be either of the reflect.Type or regular type (i.e. Foo)

func (*Planner) EmbedVariable

func (p *Planner) EmbedVariable(val interface{}) error

EmbedVariable enrich the Type by adding Anonymous field with given name. val can be either of the reflect.Type or regular type (i.e. Foo)

func (*Planner) Func

func (p *Planner) Func(prev *op.Selector, methodName string, call *expr.Call) (*op.Func, error)

func (*Planner) New

func (p *Planner) New() *Planner

type PlannerListener added in v0.3.0

type PlannerListener interface {
	// OnDefineVariable is called when a planner defines a top-level variable.
	OnDefineVariable(name string, rtype reflect.Type)
	// OnSelectorResolved is called when an expression resolves to a selector.
	OnSelectorResolved(sel *op.Selector)
	// OnFunctionBind is called when a function/method is bound.
	OnFunctionBind(name string, f *op.Func, receiverType reflect.Type)
	// OnForEachResolved is called when a foreach item is resolved with types.
	OnForEachResolved(itemName string, itemType, setType reflect.Type)
}

PlannerListener receives hooks during planning/binding (post-parse).

type Policies added in v0.3.0

type Policies *PolicyRegistry

Policies registers a PolicyRegistry to drive adjustments. Usage: New(Policies(reg)) and then use reg.AsAdjuster() as Adjuster.

type Policy added in v0.3.0

type Policy interface {
	// Name is the policy identifier.
	Name() string
	// Priority defines policy ordering (lower first).
	Priority() int
	// Enabled reports whether policy is active.
	Enabled() bool
	// Match selects nodes/contexts this policy applies to.
	Match(node ast.Node, ctx *ParserContext) bool
	// Apply executes policy logic and returns an action.
	Apply(node ast.Node, ctx *ParserContext) (Action, error)
}

Policy represents a composable rule applied over nodes with metadata.

type PolicyKind added in v0.3.0

type PolicyKind string

PolicyKind is a string label for node kinds (e.g., "Selector", "Call", "If", "ForEach", ...)

func NodeKindOf added in v0.3.0

func NodeKindOf(n ast.Node) PolicyKind

NodeKindOf returns a coarse node kind name for policies.

type PolicyRegistry added in v0.3.0

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

PolicyRegistry stores and applies policies in priority order.

func NewPolicyRegistry added in v0.3.0

func NewPolicyRegistry() *PolicyRegistry

func (*PolicyRegistry) AsAdjuster added in v0.3.0

func (r *PolicyRegistry) AsAdjuster() NodeAdjuster

AsAdjuster builds a NodeAdjuster that evaluates registered policies.

func (*PolicyRegistry) Enable added in v0.3.0

func (r *PolicyRegistry) Enable(name string, on bool)

Enable toggles a policy by name when it implements BasicPolicy convention.

func (*PolicyRegistry) Policies added in v0.3.0

func (r *PolicyRegistry) Policies() []Policy

func (*PolicyRegistry) Register added in v0.3.0

func (r *PolicyRegistry) Register(p Policy)

Register adds or replaces a policy by name.

type Pool

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

func NewPool

func NewPool(size int, newState func() *est.State) *Pool

func (*Pool) Put

func (p *Pool) Put(state *est.State)

func (*Pool) State

func (p *Pool) State() *est.State

type Position added in v0.3.0

type Position struct {
	Line    int
	Col     int
	EndLine int
	EndCol  int
}

Position holds line/column coordinates for a span.

type Reporter added in v0.3.0

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

Reporter accumulates diagnostics and can flush them to a sink.

func NewReporter added in v0.3.0

func NewReporter() *Reporter

func (*Reporter) Records added in v0.3.0

func (r *Reporter) Records() []ErrorRecord

Records returns current diagnostics.

func (*Reporter) Report added in v0.3.0

func (r *Reporter) Report(rec ErrorRecord)

Report appends a diagnostic record.

func (*Reporter) Reset added in v0.3.0

func (r *Reporter) Reset()

Reset clears buffered diagnostics.

type Scope added in v0.3.0

type Scope struct {
	Parent *Scope
	Vars   map[string]interface{}
}

Scope represents a lexical scope with variables and an optional parent scope.

func NewScope added in v0.3.0

func NewScope(parent *Scope) *Scope

NewScope creates a new Scope with the given parent.

type Severity added in v0.3.0

type Severity int

Severity level for diagnostics.

const (
	SeverityInfo Severity = iota
	SeverityWarning
	SeverityError
)

type Span added in v0.3.0

type Span struct {
	Start int
	End   int
}

Span represents byte offsets for a node within a source template.

type SymbolSeeds added in v0.3.0

type SymbolSeeds struct {
	Params     []string
	Namespaces []string
	Standalone []string
	Consts     []string
}

applyParserHooksWithConfig initializes context with source and evaluate config, then traverses.

type Tag

type Tag struct {
	Names  []string
	Prefix string
	Omit   bool
}

Tag represent field tag

func Parse

func Parse(tagString string) *Tag

Parse parses tag

type TypeParser

type TypeParser = functions.TypeParser

TypeParser parses type string representation into reflect.Type

type VarKind added in v0.3.0

type VarKind uint8

VarKind classifies a symbol within the current parser context.

const (
	VarUnknown VarKind = iota
	VarLocal
	VarParam
	VarConst
	VarNamespace
	VarFunc
)

Directories

Path Synopsis
ast
est
op

Jump to

Keyboard shortcuts

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