source: code/trunk/vendor/modernc.org/token/position.go@ 822

Last change on this file since 822 was 822, checked in by yakumo.izuru, 22 months ago

Prefer immortal.run over runit and rc.d, use vendored modules
for convenience.

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

File size: 10.2 KB
Line 
1// Copyright 2010 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package token is variant of the stdlib package token with types FileSet and
6// Token removed.
7package token // import "modernc.org/token"
8
9import (
10 "fmt"
11 "sort"
12)
13
14// -----------------------------------------------------------------------------
15// Positions
16
17// Position describes an arbitrary source position
18// including the file, line, and column location.
19// A Position is valid if the line number is > 0.
20//
21type Position struct {
22 Filename string // filename, if any
23 Offset int // offset, starting at 0
24 Line int // line number, starting at 1
25 Column int // column number, starting at 1 (byte count)
26}
27
28// IsValid reports whether the position is valid.
29func (pos *Position) IsValid() bool { return pos.Line > 0 }
30
31// String returns a string in one of several forms:
32//
33// file:line:column valid position with file name
34// line:column valid position without file name
35// file invalid position with file name
36// - invalid position without file name
37//
38func (pos Position) String() string {
39 s := pos.Filename
40 if pos.IsValid() {
41 if s != "" {
42 s += ":"
43 }
44 s += fmt.Sprintf("%d:%d", pos.Line, pos.Column)
45 }
46 if s == "" {
47 s = "-"
48 }
49 return s
50}
51
52// Pos is a compact encoding of a source position within a file set.
53// It can be converted into a Position for a more convenient, but much
54// larger, representation.
55//
56// The Pos value for a given file is a number in the range [base, base+size],
57// where base and size are specified when adding the file to the file set via
58// AddFile.
59//
60// To create the Pos value for a specific source offset (measured in bytes),
61// first add the respective file to the current file set using FileSet.AddFile
62// and then call File.Pos(offset) for that file. Given a Pos value p
63// for a specific file set fset, the corresponding Position value is
64// obtained by calling fset.Position(p).
65//
66// Pos values can be compared directly with the usual comparison operators:
67// If two Pos values p and q are in the same file, comparing p and q is
68// equivalent to comparing the respective source file offsets. If p and q
69// are in different files, p < q is true if the file implied by p was added
70// to the respective file set before the file implied by q.
71//
72type Pos int
73
74// NoPos is the zero value of Pos; there is no file and line information
75// associated with it, and NoPos.IsValid() is false. NoPos is always
76// smaller than any other Pos value. The corresponding Position value
77// for NoPos is the zero value for Position.
78//
79const NoPos Pos = 0
80
81// IsValid reports whether the position is valid.
82func (p Pos) IsValid() bool {
83 return p != NoPos
84}
85
86// -----------------------------------------------------------------------------
87// File
88
89// A File is a handle for a file. A File has a name, size, and line offset
90// table.
91//
92type File struct {
93 name string // file name as provided to AddFile
94 base int // Pos value range for this file is [base...base+size]
95 size int // file size as provided to AddFile
96
97 lines []int // lines contains the offset of the first character for each line (the first entry is always 0)
98 infos []lineInfo
99}
100
101// NewFile returns a new file with a given filename and file size.
102//
103func NewFile(filename string, size int) *File {
104 return &File{name: filename, base: 1, size: size, lines: []int{0}}
105}
106
107// Name returns the file name of file f as registered with AddFile.
108func (f *File) Name() string {
109 return f.name
110}
111
112// Base returns the base offset of file f as registered with AddFile.
113func (f *File) Base() int {
114 return f.base
115}
116
117// Size returns the size of file f as registered with AddFile.
118func (f *File) Size() int {
119 return f.size
120}
121
122// LineCount returns the number of lines in file f.
123func (f *File) LineCount() int {
124 n := len(f.lines)
125 return n
126}
127
128// AddLine adds the line offset for a new line.
129// The line offset must be larger than the offset for the previous line
130// and smaller than the file size; otherwise the line offset is ignored.
131//
132func (f *File) AddLine(offset int) {
133 if i := len(f.lines); (i == 0 || f.lines[i-1] < offset) && offset < f.size {
134 f.lines = append(f.lines, offset)
135 }
136}
137
138// MergeLine merges a line with the following line. It is akin to replacing
139// the newline character at the end of the line with a space (to not change the
140// remaining offsets). To obtain the line number, consult e.g. Position.Line.
141// MergeLine will panic if given an invalid line number.
142//
143func (f *File) MergeLine(line int) {
144 if line <= 0 {
145 panic("illegal line number (line numbering starts at 1)")
146 }
147 if line >= len(f.lines) {
148 panic("illegal line number")
149 }
150 // To merge the line numbered <line> with the line numbered <line+1>,
151 // we need to remove the entry in lines corresponding to the line
152 // numbered <line+1>. The entry in lines corresponding to the line
153 // numbered <line+1> is located at index <line>, since indices in lines
154 // are 0-based and line numbers are 1-based.
155 copy(f.lines[line:], f.lines[line+1:])
156 f.lines = f.lines[:len(f.lines)-1]
157}
158
159// SetLines sets the line offsets for a file and reports whether it succeeded.
160// The line offsets are the offsets of the first character of each line;
161// for instance for the content "ab\nc\n" the line offsets are {0, 3}.
162// An empty file has an empty line offset table.
163// Each line offset must be larger than the offset for the previous line
164// and smaller than the file size; otherwise SetLines fails and returns
165// false.
166// Callers must not mutate the provided slice after SetLines returns.
167//
168func (f *File) SetLines(lines []int) bool {
169 // verify validity of lines table
170 size := f.size
171 for i, offset := range lines {
172 if i > 0 && offset <= lines[i-1] || size <= offset {
173 return false
174 }
175 }
176
177 // set lines table
178 f.lines = lines
179 return true
180}
181
182// SetLinesForContent sets the line offsets for the given file content.
183// It ignores position-altering //line comments.
184func (f *File) SetLinesForContent(content []byte) {
185 var lines []int
186 line := 0
187 for offset, b := range content {
188 if line >= 0 {
189 lines = append(lines, line)
190 }
191 line = -1
192 if b == '\n' {
193 line = offset + 1
194 }
195 }
196
197 // set lines table
198 f.lines = lines
199}
200
201// A lineInfo object describes alternative file and line number
202// information (such as provided via a //line comment in a .go
203// file) for a given file offset.
204type lineInfo struct {
205 // fields are exported to make them accessible to gob
206 Offset int
207 Filename string
208 Line int
209}
210
211// AddLineInfo adds alternative file and line number information for
212// a given file offset. The offset must be larger than the offset for
213// the previously added alternative line info and smaller than the
214// file size; otherwise the information is ignored.
215//
216// AddLineInfo is typically used to register alternative position
217// information for //line filename:line comments in source files.
218//
219func (f *File) AddLineInfo(offset int, filename string, line int) {
220 if i := len(f.infos); i == 0 || f.infos[i-1].Offset < offset && offset < f.size {
221 f.infos = append(f.infos, lineInfo{offset, filename, line})
222 }
223}
224
225// Pos returns the Pos value for the given file offset;
226// the offset must be <= f.Size().
227// f.Pos(f.Offset(p)) == p.
228//
229func (f *File) Pos(offset int) Pos {
230 if offset > f.size {
231 panic("illegal file offset")
232 }
233 return Pos(f.base + offset)
234}
235
236// Offset returns the offset for the given file position p;
237// p must be a valid Pos value in that file.
238// f.Offset(f.Pos(offset)) == offset.
239//
240func (f *File) Offset(p Pos) int {
241 if int(p) < f.base || int(p) > f.base+f.size {
242 panic("illegal Pos value")
243 }
244 return int(p) - f.base
245}
246
247// Line returns the line number for the given file position p;
248// p must be a Pos value in that file or NoPos.
249//
250func (f *File) Line(p Pos) int {
251 return f.Position(p).Line
252}
253
254func searchLineInfos(a []lineInfo, x int) int {
255 return sort.Search(len(a), func(i int) bool { return a[i].Offset > x }) - 1
256}
257
258// unpack returns the filename and line and column number for a file offset.
259// If adjusted is set, unpack will return the filename and line information
260// possibly adjusted by //line comments; otherwise those comments are ignored.
261//
262func (f *File) unpack(offset int, adjusted bool) (filename string, line, column int) {
263 filename = f.name
264 if i := searchInts(f.lines, offset); i >= 0 {
265 line, column = i+1, offset-f.lines[i]+1
266 }
267 if adjusted && len(f.infos) > 0 {
268 // almost no files have extra line infos
269 if i := searchLineInfos(f.infos, offset); i >= 0 {
270 alt := &f.infos[i]
271 filename = alt.Filename
272 if i := searchInts(f.lines, alt.Offset); i >= 0 {
273 line += alt.Line - i - 1
274 }
275 }
276 }
277 return
278}
279
280func (f *File) position(p Pos, adjusted bool) (pos Position) {
281 offset := int(p) - f.base
282 pos.Offset = offset
283 pos.Filename, pos.Line, pos.Column = f.unpack(offset, adjusted)
284 return
285}
286
287// PositionFor returns the Position value for the given file position p.
288// If adjusted is set, the position may be adjusted by position-altering
289// //line comments; otherwise those comments are ignored.
290// p must be a Pos value in f or NoPos.
291//
292func (f *File) PositionFor(p Pos, adjusted bool) (pos Position) {
293 if p != NoPos {
294 if int(p) < f.base || int(p) > f.base+f.size {
295 panic("illegal Pos value")
296 }
297 pos = f.position(p, adjusted)
298 }
299 return
300}
301
302// Position returns the Position value for the given file position p.
303// Calling f.Position(p) is equivalent to calling f.PositionFor(p, true).
304//
305func (f *File) Position(p Pos) (pos Position) {
306 return f.PositionFor(p, true)
307}
308
309// -----------------------------------------------------------------------------
310// Helper functions
311
312func searchInts(a []int, x int) int {
313 // This function body is a manually inlined version of:
314 //
315 // return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1
316 //
317 // With better compiler optimizations, this may not be needed in the
318 // future, but at the moment this change improves the go/printer
319 // benchmark performance by ~30%. This has a direct impact on the
320 // speed of gofmt and thus seems worthwhile (2011-04-29).
321 // TODO(gri): Remove this when compilers have caught up.
322 i, j := 0, len(a)
323 for i < j {
324 h := i + (j-i)/2 // avoid overflow when computing h
325 // i ≤ h < j
326 if a[h] <= x {
327 i = h + 1
328 } else {
329 j = h
330 }
331 }
332 return i - 1
333}
Note: See TracBrowser for help on using the repository browser.