1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
//! Universal low-level interface to let Tokay read input from different sources.
use num_parse::PeekableIterator;
use std::io::prelude::*;
use std::io::BufReader;
/// Position inside a reader, with row and column counting.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Offset {
// todo: Hold source filename information as well in the future?
pub offset: usize,
pub row: u32,
pub col: u32,
}
pub type Range = std::ops::Range<usize>;
// Abstraction of a buffered Reader with internal buffering, offset counting and clean-up.
pub struct Reader {
reader: Box<dyn BufRead>, // Reader object to read from
buffer: String, // Internal buffer
peeked: char, // Currently peeked char
offset: Offset, // Current offset
start: Offset, // Offset of last commit
eof: bool, // EOF marker
}
impl Reader {
/// Creates a new reader on buffer read.
pub fn new(read: Box<dyn Read>) -> Self {
Self {
reader: Box::new(BufReader::new(read)),
buffer: String::with_capacity(1024), //fixme: Modifyable capacity?
peeked: ' ',
offset: Offset {
offset: 0,
row: 1,
col: 1,
},
start: Offset {
offset: 0,
row: 1,
col: 1,
},
eof: false,
}
}
/// Internal function for reading a line.
fn read_line(&mut self) -> Option<usize> {
if let Ok(n) = self.reader.read_line(&mut self.buffer) {
if n == 0 {
self.eof = true;
return None;
}
Some(n)
} else {
self.eof = true;
None
}
}
pub fn tell(&self) -> Offset {
self.offset
}
pub fn start(&self) -> Offset {
self.start
}
pub fn eof(&mut self) -> bool {
if self.buffer[self.offset.offset..].chars().next().is_some() {
false
} else {
if !self.eof {
self.peek();
}
self.eof
}
}
pub fn reset(&mut self, offset: Offset) {
self.offset = offset;
}
/// Capture last length characters.
pub fn capture_last(&self, mut length: usize) -> Range {
if length > self.offset.offset {
length = self.offset.offset;
}
self.offset.offset - length..self.offset.offset
}
// Capture all characters from start to current offset.
pub fn capture_from(&self, start: &Offset) -> Range {
let mut start = start.offset;
if start > self.offset.offset {
start = self.offset.offset;
}
start..self.offset.offset
}
pub fn print(&self, start: usize) {
println!("{:?}", &self.buffer[start..self.offset.offset])
}
/// Get slice from range
pub fn get(&self, range: &Range) -> &str {
&self.buffer[range.start..range.end]
}
/// Commits current input buffer and removes cached content
pub fn commit(&mut self) {
self.buffer.drain(0..self.offset.offset);
self.start = self.offset;
self.offset.offset = 0; // reset offset to 0
}
/// Take one character accepted by callback
pub fn once<F>(&mut self, accept: F) -> Option<char>
where
F: Fn(char) -> bool,
{
if let Some(ch) = self.peek() {
if accept(*ch) {
return Some(self.next().unwrap());
}
}
None
}
/// Read while conditional callback accepts characters
pub fn span<F>(&mut self, accept: F) -> Option<&str>
where
F: Fn(char) -> bool + Copy,
{
let start = self.offset.offset;
while self.once(accept).is_some() {}
if start < self.offset.offset {
Some(&self.buffer[start..self.offset.offset])
} else {
None
}
}
}
impl Iterator for Reader {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(ch) = self.buffer[self.offset.offset..].chars().next() {
self.offset.offset += ch.len_utf8();
if ch == '\n' {
self.offset.row += 1;
self.offset.col = 1;
} else {
self.offset.col += 1;
}
return Some(ch);
}
if self.eof {
return None;
}
self.read_line();
}
}
}
impl PeekableIterator for Reader {
fn peek(&mut self) -> Option<&Self::Item> {
loop {
if let Some(ch) = self.buffer[self.offset.offset..].chars().next() {
self.peeked = ch;
return Some(&self.peeked);
}
if self.eof {
return None;
}
self.read_line();
}
}
}