summaryrefslogtreecommitdiff
path: root/src/pkg/template
diff options
context:
space:
mode:
authorRob Pike <r@golang.org>2011-06-06 21:33:02 +0000
committerRob Pike <r@golang.org>2011-06-06 21:33:02 +0000
commit19bc64f4e51c8965172f1fe4a7e07410270c7254 (patch)
tree424456f212cb38facabf277921eac48f8e71a83e /src/pkg/template
parent823e6c7c758bd7692b9ed7b8d8e30dc88c78ef00 (diff)
downloadgo-19bc64f4e51c8965172f1fe4a7e07410270c7254.tar.gz
template: rearrange the code into separate files.
The single file was getting unwieldy. Also remove use of vector; a slice works fine - although it's an unusual one. R=golang-dev, r, gri CC=golang-dev http://codereview.appspot.com/4576042
Diffstat (limited to 'src/pkg/template')
-rw-r--r--src/pkg/template/Makefile4
-rw-r--r--src/pkg/template/doc.go91
-rw-r--r--src/pkg/template/execute.go346
-rw-r--r--src/pkg/template/parse.go (renamed from src/pkg/template/template.go)486
4 files changed, 474 insertions, 453 deletions
diff --git a/src/pkg/template/Makefile b/src/pkg/template/Makefile
index 4915527b4..4f1e06527 100644
--- a/src/pkg/template/Makefile
+++ b/src/pkg/template/Makefile
@@ -6,7 +6,9 @@ include ../../Make.inc
TARG=template
GOFILES=\
+ doc.go\
+ execute.go\
format.go\
- template.go\
+ parse.go\
include ../../Make.pkg
diff --git a/src/pkg/template/doc.go b/src/pkg/template/doc.go
new file mode 100644
index 000000000..e778d801d
--- /dev/null
+++ b/src/pkg/template/doc.go
@@ -0,0 +1,91 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ Package template implements data-driven templates for generating textual
+ output such as HTML.
+
+ Templates are executed by applying them to a data structure.
+ Annotations in the template refer to elements of the data
+ structure (typically a field of a struct or a key in a map)
+ to control execution and derive values to be displayed.
+ The template walks the structure as it executes and the
+ "cursor" @ represents the value at the current location
+ in the structure.
+
+ Data items may be values or pointers; the interface hides the
+ indirection.
+
+ In the following, 'Field' is one of several things, according to the data.
+
+ - The name of a field of a struct (result = data.Field),
+ - The value stored in a map under that key (result = data["Field"]), or
+ - The result of invoking a niladic single-valued method with that name
+ (result = data.Field())
+
+ If Field is a struct field or method name, it must be an exported
+ (capitalized) name.
+
+ Major constructs ({} are the default delimiters for template actions;
+ [] are the notation in this comment for optional elements):
+
+ {# comment }
+
+ A one-line comment.
+
+ {.section field} XXX [ {.or} YYY ] {.end}
+
+ Set @ to the value of the field. It may be an explicit @
+ to stay at the same point in the data. If the field is nil
+ or empty, execute YYY; otherwise execute XXX.
+
+ {.repeated section field} XXX [ {.alternates with} ZZZ ] [ {.or} YYY ] {.end}
+
+ Like .section, but field must be an array or slice. XXX
+ is executed for each element. If the array is nil or empty,
+ YYY is executed instead. If the {.alternates with} marker
+ is present, ZZZ is executed between iterations of XXX.
+
+ {field}
+ {field1 field2 ...}
+ {field|formatter}
+ {field1 field2...|formatter}
+ {field|formatter1|formatter2}
+
+ Insert the value of the fields into the output. Each field is
+ first looked for in the cursor, as in .section and .repeated.
+ If it is not found, the search continues in outer sections
+ until the top level is reached.
+
+ If the field value is a pointer, leading asterisks indicate
+ that the value to be inserted should be evaluated through the
+ pointer. For example, if x.p is of type *int, {x.p} will
+ insert the value of the pointer but {*x.p} will insert the
+ value of the underlying integer. If the value is nil or not a
+ pointer, asterisks have no effect.
+
+ If a formatter is specified, it must be named in the formatter
+ map passed to the template set up routines or in the default
+ set ("html","str","") and is used to process the data for
+ output. The formatter function has signature
+ func(wr io.Writer, formatter string, data ...interface{})
+ where wr is the destination for output, data holds the field
+ values at the instantiation, and formatter is its name at
+ the invocation site. The default formatter just concatenates
+ the string representations of the fields.
+
+ Multiple formatters separated by the pipeline character | are
+ executed sequentially, with each formatter receiving the bytes
+ emitted by the one to its left.
+
+ As well as field names, one may use literals with Go syntax.
+ Integer, floating-point, and string literals are supported.
+ Raw strings may not span newlines.
+
+ The delimiter strings get their default value, "{" and "}", from
+ JSON-template. They may be set to any non-empty, space-free
+ string using the SetDelims method. Their value can be printed
+ in the output using {.meta-left} and {.meta-right}.
+*/
+package template
diff --git a/src/pkg/template/execute.go b/src/pkg/template/execute.go
new file mode 100644
index 000000000..5bc7ff7e9
--- /dev/null
+++ b/src/pkg/template/execute.go
@@ -0,0 +1,346 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Code to execute a parsed template.
+
+package template
+
+import (
+ "bytes"
+ "io"
+ "reflect"
+ "strings"
+)
+
+// Internal state for executing a Template. As we evaluate the struct,
+// the data item descends into the fields associated with sections, etc.
+// Parent is used to walk upwards to find variables higher in the tree.
+type state struct {
+ parent *state // parent in hierarchy
+ data reflect.Value // the driver data for this section etc.
+ wr io.Writer // where to send output
+ buf [2]bytes.Buffer // alternating buffers used when chaining formatters
+}
+
+func (parent *state) clone(data reflect.Value) *state {
+ return &state{parent: parent, data: data, wr: parent.wr}
+}
+
+// Evaluate interfaces and pointers looking for a value that can look up the name, via a
+// struct field, method, or map key, and return the result of the lookup.
+func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value {
+ for v.IsValid() {
+ typ := v.Type()
+ if n := v.Type().NumMethod(); n > 0 {
+ for i := 0; i < n; i++ {
+ m := typ.Method(i)
+ mtyp := m.Type
+ if m.Name == name && mtyp.NumIn() == 1 && mtyp.NumOut() == 1 {
+ if !isExported(name) {
+ t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type())
+ }
+ return v.Method(i).Call(nil)[0]
+ }
+ }
+ }
+ switch av := v; av.Kind() {
+ case reflect.Ptr:
+ v = av.Elem()
+ case reflect.Interface:
+ v = av.Elem()
+ case reflect.Struct:
+ if !isExported(name) {
+ t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type())
+ }
+ return av.FieldByName(name)
+ case reflect.Map:
+ if v := av.MapIndex(reflect.ValueOf(name)); v.IsValid() {
+ return v
+ }
+ return reflect.Zero(typ.Elem())
+ default:
+ return reflect.Value{}
+ }
+ }
+ return v
+}
+
+// indirectPtr returns the item numLevels levels of indirection below the value.
+// It is forgiving: if the value is not a pointer, it returns it rather than giving
+// an error. If the pointer is nil, it is returned as is.
+func indirectPtr(v reflect.Value, numLevels int) reflect.Value {
+ for i := numLevels; v.IsValid() && i > 0; i++ {
+ if p := v; p.Kind() == reflect.Ptr {
+ if p.IsNil() {
+ return v
+ }
+ v = p.Elem()
+ } else {
+ break
+ }
+ }
+ return v
+}
+
+// Walk v through pointers and interfaces, extracting the elements within.
+func indirect(v reflect.Value) reflect.Value {
+loop:
+ for v.IsValid() {
+ switch av := v; av.Kind() {
+ case reflect.Ptr:
+ v = av.Elem()
+ case reflect.Interface:
+ v = av.Elem()
+ default:
+ break loop
+ }
+ }
+ return v
+}
+
+// If the data for this template is a struct, find the named variable.
+// Names of the form a.b.c are walked down the data tree.
+// The special name "@" (the "cursor") denotes the current data.
+// The value coming in (st.data) might need indirecting to reach
+// a struct while the return value is not indirected - that is,
+// it represents the actual named field. Leading stars indicate
+// levels of indirection to be applied to the value.
+func (t *Template) findVar(st *state, s string) reflect.Value {
+ data := st.data
+ flattenedName := strings.TrimLeft(s, "*")
+ numStars := len(s) - len(flattenedName)
+ s = flattenedName
+ if s == "@" {
+ return indirectPtr(data, numStars)
+ }
+ for _, elem := range strings.Split(s, ".", -1) {
+ // Look up field; data must be a struct or map.
+ data = t.lookup(st, data, elem)
+ if !data.IsValid() {
+ return reflect.Value{}
+ }
+ }
+ return indirectPtr(data, numStars)
+}
+
+// Is there no data to look at?
+func empty(v reflect.Value) bool {
+ v = indirect(v)
+ if !v.IsValid() {
+ return true
+ }
+ switch v.Kind() {
+ case reflect.Bool:
+ return v.Bool() == false
+ case reflect.String:
+ return v.String() == ""
+ case reflect.Struct:
+ return false
+ case reflect.Map:
+ return false
+ case reflect.Array:
+ return v.Len() == 0
+ case reflect.Slice:
+ return v.Len() == 0
+ }
+ return false
+}
+
+// Look up a variable or method, up through the parent if necessary.
+func (t *Template) varValue(name string, st *state) reflect.Value {
+ field := t.findVar(st, name)
+ if !field.IsValid() {
+ if st.parent == nil {
+ t.execError(st, t.linenum, "name not found: %s in type %s", name, st.data.Type())
+ }
+ return t.varValue(name, st.parent)
+ }
+ return field
+}
+
+func (t *Template) format(wr io.Writer, fmt string, val []interface{}, v *variableElement, st *state) {
+ fn := t.formatter(fmt)
+ if fn == nil {
+ t.execError(st, v.linenum, "missing formatter %s for variable", fmt)
+ }
+ fn(wr, fmt, val...)
+}
+
+// Evaluate a variable, looking up through the parent if necessary.
+// If it has a formatter attached ({var|formatter}) run that too.
+func (t *Template) writeVariable(v *variableElement, st *state) {
+ // Resolve field names
+ val := make([]interface{}, len(v.args))
+ for i, arg := range v.args {
+ if name, ok := arg.(fieldName); ok {
+ val[i] = t.varValue(string(name), st).Interface()
+ } else {
+ val[i] = arg
+ }
+ }
+ for i, fmt := range v.fmts[:len(v.fmts)-1] {
+ b := &st.buf[i&1]
+ b.Reset()
+ t.format(b, fmt, val, v, st)
+ val = val[0:1]
+ val[0] = b.Bytes()
+ }
+ t.format(st.wr, v.fmts[len(v.fmts)-1], val, v, st)
+}
+
+// Execute element i. Return next index to execute.
+func (t *Template) executeElement(i int, st *state) int {
+ switch elem := t.elems[i].(type) {
+ case *textElement:
+ st.wr.Write(elem.text)
+ return i + 1
+ case *literalElement:
+ st.wr.Write(elem.text)
+ return i + 1
+ case *variableElement:
+ t.writeVariable(elem, st)
+ return i + 1
+ case *sectionElement:
+ t.executeSection(elem, st)
+ return elem.end
+ case *repeatedElement:
+ t.executeRepeated(elem, st)
+ return elem.end
+ }
+ e := t.elems[i]
+ t.execError(st, 0, "internal error: bad directive in execute: %v %T\n", reflect.ValueOf(e).Interface(), e)
+ return 0
+}
+
+// Execute the template.
+func (t *Template) execute(start, end int, st *state) {
+ for i := start; i < end; {
+ i = t.executeElement(i, st)
+ }
+}
+
+// Execute a .section
+func (t *Template) executeSection(s *sectionElement, st *state) {
+ // Find driver data for this section. It must be in the current struct.
+ field := t.varValue(s.field, st)
+ if !field.IsValid() {
+ t.execError(st, s.linenum, ".section: cannot find field %s in %s", s.field, st.data.Type())
+ }
+ st = st.clone(field)
+ start, end := s.start, s.or
+ if !empty(field) {
+ // Execute the normal block.
+ if end < 0 {
+ end = s.end
+ }
+ } else {
+ // Execute the .or block. If it's missing, do nothing.
+ start, end = s.or, s.end
+ if start < 0 {
+ return
+ }
+ }
+ for i := start; i < end; {
+ i = t.executeElement(i, st)
+ }
+}
+
+// Return the result of calling the Iter method on v, or nil.
+func iter(v reflect.Value) reflect.Value {
+ for j := 0; j < v.Type().NumMethod(); j++ {
+ mth := v.Type().Method(j)
+ fv := v.Method(j)
+ ft := fv.Type()
+ // TODO(rsc): NumIn() should return 0 here, because ft is from a curried FuncValue.
+ if mth.Name != "Iter" || ft.NumIn() != 1 || ft.NumOut() != 1 {
+ continue
+ }
+ ct := ft.Out(0)
+ if ct.Kind() != reflect.Chan ||
+ ct.ChanDir()&reflect.RecvDir == 0 {
+ continue
+ }
+ return fv.Call(nil)[0]
+ }
+ return reflect.Value{}
+}
+
+// Execute a .repeated section
+func (t *Template) executeRepeated(r *repeatedElement, st *state) {
+ // Find driver data for this section. It must be in the current struct.
+ field := t.varValue(r.field, st)
+ if !field.IsValid() {
+ t.execError(st, r.linenum, ".repeated: cannot find field %s in %s", r.field, st.data.Type())
+ }
+ field = indirect(field)
+
+ start, end := r.start, r.or
+ if end < 0 {
+ end = r.end
+ }
+ if r.altstart >= 0 {
+ end = r.altstart
+ }
+ first := true
+
+ // Code common to all the loops.
+ loopBody := func(newst *state) {
+ // .alternates between elements
+ if !first && r.altstart >= 0 {
+ for i := r.altstart; i < r.altend; {
+ i = t.executeElement(i, newst)
+ }
+ }
+ first = false
+ for i := start; i < end; {
+ i = t.executeElement(i, newst)
+ }
+ }
+
+ if array := field; array.Kind() == reflect.Array || array.Kind() == reflect.Slice {
+ for j := 0; j < array.Len(); j++ {
+ loopBody(st.clone(array.Index(j)))
+ }
+ } else if m := field; m.Kind() == reflect.Map {
+ for _, key := range m.MapKeys() {
+ loopBody(st.clone(m.MapIndex(key)))
+ }
+ } else if ch := iter(field); ch.IsValid() {
+ for {
+ e, ok := ch.Recv()
+ if !ok {
+ break
+ }
+ loopBody(st.clone(e))
+ }
+ } else {
+ t.execError(st, r.linenum, ".repeated: cannot repeat %s (type %s)",
+ r.field, field.Type())
+ }
+
+ if first {
+ // Empty. Execute the .or block, once. If it's missing, do nothing.
+ start, end := r.or, r.end
+ if start >= 0 {
+ newst := st.clone(field)
+ for i := start; i < end; {
+ i = t.executeElement(i, newst)
+ }
+ }
+ return
+ }
+}
+
+// A valid delimiter must contain no space and be non-empty.
+func validDelim(d []byte) bool {
+ if len(d) == 0 {
+ return false
+ }
+ for _, c := range d {
+ if isSpace(c) {
+ return false
+ }
+ }
+ return true
+}
diff --git a/src/pkg/template/template.go b/src/pkg/template/parse.go
index f481cbd1e..b4aa5fcd2 100644
--- a/src/pkg/template/template.go
+++ b/src/pkg/template/parse.go
@@ -2,97 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-/*
- Package template implements data-driven templates for generating textual
- output such as HTML.
+// Code to parse a template.
- Templates are executed by applying them to a data structure.
- Annotations in the template refer to elements of the data
- structure (typically a field of a struct or a key in a map)
- to control execution and derive values to be displayed.
- The template walks the structure as it executes and the
- "cursor" @ represents the value at the current location
- in the structure.
-
- Data items may be values or pointers; the interface hides the
- indirection.
-
- In the following, 'Field' is one of several things, according to the data.
-
- - The name of a field of a struct (result = data.Field),
- - The value stored in a map under that key (result = data["Field"]), or
- - The result of invoking a niladic single-valued method with that name
- (result = data.Field())
-
- If Field is a struct field or method name, it must be an exported
- (capitalized) name.
-
- Major constructs ({} are the default delimiters for template actions;
- [] are the notation in this comment for optional elements):
-
- {# comment }
-
- A one-line comment.
-
- {.section field} XXX [ {.or} YYY ] {.end}
-
- Set @ to the value of the field. It may be an explicit @
- to stay at the same point in the data. If the field is nil
- or empty, execute YYY; otherwise execute XXX.
-
- {.repeated section field} XXX [ {.alternates with} ZZZ ] [ {.or} YYY ] {.end}
-
- Like .section, but field must be an array or slice. XXX
- is executed for each element. If the array is nil or empty,
- YYY is executed instead. If the {.alternates with} marker
- is present, ZZZ is executed between iterations of XXX.
-
- {field}
- {field1 field2 ...}
- {field|formatter}
- {field1 field2...|formatter}
- {field|formatter1|formatter2}
-
- Insert the value of the fields into the output. Each field is
- first looked for in the cursor, as in .section and .repeated.
- If it is not found, the search continues in outer sections
- until the top level is reached.
-
- If the field value is a pointer, leading asterisks indicate
- that the value to be inserted should be evaluated through the
- pointer. For example, if x.p is of type *int, {x.p} will
- insert the value of the pointer but {*x.p} will insert the
- value of the underlying integer. If the value is nil or not a
- pointer, asterisks have no effect.
-
- If a formatter is specified, it must be named in the formatter
- map passed to the template set up routines or in the default
- set ("html","str","") and is used to process the data for
- output. The formatter function has signature
- func(wr io.Writer, formatter string, data ...interface{})
- where wr is the destination for output, data holds the field
- values at the instantiation, and formatter is its name at
- the invocation site. The default formatter just concatenates
- the string representations of the fields.
-
- Multiple formatters separated by the pipeline character | are
- executed sequentially, with each formatter receiving the bytes
- emitted by the one to its left.
-
- As well as field names, one may use literals with Go syntax.
- Integer, floating-point, and string literals are supported.
- Raw strings may not span newlines.
-
- The delimiter strings get their default value, "{" and "}", from
- JSON-template. They may be set to any non-empty, space-free
- string using the SetDelims method. Their value can be printed
- in the output using {.meta-left} and {.meta-right}.
-*/
package template
import (
- "bytes"
- "container/vector"
"fmt"
"io"
"io/ioutil"
@@ -113,6 +27,19 @@ type Error struct {
func (e *Error) String() string { return fmt.Sprintf("line %d: %s", e.Line, e.Msg) }
+// checkError is a deferred function to turn a panic with type *Error into a plain error return.
+// Other panics are unexpected and so are re-enabled.
+func checkError(error *os.Error) {
+ if v := recover(); v != nil {
+ if e, ok := v.(*Error); ok {
+ *error = e
+ } else {
+ // runtime errors should crash
+ panic(v)
+ }
+ }
+}
+
// Most of the literals are aces.
var lbrace = []byte{'{'}
var rbrace = []byte{'}'}
@@ -192,21 +119,7 @@ type Template struct {
p int // position in buf
linenum int // position in input
// Parsed results:
- elems *vector.Vector
-}
-
-// Internal state for executing a Template. As we evaluate the struct,
-// the data item descends into the fields associated with sections, etc.
-// Parent is used to walk upwards to find variables higher in the tree.
-type state struct {
- parent *state // parent in hierarchy
- data reflect.Value // the driver data for this section etc.
- wr io.Writer // where to send output
- buf [2]bytes.Buffer // alternating buffers used when chaining formatters
-}
-
-func (parent *state) clone(data reflect.Value) *state {
- return &state{parent: parent, data: data, wr: parent.wr}
+ elems []interface{}
}
// New creates a new template with the specified formatter map (which
@@ -216,7 +129,7 @@ func New(fmap FormatterMap) *Template {
t.fmap = fmap
t.ldelim = lbrace
t.rdelim = rbrace
- t.elems = new(vector.Vector)
+ t.elems = make([]interface{}, 0, 16)
return t
}
@@ -583,24 +496,24 @@ func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) {
case tokComment:
return
case tokText:
- t.elems.Push(&textElement{item})
+ t.elems = append(t.elems, &textElement{item})
return
case tokLiteral:
switch w[0] {
case ".meta-left":
- t.elems.Push(&literalElement{t.ldelim})
+ t.elems = append(t.elems, &literalElement{t.ldelim})
case ".meta-right":
- t.elems.Push(&literalElement{t.rdelim})
+ t.elems = append(t.elems, &literalElement{t.rdelim})
case ".space":
- t.elems.Push(&literalElement{space})
+ t.elems = append(t.elems, &literalElement{space})
case ".tab":
- t.elems.Push(&literalElement{tab})
+ t.elems = append(t.elems, &literalElement{tab})
default:
t.parseError("internal error: unknown literal: %s", w[0])
}
return
case tokVariable:
- t.elems.Push(t.newVariable(w))
+ t.elems = append(t.elems, t.newVariable(w))
return
}
return false, tok, w
@@ -610,11 +523,11 @@ func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) {
func (t *Template) parseRepeated(words []string) *repeatedElement {
r := new(repeatedElement)
- t.elems.Push(r)
+ t.elems = append(t.elems, r)
r.linenum = t.linenum
r.field = words[2]
// Scan section, collecting true and false (.or) blocks.
- r.start = t.elems.Len()
+ r.start = len(t.elems)
r.or = -1
r.altstart = -1
r.altend = -1
@@ -637,8 +550,8 @@ Loop:
t.parseError("extra .or in .repeated section")
break Loop
}
- r.altend = t.elems.Len()
- r.or = t.elems.Len()
+ r.altend = len(t.elems)
+ r.or = len(t.elems)
case tokSection:
t.parseSection(w)
case tokRepeated:
@@ -652,26 +565,26 @@ Loop:
t.parseError(".alternates inside .or block in .repeated section")
break Loop
}
- r.altstart = t.elems.Len()
+ r.altstart = len(t.elems)
default:
t.parseError("internal error: unknown repeated section item: %s", item)
break Loop
}
}
if r.altend < 0 {
- r.altend = t.elems.Len()
+ r.altend = len(t.elems)
}
- r.end = t.elems.Len()
+ r.end = len(t.elems)
return r
}
func (t *Template) parseSection(words []string) *sectionElement {
s := new(sectionElement)
- t.elems.Push(s)
+ t.elems = append(t.elems, s)
s.linenum = t.linenum
s.field = words[1]
// Scan section, collecting true and false (.or) blocks.
- s.start = t.elems.Len()
+ s.start = len(t.elems)
s.or = -1
Loop:
for {
@@ -692,7 +605,7 @@ Loop:
t.parseError("extra .or in .section")
break Loop
}
- s.or = t.elems.Len()
+ s.or = len(t.elems)
case tokSection:
t.parseSection(w)
case tokRepeated:
@@ -703,7 +616,7 @@ Loop:
t.parseError("internal error: unknown section item: %s", item)
}
}
- s.end = t.elems.Len()
+ s.end = len(t.elems)
return s
}
@@ -732,337 +645,6 @@ func (t *Template) parse() {
// -- Execution
-// Evaluate interfaces and pointers looking for a value that can look up the name, via a
-// struct field, method, or map key, and return the result of the lookup.
-func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value {
- for v.IsValid() {
- typ := v.Type()
- if n := v.Type().NumMethod(); n > 0 {
- for i := 0; i < n; i++ {
- m := typ.Method(i)
- mtyp := m.Type
- if m.Name == name && mtyp.NumIn() == 1 && mtyp.NumOut() == 1 {
- if !isExported(name) {
- t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type())
- }
- return v.Method(i).Call(nil)[0]
- }
- }
- }
- switch av := v; av.Kind() {
- case reflect.Ptr:
- v = av.Elem()
- case reflect.Interface:
- v = av.Elem()
- case reflect.Struct:
- if !isExported(name) {
- t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type())
- }
- return av.FieldByName(name)
- case reflect.Map:
- if v := av.MapIndex(reflect.ValueOf(name)); v.IsValid() {
- return v
- }
- return reflect.Zero(typ.Elem())
- default:
- return reflect.Value{}
- }
- }
- return v
-}
-
-// indirectPtr returns the item numLevels levels of indirection below the value.
-// It is forgiving: if the value is not a pointer, it returns it rather than giving
-// an error. If the pointer is nil, it is returned as is.
-func indirectPtr(v reflect.Value, numLevels int) reflect.Value {
- for i := numLevels; v.IsValid() && i > 0; i++ {
- if p := v; p.Kind() == reflect.Ptr {
- if p.IsNil() {
- return v
- }
- v = p.Elem()
- } else {
- break
- }
- }
- return v
-}
-
-// Walk v through pointers and interfaces, extracting the elements within.
-func indirect(v reflect.Value) reflect.Value {
-loop:
- for v.IsValid() {
- switch av := v; av.Kind() {
- case reflect.Ptr:
- v = av.Elem()
- case reflect.Interface:
- v = av.Elem()
- default:
- break loop
- }
- }
- return v
-}
-
-// If the data for this template is a struct, find the named variable.
-// Names of the form a.b.c are walked down the data tree.
-// The special name "@" (the "cursor") denotes the current data.
-// The value coming in (st.data) might need indirecting to reach
-// a struct while the return value is not indirected - that is,
-// it represents the actual named field. Leading stars indicate
-// levels of indirection to be applied to the value.
-func (t *Template) findVar(st *state, s string) reflect.Value {
- data := st.data
- flattenedName := strings.TrimLeft(s, "*")
- numStars := len(s) - len(flattenedName)
- s = flattenedName
- if s == "@" {
- return indirectPtr(data, numStars)
- }
- for _, elem := range strings.Split(s, ".", -1) {
- // Look up field; data must be a struct or map.
- data = t.lookup(st, data, elem)
- if !data.IsValid() {
- return reflect.Value{}
- }
- }
- return indirectPtr(data, numStars)
-}
-
-// Is there no data to look at?
-func empty(v reflect.Value) bool {
- v = indirect(v)
- if !v.IsValid() {
- return true
- }
- switch v.Kind() {
- case reflect.Bool:
- return v.Bool() == false
- case reflect.String:
- return v.String() == ""
- case reflect.Struct:
- return false
- case reflect.Map:
- return false
- case reflect.Array:
- return v.Len() == 0
- case reflect.Slice:
- return v.Len() == 0
- }
- return false
-}
-
-// Look up a variable or method, up through the parent if necessary.
-func (t *Template) varValue(name string, st *state) reflect.Value {
- field := t.findVar(st, name)
- if !field.IsValid() {
- if st.parent == nil {
- t.execError(st, t.linenum, "name not found: %s in type %s", name, st.data.Type())
- }
- return t.varValue(name, st.parent)
- }
- return field
-}
-
-func (t *Template) format(wr io.Writer, fmt string, val []interface{}, v *variableElement, st *state) {
- fn := t.formatter(fmt)
- if fn == nil {
- t.execError(st, v.linenum, "missing formatter %s for variable", fmt)
- }
- fn(wr, fmt, val...)
-}
-
-// Evaluate a variable, looking up through the parent if necessary.
-// If it has a formatter attached ({var|formatter}) run that too.
-func (t *Template) writeVariable(v *variableElement, st *state) {
- // Resolve field names
- val := make([]interface{}, len(v.args))
- for i, arg := range v.args {
- if name, ok := arg.(fieldName); ok {
- val[i] = t.varValue(string(name), st).Interface()
- } else {
- val[i] = arg
- }
- }
- for i, fmt := range v.fmts[:len(v.fmts)-1] {
- b := &st.buf[i&1]
- b.Reset()
- t.format(b, fmt, val, v, st)
- val = val[0:1]
- val[0] = b.Bytes()
- }
- t.format(st.wr, v.fmts[len(v.fmts)-1], val, v, st)
-}
-
-// Execute element i. Return next index to execute.
-func (t *Template) executeElement(i int, st *state) int {
- switch elem := t.elems.At(i).(type) {
- case *textElement:
- st.wr.Write(elem.text)
- return i + 1
- case *literalElement:
- st.wr.Write(elem.text)
- return i + 1
- case *variableElement:
- t.writeVariable(elem, st)
- return i + 1
- case *sectionElement:
- t.executeSection(elem, st)
- return elem.end
- case *repeatedElement:
- t.executeRepeated(elem, st)
- return elem.end
- }
- e := t.elems.At(i)
- t.execError(st, 0, "internal error: bad directive in execute: %v %T\n", reflect.ValueOf(e).Interface(), e)
- return 0
-}
-
-// Execute the template.
-func (t *Template) execute(start, end int, st *state) {
- for i := start; i < end; {
- i = t.executeElement(i, st)
- }
-}
-
-// Execute a .section
-func (t *Template) executeSection(s *sectionElement, st *state) {
- // Find driver data for this section. It must be in the current struct.
- field := t.varValue(s.field, st)
- if !field.IsValid() {
- t.execError(st, s.linenum, ".section: cannot find field %s in %s", s.field, st.data.Type())
- }
- st = st.clone(field)
- start, end := s.start, s.or
- if !empty(field) {
- // Execute the normal block.
- if end < 0 {
- end = s.end
- }
- } else {
- // Execute the .or block. If it's missing, do nothing.
- start, end = s.or, s.end
- if start < 0 {
- return
- }
- }
- for i := start; i < end; {
- i = t.executeElement(i, st)
- }
-}
-
-// Return the result of calling the Iter method on v, or nil.
-func iter(v reflect.Value) reflect.Value {
- for j := 0; j < v.Type().NumMethod(); j++ {
- mth := v.Type().Method(j)
- fv := v.Method(j)
- ft := fv.Type()
- // TODO(rsc): NumIn() should return 0 here, because ft is from a curried FuncValue.
- if mth.Name != "Iter" || ft.NumIn() != 1 || ft.NumOut() != 1 {
- continue
- }
- ct := ft.Out(0)
- if ct.Kind() != reflect.Chan ||
- ct.ChanDir()&reflect.RecvDir == 0 {
- continue
- }
- return fv.Call(nil)[0]
- }
- return reflect.Value{}
-}
-
-// Execute a .repeated section
-func (t *Template) executeRepeated(r *repeatedElement, st *state) {
- // Find driver data for this section. It must be in the current struct.
- field := t.varValue(r.field, st)
- if !field.IsValid() {
- t.execError(st, r.linenum, ".repeated: cannot find field %s in %s", r.field, st.data.Type())
- }
- field = indirect(field)
-
- start, end := r.start, r.or
- if end < 0 {
- end = r.end
- }
- if r.altstart >= 0 {
- end = r.altstart
- }
- first := true
-
- // Code common to all the loops.
- loopBody := func(newst *state) {
- // .alternates between elements
- if !first && r.altstart >= 0 {
- for i := r.altstart; i < r.altend; {
- i = t.executeElement(i, newst)
- }
- }
- first = false
- for i := start; i < end; {
- i = t.executeElement(i, newst)
- }
- }
-
- if array := field; array.Kind() == reflect.Array || array.Kind() == reflect.Slice {
- for j := 0; j < array.Len(); j++ {
- loopBody(st.clone(array.Index(j)))
- }
- } else if m := field; m.Kind() == reflect.Map {
- for _, key := range m.MapKeys() {
- loopBody(st.clone(m.MapIndex(key)))
- }
- } else if ch := iter(field); ch.IsValid() {
- for {
- e, ok := ch.Recv()
- if !ok {
- break
- }
- loopBody(st.clone(e))
- }
- } else {
- t.execError(st, r.linenum, ".repeated: cannot repeat %s (type %s)",
- r.field, field.Type())
- }
-
- if first {
- // Empty. Execute the .or block, once. If it's missing, do nothing.
- start, end := r.or, r.end
- if start >= 0 {
- newst := st.clone(field)
- for i := start; i < end; {
- i = t.executeElement(i, newst)
- }
- }
- return
- }
-}
-
-// A valid delimiter must contain no space and be non-empty.
-func validDelim(d []byte) bool {
- if len(d) == 0 {
- return false
- }
- for _, c := range d {
- if isSpace(c) {
- return false
- }
- }
- return true
-}
-
-// checkError is a deferred function to turn a panic with type *Error into a plain error return.
-// Other panics are unexpected and so are re-enabled.
-func checkError(error *os.Error) {
- if v := recover(); v != nil {
- if e, ok := v.(*Error); ok {
- *error = e
- } else {
- // runtime errors should crash
- panic(v)
- }
- }
-}
-
// -- Public interface
// Parse initializes a Template by parsing its definition. The string
@@ -1100,7 +682,7 @@ func (t *Template) Execute(wr io.Writer, data interface{}) (err os.Error) {
val := reflect.ValueOf(data)
defer checkError(&err)
t.p = 0
- t.execute(0, t.elems.Len(), &state{parent: nil, data: val, wr: wr})
+ t.execute(0, len(t.elems), &state{parent: nil, data: val, wr: wr})
return nil
}