1// Package file encapsulates the file abstractions used by the ast & parser. 2// 3package file 4 5import ( 6 "fmt" 7 "strings" 8) 9 10// Idx is a compact encoding of a source position within a file set. 11// It can be converted into a Position for a more convenient, but much 12// larger, representation. 13type Idx int 14 15// Position describes an arbitrary source position 16// including the filename, line, and column location. 17type Position struct { 18 Filename string // The filename where the error occurred, if any 19 Offset int // The src offset 20 Line int // The line number, starting at 1 21 Column int // The column number, starting at 1 (The character count) 22 23} 24 25// A Position is valid if the line number is > 0. 26 27func (self *Position) isValid() bool { 28 return self.Line > 0 29} 30 31// String returns a string in one of several forms: 32// 33// file:line:column A valid position with filename 34// line:column A valid position without filename 35// file An invalid position with filename 36// - An invalid position without filename 37// 38func (self *Position) String() string { 39 str := self.Filename 40 if self.isValid() { 41 if str != "" { 42 str += ":" 43 } 44 str += fmt.Sprintf("%d:%d", self.Line, self.Column) 45 } 46 if str == "" { 47 str = "-" 48 } 49 return str 50} 51 52// FileSet 53 54// A FileSet represents a set of source files. 55type FileSet struct { 56 files []*File 57 last *File 58} 59 60// AddFile adds a new file with the given filename and src. 61// 62// This an internal method, but exported for cross-package use. 63func (self *FileSet) AddFile(filename, src string) int { 64 base := self.nextBase() 65 file := &File{ 66 name: filename, 67 src: src, 68 base: base, 69 } 70 self.files = append(self.files, file) 71 self.last = file 72 return base 73} 74 75func (self *FileSet) nextBase() int { 76 if self.last == nil { 77 return 1 78 } 79 return self.last.base + len(self.last.src) + 1 80} 81 82func (self *FileSet) File(idx Idx) *File { 83 for _, file := range self.files { 84 if idx <= Idx(file.base+len(file.src)) { 85 return file 86 } 87 } 88 return nil 89} 90 91// Position converts an Idx in the FileSet into a Position. 92func (self *FileSet) Position(idx Idx) *Position { 93 position := &Position{} 94 for _, file := range self.files { 95 if idx <= Idx(file.base+len(file.src)) { 96 offset := int(idx) - file.base 97 src := file.src[:offset] 98 position.Filename = file.name 99 position.Offset = offset 100 position.Line = 1 + strings.Count(src, "\n") 101 if index := strings.LastIndex(src, "\n"); index >= 0 { 102 position.Column = offset - index 103 } else { 104 position.Column = 1 + len(src) 105 } 106 } 107 } 108 return position 109} 110 111type File struct { 112 name string 113 src string 114 base int // This will always be 1 or greater 115} 116 117func NewFile(filename, src string, base int) *File { 118 return &File{ 119 name: filename, 120 src: src, 121 base: base, 122 } 123} 124 125func (fl *File) Name() string { 126 return fl.name 127} 128 129func (fl *File) Source() string { 130 return fl.src 131} 132 133func (fl *File) Base() int { 134 return fl.base 135} 136