goscript_parser/
position.rs1use std::borrow::Borrow;
2use std::fmt;
3use std::fmt::Write;
4use std::rc::Rc;
5
6pub type Pos = usize;
7
8#[derive(Clone, Debug)]
9pub struct Position {
10 pub filename: Rc<String>,
11 pub offset: usize, pub line: usize,
13 pub column: usize,
14}
15
16impl Position {
17 pub fn is_valid(&self) -> bool {
18 self.line > 0
19 }
20}
21
22impl fmt::Display for Position {
23 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24 let mut s = String::clone(&*self.filename);
25 if self.is_valid() {
26 if s != "" {
27 s.push(':');
28 }
29 s.push_str(&self.line.to_string());
30 }
31 if self.column != 0 {
32 write!(&mut s, ":{}", self.column).unwrap();
33 }
34 if s.is_empty() {
35 s.push('-');
36 }
37 f.write_str(&s)
38 }
39}
40
41#[derive(Debug)]
42pub struct File {
43 name: Rc<String>,
44 base: usize,
45 size: usize,
46 lines: Vec<usize>,
47}
48
49impl File {
50 pub fn new(name: String) -> File {
51 File {
52 name: Rc::new(name),
53 base: 0,
54 size: 0,
55 lines: vec![0],
56 }
57 }
58
59 pub fn name(&self) -> &str {
60 &self.name
61 }
62
63 pub fn base(&self) -> usize {
64 self.base
65 }
66
67 pub fn size(&self) -> usize {
68 self.size
69 }
70
71 pub fn line_count(&self) -> usize {
72 self.lines.len()
73 }
74
75 pub fn add_line(&mut self, offset: usize) {
76 let i = self.line_count();
77 if (i == 0 || self.lines[i - 1] < offset) && offset < self.size {
78 self.lines.push(offset);
79 }
80 }
81
82 pub fn merge_line(&mut self, line: usize) {
83 if line < 1 {
84 panic!("illegal line number (line numbering starts at 1)");
85 }
86 if line >= self.line_count() {
87 panic!("illegal line number");
88 }
89 let lines = &self.lines;
95 self.lines = lines
96 .into_iter()
97 .enumerate()
98 .filter(|&(i, _)| i != line)
99 .map(|(_, l)| *l)
100 .collect();
101 }
102
103 pub fn set_lines(&mut self, lines: Vec<usize>) -> bool {
104 let size = self.size;
105 for (i, &offset) in self.lines.iter().enumerate() {
106 if (i == 0 && size <= offset) || offset < lines[i - 1] {
107 return false;
108 }
109 }
110 self.lines = lines;
111 true
112 }
113
114 pub fn set_lines_for_content(&mut self, content: &mut std::str::Chars) {
115 let (mut new_line, mut line) = (true, 0);
116 for (offset, b) in content.enumerate() {
117 if new_line {
118 self.lines.push(line);
119 }
120 new_line = false;
121 if b == '\n' {
122 new_line = true;
123 line = offset + 1;
124 }
125 }
126 }
127
128 pub fn line_start(&self, line: usize) -> usize {
129 if line < 1 {
130 panic!("illegal line number (line numbering starts at 1)");
131 }
132 if line >= self.line_count() {
133 panic!("illegal line number");
134 }
135 self.base + self.lines[line - 1]
136 }
137
138 pub fn pos(&self, offset: usize) -> Pos {
139 if offset > self.size() {
140 panic!("illegal file offset")
141 }
142 self.base() + offset
143 }
144
145 pub fn position(&self, p: Pos) -> Position {
146 if p < self.base || p > self.base + self.size {
147 panic!("illegal Pos value");
148 }
149
150 let line_count = self.line_count();
151 let offset = p - self.base;
152 let line = match self
153 .lines
154 .iter()
155 .enumerate()
156 .find(|&(_, &line)| line > offset)
157 {
158 Some((i, _)) => i,
159 None => line_count,
160 };
161 let column = offset - self.lines[line - 1] + 1;
162
163 Position {
164 filename: self.name.clone(),
165 line: line,
166 offset: offset,
167 column: column,
168 }
169 }
170}
171
172#[derive(Debug)]
173pub struct FileSet {
174 base: usize,
175 files: Vec<File>,
176}
177
178impl FileSet {
179 pub fn new() -> FileSet {
180 FileSet {
181 base: 0,
182 files: vec![],
183 }
184 }
185
186 pub fn base(&self) -> usize {
187 self.base
188 }
189
190 pub fn iter(&self) -> FileSetIter {
191 FileSetIter { fs: self, cur: 0 }
192 }
193
194 pub fn file(&self, p: Pos) -> Option<&File> {
195 for f in self.files.iter() {
196 if f.base <= p && f.base + f.size >= p {
197 return Some(f.borrow());
198 }
199 }
200 None
201 }
202
203 pub fn position(&self, p: Pos) -> Position {
204 if let Some(f) = self.file(p) {
205 f.position(p)
206 } else {
207 return Position {
208 filename: Rc::new("non_file_name".to_string()),
209 line: 0,
210 offset: 0,
211 column: 0,
212 };
213 }
214 }
215
216 pub fn index_file(&mut self, i: usize) -> Option<&mut File> {
217 if i >= self.files.len() {
218 None
219 } else {
220 Some(&mut self.files[i])
221 }
222 }
223
224 pub fn recent_file(&mut self) -> Option<&mut File> {
225 let c = self.files.len();
226 if c == 0 {
227 None
228 } else {
229 self.index_file(c - 1)
230 }
231 }
232
233 pub fn add_file(&mut self, name: String, base: Option<usize>, size: usize) -> &mut File {
234 let real_base = if let Some(b) = base { b } else { self.base };
235 if real_base < self.base {
236 panic!("illegal base");
237 }
238
239 let mut f = File::new(name);
240 f.base = real_base;
241 f.size = size;
242 let set_base = self.base + size + 1; if set_base < self.base {
244 panic!("token.Pos offset overflow (> 2G of source code in file set)");
245 }
246 self.base = set_base;
247 self.files.push(f);
248 self.recent_file().unwrap()
249 }
250}
251
252pub struct FileSetIter<'a> {
253 fs: &'a FileSet,
254 cur: usize,
255}
256
257impl<'a> Iterator for FileSetIter<'a> {
258 type Item = &'a File;
259
260 fn next(&mut self) -> Option<&'a File> {
261 if self.cur < self.fs.files.len() {
262 self.cur += 1;
263 Some(self.fs.files[self.cur - 1].borrow())
264 } else {
265 None
266 }
267 }
268}
269
270#[cfg(test)]
271mod test {
272 use super::*;
273
274 #[test]
275 fn test_position() {
276 let p = Position {
277 filename: Rc::new("test.gs".to_string()),
278 offset: 0,
279 line: 54321,
280 column: 8,
281 };
282 print!("this is the position: {} ", p);
283 let mut fs = FileSet::new();
284 let mut f = File::new("test.gs".to_string());
285 f.size = 12345;
286 f.add_line(123);
287 f.add_line(133);
288 f.add_line(143);
289 print!("\nfile: {:?}", f);
290 f.merge_line(1);
291 print!("\nfile after merge: {:?}", f);
292
293 {
294 fs.add_file("testfile1.gs".to_string(), None, 222);
295 fs.add_file("testfile2.gs".to_string(), None, 222);
296 fs.add_file("testfile3.gs".to_string(), None, 222);
297 print!("\nset {:?}", fs);
298 }
299
300 for f in fs.iter() {
301 print!("\nfiles in set: {:?}", f);
302 }
303 print!("\nfile at 100: {:?}", fs.file(100))
304 }
305}