diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-10-26 23:57:58 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-10-26 23:57:58 +0000 |
commit | d8f412571f8768df2d3239e72392dfeabbad1559 (patch) | |
tree | 19d182df05ead7ff8ba7ee00a7d57555e1383fdf /libgo/go/template | |
parent | e0c39d66d4f0607177b1cf8995dda56a667e07b3 (diff) | |
download | gcc-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.go | 2 | ||||
-rw-r--r-- | libgo/go/template/exec_test.go | 67 | ||||
-rw-r--r-- | libgo/go/template/helper.go | 6 | ||||
-rw-r--r-- | libgo/go/template/parse.go | 16 | ||||
-rw-r--r-- | libgo/go/template/parse/lex.go | 52 | ||||
-rw-r--r-- | libgo/go/template/parse/lex_test.go | 40 | ||||
-rw-r--r-- | libgo/go/template/parse/node.go | 74 | ||||
-rw-r--r-- | libgo/go/template/parse/parse.go | 7 | ||||
-rw-r--r-- | libgo/go/template/parse/parse_test.go | 2 | ||||
-rw-r--r-- | libgo/go/template/parse/set.go | 4 | ||||
-rw-r--r-- | libgo/go/template/set.go | 14 |
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 } |