summaryrefslogtreecommitdiff
path: root/src/pkg/go/token
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/go/token')
-rw-r--r--src/pkg/go/token/position.go273
-rw-r--r--src/pkg/go/token/position_test.go9
-rw-r--r--src/pkg/go/token/serialize.go20
-rw-r--r--src/pkg/go/token/serialize_test.go3
-rw-r--r--src/pkg/go/token/token.go12
5 files changed, 165 insertions, 152 deletions
diff --git a/src/pkg/go/token/position.go b/src/pkg/go/token/position.go
index 9155b501d..647d1b770 100644
--- a/src/pkg/go/token/position.go
+++ b/src/pkg/go/token/position.go
@@ -12,6 +12,9 @@ import (
"sync"
)
+// -----------------------------------------------------------------------------
+// Positions
+
// Position describes an arbitrary source position
// including the file, line, and column location.
// A Position is valid if the line number is > 0.
@@ -81,86 +84,8 @@ func (p Pos) IsValid() bool {
return p != NoPos
}
-func searchFiles(a []*File, x int) int {
- return sort.Search(len(a), func(i int) bool { return a[i].base > x }) - 1
-}
-
-func (s *FileSet) file(p Pos) *File {
- if f := s.last; f != nil && f.base <= int(p) && int(p) <= f.base+f.size {
- return f
- }
- if i := searchFiles(s.files, int(p)); i >= 0 {
- f := s.files[i]
- // f.base <= int(p) by definition of searchFiles
- if int(p) <= f.base+f.size {
- s.last = f
- return f
- }
- }
- return nil
-}
-
-// File returns the file which contains the position p.
-// If no such file is found (for instance for p == NoPos),
-// the result is nil.
-//
-func (s *FileSet) File(p Pos) (f *File) {
- if p != NoPos {
- s.mutex.RLock()
- f = s.file(p)
- s.mutex.RUnlock()
- }
- return
-}
-
-func (f *File) position(p Pos) (pos Position) {
- offset := int(p) - f.base
- pos.Offset = offset
- pos.Filename, pos.Line, pos.Column = f.info(offset)
- return
-}
-
-// Position converts a Pos in the fileset into a general Position.
-func (s *FileSet) Position(p Pos) (pos Position) {
- if p != NoPos {
- // TODO(gri) consider optimizing the case where p
- // is in the last file added, or perhaps
- // looked at - will eliminate one level
- // of search
- s.mutex.RLock()
- if f := s.file(p); f != nil {
- pos = f.position(p)
- }
- s.mutex.RUnlock()
- }
- return
-}
-
-// A lineInfo object describes alternative file and line number
-// information (such as provided via a //line comment in a .go
-// file) for a given file offset.
-type lineInfo struct {
- // fields are exported to make them accessible to gob
- Offset int
- Filename string
- Line int
-}
-
-// AddLineInfo adds alternative file and line number information for
-// a given file offset. The offset must be larger than the offset for
-// the previously added alternative line info and smaller than the
-// file size; otherwise the information is ignored.
-//
-// AddLineInfo is typically used to register alternative position
-// information for //line filename:line comments in source files.
-//
-func (f *File) AddLineInfo(offset int, filename string, line int) {
- f.set.mutex.Lock()
- if i := len(f.infos); i == 0 || f.infos[i-1].Offset < offset && offset < f.size {
- f.infos = append(f.infos, lineInfo{offset, filename, line})
- }
- f.set.mutex.Unlock()
-}
+// -----------------------------------------------------------------------------
+// File
// A File is a handle for a file belonging to a FileSet.
// A File has a name, size, and line offset table.
@@ -255,6 +180,32 @@ func (f *File) SetLinesForContent(content []byte) {
f.set.mutex.Unlock()
}
+// A lineInfo object describes alternative file and line number
+// information (such as provided via a //line comment in a .go
+// file) for a given file offset.
+type lineInfo struct {
+ // fields are exported to make them accessible to gob
+ Offset int
+ Filename string
+ Line int
+}
+
+// AddLineInfo adds alternative file and line number information for
+// a given file offset. The offset must be larger than the offset for
+// the previously added alternative line info and smaller than the
+// file size; otherwise the information is ignored.
+//
+// AddLineInfo is typically used to register alternative position
+// information for //line filename:line comments in source files.
+//
+func (f *File) AddLineInfo(offset int, filename string, line int) {
+ f.set.mutex.Lock()
+ if i := len(f.infos); i == 0 || f.infos[i-1].Offset < offset && offset < f.size {
+ f.infos = append(f.infos, lineInfo{offset, filename, line})
+ }
+ f.set.mutex.Unlock()
+}
+
// Pos returns the Pos value for the given file offset;
// the offset must be <= f.Size().
// f.Pos(f.Offset(p)) == p.
@@ -285,41 +236,6 @@ func (f *File) Line(p Pos) int {
return f.Position(p).Line
}
-// Position returns the Position value for the given file position p;
-// p must be a Pos value in that file or NoPos.
-//
-func (f *File) Position(p Pos) (pos Position) {
- if p != NoPos {
- if int(p) < f.base || int(p) > f.base+f.size {
- panic("illegal Pos value")
- }
- pos = f.position(p)
- }
- return
-}
-
-func searchInts(a []int, x int) int {
- // This function body is a manually inlined version of:
- //
- // return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1
- //
- // With better compiler optimizations, this may not be needed in the
- // future, but at the moment this change improves the go/printer
- // benchmark performance by ~30%. This has a direct impact on the
- // speed of gofmt and thus seems worthwhile (2011-04-29).
- i, j := 0, len(a)
- for i < j {
- h := i + (j-i)/2 // avoid overflow when computing h
- // i ≤ h < j
- if a[h] <= x {
- i = h + 1
- } else {
- j = h
- }
- }
- return i - 1
-}
-
func searchLineInfos(a []lineInfo, x int) int {
return sort.Search(len(a), func(i int) bool { return a[i].Offset > x }) - 1
}
@@ -343,6 +259,29 @@ func (f *File) info(offset int) (filename string, line, column int) {
return
}
+func (f *File) position(p Pos) (pos Position) {
+ offset := int(p) - f.base
+ pos.Offset = offset
+ pos.Filename, pos.Line, pos.Column = f.info(offset)
+ return
+}
+
+// Position returns the Position value for the given file position p;
+// p must be a Pos value in that file or NoPos.
+//
+func (f *File) Position(p Pos) (pos Position) {
+ if p != NoPos {
+ if int(p) < f.base || int(p) > f.base+f.size {
+ panic("illegal Pos value")
+ }
+ pos = f.position(p)
+ }
+ return
+}
+
+// -----------------------------------------------------------------------------
+// FileSet
+
// A FileSet represents a set of source files.
// Methods of file sets are synchronized; multiple goroutines
// may invoke them concurrently.
@@ -406,23 +345,91 @@ func (s *FileSet) AddFile(filename string, base, size int) *File {
return f
}
-// Files returns the files added to the file set.
-func (s *FileSet) Files() <-chan *File {
- ch := make(chan *File)
- go func() {
- for i := 0; ; i++ {
- var f *File
- s.mutex.RLock()
- if i < len(s.files) {
- f = s.files[i]
- }
- s.mutex.RUnlock()
- if f == nil {
- break
- }
- ch <- f
+// Iterate calls f for the files in the file set in the order they were added
+// until f returns false.
+//
+func (s *FileSet) Iterate(f func(*File) bool) {
+ for i := 0; ; i++ {
+ var file *File
+ s.mutex.RLock()
+ if i < len(s.files) {
+ file = s.files[i]
+ }
+ s.mutex.RUnlock()
+ if file == nil || !f(file) {
+ break
}
- close(ch)
- }()
- return ch
+ }
+}
+
+func searchFiles(a []*File, x int) int {
+ return sort.Search(len(a), func(i int) bool { return a[i].base > x }) - 1
+}
+
+func (s *FileSet) file(p Pos) *File {
+ // common case: p is in last file
+ if f := s.last; f != nil && f.base <= int(p) && int(p) <= f.base+f.size {
+ return f
+ }
+ // p is not in last file - search all files
+ if i := searchFiles(s.files, int(p)); i >= 0 {
+ f := s.files[i]
+ // f.base <= int(p) by definition of searchFiles
+ if int(p) <= f.base+f.size {
+ s.last = f
+ return f
+ }
+ }
+ return nil
+}
+
+// File returns the file that contains the position p.
+// If no such file is found (for instance for p == NoPos),
+// the result is nil.
+//
+func (s *FileSet) File(p Pos) (f *File) {
+ if p != NoPos {
+ s.mutex.RLock()
+ f = s.file(p)
+ s.mutex.RUnlock()
+ }
+ return
+}
+
+// Position converts a Pos in the fileset into a general Position.
+func (s *FileSet) Position(p Pos) (pos Position) {
+ if p != NoPos {
+ s.mutex.RLock()
+ if f := s.file(p); f != nil {
+ pos = f.position(p)
+ }
+ s.mutex.RUnlock()
+ }
+ return
+}
+
+// -----------------------------------------------------------------------------
+// Helper functions
+
+func searchInts(a []int, x int) int {
+ // This function body is a manually inlined version of:
+ //
+ // return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1
+ //
+ // With better compiler optimizations, this may not be needed in the
+ // future, but at the moment this change improves the go/printer
+ // benchmark performance by ~30%. This has a direct impact on the
+ // speed of gofmt and thus seems worthwhile (2011-04-29).
+ // TODO(gri): Remove this when compilers have caught up.
+ i, j := 0, len(a)
+ for i < j {
+ h := i + (j-i)/2 // avoid overflow when computing h
+ // i ≤ h < j
+ if a[h] <= x {
+ i = h + 1
+ } else {
+ j = h
+ }
+ }
+ return i - 1
}
diff --git a/src/pkg/go/token/position_test.go b/src/pkg/go/token/position_test.go
index 30bec5991..160107df4 100644
--- a/src/pkg/go/token/position_test.go
+++ b/src/pkg/go/token/position_test.go
@@ -167,12 +167,13 @@ func TestFiles(t *testing.T) {
for i, test := range tests {
fset.AddFile(test.filename, fset.Base(), test.size)
j := 0
- for g := range fset.Files() {
- if g.Name() != tests[j].filename {
- t.Errorf("expected filename = %s; got %s", tests[j].filename, g.Name())
+ fset.Iterate(func(f *File) bool {
+ if f.Name() != tests[j].filename {
+ t.Errorf("expected filename = %s; got %s", tests[j].filename, f.Name())
}
j++
- }
+ return true
+ })
if j != i+1 {
t.Errorf("expected %d files; got %d", i+1, j)
}
diff --git a/src/pkg/go/token/serialize.go b/src/pkg/go/token/serialize.go
index 80a3323f9..042d6abdf 100644
--- a/src/pkg/go/token/serialize.go
+++ b/src/pkg/go/token/serialize.go
@@ -5,9 +5,8 @@
package token
import (
- "gob"
+ "encoding/gob"
"io"
- "os"
)
type serializedFile struct {
@@ -24,10 +23,19 @@ type serializedFileSet struct {
Files []serializedFile
}
+func (s *serializedFileSet) Read(r io.Reader) error {
+ return gob.NewDecoder(r).Decode(s)
+}
+
+func (s *serializedFileSet) Write(w io.Writer) error {
+ return gob.NewEncoder(w).Encode(s)
+}
+
// Read reads the fileset from r into s; s must not be nil.
-func (s *FileSet) Read(r io.Reader) os.Error {
+// If r does not also implement io.ByteReader, it will be wrapped in a bufio.Reader.
+func (s *FileSet) Read(r io.Reader) error {
var ss serializedFileSet
- if err := gob.NewDecoder(r).Decode(&ss); err != nil {
+ if err := ss.Read(r); err != nil {
return err
}
@@ -46,7 +54,7 @@ func (s *FileSet) Read(r io.Reader) os.Error {
}
// Write writes the fileset s to w.
-func (s *FileSet) Write(w io.Writer) os.Error {
+func (s *FileSet) Write(w io.Writer) error {
var ss serializedFileSet
s.mutex.Lock()
@@ -58,5 +66,5 @@ func (s *FileSet) Write(w io.Writer) os.Error {
ss.Files = files
s.mutex.Unlock()
- return gob.NewEncoder(w).Encode(ss)
+ return ss.Write(w)
}
diff --git a/src/pkg/go/token/serialize_test.go b/src/pkg/go/token/serialize_test.go
index 24e419abf..a8ce30ab2 100644
--- a/src/pkg/go/token/serialize_test.go
+++ b/src/pkg/go/token/serialize_test.go
@@ -7,13 +7,12 @@ package token
import (
"bytes"
"fmt"
- "os"
"testing"
)
// equal returns nil if p and q describe the same file set;
// otherwise it returns an error describing the discrepancy.
-func equal(p, q *FileSet) os.Error {
+func equal(p, q *FileSet) error {
if p == q {
// avoid deadlock if p == q
return nil
diff --git a/src/pkg/go/token/token.go b/src/pkg/go/token/token.go
index 557374052..84b6314d5 100644
--- a/src/pkg/go/token/token.go
+++ b/src/pkg/go/token/token.go
@@ -283,10 +283,8 @@ func init() {
// Lookup maps an identifier to its keyword token or IDENT (if not a keyword).
//
-func Lookup(ident []byte) Token {
- // TODO Maps with []byte key are illegal because []byte does not
- // support == . Should find a more efficient solution eventually.
- if tok, is_keyword := keywords[string(ident)]; is_keyword {
+func Lookup(ident string) Token {
+ if tok, is_keyword := keywords[ident]; is_keyword {
return tok
}
return IDENT
@@ -295,16 +293,16 @@ func Lookup(ident []byte) Token {
// Predicates
// IsLiteral returns true for tokens corresponding to identifiers
-// and basic type literals; returns false otherwise.
+// and basic type literals; it returns false otherwise.
//
func (tok Token) IsLiteral() bool { return literal_beg < tok && tok < literal_end }
// IsOperator returns true for tokens corresponding to operators and
-// delimiters; returns false otherwise.
+// delimiters; it returns false otherwise.
//
func (tok Token) IsOperator() bool { return operator_beg < tok && tok < operator_end }
// IsKeyword returns true for tokens corresponding to keywords;
-// returns false otherwise.
+// it returns false otherwise.
//
func (tok Token) IsKeyword() bool { return keyword_beg < tok && tok < keyword_end }