go_parser/
position.rs

1// Copyright 2022 The Goscript 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//
6// This code is adapted from the offical Go code written in Go
7// with license as follows:
8// Copyright 2013 The Go Authors. All rights reserved.
9// Use of this source code is governed by a BSD-style
10// license that can be found in the LICENSE file.
11
12#[cfg(feature = "serde_borsh")]
13use borsh::{
14    maybestd::io::Result as BorshResult, maybestd::io::Write as BorshWrite, BorshDeserialize,
15    BorshSerialize,
16};
17use std::borrow::Borrow;
18use std::fmt;
19use std::fmt::Write;
20use std::rc::Rc;
21
22pub type Pos = usize;
23
24#[derive(Clone, Debug)]
25pub struct FilePos {
26    pub filename: Rc<String>,
27    pub offset: usize, // offset in utf8 char
28    pub line: usize,
29    pub column: usize,
30}
31
32impl FilePos {
33    pub fn is_valid(&self) -> bool {
34        self.line > 0
35    }
36
37    pub fn null() -> FilePos {
38        FilePos {
39            filename: Rc::new("[null_file]".to_owned()),
40            line: 0,
41            offset: 0,
42            column: 0,
43        }
44    }
45}
46
47impl fmt::Display for FilePos {
48    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49        let mut s = String::clone(&*self.filename);
50        if self.is_valid() {
51            if s != "" {
52                s.push(':');
53            }
54            s.push_str(&self.line.to_string());
55        }
56        if self.column != 0 {
57            write!(&mut s, ":{}", self.column).unwrap();
58        }
59        if s.is_empty() {
60            s.push('-');
61        }
62        f.write_str(&s)
63    }
64}
65
66#[derive(Debug)]
67pub struct File {
68    name: Rc<String>,
69    base: usize,
70    size: usize,
71    lines: Vec<usize>,
72}
73
74impl File {
75    pub fn new(name: String) -> File {
76        File {
77            name: Rc::new(name),
78            base: 0,
79            size: 0,
80            lines: vec![0],
81        }
82    }
83
84    pub fn name(&self) -> &str {
85        &self.name
86    }
87
88    pub fn base(&self) -> usize {
89        self.base
90    }
91
92    pub fn size(&self) -> usize {
93        self.size
94    }
95
96    pub fn line_count(&self) -> usize {
97        self.lines.len()
98    }
99
100    pub fn add_line(&mut self, offset: usize) {
101        let i = self.line_count();
102        if (i == 0 || self.lines[i - 1] < offset) && offset < self.size {
103            self.lines.push(offset);
104        }
105    }
106
107    pub fn merge_line(&mut self, line: usize) {
108        if line < 1 {
109            panic!("illegal line number (line numbering starts at 1)");
110        }
111        if line >= self.line_count() {
112            panic!("illegal line number");
113        }
114        /*
115        let mut shalf = self.lines.split_off(line);
116        self.lines.pop().unwrap();
117        self.lines.append(&mut shalf);
118        */
119        let lines = &self.lines;
120        self.lines = lines
121            .into_iter()
122            .enumerate()
123            .filter(|&(i, _)| i != line)
124            .map(|(_, l)| *l)
125            .collect();
126    }
127
128    pub fn set_lines(&mut self, lines: Vec<usize>) -> bool {
129        let size = self.size;
130        for (i, &offset) in self.lines.iter().enumerate() {
131            if (i == 0 && size <= offset) || offset < lines[i - 1] {
132                return false;
133            }
134        }
135        self.lines = lines;
136        true
137    }
138
139    pub fn set_lines_for_content(&mut self, content: &mut std::str::Chars) {
140        let (mut new_line, mut line) = (true, 0);
141        for (offset, b) in content.enumerate() {
142            if new_line {
143                self.lines.push(line);
144            }
145            new_line = false;
146            if b == '\n' {
147                new_line = true;
148                line = offset + 1;
149            }
150        }
151    }
152
153    pub fn line_start(&self, line: usize) -> usize {
154        if line < 1 {
155            panic!("illegal line number (line numbering starts at 1)");
156        }
157        if line >= self.line_count() {
158            panic!("illegal line number");
159        }
160        self.base + self.lines[line - 1]
161    }
162
163    pub fn pos(&self, offset: usize) -> Pos {
164        if offset > self.size() {
165            panic!("illegal file offset")
166        }
167        self.base() + offset
168    }
169
170    pub fn position(&self, p: Pos) -> FilePos {
171        if p < self.base || p > self.base + self.size {
172            panic!("illegal Pos value");
173        }
174
175        let line_count = self.line_count();
176        let offset = p - self.base;
177        let line = match self
178            .lines
179            .iter()
180            .enumerate()
181            .find(|&(_, &line)| line > offset)
182        {
183            Some((i, _)) => i,
184            None => line_count,
185        };
186        let column = offset - self.lines[line - 1] + 1;
187
188        FilePos {
189            filename: self.name.clone(),
190            line: line,
191            offset: offset,
192            column: column,
193        }
194    }
195}
196
197#[cfg(feature = "serde_borsh")]
198impl BorshSerialize for File {
199    #[inline]
200    fn serialize<W: BorshWrite>(&self, writer: &mut W) -> BorshResult<()> {
201        let name_str: &str = &*self.name;
202        name_str.serialize(writer)?;
203        self.base.serialize(writer)?;
204        self.size.serialize(writer)?;
205        self.lines.serialize(writer)
206    }
207}
208
209#[cfg(feature = "serde_borsh")]
210impl BorshDeserialize for File {
211    #[inline]
212    fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> BorshResult<Self> {
213        let name = String::deserialize_reader(reader)?;
214        let base = usize::deserialize_reader(reader)?;
215        let size = usize::deserialize_reader(reader)?;
216        let lines = Vec::<usize>::deserialize_reader(reader)?;
217        Ok(File {
218            name: Rc::new(name),
219            base,
220            size,
221            lines,
222        })
223    }
224}
225
226#[cfg_attr(feature = "serde_borsh", derive(BorshDeserialize, BorshSerialize))]
227#[derive(Debug)]
228pub struct FileSet {
229    base: usize,
230    files: Vec<File>,
231}
232
233impl FileSet {
234    pub fn new() -> FileSet {
235        FileSet {
236            base: 0,
237            files: vec![],
238        }
239    }
240
241    pub fn base(&self) -> usize {
242        self.base
243    }
244
245    pub fn iter(&self) -> FileSetIter {
246        FileSetIter { fs: self, cur: 0 }
247    }
248
249    pub fn file(&self, p: Pos) -> Option<&File> {
250        for f in self.files.iter() {
251            if f.base <= p && f.base + f.size >= p {
252                return Some(f.borrow());
253            }
254        }
255        None
256    }
257
258    pub fn position(&self, p: Pos) -> Option<FilePos> {
259        self.file(p).map(|f| f.position(p))
260    }
261
262    pub fn index_file(&mut self, i: usize) -> Option<&mut File> {
263        if i >= self.files.len() {
264            None
265        } else {
266            Some(&mut self.files[i])
267        }
268    }
269
270    pub fn recent_file(&mut self) -> Option<&mut File> {
271        let c = self.files.len();
272        if c == 0 {
273            None
274        } else {
275            self.index_file(c - 1)
276        }
277    }
278
279    pub fn add_file(&mut self, name: String, base: Option<usize>, size: usize) -> &mut File {
280        let real_base = if let Some(b) = base { b } else { self.base };
281        if real_base < self.base {
282            panic!("illegal base");
283        }
284
285        let mut f = File::new(name);
286        f.base = real_base;
287        f.size = size;
288        let set_base = self.base + size + 1; // +1 because EOF also has a position
289        if set_base < self.base {
290            panic!("token.Pos offset overflow (> 2G of source code in file set)");
291        }
292        self.base = set_base;
293        self.files.push(f);
294        self.recent_file().unwrap()
295    }
296}
297
298pub struct FileSetIter<'a> {
299    fs: &'a FileSet,
300    cur: usize,
301}
302
303impl<'a> Iterator for FileSetIter<'a> {
304    type Item = &'a File;
305
306    fn next(&mut self) -> Option<&'a File> {
307        if self.cur < self.fs.files.len() {
308            self.cur += 1;
309            Some(self.fs.files[self.cur - 1].borrow())
310        } else {
311            None
312        }
313    }
314}
315
316#[cfg(test)]
317mod test {
318    use super::*;
319
320    #[test]
321    fn test_position() {
322        let p = FilePos {
323            filename: Rc::new("test.gs".to_owned()),
324            offset: 0,
325            line: 54321,
326            column: 8,
327        };
328        print!("this is the position: {} ", p);
329        let mut fs = FileSet::new();
330        let mut f = File::new("test.gs".to_owned());
331        f.size = 12345;
332        f.add_line(123);
333        f.add_line(133);
334        f.add_line(143);
335        print!("\nfile: {:?}", f);
336        f.merge_line(1);
337        print!("\nfile after merge: {:?}", f);
338
339        {
340            fs.add_file("testfile1.gs".to_owned(), None, 222);
341            fs.add_file("testfile2.gs".to_owned(), None, 222);
342            fs.add_file("testfile3.gs".to_owned(), None, 222);
343            print!("\nset {:?}", fs);
344        }
345
346        for f in fs.iter() {
347            print!("\nfiles in set: {:?}", f);
348        }
349        print!("\nfile at 100: {:?}", fs.file(100))
350    }
351}