Skip to main content

blues_lsp/syntax/
streams.rs

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    // Returns `true` if chunk boundary was crossed
18    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            // Start with empty iterator
99            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    /// Returns first char of new chunk, or None if no chunks are left
112    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    /// Advance one char, including it in the next subchunk.
215    /// Return Some(subchunk) on chunk boundary.
216    #[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    /// Advance one char, ommiting it from the next subchunk.
233    /// Return pending subchunk.
234    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    /// Consume stream, return pending subchunk
243    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
261// To hide away all those lifetimes
262pub trait ChunkWriter<'src> {
263    type Chars: CharStream<'src> + ?Sized;
264
265    fn peek(&mut self) -> Option<char>;
266
267    /// Advance one char, including it in the next subchunk.
268    fn advance(&mut self);
269
270    /// Advance one char, ommiting it from the next subchunk.
271    fn skip(&mut self);
272
273    /// Consume stream, return pending subchunk
274    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    /// Advance one char, including it in the next subchunk.
341    fn advance(&mut self) {
342        self.sub.advance().then(self.writer());
343    }
344
345    /// Advance one char, ommiting it from the next subchunk.
346    fn skip(&mut self) {
347        self.sub.skip().then(self.writer())
348    }
349
350    /// Consume stream, return pending subchunk
351    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    // No need to do anything
389    fn flush(&mut self) {}
390
391    fn chars(&self) -> &Self::Chars {
392        self.sub.chars()
393    }
394}