diff options
| author | Robert Griesemer <gri@golang.org> | 2016-12-09 17:15:05 -0800 |
|---|---|---|
| committer | Robert Griesemer <gri@golang.org> | 2017-01-09 22:33:23 +0000 |
| commit | 4808fc444307fa683bf3df6d55f9ad1828891a36 (patch) | |
| tree | ca3c3cc1b0b52d467091bca2a42f2722154854d9 /src/cmd/compile/internal/syntax | |
| parent | 2d429f01bd917c42e66e1991eab9c2e33d813d16 (diff) | |
| download | go-4808fc444307fa683bf3df6d55f9ad1828891a36.tar.xz | |
[dev.inline] cmd/internal/src: replace src.Pos with syntax.Pos
This replaces the src.Pos LineHist-based position tracking with
the syntax.Pos implementation and updates all uses.
The LineHist table is not used anymore - the respective code is still
there but should be removed eventually. CL forthcoming.
Passes toolstash -cmp when comparing to the master repo (with the
exception of a couple of swapped assembly instructions, likely due
to different instruction scheduling because the line-based sorting
has changed; though this is won't affect correctness).
The sizes of various important compiler data structures have increased
significantly (see the various sizes_test.go files); this is probably
the reason for an increase of compilation times (to be addressed). Here
are the results of compilebench -count 5, run on a "quiet" machine (no
apps running besides a terminal):
name old time/op new time/op delta
Template 256ms ± 1% 280ms ±15% +9.54% (p=0.008 n=5+5)
Unicode 132ms ± 1% 132ms ± 1% ~ (p=0.690 n=5+5)
GoTypes 891ms ± 1% 917ms ± 2% +2.88% (p=0.008 n=5+5)
Compiler 3.84s ± 2% 3.99s ± 2% +3.95% (p=0.016 n=5+5)
MakeBash 47.1s ± 1% 47.2s ± 2% ~ (p=0.841 n=5+5)
name old user-ns/op new user-ns/op delta
Template 309M ± 1% 326M ± 2% +5.18% (p=0.008 n=5+5)
Unicode 165M ± 1% 168M ± 4% ~ (p=0.421 n=5+5)
GoTypes 1.14G ± 2% 1.18G ± 1% +3.47% (p=0.008 n=5+5)
Compiler 5.00G ± 1% 5.16G ± 1% +3.12% (p=0.008 n=5+5)
Change-Id: I241c4246cdff627d7ecb95cac23060b38f9775ec
Reviewed-on: https://go-review.googlesource.com/34273
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Diffstat (limited to 'src/cmd/compile/internal/syntax')
| -rw-r--r-- | src/cmd/compile/internal/syntax/dumper_test.go | 2 | ||||
| -rw-r--r-- | src/cmd/compile/internal/syntax/nodes.go | 8 | ||||
| -rw-r--r-- | src/cmd/compile/internal/syntax/parser.go | 24 | ||||
| -rw-r--r-- | src/cmd/compile/internal/syntax/parser_test.go | 11 | ||||
| -rw-r--r-- | src/cmd/compile/internal/syntax/pos.go | 180 | ||||
| -rw-r--r-- | src/cmd/compile/internal/syntax/pos_test.go | 130 | ||||
| -rw-r--r-- | src/cmd/compile/internal/syntax/printer_test.go | 2 | ||||
| -rw-r--r-- | src/cmd/compile/internal/syntax/syntax.go | 21 |
8 files changed, 38 insertions, 340 deletions
diff --git a/src/cmd/compile/internal/syntax/dumper_test.go b/src/cmd/compile/internal/syntax/dumper_test.go index 2b20cbdd97..1186193aba 100644 --- a/src/cmd/compile/internal/syntax/dumper_test.go +++ b/src/cmd/compile/internal/syntax/dumper_test.go @@ -14,7 +14,7 @@ func TestDump(t *testing.T) { t.Skip("skipping test in short mode") } - ast, err := ParseFile(*src, nil, nil, 0) + ast, err := ParseFile(*src_, nil, nil, 0) if err != nil { t.Fatal(err) } diff --git a/src/cmd/compile/internal/syntax/nodes.go b/src/cmd/compile/internal/syntax/nodes.go index adbb4da750..82c6c1907f 100644 --- a/src/cmd/compile/internal/syntax/nodes.go +++ b/src/cmd/compile/internal/syntax/nodes.go @@ -4,11 +4,13 @@ package syntax +import "cmd/internal/src" + // ---------------------------------------------------------------------------- // Nodes type Node interface { - Pos() Pos + Pos() src.Pos aNode() init(p *parser) } @@ -16,10 +18,10 @@ type Node interface { type node struct { // commented out for now since not yet used // doc *Comment // nil means no comment(s) attached - pos Pos + pos src.Pos } -func (n *node) Pos() Pos { +func (n *node) Pos() src.Pos { return n.pos } diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go index eb9c3e4aa5..e70ac3d714 100644 --- a/src/cmd/compile/internal/syntax/parser.go +++ b/src/cmd/compile/internal/syntax/parser.go @@ -5,6 +5,7 @@ package syntax import ( + "cmd/internal/src" "fmt" "io" "strconv" @@ -20,7 +21,7 @@ const trace = false const gcCompat = true type parser struct { - base *PosBase + base *src.PosBase errh ErrorHandler scanner @@ -32,11 +33,11 @@ type parser struct { indent []byte // tracing support } -func (p *parser) init(filename string, src io.Reader, errh ErrorHandler, pragh PragmaHandler) { - p.base = NewFileBase(filename) +func (p *parser) init(base *src.PosBase, r io.Reader, errh ErrorHandler, pragh PragmaHandler) { + p.base = base p.errh = errh p.scanner.init( - src, + r, // Error and pragma handlers for scanner. // Because the (line, col) positions passed to these // handlers are always at or after the current reading @@ -48,6 +49,7 @@ func (p *parser) init(filename string, src io.Reader, errh ErrorHandler, pragh P func(line, col uint, text string) { if strings.HasPrefix(text, "line ") { p.updateBase(line, col+5, text[5:]) + return } if pragh != nil { p.pragma |= pragh(p.pos_at(line, col), text) @@ -63,6 +65,8 @@ func (p *parser) init(filename string, src io.Reader, errh ErrorHandler, pragh P p.indent = nil } +const lineMax = 1<<24 - 1 // TODO(gri) this limit is defined for src.Pos - fix + func (p *parser) updateBase(line, col uint, text string) { // Want to use LastIndexByte below but it's not defined in Go1.4 and bootstrap fails. i := strings.LastIndex(text, ":") // look from right (Windows filenames may contain ':') @@ -75,7 +79,7 @@ func (p *parser) updateBase(line, col uint, text string) { p.error_at(p.pos_at(line, col+uint(i+1)), "invalid line number: "+nstr) return } - p.base = NewLinePragmaBase(MakePos(p.base.Pos().Base(), line, col), text[:i], uint(n)) + p.base = src.NewLinePragmaBase(src.MakePos(p.base.Pos().Base(), line, col), text[:i], uint(n)) } func (p *parser) got(tok token) bool { @@ -97,12 +101,12 @@ func (p *parser) want(tok token) { // Error handling // pos_at returns the Pos value for (line, col) and the current position base. -func (p *parser) pos_at(line, col uint) Pos { - return MakePos(p.base, line, col) +func (p *parser) pos_at(line, col uint) src.Pos { + return src.MakePos(p.base, line, col) } // error reports an error at the given position. -func (p *parser) error_at(pos Pos, msg string) { +func (p *parser) error_at(pos src.Pos, msg string) { err := Error{pos, msg} if p.first == nil { p.first = err @@ -114,7 +118,7 @@ func (p *parser) error_at(pos Pos, msg string) { } // syntax_error_at reports a syntax error at the given position. -func (p *parser) syntax_error_at(pos Pos, msg string) { +func (p *parser) syntax_error_at(pos src.Pos, msg string) { if trace { defer p.trace("syntax_error (" + msg + ")")() } @@ -159,7 +163,7 @@ func (p *parser) syntax_error_at(pos Pos, msg string) { } // Convenience methods using the current token position. -func (p *parser) pos() Pos { return p.pos_at(p.line, p.col) } +func (p *parser) pos() src.Pos { return p.pos_at(p.line, p.col) } func (p *parser) error(msg string) { p.error_at(p.pos(), msg) } func (p *parser) syntax_error(msg string) { p.syntax_error_at(p.pos(), msg) } diff --git a/src/cmd/compile/internal/syntax/parser_test.go b/src/cmd/compile/internal/syntax/parser_test.go index 23fed3b105..da56168957 100644 --- a/src/cmd/compile/internal/syntax/parser_test.go +++ b/src/cmd/compile/internal/syntax/parser_test.go @@ -6,6 +6,7 @@ package syntax import ( "bytes" + "cmd/internal/src" "flag" "fmt" "io/ioutil" @@ -18,11 +19,11 @@ import ( ) var fast = flag.Bool("fast", false, "parse package files in parallel") -var src = flag.String("src", "parser.go", "source file to parse") +var src_ = flag.String("src", "parser.go", "source file to parse") var verify = flag.Bool("verify", false, "verify idempotent printing") func TestParse(t *testing.T) { - _, err := ParseFile(*src, nil, nil, 0) + _, err := ParseFile(*src_, nil, nil, 0) if err != nil { t.Fatal(err) } @@ -133,7 +134,7 @@ func verifyPrint(filename string, ast1 *File) { panic(err) } - ast2, err := ParseBytes(filename, buf1.Bytes(), nil, nil, 0) + ast2, err := ParseBytes(src.NewFileBase(filename, filename), buf1.Bytes(), nil, nil, 0) if err != nil { panic(err) } @@ -157,7 +158,7 @@ func verifyPrint(filename string, ast1 *File) { } func TestIssue17697(t *testing.T) { - _, err := ParseBytes("", nil, nil, nil, 0) // return with parser error, don't panic + _, err := ParseBytes(nil, nil, nil, nil, 0) // return with parser error, don't panic if err == nil { t.Errorf("no error reported") } @@ -202,7 +203,7 @@ func TestLineDirectives(t *testing.T) { {"//line foo:123\n foo", "syntax error: package statement must be first", "foo", 123, 3}, {"//line foo:123\n//line bar:345\nfoo", "syntax error: package statement must be first", "bar", 345, 0}, } { - _, err := ParseBytes("", []byte(test.src), nil, nil, 0) + _, err := ParseBytes(nil, []byte(test.src), nil, nil, 0) if err == nil { t.Errorf("%s: no error reported", test.src) continue diff --git a/src/cmd/compile/internal/syntax/pos.go b/src/cmd/compile/internal/syntax/pos.go deleted file mode 100644 index 98cdae9327..0000000000 --- a/src/cmd/compile/internal/syntax/pos.go +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2016 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. - -// This file implements the encoding of source positions. - -package syntax - -import "strconv" - -// A Pos encodes a source position consisting of a (line, column) number pair -// and a position base. A zero Pos is a ready to use "unknown" position (nil -// position base and zero line number). -// -// The (line, column) values refer to a position in a file independent of any -// position base ("absolute" position). Line numbers start at 1, column values -// start at 0 and are byte offsets from the beginning of the line. -// -// The position base is used to determine the "relative" position, that is the -// filename and line number relative to the position base. If the base refers -// to the current file, there is no difference between absolute and relative -// positions. If it refers to a //line pragma, a relative position is relative -// to that pragma. A position base in turn contains the position at which it -// was introduced in the current file. -type Pos struct { - base *PosBase - lico -} - -// NoPos is a valid unknown position. -var NoPos Pos - -// MakePos creates a new Pos value with the given base, and (file-absolute) -// line and column. -func MakePos(base *PosBase, line, col uint) Pos { - return Pos{base, makeLico(line, col)} -} - -// IsKnown reports whether the position p is known. -func (p Pos) IsKnown() bool { - return p.base != nil || p.Line() != 0 -} - -// Before reports whether the position p comes before q in the source. -// For positions in different files, ordering is by filename. -func (p Pos) Before(q Pos) bool { - n, m := p.Filename(), q.Filename() - return n < m || n == m && p.lico < q.lico -} - -// After reports whether the position p comes after q in the source. -// For positions in different files, ordering is by filename. -func (p Pos) After(q Pos) bool { - n, m := p.Filename(), q.Filename() - return n > m || n == m && p.lico > q.lico -} - -// Filename returns the name of the actual file containing this position. -func (p Pos) Filename() string { return p.base.Pos().RelFilename() } - -// Base returns the position base. -func (p Pos) Base() *PosBase { return p.base } - -// RelFilename returns the filename recorded with the position's base. -func (p Pos) RelFilename() string { return p.base.Filename() } - -// RelLine returns the line number relative to the positions's base. -func (p Pos) RelLine() uint { b := p.base; return b.Line() + p.Line() - b.Pos().Line() } - -func (p Pos) String() string { - b := p.base - - if b == b.Pos().base { - // base is file base (incl. nil) - return posString(b.Filename(), p.Line(), p.Col()) - } - - // base is relative - return posString(b.Filename(), p.RelLine(), p.Col()) + "[" + b.Pos().String() + "]" -} - -// posString formats a (filename, line, col) tuple as a printable position. -func posString(filename string, line, col uint) string { - s := filename + ":" + strconv.FormatUint(uint64(line), 10) - // col == colMax is interpreted as unknown column value - if col < colMax { - s += ":" + strconv.FormatUint(uint64(col), 10) - } - return s -} - -// ---------------------------------------------------------------------------- -// PosBase - -// A PosBase encodes a filename and base line number. -// Typically, each file and line pragma introduce a PosBase. -// A nil *PosBase is a ready to use file PosBase for an unnamed -// file with line numbers starting at 1. -type PosBase struct { - pos Pos - filename string - line uint -} - -// NewFileBase returns a new *PosBase for a file with the given filename. -func NewFileBase(filename string) *PosBase { - if filename != "" { - base := &PosBase{filename: filename} - base.pos = MakePos(base, 0, 0) - return base - } - return nil -} - -// NewLinePragmaBase returns a new *PosBase for a line pragma of the form -// //line filename:line -// at position pos. -func NewLinePragmaBase(pos Pos, filename string, line uint) *PosBase { - return &PosBase{pos, filename, line - 1} -} - -var noPos Pos - -// Pos returns the position at which base is located. -// If b == nil, the result is the zero position. -func (b *PosBase) Pos() *Pos { - if b != nil { - return &b.pos - } - return &noPos -} - -// Filename returns the filename recorded with the base. -// If b == nil, the result is the empty string. -func (b *PosBase) Filename() string { - if b != nil { - return b.filename - } - return "" -} - -// Line returns the line number recorded with the base. -// If b == nil, the result is 0. -func (b *PosBase) Line() uint { - if b != nil { - return b.line - } - return 0 -} - -// ---------------------------------------------------------------------------- -// lico - -// A lico is a compact encoding of a LIne and COlumn number. -type lico uint32 - -// Layout constants: 24 bits for line, 8 bits for column. -// (If this is too tight, we can either make lico 64b wide, -// or we can introduce a tiered encoding where we remove column -// information as line numbers grow bigger; similar to what gcc -// does.) -const ( - lineBits, lineMax = 24, 1<<lineBits - 1 - colBits, colMax = 32 - lineBits, 1<<colBits - 1 -) - -func makeLico(line, col uint) lico { - if line > lineMax { - // cannot represent line, use max. line so we have some information - line = lineMax - } - if col > colMax { - // cannot represent column, use max. column so we have some information - col = colMax - } - return lico(line<<colBits | col) -} - -func (x lico) Line() uint { return uint(x) >> colBits } -func (x lico) Col() uint { return uint(x) & colMax } diff --git a/src/cmd/compile/internal/syntax/pos_test.go b/src/cmd/compile/internal/syntax/pos_test.go deleted file mode 100644 index bf2a0c1dfa..0000000000 --- a/src/cmd/compile/internal/syntax/pos_test.go +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2016 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 syntax - -import ( - "fmt" - "testing" -) - -func TestPos(t *testing.T) { - f0 := NewFileBase("") - f1 := NewFileBase("f1") - f2 := NewLinePragmaBase(Pos{}, "f2", 10) - f3 := NewLinePragmaBase(MakePos(f1, 10, 1), "f3", 100) - f4 := NewLinePragmaBase(MakePos(f3, 10, 1), "f4", 100) - - for _, test := range []struct { - pos Pos - string string - - // absolute info - filename string - line, col uint - - // relative info - relFilename string - relLine uint - }{ - {Pos{}, ":0:0", "", 0, 0, "", 0}, - {MakePos(nil, 2, 3), ":2:3", "", 2, 3, "", 2}, - {MakePos(f0, 2, 3), ":2:3", "", 2, 3, "", 2}, - {MakePos(f1, 1, 1), "f1:1:1", "f1", 1, 1, "f1", 1}, - {MakePos(f2, 7, 10), "f2:16:10[:0:0]", "", 7, 10, "f2", 16}, - {MakePos(f3, 12, 7), "f3:101:7[f1:10:1]", "f1", 12, 7, "f3", 101}, - {MakePos(f4, 25, 1), "f4:114:1[f3:99:1[f1:10:1]]", "f3", 25, 1, "f4", 114}, // doesn't occur in Go code - } { - pos := test.pos - if got := pos.String(); got != test.string { - t.Errorf("%s: got %q", test.string, got) - } - - // absolute info - if got := pos.Filename(); got != test.filename { - t.Errorf("%s: got filename %q; want %q", test.string, got, test.filename) - } - if got := pos.Line(); got != test.line { - t.Errorf("%s: got line %d; want %d", test.string, got, test.line) - } - if got := pos.Col(); got != test.col { - t.Errorf("%s: got col %d; want %d", test.string, got, test.col) - } - - // relative info - if got := pos.RelFilename(); got != test.relFilename { - t.Errorf("%s: got relFilename %q; want %q", test.string, got, test.relFilename) - } - if got := pos.RelLine(); got != test.relLine { - t.Errorf("%s: got relLine %d; want %d", test.string, got, test.relLine) - } - } -} - -func TestPredicates(t *testing.T) { - b1 := NewFileBase("b1") - b2 := NewFileBase("b2") - for _, test := range []struct { - p, q Pos - known, before, after bool - }{ - {NoPos, NoPos, false, false, false}, - {NoPos, MakePos(nil, 1, 0), false, true, false}, - {MakePos(b1, 0, 0), NoPos, true, false, true}, - {MakePos(nil, 1, 0), NoPos, true, false, true}, - - {MakePos(nil, 1, 1), MakePos(nil, 1, 1), true, false, false}, - {MakePos(nil, 1, 1), MakePos(nil, 1, 2), true, true, false}, - {MakePos(nil, 1, 2), MakePos(nil, 1, 1), true, false, true}, - {MakePos(nil, 123, 1), MakePos(nil, 1, 123), true, false, true}, - - {MakePos(b1, 1, 1), MakePos(b1, 1, 1), true, false, false}, - {MakePos(b1, 1, 1), MakePos(b1, 1, 2), true, true, false}, - {MakePos(b1, 1, 2), MakePos(b1, 1, 1), true, false, true}, - {MakePos(b1, 123, 1), MakePos(b1, 1, 123), true, false, true}, - - {MakePos(b1, 1, 1), MakePos(b2, 1, 1), true, true, false}, - {MakePos(b1, 1, 1), MakePos(b2, 1, 2), true, true, false}, - {MakePos(b1, 1, 2), MakePos(b2, 1, 1), true, true, false}, - {MakePos(b1, 123, 1), MakePos(b2, 1, 123), true, true, false}, - - // special case: unknown column (column too large to represent) - {MakePos(nil, 1, colMax+10), MakePos(nil, 1, colMax+20), true, false, false}, - } { - if got := test.p.IsKnown(); got != test.known { - t.Errorf("%s known: got %v; want %v", test.p, got, test.known) - } - if got := test.p.Before(test.q); got != test.before { - t.Errorf("%s < %s: got %v; want %v", test.p, test.q, got, test.before) - } - if got := test.p.After(test.q); got != test.after { - t.Errorf("%s > %s: got %v; want %v", test.p, test.q, got, test.after) - } - } -} - -func TestLico(t *testing.T) { - for _, test := range []struct { - x lico - string string - line, col uint - }{ - {0, ":0:0", 0, 0}, - {makeLico(0, 0), ":0:0", 0, 0}, - {makeLico(0, 1), ":0:1", 0, 1}, - {makeLico(1, 0), ":1:0", 1, 0}, - {makeLico(1, 1), ":1:1", 1, 1}, - {makeLico(2, 3), ":2:3", 2, 3}, - {makeLico(lineMax, 1), fmt.Sprintf(":%d:1", lineMax), lineMax, 1}, - {makeLico(lineMax+1, 1), fmt.Sprintf(":%d:1", lineMax), lineMax, 1}, // line too large, stick with max. line - {makeLico(1, colMax), ":1", 1, colMax}, - {makeLico(1, colMax+1), ":1", 1, 0}, // column too large - {makeLico(lineMax+1, colMax+1), fmt.Sprintf(":%d", lineMax), lineMax, 0}, - } { - x := test.x - if got := posString("", x.Line(), x.Col()); got != test.string { - t.Errorf("%s: got %q", test.string, got) - } - } -} diff --git a/src/cmd/compile/internal/syntax/printer_test.go b/src/cmd/compile/internal/syntax/printer_test.go index 5c0fc776a1..dc9a32b6d3 100644 --- a/src/cmd/compile/internal/syntax/printer_test.go +++ b/src/cmd/compile/internal/syntax/printer_test.go @@ -15,7 +15,7 @@ func TestPrint(t *testing.T) { t.Skip("skipping test in short mode") } - ast, err := ParseFile(*src, nil, nil, 0) + ast, err := ParseFile(*src_, nil, nil, 0) if err != nil { t.Fatal(err) } diff --git a/src/cmd/compile/internal/syntax/syntax.go b/src/cmd/compile/internal/syntax/syntax.go index 4585defb8f..db2bcb4a0c 100644 --- a/src/cmd/compile/internal/syntax/syntax.go +++ b/src/cmd/compile/internal/syntax/syntax.go @@ -5,6 +5,7 @@ package syntax import ( + "cmd/internal/src" "fmt" "io" "os" @@ -15,7 +16,7 @@ type Mode uint // Error describes a syntax error. Error implements the error interface. type Error struct { - Pos Pos + Pos src.Pos Msg string } @@ -36,11 +37,11 @@ type Pragma uint16 // A PragmaHandler is used to process //line and //go: directives as // they're scanned. The returned Pragma value will be unioned into the // next FuncDecl node. -type PragmaHandler func(pos Pos, text string) Pragma +type PragmaHandler func(pos src.Pos, text string) Pragma // Parse parses a single Go source file from src and returns the corresponding // syntax tree. If there are errors, Parse will return the first error found. -// The filename is only used for position information. +// The base argument is only used for position information. // // If errh != nil, it is called with each error encountered, and Parse will // process as much source as possible. If errh is nil, Parse will terminate @@ -49,7 +50,7 @@ type PragmaHandler func(pos Pos, text string) Pragma // If a PragmaHandler is provided, it is called with each pragma encountered. // // The Mode argument is currently ignored. -func Parse(filename string, src io.Reader, errh ErrorHandler, pragh PragmaHandler, mode Mode) (_ *File, first error) { +func Parse(base *src.PosBase, src io.Reader, errh ErrorHandler, pragh PragmaHandler, mode Mode) (_ *File, first error) { defer func() { if p := recover(); p != nil { if err, ok := p.(Error); ok { @@ -61,14 +62,14 @@ func Parse(filename string, src io.Reader, errh ErrorHandler, pragh PragmaHandle }() var p parser - p.init(filename, src, errh, pragh) + p.init(base, src, errh, pragh) p.next() return p.file(), p.first } // ParseBytes behaves like Parse but it reads the source from the []byte slice provided. -func ParseBytes(filename string, src []byte, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) { - return Parse(filename, &bytesReader{src}, errh, pragh, mode) +func ParseBytes(base *src.PosBase, src []byte, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) { + return Parse(base, &bytesReader{src}, errh, pragh, mode) } type bytesReader struct { @@ -86,13 +87,13 @@ func (r *bytesReader) Read(p []byte) (int, error) { // ParseFile behaves like Parse but it reads the source from the named file. func ParseFile(filename string, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) { - src, err := os.Open(filename) + f, err := os.Open(filename) if err != nil { if errh != nil { errh(err) } return nil, err } - defer src.Close() - return Parse(filename, src, errh, pragh, mode) + defer f.Close() + return Parse(src.NewFileBase(filename, filename), f, errh, pragh, mode) } |
