1use std::{marker::PhantomData, str::CharIndices};
2
3use arcstr::Substr;
4
5use crate::util::ext::{
6 arcstr::{EMPTY_SUBSTR, SubStrExt},
7 option::OptionThenExt,
8};
9
10use super::{
11 TextChunk,
12 location::{LineColCounter, Pos},
13};
14
15pub trait CharStream<'src>: Clone {
16 fn peek(&self) -> Option<char>;
17 fn step(&mut self) -> bool;
19
20 fn next(&mut self) -> Option<char> {
21 let out = self.peek()?;
22 self.step();
23 Some(out)
24 }
25
26 fn next_peek(&mut self) -> Option<char> {
27 self.next();
28 self.peek()
29 }
30
31 fn char_idx(&self) -> usize;
32 fn pos(&self) -> Pos;
33
34 fn current_chunk(&self) -> &'src TextChunk;
35
36 fn chunk_state<'slf>(&'slf mut self) -> ChunkState<'slf, 'src, Self> {
37 ChunkState::new(self)
38 }
39
40 fn expect_any(&mut self) {
41 self.next().expect("consume_any called on empty chunk");
42 }
43
44 fn expect(&mut self, expected: char) {
45 assert_eq!(self.next(), Some(expected))
46 }
47
48 fn read_while(&mut self, mut pred: impl FnMut(char) -> bool) -> Substr {
49 let mut s = EMPTY_SUBSTR;
50 let mut sub = self.chunk_state().writer(|c| s.append(c.text));
51
52 while let Some(c) = sub.peek() {
53 if !pred(c) {
54 break;
55 }
56 sub.advance();
57 }
58 sub.flush();
59 s
60 }
61
62 fn skip_while(&mut self, mut pred: impl FnMut(char) -> bool) -> usize {
63 let mut cnt = 0;
64
65 while let Some(c) = self.peek() {
66 if !pred(c) {
67 break;
68 }
69 cnt += 1;
70 self.expect_any();
71 }
72
73 cnt
74 }
75}
76
77#[derive(Clone)]
78pub struct TextCharStream<'src, I: Iterator<Item = &'src TextChunk>> {
79 chunks: I,
80
81 current_chunk: &'src TextChunk,
82 current_chars: CharIndices<'src>,
83 current_pos: LineColCounter,
84
85 peeked: Option<(usize, char)>,
86}
87
88static EMPTY_CHUNK: TextChunk = TextChunk {
89 pos: Pos::zero(),
90 text: EMPTY_SUBSTR,
91};
92
93impl<'src, I: Iterator<Item = &'src TextChunk>> TextCharStream<'src, I> {
94 pub fn new(input: I) -> Self {
95 let mut stream = Self {
96 chunks: input,
97
98 current_chunk: &EMPTY_CHUNK,
100 current_chars: "".char_indices(),
101 current_pos: LineColCounter::default(),
102
103 peeked: None,
104 };
105
106 stream.peeked = stream.next_new();
107
108 stream
109 }
110
111 fn next_new(&mut self) -> Option<(usize, char)> {
113 let chunk = self.chunks.next()?;
114
115 self.current_chunk = chunk;
116 self.current_chars = chunk.text.char_indices();
117 self.current_pos = LineColCounter::start_at(chunk.pos.text_pos);
118
119 Some(
120 self.current_chars
121 .next()
122 .expect("New chunks cannot be empty"),
123 )
124 }
125}
126
127impl<'src, I: Iterator<Item = &'src TextChunk> + Clone> CharStream<'src>
128 for TextCharStream<'src, I>
129{
130 fn peek(&self) -> Option<char> {
131 self.peeked.map(|c| c.1)
132 }
133
134 fn step(&mut self) -> bool {
135 let c = self.peek().expect("step called on EOF");
136 self.current_pos.advance(c);
137 match self.current_chars.next() {
138 Some(c) => {
139 self.peeked = Some(c);
140 false
141 }
142 None => {
143 self.peeked = self.next_new();
144 true
145 }
146 }
147 }
148
149 fn char_idx(&self) -> usize {
150 match self.peeked {
151 Some((ofs, _)) => ofs,
152 None => self.current_chunk.text.len(),
153 }
154 }
155
156 fn pos(&self) -> Pos {
157 Pos {
158 origin: self.current_chunk.pos.origin,
159 text_pos: self.current_pos.pos(self.peek()),
160 }
161 }
162
163 fn current_chunk(&self) -> &'src TextChunk {
164 self.current_chunk
165 }
166}
167
168pub struct ChunkState<'chr, 'src, I: CharStream<'src>> {
169 chars: &'chr mut I,
170 _pd: PhantomData<&'src ()>,
171
172 start_idx: usize,
173 start_pos: Pos,
174}
175
176impl<'chr, 'src, I: CharStream<'src>> ChunkState<'chr, 'src, I> {
177 fn new(chars: &'chr mut I) -> Self {
178 let start_idx = chars.char_idx();
179 let start_pos = chars.pos();
180 Self {
181 chars,
182 _pd: PhantomData,
183 start_idx,
184 start_pos,
185 }
186 }
187
188 pub fn peek(&mut self) -> Option<char> {
189 self.chars.peek()
190 }
191
192 fn get_subchunk_from_str(&self, chunk: &TextChunk, end: usize) -> Option<TextChunk> {
193 let range = self.start_idx..end;
194
195 if range.start == range.end {
196 return None;
197 }
198
199 Some(TextChunk {
200 pos: self.start_pos,
201 text: chunk.text.substr(range),
202 })
203 }
204
205 fn get_subchunk(&self, end: usize) -> Option<TextChunk> {
206 self.get_subchunk_from_str(self.chars.current_chunk(), end)
207 }
208
209 fn reset_subchunk(&mut self) {
210 self.start_idx = self.chars.char_idx();
211 self.start_pos = self.chars.pos();
212 }
213
214 #[must_use]
217 pub fn advance(&mut self) -> Option<TextChunk> {
218 self.chars.peek().expect("advance called on EOF");
219 let chunk = self.chars.current_chunk();
220
221 match self.chars.step() {
222 false => None,
223 true => {
224 let chunk = self.get_subchunk_from_str(chunk, chunk.text.len());
225 self.reset_subchunk();
226 chunk
227 }
228 }
229 }
230
231 #[must_use]
232 pub fn skip(&mut self) -> Option<TextChunk> {
235 let chunk = self.get_subchunk(self.chars.char_idx());
236 self.chars.next();
237 self.reset_subchunk();
238 chunk
239 }
240
241 #[must_use]
242 fn flush(&mut self) -> Option<TextChunk> {
244 let chunk = self.get_subchunk(self.chars.char_idx());
245 self.reset_subchunk();
246 chunk
247 }
248
249 pub fn writer<F: FnMut(TextChunk)>(self, f: F) -> FnChunkWriter<'chr, 'src, I, F> {
250 FnChunkWriter {
251 sub: self,
252 writer: f,
253 }
254 }
255
256 pub fn pos(&self) -> Pos {
257 self.chars.pos()
258 }
259}
260
261pub trait ChunkWriter<'src> {
263 type Chars: CharStream<'src> + ?Sized;
264
265 fn peek(&mut self) -> Option<char>;
266
267 fn advance(&mut self);
269
270 fn skip(&mut self);
272
273 fn flush(&mut self);
275
276 fn pos(&self) -> Pos;
277
278 fn chars(&self) -> &Self::Chars;
279
280 fn skipped(&mut self) -> SubChunkSkip<'_, 'src, Self> {
281 SubChunkSkip {
282 sub: self,
283 _pd: PhantomData,
284 }
285 }
286
287 fn check_any(&mut self) {
288 self.peek().expect("consume_any called on empty chunk");
289 }
290
291 fn check(&mut self, expected: char) {
292 assert_eq!(self.peek(), Some(expected))
293 }
294
295 fn advance_while(&mut self, mut pred: impl FnMut(char) -> bool) -> usize {
296 let mut cnt = 0;
297 while let Some(c) = self.peek() {
298 if !pred(c) {
299 break;
300 }
301 cnt += 1;
302 self.advance();
303 }
304 cnt
305 }
306
307 fn skip_while(&mut self, mut pred: impl FnMut(char) -> bool) -> usize {
308 let mut cnt = 0;
309 while let Some(c) = self.peek() {
310 if !pred(c) {
311 break;
312 }
313 cnt += 1;
314 self.skip();
315 }
316 cnt
317 }
318}
319
320pub struct FnChunkWriter<'chr, 'src, I: CharStream<'src>, F: FnMut(TextChunk)> {
321 sub: ChunkState<'chr, 'src, I>,
322 writer: F,
323}
324
325impl<'src, I: CharStream<'src>, F: FnMut(TextChunk)> FnChunkWriter<'_, 'src, I, F> {
326 fn writer(&mut self) -> impl FnOnce(TextChunk) + '_ {
327 |chunk| (self.writer)(chunk)
328 }
329}
330
331impl<'src, I: CharStream<'src>, F: FnMut(TextChunk)> ChunkWriter<'src>
332 for FnChunkWriter<'_, 'src, I, F>
333{
334 type Chars = I;
335
336 fn peek(&mut self) -> Option<char> {
337 self.sub.peek()
338 }
339
340 fn advance(&mut self) {
342 self.sub.advance().then(self.writer());
343 }
344
345 fn skip(&mut self) {
347 self.sub.skip().then(self.writer())
348 }
349
350 fn flush(&mut self) {
352 self.sub.flush().then(self.writer())
353 }
354
355 fn pos(&self) -> Pos {
356 self.sub.pos()
357 }
358
359 fn chars(&self) -> &Self::Chars {
360 self.sub.chars
361 }
362}
363
364pub struct SubChunkSkip<'a, 'src, T: ChunkWriter<'src> + ?Sized> {
365 sub: &'a mut T,
366 _pd: PhantomData<&'src ()>,
367}
368
369impl<'src, T: ChunkWriter<'src>> ChunkWriter<'src> for SubChunkSkip<'_, 'src, T> {
370 type Chars = T::Chars;
371
372 fn peek(&mut self) -> Option<char> {
373 self.sub.peek()
374 }
375
376 fn advance(&mut self) {
377 self.sub.skip();
378 }
379
380 fn skip(&mut self) {
381 self.sub.skip();
382 }
383
384 fn pos(&self) -> Pos {
385 self.sub.pos()
386 }
387
388 fn flush(&mut self) {}
390
391 fn chars(&self) -> &Self::Chars {
392 self.sub.chars()
393 }
394}