toml_parser/decoder/
ws.rs

1use core::ops::RangeInclusive;
2
3use winnow::stream::ContainsToken as _;
4
5use crate::lexer::COMMENT_START_SYMBOL;
6use crate::ErrorSink;
7use crate::Expected;
8use crate::ParseError;
9use crate::Raw;
10use crate::Span;
11
12/// Parse comment
13///
14/// ```bnf
15/// ;; Comment
16///
17/// comment-start-symbol = %x23 ; #
18/// non-ascii = %x80-D7FF / %xE000-10FFFF
19/// non-eol = %x09 / %x20-7F / non-ascii
20///
21/// comment = comment-start-symbol *non-eol
22/// ```
23pub(crate) fn decode_comment(raw: Raw<'_>, error: &mut dyn ErrorSink) {
24    let s = raw.as_bytes();
25
26    if s.first() != Some(&COMMENT_START_SYMBOL) {
27        error.report_error(
28            ParseError::new("missing comment start")
29                .with_context(Span::new_unchecked(0, raw.len()))
30                .with_expected(&[Expected::Literal("#")])
31                .with_unexpected(Span::new_unchecked(0, 0)),
32        );
33    }
34
35    for (i, b) in s.iter().copied().enumerate() {
36        if !NON_EOL.contains_token(b) {
37            error.report_error(
38                ParseError::new("invalid comment character")
39                    .with_context(Span::new_unchecked(0, raw.len()))
40                    .with_expected(&[Expected::Description("printable characters")])
41                    .with_unexpected(Span::new_unchecked(i, i)),
42            );
43        }
44    }
45}
46
47// non-ascii = %x80-D7FF / %xE000-10FFFF
48// - ASCII is 0xxxxxxx
49// - First byte for UTF-8 is 11xxxxxx
50// - Subsequent UTF-8 bytes are 10xxxxxx
51pub(crate) const NON_ASCII: RangeInclusive<u8> = 0x80..=0xff;
52
53// non-eol = %x09 / %x20-7E / non-ascii
54pub(crate) const NON_EOL: (u8, RangeInclusive<u8>, RangeInclusive<u8>) =
55    (0x09, 0x20..=0x7E, NON_ASCII);
56
57/// Parse newline
58///
59/// ```bnf
60///;; Newline
61///
62/// newline =  %x0A     ; LF
63/// newline =/ %x0D.0A  ; CRLF
64/// ```
65pub(crate) fn decode_newline(raw: Raw<'_>, error: &mut dyn ErrorSink) {
66    let s = raw.as_str();
67
68    if s == "\r" {
69        error.report_error(
70            ParseError::new("carriage return must be followed by newline")
71                .with_context(Span::new_unchecked(0, raw.len()))
72                .with_expected(&[Expected::Literal("\n")])
73                .with_unexpected(Span::new_unchecked(raw.len(), raw.len())),
74        );
75    }
76}