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=\
exp/norm\
exp/regexp/syntax\
exp/template\
exp/template/parse\
expvar\
flag\
fmt\
......
......@@ -9,7 +9,6 @@ GOFILES=\
exec.go\
funcs.go\
helper.go\
lex.go\
parse.go\
set.go\
......
This diff is collapsed.
......@@ -6,6 +6,7 @@ package template
import (
"bytes"
"flag"
"fmt"
"os"
"reflect"
......@@ -14,6 +15,8 @@ import (
"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.
type T struct {
// Basics
......
......@@ -22,7 +22,7 @@ import (
// during execution, execution terminates and Execute returns an error.
type FuncMap map[string]interface{}
var funcs = map[string]reflect.Value{
var builtins = map[string]reflect.Value{
"and": reflect.ValueOf(and),
"html": reflect.ValueOf(HTMLEscaper),
"index": reflect.ValueOf(index),
......@@ -73,7 +73,7 @@ func findFunction(name string, tmpl *Template, set *Set) (reflect.Value, bool) {
return fn, true
}
}
if fn := funcs[name]; fn.IsValid() {
if fn := builtins[name]; fn.IsValid() {
return fn, true
}
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 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package template
package parse
import (
"fmt"
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package template
package parse
import (
"reflect"
......
This diff is collapsed.
This diff is collapsed.
......@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package template
package parse
import (
"flag"
"fmt"
"reflect"
"testing"
)
......@@ -100,47 +101,47 @@ func TestNumberParse(t *testing.T) {
}
continue
}
if n.isComplex != test.isComplex {
if n.IsComplex != test.isComplex {
t.Errorf("complex incorrect for %q; should be %t", test.text, test.isComplex)
}
if test.isInt {
if !n.isInt {
if !n.IsInt {
t.Errorf("expected integer for %q", test.text)
}
if n.int64 != test.int64 {
t.Errorf("int64 for %q should be %d is %d", test.text, test.int64, n.int64)
if n.Int64 != test.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)
}
if test.isUint {
if !n.isUint {
if !n.IsUint {
t.Errorf("expected unsigned integer for %q", test.text)
}
if n.uint64 != test.uint64 {
t.Errorf("uint64 for %q should be %d is %d", test.text, test.uint64, n.uint64)
if n.Uint64 != test.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)
}
if test.isFloat {
if !n.isFloat {
if !n.IsFloat {
t.Errorf("expected float for %q", test.text)
}
if n.float64 != test.float64 {
t.Errorf("float64 for %q should be %g is %g", test.text, test.float64, n.float64)
if n.Float64 != test.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)
}
if test.isComplex {
if !n.isComplex {
if !n.IsComplex {
t.Errorf("expected complex for %q", test.text)
}
if n.complex128 != test.complex128 {
t.Errorf("complex128 for %q should be %g is %g", test.text, test.complex128, n.complex128)
if n.Complex128 != test.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)
}
}
......@@ -161,6 +162,8 @@ const (
var parseTests = []parseTest{
{"empty", "", noError,
`[]`},
{"comment", "{{/*\n\n\n*/}}", noError,
`[]`},
{"spaces", " \t\n", noError,
`[(text: " \t\n")]`},
{"text", "some text", noError,
......@@ -228,9 +231,13 @@ var parseTests = []parseTest{
{"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) {
for _, test := range parseTests {
tmpl, err := New(test.name).Parse(test.input)
tmpl, err := New(test.name).Parse(test.input, builtins)
switch {
case err == nil && !test.ok:
t.Errorf("%q: expected error; got none", test.name)
......@@ -245,7 +252,7 @@ func TestParse(t *testing.T) {
}
continue
}
result := tmpl.root.String()
result := tmpl.Root.String()
if 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 @@
package template
import (
"exp/template/parse"
"fmt"
"io"
"os"
"reflect"
"runtime"
"strconv"
)
// 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 {
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
// the output to wr.
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)
}
// 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
// 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
// overwritten with the new definition.
func (s *Set) Parse(text string) (set *Set, err os.Error) {
set = s
func (s *Set) Parse(text string) (*Set, os.Error) {
trees, err := parse.Set(text, s.funcs, builtins)
if err != nil {
return nil, err
}
s.init()
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 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
for name, tree := range trees {
tmpl := New(name)
tmpl.Tree = tree
tmpl.addToSet(s)
s.tmpl[name] = tmpl
}
return s, nil
}
......@@ -9,6 +9,11 @@ import (
"testing"
)
const (
noError = true
hasError = false
)
type setParseTest struct {
name string
input string
......@@ -66,7 +71,7 @@ func TestSetParse(t *testing.T) {
t.Errorf("%s: can't find template %q", test.name, name)
continue
}
result := tmpl.root.String()
result := tmpl.Root.String()
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])
}
......
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