hara_native/kernel/
reader.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2pub struct Position {
3 pub offset: usize,
4 pub line: usize,
5 pub column: usize,
6}
7
8#[derive(Debug, Clone)]
9pub struct Reader<'a> {
10 source: &'a str,
11 cursor: usize,
12 line: usize,
13 column: usize,
14 history: Vec<Position>,
15}
16impl<'a> Reader<'a> {
17 pub fn new(source: &'a str) -> Self {
18 Self {
19 source,
20 cursor: 0,
21 line: 1,
22 column: 1,
23 history: Vec::new(),
24 }
25 }
26 pub fn position(&self) -> Position {
27 Position {
28 offset: self.cursor,
29 line: self.line,
30 column: self.column,
31 }
32 }
33 pub fn peek_char(&self) -> Option<char> {
34 self.source[self.cursor..].chars().next()
35 }
36 pub fn read_char(&mut self) -> Option<char> {
37 let ch = self.peek_char()?;
38 self.history.push(self.position());
39 self.cursor += ch.len_utf8();
40 if ch == '\n' {
41 self.line += 1;
42 self.column = 1
43 } else {
44 self.column += 1
45 }
46 Some(ch)
47 }
48 pub fn unread_char(&mut self) -> Option<char> {
49 let position = self.history.pop()?;
50 let ch = self.source[position.offset..self.cursor].chars().next();
51 self.cursor = position.offset;
52 self.line = position.line;
53 self.column = position.column;
54 ch
55 }
56 pub fn read_while(&mut self, predicate: impl Fn(char) -> bool) -> String {
57 let mut value = String::new();
58 while let Some(ch) = self.read_char() {
59 if predicate(ch) {
60 value.push(ch)
61 } else {
62 self.unread_char();
63 break;
64 }
65 }
66 value
67 }
68 pub fn read_until(&mut self, predicate: impl Fn(char) -> bool) -> String {
69 self.read_while(|ch| !predicate(ch))
70 }
71 pub fn is_eof(&self) -> bool {
72 self.cursor >= self.source.len()
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::{Position, Reader};
79 #[test]
80 fn tracks_unicode_lines_columns_and_unread() {
81 let mut r = Reader::new("λ\nx");
82 assert_eq!(
83 r.position(),
84 Position {
85 offset: 0,
86 line: 1,
87 column: 1
88 }
89 );
90 assert_eq!(r.read_char(), Some('λ'));
91 assert_eq!(r.position().column, 2);
92 r.unread_char();
93 assert_eq!(r.position().column, 1);
94 r.read_char();
95 r.read_char();
96 assert_eq!(
97 r.position(),
98 Position {
99 offset: 3,
100 line: 2,
101 column: 1
102 }
103 );
104 }
105}