Commit c66917d2 authored by Rob Pike's avatar Rob Pike

exp/template: split the parse tree into a separate package exp/template/parse

Mostly a mechanical change, with a few cleanups to make the split easier.
The external interface to exp/template is unaffected.

In another round I will play with the function map setup to see if I can
avoid exposing reflect across the boundary, but that will require some
structural changes I did not want to mix into this CL.

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4849049
parent 476150f4
...@@ -83,6 +83,7 @@ DIRS=\ ...@@ -83,6 +83,7 @@ DIRS=\
exp/norm\ exp/norm\
exp/regexp/syntax\ exp/regexp/syntax\
exp/template\ exp/template\
exp/template/parse\
expvar\ expvar\
flag\ flag\
fmt\ fmt\
......
...@@ -9,7 +9,6 @@ GOFILES=\ ...@@ -9,7 +9,6 @@ GOFILES=\
exec.go\ exec.go\
funcs.go\ funcs.go\
helper.go\ helper.go\
lex.go\
parse.go\ parse.go\
set.go\ set.go\
......
This diff is collapsed.
...@@ -6,6 +6,7 @@ package template ...@@ -6,6 +6,7 @@ package template
import ( import (
"bytes" "bytes"
"flag"
"fmt" "fmt"
"os" "os"
"reflect" "reflect"
...@@ -14,6 +15,8 @@ import ( ...@@ -14,6 +15,8 @@ import (
"testing" "testing"
) )
var debug = flag.Bool("debug", false, "show the errors produced by the tests")
// T has lots of interesting pieces to use to test execution. // T has lots of interesting pieces to use to test execution.
type T struct { type T struct {
// Basics // Basics
......
...@@ -22,7 +22,7 @@ import ( ...@@ -22,7 +22,7 @@ import (
// during execution, execution terminates and Execute returns an error. // during execution, execution terminates and Execute returns an error.
type FuncMap map[string]interface{} type FuncMap map[string]interface{}
var funcs = map[string]reflect.Value{ var builtins = map[string]reflect.Value{
"and": reflect.ValueOf(and), "and": reflect.ValueOf(and),
"html": reflect.ValueOf(HTMLEscaper), "html": reflect.ValueOf(HTMLEscaper),
"index": reflect.ValueOf(index), "index": reflect.ValueOf(index),
...@@ -73,7 +73,7 @@ func findFunction(name string, tmpl *Template, set *Set) (reflect.Value, bool) { ...@@ -73,7 +73,7 @@ func findFunction(name string, tmpl *Template, set *Set) (reflect.Value, bool) {
return fn, true return fn, true
} }
} }
if fn := funcs[name]; fn.IsValid() { if fn := builtins[name]; fn.IsValid() {
return fn, true return fn, true
} }
return reflect.Value{}, false return reflect.Value{}, false
......
This diff is collapsed.
# 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.
include ../../../../Make.inc
TARG=exp/template/parse
GOFILES=\
lex.go\
node.go\
parse.go\
set.go\
include ../../../../Make.pkg
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package template package parse
import ( import (
"fmt" "fmt"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package template package parse
import ( import (
"reflect" "reflect"
......
This diff is collapsed.
This diff is collapsed.
...@@ -2,11 +2,12 @@ ...@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package template package parse
import ( import (
"flag" "flag"
"fmt" "fmt"
"reflect"
"testing" "testing"
) )
...@@ -100,47 +101,47 @@ func TestNumberParse(t *testing.T) { ...@@ -100,47 +101,47 @@ func TestNumberParse(t *testing.T) {
} }
continue continue
} }
if n.isComplex != test.isComplex { if n.IsComplex != test.isComplex {
t.Errorf("complex incorrect for %q; should be %t", test.text, test.isComplex) t.Errorf("complex incorrect for %q; should be %t", test.text, test.isComplex)
} }
if test.isInt { if test.isInt {
if !n.isInt { if !n.IsInt {
t.Errorf("expected integer for %q", test.text) t.Errorf("expected integer for %q", test.text)
} }
if n.int64 != test.int64 { if n.Int64 != test.int64 {
t.Errorf("int64 for %q should be %d is %d", test.text, test.int64, n.int64) t.Errorf("int64 for %q should be %d Is %d", test.text, test.int64, n.Int64)
} }
} else if n.isInt { } else if n.IsInt {
t.Errorf("did not expect integer for %q", test.text) t.Errorf("did not expect integer for %q", test.text)
} }
if test.isUint { if test.isUint {
if !n.isUint { if !n.IsUint {
t.Errorf("expected unsigned integer for %q", test.text) t.Errorf("expected unsigned integer for %q", test.text)
} }
if n.uint64 != test.uint64 { if n.Uint64 != test.uint64 {
t.Errorf("uint64 for %q should be %d is %d", test.text, test.uint64, n.uint64) t.Errorf("uint64 for %q should be %d Is %d", test.text, test.uint64, n.Uint64)
} }
} else if n.isUint { } else if n.IsUint {
t.Errorf("did not expect unsigned integer for %q", test.text) t.Errorf("did not expect unsigned integer for %q", test.text)
} }
if test.isFloat { if test.isFloat {
if !n.isFloat { if !n.IsFloat {
t.Errorf("expected float for %q", test.text) t.Errorf("expected float for %q", test.text)
} }
if n.float64 != test.float64 { if n.Float64 != test.float64 {
t.Errorf("float64 for %q should be %g is %g", test.text, test.float64, n.float64) t.Errorf("float64 for %q should be %g Is %g", test.text, test.float64, n.Float64)
} }
} else if n.isFloat { } else if n.IsFloat {
t.Errorf("did not expect float for %q", test.text) t.Errorf("did not expect float for %q", test.text)
} }
if test.isComplex { if test.isComplex {
if !n.isComplex { if !n.IsComplex {
t.Errorf("expected complex for %q", test.text) t.Errorf("expected complex for %q", test.text)
} }
if n.complex128 != test.complex128 { if n.Complex128 != test.complex128 {
t.Errorf("complex128 for %q should be %g is %g", test.text, test.complex128, n.complex128) t.Errorf("complex128 for %q should be %g Is %g", test.text, test.complex128, n.Complex128)
} }
} else if n.isComplex { } else if n.IsComplex {
t.Errorf("did not expect complex for %q", test.text) t.Errorf("did not expect complex for %q", test.text)
} }
} }
...@@ -161,6 +162,8 @@ const ( ...@@ -161,6 +162,8 @@ const (
var parseTests = []parseTest{ var parseTests = []parseTest{
{"empty", "", noError, {"empty", "", noError,
`[]`}, `[]`},
{"comment", "{{/*\n\n\n*/}}", noError,
`[]`},
{"spaces", " \t\n", noError, {"spaces", " \t\n", noError,
`[(text: " \t\n")]`}, `[(text: " \t\n")]`},
{"text", "some text", noError, {"text", "some text", noError,
...@@ -228,9 +231,13 @@ var parseTests = []parseTest{ ...@@ -228,9 +231,13 @@ var parseTests = []parseTest{
{"too many decls in range", "{{range $u, $v, $w := 3}}{{end}}", hasError, ""}, {"too many decls in range", "{{range $u, $v, $w := 3}}{{end}}", hasError, ""},
} }
var builtins = map[string]reflect.Value{
"printf": reflect.ValueOf(fmt.Sprintf),
}
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
for _, test := range parseTests { for _, test := range parseTests {
tmpl, err := New(test.name).Parse(test.input) tmpl, err := New(test.name).Parse(test.input, builtins)
switch { switch {
case err == nil && !test.ok: case err == nil && !test.ok:
t.Errorf("%q: expected error; got none", test.name) t.Errorf("%q: expected error; got none", test.name)
...@@ -245,7 +252,7 @@ func TestParse(t *testing.T) { ...@@ -245,7 +252,7 @@ func TestParse(t *testing.T) {
} }
continue continue
} }
result := tmpl.root.String() result := tmpl.Root.String()
if result != test.result { if result != test.result {
t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.result) t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.result)
} }
......
// 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 parse
import (
"fmt"
"os"
"reflect"
"strconv"
)
// Set returns a slice of Trees created by parsing the template set
// definition in the argument string. If an error is encountered,
// parsing stops and an empty slice is returned with the error.
func Set(text string, funcs ...map[string]reflect.Value) (tree map[string]*Tree, err os.Error) {
tree = make(map[string]*Tree)
defer (*Tree)(nil).recover(&err)
lex := lex("set", text)
const context = "define clause"
for {
t := New("set") // name will be updated once we know it.
t.startParse(funcs, lex)
// Expect EOF or "{{ define name }}".
if t.atEOF() {
break
}
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.Type() != NodeEnd {
t.errorf("unexpected %s in %s", end, context)
}
t.stopParse()
if _, present := tree[t.Name]; present {
return nil, fmt.Errorf("template: %q multiply defined", name)
}
tree[t.Name] = t
}
return
}
...@@ -5,12 +5,11 @@ ...@@ -5,12 +5,11 @@
package template package template
import ( import (
"exp/template/parse"
"fmt" "fmt"
"io" "io"
"os" "os"
"reflect" "reflect"
"runtime"
"strconv"
) )
// Set holds a set of related templates that can refer to one another by name. // Set holds a set of related templates that can refer to one another by name.
...@@ -71,6 +70,11 @@ func (s *Set) Template(name string) *Template { ...@@ -71,6 +70,11 @@ func (s *Set) Template(name string) *Template {
return s.tmpl[name] return s.tmpl[name]
} }
// FuncMap returns the set's function map.
func (s *Set) FuncMap() map[string]reflect.Value {
return s.funcs
}
// Execute applies the named template to the specified data object, writing // Execute applies the named template to the specified data object, writing
// the output to wr. // the output to wr.
func (s *Set) Execute(wr io.Writer, name string, data interface{}) os.Error { func (s *Set) Execute(wr io.Writer, name string, data interface{}) os.Error {
...@@ -81,55 +85,21 @@ func (s *Set) Execute(wr io.Writer, name string, data interface{}) os.Error { ...@@ -81,55 +85,21 @@ func (s *Set) Execute(wr io.Writer, name string, data interface{}) os.Error {
return tmpl.Execute(wr, data) return tmpl.Execute(wr, data)
} }
// 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 a string into a set of named templates. Parse may be called // Parse parses a string into a set of named templates. Parse may be called
// multiple times for a given set, adding the templates defined in the string // multiple times for a given set, adding the templates defined in the string
// to the set. If a template is redefined, the element in the set is // to the set. If a template is redefined, the element in the set is
// overwritten with the new definition. // overwritten with the new definition.
func (s *Set) Parse(text string) (set *Set, err os.Error) { func (s *Set) Parse(text string) (*Set, os.Error) {
set = s trees, err := parse.Set(text, s.funcs, builtins)
if err != nil {
return nil, err
}
s.init() s.init()
defer s.recover(&err) for name, tree := range trees {
lex := lex("set", text) tmpl := New(name)
const context = "define clause" tmpl.Tree = tree
for { tmpl.addToSet(s)
t := New("set") // name will be updated once we know it. s.tmpl[name] = tmpl
t.startParse(s, lex)
// Expect EOF or "{{ define name }}".
if t.atEOF() {
return nil, err
}
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()
t.addToSet(s)
s.tmpl[t.name] = t
} }
return s, nil return s, nil
} }
...@@ -9,6 +9,11 @@ import ( ...@@ -9,6 +9,11 @@ import (
"testing" "testing"
) )
const (
noError = true
hasError = false
)
type setParseTest struct { type setParseTest struct {
name string name string
input string input string
...@@ -66,7 +71,7 @@ func TestSetParse(t *testing.T) { ...@@ -66,7 +71,7 @@ func TestSetParse(t *testing.T) {
t.Errorf("%s: can't find template %q", test.name, name) t.Errorf("%s: can't find template %q", test.name, name)
continue continue
} }
result := tmpl.root.String() result := tmpl.Root.String()
if result != test.results[i] { if result != test.results[i] {
t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.results[i]) t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.results[i])
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment