aboutsummaryrefslogtreecommitdiff
path: root/src/text
diff options
context:
space:
mode:
authorCherry Zhang <cherryyz@google.com>2019-10-04 16:55:52 -0400
committerCherry Zhang <cherryyz@google.com>2019-10-04 16:55:52 -0400
commitb0d930577ebe86d0dbd6d3f4bf551a4e4ff1fcde (patch)
treec1382943d8874ef6f278b1b49d38b4a5090993a8 /src/text
parente63c1df34856fbf61f72fef84f810cf3306ec204 (diff)
parentc450ace12c657e3953d79975c04f51605395cd50 (diff)
downloadgo-b0d930577ebe86d0dbd6d3f4bf551a4e4ff1fcde.tar.xz
[dev.link] all: merge branch 'master' into dev.link
Weekly merge. Change-Id: I98c6121f04f347df2788ac5eaf99afad5da4a039
Diffstat (limited to 'src/text')
-rw-r--r--src/text/template/parse/node.go170
-rw-r--r--src/text/template/parse/parse.go9
-rw-r--r--src/text/template/parse/parse_test.go52
3 files changed, 187 insertions, 44 deletions
diff --git a/src/text/template/parse/node.go b/src/text/template/parse/node.go
index 1174a4b970..1c116ea6fa 100644
--- a/src/text/template/parse/node.go
+++ b/src/text/template/parse/node.go
@@ -7,7 +7,6 @@
package parse
import (
- "bytes"
"fmt"
"strconv"
"strings"
@@ -29,6 +28,8 @@ type Node interface {
// tree returns the containing *Tree.
// It is unexported so all implementations of Node are in this package.
tree() *Tree
+ // writeTo writes the String output to the builder.
+ writeTo(*strings.Builder)
}
// NodeType identifies the type of a parse tree node.
@@ -94,11 +95,15 @@ func (l *ListNode) tree() *Tree {
}
func (l *ListNode) String() string {
- b := new(bytes.Buffer)
+ var sb strings.Builder
+ l.writeTo(&sb)
+ return sb.String()
+}
+
+func (l *ListNode) writeTo(sb *strings.Builder) {
for _, n := range l.Nodes {
- fmt.Fprint(b, n)
+ n.writeTo(sb)
}
- return b.String()
}
func (l *ListNode) CopyList() *ListNode {
@@ -132,6 +137,10 @@ func (t *TextNode) String() string {
return fmt.Sprintf(textFormat, t.Text)
}
+func (t *TextNode) writeTo(sb *strings.Builder) {
+ sb.WriteString(t.String())
+}
+
func (t *TextNode) tree() *Tree {
return t.tr
}
@@ -160,23 +169,27 @@ func (p *PipeNode) append(command *CommandNode) {
}
func (p *PipeNode) String() string {
- s := ""
+ var sb strings.Builder
+ p.writeTo(&sb)
+ return sb.String()
+}
+
+func (p *PipeNode) writeTo(sb *strings.Builder) {
if len(p.Decl) > 0 {
for i, v := range p.Decl {
if i > 0 {
- s += ", "
+ sb.WriteString(", ")
}
- s += v.String()
+ v.writeTo(sb)
}
- s += " := "
+ sb.WriteString(" := ")
}
for i, c := range p.Cmds {
if i > 0 {
- s += " | "
+ sb.WriteString(" | ")
}
- s += c.String()
+ c.writeTo(sb)
}
- return s
}
func (p *PipeNode) tree() *Tree {
@@ -187,9 +200,9 @@ func (p *PipeNode) CopyPipe() *PipeNode {
if p == nil {
return p
}
- var vars []*VariableNode
- for _, d := range p.Decl {
- vars = append(vars, d.Copy().(*VariableNode))
+ vars := make([]*VariableNode, len(p.Decl))
+ for i, d := range p.Decl {
+ vars[i] = d.Copy().(*VariableNode)
}
n := p.tr.newPipeline(p.Pos, p.Line, vars)
n.IsAssign = p.IsAssign
@@ -219,8 +232,15 @@ func (t *Tree) newAction(pos Pos, line int, pipe *PipeNode) *ActionNode {
}
func (a *ActionNode) String() string {
- return fmt.Sprintf("{{%s}}", a.Pipe)
+ var sb strings.Builder
+ a.writeTo(&sb)
+ return sb.String()
+}
+func (a *ActionNode) writeTo(sb *strings.Builder) {
+ sb.WriteString("{{")
+ a.Pipe.writeTo(sb)
+ sb.WriteString("}}")
}
func (a *ActionNode) tree() *Tree {
@@ -249,18 +269,24 @@ func (c *CommandNode) append(arg Node) {
}
func (c *CommandNode) String() string {
- s := ""
+ var sb strings.Builder
+ c.writeTo(&sb)
+ return sb.String()
+}
+
+func (c *CommandNode) writeTo(sb *strings.Builder) {
for i, arg := range c.Args {
if i > 0 {
- s += " "
+ sb.WriteByte(' ')
}
if arg, ok := arg.(*PipeNode); ok {
- s += "(" + arg.String() + ")"
+ sb.WriteByte('(')
+ arg.writeTo(sb)
+ sb.WriteByte(')')
continue
}
- s += arg.String()
+ arg.writeTo(sb)
}
- return s
}
func (c *CommandNode) tree() *Tree {
@@ -311,6 +337,10 @@ func (i *IdentifierNode) String() string {
return i.Ident
}
+func (i *IdentifierNode) writeTo(sb *strings.Builder) {
+ sb.WriteString(i.String())
+}
+
func (i *IdentifierNode) tree() *Tree {
return i.tr
}
@@ -333,14 +363,18 @@ func (t *Tree) newVariable(pos Pos, ident string) *VariableNode {
}
func (v *VariableNode) String() string {
- s := ""
+ var sb strings.Builder
+ v.writeTo(&sb)
+ return sb.String()
+}
+
+func (v *VariableNode) writeTo(sb *strings.Builder) {
for i, id := range v.Ident {
if i > 0 {
- s += "."
+ sb.WriteByte('.')
}
- s += id
+ sb.WriteString(id)
}
- return s
}
func (v *VariableNode) tree() *Tree {
@@ -373,6 +407,10 @@ func (d *DotNode) String() string {
return "."
}
+func (d *DotNode) writeTo(sb *strings.Builder) {
+ sb.WriteString(d.String())
+}
+
func (d *DotNode) tree() *Tree {
return d.tr
}
@@ -403,6 +441,10 @@ func (n *NilNode) String() string {
return "nil"
}
+func (n *NilNode) writeTo(sb *strings.Builder) {
+ sb.WriteString(n.String())
+}
+
func (n *NilNode) tree() *Tree {
return n.tr
}
@@ -426,11 +468,16 @@ func (t *Tree) newField(pos Pos, ident string) *FieldNode {
}
func (f *FieldNode) String() string {
- s := ""
+ var sb strings.Builder
+ f.writeTo(&sb)
+ return sb.String()
+}
+
+func (f *FieldNode) writeTo(sb *strings.Builder) {
for _, id := range f.Ident {
- s += "." + id
+ sb.WriteByte('.')
+ sb.WriteString(id)
}
- return s
}
func (f *FieldNode) tree() *Tree {
@@ -469,14 +516,23 @@ func (c *ChainNode) Add(field string) {
}
func (c *ChainNode) String() string {
- s := c.Node.String()
+ var sb strings.Builder
+ c.writeTo(&sb)
+ return sb.String()
+}
+
+func (c *ChainNode) writeTo(sb *strings.Builder) {
if _, ok := c.Node.(*PipeNode); ok {
- s = "(" + s + ")"
+ sb.WriteByte('(')
+ c.Node.writeTo(sb)
+ sb.WriteByte(')')
+ } else {
+ c.Node.writeTo(sb)
}
for _, field := range c.Field {
- s += "." + field
+ sb.WriteByte('.')
+ sb.WriteString(field)
}
- return s
}
func (c *ChainNode) tree() *Tree {
@@ -506,6 +562,10 @@ func (b *BoolNode) String() string {
return "false"
}
+func (b *BoolNode) writeTo(sb *strings.Builder) {
+ sb.WriteString(b.String())
+}
+
func (b *BoolNode) tree() *Tree {
return b.tr
}
@@ -639,6 +699,10 @@ func (n *NumberNode) String() string {
return n.Text
}
+func (n *NumberNode) writeTo(sb *strings.Builder) {
+ sb.WriteString(n.String())
+}
+
func (n *NumberNode) tree() *Tree {
return n.tr
}
@@ -666,6 +730,10 @@ func (s *StringNode) String() string {
return s.Quoted
}
+func (s *StringNode) writeTo(sb *strings.Builder) {
+ sb.WriteString(s.String())
+}
+
func (s *StringNode) tree() *Tree {
return s.tr
}
@@ -690,6 +758,10 @@ func (e *endNode) String() string {
return "{{end}}"
}
+func (e *endNode) writeTo(sb *strings.Builder) {
+ sb.WriteString(e.String())
+}
+
func (e *endNode) tree() *Tree {
return e.tr
}
@@ -718,6 +790,10 @@ func (e *elseNode) String() string {
return "{{else}}"
}
+func (e *elseNode) writeTo(sb *strings.Builder) {
+ sb.WriteString(e.String())
+}
+
func (e *elseNode) tree() *Tree {
return e.tr
}
@@ -738,6 +814,12 @@ type BranchNode struct {
}
func (b *BranchNode) String() string {
+ var sb strings.Builder
+ b.writeTo(&sb)
+ return sb.String()
+}
+
+func (b *BranchNode) writeTo(sb *strings.Builder) {
name := ""
switch b.NodeType {
case NodeIf:
@@ -749,10 +831,17 @@ func (b *BranchNode) String() string {
default:
panic("unknown branch type")
}
+ sb.WriteString("{{")
+ sb.WriteString(name)
+ sb.WriteByte(' ')
+ b.Pipe.writeTo(sb)
+ sb.WriteString("}}")
+ b.List.writeTo(sb)
if b.ElseList != nil {
- return fmt.Sprintf("{{%s %s}}%s{{else}}%s{{end}}", name, b.Pipe, b.List, b.ElseList)
+ sb.WriteString("{{else}}")
+ b.ElseList.writeTo(sb)
}
- return fmt.Sprintf("{{%s %s}}%s{{end}}", name, b.Pipe, b.List)
+ sb.WriteString("{{end}}")
}
func (b *BranchNode) tree() *Tree {
@@ -826,10 +915,19 @@ func (t *Tree) newTemplate(pos Pos, line int, name string, pipe *PipeNode) *Temp
}
func (t *TemplateNode) String() string {
- if t.Pipe == nil {
- return fmt.Sprintf("{{template %q}}", t.Name)
+ var sb strings.Builder
+ t.writeTo(&sb)
+ return sb.String()
+}
+
+func (t *TemplateNode) writeTo(sb *strings.Builder) {
+ sb.WriteString("{{template ")
+ sb.WriteString(strconv.Quote(t.Name))
+ if t.Pipe != nil {
+ sb.WriteByte(' ')
+ t.Pipe.writeTo(sb)
}
- return fmt.Sprintf("{{template %q %s}}", t.Name, t.Pipe)
+ sb.WriteString("}}")
}
func (t *TemplateNode) tree() *Tree {
diff --git a/src/text/template/parse/parse.go b/src/text/template/parse/parse.go
index 7c35b0ff3d..c9b80f4a24 100644
--- a/src/text/template/parse/parse.go
+++ b/src/text/template/parse/parse.go
@@ -108,13 +108,8 @@ func (t *Tree) nextNonSpace() (token item) {
}
// peekNonSpace returns but does not consume the next non-space token.
-func (t *Tree) peekNonSpace() (token item) {
- for {
- token = t.next()
- if token.typ != itemSpace {
- break
- }
- }
+func (t *Tree) peekNonSpace() item {
+ token := t.nextNonSpace()
t.backup()
return token
}
diff --git a/src/text/template/parse/parse_test.go b/src/text/template/parse/parse_test.go
index 6932cf232e..4e09a7852c 100644
--- a/src/text/template/parse/parse_test.go
+++ b/src/text/template/parse/parse_test.go
@@ -304,7 +304,8 @@ var parseTests = []parseTest{
}
var builtins = map[string]interface{}{
- "printf": fmt.Sprintf,
+ "printf": fmt.Sprintf,
+ "contains": strings.Contains,
}
func testParse(doCopy bool, t *testing.T) {
@@ -553,3 +554,52 @@ func BenchmarkParseLarge(b *testing.B) {
}
}
}
+
+var sinkv, sinkl string
+
+func BenchmarkVariableString(b *testing.B) {
+ v := &VariableNode{
+ Ident: []string{"$", "A", "BB", "CCC", "THIS_IS_THE_VARIABLE_BEING_PROCESSED"},
+ }
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ sinkv = v.String()
+ }
+ if sinkv == "" {
+ b.Fatal("Benchmark was not run")
+ }
+}
+
+func BenchmarkListString(b *testing.B) {
+ text := `
+{{(printf .Field1.Field2.Field3).Value}}
+{{$x := (printf .Field1.Field2.Field3).Value}}
+{{$y := (printf $x.Field1.Field2.Field3).Value}}
+{{$z := $y.Field1.Field2.Field3}}
+{{if contains $y $z}}
+ {{printf "%q" $y}}
+{{else}}
+ {{printf "%q" $x}}
+{{end}}
+{{with $z.Field1 | contains "boring"}}
+ {{printf "%q" . | printf "%s"}}
+{{else}}
+ {{printf "%d %d %d" 11 11 11}}
+ {{printf "%d %d %s" 22 22 $x.Field1.Field2.Field3 | printf "%s"}}
+ {{printf "%v" (contains $z.Field1.Field2 $y)}}
+{{end}}
+`
+ tree, err := New("bench").Parse(text, "", "", make(map[string]*Tree), builtins)
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ sinkl = tree.Root.String()
+ }
+ if sinkl == "" {
+ b.Fatal("Benchmark was not run")
+ }
+}