dora_parser/lexer/
reader.rs

1use std::fs;
2use std::io::{self, Error, Read};
3
4use crate::lexer::position::Position;
5use crate::lexer::File;
6
7pub struct Reader {
8    name: String,
9    content: String,
10    line_ends: Vec<u32>,
11
12    idx: usize,
13    pos: Position,
14
15    tabwidth: u32,
16}
17
18impl Reader {
19    pub fn from_input() -> Result<Reader, Error> {
20        let mut src = String::new();
21        io::stdin().read_to_string(&mut src)?;
22
23        Ok(common_init("<<stdin>>".into(), src))
24    }
25
26    pub fn from_file(filename: &str) -> Result<Reader, Error> {
27        let mut src = String::new();
28
29        let mut file = fs::File::open(filename)?;
30        file.read_to_string(&mut src)?;
31
32        Ok(common_init(filename.into(), src))
33    }
34
35    pub fn from_string(filename: &str, src: &str) -> Reader {
36        common_init(filename.into(), src.into())
37    }
38
39    pub fn set_tabwidth(&mut self, tabwidth: u32) {
40        self.tabwidth = tabwidth;
41    }
42
43    pub fn path(&self) -> &str {
44        &self.name
45    }
46
47    pub fn advance(&mut self) -> Option<char> {
48        let curr = self.curr();
49
50        match curr {
51            Some('\n') => {
52                self.pos = Position::new(self.pos.line + 1, 1);
53                self.line_ends.push(self.idx as u32);
54            }
55
56            Some('\t') => {
57                let tabdepth = (self.pos.column - 1) / self.tabwidth;
58                let col = 1 + self.tabwidth * (tabdepth + 1);
59                self.pos = Position::new(self.pos.line, col);
60            }
61
62            Some(_) => {
63                self.pos = Position::new(self.pos.line, self.pos.column + 1);
64            }
65
66            None => panic!("advancing from eof"),
67        }
68
69        if let Some(ch) = curr {
70            self.idx += ch.len_utf8();
71        }
72
73        self.curr()
74    }
75
76    pub fn file(self) -> File {
77        File {
78            name: self.name,
79            content: self.content,
80            line_ends: self.line_ends,
81        }
82    }
83
84    pub fn curr(&self) -> Option<char> {
85        self.nth(0)
86    }
87
88    pub fn nth(&self, offset: usize) -> Option<char> {
89        let pos = self.idx + offset;
90
91        if pos < self.content.len() {
92            self.content[pos..].chars().next()
93        } else {
94            None
95        }
96    }
97
98    pub fn pos(&self) -> Position {
99        self.pos
100    }
101
102    pub fn idx(&self) -> u32 {
103        self.idx as u32
104    }
105}
106
107fn common_init(name: String, content: String) -> Reader {
108    let reader = Reader {
109        name,
110        content,
111        line_ends: Vec::new(),
112
113        idx: 0,
114        pos: Position::new(1, 1),
115        tabwidth: 4,
116    };
117
118    reader
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[test]
126    fn read_from_str() {
127        let mut reader = Reader::from_string("<<code>>", "abc");
128
129        assert_eq!(Some('a'), reader.curr());
130        assert_eq!(Some('b'), reader.nth(1));
131        reader.advance();
132
133        assert_eq!(Some('b'), reader.curr());
134        assert_eq!(Some('c'), reader.nth(1));
135        reader.advance();
136
137        assert_eq!(Some('c'), reader.curr());
138        assert_eq!(None, reader.nth(1));
139        reader.advance();
140
141        assert_eq!(None, reader.curr());
142        assert_eq!(None, reader.nth(1));
143    }
144}