Skip to main content

jjpwrgem_parse/tokens/
stream.rs

1use core::{iter::Peekable, range::Range};
2
3use crate::{
4    Error, ErrorKind, Result,
5    tokens::{
6        ByteWithContext, BytesWithContext, FALSE, NULL, TRUE, Token, TokenWithContext,
7        current_byte_pos,
8        lexical::JsonChar,
9        number::{parse_exponent, parse_mantissa},
10        string::parse_string,
11    },
12};
13
14fn skip_whitespace(bytes: &[u8]) -> usize {
15    use std::simd::{cmp::SimdPartialEq as _, u8x32};
16
17    let space = u8x32::splat(b' ');
18    let horizontal_tab = u8x32::splat(b'\t');
19    let line_feed = u8x32::splat(b'\n');
20    let carriage_return = u8x32::splat(b'\r');
21
22    let mut i = 0;
23    while i + 32 <= bytes.len() {
24        let chunk = u8x32::from_slice(&bytes[i..i + 32]);
25        let is_whitespace = chunk.simd_eq(space)
26            | chunk.simd_eq(horizontal_tab)
27            | chunk.simd_eq(line_feed)
28            | chunk.simd_eq(carriage_return);
29        let whitespace_count = is_whitespace.to_bitmask().trailing_ones() as usize;
30        i += whitespace_count;
31        if whitespace_count < 32 {
32            return i;
33        }
34    }
35
36    i + bytes[i..]
37        .iter()
38        .position(|&b| !matches!(b, b' ' | b'\t' | b'\n' | b'\r'))
39        .unwrap_or(bytes.len().saturating_sub(i))
40}
41
42#[derive(Debug, Clone)]
43struct TokenStreamInner<'a> {
44    input: &'a str,
45    pos: usize,
46}
47
48impl<'a> TokenStreamInner<'a> {
49    fn new_at_range(full_source: &'a str, range: Range<usize>) -> Self {
50        Self {
51            input: &full_source[..range.end],
52            pos: range.start,
53        }
54    }
55
56    fn peek_byte_with_context(&self) -> Option<ByteWithContext> {
57        self.input
58            .as_bytes()
59            .get(self.pos)
60            .copied()
61            .map(|byte| (self.pos, byte).into())
62    }
63
64    fn bytes_from_pos(&self) -> Peekable<BytesWithContext<'a>> {
65        BytesWithContext::new(self.input, self.pos).peekable()
66    }
67
68    fn parse_with_bytes<T>(
69        &mut self,
70        f: impl FnOnce(&'a str, &mut Peekable<BytesWithContext<'a>>) -> Result<T>,
71    ) -> Result<T> {
72        let mut bytes = self.bytes_from_pos();
73        let result = f(self.input, &mut bytes);
74        if result.is_ok() {
75            self.update_pos_from_bytes(&mut bytes);
76        }
77        result
78    }
79
80    fn update_pos_from_bytes(&mut self, bytes: &mut Peekable<BytesWithContext<'a>>) {
81        self.pos = current_byte_pos(bytes, self.input);
82    }
83
84    fn unexpected_character(&self) -> Result<TokenWithContext> {
85        let unexpected = self.input[self.pos..]
86            .chars()
87            .next()
88            .expect("pos must be in bounds");
89        Err(Error::new(
90            ErrorKind::UnexpectedCharacter(JsonChar(unexpected)),
91            self.pos..self.pos + unexpected.len_utf8(),
92            self.input,
93        ))
94    }
95
96    fn consume_whitespace(&mut self) {
97        let Some(ByteWithContext(_, byte)) = self.peek_byte_with_context() else {
98            return;
99        };
100        if !byte.is_whitespace() {
101            return;
102        }
103
104        self.pos += skip_whitespace(&self.input.as_bytes()[self.pos..]);
105    }
106}
107
108impl Iterator for TokenStreamInner<'_> {
109    type Item = Result<TokenWithContext>;
110
111    fn next(&mut self) -> Option<Self::Item> {
112        loop {
113            self.consume_whitespace();
114            let ctx = self.peek_byte_with_context()?;
115
116            if let Some(tok) = ctx.as_token_with_context() {
117                self.pos = ctx.range().end;
118                return Some(Ok(tok));
119            }
120
121            let token = match ctx.as_byte() {
122                b'"' => {
123                    let result = parse_string(self.input, self.pos);
124                    if let Ok(token) = &result {
125                        self.pos = token.range.end;
126                    }
127                    result
128                }
129                b'0'..=b'9' | b'-' => self.parse_with_bytes(parse_mantissa),
130                b'e' | b'E' => {
131                    match self.parse_with_bytes(|input, bytes| {
132                        bytes.next();
133                        parse_exponent(input, ctx.range(), bytes)
134                    }) {
135                        Ok(Some(tok)) => Ok(tok),
136                        Ok(None) => continue,
137                        Err(err) => Err(err),
138                    }
139                }
140                b'n' | b't' | b'f' => {
141                    let (expected, token) = match ctx.as_byte() {
142                        b'n' => (NULL, Token::Null),
143                        b't' => (TRUE, Token::True),
144                        b'f' => (FALSE, Token::False),
145                        _ => unreachable!("matched above"),
146                    };
147                    let end = self.pos + expected.len();
148                    if self.input.as_bytes().get(self.pos..end) == Some(expected.as_bytes()) {
149                        self.pos = end;
150                        Ok(TokenWithContext::new(token, ctx.0..end))
151                    } else {
152                        self.unexpected_character()
153                    }
154                }
155                _ => self.unexpected_character(),
156            };
157
158            return Some(token);
159        }
160    }
161}
162
163#[derive(Debug, Clone)]
164pub struct TokenStream<'a> {
165    inner: TokenStreamInner<'a>,
166    cached: Option<TokenWithContext>,
167}
168
169impl<'a> TokenStream<'a> {
170    pub(crate) fn new_at_range(full_source: &'a str, range: Range<usize>) -> Self {
171        Self {
172            inner: TokenStreamInner::new_at_range(full_source, range),
173            cached: None,
174        }
175    }
176
177    pub fn new(s: &'a str) -> Self {
178        Self::new_at_range(s, 0..s.len())
179    }
180
181    pub(crate) fn pos(&self) -> usize {
182        self.inner.pos
183    }
184
185    pub fn peek_token(&mut self) -> Result<Option<&TokenWithContext>> {
186        if self.cached.is_none() {
187            match self.inner.next() {
188                Some(Ok(token)) => self.cached = Some(token),
189                Some(Err(err)) => return Err(err),
190                None => return Ok(None),
191            }
192        }
193
194        Ok(self.cached.as_ref())
195    }
196
197    pub fn next_token(&mut self) -> Result<Option<TokenWithContext>> {
198        if let Some(token) = self.cached.take() {
199            Ok(Some(token))
200        } else {
201            self.next().transpose()
202        }
203    }
204}
205
206impl Iterator for TokenStream<'_> {
207    type Item = Result<TokenWithContext>;
208
209    fn next(&mut self) -> Option<Self::Item> {
210        self.inner.next()
211    }
212}