mirror of
https://github.com/golang/go
synced 2024-11-22 03:24:41 -07:00
exp/template: delete upward evaluation.
It was an ill-advised carryover from the previous template package. Also clean up function evaluation. Also add a Name method to Template. R=golang-dev, gri CC=golang-dev https://golang.org/cl/4657088
This commit is contained in:
parent
d3d08e1e3a
commit
bbf5eb5ba2
@ -22,21 +22,6 @@ type state struct {
|
|||||||
wr io.Writer
|
wr io.Writer
|
||||||
set *Set
|
set *Set
|
||||||
line int // line number for errors
|
line int // line number for errors
|
||||||
// parent holds the state for the surrounding data object,
|
|
||||||
// typically the structure containing the field we are evaluating.
|
|
||||||
parent struct {
|
|
||||||
state *state
|
|
||||||
data reflect.Value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// down returns a new state representing a child of the current state.
|
|
||||||
// data represents the parent of the child.
|
|
||||||
func (s *state) down(data reflect.Value) *state {
|
|
||||||
var child = *s
|
|
||||||
child.parent.state = s
|
|
||||||
child.parent.data = data
|
|
||||||
return &child
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var zero reflect.Value
|
var zero reflect.Value
|
||||||
@ -118,7 +103,7 @@ func (s *state) walkIfOrWith(typ nodeType, data reflect.Value, pipe *pipeNode, l
|
|||||||
}
|
}
|
||||||
if truth {
|
if truth {
|
||||||
if typ == nodeWith {
|
if typ == nodeWith {
|
||||||
s.down(data).walk(val, list)
|
s.walk(val, list)
|
||||||
} else {
|
} else {
|
||||||
s.walk(data, list)
|
s.walk(data, list)
|
||||||
}
|
}
|
||||||
@ -153,14 +138,13 @@ func isTrue(val reflect.Value) (truth, ok bool) {
|
|||||||
|
|
||||||
func (s *state) walkRange(data reflect.Value, r *rangeNode) {
|
func (s *state) walkRange(data reflect.Value, r *rangeNode) {
|
||||||
val := s.evalPipeline(data, r.pipe)
|
val := s.evalPipeline(data, r.pipe)
|
||||||
down := s.down(data)
|
|
||||||
switch val.Kind() {
|
switch val.Kind() {
|
||||||
case reflect.Array, reflect.Slice:
|
case reflect.Array, reflect.Slice:
|
||||||
if val.Len() == 0 {
|
if val.Len() == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
for i := 0; i < val.Len(); i++ {
|
for i := 0; i < val.Len(); i++ {
|
||||||
down.walk(val.Index(i), r.list)
|
s.walk(val.Index(i), r.list)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
@ -168,7 +152,7 @@ func (s *state) walkRange(data reflect.Value, r *rangeNode) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
for _, key := range val.MapKeys() {
|
for _, key := range val.MapKeys() {
|
||||||
down.walk(val.MapIndex(key), r.list)
|
s.walk(val.MapIndex(key), r.list)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
@ -216,7 +200,8 @@ func (s *state) evalCommand(data reflect.Value, cmd *commandNode, final reflect.
|
|||||||
case *fieldNode:
|
case *fieldNode:
|
||||||
return s.evalFieldNode(data, n, cmd.args, final)
|
return s.evalFieldNode(data, n, cmd.args, final)
|
||||||
case *identifierNode:
|
case *identifierNode:
|
||||||
return s.evalField(data, n.ident, cmd.args, final, true, true)
|
// Must be a function.
|
||||||
|
return s.evalFunction(data, n.ident, cmd.args, final)
|
||||||
}
|
}
|
||||||
if len(cmd.args) > 1 || final.IsValid() {
|
if len(cmd.args) > 1 || final.IsValid() {
|
||||||
s.errorf("can't give argument to non-function %s", cmd.args[0])
|
s.errorf("can't give argument to non-function %s", cmd.args[0])
|
||||||
@ -251,10 +236,18 @@ func (s *state) evalFieldNode(data reflect.Value, field *fieldNode, args []node,
|
|||||||
// Up to the last entry, it must be a field.
|
// Up to the last entry, it must be a field.
|
||||||
n := len(field.ident)
|
n := len(field.ident)
|
||||||
for i := 0; i < n-1; i++ {
|
for i := 0; i < n-1; i++ {
|
||||||
data = s.evalField(data, field.ident[i], nil, zero, i == 0, false)
|
data = s.evalField(data, field.ident[i], nil, zero, false)
|
||||||
}
|
}
|
||||||
// Now it can be a field or method and if a method, gets arguments.
|
// Now it can be a field or method and if a method, gets arguments.
|
||||||
return s.evalField(data, field.ident[n-1], args, final, len(field.ident) == 1, true)
|
return s.evalField(data, field.ident[n-1], args, final, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *state) evalFunction(data reflect.Value, name string, args []node, final reflect.Value) reflect.Value {
|
||||||
|
function, ok := findFunction(name, s.tmpl, s.set)
|
||||||
|
if !ok {
|
||||||
|
s.errorf("%q is not a defined function", name)
|
||||||
|
}
|
||||||
|
return s.evalCall(data, function, name, false, args, final)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this an exported - upper case - name?
|
// Is this an exported - upper case - name?
|
||||||
@ -268,53 +261,38 @@ func isExported(name string) bool {
|
|||||||
// value of the pipeline, if any.
|
// value of the pipeline, if any.
|
||||||
// If we're in a chain, such as (.X.Y.Z), .X and .Y cannot be methods;
|
// If we're in a chain, such as (.X.Y.Z), .X and .Y cannot be methods;
|
||||||
// canBeMethod will be true only for the last element of such chains (here .Z).
|
// canBeMethod will be true only for the last element of such chains (here .Z).
|
||||||
// The isFirst argument tells whether this is the first element of a chain (here .X).
|
|
||||||
// If true, evaluation is allowed to examine the parent to resolve the reference.
|
|
||||||
func (s *state) evalField(data reflect.Value, fieldName string, args []node, final reflect.Value,
|
func (s *state) evalField(data reflect.Value, fieldName string, args []node, final reflect.Value,
|
||||||
isFirst, canBeMethod bool) reflect.Value {
|
canBeMethod bool) reflect.Value {
|
||||||
topState, topData := s, data // Remember initial state for diagnostics.
|
typ := data.Type()
|
||||||
// Is it a function?
|
var isNil bool
|
||||||
if function, ok := findFunction(fieldName, s.tmpl, s.set); ok {
|
data, isNil = indirect(data)
|
||||||
return s.evalCall(data, function, fieldName, false, args, final)
|
if canBeMethod {
|
||||||
|
// Need to get to a value of type *T to guarantee we see all
|
||||||
|
// methods of T and *T.
|
||||||
|
ptr := data
|
||||||
|
if ptr.CanAddr() {
|
||||||
|
ptr = ptr.Addr()
|
||||||
|
}
|
||||||
|
if method, ok := methodByName(ptr.Type(), fieldName); ok {
|
||||||
|
return s.evalCall(ptr, method.Func, fieldName, true, args, final)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Look for methods and fields at this level, and then in the parent.
|
// It's not a method; is it a field of a struct?
|
||||||
for s != nil {
|
if data.Kind() == reflect.Struct {
|
||||||
var isNil bool
|
field := data.FieldByName(fieldName)
|
||||||
data, isNil = indirect(data)
|
if field.IsValid() {
|
||||||
if canBeMethod {
|
if len(args) > 1 || final.IsValid() {
|
||||||
// Need to get to a value of type *T to guarantee we see all
|
s.errorf("%s is not a method but has arguments", fieldName)
|
||||||
// methods of T and *T.
|
|
||||||
ptr := data
|
|
||||||
if ptr.CanAddr() {
|
|
||||||
ptr = ptr.Addr()
|
|
||||||
}
|
}
|
||||||
if method, ok := methodByName(ptr.Type(), fieldName); ok {
|
if isExported(fieldName) { // valid and exported
|
||||||
return s.evalCall(ptr, method.Func, fieldName, true, args, final)
|
return field
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// It's not a method; is it a field of a struct?
|
|
||||||
if data.Kind() == reflect.Struct {
|
|
||||||
field := data.FieldByName(fieldName)
|
|
||||||
if field.IsValid() {
|
|
||||||
if len(args) > 1 || final.IsValid() {
|
|
||||||
s.errorf("%s is not a method but has arguments", fieldName)
|
|
||||||
}
|
|
||||||
if isExported(fieldName) { // valid and exported
|
|
||||||
return field
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !isFirst {
|
|
||||||
// We check for nil pointers only if there's no possibility of resolution
|
|
||||||
// in the parent.
|
|
||||||
if isNil {
|
|
||||||
s.errorf("nil pointer evaluating %s.%s", topData.Type(), fieldName)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
s, data = s.parent.state, s.parent.data
|
|
||||||
}
|
}
|
||||||
topState.errorf("can't handle evaluation of field %s in type %s", fieldName, topData.Type())
|
if isNil {
|
||||||
|
s.errorf("nil pointer evaluating %s.%s", typ, fieldName)
|
||||||
|
}
|
||||||
|
s.errorf("can't handle evaluation of field %s in type %s", fieldName, typ)
|
||||||
panic("not reached")
|
panic("not reached")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -489,7 +467,7 @@ func (s *state) evalEmptyInterface(data reflect.Value, typ reflect.Type, n node)
|
|||||||
case *fieldNode:
|
case *fieldNode:
|
||||||
return s.evalFieldNode(data, n, nil, zero)
|
return s.evalFieldNode(data, n, nil, zero)
|
||||||
case *identifierNode:
|
case *identifierNode:
|
||||||
return s.evalField(data, n.ident, nil, zero, false, true)
|
return s.evalFunction(data, n.ident, nil, zero)
|
||||||
case *numberNode:
|
case *numberNode:
|
||||||
if n.isComplex {
|
if n.isComplex {
|
||||||
return reflect.ValueOf(n.complex128)
|
return reflect.ValueOf(n.complex128)
|
||||||
|
@ -249,12 +249,6 @@ var execTests = []execTest{
|
|||||||
{"with map", "{{with .MSIone}}{{.}}{{else}}EMPTY{{end}}", "map[one:1]", tVal, true},
|
{"with map", "{{with .MSIone}}{{.}}{{else}}EMPTY{{end}}", "map[one:1]", tVal, true},
|
||||||
{"with empty interface, struct field", "{{with .Empty4}}{{.V}}{{end}}", "v", tVal, true},
|
{"with empty interface, struct field", "{{with .Empty4}}{{.V}}{{end}}", "v", tVal, true},
|
||||||
|
|
||||||
// Fields and methods in parent struct.
|
|
||||||
{"with .U, get .I", "{{with .U}}{{.I}}{{end}}", "17", tVal, true},
|
|
||||||
{"with .U, do .Method0", "{{with .U}}{{.Method0}}{{end}}", "M0", tVal, true},
|
|
||||||
{"range .SI .I", "{{range .SI}}<{{.I}}>{{end}}", "<17><17><17>", tVal, true},
|
|
||||||
{"range .SI .Method0", "{{range .SI}}{{.Method0}}{{end}}", "M0M0M0", tVal, true},
|
|
||||||
|
|
||||||
// Range.
|
// Range.
|
||||||
{"range []int", "{{range .SI}}-{{.}}-{{end}}", "-3--4--5-", tVal, true},
|
{"range []int", "{{range .SI}}-{{.}}-{{end}}", "-3--4--5-", tVal, true},
|
||||||
{"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true},
|
{"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true},
|
||||||
|
@ -27,6 +27,11 @@ type Template struct {
|
|||||||
peekCount int
|
peekCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name returns the name of the template.
|
||||||
|
func (t *Template) Name() string {
|
||||||
|
return t.name
|
||||||
|
}
|
||||||
|
|
||||||
// next returns the next token.
|
// next returns the next token.
|
||||||
func (t *Template) next() item {
|
func (t *Template) next() item {
|
||||||
if t.peekCount > 0 {
|
if t.peekCount > 0 {
|
||||||
|
Loading…
Reference in New Issue
Block a user