goscript_parser/
position.rs

1use 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, // offset in utf8 char
12    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        /*
90        let mut shalf = self.lines.split_off(line);
91        self.lines.pop().unwrap();
92        self.lines.append(&mut shalf);
93        */
94        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; // +1 because EOF also has a position
243        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}