vq / vlib / v3 / token / position.v
185 lines · 166 sloc · 3.81 KB · 6c4d26f1f98c5464b012a375667ab345e462f7e0
Raw
1module token
2
3// Pos represents pos data used by token.
4pub struct Pos {
5pub:
6 offset int
7 id int
8}
9
10// str returns the string form for Pos.
11pub fn (p Pos) str() string {
12 return '{ offset: ${p.offset}, id: ${p.id} }'
13}
14
15// is_valid reports whether is valid applies in token.
16pub fn (p Pos) is_valid() bool {
17 return p.id > 0
18}
19
20// Position represents position data used by token.
21pub struct Position {
22pub:
23 filename string
24 offset int
25 line int
26 column int
27}
28
29// str returns the string form for Position.
30pub fn (p Position) str() string {
31 return '${p.filename}:${p.line}:${p.column}'
32}
33
34// File represents file data used by token.
35pub struct File {
36pub:
37 name string
38 base int
39 size int
40mut:
41 line_offsets []int = [0]
42 id_counter &int = unsafe { nil }
43}
44
45// FileSet represents file set data used by token.
46pub struct FileSet {
47mut:
48 base int = 1
49 id_counter int
50 files []&File
51}
52
53// new creates a FileSet value for token.
54pub fn FileSet.new() &FileSet {
55 return &FileSet{}
56}
57
58// add_file updates add file state for FileSet.
59pub fn (mut fs FileSet) add_file(filename string, base_ int, size int) &File {
60 mut base := if base_ < 0 { fs.base } else { base_ }
61 if base < fs.base {
62 panic('invalid base ${base} (should be >= ${fs.base}')
63 }
64 file := &File{
65 name: filename
66 base: base
67 size: size
68 id_counter: &fs.id_counter
69 }
70 if size < 0 {
71 panic('invalid size ${size} (should be >= 0)')
72 }
73 base += size + 1
74 if base < 0 {
75 panic('token.Pos offset overflow (> 2G of source code in file set)')
76 }
77 fs.base = base
78 fs.files << file
79 return file
80}
81
82// search_files supports search files handling for token.
83fn search_files(files []&File, x int) int {
84 mut min, mut max := 0, files.len
85 for min < max {
86 mid := (min + max) / 2
87 if files[mid].base <= x {
88 min = mid + 1
89 } else {
90 max = mid
91 }
92 }
93 return min - 1
94}
95
96// file supports file handling for FileSet.
97pub fn (mut fs FileSet) file(pos Pos) &File {
98 i := search_files(fs.files, pos.offset)
99 if i >= 0 {
100 file := fs.files[i]
101 if pos.offset <= file.base + file.size {
102 return file
103 }
104 }
105 dump(fs)
106 panic('cannot find file for pos: ${pos}')
107}
108
109// add_line updates add line state for File.
110@[inline]
111pub fn (mut f File) add_line(offset int) {
112 f.line_offsets << offset
113}
114
115// line_count supports line count handling for File.
116@[inline]
117pub fn (f &File) line_count() int {
118 return f.line_offsets.len
119}
120
121// line_start supports line start handling for File.
122pub fn (f &File) line_start(line int) int {
123 idx := line - 1
124 if idx < 0 || idx >= f.line_offsets.len {
125 panic('invalid line `${line}` (must be > 0 & < ${f.line_count()})')
126 }
127 return f.line_offsets[idx]
128}
129
130// line supports line handling for File.
131pub fn (f &File) line(pos Pos) int {
132 return f.find_line(pos.offset - f.base)
133}
134
135// pos supports pos handling for File.
136pub fn (mut f File) pos(offset int) Pos {
137 if offset > f.size {
138 panic('invalid offset')
139 }
140 mut current_id := 0
141 mut next_id := 0
142 unsafe {
143 current_id = *f.id_counter
144 }
145 next_id = current_id + 1
146 unsafe {
147 *f.id_counter = next_id
148 }
149 return Pos{
150 offset: f.base + offset
151 id: next_id
152 }
153}
154
155// position supports position handling for File.
156pub fn (f &File) position(pos Pos) Position {
157 offset := pos.offset - f.base
158 line, column := f.find_line_and_column(offset)
159 return Position{
160 filename: f.name
161 offset: offset
162 line: line
163 column: column
164 }
165}
166
167// find_line_and_column resolves find line and column information for token.
168pub fn (f &File) find_line_and_column(pos int) (int, int) {
169 line := f.find_line(pos)
170 return line, pos - f.line_offsets[line - 1] + 1
171}
172
173// find_line resolves find line information for token.
174pub fn (f &File) find_line(pos int) int {
175 mut min, mut max := 0, f.line_offsets.len
176 for min < max {
177 mid := (min + max) / 2
178 if f.line_offsets[mid] <= pos {
179 min = mid + 1
180 } else {
181 max = mid
182 }
183 }
184 return min
185}
186