| | |
| | |
| | |
| |
|
| | 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.UnsafePointer, 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.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, |
| | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: |
| | if len(r.Pipe.Decl) > 1 { |
| | s.errorf("can't use %v to iterate over more than one variable", val) |
| | break |
| | } |
| | run := false |
| | for v := range val.Seq() { |
| | run = true |
| | |
| | oneIteration(reflect.Value{}, v) |
| | } |
| | if !run { |
| | break |
| | } |
| | return |
| | 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 |
| | case reflect.Func: |
| | if val.Type().CanSeq() { |
| | if len(r.Pipe.Decl) > 1 { |
| | s.errorf("can't use %v iterate over more than one variable", val) |
| | break |
| | } |
| | run := false |
| | for v := range val.Seq() { |
| | run = true |
| | |
| | |
| | oneIteration(reflect.Value{}, v) |
| | } |
| | if !run { |
| | break |
| | } |
| | return |
| | } |
| | if val.Type().CanSeq2() { |
| | run := false |
| | for i, v := range val.Seq2() { |
| | run = true |
| | if len(r.Pipe.Decl) > 1 { |
| | oneIteration(i, v) |
| | } else { |
| | |
| | |
| | |
| | oneIteration(reflect.Value{}, i) |
| | } |
| | } |
| | if !run { |
| | break |
| | } |
| | return |
| | } |
| | fallthrough |
| | 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.Equal(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" { |
| | var calleeName string |
| | if len(args) == 0 { |
| | |
| | calleeName = final.String() |
| | } else { |
| | 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 |
| | } |
| |
|