package template
import (
"errors"
"fmt"
"internal/fmtsort"
"io"
"reflect"
"runtime"
"strings"
"text/template/parse"
)
var maxExecDepth = initMaxExecDepth ()
func initMaxExecDepth() 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 (s *state ) push (name string , value reflect .Value ) {
s .vars = append (s .vars , variable {name , value })
}
func (s *state ) mark () int {
return len (s .vars )
}
func (s *state ) pop (mark int ) {
s .vars = s .vars [0 :mark ]
}
func (s *state ) setVar (name string , value reflect .Value ) {
for i := s .mark () - 1 ; i >= 0 ; i -- {
if s .vars [i ].name == name {
s .vars [i ].value = value
return
}
}
s .errorf ("undefined variable: %s" , name )
}
func (s *state ) setTopVar (n int , value reflect .Value ) {
s .vars [len (s .vars )-n ].value = value
}
func (s *state ) varValue (name string ) reflect .Value {
for i := s .mark () - 1 ; i >= 0 ; i -- {
if s .vars [i ].name == name {
return s .vars [i ].value
}
}
s .errorf ("undefined variable: %s" , name )
return zero
}
var zero reflect .Value
type missingValType struct {}
var missingVal = reflect .ValueOf (missingValType {})
var missingValReflectType = reflect .TypeFor [missingValType ]()
func isMissing(v reflect .Value ) bool {
return v .IsValid () && v .Type () == missingValReflectType
}
func (s *state ) at (node parse .Node ) {
s .node = node
}
func doublePercent(str string ) string {
return strings .ReplaceAll (str , "%" , "%%" )
}
type ExecError struct {
Name string
Err error
}
func (e ExecError ) Error () string {
return e .Err .Error()
}
func (e ExecError ) Unwrap () error {
return e .Err
}
func (s *state ) errorf (format string , args ...any ) {
name := doublePercent (s .tmpl .Name ())
if s .node == nil {
format = fmt .Sprintf ("template: %s: %s" , name , format )
} else {
location , context := s .tmpl .ErrorContext (s .node )
format = fmt .Sprintf ("template: %s: executing %q at <%s>: %s" , location , name , doublePercent (context ), format )
}
panic (ExecError {
Name : s .tmpl .Name (),
Err : fmt .Errorf (format , args ...),
})
}
type writeError struct {
Err error
}
func (s *state ) writeError (err error ) {
panic (writeError {
Err : err ,
})
}
func errRecover(errp *error ) {
e := recover ()
if e != nil {
switch err := e .(type ) {
case runtime .Error :
panic (e )
case writeError :
*errp = err .Err
case ExecError :
*errp = err
default :
panic (e )
}
}
}
func (t *Template ) ExecuteTemplate (wr io .Writer , name string , data any ) error {
tmpl := t .Lookup (name )
if tmpl == nil {
return fmt .Errorf ("template: no template %q associated with template %q" , name , t .name )
}
return tmpl .Execute (wr , data )
}
func (t *Template ) Execute (wr io .Writer , data any ) error {
return t .execute (wr , data )
}
func (t *Template ) execute (wr io .Writer , data any ) (err error ) {
defer errRecover (&err )
value , ok := data .(reflect .Value )
if !ok {
value = reflect .ValueOf (data )
}
state := &state {
tmpl : t ,
wr : wr ,
vars : []variable {{"$" , value }},
}
if t .Tree == nil || t .Root == nil {
state .errorf ("%q is an incomplete or empty template" , t .Name ())
}
state .walk (value , t .Root )
return
}
func (t *Template ) DefinedTemplates () string {
if t .common == nil {
return ""
}
var b strings .Builder
t .muTmpl .RLock ()
defer t .muTmpl .RUnlock ()
for name , tmpl := range t .tmpl {
if tmpl .Tree == nil || tmpl .Root == nil {
continue
}
if b .Len () == 0 {
b .WriteString ("; defined templates are: " )
} else {
b .WriteString (", " )
}
fmt .Fprintf (&b , "%q" , name )
}
return b .String ()
}
var (
walkBreak = errors .New ("break" )
walkContinue = errors .New ("continue" )
)
func (s *state ) walk (dot reflect .Value , node parse .Node ) {
s .at (node )
switch node := node .(type ) {
case *parse .ActionNode :
val := s .evalPipeline (dot , node .Pipe )
if len (node .Pipe .Decl ) == 0 {
s .printValue (node , val )
}
case *parse .BreakNode :
panic (walkBreak )
case *parse .CommentNode :
case *parse .ContinueNode :
panic (walkContinue )
case *parse .IfNode :
s .walkIfOrWith (parse .NodeIf , dot , node .Pipe , node .List , node .ElseList )
case *parse .ListNode :
for _ , node := range node .Nodes {
s .walk (dot , node )
}
case *parse .RangeNode :
s .walkRange (dot , node )
case *parse .TemplateNode :
s .walkTemplate (dot , node )
case *parse .TextNode :
if _ , err := s .wr .Write (node .Text ); err != nil {
s .writeError (err )
}
case *parse .WithNode :
s .walkIfOrWith (parse .NodeWith , dot , node .Pipe , node .List , node .ElseList )
default :
s .errorf ("unknown node: %s" , node )
}
}
func (s *state ) walkIfOrWith (typ parse .NodeType , dot reflect .Value , pipe *parse .PipeNode , list , elseList *parse .ListNode ) {
defer s .pop (s .mark ())
val := s .evalPipeline (dot , pipe )
truth , ok := isTrue (indirectInterface (val ))
if !ok {
s .errorf ("if/with can't use %v" , val )
}
if truth {
if typ == parse .NodeWith {
s .walk (val , list )
} else {
s .walk (dot , list )
}
} else if elseList != nil {
s .walk (dot , elseList )
}
}
func IsTrue (val any ) (truth , ok bool ) {
return isTrue (reflect .ValueOf (val ))
}
func isTrue(val reflect .Value ) (truth , ok bool ) {
if !val .IsValid () {
return false , true
}
switch val .Kind () {
case reflect .Array , reflect .Map , reflect .Slice , reflect .String :
truth = val .Len () > 0
case reflect .Bool :
truth = val .Bool ()
case reflect .Complex64 , reflect .Complex128 :
truth = val .Complex () != 0
case reflect .Chan , reflect .Func , reflect .Pointer , reflect .Interface :
truth = !val .IsNil ()
case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
truth = val .Int () != 0
case reflect .Float32 , reflect .Float64 :
truth = val .Float () != 0
case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 , reflect .Uintptr :
truth = val .Uint () != 0
case reflect .Struct :
truth = true
default :
return
}
return truth , true
}
func (s *state ) walkRange (dot reflect .Value , r *parse .RangeNode ) {
s .at (r )
defer func () {
if r := recover (); r != nil && r != walkBreak {
panic (r )
}
}()
defer s .pop (s .mark ())
val , _ := indirect (s .evalPipeline (dot , r .Pipe ))
mark := s .mark ()
oneIteration := func (index , elem reflect .Value ) {
if len (r .Pipe .Decl ) > 0 {
if r .Pipe .IsAssign {
if len (r .Pipe .Decl ) > 1 {
s .setVar (r .Pipe .Decl [0 ].Ident [0 ], index )
} else {
s .setVar (r .Pipe .Decl [0 ].Ident [0 ], elem )
}
} else {
s .setTopVar (1 , elem )
}
}
if len (r .Pipe .Decl ) > 1 {
if r .Pipe .IsAssign {
s .setVar (r .Pipe .Decl [1 ].Ident [0 ], elem )
} else {
s .setTopVar (2 , index )
}
}
defer s .pop (mark )
defer func () {
if r := recover (); r != nil && r != walkContinue {
panic (r )
}
}()
s .walk (elem , r .List )
}
switch val .Kind () {
case reflect .Array , reflect .Slice :
if val .Len () == 0 {
break
}
for i := 0 ; i < val .Len (); i ++ {
oneIteration (reflect .ValueOf (i ), val .Index (i ))
}
return
case reflect .Map :
if val .Len () == 0 {
break
}
om := fmtsort .Sort (val )
for _ , m := range om {
oneIteration (m .Key , m .Value )
}
return
case reflect .Chan :
if val .IsNil () {
break
}
if val .Type ().ChanDir () == reflect .SendDir {
s .errorf ("range over send-only channel %v" , val )
break
}
i := 0
for ; ; i ++ {
elem , ok := val .Recv ()
if !ok {
break
}
oneIteration (reflect .ValueOf (i ), elem )
}
if i == 0 {
break
}
return
case reflect .Invalid :
break
default :
s .errorf ("range can't iterate over %v" , val )
}
if r .ElseList != nil {
s .walk (dot , r .ElseList )
}
}
func (s *state ) walkTemplate (dot reflect .Value , t *parse .TemplateNode ) {
s .at (t )
tmpl := s .tmpl .Lookup (t .Name )
if tmpl == nil {
s .errorf ("template %q not defined" , t .Name )
}
if s .depth == maxExecDepth {
s .errorf ("exceeded maximum template depth (%v)" , maxExecDepth )
}
dot = s .evalPipeline (dot , t .Pipe )
newState := *s
newState .depth ++
newState .tmpl = tmpl
newState .vars = []variable {{"$" , dot }}
newState .walk (dot , tmpl .Root )
}
func (s *state ) evalPipeline (dot reflect .Value , pipe *parse .PipeNode ) (value reflect .Value ) {
if pipe == nil {
return
}
s .at (pipe )
value = missingVal
for _ , cmd := range pipe .Cmds {
value = s .evalCommand (dot , cmd , value )
if value .Kind () == reflect .Interface && value .Type ().NumMethod () == 0 {
value = value .Elem ()
}
}
for _ , variable := range pipe .Decl {
if pipe .IsAssign {
s .setVar (variable .Ident [0 ], value )
} else {
s .push (variable .Ident [0 ], value )
}
}
return value
}
func (s *state ) notAFunction (args []parse .Node , final reflect .Value ) {
if len (args ) > 1 || !isMissing (final ) {
s .errorf ("can't give argument to non-function %s" , args [0 ])
}
}
func (s *state ) evalCommand (dot reflect .Value , cmd *parse .CommandNode , final reflect .Value ) reflect .Value {
firstWord := cmd .Args [0 ]
switch n := firstWord .(type ) {
case *parse .FieldNode :
return s .evalFieldNode (dot , n , cmd .Args , final )
case *parse .ChainNode :
return s .evalChainNode (dot , n , cmd .Args , final )
case *parse .IdentifierNode :
return s .evalFunction (dot , n , cmd , cmd .Args , final )
case *parse .PipeNode :
s .notAFunction (cmd .Args , final )
return s .evalPipeline (dot , n )
case *parse .VariableNode :
return s .evalVariableNode (dot , n , cmd .Args , final )
}
s .at (firstWord )
s .notAFunction (cmd .Args , final )
switch word := firstWord .(type ) {
case *parse .BoolNode :
return reflect .ValueOf (word .True )
case *parse .DotNode :
return dot
case *parse .NilNode :
s .errorf ("nil is not a command" )
case *parse .NumberNode :
return s .idealConstant (word )
case *parse .StringNode :
return reflect .ValueOf (word .Text )
}
s .errorf ("can't evaluate command %q" , firstWord )
panic ("not reached" )
}
func (s *state ) idealConstant (constant *parse .NumberNode ) reflect .Value {
s .at (constant )
switch {
case constant .IsComplex :
return reflect .ValueOf (constant .Complex128 )
case constant .IsFloat &&
!isHexInt (constant .Text ) && !isRuneInt (constant .Text ) &&
strings .ContainsAny (constant .Text , ".eEpP" ):
return reflect .ValueOf (constant .Float64 )
case constant .IsInt :
n := int (constant .Int64 )
if int64 (n ) != constant .Int64 {
s .errorf ("%s overflows int" , constant .Text )
}
return reflect .ValueOf (n )
case constant .IsUint :
s .errorf ("%s overflows int" , constant .Text )
}
return zero
}
func isRuneInt(s string ) bool {
return len (s ) > 0 && s [0 ] == '\''
}
func isHexInt(s string ) bool {
return len (s ) > 2 && s [0 ] == '0' && (s [1 ] == 'x' || s [1 ] == 'X' ) && !strings .ContainsAny (s , "pP" )
}
func (s *state ) evalFieldNode (dot reflect .Value , field *parse .FieldNode , args []parse .Node , final reflect .Value ) reflect .Value {
s .at (field )
return s .evalFieldChain (dot , dot , field , field .Ident , args , final )
}
func (s *state ) evalChainNode (dot reflect .Value , chain *parse .ChainNode , args []parse .Node , final reflect .Value ) reflect .Value {
s .at (chain )
if len (chain .Field ) == 0 {
s .errorf ("internal error: no fields in evalChainNode" )
}
if chain .Node .Type () == parse .NodeNil {
s .errorf ("indirection through explicit nil in %s" , chain )
}
pipe := s .evalArg (dot , nil , chain .Node )
return s .evalFieldChain (dot , pipe , chain , chain .Field , args , final )
}
func (s *state ) evalVariableNode (dot reflect .Value , variable *parse .VariableNode , args []parse .Node , final reflect .Value ) reflect .Value {
s .at (variable )
value := s .varValue (variable .Ident [0 ])
if len (variable .Ident ) == 1 {
s .notAFunction (args , final )
return value
}
return s .evalFieldChain (dot , value , variable , variable .Ident [1 :], args , final )
}
func (s *state ) evalFieldChain (dot , receiver reflect .Value , node parse .Node , ident []string , args []parse .Node , final reflect .Value ) reflect .Value {
n := len (ident )
for i := 0 ; i < n -1 ; i ++ {
receiver = s .evalField (dot , ident [i ], node , nil , missingVal , receiver )
}
return s .evalField (dot , ident [n -1 ], node , args , final , receiver )
}
func (s *state ) evalFunction (dot reflect .Value , node *parse .IdentifierNode , cmd parse .Node , args []parse .Node , final reflect .Value ) reflect .Value {
s .at (node )
name := node .Ident
function , isBuiltin , ok := findFunction (name , s .tmpl )
if !ok {
s .errorf ("%q is not a defined function" , name )
}
return s .evalCall (dot , function , isBuiltin , cmd , name , args , final )
}
func (s *state ) evalField (dot reflect .Value , fieldName string , node parse .Node , args []parse .Node , final , receiver reflect .Value ) reflect .Value {
if !receiver .IsValid () {
if s .tmpl .option .missingKey == mapError {
s .errorf ("nil data; no entry for key %q" , fieldName )
}
return zero
}
typ := receiver .Type ()
receiver , isNil := indirect (receiver )
if receiver .Kind () == reflect .Interface && isNil {
s .errorf ("nil pointer evaluating %s.%s" , typ , fieldName )
return zero
}
ptr := receiver
if ptr .Kind () != reflect .Interface && ptr .Kind () != reflect .Pointer && ptr .CanAddr () {
ptr = ptr .Addr ()
}
if method := ptr .MethodByName (fieldName ); method .IsValid () {
return s .evalCall (dot , method , false , node , fieldName , args , final )
}
hasArgs := len (args ) > 1 || !isMissing (final )
switch receiver .Kind () {
case reflect .Struct :
tField , ok := receiver .Type ().FieldByName (fieldName )
if ok {
field , err := receiver .FieldByIndexErr (tField .Index )
if !tField .IsExported () {
s .errorf ("%s is an unexported field of struct type %s" , fieldName , typ )
}
if err != nil {
s .errorf ("%v" , err )
}
if hasArgs {
s .errorf ("%s has arguments but cannot be invoked as function" , fieldName )
}
return field
}
case reflect .Map :
nameVal := reflect .ValueOf (fieldName )
if nameVal .Type ().AssignableTo (receiver .Type ().Key ()) {
if hasArgs {
s .errorf ("%s is not a method but has arguments" , fieldName )
}
result := receiver .MapIndex (nameVal )
if !result .IsValid () {
switch s .tmpl .option .missingKey {
case mapInvalid :
case mapZeroValue :
result = reflect .Zero (receiver .Type ().Elem ())
case mapError :
s .errorf ("map has no entry for key %q" , fieldName )
}
}
return result
}
case reflect .Pointer :
etyp := receiver .Type ().Elem ()
if etyp .Kind () == reflect .Struct {
if _ , ok := etyp .FieldByName (fieldName ); !ok {
break
}
}
if isNil {
s .errorf ("nil pointer evaluating %s.%s" , typ , fieldName )
}
}
s .errorf ("can't evaluate field %s in type %s" , fieldName , typ )
panic ("not reached" )
}
var (
errorType = reflect .TypeFor [error ]()
fmtStringerType = reflect .TypeFor [fmt .Stringer ]()
reflectValueType = reflect .TypeFor [reflect .Value ]()
)
func (s *state ) evalCall (dot , fun reflect .Value , isBuiltin bool , node parse .Node , name string , args []parse .Node , final reflect .Value ) reflect .Value {
if args != nil {
args = args [1 :]
}
typ := fun .Type ()
numIn := len (args )
if !isMissing (final ) {
numIn ++
}
numFixed := len (args )
if typ .IsVariadic () {
numFixed = typ .NumIn () - 1
if numIn < numFixed {
s .errorf ("wrong number of args for %s: want at least %d got %d" , name , typ .NumIn ()-1 , len (args ))
}
} else if numIn != typ .NumIn () {
s .errorf ("wrong number of args for %s: want %d got %d" , name , typ .NumIn (), numIn )
}
if err := goodFunc (name , typ ); err != nil {
s .errorf ("%v" , err )
}
unwrap := func (v reflect .Value ) reflect .Value {
if v .Type () == reflectValueType {
v = v .Interface ().(reflect .Value )
}
return v
}
if isBuiltin && (name == "and" || name == "or" ) {
argType := typ .In (0 )
var v reflect .Value
for _ , arg := range args {
v = s .evalArg (dot , argType , arg ).Interface ().(reflect .Value )
if truth (v ) == (name == "or" ) {
return v
}
}
if final != missingVal {
v = unwrap (s .validateType (final , argType ))
}
return v
}
argv := make ([]reflect .Value , numIn )
i := 0
for ; i < numFixed && i < len (args ); i ++ {
argv [i ] = s .evalArg (dot , typ .In (i ), args [i ])
}
if typ .IsVariadic () {
argType := typ .In (typ .NumIn () - 1 ).Elem ()
for ; i < len (args ); i ++ {
argv [i ] = s .evalArg (dot , argType , args [i ])
}
}
if !isMissing (final ) {
t := typ .In (typ .NumIn () - 1 )
if typ .IsVariadic () {
if numIn -1 < numFixed {
t = typ .In (numIn - 1 )
} else {
t = t .Elem ()
}
}
argv [i ] = s .validateType (final , t )
}
if isBuiltin && name == "call" {
calleeName := args [0 ].String ()
argv = append ([]reflect .Value {reflect .ValueOf (calleeName )}, argv ...)
fun = reflect .ValueOf (call )
}
v , err := safeCall (fun , argv )
if err != nil {
s .at (node )
s .errorf ("error calling %s: %w" , name , err )
}
return unwrap (v )
}
func canBeNil(typ reflect .Type ) bool {
switch typ .Kind () {
case reflect .Chan , reflect .Func , reflect .Interface , reflect .Map , reflect .Pointer , reflect .Slice :
return true
case reflect .Struct :
return typ == reflectValueType
}
return false
}
func (s *state ) validateType (value reflect .Value , typ reflect .Type ) reflect .Value {
if !value .IsValid () {
if typ == nil {
return reflect .ValueOf (nil )
}
if canBeNil (typ ) {
return reflect .Zero (typ )
}
s .errorf ("invalid value; expected %s" , typ )
}
if typ == reflectValueType && value .Type () != typ {
return reflect .ValueOf (value )
}
if typ != nil && !value .Type ().AssignableTo (typ ) {
if value .Kind () == reflect .Interface && !value .IsNil () {
value = value .Elem ()
if value .Type ().AssignableTo (typ ) {
return value
}
}
switch {
case value .Kind () == reflect .Pointer && value .Type ().Elem ().AssignableTo (typ ):
value = value .Elem ()
if !value .IsValid () {
s .errorf ("dereference of nil pointer of type %s" , typ )
}
case reflect .PointerTo (value .Type ()).AssignableTo (typ ) && value .CanAddr ():
value = value .Addr ()
default :
s .errorf ("wrong type for value; expected %s; got %s" , typ , value .Type ())
}
}
return value
}
func (s *state ) evalArg (dot reflect .Value , typ reflect .Type , n parse .Node ) reflect .Value {
s .at (n )
switch arg := n .(type ) {
case *parse .DotNode :
return s .validateType (dot , typ )
case *parse .NilNode :
if canBeNil (typ ) {
return reflect .Zero (typ )
}
s .errorf ("cannot assign nil to %s" , typ )
case *parse .FieldNode :
return s .validateType (s .evalFieldNode (dot , arg , []parse .Node {n }, missingVal ), typ )
case *parse .VariableNode :
return s .validateType (s .evalVariableNode (dot , arg , nil , missingVal ), typ )
case *parse .PipeNode :
return s .validateType (s .evalPipeline (dot , arg ), typ )
case *parse .IdentifierNode :
return s .validateType (s .evalFunction (dot , arg , arg , nil , missingVal ), typ )
case *parse .ChainNode :
return s .validateType (s .evalChainNode (dot , arg , nil , missingVal ), typ )
}
switch typ .Kind () {
case reflect .Bool :
return s .evalBool (typ , n )
case reflect .Complex64 , reflect .Complex128 :
return s .evalComplex (typ , n )
case reflect .Float32 , reflect .Float64 :
return s .evalFloat (typ , n )
case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
return s .evalInteger (typ , n )
case reflect .Interface :
if typ .NumMethod () == 0 {
return s .evalEmptyInterface (dot , n )
}
case reflect .Struct :
if typ == reflectValueType {
return reflect .ValueOf (s .evalEmptyInterface (dot , n ))
}
case reflect .String :
return s .evalString (typ , n )
case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 , reflect .Uintptr :
return s .evalUnsignedInteger (typ , n )
}
s .errorf ("can't handle %s for arg of type %s" , n , typ )
panic ("not reached" )
}
func (s *state ) evalBool (typ reflect .Type , n parse .Node ) reflect .Value {
s .at (n )
if n , ok := n .(*parse .BoolNode ); ok {
value := reflect .New (typ ).Elem ()
value .SetBool (n .True )
return value
}
s .errorf ("expected bool; found %s" , n )
panic ("not reached" )
}
func (s *state ) evalString (typ reflect .Type , n parse .Node ) reflect .Value {
s .at (n )
if n , ok := n .(*parse .StringNode ); ok {
value := reflect .New (typ ).Elem ()
value .SetString (n .Text )
return value
}
s .errorf ("expected string; found %s" , n )
panic ("not reached" )
}
func (s *state ) evalInteger (typ reflect .Type , n parse .Node ) reflect .Value {
s .at (n )
if n , ok := n .(*parse .NumberNode ); ok && n .IsInt {
value := reflect .New (typ ).Elem ()
value .SetInt (n .Int64 )
return value
}
s .errorf ("expected integer; found %s" , n )
panic ("not reached" )
}
func (s *state ) evalUnsignedInteger (typ reflect .Type , n parse .Node ) reflect .Value {
s .at (n )
if n , ok := n .(*parse .NumberNode ); ok && n .IsUint {
value := reflect .New (typ ).Elem ()
value .SetUint (n .Uint64 )
return value
}
s .errorf ("expected unsigned integer; found %s" , n )
panic ("not reached" )
}
func (s *state ) evalFloat (typ reflect .Type , n parse .Node ) reflect .Value {
s .at (n )
if n , ok := n .(*parse .NumberNode ); ok && n .IsFloat {
value := reflect .New (typ ).Elem ()
value .SetFloat (n .Float64 )
return value
}
s .errorf ("expected float; found %s" , n )
panic ("not reached" )
}
func (s *state ) evalComplex (typ reflect .Type , n parse .Node ) reflect .Value {
if n , ok := n .(*parse .NumberNode ); ok && n .IsComplex {
value := reflect .New (typ ).Elem ()
value .SetComplex (n .Complex128 )
return value
}
s .errorf ("expected complex; found %s" , n )
panic ("not reached" )
}
func (s *state ) evalEmptyInterface (dot reflect .Value , n parse .Node ) reflect .Value {
s .at (n )
switch n := n .(type ) {
case *parse .BoolNode :
return reflect .ValueOf (n .True )
case *parse .DotNode :
return dot
case *parse .FieldNode :
return s .evalFieldNode (dot , n , nil , missingVal )
case *parse .IdentifierNode :
return s .evalFunction (dot , n , n , nil , missingVal )
case *parse .NilNode :
s .errorf ("evalEmptyInterface: nil (can't happen)" )
case *parse .NumberNode :
return s .idealConstant (n )
case *parse .StringNode :
return reflect .ValueOf (n .Text )
case *parse .VariableNode :
return s .evalVariableNode (dot , n , nil , missingVal )
case *parse .PipeNode :
return s .evalPipeline (dot , n )
}
s .errorf ("can't handle assignment of %s to empty interface argument" , n )
panic ("not reached" )
}
func indirect(v reflect .Value ) (rv reflect .Value , isNil bool ) {
for ; v .Kind () == reflect .Pointer || v .Kind () == reflect .Interface ; v = v .Elem () {
if v .IsNil () {
return v , true
}
}
return v , false
}
func indirectInterface(v reflect .Value ) reflect .Value {
if v .Kind () != reflect .Interface {
return v
}
if v .IsNil () {
return reflect .Value {}
}
return v .Elem ()
}
func (s *state ) printValue (n parse .Node , v reflect .Value ) {
s .at (n )
iface , ok := printableValue (v )
if !ok {
s .errorf ("can't print %s of type %s" , n , v .Type ())
}
_ , err := fmt .Fprint (s .wr , iface )
if err != nil {
s .writeError (err )
}
}
func printableValue(v reflect .Value ) (any , bool ) {
if v .Kind () == reflect .Pointer {
v , _ = indirect (v )
}
if !v .IsValid () {
return "<no value>" , true
}
if !v .Type ().Implements (errorType ) && !v .Type ().Implements (fmtStringerType ) {
if v .CanAddr () && (reflect .PointerTo (v .Type ()).Implements (errorType ) || reflect .PointerTo (v .Type ()).Implements (fmtStringerType )) {
v = v .Addr ()
} else {
switch v .Kind () {
case reflect .Chan , reflect .Func :
return nil , false
}
}
}
return v .Interface (), true
}
The pages are generated with Golds v0.7.0-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .