diff options
Diffstat (limited to 'src/pkg/exp/template/set.go')
-rw-r--r-- | src/pkg/exp/template/set.go | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/src/pkg/exp/template/set.go b/src/pkg/exp/template/set.go new file mode 100644 index 000000000..492e270e1 --- /dev/null +++ b/src/pkg/exp/template/set.go @@ -0,0 +1,115 @@ +// Copyright 2011 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 + +import ( + "fmt" + "io" + "os" + "reflect" + "runtime" + "strconv" +) + +// Set holds a set of related templates that can refer to one another by name. +// A template may be a member of multiple sets. +type Set struct { + tmpl map[string]*Template + funcs map[string]reflect.Value +} + +// NewSet allocates a new, empty template set. +func NewSet() *Set { + return &Set{ + tmpl: make(map[string]*Template), + funcs: make(map[string]reflect.Value), + } +} + +// Funcs adds to the set's function map the elements of the +// argument map. It panics if a value in the map is not a function +// with appropriate return type. +// The return value is the set, so calls can be chained. +func (s *Set) Funcs(funcMap FuncMap) *Set { + addFuncs(s.funcs, funcMap) + return s +} + +// Add adds the argument templates to the set. It panics if the call +// attempts to reuse a name defined in the template. +// The return value is the set, so calls can be chained. +func (s *Set) Add(templates ...*Template) *Set { + for _, t := range templates { + if _, ok := s.tmpl[t.name]; ok { + panic(fmt.Errorf("template: %q already defined in set", t.name)) + } + s.tmpl[t.name] = t + } + 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) { + e := recover() + if e != nil { + if _, ok := e.(runtime.Error); ok { + panic(e) + } + s.tmpl = nil + *errp = e.(os.Error) + } + return +} + +// Parse parses the file into a set of named templates. +func (s *Set) Parse(text string) (err os.Error) { + defer s.recover(&err) + lex := lex("set", text) + const context = "define clause" + for { + t := New("set") // name will be updated once we know it. + t.startParse(s, lex) + // Expect EOF or "{{ define name }}". + if t.atEOF() { + return + } + t.expect(itemLeftDelim, context) + t.expect(itemDefine, context) + name := t.expect(itemString, context) + t.name, err = strconv.Unquote(name.val) + if err != nil { + t.error(err) + } + t.expect(itemRightDelim, context) + end := t.parse(false) + if end == nil { + t.errorf("unexpected EOF in %s", context) + } + if end.typ() != nodeEnd { + t.errorf("unexpected %s in %s", end, context) + } + t.stopParse() + s.tmpl[t.name] = t + } + return nil +} |