mirror of
https://github.com/golang/go
synced 2024-11-19 14:54:43 -07:00
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 https://golang.org/cl/4576042
This commit is contained in:
parent
f35a3df80c
commit
9e857dbdcc
@ -6,7 +6,9 @@ include ../../Make.inc
|
||||
|
||||
TARG=template
|
||||
GOFILES=\
|
||||
doc.go\
|
||||
execute.go\
|
||||
format.go\
|
||||
template.go\
|
||||
parse.go\
|
||||
|
||||
include ../../Make.pkg
|
||||
|
91
src/pkg/template/doc.go
Normal file
91
src/pkg/template/doc.go
Normal file
@ -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
|
346
src/pkg/template/execute.go
Normal file
346
src/pkg/template/execute.go
Normal file
@ -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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user