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}