litrs/
parse.rs

1use crate::{
2    BoolLit,
3    Buffer,
4    ByteLit,
5    ByteStringLit,
6    CharLit,
7    ParseError,
8    FloatLit,
9    IntegerLit,
10    Literal,
11    StringLit,
12    err::{perr, ParseErrorKind::*},
13};
14
15
16impl<B: Buffer> Literal<B> {
17    /// Parses the given input as a Rust literal.
18    pub fn parse(input: B) -> Result<Self, ParseError> {
19        let first = first_byte_or_empty(&input)?;
20        let second = input.as_bytes().get(1).copied();
21
22        match first {
23            b'f' if &*input == "false" => Ok(Self::Bool(BoolLit::False)),
24            b't' if &*input == "true" => Ok(Self::Bool(BoolLit::True)),
25
26            // A number literal (integer or float).
27            digit @ b'0'..=b'9' => {
28                // To figure out whether this is a float or integer, we do some
29                // quick inspection here. Yes, this is technically duplicate
30                // work with what is happening in the integer/float parse
31                // methods, but it makes the code way easier for now and won't
32                // be a huge performance loss.
33                let end = 1 + end_dec_digits(&input[1..]);
34                match input.as_bytes().get(end) {
35                    // Potential chars in integer literals: b, o, x for base; u
36                    // and i for type suffix.
37                    None | Some(b'b') | Some(b'o') | Some(b'x') | Some(b'u') | Some(b'i')
38                        => IntegerLit::parse_impl(input, digit).map(Literal::Integer),
39
40                    // Potential chars for float literals: `.` as fractional
41                    // period, e and E as exponent start and f as type suffix.
42                    Some(b'.') | Some(b'e') | Some(b'E') | Some(b'f')
43                        => FloatLit::parse_impl(input).map(Literal::Float),
44
45                    _ => Err(perr(end, UnexpectedChar)),
46                }
47            },
48
49            b'\'' => CharLit::parse_impl(input).map(Literal::Char),
50            b'"' | b'r' => StringLit::parse_impl(input).map(Literal::String),
51
52            b'b' if second == Some(b'\'') => ByteLit::parse_impl(input).map(Literal::Byte),
53            b'b' if second == Some(b'r') || second == Some(b'"')
54                => ByteStringLit::parse_impl(input).map(Literal::ByteString),
55
56            _ => Err(perr(None, InvalidLiteral)),
57        }
58    }
59}
60
61
62pub(crate) fn first_byte_or_empty(s: &str) -> Result<u8, ParseError> {
63    s.as_bytes().get(0).copied().ok_or(perr(None, Empty))
64}
65
66/// Returns the index of the first non-underscore, non-decimal digit in `input`,
67/// or the `input.len()` if all characters are decimal digits.
68pub(crate) fn end_dec_digits(input: &str) -> usize {
69    input.bytes()
70        .position(|b| !matches!(b, b'_' | b'0'..=b'9'))
71        .unwrap_or(input.len())
72}
73
74pub(crate) fn hex_digit_value(digit: u8) -> Option<u8> {
75    match digit {
76        b'0'..=b'9' => Some(digit - b'0'),
77        b'a'..=b'f' => Some(digit - b'a' + 10),
78        b'A'..=b'F' => Some(digit - b'A' + 10),
79        _ => None,
80    }
81}