aboutsummaryrefslogtreecommitdiff
path: root/src/pkg/text/template/exec.go
diff options
context:
space:
mode:
authorRob Pike <r@golang.org>2012-09-24 13:23:15 +1000
committerRob Pike <r@golang.org>2012-09-24 13:23:15 +1000
commit9050550c12e2d09cf8f0c22a270cfa90120cdf6d (patch)
tree014cb848c742805de43d7d3f5293e830af2baef6 /src/pkg/text/template/exec.go
parentedce6349639e321c3b1a34036a8fbc08ad363cd3 (diff)
downloadgo-9050550c12e2d09cf8f0c22a270cfa90120cdf6d.tar.xz
text/template: allow .Field access to parenthesized expressions
Change the grammar so that field access is a proper operator. This introduces a new node, ChainNode, into the public (but actually internal) API of text/template/parse. For compatibility, we only use the new node type for the specific construct, which was not parseable before. Therefore this should be backward-compatible. Before, .X.Y was a token in the lexer; this CL breaks it out into .Y applied to .X. But for compatibility we mush them back together before delivering. One day we might remove that hack; it's the simple TODO in parse.go/operand. This change also provides grammatical distinction between f and (f) which might permit function values later, but not now. Fixes #3999. R=golang-dev, dsymonds, gri, rsc, mikesamuel CC=golang-dev https://golang.org/cl/6494119
Diffstat (limited to 'src/pkg/text/template/exec.go')
-rw-r--r--src/pkg/text/template/exec.go19
1 files changed, 17 insertions, 2 deletions
diff --git a/src/pkg/text/template/exec.go b/src/pkg/text/template/exec.go
index 1739a86179..5e127d7db4 100644
--- a/src/pkg/text/template/exec.go
+++ b/src/pkg/text/template/exec.go
@@ -315,9 +315,15 @@ func (s *state) evalCommand(dot reflect.Value, cmd *parse.CommandNode, final ref
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:
// Must be a function.
return s.evalFunction(dot, n.Ident, cmd.Args, final)
+ case *parse.PipeNode:
+ // Parenthesized pipeline. The arguments are all inside the pipeline; final is ignored.
+ // TODO: is this right?
+ return s.evalPipeline(dot, n)
case *parse.VariableNode:
return s.evalVariableNode(dot, n, cmd.Args, final)
}
@@ -367,6 +373,15 @@ func (s *state) evalFieldNode(dot reflect.Value, field *parse.FieldNode, args []
return s.evalFieldChain(dot, dot, field.Ident, args, final)
}
+func (s *state) evalChainNode(dot reflect.Value, chain *parse.ChainNode, args []parse.Node, final reflect.Value) reflect.Value {
+ // (pipe).Field1.Field2 has pipe as .Node, fields as .Field. Eval the pipeline, then the fields.
+ pipe := s.evalArg(dot, nil, chain.Node)
+ if len(chain.Field) == 0 {
+ s.errorf("internal error: no fields in evalChainNode")
+ }
+ return s.evalFieldChain(dot, pipe, chain.Field, args, final)
+}
+
func (s *state) evalVariableNode(dot reflect.Value, v *parse.VariableNode, args []parse.Node, final reflect.Value) reflect.Value {
// $x.Field has $x as the first ident, Field as the second. Eval the var, then the fields.
value := s.varValue(v.Ident[0])
@@ -521,13 +536,13 @@ func canBeNil(typ reflect.Type) bool {
// validateType guarantees that the value is valid and assignable to the type.
func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Value {
if !value.IsValid() {
- if canBeNil(typ) {
+ if typ == nil || canBeNil(typ) {
// An untyped nil interface{}. Accept as a proper nil value.
return reflect.Zero(typ)
}
s.errorf("invalid value; expected %s", typ)
}
- if !value.Type().AssignableTo(typ) {
+ if typ != nil && !value.Type().AssignableTo(typ) {
if value.Kind() == reflect.Interface && !value.IsNil() {
value = value.Elem()
if value.Type().AssignableTo(typ) {