diff options
Diffstat (limited to 'libgo/go/text')
-rw-r--r-- | libgo/go/text/scanner/scanner.go | 7 | ||||
-rw-r--r-- | libgo/go/text/tabwriter/example_test.go | 38 | ||||
-rw-r--r-- | libgo/go/text/tabwriter/tabwriter.go | 6 | ||||
-rw-r--r-- | libgo/go/text/tabwriter/tabwriter_test.go | 3 | ||||
-rw-r--r-- | libgo/go/text/template/doc.go | 37 | ||||
-rw-r--r-- | libgo/go/text/template/exec.go | 7 | ||||
-rw-r--r-- | libgo/go/text/template/exec_test.go | 44 | ||||
-rw-r--r-- | libgo/go/text/template/funcs.go | 48 | ||||
-rw-r--r-- | libgo/go/text/template/multi_test.go | 8 | ||||
-rw-r--r-- | libgo/go/text/template/parse/lex.go | 2 | ||||
-rw-r--r-- | libgo/go/text/template/parse/node.go | 112 | ||||
-rw-r--r-- | libgo/go/text/template/parse/parse.go | 8 | ||||
-rw-r--r-- | libgo/go/text/template/parse/parse_test.go | 21 | ||||
-rw-r--r-- | libgo/go/text/template/template.go | 28 |
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 } |