dora_parser/lexer/
reader.rs1use 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}