1
use std::str::Chars;
2

            
3
use crate::source_map::{BytePos, Pos};
4

            
5
1534
#[derive(Clone)]
6
pub struct CharStream<'chars> {
7
    chars: Chars<'chars>,
8
    cached_peek: Option<PeekChar>,
9
    byte_pos_of_peeked_char: BytePos,
10
}
11

            
12
1526
#[derive(Clone, Copy)]
13
struct PeekChar {
14
    ch: char,
15
    size: usize,
16
}
17

            
18
impl CharStream<'_> {
19
    pub const EOF_CHAR: char = '\0';
20
    const LOOKAHEAD_LIMIT: usize = 8;
21

            
22
6990
    pub fn with_text(text: &str) -> CharStream {
23
6990
        CharStream {
24
6990
            chars: text.chars(),
25
6990
            cached_peek: None,
26
6990
            byte_pos_of_peeked_char: BytePos::from_usize(0),
27
6990
        }
28
6990
    }
29

            
30
604579
    pub fn peek(&mut self) -> char {
31
604579
        match self.cached_peek {
32
368802
            Some(peek) => peek.ch,
33
            _ => {
34
235777
                let peeked = self.get_next_char_and_size();
35
235777
                self.cached_peek = Some(peeked);
36
235777
                peeked.ch
37
            }
38
        }
39
604579
    }
40

            
41
235777
    fn get_next_char_and_size(&mut self) -> PeekChar {
42
235777
        let next_char = self.chars.next();
43
235777
        let lookahead_char = self.chars.clone().next();
44
235777

            
45
235777
        if is_newline(next_char) && is_newline(lookahead_char) && next_char != lookahead_char {
46
250
            self.chars.next();
47
250
            return PeekChar { ch: '\n', size: 2 };
48
235527
        }
49
235527

            
50
235527
        match next_char {
51
229704
            Some(ch) => PeekChar {
52
229704
                ch,
53
229704
                size: ch.len_utf8(),
54
229704
            },
55
5823
            _ => PeekChar {
56
5823
                ch: CharStream::EOF_CHAR,
57
5823
                size: 0,
58
5823
            },
59
        }
60
235777
    }
61

            
62
33902
    pub fn peek_byte_pos(&self) -> BytePos {
63
33902
        self.byte_pos_of_peeked_char
64
33902
    }
65

            
66
1539
    pub fn lookahead(&mut self, n: usize) -> char {
67
1539
        assert!(
68
1539
            n <= CharStream::LOOKAHEAD_LIMIT,
69
1
            "cannot look further than {} chars ahead",
70
            CharStream::LOOKAHEAD_LIMIT,
71
        );
72

            
73
1538
        if n == 0 {
74
4
            self.peek()
75
        } else {
76
1534
            let mut cloned_self = self.clone();
77
1557
            for _ in 0..n {
78
1557
                let _ = cloned_self.consume();
79
1557
            }
80
1534
            cloned_self.peek()
81
        }
82
1538
    }
83

            
84
235512
    pub fn consume(&mut self) -> char {
85
235512
        // Guarantees that `self.cached_peek` has a peeked char.
86
235512
        self.peek();
87
235512
        debug_assert!(self.cached_peek.is_some());
88

            
89
235512
        let peeked = self.cached_peek.unwrap();
90
235512

            
91
235512
        if peeked.ch != CharStream::EOF_CHAR {
92
229061
            self.byte_pos_of_peeked_char += BytePos::from_usize(peeked.size);
93
229061
            self.cached_peek = None;
94
229061
        }
95

            
96
235512
        peeked.ch
97
235512
    }
98

            
99
4687
    pub fn try_consume(&mut self, ch: char) -> bool {
100
4687
        assert!(ch != CharStream::EOF_CHAR, "cannot consume an EOF char");
101

            
102
4686
        let is_ch_peek = self.peek() == ch;
103
4686
        if is_ch_peek {
104
569
            self.consume();
105
4117
        }
106
4686
        is_ch_peek
107
4686
    }
108
}
109

            
110
238658
fn is_newline(ch: impl Into<Option<char>>) -> bool {
111
238658
    matches!(ch.into(), Some('\n' | '\r'))
112
238658
}