aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/template
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2011-10-26 23:57:58 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2011-10-26 23:57:58 +0000
commitd8f412571f8768df2d3239e72392dfeabbad1559 (patch)
tree19d182df05ead7ff8ba7ee00a7d57555e1383fdf /libgo/go/template
parente0c39d66d4f0607177b1cf8995dda56a667e07b3 (diff)
downloadgcc-d8f412571f8768df2d3239e72392dfeabbad1559.zip
gcc-d8f412571f8768df2d3239e72392dfeabbad1559.tar.gz
gcc-d8f412571f8768df2d3239e72392dfeabbad1559.tar.bz2
Update Go library to last weekly.
From-SVN: r180552
Diffstat (limited to 'libgo/go/template')
-rw-r--r--libgo/go/template/exec.go2
-rw-r--r--libgo/go/template/exec_test.go67
-rw-r--r--libgo/go/template/helper.go6
-rw-r--r--libgo/go/template/parse.go16
-rw-r--r--libgo/go/template/parse/lex.go52
-rw-r--r--libgo/go/template/parse/lex_test.go40
-rw-r--r--libgo/go/template/parse/node.go74
-rw-r--r--libgo/go/template/parse/parse.go7
-rw-r--r--libgo/go/template/parse/parse_test.go2
-rw-r--r--libgo/go/template/parse/set.go4
-rw-r--r--libgo/go/template/set.go14
11 files changed, 198 insertions, 86 deletions
diff --git a/libgo/go/template/exec.go b/libgo/go/template/exec.go
index f1590b3..e7fad72 100644
--- a/libgo/go/template/exec.go
+++ b/libgo/go/template/exec.go
@@ -511,7 +511,7 @@ func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Valu
// are much more constrained, so it makes more sense there than here.
// Besides, one is almost always all you need.
switch {
- case value.Kind() == reflect.Ptr && value.Elem().Type().AssignableTo(typ):
+ case value.Kind() == reflect.Ptr && value.Type().Elem().AssignableTo(typ):
value = value.Elem()
case reflect.PtrTo(value.Type()).AssignableTo(typ) && value.CanAddr():
value = value.Addr()
diff --git a/libgo/go/template/exec_test.go b/libgo/go/template/exec_test.go
index 8e1894e..50b0ad2 100644
--- a/libgo/go/template/exec_test.go
+++ b/libgo/go/template/exec_test.go
@@ -231,7 +231,7 @@ var execTests = []execTest{
{"dot complex", "<{{.}}>", "<(16.2-17i)>", 16.2 - 17i, true},
{"dot string", "<{{.}}>", "<hello>", "hello", true},
{"dot slice", "<{{.}}>", "<[-1 -2 -3]>", []int{-1, -2, -3}, true},
- {"dot map", "<{{.}}>", "<map[two:22 one:11]>", map[string]int{"one": 11, "two": 22}, true},
+ {"dot map", "<{{.}}>", "<map[two:22]>", map[string]int{"two": 22}, true},
{"dot struct", "<{{.}}>", "<{7 seven}>", struct {
a int
b string
@@ -493,6 +493,50 @@ func TestExecute(t *testing.T) {
testExecute(execTests, nil, t)
}
+var delimPairs = []string{
+ "", "", // default
+ "{{", "}}", // same as default
+ "<<", ">>", // distinct
+ "|", "|", // same
+ "(日)", "(本)", // peculiar
+}
+
+func TestDelims(t *testing.T) {
+ const hello = "Hello, world"
+ var value = struct{ Str string }{hello}
+ for i := 0; i < len(delimPairs); i += 2 {
+ text := ".Str"
+ left := delimPairs[i+0]
+ trueLeft := left
+ right := delimPairs[i+1]
+ trueRight := right
+ if left == "" { // default case
+ trueLeft = "{{"
+ }
+ if right == "" { // default case
+ trueRight = "}}"
+ }
+ text = trueLeft + text + trueRight
+ // Now add a comment
+ text += trueLeft + "/*comment*/" + trueRight
+ // Now add an action containing a string.
+ text += trueLeft + `"` + trueLeft + `"` + trueRight
+ // At this point text looks like `{{.Str}}{{/*comment*/}}{{"{{"}}`.
+ tmpl, err := New("delims").Delims(left, right).Parse(text)
+ if err != nil {
+ t.Fatalf("delim %q text %q parse err %s", left, text, err)
+ }
+ var b = new(bytes.Buffer)
+ err = tmpl.Execute(b, value)
+ if err != nil {
+ t.Fatalf("delim %q exec err %s", left, err)
+ }
+ if b.String() != hello+trueLeft {
+ t.Errorf("expected %q got %q", hello+trueLeft, b.String())
+ }
+ }
+}
+
// Check that an error from a method flows back to the top.
func TestExecuteError(t *testing.T) {
b := new(bytes.Buffer)
@@ -538,18 +582,19 @@ type Tree struct {
Left, Right *Tree
}
+// Use different delimiters to test Set.Delims.
const treeTemplate = `
- {{define "tree"}}
+ (define "tree")
[
- {{.Val}}
- {{with .Left}}
- {{template "tree" .}}
- {{end}}
- {{with .Right}}
- {{template "tree" .}}
- {{end}}
+ (.Val)
+ (with .Left)
+ (template "tree" .)
+ (end)
+ (with .Right)
+ (template "tree" .)
+ (end)
]
- {{end}}
+ (end)
`
func TestTree(t *testing.T) {
@@ -590,7 +635,7 @@ func TestTree(t *testing.T) {
},
}
set := new(Set)
- _, err := set.Parse(treeTemplate)
+ _, err := set.Delims("(", ")").Parse(treeTemplate)
if err != nil {
t.Fatal("parse error:", err)
}
diff --git a/libgo/go/template/helper.go b/libgo/go/template/helper.go
index c9b0998..1dc90f7 100644
--- a/libgo/go/template/helper.go
+++ b/libgo/go/template/helper.go
@@ -210,7 +210,8 @@ func ParseTemplateFiles(filenames ...string) (*Set, os.Error) {
}
// ParseTemplateGlob creates a set by parsing the files matched
-// by the pattern, each of which defines a single template. Each
+// by the pattern, each of which defines a single template. The pattern
+// is processed by filepath.Glob and must match at least one file. Each
// template will be named the base name of its file.
// Unlike with ParseGlob, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
@@ -225,6 +226,9 @@ func ParseTemplateGlob(pattern string) (*Set, os.Error) {
if err != nil {
return nil, err
}
+ if len(filenames) == 0 {
+ return nil, fmt.Errorf("pattern matches no files: %#q", pattern)
+ }
for _, filename := range filenames {
t, err := ParseFile(filename)
if err != nil {
diff --git a/libgo/go/template/parse.go b/libgo/go/template/parse.go
index b089c59..3068a77 100644
--- a/libgo/go/template/parse.go
+++ b/libgo/go/template/parse.go
@@ -14,6 +14,8 @@ import (
type Template struct {
name string
*parse.Tree
+ leftDelim string
+ rightDelim string
// We use two maps, one for parsing and one for execution.
// This separation makes the API cleaner since it doesn't
// expose reflection to the client.
@@ -38,6 +40,16 @@ func New(name string) *Template {
}
}
+// Delims sets the action delimiters, to be used in a subsequent
+// parse, to the specified strings.
+// An empty delimiter stands for the corresponding default: {{ or }}.
+// The return value is the template, so calls can be chained.
+func (t *Template) Delims(left, right string) *Template {
+ t.leftDelim = left
+ t.rightDelim = right
+ return t
+}
+
// Funcs adds the elements of the argument map to the template's function
// map. It panics if a value in the map is not a function with appropriate
// return type.
@@ -51,7 +63,7 @@ func (t *Template) Funcs(funcMap FuncMap) *Template {
// Parse parses the template definition string to construct an internal
// representation of the template for execution.
func (t *Template) Parse(s string) (tmpl *Template, err os.Error) {
- t.Tree, err = parse.New(t.name).Parse(s, t.parseFuncs, builtins)
+ t.Tree, err = parse.New(t.name).Parse(s, t.leftDelim, t.rightDelim, t.parseFuncs, builtins)
if err != nil {
return nil, err
}
@@ -67,7 +79,7 @@ func (t *Template) ParseInSet(s string, set *Set) (tmpl *Template, err os.Error)
if set != nil {
setFuncs = set.parseFuncs
}
- t.Tree, err = parse.New(t.name).Parse(s, t.parseFuncs, setFuncs, builtins)
+ t.Tree, err = parse.New(t.name).Parse(s, t.leftDelim, t.rightDelim, t.parseFuncs, setFuncs, builtins)
if err != nil {
return nil, err
}
diff --git a/libgo/go/template/parse/lex.go b/libgo/go/template/parse/lex.go
index 83ad6c6..16ff590 100644
--- a/libgo/go/template/parse/lex.go
+++ b/libgo/go/template/parse/lex.go
@@ -119,13 +119,15 @@ type stateFn func(*lexer) stateFn
// lexer holds the state of the scanner.
type lexer struct {
- name string // the name of the input; used only for error reports.
- input string // the string being scanned.
- state stateFn // the next lexing function to enter
- pos int // current position in the input.
- start int // start position of this item.
- width int // width of last rune read from input.
- items chan item // channel of scanned items.
+ name string // the name of the input; used only for error reports.
+ input string // the string being scanned.
+ leftDelim string // start of action.
+ rightDelim string // end of action.
+ state stateFn // the next lexing function to enter.
+ pos int // current position in the input.
+ start int // start position of this item.
+ width int // width of last rune read from input.
+ items chan item // channel of scanned items.
}
// next returns the next rune in the input.
@@ -205,12 +207,20 @@ func (l *lexer) nextItem() item {
}
// lex creates a new scanner for the input string.
-func lex(name, input string) *lexer {
+func lex(name, input, left, right string) *lexer {
+ if left == "" {
+ left = leftDelim
+ }
+ if right == "" {
+ right = rightDelim
+ }
l := &lexer{
- name: name,
- input: input,
- state: lexText,
- items: make(chan item, 2), // Two items of buffering is sufficient for all state functions
+ name: name,
+ input: input,
+ leftDelim: left,
+ rightDelim: right,
+ state: lexText,
+ items: make(chan item, 2), // Two items of buffering is sufficient for all state functions
}
return l
}
@@ -220,14 +230,14 @@ func lex(name, input string) *lexer {
const (
leftDelim = "{{"
rightDelim = "}}"
- leftComment = "{{/*"
- rightComment = "*/}}"
+ leftComment = "/*"
+ rightComment = "*/"
)
// lexText scans until an opening action delimiter, "{{".
func lexText(l *lexer) stateFn {
for {
- if strings.HasPrefix(l.input[l.pos:], leftDelim) {
+ if strings.HasPrefix(l.input[l.pos:], l.leftDelim) {
if l.pos > l.start {
l.emit(itemText)
}
@@ -247,28 +257,28 @@ func lexText(l *lexer) stateFn {
// lexLeftDelim scans the left delimiter, which is known to be present.
func lexLeftDelim(l *lexer) stateFn {
- if strings.HasPrefix(l.input[l.pos:], leftComment) {
+ if strings.HasPrefix(l.input[l.pos:], l.leftDelim+leftComment) {
return lexComment
}
- l.pos += len(leftDelim)
+ l.pos += len(l.leftDelim)
l.emit(itemLeftDelim)
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)
+ i := strings.Index(l.input[l.pos:], rightComment+l.rightDelim)
if i < 0 {
return l.errorf("unclosed comment")
}
- l.pos += i + len(rightComment)
+ l.pos += i + len(rightComment) + len(l.rightDelim)
l.ignore()
return lexText
}
// lexRightDelim scans the right delimiter, which is known to be present.
func lexRightDelim(l *lexer) stateFn {
- l.pos += len(rightDelim)
+ l.pos += len(l.rightDelim)
l.emit(itemRightDelim)
return lexText
}
@@ -278,7 +288,7 @@ func lexInsideAction(l *lexer) stateFn {
// Either number, quoted string, or identifier.
// Spaces separate and are ignored.
// Pipe symbols separate and are emitted.
- if strings.HasPrefix(l.input[l.pos:], rightDelim) {
+ if strings.HasPrefix(l.input[l.pos:], l.rightDelim) {
return lexRightDelim
}
switch r := l.next(); {
diff --git a/libgo/go/template/parse/lex_test.go b/libgo/go/template/parse/lex_test.go
index d71c8e6..6ee1b47 100644
--- a/libgo/go/template/parse/lex_test.go
+++ b/libgo/go/template/parse/lex_test.go
@@ -201,8 +201,8 @@ var lexTests = []lexTest{
}
// collect gathers the emitted items into a slice.
-func collect(t *lexTest) (items []item) {
- l := lex(t.name, t.input)
+func collect(t *lexTest, left, right string) (items []item) {
+ l := lex(t.name, t.input, left, right)
for {
item := l.nextItem()
items = append(items, item)
@@ -215,7 +215,41 @@ func collect(t *lexTest) (items []item) {
func TestLex(t *testing.T) {
for _, test := range lexTests {
- items := collect(&test)
+ items := collect(&test, "", "")
+ if !reflect.DeepEqual(items, test.items) {
+ t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items)
+ }
+ }
+}
+
+// Some easy cases from above, but with delimiters $$ and @@
+var lexDelimTests = []lexTest{
+ {"punctuation", "$$,@%{{}}@@", []item{
+ tLeftDelim,
+ {itemChar, ","},
+ {itemChar, "@"},
+ {itemChar, "%"},
+ {itemChar, "{"},
+ {itemChar, "{"},
+ {itemChar, "}"},
+ {itemChar, "}"},
+ tRightDelim,
+ tEOF,
+ }},
+ {"empty action", `$$@@`, []item{tLeftDelim, tRightDelim, tEOF}},
+ {"for", `$$for @@`, []item{tLeftDelim, tFor, tRightDelim, tEOF}},
+ {"quote", `$$"abc \n\t\" "@@`, []item{tLeftDelim, tQuote, tRightDelim, tEOF}},
+ {"raw quote", "$$" + raw + "@@", []item{tLeftDelim, tRawQuote, tRightDelim, tEOF}},
+}
+
+var (
+ tLeftDelim = item{itemLeftDelim, "$$"}
+ tRightDelim = item{itemRightDelim, "@@"}
+)
+
+func TestDelims(t *testing.T) {
+ for _, test := range lexDelimTests {
+ items := collect(&test, "$$", "@@")
if !reflect.DeepEqual(items, test.items) {
t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items)
}
diff --git a/libgo/go/template/parse/node.go b/libgo/go/template/parse/node.go
index 6f0b429..7411327 100644
--- a/libgo/go/template/parse/node.go
+++ b/libgo/go/template/parse/node.go
@@ -390,8 +390,8 @@ func (e *elseNode) String() string {
return "{{else}}"
}
-// IfNode represents an {{if}} action and its commands.
-type IfNode struct {
+// BranchNode is the common representation of if, range, and with.
+type BranchNode struct {
NodeType
Line int // The line number in the input.
Pipe *PipeNode // The pipeline to be evaluated.
@@ -399,35 +399,49 @@ type IfNode struct {
ElseList *ListNode // What to execute if the value is empty (nil if absent).
}
-func newIf(line int, pipe *PipeNode, list, elseList *ListNode) *IfNode {
- return &IfNode{NodeType: NodeIf, Line: line, Pipe: pipe, List: list, ElseList: elseList}
+func (b *BranchNode) String() string {
+ name := ""
+ switch b.NodeType {
+ case NodeIf:
+ name = "if"
+ case NodeRange:
+ name = "range"
+ case NodeWith:
+ name = "with"
+ default:
+ panic("unknown branch type")
+ }
+ if b.ElseList != nil {
+ return fmt.Sprintf("({{%s %s}} %s {{else}} %s)", name, b.Pipe, b.List, b.ElseList)
+ }
+ return fmt.Sprintf("({{%s %s}} %s)", name, b.Pipe, b.List)
}
-func (i *IfNode) String() string {
- if i.ElseList != nil {
- return fmt.Sprintf("({{if %s}} %s {{else}} %s)", i.Pipe, i.List, i.ElseList)
- }
- return fmt.Sprintf("({{if %s}} %s)", i.Pipe, i.List)
+// IfNode represents an {{if}} action and its commands.
+type IfNode struct {
+ BranchNode
+}
+
+func newIf(line int, pipe *PipeNode, list, elseList *ListNode) *IfNode {
+ return &IfNode{BranchNode{NodeType: NodeIf, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
}
// RangeNode represents a {{range}} action and its commands.
type RangeNode struct {
- NodeType
- Line int // The line number in the input.
- Pipe *PipeNode // The pipeline to be evaluated.
- List *ListNode // What to execute if the value is non-empty.
- ElseList *ListNode // What to execute if the value is empty (nil if absent).
+ BranchNode
}
func newRange(line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode {
- return &RangeNode{NodeType: NodeRange, Line: line, Pipe: pipe, List: list, ElseList: elseList}
+ return &RangeNode{BranchNode{NodeType: NodeRange, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
}
-func (r *RangeNode) String() string {
- if r.ElseList != nil {
- return fmt.Sprintf("({{range %s}} %s {{else}} %s)", r.Pipe, r.List, r.ElseList)
- }
- return fmt.Sprintf("({{range %s}} %s)", r.Pipe, r.List)
+// WithNode represents a {{with}} action and its commands.
+type WithNode struct {
+ BranchNode
+}
+
+func newWith(line int, pipe *PipeNode, list, elseList *ListNode) *WithNode {
+ return &WithNode{BranchNode{NodeType: NodeWith, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
}
// TemplateNode represents a {{template}} action.
@@ -448,23 +462,3 @@ func (t *TemplateNode) String() string {
}
return fmt.Sprintf("{{template %q %s}}", t.Name, t.Pipe)
}
-
-// WithNode represents a {{with}} action and its commands.
-type WithNode struct {
- NodeType
- Line int // The line number in the input.
- Pipe *PipeNode // The pipeline to be evaluated.
- List *ListNode // What to execute if the value is non-empty.
- ElseList *ListNode // What to execute if the value is empty (nil if absent).
-}
-
-func newWith(line int, pipe *PipeNode, list, elseList *ListNode) *WithNode {
- return &WithNode{NodeType: NodeWith, Line: line, Pipe: pipe, List: list, ElseList: elseList}
-}
-
-func (w *WithNode) String() string {
- if w.ElseList != nil {
- return fmt.Sprintf("({{with %s}} %s {{else}} %s)", w.Pipe, w.List, w.ElseList)
- }
- return fmt.Sprintf("({{with %s}} %s)", w.Pipe, w.List)
-}
diff --git a/libgo/go/template/parse/parse.go b/libgo/go/template/parse/parse.go
index 6918074..9934d82 100644
--- a/libgo/go/template/parse/parse.go
+++ b/libgo/go/template/parse/parse.go
@@ -145,10 +145,11 @@ func (t *Tree) atEOF() bool {
}
// Parse parses the template definition string to construct an internal
-// representation of the template for execution.
-func (t *Tree) Parse(s string, funcs ...map[string]interface{}) (tree *Tree, err os.Error) {
+// representation of the template for execution. If either action delimiter
+// string is empty, the default ("{{" or "}}") is used.
+func (t *Tree) Parse(s, leftDelim, rightDelim string, funcs ...map[string]interface{}) (tree *Tree, err os.Error) {
defer t.recover(&err)
- t.startParse(funcs, lex(t.Name, s))
+ t.startParse(funcs, lex(t.Name, s, leftDelim, rightDelim))
t.parse(true)
t.stopParse()
return t, nil
diff --git a/libgo/go/template/parse/parse_test.go b/libgo/go/template/parse/parse_test.go
index 1928c31..f05f6e3 100644
--- a/libgo/go/template/parse/parse_test.go
+++ b/libgo/go/template/parse/parse_test.go
@@ -236,7 +236,7 @@ var builtins = map[string]interface{}{
func TestParse(t *testing.T) {
for _, test := range parseTests {
- tmpl, err := New(test.name).Parse(test.input, builtins)
+ tmpl, err := New(test.name).Parse(test.input, "", "", builtins)
switch {
case err == nil && !test.ok:
t.Errorf("%q: expected error; got none", test.name)
diff --git a/libgo/go/template/parse/set.go b/libgo/go/template/parse/set.go
index dca41ea..b909f71 100644
--- a/libgo/go/template/parse/set.go
+++ b/libgo/go/template/parse/set.go
@@ -13,10 +13,10 @@ import (
// 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]interface{}) (tree map[string]*Tree, err os.Error) {
+func Set(text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (tree map[string]*Tree, err os.Error) {
tree = make(map[string]*Tree)
defer (*Tree)(nil).recover(&err)
- lex := lex("set", text)
+ lex := lex("set", text, leftDelim, rightDelim)
const context = "define clause"
for {
t := New("set") // name will be updated once we know it.
diff --git a/libgo/go/template/set.go b/libgo/go/template/set.go
index f778fd1..712961b 100644
--- a/libgo/go/template/set.go
+++ b/libgo/go/template/set.go
@@ -17,6 +17,8 @@ import (
// A template may be a member of multiple sets.
type Set struct {
tmpl map[string]*Template
+ leftDelim string
+ rightDelim string
parseFuncs FuncMap
execFuncs map[string]reflect.Value
}
@@ -29,6 +31,16 @@ func (s *Set) init() {
}
}
+// Delims sets the action delimiters, to be used in a subsequent
+// parse, to the specified strings.
+// An empty delimiter stands for the corresponding default: {{ or }}.
+// The return value is the set, so calls can be chained.
+func (s *Set) Delims(left, right string) *Set {
+ s.leftDelim = left
+ s.rightDelim = right
+ return s
+}
+
// Funcs adds the elements of the argument map to the set's function map. It
// panics if a value in the map is not a function with appropriate return
// type.
@@ -93,7 +105,7 @@ func (s *Set) Execute(wr io.Writer, name string, data interface{}) os.Error {
// 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, os.Error) {
- trees, err := parse.Set(text, s.parseFuncs, builtins)
+ trees, err := parse.Set(text, s.leftDelim, s.rightDelim, s.parseFuncs, builtins)
if err != nil {
return nil, err
}