char/avril

(wip) why'd you have to go and make things so complicaaaaated?

git clone https://git.t4t.associates/char/avril

Charlotte Somrearrange libs into lib and extensions into extsf4a0f5b

main
16.7 KiB518 linesraw
1use std::collections::HashMap;
2use std::fmt;
3use std::ops::Range;
4
5/// UTF-8 byte offsets into the original source.
6pub type Span = Range<usize>;
7
8#[derive(Debug, Clone, PartialEq)]
9pub struct Value {
10    pub kind: ValueKind,
11    pub span: Span,
12}
13
14#[derive(Debug, Clone, PartialEq)]
15pub enum ValueKind {
16    Null,
17    Bool(bool),
18    // Preserve precision until the config schema chooses a numeric type.
19    Number(String),
20    String(String),
21    Array(Vec<Value>),
22    Object(Vec<Member>),
23}
24
25#[derive(Debug, Clone, PartialEq)]
26pub struct Member {
27    pub key: String,
28    pub key_span: Span,
29    pub value: Value,
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum ErrorKind {
34    Syntax,
35    Unicode,
36    Number,
37    Nesting,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct Error {
42    pub kind: ErrorKind,
43    pub message: String,
44    pub span: Span,
45    pub previous: Option<Span>,
46}
47
48impl Error {
49    /// Render against the same source that was parsed.
50    pub fn render(&self, name: &str, source: &str) -> String {
51        let location = |offset: usize| {
52            let mut line = 1;
53            let mut column = 1;
54            let mut previous_cr = false;
55            for ch in source[..offset].chars() {
56                match ch {
57                    '\r' => {
58                        line += 1;
59                        column = 1;
60                    }
61                    '\n' if previous_cr => {}
62                    '\n' => {
63                        line += 1;
64                        column = 1;
65                    }
66                    _ => column += 1,
67                }
68                previous_cr = ch == '\r';
69            }
70            format!("{name}:{line}:{column}")
71        };
72        let mut message = format!("{}: {}", location(self.span.start), self.message);
73        if let Some(previous) = &self.previous {
74            message.push_str(&format!(
75                "\n{}: First defined here",
76                location(previous.start)
77            ));
78        }
79        message
80    }
81}
82
83impl fmt::Display for Error {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        f.write_str(&self.message)
86    }
87}
88
89impl std::error::Error for Error {}
90
91pub fn parse_object(source: &str) -> Result<Value, Error> {
92    parse_object_with(source, &crate::Options::default())
93}
94
95pub fn parse_object_with(source: &str, options: &crate::Options) -> Result<Value, Error> {
96    let mut parser = Parser {
97        source,
98        offset: 0,
99        max_depth: options.max_depth,
100    };
101    parser.trivia()?;
102    let value = parser.object(parser.peek() == Some('{'), 0)?;
103    parser.trivia()?;
104    if parser.peek().is_some() {
105        return Err(parser.error("Unexpected content after the root object"));
106    }
107    Ok(value)
108}
109
110pub(crate) fn nesting_limit(span: Span, limit: usize) -> Error {
111    Error {
112        kind: ErrorKind::Nesting,
113        span,
114        message: format!("fJSON nesting exceeds {limit} levels below the root"),
115        previous: None,
116    }
117}
118
119pub(crate) fn invalid(span: Span, message: impl Into<String>) -> Error {
120    Error {
121        kind: ErrorKind::Syntax,
122        span,
123        message: message.into(),
124        previous: None,
125    }
126}
127
128pub fn parse(source: &str) -> Result<Value, Error> {
129    parse_with(source, &crate::Options::default())
130}
131
132pub fn parse_with(source: &str, options: &crate::Options) -> Result<Value, Error> {
133    let mut parser = Parser {
134        source,
135        offset: 0,
136        max_depth: options.max_depth,
137    };
138    parser.trivia()?;
139    let start = parser.offset;
140    let implicit = match parser.peek() {
141        None => true,
142        Some('"' | '\'') => {
143            parser.string()?;
144            parser.trivia()?;
145            parser.peek() == Some(':')
146        }
147        Some(ch) if ch.is_ascii_alphabetic() || ch == '_' || ch == '$' => {
148            parser.identifier();
149            parser.trivia()?;
150            parser.peek() == Some(':')
151        }
152        _ => false,
153    };
154    parser.offset = start;
155    let value = if implicit {
156        parser.object(false, 0)?
157    } else {
158        parser.value(0)?
159    };
160    parser.trivia()?;
161    if parser.peek().is_some() {
162        return Err(parser.error("Unexpected content after the root value"));
163    }
164    Ok(value)
165}
166
167struct Parser<'a> {
168    source: &'a str,
169    offset: usize,
170    max_depth: usize,
171}
172
173impl Parser<'_> {
174    fn peek(&self) -> Option<char> {
175        self.source[self.offset..].chars().next()
176    }
177
178    fn bump(&mut self) -> Option<char> {
179        let ch = self.peek()?;
180        self.offset += ch.len_utf8();
181        Some(ch)
182    }
183
184    fn error(&self, message: impl Into<String>) -> Error {
185        Error {
186            kind: ErrorKind::Syntax,
187            message: message.into(),
188            span: self.offset..self.offset + self.peek().map_or(0, char::len_utf8),
189            previous: None,
190        }
191    }
192
193    fn expect(&mut self, expected: char) -> Result<(), Error> {
194        if self.peek() != Some(expected) {
195            return Err(self.error(format!("Expected `{expected}`")));
196        }
197        self.bump();
198        Ok(())
199    }
200
201    fn trivia(&mut self) -> Result<bool, Error> {
202        let mut newline = false;
203        loop {
204            match self.peek() {
205                Some(' ' | '\t') => {
206                    self.bump();
207                }
208                Some('\r' | '\n') => {
209                    newline = true;
210                    self.bump();
211                }
212                Some('/') if self.source[self.offset..].starts_with("//") => {
213                    while self.peek().is_some_and(|ch| ch != '\r' && ch != '\n') {
214                        self.bump();
215                    }
216                }
217                Some('/') if self.source[self.offset..].starts_with("/*") => {
218                    let start = self.offset;
219                    self.offset += 2;
220                    let Some(end) = self.source[self.offset..].find("*/") else {
221                        return Err(Error {
222                            kind: ErrorKind::Syntax,
223                            message: "Unterminated block comment".into(),
224                            span: start..start + 2,
225                            previous: None,
226                        });
227                    };
228                    self.offset += end + 2;
229                }
230                _ => return Ok(newline),
231            }
232        }
233    }
234
235    fn end(&mut self, closing: Option<char>) -> Result<bool, Error> {
236        match (closing, self.peek()) {
237            (Some(expected), Some(ch)) if ch == expected => {
238                self.bump();
239                Ok(true)
240            }
241            (Some(expected), None) => Err(self.error(format!("Expected `{expected}`"))),
242            (None, None) => Ok(true),
243            _ => Ok(false),
244        }
245    }
246
247    fn more(&mut self, closing: Option<char>) -> Result<bool, Error> {
248        let newline = self.trivia()?;
249        if self.end(closing)? {
250            return Ok(false);
251        }
252        if self.peek() == Some(',') {
253            self.bump();
254            self.trivia()?;
255        } else if !newline {
256            return Err(self.error("Expected `,` or newline"));
257        }
258        Ok(!self.end(closing)?)
259    }
260
261    fn object(&mut self, braced: bool, depth: usize) -> Result<Value, Error> {
262        let start = self.offset;
263        if braced {
264            self.expect('{')?;
265        }
266        let closing = braced.then_some('}');
267        let mut members = Vec::new();
268        let mut end = start;
269        let mut keys = HashMap::new();
270        self.trivia()?;
271        if !self.end(closing)? {
272            loop {
273                let key_start = self.offset;
274                let key = match self.peek() {
275                    Some('"' | '\'') => self.string()?,
276                    Some(ch) if ch.is_ascii_alphabetic() || ch == '_' || ch == '$' => {
277                        self.identifier().to_owned()
278                    }
279                    _ => return Err(self.error("Expected an object key")),
280                };
281                let key_span = key_start..self.offset;
282                if let Some(previous) = keys.insert(key.clone(), key_span.clone()) {
283                    return Err(Error {
284                        kind: ErrorKind::Syntax,
285                        message: format!("Duplicate key {key:?}"),
286                        span: key_span,
287                        previous: Some(previous),
288                    });
289                }
290                self.trivia()?;
291                self.expect(':')?;
292                self.trivia()?;
293                let value = self.value(depth + 1)?;
294                end = self.offset;
295                members.push(Member { key, key_span, value });
296                if !self.more(closing)? {
297                    break;
298                }
299            }
300        }
301        Ok(Value {
302            kind: ValueKind::Object(members),
303            span: start..if braced { self.offset } else { end },
304        })
305    }
306
307    fn array(&mut self, depth: usize) -> Result<Value, Error> {
308        let start = self.offset;
309        self.expect('[')?;
310        self.trivia()?;
311        let mut values = Vec::new();
312        if !self.end(Some(']'))? {
313            loop {
314                values.push(self.value(depth + 1)?);
315                if !self.more(Some(']'))? {
316                    break;
317                }
318            }
319        }
320        Ok(Value {
321            kind: ValueKind::Array(values),
322            span: start..self.offset,
323        })
324    }
325
326    fn value(&mut self, depth: usize) -> Result<Value, Error> {
327        let start = self.offset;
328        let kind = match self.peek() {
329            Some('{' | '[') if depth > self.max_depth => {
330                return Err(nesting_limit(start..start + 1, self.max_depth));
331            }
332            Some('{') => return self.object(true, depth),
333            Some('[') => return self.array(depth),
334            Some('"' | '\'') => ValueKind::String(self.string()?),
335            Some('-' | '0'..='9') => ValueKind::Number(self.number()?),
336            Some(ch) if ch.is_ascii_alphabetic() || ch == '_' || ch == '$' => {
337                let word = self.identifier();
338                match word {
339                    "true" => ValueKind::Bool(true),
340                    "false" => ValueKind::Bool(false),
341                    "null" => ValueKind::Null,
342                    _ => {
343                        return Err(Error {
344                            kind: ErrorKind::Syntax,
345                            message: format!("Unknown value {word:?}; strings must be quoted"),
346                            span: start..self.offset,
347                            previous: None,
348                        });
349                    }
350                }
351            }
352            _ => return Err(self.error("Expected a value")),
353        };
354        Ok(Value { kind, span: start..self.offset })
355    }
356
357    fn identifier(&mut self) -> &str {
358        let start = self.offset;
359        while self
360            .peek()
361            .is_some_and(|ch| ch.is_ascii_alphanumeric() || ch == '_' || ch == '$')
362        {
363            self.bump();
364        }
365        &self.source[start..self.offset]
366    }
367
368    fn number(&mut self) -> Result<String, Error> {
369        let start = self.offset;
370        if self.peek() == Some('-') {
371            self.bump();
372        }
373        match self.peek() {
374            Some('0') => {
375                self.bump();
376                if self.peek().is_some_and(|ch| ch.is_ascii_digit()) {
377                    return Err(self.error("Leading zeros are not allowed"));
378                }
379            }
380            Some('1'..='9') => self.digits()?,
381            _ => return Err(self.error("Expected a digit")),
382        }
383        if self.peek() == Some('.') {
384            self.bump();
385            self.digits()?;
386        }
387        if matches!(self.peek(), Some('e' | 'E')) {
388            self.bump();
389            if matches!(self.peek(), Some('+' | '-')) {
390                self.bump();
391            }
392            self.digits()?;
393        }
394        if self
395            .peek()
396            .is_some_and(|ch| ch.is_ascii_alphanumeric() || matches!(ch, '.' | '_' | '$'))
397        {
398            return Err(self.error("Invalid decimal number"));
399        }
400        Ok(self.source[start..self.offset].to_owned())
401    }
402
403    fn digits(&mut self) -> Result<(), Error> {
404        if !self.peek().is_some_and(|ch| ch.is_ascii_digit()) {
405            return Err(self.error("Expected a digit"));
406        }
407        while self.peek().is_some_and(|ch| ch.is_ascii_digit()) {
408            self.bump();
409        }
410        Ok(())
411    }
412
413    fn string(&mut self) -> Result<String, Error> {
414        let start = self.offset;
415        let quote = self.bump().unwrap();
416        let mut value = String::new();
417        loop {
418            let ch = self.peek().ok_or_else(|| Error {
419                kind: ErrorKind::Syntax,
420                message: "Unterminated string".into(),
421                span: start..start + 1,
422                previous: None,
423            })?;
424            match ch {
425                ch if ch == quote => {
426                    self.bump();
427                    return Ok(value);
428                }
429                '\\' => {
430                    self.bump();
431                    let escaped = match self.peek() {
432                        Some('"' | '\'' | '\\' | '/') => self.bump().unwrap(),
433                        Some('b') => {
434                            self.bump();
435                            '\u{0008}'
436                        }
437                        Some('f') => {
438                            self.bump();
439                            '\u{000c}'
440                        }
441                        Some('n') => {
442                            self.bump();
443                            '\n'
444                        }
445                        Some('r') => {
446                            self.bump();
447                            '\r'
448                        }
449                        Some('t') => {
450                            self.bump();
451                            '\t'
452                        }
453                        Some('u') => {
454                            self.bump();
455                            let unicode_start = self.offset;
456                            let first = self.hex_quad()?;
457                            let scalar = match first {
458                                0xd800..=0xdbff => {
459                                    if !self.source[self.offset..].starts_with("\\u") {
460                                        return Err(Error {
461                                            kind: ErrorKind::Unicode,
462                                            message: "Unpaired Unicode surrogate".into(),
463                                            span: unicode_start..self.offset,
464                                            previous: None,
465                                        });
466                                    }
467                                    self.offset += 2;
468                                    let second = self.hex_quad()?;
469                                    if !(0xdc00..=0xdfff).contains(&second) {
470                                        return Err(Error {
471                                            kind: ErrorKind::Unicode,
472                                            message: "Expected a low Unicode surrogate".into(),
473                                            span: self.offset - 4..self.offset,
474                                            previous: None,
475                                        });
476                                    }
477                                    0x10000 + ((first - 0xd800) << 10) + second - 0xdc00
478                                }
479                                0xdc00..=0xdfff => {
480                                    return Err(Error {
481                                        kind: ErrorKind::Unicode,
482                                        message: "Unexpected low Unicode surrogate".into(),
483                                        span: unicode_start..self.offset,
484                                        previous: None,
485                                    });
486                                }
487                                _ => first,
488                            };
489                            char::from_u32(scalar).unwrap()
490                        }
491                        _ => return Err(self.error("Invalid string escape")),
492                    };
493                    value.push(escaped);
494                }
495                ch if ch <= '\u{001f}' => {
496                    return Err(self.error("Control characters must be escaped in strings"));
497                }
498                _ => {
499                    value.push(ch);
500                    self.bump();
501                }
502            }
503        }
504    }
505
506    fn hex_quad(&mut self) -> Result<u32, Error> {
507        let mut value = 0;
508        for _ in 0..4 {
509            let digit = self
510                .peek()
511                .and_then(|ch| ch.to_digit(16))
512                .ok_or_else(|| self.error("Expected four hexadecimal digits in Unicode escape"))?;
513            self.bump();
514            value = value * 16 + digit;
515        }
516        Ok(value)
517    }
518}