]> Cypherpunks repositories - gostls13.git/commitdiff
template: rearrange the code into separate files.
authorRob Pike <r@golang.org>
Mon, 6 Jun 2011 21:33:02 +0000 (21:33 +0000)
committerRob Pike <r@golang.org>
Mon, 6 Jun 2011 21:33:02 +0000 (21:33 +0000)
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
https://golang.org/cl/4576042

src/pkg/template/Makefile
src/pkg/template/doc.go [new file with mode: 0644]
src/pkg/template/execute.go [new file with mode: 0644]
src/pkg/template/parse.go [moved from src/pkg/template/template.go with 58% similarity]

index 4915527b44c51ff5f23916fabbf2008a77c13b0e..4f1e06527d7bb2b9cfad1e92375f3a16465b4e26 100644 (file)
@@ -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 (file)
index 0000000..e778d80
--- /dev/null
@@ -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 (file)
index 0000000..5bc7ff7
--- /dev/null
@@ -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
+}
similarity index 58%
rename from src/pkg/template/template.go
rename to src/pkg/template/parse.go
index f481cbd1ee36e7b455adf3099d0006b358e8d5ab..b4aa5fcd2d35fae230276c75d64911bb0c9a3ae3 100644 (file)
@@ -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
 }