summaryrefslogtreecommitdiff
path: root/libgo/go/text
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/text')
-rw-r--r--libgo/go/text/scanner/scanner.go7
-rw-r--r--libgo/go/text/tabwriter/example_test.go38
-rw-r--r--libgo/go/text/tabwriter/tabwriter.go6
-rw-r--r--libgo/go/text/tabwriter/tabwriter_test.go3
-rw-r--r--libgo/go/text/template/doc.go37
-rw-r--r--libgo/go/text/template/exec.go7
-rw-r--r--libgo/go/text/template/exec_test.go44
-rw-r--r--libgo/go/text/template/funcs.go48
-rw-r--r--libgo/go/text/template/multi_test.go8
-rw-r--r--libgo/go/text/template/parse/lex.go2
-rw-r--r--libgo/go/text/template/parse/node.go112
-rw-r--r--libgo/go/text/template/parse/parse.go8
-rw-r--r--libgo/go/text/template/parse/parse_test.go21
-rw-r--r--libgo/go/text/template/template.go28
14 files changed, 324 insertions, 45 deletions
diff --git a/libgo/go/text/scanner/scanner.go b/libgo/go/text/scanner/scanner.go
index f46f63d0ee6..565650edf9c 100644
--- a/libgo/go/text/scanner/scanner.go
+++ b/libgo/go/text/scanner/scanner.go
@@ -5,8 +5,7 @@
// Package scanner provides a scanner and tokenizer for UTF-8-encoded text.
// It takes an io.Reader providing the source, which then can be tokenized
// through repeated calls to the Scan function. For compatibility with
-// existing tools, the NUL character is not allowed (implementation
-// restriction).
+// existing tools, the NUL character is not allowed.
//
// By default, a Scanner skips white space and Go comments and recognizes all
// literals as defined by the Go language specification. It may be
@@ -104,7 +103,7 @@ var tokenString = map[rune]string{
Comment: "Comment",
}
-// TokenString returns a (visible) string for a token or Unicode character.
+// TokenString returns a printable string for a token or Unicode character.
func TokenString(tok rune) string {
if s, found := tokenString[tok]; found {
return s
@@ -287,7 +286,7 @@ func (s *Scanner) next() rune {
// special situations
switch ch {
case 0:
- // implementation restriction for compatibility with other tools
+ // for compatibility with other tools
s.error("illegal character NUL")
case '\n':
s.line++
diff --git a/libgo/go/text/tabwriter/example_test.go b/libgo/go/text/tabwriter/example_test.go
new file mode 100644
index 00000000000..20443cb1ffb
--- /dev/null
+++ b/libgo/go/text/tabwriter/example_test.go
@@ -0,0 +1,38 @@
+// Copyright 2012 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 tabwriter_test
+
+import (
+ "fmt"
+ "os"
+ "text/tabwriter"
+)
+
+func ExampleWriter_Init() {
+ w := new(tabwriter.Writer)
+
+ // Format in tab-separated columns with a tab stop of 8.
+ w.Init(os.Stdout, 0, 8, 0, '\t', 0)
+ fmt.Fprintln(w, "a\tb\tc\td\t.")
+ fmt.Fprintln(w, "123\t12345\t1234567\t123456789\t.")
+ fmt.Fprintln(w)
+ w.Flush()
+
+ // Format right-aligned in space-separated columns of minimal width 5
+ // and at least one blank of padding (so wider column entries do not
+ // touch each other).
+ w.Init(os.Stdout, 5, 0, 1, ' ', tabwriter.AlignRight)
+ fmt.Fprintln(w, "a\tb\tc\td\t.")
+ fmt.Fprintln(w, "123\t12345\t1234567\t123456789\t.")
+ fmt.Fprintln(w)
+ w.Flush()
+
+ // output:
+ // a b c d .
+ // 123 12345 1234567 123456789 .
+ //
+ // a b c d.
+ // 123 12345 1234567 123456789.
+}
diff --git a/libgo/go/text/tabwriter/tabwriter.go b/libgo/go/text/tabwriter/tabwriter.go
index ea7c4008111..ce84600d604 100644
--- a/libgo/go/text/tabwriter/tabwriter.go
+++ b/libgo/go/text/tabwriter/tabwriter.go
@@ -169,12 +169,6 @@ const (
// to the tab width in the viewer displaying the result)
// flags formatting control
//
-// To format in tab-separated columns with a tab stop of 8:
-// b.Init(w, 8, 1, 8, '\t', 0);
-//
-// To format in space-separated columns with at least 4 spaces between columns:
-// b.Init(w, 0, 4, 8, ' ', 0);
-//
func (b *Writer) Init(output io.Writer, minwidth, tabwidth, padding int, padchar byte, flags uint) *Writer {
if minwidth < 0 || tabwidth < 0 || padding < 0 {
panic("negative minwidth, tabwidth, or padding")
diff --git a/libgo/go/text/tabwriter/tabwriter_test.go b/libgo/go/text/tabwriter/tabwriter_test.go
index 1ffb330d432..ace53564737 100644
--- a/libgo/go/text/tabwriter/tabwriter_test.go
+++ b/libgo/go/text/tabwriter/tabwriter_test.go
@@ -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 tabwriter
+package tabwriter_test
import (
"io"
"testing"
+ . "text/tabwriter"
)
type buffer struct {
diff --git a/libgo/go/text/template/doc.go b/libgo/go/text/template/doc.go
index 3be1ec44e69..10e0f7fc373 100644
--- a/libgo/go/text/template/doc.go
+++ b/libgo/go/text/template/doc.go
@@ -3,8 +3,10 @@
// license that can be found in the LICENSE file.
/*
-Package template implements data-driven templates for generating textual output
-such as HTML.
+Package template implements data-driven templates for generating textual output.
+
+To generate HTML output, see package html/template, which has the same interface
+as this package but automatically secures HTML output against certain attacks.
Templates are executed by applying them to a data structure. Annotations in the
template refer to elements of the data structure (typically a field of a struct
@@ -20,6 +22,20 @@ Actions may not span newlines, although comments can.
Once constructed, a template may be executed safely in parallel.
+Here is a trivial example that prints "17 items are made of wool".
+
+ type Inventory struct {
+ Material string
+ Count uint
+ }
+ sweaters := Inventory{"wool", 17}
+ tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
+ if err != nil { panic(err) }
+ err = tmpl.Execute(os.Stdout, sweaters)
+ if err != nil { panic(err) }
+
+More intricate examples appear below.
+
Actions
Here is the list of actions. "Arguments" and "pipelines" are evaluations of
@@ -134,6 +150,10 @@ An argument is a simple value, denoted by one of the following.
Arguments may evaluate to any type; if they are pointers the implementation
automatically indirects to the base type when required.
+If an evaluation yields a function value, such as a function-valued
+field of a struct, the function is not invoked automatically, but it
+can be used as a truth value for an if action and the like. To invoke
+it, use the call function, defined below.
A pipeline is a possibly chained sequence of "commands". A command is a simple
value (argument) or a function or method call, possibly with multiple arguments:
@@ -233,6 +253,17 @@ Predefined global functions are named as follows.
first empty argument or the last argument, that is,
"and x y" behaves as "if x then y else x". All the
arguments are evaluated.
+ call
+ Returns the result of calling the first argument, which
+ must be a function, with the remaining arguments as parameters.
+ Thus "call .X.Y 1 2" is, in Go notation, dot.X.Y(1, 2) where
+ Y is a func-valued field, map entry, or the like.
+ The first argument must be the result of an evaluation
+ that yields a value of function type (as distinct from
+ a predefined function such as print). The function must
+ return either one or two result values, the second of which
+ is of type error. If the arguments don't match the function
+ or the returned error value is non-nil, execution stops.
html
Returns the escaped HTML equivalent of the textual
representation of its arguments.
@@ -301,7 +332,7 @@ produce the text
By construction, a template may reside in only one association. If it's
necessary to have a template addressable from multiple associations, the
template definition must be parsed multiple times to create distinct *Template
-values.
+values, or must be copied with the Clone or AddParseTree method.
Parse may be called multiple times to assemble the various associated templates;
see the ParseFiles and ParseGlob functions and methods for simple ways to parse
diff --git a/libgo/go/text/template/exec.go b/libgo/go/text/template/exec.go
index 973189a8a62..ad0118e4e68 100644
--- a/libgo/go/text/template/exec.go
+++ b/libgo/go/text/template/exec.go
@@ -419,10 +419,11 @@ func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node
tField, ok := receiver.Type().FieldByName(fieldName)
if ok {
field := receiver.FieldByIndex(tField.Index)
- if hasArgs {
- s.errorf("%s is not a method but has arguments", fieldName)
- }
if tField.PkgPath == "" { // field is exported
+ // If it's a function, we must call it.
+ if hasArgs {
+ s.errorf("%s has arguments but cannot be invoked as function", fieldName)
+ }
return field
}
}
diff --git a/libgo/go/text/template/exec_test.go b/libgo/go/text/template/exec_test.go
index 2070cefde73..70ab39cad20 100644
--- a/libgo/go/text/template/exec_test.go
+++ b/libgo/go/text/template/exec_test.go
@@ -9,7 +9,6 @@ import (
"errors"
"flag"
"fmt"
- "os"
"reflect"
"strings"
"testing"
@@ -60,6 +59,10 @@ type T struct {
PI *int
PSI *[]int
NIL *int
+ // Function (not method)
+ BinaryFunc func(string, string) string
+ VariadicFunc func(...string) string
+ VariadicFuncInt func(int, ...string) string
// Template to test evaluation of templates.
Tmpl *Template
}
@@ -119,6 +122,9 @@ var tVal = &T{
Err: errors.New("erroozle"),
PI: newInt(23),
PSI: newIntSlice(21, 22, 23),
+ BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) },
+ VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") },
+ VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") },
Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X
}
@@ -168,10 +174,12 @@ func (t *T) MAdd(a int, b []int) []int {
return v
}
-// EPERM returns a value and an error according to its argument.
-func (t *T) EPERM(error bool) (bool, error) {
+var myError = errors.New("my error")
+
+// MyError returns a value and an error according to its argument.
+func (t *T) MyError(error bool) (bool, error) {
if error {
- return true, os.EPERM
+ return true, myError
}
return false, nil
}
@@ -296,8 +304,26 @@ var execTests = []execTest{
"{{with $x := .}}{{with .SI}}{{$.GetU.TrueFalse $.True}}{{end}}{{end}}",
"true", tVal, true},
+ // Function call builtin.
+ {".BinaryFunc", "{{call .BinaryFunc `1` `2`}}", "[1=2]", tVal, true},
+ {".VariadicFunc0", "{{call .VariadicFunc}}", "<>", tVal, true},
+ {".VariadicFunc2", "{{call .VariadicFunc `he` `llo`}}", "<he+llo>", tVal, true},
+ {".VariadicFuncInt", "{{call .VariadicFuncInt 33 `he` `llo`}}", "33=<he+llo>", tVal, true},
+ {"if .BinaryFunc call", "{{ if .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{end}}", "[1=2]", tVal, true},
+ {"if not .BinaryFunc call", "{{ if not .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{else}}No{{end}}", "No", tVal, true},
+
+ // Erroneous function calls (check args).
+ {".BinaryFuncTooFew", "{{call .BinaryFunc `1`}}", "", tVal, false},
+ {".BinaryFuncTooMany", "{{call .BinaryFunc `1` `2` `3`}}", "", tVal, false},
+ {".BinaryFuncBad0", "{{call .BinaryFunc 1 3}}", "", tVal, false},
+ {".BinaryFuncBad1", "{{call .BinaryFunc `1` 3}}", "", tVal, false},
+ {".VariadicFuncBad0", "{{call .VariadicFunc 3}}", "", tVal, false},
+ {".VariadicFuncIntBad0", "{{call .VariadicFuncInt}}", "", tVal, false},
+ {".VariadicFuncIntBad`", "{{call .VariadicFuncInt `x`}}", "", tVal, false},
+
// Pipelines.
{"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true},
+ {"pipeline func", "-{{call .VariadicFunc `llo` | call .VariadicFunc `he` }}-", "-<he+<llo>>-", tVal, true},
// If.
{"if true", "{{if true}}TRUE{{end}}", "TRUE", tVal, true},
@@ -417,8 +443,8 @@ var execTests = []execTest{
{"or as if false", `{{or .SIEmpty "slice is empty"}}`, "slice is empty", tVal, true},
// Error handling.
- {"error method, error", "{{.EPERM true}}", "", tVal, false},
- {"error method, no error", "{{.EPERM false}}", "false", tVal, true},
+ {"error method, error", "{{.MyError true}}", "", tVal, false},
+ {"error method, no error", "{{.MyError false}}", "false", tVal, true},
// Fixed bugs.
// Must separate dot and receiver; otherwise args are evaluated with dot set to variable.
@@ -565,18 +591,18 @@ func TestDelims(t *testing.T) {
func TestExecuteError(t *testing.T) {
b := new(bytes.Buffer)
tmpl := New("error")
- _, err := tmpl.Parse("{{.EPERM true}}")
+ _, err := tmpl.Parse("{{.MyError true}}")
if err != nil {
t.Fatalf("parse error: %s", err)
}
err = tmpl.Execute(b, tVal)
if err == nil {
t.Errorf("expected error; got none")
- } else if !strings.Contains(err.Error(), os.EPERM.Error()) {
+ } else if !strings.Contains(err.Error(), myError.Error()) {
if *debug {
fmt.Printf("test execute error: %s\n", err)
}
- t.Errorf("expected os.EPERM; got %s", err)
+ t.Errorf("expected myError; got %s", err)
}
}
diff --git a/libgo/go/text/template/funcs.go b/libgo/go/text/template/funcs.go
index d6e4bf1a216..525179cb499 100644
--- a/libgo/go/text/template/funcs.go
+++ b/libgo/go/text/template/funcs.go
@@ -24,6 +24,7 @@ type FuncMap map[string]interface{}
var builtins = FuncMap{
"and": and,
+ "call": call,
"html": HTMLEscaper,
"index": index,
"js": JSEscaper,
@@ -151,6 +152,53 @@ func length(item interface{}) (int, error) {
return 0, fmt.Errorf("len of type %s", v.Type())
}
+// Function invocation
+
+// call returns the result of evaluating the the first argument as a function.
+// The function must return 1 result, or 2 results, the second of which is an error.
+func call(fn interface{}, args ...interface{}) (interface{}, error) {
+ v := reflect.ValueOf(fn)
+ typ := v.Type()
+ if typ.Kind() != reflect.Func {
+ return nil, fmt.Errorf("non-function of type %s", typ)
+ }
+ if !goodFunc(typ) {
+ return nil, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut())
+ }
+ numIn := typ.NumIn()
+ var dddType reflect.Type
+ if typ.IsVariadic() {
+ if len(args) < numIn-1 {
+ return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1)
+ }
+ dddType = typ.In(numIn - 1).Elem()
+ } else {
+ if len(args) != numIn {
+ return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn)
+ }
+ }
+ argv := make([]reflect.Value, len(args))
+ for i, arg := range args {
+ value := reflect.ValueOf(arg)
+ // Compute the expected type. Clumsy because of variadics.
+ var argType reflect.Type
+ if !typ.IsVariadic() || i < numIn-1 {
+ argType = typ.In(i)
+ } else {
+ argType = dddType
+ }
+ if !value.Type().AssignableTo(argType) {
+ return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType)
+ }
+ argv[i] = reflect.ValueOf(arg)
+ }
+ result := v.Call(argv)
+ if len(result) == 2 {
+ return result[0].Interface(), result[1].Interface().(error)
+ }
+ return result[0].Interface(), nil
+}
+
// Boolean logic.
func truth(a interface{}) bool {
diff --git a/libgo/go/text/template/multi_test.go b/libgo/go/text/template/multi_test.go
index 274f5ef1477..22dedc4f839 100644
--- a/libgo/go/text/template/multi_test.go
+++ b/libgo/go/text/template/multi_test.go
@@ -193,7 +193,7 @@ func TestClone(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- clone := root.Clone()
+ clone := Must(root.Clone())
// Add variants to both.
_, err = root.Parse(cloneText3)
if err != nil {
@@ -265,6 +265,12 @@ func TestRedefinition(t *testing.T) {
if tmpl, err = New("tmpl1").Parse(`{{define "test"}}foo{{end}}`); err != nil {
t.Fatalf("parse 1: %v", err)
}
+ if _, err = tmpl.Parse(`{{define "test"}}bar{{end}}`); err == nil {
+ t.Fatal("expected error")
+ }
+ if !strings.Contains(err.Error(), "redefinition") {
+ t.Fatalf("expected redefinition error; got %v", err)
+ }
if _, err = tmpl.New("tmpl2").Parse(`{{define "test"}}bar{{end}}`); err == nil {
t.Fatal("expected error")
}
diff --git a/libgo/go/text/template/parse/lex.go b/libgo/go/text/template/parse/lex.go
index 97c19a160b0..54e75ee0ca2 100644
--- a/libgo/go/text/template/parse/lex.go
+++ b/libgo/go/text/template/parse/lex.go
@@ -187,7 +187,7 @@ func (l *lexer) lineNumber() int {
}
// error returns an error token and terminates the scan by passing
-// back a nil pointer that will be the next state, terminating l.run.
+// back a nil pointer that will be the next state, terminating l.nextItem.
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
l.items <- item{itemError, fmt.Sprintf(format, args...)}
return nil
diff --git a/libgo/go/text/template/parse/node.go b/libgo/go/text/template/parse/node.go
index 0d030b8b4be..db645624c56 100644
--- a/libgo/go/text/template/parse/node.go
+++ b/libgo/go/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/libgo/go/text/template/parse/parse.go b/libgo/go/text/template/parse/parse.go
index 4da756657d5..d67b3888085 100644
--- a/libgo/go/text/template/parse/parse.go
+++ b/libgo/go/text/template/parse/parse.go
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package parse builds parse trees for templates. The grammar is defined
-// in the documents for the template package.
+// Package parse builds parse trees for templates as defined by text/template
+// and html/template. Clients should use those packages to construct templates
+// rather than this one, which provides shared internal data structures not
+// intended for general use.
package parse
import (
@@ -191,6 +193,8 @@ func (t *Tree) add(treeSet map[string]*Tree) {
// IsEmptyTree reports whether this tree (node) is empty of everything but space.
func IsEmptyTree(n Node) bool {
switch n := n.(type) {
+ case nil:
+ return true
case *ActionNode:
case *IfNode:
case *ListNode:
diff --git a/libgo/go/text/template/parse/parse_test.go b/libgo/go/text/template/parse/parse_test.go
index 13c5548abbf..18c0a8b835e 100644
--- a/libgo/go/text/template/parse/parse_test.go
+++ b/libgo/go/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
@@ -273,6 +287,9 @@ var isEmptyTests = []isEmptyTest{
}
func TestIsEmpty(t *testing.T) {
+ if !IsEmptyTree(nil) {
+ t.Errorf("nil tree is not empty")
+ }
for _, test := range isEmptyTests {
tree, err := New("root").Parse(test.input, "", "", make(map[string]*Tree), nil)
if err != nil {
diff --git a/libgo/go/text/template/template.go b/libgo/go/text/template/template.go
index 87e39d3af74..82fc9e5e39d 100644
--- a/libgo/go/text/template/template.go
+++ b/libgo/go/text/template/template.go
@@ -69,9 +69,9 @@ func (t *Template) init() {
// templates. The actual representation is not copied, but the name space of
// associated templates is, so further calls to Parse in the copy will add
// templates to the copy but not to the original. Clone can be used to prepare
-// common templates and use them with variant definitions for other templates by
-// adding the variants after the clone is made.
-func (t *Template) Clone() *Template {
+// common templates and use them with variant definitions for other templates
+// by adding the variants after the clone is made.
+func (t *Template) Clone() (*Template, error) {
nt := t.copy(nil)
nt.init()
nt.tmpl[t.name] = nt
@@ -89,7 +89,7 @@ func (t *Template) Clone() *Template {
for k, v := range t.execFuncs {
nt.execFuncs[k] = v
}
- return nt
+ return nt, nil
}
// copy returns a shallow copy of t, with common set to the argument.
@@ -178,10 +178,11 @@ func (t *Template) Parse(text string) (*Template, error) {
tmpl = t.New(name)
}
// Even if t == tmpl, we need to install it in the common.tmpl map.
- if err := t.associate(tmpl); err != nil {
+ if replace, err := t.associate(tmpl, tree); err != nil {
return nil, err
+ } else if replace {
+ tmpl.Tree = tree
}
- tmpl.Tree = tree
tmpl.leftDelim = t.leftDelim
tmpl.rightDelim = t.rightDelim
}
@@ -191,22 +192,23 @@ func (t *Template) Parse(text string) (*Template, error) {
// associate installs the new template into the group of templates associated
// with t. It is an error to reuse a name except to overwrite an empty
// template. The two are already known to share the common structure.
-func (t *Template) associate(new *Template) error {
+// The boolean return value reports wither to store this tree as t.Tree.
+func (t *Template) associate(new *Template, tree *parse.Tree) (bool, error) {
if new.common != t.common {
panic("internal error: associate not common")
}
name := new.name
if old := t.tmpl[name]; old != nil {
oldIsEmpty := parse.IsEmptyTree(old.Root)
- newIsEmpty := new.Tree != nil && parse.IsEmptyTree(new.Root)
- if !oldIsEmpty && !newIsEmpty {
- return fmt.Errorf("template: redefinition of template %q", name)
- }
+ newIsEmpty := parse.IsEmptyTree(tree.Root)
if newIsEmpty {
// Whether old is empty or not, new is empty; no reason to replace old.
- return nil
+ return false, nil
+ }
+ if !oldIsEmpty {
+ return false, fmt.Errorf("template: redefinition of template %q", name)
}
}
t.tmpl[name] = new
- return nil
+ return true, nil
}