diff options
author | Ian Lance Taylor <iant@google.com> | 2015-01-15 00:27:56 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2015-01-15 00:27:56 +0000 |
commit | f8d9fa9e80b57f89e7877ce6cad8a3464879009b (patch) | |
tree | 58a1724fee16d2b03c65678c4dd9b50bb97137a9 /libgo/go/text | |
parent | 6bd3f109d8d8fa58eeccd6b3504721b4f20c00c2 (diff) | |
download | gcc-f8d9fa9e80b57f89e7877ce6cad8a3464879009b.zip gcc-f8d9fa9e80b57f89e7877ce6cad8a3464879009b.tar.gz gcc-f8d9fa9e80b57f89e7877ce6cad8a3464879009b.tar.bz2 |
libgo, compiler: Upgrade libgo to Go 1.4, except for runtime.
This upgrades all of libgo other than the runtime package to
the Go 1.4 release. In Go 1.4 much of the runtime was
rewritten into Go. Merging that code will take more time and
will not change the API, so I'm putting it off for now.
There are a few runtime changes anyhow, to accomodate other
packages that rely on minor modifications to the runtime
support.
The compiler changes slightly to add a one-bit flag to each
type descriptor kind that is stored directly in an interface,
which for gccgo is currently only pointer types. Another
one-bit flag (gcprog) is reserved because it is used by the gc
compiler, but gccgo does not currently use it.
There is another error check in the compiler since I ran
across it during testing.
gotools/:
* Makefile.am (go_cmd_go_files): Sort entries. Add generate.go.
* Makefile.in: Rebuild.
From-SVN: r219627
Diffstat (limited to 'libgo/go/text')
-rw-r--r-- | libgo/go/text/scanner/scanner.go | 31 | ||||
-rw-r--r-- | libgo/go/text/scanner/scanner_test.go | 22 | ||||
-rw-r--r-- | libgo/go/text/template/doc.go | 9 | ||||
-rw-r--r-- | libgo/go/text/template/exec.go | 10 | ||||
-rw-r--r-- | libgo/go/text/template/exec_test.go | 91 | ||||
-rw-r--r-- | libgo/go/text/template/funcs.go | 86 | ||||
-rw-r--r-- | libgo/go/text/template/parse/node.go | 248 | ||||
-rw-r--r-- | libgo/go/text/template/parse/parse.go | 62 | ||||
-rw-r--r-- | libgo/go/text/template/parse/parse_test.go | 5 |
9 files changed, 403 insertions, 161 deletions
diff --git a/libgo/go/text/scanner/scanner.go b/libgo/go/text/scanner/scanner.go index db7ca73..5199ee4 100644 --- a/libgo/go/text/scanner/scanner.go +++ b/libgo/go/text/scanner/scanner.go @@ -11,7 +11,7 @@ // By default, a Scanner skips white space and Go comments and recognizes all // literals as defined by the Go language specification. It may be // customized to recognize only a subset of those literals and to recognize -// different white space characters. +// different identifier and white space characters. // // Basic usage pattern: // @@ -34,8 +34,6 @@ import ( "unicode/utf8" ) -// TODO(gri): Consider changing this to use the new (token) Position package. - // A source position is represented by a Position value. // A position is valid if Line > 0. type Position struct { @@ -68,6 +66,12 @@ func (pos Position) String() string { // // ScanIdents | ScanInts | SkipComments // +// With the exceptions of comments, which are skipped if SkipComments is +// set, unrecognized tokens are not ignored. Instead, the scanner simply +// returns the respective individual characters (or possibly sub-tokens). +// For instance, if the mode is ScanIdents (not ScanStrings), the string +// "foo" is scanned as the token sequence '"' Ident '"'. +// const ( ScanIdents = 1 << -Ident ScanInts = 1 << -Int @@ -164,6 +168,13 @@ type Scanner struct { // for values ch > ' '). The field may be changed at any time. Whitespace uint64 + // IsIdentRune is a predicate controlling the characters accepted + // as the ith rune in an identifier. The set of valid characters + // must not intersect with the set of white space characters. + // If no IsIdentRune function is set, regular Go identifiers are + // accepted instead. The field may be changed at any time. + IsIdentRune func(ch rune, i int) bool + // Start position of most recently scanned token; set by Scan. // Calling Init or Next invalidates the position (Line == 0). // The Filename field is always left untouched by the Scanner. @@ -334,9 +345,17 @@ func (s *Scanner) error(msg string) { fmt.Fprintf(os.Stderr, "%s: %s\n", pos, msg) } +func (s *Scanner) isIdentRune(ch rune, i int) bool { + if s.IsIdentRune != nil { + return s.IsIdentRune(ch, i) + } + return ch == '_' || unicode.IsLetter(ch) || unicode.IsDigit(ch) && i > 0 +} + func (s *Scanner) scanIdentifier() rune { - ch := s.next() // read character after first '_' or letter - for ch == '_' || unicode.IsLetter(ch) || unicode.IsDigit(ch) { + // we know the zero'th rune is OK; start scanning at the next one + ch := s.next() + for i := 1; s.isIdentRune(ch, i); i++ { ch = s.next() } return ch @@ -563,7 +582,7 @@ redo: // determine token value tok := ch switch { - case unicode.IsLetter(ch) || ch == '_': + case s.isIdentRune(ch, 0): if s.Mode&ScanIdents != 0 { tok = Ident ch = s.scanIdentifier() diff --git a/libgo/go/text/scanner/scanner_test.go b/libgo/go/text/scanner/scanner_test.go index 7d3f597..702fac2 100644 --- a/libgo/go/text/scanner/scanner_test.go +++ b/libgo/go/text/scanner/scanner_test.go @@ -357,6 +357,28 @@ func TestScanSelectedMask(t *testing.T) { testScanSelectedMode(t, ScanComments, Comment) } +func TestScanCustomIdent(t *testing.T) { + const src = "faab12345 a12b123 a12 3b" + s := new(Scanner).Init(strings.NewReader(src)) + // ident = ( 'a' | 'b' ) { digit } . + // digit = '0' .. '3' . + // with a maximum length of 4 + s.IsIdentRune = func(ch rune, i int) bool { + return i == 0 && (ch == 'a' || ch == 'b') || 0 < i && i < 4 && '0' <= ch && ch <= '3' + } + checkTok(t, s, 1, s.Scan(), 'f', "f") + checkTok(t, s, 1, s.Scan(), Ident, "a") + checkTok(t, s, 1, s.Scan(), Ident, "a") + checkTok(t, s, 1, s.Scan(), Ident, "b123") + checkTok(t, s, 1, s.Scan(), Int, "45") + checkTok(t, s, 1, s.Scan(), Ident, "a12") + checkTok(t, s, 1, s.Scan(), Ident, "b123") + checkTok(t, s, 1, s.Scan(), Ident, "a12") + checkTok(t, s, 1, s.Scan(), Int, "3") + checkTok(t, s, 1, s.Scan(), Ident, "b") + checkTok(t, s, 1, s.Scan(), EOF, "") +} + func TestScanNext(t *testing.T) { const BOM = '\uFEFF' BOMs := string(BOM) diff --git a/libgo/go/text/template/doc.go b/libgo/go/text/template/doc.go index 7c6efd5..223c595 100644 --- a/libgo/go/text/template/doc.go +++ b/libgo/go/text/template/doc.go @@ -338,10 +338,11 @@ arguments will be evaluated.) The comparison functions work on basic types only (or named basic types, such as "type Celsius float32"). They implement the Go rules for comparison of values, except that size and exact type are -ignored, so any integer value may be compared with any other integer -value, any unsigned integer value may be compared with any other -unsigned integer value, and so on. However, as usual, one may not -compare an int with a float32 and so on. +ignored, so any integer value, signed or unsigned, may be compared +with any other integer value. (The arithmetic value is compared, +not the bit pattern, so all negative integers are less than all +unsigned integers.) However, as usual, one may not compare an int +with a float32 and so on. Associated templates diff --git a/libgo/go/text/template/exec.go b/libgo/go/text/template/exec.go index 2f32312..b00e10c 100644 --- a/libgo/go/text/template/exec.go +++ b/libgo/go/text/template/exec.go @@ -393,7 +393,7 @@ func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value { switch { case constant.IsComplex: return reflect.ValueOf(constant.Complex128) // incontrovertible. - case constant.IsFloat && strings.IndexAny(constant.Text, ".eE") >= 0: + case constant.IsFloat && !isHexConstant(constant.Text) && strings.IndexAny(constant.Text, ".eE") >= 0: return reflect.ValueOf(constant.Float64) case constant.IsInt: n := int(constant.Int64) @@ -407,6 +407,10 @@ func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value { return zero } +func isHexConstant(s string) bool { + return len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') +} + func (s *state) evalFieldNode(dot reflect.Value, field *parse.FieldNode, args []parse.Node, final reflect.Value) reflect.Value { s.at(field) return s.evalFieldChain(dot, dot, field, field.Ident, args, final) @@ -542,7 +546,7 @@ func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, a argv := make([]reflect.Value, numIn) // Args must be evaluated. Fixed args first. i := 0 - for ; i < numFixed; i++ { + for ; i < numFixed && i < len(args); i++ { argv[i] = s.evalArg(dot, typ.In(i), args[i]) } // Now the ... args. @@ -632,6 +636,8 @@ func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) refle return s.validateType(s.evalPipeline(dot, arg), typ) case *parse.IdentifierNode: return s.evalFunction(dot, arg, arg, nil, zero) + case *parse.ChainNode: + return s.validateType(s.evalChainNode(dot, arg, nil, zero), typ) } switch typ.Kind() { case reflect.Bool: diff --git a/libgo/go/text/template/exec_test.go b/libgo/go/text/template/exec_test.go index 868f2cb..69c213e 100644 --- a/libgo/go/text/template/exec_test.go +++ b/libgo/go/text/template/exec_test.go @@ -176,6 +176,12 @@ func (t *T) Method3(v interface{}) string { return fmt.Sprintf("Method3: %v", v) } +func (t *T) Copy() *T { + n := new(T) + *n = *t + return n +} + func (t *T) MAdd(a int, b []int) []int { v := make([]int, len(b)) for i, x := range b { @@ -514,6 +520,13 @@ var execTests = []execTest{ {"bug10", "{{mapOfThree.three}}-{{(mapOfThree).three}}", "3-3", 0, true}, // Dereferencing nil pointer while evaluating function arguments should not panic. Issue 7333. {"bug11", "{{valueString .PS}}", "", T{}, false}, + // 0xef gave constant type float64. Issue 8622. + {"bug12xe", "{{printf `%T` 0xef}}", "int", T{}, true}, + {"bug12xE", "{{printf `%T` 0xEE}}", "int", T{}, true}, + {"bug12Xe", "{{printf `%T` 0Xef}}", "int", T{}, true}, + {"bug12XE", "{{printf `%T` 0XEE}}", "int", T{}, true}, + // Chained nodes did not work as arguments. Issue 8473. + {"bug13", "{{print (.Copy).I}}", "17", tVal, true}, } func zeroArgs() string { @@ -880,6 +893,18 @@ func TestMessageForExecuteEmpty(t *testing.T) { } } +func TestFinalForPrintf(t *testing.T) { + tmpl, err := New("").Parse(`{{"x" | printf}}`) + if err != nil { + t.Fatal(err) + } + var b bytes.Buffer + err = tmpl.Execute(&b, 0) + if err != nil { + t.Fatal(err) + } +} + type cmpTest struct { expr string truth string @@ -897,8 +922,8 @@ var cmpTests = []cmpTest{ {"eq 1 2", "false", true}, {"eq `xy` `xy`", "true", true}, {"eq `xy` `xyz`", "false", true}, - {"eq .Xuint .Xuint", "true", true}, - {"eq .Xuint .Yuint", "false", true}, + {"eq .Uthree .Uthree", "true", true}, + {"eq .Uthree .Ufour", "false", true}, {"eq 3 4 5 6 3", "true", true}, {"eq 3 4 5 6 7", "false", true}, {"ne true true", "false", true}, @@ -911,16 +936,16 @@ var cmpTests = []cmpTest{ {"ne 1 2", "true", true}, {"ne `xy` `xy`", "false", true}, {"ne `xy` `xyz`", "true", true}, - {"ne .Xuint .Xuint", "false", true}, - {"ne .Xuint .Yuint", "true", true}, + {"ne .Uthree .Uthree", "false", true}, + {"ne .Uthree .Ufour", "true", true}, {"lt 1.5 1.5", "false", true}, {"lt 1.5 2.5", "true", true}, {"lt 1 1", "false", true}, {"lt 1 2", "true", true}, {"lt `xy` `xy`", "false", true}, {"lt `xy` `xyz`", "true", true}, - {"lt .Xuint .Xuint", "false", true}, - {"lt .Xuint .Yuint", "true", true}, + {"lt .Uthree .Uthree", "false", true}, + {"lt .Uthree .Ufour", "true", true}, {"le 1.5 1.5", "true", true}, {"le 1.5 2.5", "true", true}, {"le 2.5 1.5", "false", true}, @@ -930,9 +955,9 @@ var cmpTests = []cmpTest{ {"le `xy` `xy`", "true", true}, {"le `xy` `xyz`", "true", true}, {"le `xyz` `xy`", "false", true}, - {"le .Xuint .Xuint", "true", true}, - {"le .Xuint .Yuint", "true", true}, - {"le .Yuint .Xuint", "false", true}, + {"le .Uthree .Uthree", "true", true}, + {"le .Uthree .Ufour", "true", true}, + {"le .Ufour .Uthree", "false", true}, {"gt 1.5 1.5", "false", true}, {"gt 1.5 2.5", "false", true}, {"gt 1 1", "false", true}, @@ -940,9 +965,9 @@ var cmpTests = []cmpTest{ {"gt 1 2", "false", true}, {"gt `xy` `xy`", "false", true}, {"gt `xy` `xyz`", "false", true}, - {"gt .Xuint .Xuint", "false", true}, - {"gt .Xuint .Yuint", "false", true}, - {"gt .Yuint .Xuint", "true", true}, + {"gt .Uthree .Uthree", "false", true}, + {"gt .Uthree .Ufour", "false", true}, + {"gt .Ufour .Uthree", "true", true}, {"ge 1.5 1.5", "true", true}, {"ge 1.5 2.5", "false", true}, {"ge 2.5 1.5", "true", true}, @@ -952,11 +977,40 @@ var cmpTests = []cmpTest{ {"ge `xy` `xy`", "true", true}, {"ge `xy` `xyz`", "false", true}, {"ge `xyz` `xy`", "true", true}, - {"ge .Xuint .Xuint", "true", true}, - {"ge .Xuint .Yuint", "false", true}, - {"ge .Yuint .Xuint", "true", true}, + {"ge .Uthree .Uthree", "true", true}, + {"ge .Uthree .Ufour", "false", true}, + {"ge .Ufour .Uthree", "true", true}, + // Mixing signed and unsigned integers. + {"eq .Uthree .Three", "true", true}, + {"eq .Three .Uthree", "true", true}, + {"le .Uthree .Three", "true", true}, + {"le .Three .Uthree", "true", true}, + {"ge .Uthree .Three", "true", true}, + {"ge .Three .Uthree", "true", true}, + {"lt .Uthree .Three", "false", true}, + {"lt .Three .Uthree", "false", true}, + {"gt .Uthree .Three", "false", true}, + {"gt .Three .Uthree", "false", true}, + {"eq .Ufour .Three", "false", true}, + {"lt .Ufour .Three", "false", true}, + {"gt .Ufour .Three", "true", true}, + {"eq .NegOne .Uthree", "false", true}, + {"eq .Uthree .NegOne", "false", true}, + {"ne .NegOne .Uthree", "true", true}, + {"ne .Uthree .NegOne", "true", true}, + {"lt .NegOne .Uthree", "true", true}, + {"lt .Uthree .NegOne", "false", true}, + {"le .NegOne .Uthree", "true", true}, + {"le .Uthree .NegOne", "false", true}, + {"gt .NegOne .Uthree", "false", true}, + {"gt .Uthree .NegOne", "true", true}, + {"ge .NegOne .Uthree", "false", true}, + {"ge .Uthree .NegOne", "true", true}, + {"eq (index `x` 0) 'x'", "true", true}, // The example that triggered this rule. + {"eq (index `x` 0) 'y'", "false", true}, // Errors {"eq `xy` 1", "", false}, // Different types. + {"eq 2 2.0", "", false}, // Different types. {"lt true true", "", false}, // Unordered types. {"lt 1+0i 1+0i", "", false}, // Unordered types. } @@ -964,13 +1018,14 @@ var cmpTests = []cmpTest{ func TestComparison(t *testing.T) { b := new(bytes.Buffer) var cmpStruct = struct { - Xuint, Yuint uint - }{3, 4} + Uthree, Ufour uint + NegOne, Three int + }{3, 4, -1, 3} for _, test := range cmpTests { text := fmt.Sprintf("{{if %s}}true{{else}}false{{end}}", test.expr) tmpl, err := New("empty").Parse(text) if err != nil { - t.Fatal(err) + t.Fatalf("%q: %s", test.expr, err) } b.Reset() err = tmpl.Execute(b, &cmpStruct) diff --git a/libgo/go/text/template/funcs.go b/libgo/go/text/template/funcs.go index e854122..39ee5ed 100644 --- a/libgo/go/text/template/funcs.go +++ b/libgo/go/text/template/funcs.go @@ -314,25 +314,34 @@ func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) { if err != nil { return false, err } - if k1 != k2 { - return false, errBadComparison - } truth := false - switch k1 { - case boolKind: - truth = v1.Bool() == v2.Bool() - case complexKind: - truth = v1.Complex() == v2.Complex() - case floatKind: - truth = v1.Float() == v2.Float() - case intKind: - truth = v1.Int() == v2.Int() - case stringKind: - truth = v1.String() == v2.String() - case uintKind: - truth = v1.Uint() == v2.Uint() - default: - panic("invalid kind") + if k1 != k2 { + // Special case: Can compare integer values regardless of type's sign. + switch { + case k1 == intKind && k2 == uintKind: + truth = v1.Int() >= 0 && uint64(v1.Int()) == v2.Uint() + case k1 == uintKind && k2 == intKind: + truth = v2.Int() >= 0 && v1.Uint() == uint64(v2.Int()) + default: + return false, errBadComparison + } + } else { + switch k1 { + case boolKind: + truth = v1.Bool() == v2.Bool() + case complexKind: + truth = v1.Complex() == v2.Complex() + case floatKind: + truth = v1.Float() == v2.Float() + case intKind: + truth = v1.Int() == v2.Int() + case stringKind: + truth = v1.String() == v2.String() + case uintKind: + truth = v1.Uint() == v2.Uint() + default: + panic("invalid kind") + } } if truth { return true, nil @@ -360,23 +369,32 @@ func lt(arg1, arg2 interface{}) (bool, error) { if err != nil { return false, err } - if k1 != k2 { - return false, errBadComparison - } truth := false - switch k1 { - case boolKind, complexKind: - return false, errBadComparisonType - case floatKind: - truth = v1.Float() < v2.Float() - case intKind: - truth = v1.Int() < v2.Int() - case stringKind: - truth = v1.String() < v2.String() - case uintKind: - truth = v1.Uint() < v2.Uint() - default: - panic("invalid kind") + if k1 != k2 { + // Special case: Can compare integer values regardless of type's sign. + switch { + case k1 == intKind && k2 == uintKind: + truth = v1.Int() < 0 || uint64(v1.Int()) < v2.Uint() + case k1 == uintKind && k2 == intKind: + truth = v2.Int() >= 0 && v1.Uint() < uint64(v2.Int()) + default: + return false, errBadComparison + } + } else { + switch k1 { + case boolKind, complexKind: + return false, errBadComparisonType + case floatKind: + truth = v1.Float() < v2.Float() + case intKind: + truth = v1.Int() < v2.Int() + case stringKind: + truth = v1.String() < v2.String() + case uintKind: + truth = v1.Uint() < v2.Uint() + default: + panic("invalid kind") + } } return truth, nil } diff --git a/libgo/go/text/template/parse/node.go b/libgo/go/text/template/parse/node.go index dc6a3bb..55c37f6 100644 --- a/libgo/go/text/template/parse/node.go +++ b/libgo/go/text/template/parse/node.go @@ -26,8 +26,9 @@ type Node interface { // CopyXxx methods that return *XxxNode. Copy() Node Position() Pos // byte position of start of node in full original input string - // Make sure only functions in this package can create Nodes. - unexported() + // tree returns the containing *Tree. + // It is unexported so all implementations of Node are in this package. + tree() *Tree } // NodeType identifies the type of a parse tree node. @@ -41,11 +42,6 @@ func (p Pos) Position() Pos { return p } -// unexported keeps Node implementations local to the package. -// All implementations embed Pos, so this takes care of it. -func (Pos) unexported() { -} - // Type returns itself and provides an easy default implementation // for embedding in a Node. Embedded in all non-trivial Nodes. func (t NodeType) Type() NodeType { @@ -81,17 +77,22 @@ const ( type ListNode struct { NodeType Pos + tr *Tree Nodes []Node // The element nodes in lexical order. } -func newList(pos Pos) *ListNode { - return &ListNode{NodeType: NodeList, Pos: pos} +func (t *Tree) newList(pos Pos) *ListNode { + return &ListNode{tr: t, NodeType: NodeList, Pos: pos} } func (l *ListNode) append(n Node) { l.Nodes = append(l.Nodes, n) } +func (l *ListNode) tree() *Tree { + return l.tr +} + func (l *ListNode) String() string { b := new(bytes.Buffer) for _, n := range l.Nodes { @@ -104,7 +105,7 @@ func (l *ListNode) CopyList() *ListNode { if l == nil { return l } - n := newList(l.Pos) + n := l.tr.newList(l.Pos) for _, elem := range l.Nodes { n.append(elem.Copy()) } @@ -119,32 +120,38 @@ func (l *ListNode) Copy() Node { type TextNode struct { NodeType Pos + tr *Tree Text []byte // The text; may span newlines. } -func newText(pos Pos, text string) *TextNode { - return &TextNode{NodeType: NodeText, Pos: pos, Text: []byte(text)} +func (t *Tree) newText(pos Pos, text string) *TextNode { + return &TextNode{tr: t, NodeType: NodeText, Pos: pos, Text: []byte(text)} } func (t *TextNode) String() string { return fmt.Sprintf(textFormat, t.Text) } +func (t *TextNode) tree() *Tree { + return t.tr +} + func (t *TextNode) Copy() Node { - return &TextNode{NodeType: NodeText, Text: append([]byte{}, t.Text...)} + return &TextNode{tr: t.tr, NodeType: NodeText, Pos: t.Pos, Text: append([]byte{}, t.Text...)} } // PipeNode holds a pipeline with optional declaration type PipeNode struct { NodeType Pos + tr *Tree Line int // The line number in the input (deprecated; kept for compatibility) Decl []*VariableNode // Variable declarations in lexical order. Cmds []*CommandNode // The commands in lexical order. } -func newPipeline(pos Pos, line int, decl []*VariableNode) *PipeNode { - return &PipeNode{NodeType: NodePipe, Pos: pos, Line: line, Decl: decl} +func (t *Tree) newPipeline(pos Pos, line int, decl []*VariableNode) *PipeNode { + return &PipeNode{tr: t, NodeType: NodePipe, Pos: pos, Line: line, Decl: decl} } func (p *PipeNode) append(command *CommandNode) { @@ -171,6 +178,10 @@ func (p *PipeNode) String() string { return s } +func (p *PipeNode) tree() *Tree { + return p.tr +} + func (p *PipeNode) CopyPipe() *PipeNode { if p == nil { return p @@ -179,7 +190,7 @@ func (p *PipeNode) CopyPipe() *PipeNode { for _, d := range p.Decl { decl = append(decl, d.Copy().(*VariableNode)) } - n := newPipeline(p.Pos, p.Line, decl) + n := p.tr.newPipeline(p.Pos, p.Line, decl) for _, c := range p.Cmds { n.append(c.Copy().(*CommandNode)) } @@ -196,12 +207,13 @@ func (p *PipeNode) Copy() Node { type ActionNode struct { NodeType Pos + tr *Tree Line int // The line number in the input (deprecated; kept for compatibility) Pipe *PipeNode // The pipeline in the action. } -func newAction(pos Pos, line int, pipe *PipeNode) *ActionNode { - return &ActionNode{NodeType: NodeAction, Pos: pos, Line: line, Pipe: pipe} +func (t *Tree) newAction(pos Pos, line int, pipe *PipeNode) *ActionNode { + return &ActionNode{tr: t, NodeType: NodeAction, Pos: pos, Line: line, Pipe: pipe} } func (a *ActionNode) String() string { @@ -209,8 +221,12 @@ func (a *ActionNode) String() string { } +func (a *ActionNode) tree() *Tree { + return a.tr +} + func (a *ActionNode) Copy() Node { - return newAction(a.Pos, a.Line, a.Pipe.CopyPipe()) + return a.tr.newAction(a.Pos, a.Line, a.Pipe.CopyPipe()) } @@ -218,11 +234,12 @@ func (a *ActionNode) Copy() Node { type CommandNode struct { NodeType Pos + tr *Tree Args []Node // Arguments in lexical order: Identifier, field, or constant. } -func newCommand(pos Pos) *CommandNode { - return &CommandNode{NodeType: NodeCommand, Pos: pos} +func (t *Tree) newCommand(pos Pos) *CommandNode { + return &CommandNode{tr: t, NodeType: NodeCommand, Pos: pos} } func (c *CommandNode) append(arg Node) { @@ -244,11 +261,15 @@ func (c *CommandNode) String() string { return s } +func (c *CommandNode) tree() *Tree { + return c.tr +} + func (c *CommandNode) Copy() Node { if c == nil { return c } - n := newCommand(c.Pos) + n := c.tr.newCommand(c.Pos) for _, c := range c.Args { n.append(c.Copy()) } @@ -259,6 +280,7 @@ func (c *CommandNode) Copy() Node { type IdentifierNode struct { NodeType Pos + tr *Tree Ident string // The identifier's name. } @@ -275,12 +297,24 @@ func (i *IdentifierNode) SetPos(pos Pos) *IdentifierNode { return i } +// SetTree sets the parent tree for the node. NewIdentifier is a public method so we can't modify its signature. +// Chained for convenience. +// TODO: fix one day? +func (i *IdentifierNode) SetTree(t *Tree) *IdentifierNode { + i.tr = t + return i +} + func (i *IdentifierNode) String() string { return i.Ident } +func (i *IdentifierNode) tree() *Tree { + return i.tr +} + func (i *IdentifierNode) Copy() Node { - return NewIdentifier(i.Ident).SetPos(i.Pos) + return NewIdentifier(i.Ident).SetTree(i.tr).SetPos(i.Pos) } // VariableNode holds a list of variable names, possibly with chained field @@ -288,11 +322,12 @@ func (i *IdentifierNode) Copy() Node { type VariableNode struct { NodeType Pos + tr *Tree Ident []string // Variable name and fields in lexical order. } -func newVariable(pos Pos, ident string) *VariableNode { - return &VariableNode{NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")} +func (t *Tree) newVariable(pos Pos, ident string) *VariableNode { + return &VariableNode{tr: t, NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")} } func (v *VariableNode) String() string { @@ -306,20 +341,29 @@ func (v *VariableNode) String() string { return s } +func (v *VariableNode) tree() *Tree { + return v.tr +} + func (v *VariableNode) Copy() Node { - return &VariableNode{NodeType: NodeVariable, Pos: v.Pos, Ident: append([]string{}, v.Ident...)} + return &VariableNode{tr: v.tr, NodeType: NodeVariable, Pos: v.Pos, Ident: append([]string{}, v.Ident...)} } // DotNode holds the special identifier '.'. type DotNode struct { + NodeType Pos + tr *Tree } -func newDot(pos Pos) *DotNode { - return &DotNode{Pos: pos} +func (t *Tree) newDot(pos Pos) *DotNode { + return &DotNode{tr: t, NodeType: NodeDot, Pos: pos} } func (d *DotNode) Type() NodeType { + // Override method on embedded NodeType for API compatibility. + // TODO: Not really a problem; could change API without effect but + // api tool complains. return NodeDot } @@ -327,20 +371,29 @@ func (d *DotNode) String() string { return "." } +func (d *DotNode) tree() *Tree { + return d.tr +} + func (d *DotNode) Copy() Node { - return newDot(d.Pos) + return d.tr.newDot(d.Pos) } // NilNode holds the special identifier 'nil' representing an untyped nil constant. type NilNode struct { + NodeType Pos + tr *Tree } -func newNil(pos Pos) *NilNode { - return &NilNode{Pos: pos} +func (t *Tree) newNil(pos Pos) *NilNode { + return &NilNode{tr: t, NodeType: NodeNil, Pos: pos} } func (n *NilNode) Type() NodeType { + // Override method on embedded NodeType for API compatibility. + // TODO: Not really a problem; could change API without effect but + // api tool complains. return NodeNil } @@ -348,8 +401,12 @@ func (n *NilNode) String() string { return "nil" } +func (n *NilNode) tree() *Tree { + return n.tr +} + func (n *NilNode) Copy() Node { - return newNil(n.Pos) + return n.tr.newNil(n.Pos) } // FieldNode holds a field (identifier starting with '.'). @@ -358,11 +415,12 @@ func (n *NilNode) Copy() Node { type FieldNode struct { NodeType Pos + tr *Tree Ident []string // The identifiers in lexical order. } -func newField(pos Pos, ident string) *FieldNode { - return &FieldNode{NodeType: NodeField, Pos: pos, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period +func (t *Tree) newField(pos Pos, ident string) *FieldNode { + return &FieldNode{tr: t, NodeType: NodeField, Pos: pos, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period } func (f *FieldNode) String() string { @@ -373,8 +431,12 @@ func (f *FieldNode) String() string { return s } +func (f *FieldNode) tree() *Tree { + return f.tr +} + func (f *FieldNode) Copy() Node { - return &FieldNode{NodeType: NodeField, Pos: f.Pos, Ident: append([]string{}, f.Ident...)} + return &FieldNode{tr: f.tr, NodeType: NodeField, Pos: f.Pos, Ident: append([]string{}, f.Ident...)} } // ChainNode holds a term followed by a chain of field accesses (identifier starting with '.'). @@ -383,12 +445,13 @@ func (f *FieldNode) Copy() Node { type ChainNode struct { NodeType Pos + tr *Tree Node Node Field []string // The identifiers in lexical order. } -func newChain(pos Pos, node Node) *ChainNode { - return &ChainNode{NodeType: NodeChain, Pos: pos, Node: node} +func (t *Tree) newChain(pos Pos, node Node) *ChainNode { + return &ChainNode{tr: t, NodeType: NodeChain, Pos: pos, Node: node} } // Add adds the named field (which should start with a period) to the end of the chain. @@ -414,19 +477,24 @@ func (c *ChainNode) String() string { return s } +func (c *ChainNode) tree() *Tree { + return c.tr +} + func (c *ChainNode) Copy() Node { - return &ChainNode{NodeType: NodeChain, Pos: c.Pos, Node: c.Node, Field: append([]string{}, c.Field...)} + return &ChainNode{tr: c.tr, NodeType: NodeChain, Pos: c.Pos, Node: c.Node, Field: append([]string{}, c.Field...)} } // BoolNode holds a boolean constant. type BoolNode struct { NodeType Pos + tr *Tree True bool // The value of the boolean constant. } -func newBool(pos Pos, true bool) *BoolNode { - return &BoolNode{NodeType: NodeBool, Pos: pos, True: true} +func (t *Tree) newBool(pos Pos, true bool) *BoolNode { + return &BoolNode{tr: t, NodeType: NodeBool, Pos: pos, True: true} } func (b *BoolNode) String() string { @@ -436,8 +504,12 @@ func (b *BoolNode) String() string { return "false" } +func (b *BoolNode) tree() *Tree { + return b.tr +} + func (b *BoolNode) Copy() Node { - return newBool(b.Pos, b.True) + return b.tr.newBool(b.Pos, b.True) } // NumberNode holds a number: signed or unsigned integer, float, or complex. @@ -446,6 +518,7 @@ func (b *BoolNode) Copy() Node { type NumberNode struct { NodeType Pos + tr *Tree IsInt bool // Number has an integral value. IsUint bool // Number has an unsigned integral value. IsFloat bool // Number has a floating-point value. @@ -457,8 +530,8 @@ type NumberNode struct { Text string // The original textual representation from the input. } -func newNumber(pos Pos, text string, typ itemType) (*NumberNode, error) { - n := &NumberNode{NodeType: NodeNumber, Pos: pos, Text: text} +func (t *Tree) newNumber(pos Pos, text string, typ itemType) (*NumberNode, error) { + n := &NumberNode{tr: t, NodeType: NodeNumber, Pos: pos, Text: text} switch typ { case itemCharConstant: rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0]) @@ -559,6 +632,10 @@ func (n *NumberNode) String() string { return n.Text } +func (n *NumberNode) tree() *Tree { + return n.tr +} + func (n *NumberNode) Copy() Node { nn := new(NumberNode) *nn = *n // Easy, fast, correct. @@ -569,53 +646,61 @@ func (n *NumberNode) Copy() Node { type StringNode struct { NodeType Pos + tr *Tree Quoted string // The original text of the string, with quotes. Text string // The string, after quote processing. } -func newString(pos Pos, orig, text string) *StringNode { - return &StringNode{NodeType: NodeString, Pos: pos, Quoted: orig, Text: text} +func (t *Tree) newString(pos Pos, orig, text string) *StringNode { + return &StringNode{tr: t, NodeType: NodeString, Pos: pos, Quoted: orig, Text: text} } func (s *StringNode) String() string { return s.Quoted } +func (s *StringNode) tree() *Tree { + return s.tr +} + func (s *StringNode) Copy() Node { - return newString(s.Pos, s.Quoted, s.Text) + return s.tr.newString(s.Pos, s.Quoted, s.Text) } // endNode represents an {{end}} action. // It does not appear in the final parse tree. type endNode struct { + NodeType Pos + tr *Tree } -func newEnd(pos Pos) *endNode { - return &endNode{Pos: pos} -} - -func (e *endNode) Type() NodeType { - return nodeEnd +func (t *Tree) newEnd(pos Pos) *endNode { + return &endNode{tr: t, NodeType: nodeEnd, Pos: pos} } func (e *endNode) String() string { return "{{end}}" } +func (e *endNode) tree() *Tree { + return e.tr +} + func (e *endNode) Copy() Node { - return newEnd(e.Pos) + return e.tr.newEnd(e.Pos) } // elseNode represents an {{else}} action. Does not appear in the final tree. type elseNode struct { NodeType Pos + tr *Tree Line int // The line number in the input (deprecated; kept for compatibility) } -func newElse(pos Pos, line int) *elseNode { - return &elseNode{NodeType: nodeElse, Pos: pos, Line: line} +func (t *Tree) newElse(pos Pos, line int) *elseNode { + return &elseNode{tr: t, NodeType: nodeElse, Pos: pos, Line: line} } func (e *elseNode) Type() NodeType { @@ -626,14 +711,19 @@ func (e *elseNode) String() string { return "{{else}}" } +func (e *elseNode) tree() *Tree { + return e.tr +} + func (e *elseNode) Copy() Node { - return newElse(e.Pos, e.Line) + return e.tr.newElse(e.Pos, e.Line) } // BranchNode is the common representation of if, range, and with. type BranchNode struct { NodeType Pos + tr *Tree Line int // The line number in the input (deprecated; kept for compatibility) Pipe *PipeNode // The pipeline to be evaluated. List *ListNode // What to execute if the value is non-empty. @@ -658,17 +748,34 @@ func (b *BranchNode) String() string { return fmt.Sprintf("{{%s %s}}%s{{end}}", name, b.Pipe, b.List) } +func (b *BranchNode) tree() *Tree { + return b.tr +} + +func (b *BranchNode) Copy() Node { + switch b.NodeType { + case NodeIf: + return b.tr.newIf(b.Pos, b.Line, b.Pipe, b.List, b.ElseList) + case NodeRange: + return b.tr.newRange(b.Pos, b.Line, b.Pipe, b.List, b.ElseList) + case NodeWith: + return b.tr.newWith(b.Pos, b.Line, b.Pipe, b.List, b.ElseList) + default: + panic("unknown branch type") + } +} + // IfNode represents an {{if}} action and its commands. type IfNode struct { BranchNode } -func newIf(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *IfNode { - return &IfNode{BranchNode{NodeType: NodeIf, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} +func (t *Tree) newIf(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *IfNode { + return &IfNode{BranchNode{tr: t, NodeType: NodeIf, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} } func (i *IfNode) Copy() Node { - return newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList()) + return i.tr.newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList()) } // RangeNode represents a {{range}} action and its commands. @@ -676,12 +783,12 @@ type RangeNode struct { BranchNode } -func newRange(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode { - return &RangeNode{BranchNode{NodeType: NodeRange, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} +func (t *Tree) newRange(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode { + return &RangeNode{BranchNode{tr: t, NodeType: NodeRange, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} } func (r *RangeNode) Copy() Node { - return newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList()) + return r.tr.newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList()) } // WithNode represents a {{with}} action and its commands. @@ -689,25 +796,26 @@ type WithNode struct { BranchNode } -func newWith(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *WithNode { - return &WithNode{BranchNode{NodeType: NodeWith, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} +func (t *Tree) newWith(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *WithNode { + return &WithNode{BranchNode{tr: t, NodeType: NodeWith, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} } func (w *WithNode) Copy() Node { - return newWith(w.Pos, w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList()) + return w.tr.newWith(w.Pos, w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList()) } // TemplateNode represents a {{template}} action. type TemplateNode struct { NodeType Pos + tr *Tree Line int // The line number in the input (deprecated; kept for compatibility) Name string // The name of the template (unquoted). Pipe *PipeNode // The command to evaluate as dot for the template. } -func newTemplate(pos Pos, line int, name string, pipe *PipeNode) *TemplateNode { - return &TemplateNode{NodeType: NodeTemplate, Line: line, Pos: pos, Name: name, Pipe: pipe} +func (t *Tree) newTemplate(pos Pos, line int, name string, pipe *PipeNode) *TemplateNode { + return &TemplateNode{tr: t, NodeType: NodeTemplate, Pos: pos, Line: line, Name: name, Pipe: pipe} } func (t *TemplateNode) String() string { @@ -717,6 +825,10 @@ func (t *TemplateNode) String() string { return fmt.Sprintf("{{template %q %s}}", t.Name, t.Pipe) } +func (t *TemplateNode) tree() *Tree { + return t.tr +} + func (t *TemplateNode) Copy() Node { - return newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe()) + return t.tr.newTemplate(t.Pos, 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 34112fb..af33880 100644 --- a/libgo/go/text/template/parse/parse.go +++ b/libgo/go/text/template/parse/parse.go @@ -129,9 +129,15 @@ func New(name string, funcs ...map[string]interface{}) *Tree { } // ErrorContext returns a textual representation of the location of the node in the input text. +// The receiver is only used when the node does not have a pointer to the tree inside, +// which can occur in old code. func (t *Tree) ErrorContext(n Node) (location, context string) { pos := int(n.Position()) - text := t.text[:pos] + tree := n.tree() + if tree == nil { + tree = t + } + text := tree.text[:pos] byteNum := strings.LastIndex(text, "\n") if byteNum == -1 { byteNum = pos // On first line. @@ -144,7 +150,7 @@ func (t *Tree) ErrorContext(n Node) (location, context string) { if len(context) > 20 { context = fmt.Sprintf("%.20s...", context) } - return fmt.Sprintf("%s:%d:%d", t.ParseName, lineNum, byteNum), context + return fmt.Sprintf("%s:%d:%d", tree.ParseName, lineNum, byteNum), context } // errorf formats the error and terminates processing. @@ -268,7 +274,7 @@ func IsEmptyTree(n Node) bool { // as itemList except it also parses {{define}} actions. // It runs to EOF. func (t *Tree) parse(treeSet map[string]*Tree) (next Node) { - t.Root = newList(t.peek().pos) + t.Root = t.newList(t.peek().pos) for t.peek().typ != itemEOF { if t.peek().typ == itemLeftDelim { delim := t.next() @@ -316,7 +322,7 @@ func (t *Tree) parseDefinition(treeSet map[string]*Tree) { // textOrAction* // Terminates at {{end}} or {{else}}, returned separately. func (t *Tree) itemList() (list *ListNode, next Node) { - list = newList(t.peekNonSpace().pos) + list = t.newList(t.peekNonSpace().pos) for t.peekNonSpace().typ != itemEOF { n := t.textOrAction() switch n.Type() { @@ -334,7 +340,7 @@ func (t *Tree) itemList() (list *ListNode, next Node) { func (t *Tree) textOrAction() Node { switch token := t.nextNonSpace(); token.typ { case itemText: - return newText(token.pos, token.val) + return t.newText(token.pos, token.val) case itemLeftDelim: return t.action() default: @@ -365,7 +371,7 @@ func (t *Tree) action() (n Node) { } t.backup() // Do not pop variables; they persist until "end". - return newAction(t.peek().pos, t.lex.lineNumber(), t.pipeline("command")) + return t.newAction(t.peek().pos, t.lex.lineNumber(), t.pipeline("command")) } // Pipeline: @@ -384,7 +390,7 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) { tokenAfterVariable := t.peek() if next := t.peekNonSpace(); next.typ == itemColonEquals || (next.typ == itemChar && next.val == ",") { t.nextNonSpace() - variable := newVariable(v.pos, v.val) + variable := t.newVariable(v.pos, v.val) decl = append(decl, variable) t.vars = append(t.vars, v.val) if next.typ == itemChar && next.val == "," { @@ -401,7 +407,7 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) { } break } - pipe = newPipeline(pos, t.lex.lineNumber(), decl) + pipe = t.newPipeline(pos, t.lex.lineNumber(), decl) for { switch token := t.nextNonSpace(); token.typ { case itemRightDelim, itemRightParen: @@ -442,7 +448,7 @@ func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int // TODO: Should we allow else-if in with and range? if t.peek().typ == itemIf { t.next() // Consume the "if" token. - elseList = newList(next.Position()) + elseList = t.newList(next.Position()) elseList.append(t.ifControl()) // Do not consume the next item - only one {{end}} required. break @@ -461,7 +467,7 @@ func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int // {{if pipeline}} itemList {{else}} itemList {{end}} // If keyword is past. func (t *Tree) ifControl() Node { - return newIf(t.parseControl(true, "if")) + return t.newIf(t.parseControl(true, "if")) } // Range: @@ -469,7 +475,7 @@ func (t *Tree) ifControl() Node { // {{range pipeline}} itemList {{else}} itemList {{end}} // Range keyword is past. func (t *Tree) rangeControl() Node { - return newRange(t.parseControl(false, "range")) + return t.newRange(t.parseControl(false, "range")) } // With: @@ -477,14 +483,14 @@ func (t *Tree) rangeControl() Node { // {{with pipeline}} itemList {{else}} itemList {{end}} // If keyword is past. func (t *Tree) withControl() Node { - return newWith(t.parseControl(false, "with")) + return t.newWith(t.parseControl(false, "with")) } // End: // {{end}} // End keyword is past. func (t *Tree) endControl() Node { - return newEnd(t.expect(itemRightDelim, "end").pos) + return t.newEnd(t.expect(itemRightDelim, "end").pos) } // Else: @@ -495,9 +501,9 @@ func (t *Tree) elseControl() Node { peek := t.peekNonSpace() if peek.typ == itemIf { // We see "{{else if ... " but in effect rewrite it to {{else}}{{if ... ". - return newElse(peek.pos, t.lex.lineNumber()) + return t.newElse(peek.pos, t.lex.lineNumber()) } - return newElse(t.expect(itemRightDelim, "else").pos, t.lex.lineNumber()) + return t.newElse(t.expect(itemRightDelim, "else").pos, t.lex.lineNumber()) } // Template: @@ -523,7 +529,7 @@ func (t *Tree) templateControl() Node { // Do not pop variables; they persist until "end". pipe = t.pipeline("template") } - return newTemplate(token.pos, t.lex.lineNumber(), name, pipe) + return t.newTemplate(token.pos, t.lex.lineNumber(), name, pipe) } // command: @@ -531,7 +537,7 @@ func (t *Tree) templateControl() Node { // space-separated arguments up to a pipeline character or right delimiter. // we consume the pipe character but leave the right delim to terminate the action. func (t *Tree) command() *CommandNode { - cmd := newCommand(t.peekNonSpace().pos) + cmd := t.newCommand(t.peekNonSpace().pos) for { t.peekNonSpace() // skip leading spaces. operand := t.operand() @@ -568,7 +574,7 @@ func (t *Tree) operand() Node { return nil } if t.peek().typ == itemField { - chain := newChain(t.peek().pos, node) + chain := t.newChain(t.peek().pos, node) for t.peek().typ == itemField { chain.Add(t.next().val) } @@ -578,9 +584,9 @@ func (t *Tree) operand() Node { // TODO: Switch to Chains always when we can. switch node.Type() { case NodeField: - node = newField(chain.Position(), chain.String()) + node = t.newField(chain.Position(), chain.String()) case NodeVariable: - node = newVariable(chain.Position(), chain.String()) + node = t.newVariable(chain.Position(), chain.String()) default: node = chain } @@ -605,19 +611,19 @@ func (t *Tree) term() Node { if !t.hasFunction(token.val) { t.errorf("function %q not defined", token.val) } - return NewIdentifier(token.val).SetPos(token.pos) + return NewIdentifier(token.val).SetTree(t).SetPos(token.pos) case itemDot: - return newDot(token.pos) + return t.newDot(token.pos) case itemNil: - return newNil(token.pos) + return t.newNil(token.pos) case itemVariable: return t.useVar(token.pos, token.val) case itemField: - return newField(token.pos, token.val) + return t.newField(token.pos, token.val) case itemBool: - return newBool(token.pos, token.val == "true") + return t.newBool(token.pos, token.val == "true") case itemCharConstant, itemComplex, itemNumber: - number, err := newNumber(token.pos, token.val, token.typ) + number, err := t.newNumber(token.pos, token.val, token.typ) if err != nil { t.error(err) } @@ -633,7 +639,7 @@ func (t *Tree) term() Node { if err != nil { t.error(err) } - return newString(token.pos, token.val, s) + return t.newString(token.pos, token.val, s) } t.backup() return nil @@ -660,7 +666,7 @@ func (t *Tree) popVars(n int) { // useVar returns a node for a variable reference. It errors if the // variable is not defined. func (t *Tree) useVar(pos Pos, name string) Node { - v := newVariable(pos, name) + v := t.newVariable(pos, name) for _, varName := range t.vars { if varName == v.Ident[0] { return v diff --git a/libgo/go/text/template/parse/parse_test.go b/libgo/go/text/template/parse/parse_test.go index ba1a18e..4a504fa 100644 --- a/libgo/go/text/template/parse/parse_test.go +++ b/libgo/go/text/template/parse/parse_test.go @@ -69,6 +69,8 @@ var numberTests = []numberTest{ {text: "1+2."}, {text: "'x"}, {text: "'xx'"}, + // Issue 8622 - 0xe parsed as floating point. Very embarrassing. + {"0xef", true, true, true, false, 0xef, 0xef, 0xef, 0}, } func TestNumberParse(t *testing.T) { @@ -77,6 +79,7 @@ func TestNumberParse(t *testing.T) { // because imaginary comes out as a number. var c complex128 typ := itemNumber + var tree *Tree if test.text[0] == '\'' { typ = itemCharConstant } else { @@ -85,7 +88,7 @@ func TestNumberParse(t *testing.T) { typ = itemComplex } } - n, err := newNumber(0, test.text, typ) + n, err := tree.newNumber(0, test.text, typ) ok := test.isInt || test.isUint || test.isFloat || test.isComplex if ok && err != nil { t.Errorf("unexpected error for %q: %s", test.text, err) |