package template
import (
)
var maxExecDepth = initMaxExecDepth()
func () int {
if runtime.GOARCH == "wasm" {
return 1000
}
return 100000
}
type state struct {
tmpl *Template
wr io.Writer
node parse.Node
vars []variable
depth int
}
type variable struct {
name string
value reflect.Value
}
func ( *state) ( string, reflect.Value) {
.vars = append(.vars, variable{, })
}
func ( *state) () int {
return len(.vars)
}
func ( *state) ( int) {
.vars = .vars[0:]
}
func ( *state) ( string, reflect.Value) {
for := .mark() - 1; >= 0; -- {
if .vars[].name == {
.vars[].value =
return
}
}
.errorf("undefined variable: %s", )
}
func ( *state) ( int, reflect.Value) {
.vars[len(.vars)-].value =
}
func ( *state) ( string) reflect.Value {
for := .mark() - 1; >= 0; -- {
if .vars[].name == {
return .vars[].value
}
}
.errorf("undefined variable: %s", )
return zero
}
var zero reflect.Value
type missingValType struct{}
var missingVal = reflect.ValueOf(missingValType{})
func ( *state) ( parse.Node) {
.node =
}
func ( string) string {
return strings.ReplaceAll(, "%", "%%")
}
type ExecError struct {
Name string
Err error
}
func ( ExecError) () string {
return .Err.Error()
}
func ( ExecError) () error {
return .Err
}
func ( *state) ( string, ...any) {
:= doublePercent(.tmpl.Name())
if .node == nil {
= fmt.Sprintf("template: %s: %s", , )
} else {
, := .tmpl.ErrorContext(.node)
= fmt.Sprintf("template: %s: executing %q at <%s>: %s", , , doublePercent(), )
}
panic(ExecError{
Name: .tmpl.Name(),
Err: fmt.Errorf(, ...),
})
}
type writeError struct {
Err error
}
func ( *state) ( error) {
panic(writeError{
Err: ,
})
}
func ( *error) {
:= recover()
if != nil {
switch err := .(type) {
case runtime.Error:
panic()
case writeError:
* = .Err
case ExecError:
* =
default:
panic()
}
}
}
func ( *Template) ( io.Writer, string, any) error {
:= .Lookup()
if == nil {
return fmt.Errorf("template: no template %q associated with template %q", , .name)
}
return .Execute(, )
}
func ( *Template) ( io.Writer, any) error {
return .execute(, )
}
func ( *Template) ( io.Writer, any) ( error) {
defer errRecover(&)
, := .(reflect.Value)
if ! {
= reflect.ValueOf()
}
:= &state{
tmpl: ,
wr: ,
vars: []variable{{"$", }},
}
if .Tree == nil || .Root == nil {
.errorf("%q is an incomplete or empty template", .Name())
}
.walk(, .Root)
return
}
func ( *Template) () string {
if .common == nil {
return ""
}
var strings.Builder
.muTmpl.RLock()
defer .muTmpl.RUnlock()
for , := range .tmpl {
if .Tree == nil || .Root == nil {
continue
}
if .Len() == 0 {
.WriteString("; defined templates are: ")
} else {
.WriteString(", ")
}
fmt.Fprintf(&, "%q", )
}
return .String()
}
var (
walkBreak = errors.New("break")
walkContinue = errors.New("continue")
)
func ( *state) ( reflect.Value, parse.Node) {
.at()
switch node := .(type) {
case *parse.ActionNode:
:= .evalPipeline(, .Pipe)
if len(.Pipe.Decl) == 0 {
.printValue(, )
}
case *parse.BreakNode:
panic(walkBreak)
case *parse.CommentNode:
case *parse.ContinueNode:
panic(walkContinue)
case *parse.IfNode:
.walkIfOrWith(parse.NodeIf, , .Pipe, .List, .ElseList)
case *parse.ListNode:
for , := range .Nodes {
.(, )
}
case *parse.RangeNode:
.walkRange(, )
case *parse.TemplateNode:
.walkTemplate(, )
case *parse.TextNode:
if , := .wr.Write(.Text); != nil {
.writeError()
}
case *parse.WithNode:
.walkIfOrWith(parse.NodeWith, , .Pipe, .List, .ElseList)
default:
.errorf("unknown node: %s", )
}
}
func ( *state) ( parse.NodeType, reflect.Value, *parse.PipeNode, , *parse.ListNode) {
defer .pop(.mark())
:= .evalPipeline(, )
, := isTrue(indirectInterface())
if ! {
.errorf("if/with can't use %v", )
}
if {
if == parse.NodeWith {
.walk(, )
} else {
.walk(, )
}
} else if != nil {
.walk(, )
}
}
func ( any) (, bool) {
return isTrue(reflect.ValueOf())
}
func ( reflect.Value) (, bool) {
if !.IsValid() {
return false, true
}
switch .Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
= .Len() > 0
case reflect.Bool:
= .Bool()
case reflect.Complex64, reflect.Complex128:
= .Complex() != 0
case reflect.Chan, reflect.Func, reflect.Pointer, reflect.Interface:
= !.IsNil()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
= .Int() != 0
case reflect.Float32, reflect.Float64:
= .Float() != 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
= .Uint() != 0
case reflect.Struct:
= true
default:
return
}
return , true
}
func ( *state) ( reflect.Value, *parse.RangeNode) {
.at()
defer func() {
if := recover(); != nil && != walkBreak {
panic()
}
}()
defer .pop(.mark())
, := indirect(.evalPipeline(, .Pipe))
:= .mark()
:= func(, reflect.Value) {
if len(.Pipe.Decl) > 0 {
.setTopVar(1, )
}
if len(.Pipe.Decl) > 1 {
.setTopVar(2, )
}
defer .pop()
defer func() {
if := recover(); != nil && != walkContinue {
panic()
}
}()
.walk(, .List)
}
switch .Kind() {
case reflect.Array, reflect.Slice:
if .Len() == 0 {
break
}
for := 0; < .Len(); ++ {
(reflect.ValueOf(), .Index())
}
return
case reflect.Map:
if .Len() == 0 {
break
}
:= fmtsort.Sort()
for , := range .Key {
(, .Value[])
}
return
case reflect.Chan:
if .IsNil() {
break
}
if .Type().ChanDir() == reflect.SendDir {
.errorf("range over send-only channel %v", )
break
}
:= 0
for ; ; ++ {
, := .Recv()
if ! {
break
}
(reflect.ValueOf(), )
}
if == 0 {
break
}
return
case reflect.Invalid:
break
default:
.errorf("range can't iterate over %v", )
}
if .ElseList != nil {
.walk(, .ElseList)
}
}
func ( *state) ( reflect.Value, *parse.TemplateNode) {
.at()
:= .tmpl.Lookup(.Name)
if == nil {
.errorf("template %q not defined", .Name)
}
if .depth == maxExecDepth {
.errorf("exceeded maximum template depth (%v)", maxExecDepth)
}
= .evalPipeline(, .Pipe)
:= *
.depth++
.tmpl =
.vars = []variable{{"$", }}
.walk(, .Root)
}
func ( *state) ( reflect.Value, *parse.PipeNode) ( reflect.Value) {
if == nil {
return
}
.at()
= missingVal
for , := range .Cmds {
= .evalCommand(, , )
if .Kind() == reflect.Interface && .Type().NumMethod() == 0 {
= reflect.ValueOf(.Interface())
}
}
for , := range .Decl {
if .IsAssign {
.setVar(.Ident[0], )
} else {
.push(.Ident[0], )
}
}
return
}
func ( *state) ( []parse.Node, reflect.Value) {
if len() > 1 || != missingVal {
.errorf("can't give argument to non-function %s", [0])
}
}
func ( *state) ( reflect.Value, *parse.CommandNode, reflect.Value) reflect.Value {
:= .Args[0]
switch n := .(type) {
case *parse.FieldNode:
return .evalFieldNode(, , .Args, )
case *parse.ChainNode:
return .evalChainNode(, , .Args, )
case *parse.IdentifierNode:
return .evalFunction(, , , .Args, )
case *parse.PipeNode:
.notAFunction(.Args, )
return .evalPipeline(, )
case *parse.VariableNode:
return .evalVariableNode(, , .Args, )
}
.at()
.notAFunction(.Args, )
switch word := .(type) {
case *parse.BoolNode:
return reflect.ValueOf(.True)
case *parse.DotNode:
return
case *parse.NilNode:
.errorf("nil is not a command")
case *parse.NumberNode:
return .idealConstant()
case *parse.StringNode:
return reflect.ValueOf(.Text)
}
.errorf("can't evaluate command %q", )
panic("not reached")
}
func ( *state) ( *parse.NumberNode) reflect.Value {
.at()
switch {
case .IsComplex:
return reflect.ValueOf(.Complex128)
case .IsFloat &&
!isHexInt(.Text) && !isRuneInt(.Text) &&
strings.ContainsAny(.Text, ".eEpP"):
return reflect.ValueOf(.Float64)
case .IsInt:
:= int(.Int64)
if int64() != .Int64 {
.errorf("%s overflows int", .Text)
}
return reflect.ValueOf()
case .IsUint:
.errorf("%s overflows int", .Text)
}
return zero
}
func ( string) bool {
return len() > 0 && [0] == '\''
}
func ( string) bool {
return len() > 2 && [0] == '0' && ([1] == 'x' || [1] == 'X') && !strings.ContainsAny(, "pP")
}
func ( *state) ( reflect.Value, *parse.FieldNode, []parse.Node, reflect.Value) reflect.Value {
.at()
return .evalFieldChain(, , , .Ident, , )
}
func ( *state) ( reflect.Value, *parse.ChainNode, []parse.Node, reflect.Value) reflect.Value {
.at()
if len(.Field) == 0 {
.errorf("internal error: no fields in evalChainNode")
}
if .Node.Type() == parse.NodeNil {
.errorf("indirection through explicit nil in %s", )
}
:= .evalArg(, nil, .Node)
return .evalFieldChain(, , , .Field, , )
}
func ( *state) ( reflect.Value, *parse.VariableNode, []parse.Node, reflect.Value) reflect.Value {
.at()
:= .varValue(.Ident[0])
if len(.Ident) == 1 {
.notAFunction(, )
return
}
return .evalFieldChain(, , , .Ident[1:], , )
}
func ( *state) (, reflect.Value, parse.Node, []string, []parse.Node, reflect.Value) reflect.Value {
:= len()
for := 0; < -1; ++ {
= .evalField(, [], , nil, missingVal, )
}
return .evalField(, [-1], , , , )
}
func ( *state) ( reflect.Value, *parse.IdentifierNode, parse.Node, []parse.Node, reflect.Value) reflect.Value {
.at()
:= .Ident
, , := findFunction(, .tmpl)
if ! {
.errorf("%q is not a defined function", )
}
return .evalCall(, , , , , , )
}
func ( *state) ( reflect.Value, string, parse.Node, []parse.Node, , reflect.Value) reflect.Value {
if !.IsValid() {
if .tmpl.option.missingKey == mapError {
.errorf("nil data; no entry for key %q", )
}
return zero
}
:= .Type()
, := indirect()
if .Kind() == reflect.Interface && {
.errorf("nil pointer evaluating %s.%s", , )
return zero
}
:=
if .Kind() != reflect.Interface && .Kind() != reflect.Pointer && .CanAddr() {
= .Addr()
}
if := .MethodByName(); .IsValid() {
return .evalCall(, , false, , , , )
}
:= len() > 1 || != missingVal
switch .Kind() {
case reflect.Struct:
, := .Type().FieldByName()
if {
, := .FieldByIndexErr(.Index)
if !.IsExported() {
.errorf("%s is an unexported field of struct type %s", , )
}
if != nil {
.errorf("%v", )
}
if {
.errorf("%s has arguments but cannot be invoked as function", )
}
return
}
case reflect.Map:
:= reflect.ValueOf()
if .Type().AssignableTo(.Type().Key()) {
if {
.errorf("%s is not a method but has arguments", )
}
:= .MapIndex()
if !.IsValid() {
switch .tmpl.option.missingKey {
case mapInvalid:
case mapZeroValue:
= reflect.Zero(.Type().Elem())
case mapError:
.errorf("map has no entry for key %q", )
}
}
return
}
case reflect.Pointer:
:= .Type().Elem()
if .Kind() == reflect.Struct {
if , := .FieldByName(); ! {
break
}
}
if {
.errorf("nil pointer evaluating %s.%s", , )
}
}
.errorf("can't evaluate field %s in type %s", , )
panic("not reached")
}
var (
errorType = reflect.TypeOf((*error)(nil)).Elem()
fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
reflectValueType = reflect.TypeOf((*reflect.Value)(nil)).Elem()
)
func ( *state) (, reflect.Value, bool, parse.Node, string, []parse.Node, reflect.Value) reflect.Value {
if != nil {
= [1:]
}
:= .Type()
:= len()
if != missingVal {
++
}
:= len()
if .IsVariadic() {
= .NumIn() - 1
if < {
.errorf("wrong number of args for %s: want at least %d got %d", , .NumIn()-1, len())
}
} else if != .NumIn() {
.errorf("wrong number of args for %s: want %d got %d", , .NumIn(), )
}
if !goodFunc() {
.errorf("can't call method/function %q with %d results", , .NumOut())
}
:= func( reflect.Value) reflect.Value {
if .Type() == reflectValueType {
= .Interface().(reflect.Value)
}
return
}
if && ( == "and" || == "or") {
:= .In(0)
var reflect.Value
for , := range {
= .evalArg(, , ).Interface().(reflect.Value)
if truth() == ( == "or") {
return
}
}
if != missingVal {
= (.validateType(, ))
}
return
}
:= make([]reflect.Value, )
:= 0
for ; < && < len(); ++ {
[] = .evalArg(, .In(), [])
}
if .IsVariadic() {
:= .In(.NumIn() - 1).Elem()
for ; < len(); ++ {
[] = .evalArg(, , [])
}
}
if != missingVal {
:= .In(.NumIn() - 1)
if .IsVariadic() {
if -1 < {
= .In( - 1)
} else {
= .Elem()
}
}
[] = .validateType(, )
}
, := safeCall(, )
if != nil {
.at()
.errorf("error calling %s: %w", , )
}
return ()
}
func ( reflect.Type) bool {
switch .Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
return true
case reflect.Struct:
return == reflectValueType
}
return false
}
func ( *state) ( reflect.Value, reflect.Type) reflect.Value {
if !.IsValid() {
if == nil {
return reflect.ValueOf(nil)
}
if canBeNil() {
return reflect.Zero()
}
.errorf("invalid value; expected %s", )
}
if == reflectValueType && .Type() != {
return reflect.ValueOf()
}
if != nil && !.Type().AssignableTo() {
if .Kind() == reflect.Interface && !.IsNil() {
= .Elem()
if .Type().AssignableTo() {
return
}
}
switch {
case .Kind() == reflect.Pointer && .Type().Elem().AssignableTo():
= .Elem()
if !.IsValid() {
.errorf("dereference of nil pointer of type %s", )
}
case reflect.PointerTo(.Type()).AssignableTo() && .CanAddr():
= .Addr()
default:
.errorf("wrong type for value; expected %s; got %s", , .Type())
}
}
return
}
func ( *state) ( reflect.Value, reflect.Type, parse.Node) reflect.Value {
.at()
switch arg := .(type) {
case *parse.DotNode:
return .validateType(, )
case *parse.NilNode:
if canBeNil() {
return reflect.Zero()
}
.errorf("cannot assign nil to %s", )
case *parse.FieldNode:
return .validateType(.evalFieldNode(, , []parse.Node{}, missingVal), )
case *parse.VariableNode:
return .validateType(.evalVariableNode(, , nil, missingVal), )
case *parse.PipeNode:
return .validateType(.evalPipeline(, ), )
case *parse.IdentifierNode:
return .validateType(.evalFunction(, , , nil, missingVal), )
case *parse.ChainNode:
return .validateType(.evalChainNode(, , nil, missingVal), )
}
switch .Kind() {
case reflect.Bool:
return .evalBool(, )
case reflect.Complex64, reflect.Complex128:
return .evalComplex(, )
case reflect.Float32, reflect.Float64:
return .evalFloat(, )
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return .evalInteger(, )
case reflect.Interface:
if .NumMethod() == 0 {
return .evalEmptyInterface(, )
}
case reflect.Struct:
if == reflectValueType {
return reflect.ValueOf(.evalEmptyInterface(, ))
}
case reflect.String:
return .evalString(, )
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return .evalUnsignedInteger(, )
}
.errorf("can't handle %s for arg of type %s", , )
panic("not reached")
}
func ( *state) ( reflect.Type, parse.Node) reflect.Value {
.at()
if , := .(*parse.BoolNode); {
:= reflect.New().Elem()
.SetBool(.True)
return
}
.errorf("expected bool; found %s", )
panic("not reached")
}
func ( *state) ( reflect.Type, parse.Node) reflect.Value {
.at()
if , := .(*parse.StringNode); {
:= reflect.New().Elem()
.SetString(.Text)
return
}
.errorf("expected string; found %s", )
panic("not reached")
}
func ( *state) ( reflect.Type, parse.Node) reflect.Value {
.at()
if , := .(*parse.NumberNode); && .IsInt {
:= reflect.New().Elem()
.SetInt(.Int64)
return
}
.errorf("expected integer; found %s", )
panic("not reached")
}
func ( *state) ( reflect.Type, parse.Node) reflect.Value {
.at()
if , := .(*parse.NumberNode); && .IsUint {
:= reflect.New().Elem()
.SetUint(.Uint64)
return
}
.errorf("expected unsigned integer; found %s", )
panic("not reached")
}
func ( *state) ( reflect.Type, parse.Node) reflect.Value {
.at()
if , := .(*parse.NumberNode); && .IsFloat {
:= reflect.New().Elem()
.SetFloat(.Float64)
return
}
.errorf("expected float; found %s", )
panic("not reached")
}
func ( *state) ( reflect.Type, parse.Node) reflect.Value {
if , := .(*parse.NumberNode); && .IsComplex {
:= reflect.New().Elem()
.SetComplex(.Complex128)
return
}
.errorf("expected complex; found %s", )
panic("not reached")
}
func ( *state) ( reflect.Value, parse.Node) reflect.Value {
.at()
switch n := .(type) {
case *parse.BoolNode:
return reflect.ValueOf(.True)
case *parse.DotNode:
return
case *parse.FieldNode:
return .evalFieldNode(, , nil, missingVal)
case *parse.IdentifierNode:
return .evalFunction(, , , nil, missingVal)
case *parse.NilNode:
.errorf("evalEmptyInterface: nil (can't happen)")
case *parse.NumberNode:
return .idealConstant()
case *parse.StringNode:
return reflect.ValueOf(.Text)
case *parse.VariableNode:
return .evalVariableNode(, , nil, missingVal)
case *parse.PipeNode:
return .evalPipeline(, )
}
.errorf("can't handle assignment of %s to empty interface argument", )
panic("not reached")
}
func ( reflect.Value) ( reflect.Value, bool) {
for ; .Kind() == reflect.Pointer || .Kind() == reflect.Interface; = .Elem() {
if .IsNil() {
return , true
}
}
return , false
}
func ( reflect.Value) reflect.Value {
if .Kind() != reflect.Interface {
return
}
if .IsNil() {
return reflect.Value{}
}
return .Elem()
}
func ( *state) ( parse.Node, reflect.Value) {
.at()
, := printableValue()
if ! {
.errorf("can't print %s of type %s", , .Type())
}
, := fmt.Fprint(.wr, )
if != nil {
.writeError()
}
}
func ( reflect.Value) (any, bool) {
if .Kind() == reflect.Pointer {
, _ = indirect()
}
if !.IsValid() {
return "<no value>", true
}
if !.Type().Implements(errorType) && !.Type().Implements(fmtStringerType) {
if .CanAddr() && (reflect.PointerTo(.Type()).Implements(errorType) || reflect.PointerTo(.Type()).Implements(fmtStringerType)) {
= .Addr()
} else {
switch .Kind() {
case reflect.Chan, reflect.Func:
return nil, false
}
}
}
return .Interface(), true
}