aboutsummaryrefslogtreecommitdiff
path: root/src/pkg/text
diff options
context:
space:
mode:
authorRob Pike <r@golang.org>2012-02-11 14:21:16 +1100
committerRob Pike <r@golang.org>2012-02-11 14:21:16 +1100
commitb027a0f11857636314e3e149fc785feb79420e9e (patch)
tree974a440b1551113d01821c41d43b3910d59fd0d8 /src/pkg/text
parentca5da31f83517780893423e46665d48149e545ee (diff)
downloadgo-b027a0f11857636314e3e149fc785feb79420e9e.tar.xz
text/template/parse: deep Copy method for nodes
This will help html/template copy templates. R=golang-dev, gri, nigeltao, r CC=golang-dev https://golang.org/cl/5653062
Diffstat (limited to 'src/pkg/text')
-rw-r--r--src/pkg/text/template/parse/node.go112
-rw-r--r--src/pkg/text/template/parse/parse_test.go18
2 files changed, 128 insertions, 2 deletions
diff --git a/src/pkg/text/template/parse/node.go b/src/pkg/text/template/parse/node.go
index 0d030b8b4b..db645624c5 100644
--- a/src/pkg/text/template/parse/node.go
+++ b/src/pkg/text/template/parse/node.go
@@ -17,6 +17,10 @@ import (
type Node interface {
Type() NodeType
String() string
+ // Copy does a deep copy of the Node and all its components.
+ // To avoid type assertions, some XxxNodes also have specialized
+ // CopyXxx methods that return *XxxNode.
+ Copy() Node
}
// NodeType identifies the type of a parse tree node.
@@ -73,6 +77,21 @@ func (l *ListNode) String() string {
return b.String()
}
+func (l *ListNode) CopyList() *ListNode {
+ if l == nil {
+ return l
+ }
+ n := newList()
+ for _, elem := range l.Nodes {
+ n.append(elem.Copy())
+ }
+ return n
+}
+
+func (l *ListNode) Copy() Node {
+ return l.CopyList()
+}
+
// TextNode holds plain text.
type TextNode struct {
NodeType
@@ -87,6 +106,10 @@ func (t *TextNode) String() string {
return fmt.Sprintf("%q", t.Text)
}
+func (t *TextNode) Copy() Node {
+ return &TextNode{NodeType: NodeText, Text: append([]byte{}, t.Text...)}
+}
+
// PipeNode holds a pipeline with optional declaration
type PipeNode struct {
NodeType
@@ -123,6 +146,25 @@ func (p *PipeNode) String() string {
return s
}
+func (p *PipeNode) CopyPipe() *PipeNode {
+ if p == nil {
+ return p
+ }
+ var decl []*VariableNode
+ for _, d := range p.Decl {
+ decl = append(decl, d.Copy().(*VariableNode))
+ }
+ n := newPipeline(p.Line, decl)
+ for _, c := range p.Cmds {
+ n.append(c.Copy().(*CommandNode))
+ }
+ return n
+}
+
+func (p *PipeNode) Copy() Node {
+ return p.CopyPipe()
+}
+
// ActionNode holds an action (something bounded by delimiters).
// Control actions have their own nodes; ActionNode represents simple
// ones such as field evaluations.
@@ -141,6 +183,11 @@ func (a *ActionNode) String() string {
}
+func (a *ActionNode) Copy() Node {
+ return newAction(a.Line, a.Pipe.CopyPipe())
+
+}
+
// CommandNode holds a command (a pipeline inside an evaluating action).
type CommandNode struct {
NodeType
@@ -166,6 +213,17 @@ func (c *CommandNode) String() string {
return s
}
+func (c *CommandNode) Copy() Node {
+ if c == nil {
+ return c
+ }
+ n := newCommand()
+ for _, c := range c.Args {
+ n.append(c.Copy())
+ }
+ return n
+}
+
// IdentifierNode holds an identifier.
type IdentifierNode struct {
NodeType
@@ -181,6 +239,10 @@ func (i *IdentifierNode) String() string {
return i.Ident
}
+func (i *IdentifierNode) Copy() Node {
+ return NewIdentifier(i.Ident)
+}
+
// VariableNode holds a list of variable names. The dollar sign is
// part of the name.
type VariableNode struct {
@@ -203,6 +265,10 @@ func (v *VariableNode) String() string {
return s
}
+func (v *VariableNode) Copy() Node {
+ return &VariableNode{NodeType: NodeVariable, Ident: append([]string{}, v.Ident...)}
+}
+
// DotNode holds the special identifier '.'. It is represented by a nil pointer.
type DotNode bool
@@ -218,6 +284,10 @@ func (d *DotNode) String() string {
return "."
}
+func (d *DotNode) Copy() Node {
+ return newDot()
+}
+
// FieldNode holds a field (identifier starting with '.').
// The names may be chained ('.x.y').
// The period is dropped from each ident.
@@ -238,6 +308,10 @@ func (f *FieldNode) String() string {
return s
}
+func (f *FieldNode) Copy() Node {
+ return &FieldNode{NodeType: NodeField, Ident: append([]string{}, f.Ident...)}
+}
+
// BoolNode holds a boolean constant.
type BoolNode struct {
NodeType
@@ -255,6 +329,10 @@ func (b *BoolNode) String() string {
return "false"
}
+func (b *BoolNode) Copy() Node {
+ return newBool(b.True)
+}
+
// NumberNode holds a number: signed or unsigned integer, float, or complex.
// The value is parsed and stored under all the types that can represent the value.
// This simulates in a small amount of code the behavior of Go's ideal constants.
@@ -373,6 +451,12 @@ func (n *NumberNode) String() string {
return n.Text
}
+func (n *NumberNode) Copy() Node {
+ nn := new(NumberNode)
+ *nn = *n // Easy, fast, correct.
+ return nn
+}
+
// StringNode holds a string constant. The value has been "unquoted".
type StringNode struct {
NodeType
@@ -388,6 +472,10 @@ func (s *StringNode) String() string {
return s.Quoted
}
+func (s *StringNode) Copy() Node {
+ return newString(s.Quoted, s.Text)
+}
+
// endNode represents an {{end}} action. It is represented by a nil pointer.
// It does not appear in the final parse tree.
type endNode bool
@@ -404,6 +492,10 @@ func (e *endNode) String() string {
return "{{end}}"
}
+func (e *endNode) Copy() Node {
+ return newEnd()
+}
+
// elseNode represents an {{else}} action. Does not appear in the final tree.
type elseNode struct {
NodeType
@@ -422,6 +514,10 @@ func (e *elseNode) String() string {
return "{{else}}"
}
+func (e *elseNode) Copy() Node {
+ return newElse(e.Line)
+}
+
// BranchNode is the common representation of if, range, and with.
type BranchNode struct {
NodeType
@@ -458,6 +554,10 @@ func newIf(line int, pipe *PipeNode, list, elseList *ListNode) *IfNode {
return &IfNode{BranchNode{NodeType: NodeIf, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
}
+func (i *IfNode) Copy() Node {
+ return newIf(i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList())
+}
+
// RangeNode represents a {{range}} action and its commands.
type RangeNode struct {
BranchNode
@@ -467,6 +567,10 @@ func newRange(line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode {
return &RangeNode{BranchNode{NodeType: NodeRange, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
}
+func (r *RangeNode) Copy() Node {
+ return newRange(r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList())
+}
+
// WithNode represents a {{with}} action and its commands.
type WithNode struct {
BranchNode
@@ -476,6 +580,10 @@ func newWith(line int, pipe *PipeNode, list, elseList *ListNode) *WithNode {
return &WithNode{BranchNode{NodeType: NodeWith, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
}
+func (w *WithNode) Copy() Node {
+ return newWith(w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList())
+}
+
// TemplateNode represents a {{template}} action.
type TemplateNode struct {
NodeType
@@ -494,3 +602,7 @@ func (t *TemplateNode) String() string {
}
return fmt.Sprintf("{{template %q %s}}", t.Name, t.Pipe)
}
+
+func (t *TemplateNode) Copy() Node {
+ return newTemplate(t.Line, t.Name, t.Pipe.CopyPipe())
+}
diff --git a/src/pkg/text/template/parse/parse_test.go b/src/pkg/text/template/parse/parse_test.go
index 13c5548abb..efa7d8be74 100644
--- a/src/pkg/text/template/parse/parse_test.go
+++ b/src/pkg/text/template/parse/parse_test.go
@@ -232,7 +232,7 @@ var builtins = map[string]interface{}{
"printf": fmt.Sprintf,
}
-func TestParse(t *testing.T) {
+func testParse(doCopy bool, t *testing.T) {
for _, test := range parseTests {
tmpl, err := New(test.name).Parse(test.input, "", "", make(map[string]*Tree), builtins)
switch {
@@ -249,13 +249,27 @@ func TestParse(t *testing.T) {
}
continue
}
- result := tmpl.Root.String()
+ var result string
+ if doCopy {
+ result = tmpl.Root.Copy().String()
+ } else {
+ 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)
}
}
}
+func TestParse(t *testing.T) {
+ testParse(false, t)
+}
+
+// Same as TestParse, but we copy the node first
+func TestParseCopy(t *testing.T) {
+ testParse(true, t)
+}
+
type isEmptyTest struct {
name string
input string