From e21aac29ba179c310b4b834bc3c37f152ab20bf2 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Sat, 20 Nov 2010 21:30:36 -0800 Subject: [PATCH] position.go: more flexible AddFile method This will make it easier to use Pos values together with suffix arrays by slightly de- coupling the mapping of Pos values to global offsets. R=rsc CC=golang-dev https://golang.org/cl/3231041 --- src/pkg/go/token/position.go | 67 ++++++++++++++++++++++++------- src/pkg/go/token/position_test.go | 5 ++- 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/src/pkg/go/token/position.go b/src/pkg/go/token/position.go index 716a71beae..657b77bb5f 100644 --- a/src/pkg/go/token/position.go +++ b/src/pkg/go/token/position.go @@ -63,12 +63,15 @@ func (pos Position) String() string { // It can be converted into a Position for a more convenient, but much // larger, representation. // -// To create the Pos value for a specific source location, first add +// The Pos value for a given file is a number in the range [base, base+size], +// where base and size are specified when adding the file to the file set via +// AddFile. +// +// To create the Pos value for a specific source offset, first add // the respective file to the current file set (via FileSet.AddFile) -// and then call File.Pos(offset) of that file with the offset of -// the source location. Given a Pos value p for a specific file set -// fset, the corresponding Position value is obtained by calling -// fset.Position(p). +// and then call File.Pos(offset) for that file. Given a Pos value p +// for a specific file set fset, the corresponding Position value is +// obtained by calling fset.Position(p). // // Pos values can be compared directly with the usual comparison operators: // If two Pos values p and q are in the same file, comparing p and q is @@ -100,7 +103,11 @@ func searchFiles(a []*File, x int) int { func (s *FileSet) file(p Pos) *File { if i := searchFiles(s.files, int(p)); i >= 0 { - return s.files[i] + f := s.files[i] + // f.base <= int(p) by definition of searchFiles + if int(p) <= f.base+f.size { + return f + } } return nil } @@ -166,6 +173,12 @@ func (f *File) Name() string { } +// Base returns the base offset of file f as registered with AddFile. +func (f *File) Base() int { + return f.base +} + + // Size returns the size of file f as registered with AddFile. func (f *File) Size() int { return f.size @@ -224,7 +237,7 @@ func (f *File) Pos(offset int) Pos { if offset > f.size { panic("illegal file offset") } - return Pos(offset + f.base) + return Pos(f.base + offset) } @@ -296,19 +309,43 @@ func NewFileSet() *FileSet { } -// AddFile adds a new file with a given filename and file size to a the -// file set s and returns the file. Multiple files may have the same name. -// File.Pos may be used to create file-specific position values from a -// file offset. +// Base returns the minimum base offset that must be provided to +// AddFile when adding the next file. // -func (s *FileSet) AddFile(filename string, size int) *File { +func (s *FileSet) Base() int { + return s.base +} + + +// AddFile adds a new file with a given filename, base offset, and file size +// to the file set s and returns the file. Multiple files may have the same +// name. The base offset must not be smaller than the FileSet's Base(), and +// size must not be negative. +// +// Adding the file will set the file set's Base() value to base + size + 1 +// as the minimum base value for the next file. The following relationship +// exists between a Pos value p for a given file offset offs: +// +// int(p) = base + offs +// +// with offs in the range [0, size] and thus p in the range [base, base+size]. +// For convenience, File.Pos may be used to create file-specific position +// values from a file offset. +// +func (s *FileSet) AddFile(filename string, base, size int) *File { s.mutex.Lock() defer s.mutex.Unlock() - f := &File{s, filename, s.base, size, []int{0}, nil} - s.base += size + 1 // +1 because EOF also has a position - if s.base < 0 { + if base < s.base || size < 0 { + panic("illegal base or size") + } + // base >= s.base && size >= 0 + f := &File{s, filename, base, size, []int{0}, nil} + base += size + 1 // +1 because EOF also has a position + if base < 0 { panic("token.Pos offset overflow (> 2G of source code in file set)") } + // add the file to the file set + s.base = base s.index[f] = len(s.files) s.files = append(s.files, f) return f diff --git a/src/pkg/go/token/position_test.go b/src/pkg/go/token/position_test.go index 286819e267..bf4e67c136 100644 --- a/src/pkg/go/token/position_test.go +++ b/src/pkg/go/token/position_test.go @@ -78,10 +78,11 @@ func verifyPositions(t *testing.T, fset *FileSet, f *File, lines []int) { func TestPositions(t *testing.T) { + const delta = 7 // a non-zero base offset increment fset := NewFileSet() for _, test := range tests { // add file and verify name and size - f := fset.AddFile(test.filename, test.size) + f := fset.AddFile(test.filename, fset.Base()+delta, test.size) if f.Name() != test.filename { t.Errorf("expected filename %q; got %q", test.filename, f.Name()) } @@ -118,7 +119,7 @@ func TestPositions(t *testing.T) { func TestLineInfo(t *testing.T) { fset := NewFileSet() - f := fset.AddFile("foo", 500) + f := fset.AddFile("foo", fset.Base(), 500) lines := []int{0, 42, 77, 100, 210, 220, 277, 300, 333, 401} // add lines individually and provide alternative line information for _, offs := range lines { -- 2.48.1