"os"
"reflect"
"strings"
+ "unicode"
+ "utf8"
)
// state represents the state of an execution. It's not part of the
s.walk(data, node)
}
case *ifNode:
+ s.line = n.line
s.walkIfOrWith(nodeIf, data, n.pipeline, n.list, n.elseList)
case *rangeNode:
+ s.line = n.line
s.walkRange(data, n)
case *textNode:
if _, err := s.wr.Write(n.text); err != nil {
s.error(err)
}
case *templateNode:
+ s.line = n.line
s.walkTemplate(data, n)
case *withNode:
+ s.line = n.line
s.walkIfOrWith(nodeWith, data, n.pipeline, n.list, n.elseList)
default:
s.errorf("unknown node: %s", n)
return s.evalFieldOrCall(data, field.ident[n-1], args, final)
}
+// Is this an exported - upper case - name?
+func isExported(name string) bool {
+ rune, _ := utf8.DecodeRuneInString(name)
+ return unicode.IsUpper(rune)
+}
+
func (s *state) evalField(data reflect.Value, fieldName string) reflect.Value {
for data.Kind() == reflect.Ptr {
data = reflect.Indirect(data)
field := data.FieldByName(fieldName)
// TODO: look higher up the tree if we can't find it here. Also unexported fields
// might succeed higher up, as map keys.
- if field.IsValid() && field.Type().PkgPath() == "" { // valid and exported
+ if field.IsValid() && isExported(fieldName) { // valid and exported
return field
}
s.errorf("%s has no field %s", data.Type(), fieldName)
ptr, data = data, reflect.Indirect(data)
}
// Is it a method? We use the pointer because it has value methods too.
- if method, ok := ptr.Type().MethodByName(fieldName); ok {
+ if method, ok := methodByName(ptr.Type(), fieldName); ok {
return s.evalCall(ptr, method.Func, fieldName, true, args, final)
}
if len(args) > 1 || final.IsValid() {
panic("not reached")
}
+// TODO: delete when reflect's own MethodByName is released.
+func methodByName(typ reflect.Type, name string) (reflect.Method, bool) {
+ for i := 0; i < typ.NumMethod(); i++ {
+ if typ.Method(i).Name == name {
+ return typ.Method(i), true
+ }
+ }
+ return reflect.Method{}, false
+}
+
var (
osErrorType = reflect.TypeOf(new(os.Error)).Elem()
)
// state functions
-const leftMeta = "{{"
-const rightMeta = "}}"
+const (
+ leftMeta = "{{"
+ rightMeta = "}}"
+ leftComment = "{{/*"
+ rightComment = "*/}}"
+)
// lexText scans until a metacharacter
func lexText(l *lexer) stateFn {
// leftMeta scans the left "metacharacter", which is known to be present.
func lexLeftMeta(l *lexer) stateFn {
+ if strings.HasPrefix(l.input[l.pos:], leftComment) {
+ return lexComment
+ }
l.pos += len(leftMeta)
l.emit(itemLeftMeta)
return lexInsideAction
}
+// lexComment scans a comment. The left comment marker is known to be present.
+func lexComment(l *lexer) stateFn {
+ i := strings.Index(l.input[l.pos:], rightComment)
+ if i < 0 {
+ return l.errorf("unclosed comment")
+ }
+ l.pos += i + len(rightComment)
+ l.ignore()
+ return lexText
+}
+
// rightMeta scans the right "metacharacter", which is known to be present.
func lexRightMeta(l *lexer) stateFn {
l.pos += len(rightMeta)
{"empty", "", []item{tEOF}},
{"spaces", " \t\n", []item{{itemText, " \t\n"}, tEOF}},
{"text", `now is the time`, []item{{itemText, "now is the time"}, tEOF}},
+ {"text with comment", "hello-{{/* this is a comment */}}-world", []item{
+ {itemText, "hello-"},
+ {itemText, "-world"},
+ tEOF,
+ }},
{"empty action", `{{}}`, []item{tLeft, tRight, tEOF}},
{"for", `{{for }}`, []item{tLeft, tFor, tRight, tEOF}},
{"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}},
}
func (t *Template) parseControl(context string) (lineNum int, pipe []*commandNode, list, elseList *listNode) {
+ lineNum = t.lex.lineNumber()
pipe = t.pipeline(context)
var next node
list, next = t.itemList(false)
import (
"fmt"
+ "io"
"os"
"reflect"
"runtime"
return s
}
+// Template returns the template with the given name in the set,
+// or nil if there is no such template.
+func (s *Set) Template(name string) *Template {
+ return s.tmpl[name]
+}
+
+// Execute looks for the named template in the set and then applies that
+// template to the specified data object, writing the output to wr. Nested
+// template invocations will be resolved from the set.
+func (s *Set) Execute(name string, wr io.Writer, data interface{}) os.Error {
+ tmpl := s.tmpl[name]
+ if tmpl == nil {
+ return fmt.Errorf("template: no template %q in set", name)
+ }
+ return tmpl.ExecuteInSet(wr, data, s)
+}
+
// recover is the handler that turns panics into returns from the top
// level of Parse.
func (s *Set) recover(errp *os.Error) {