lexer/
cursor.rs

1use std::str::CharIndices;
2
3use span::Span;
4
5pub struct Cursor<'lex> {
6    base_offset: usize,
7    chars: CharIndices<'lex>,
8    start_chars: CharIndices<'lex>,
9
10    line: usize,
11    col: usize,
12}
13
14impl<'lex> Cursor<'lex> {
15    pub fn new(source: &'lex str, base_offset: usize) -> Self {
16        Self {
17            base_offset,
18            chars: source.char_indices(),
19            start_chars: source.char_indices(),
20            line: 0,
21            col: 0,
22        }
23    }
24    pub fn step(&mut self) { self.start_chars = self.chars.clone(); }
25    pub fn is_finished(&self) -> bool { self.chars.as_str().is_empty() }
26    pub fn current_offset(&self) -> usize { self.start_chars.offset() }
27    pub fn current_len(&self) -> usize { self.chars.offset() - self.start_chars.offset() }
28    pub fn current_lexem(&self) -> &str {
29        let n = self.chars.offset() - self.start_chars.offset();
30        &self.start_chars.as_str()[..n]
31    }
32    pub fn current_span(&self) -> Span {
33        Span {
34            offset: self.start_chars.offset() + self.base_offset,
35            len: self.chars.offset() - self.start_chars.offset(),
36        }
37    }
38    pub fn line(&self) -> usize { self.line }
39    pub fn col(&self) -> usize { self.col }
40    pub fn advance(&mut self) -> char {
41        let c = self.chars.next().map_or('\0', |(_, c)| c);
42        self.col += 1;
43        if c == '\n' {
44            self.line += 1;
45            self.col = 1;
46        }
47        c
48    }
49    pub fn advance_while<F>(&mut self, f: F) -> bool
50    where
51        F: Fn(&char) -> bool,
52    {
53        while f(&self.peek()) {
54            self.advance();
55            if self.is_finished() {
56                return false;
57            }
58        }
59        true
60    }
61    pub fn peek(&self) -> char { self.chars.clone().next().map_or('\0', |(_, c)| c) }
62    pub fn peek_next(&self) -> char {
63        let mut iter = self.chars.clone();
64        iter.next();
65        iter.next().map_or('\0', |(_, c)| c)
66    }
67    pub fn match_next(&mut self, c: char) -> bool {
68        if self.peek() == c {
69            self.advance();
70            return true;
71        }
72        false
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use span::source::SourceMap;
79    use super::Cursor;
80
81    #[test]
82    fn test() {
83        let text = "Hello world!";
84        let mut source = SourceMap::default();
85        let (src, id) = source.add_file_annon(text.into()).into_parts();
86        let mut cursor = Cursor::new(&src, id);
87        for c in text.chars() {
88            assert!(!cursor.is_finished());
89            let next = cursor.advance();
90            assert_eq!(next, c);
91        }
92        assert!(cursor.is_finished());
93    }
94}