use crate::{ BoolLit, Buffer, ByteLit, ByteStringLit, CharLit, ParseError, FloatLit, IntegerLit, Literal, StringLit, err::{perr, ParseErrorKind::*}, }; impl Literal { /// Parses the given input as a Rust literal. pub fn parse(input: B) -> Result { let (first, rest) = input.as_bytes().split_first().ok_or(perr(None, Empty))?; let second = input.as_bytes().get(1).copied(); match first { b'f' if &*input == "false" => Ok(Self::Bool(BoolLit::False)), b't' if &*input == "true" => Ok(Self::Bool(BoolLit::True)), // A number literal (integer or float). b'0'..=b'9' => { // To figure out whether this is a float or integer, we do some // quick inspection here. Yes, this is technically duplicate // work with what is happening in the integer/float parse // methods, but it makes the code way easier for now and won't // be a huge performance loss. let end = 1 + end_dec_digits(rest); match input.as_bytes().get(end) { // Potential chars in integer literals: b, o, x for base; u // and i for type suffix. None | Some(b'b') | Some(b'o') | Some(b'x') | Some(b'u') | Some(b'i') => IntegerLit::parse(input).map(Literal::Integer), // Potential chars for float literals: `.` as fractional // period, e and E as exponent start and f as type suffix. Some(b'.') | Some(b'e') | Some(b'E') | Some(b'f') => FloatLit::parse(input).map(Literal::Float), _ => Err(perr(end, UnexpectedChar)), } }, b'\'' => CharLit::parse(input).map(Literal::Char), b'"' | b'r' => StringLit::parse_impl(input).map(Literal::String), b'b' if second == Some(b'\'') => ByteLit::parse(input).map(Literal::Byte), b'b' if second == Some(b'r') || second == Some(b'"') => ByteStringLit::parse_impl(input).map(Literal::ByteString), _ => Err(perr(None, InvalidLiteral)), } } } pub(crate) fn first_byte_or_empty(s: &str) -> Result { s.as_bytes().get(0).copied().ok_or(perr(None, Empty)) } /// Returns the index of the first non-underscore, non-decimal digit in `input`, /// or the `input.len()` if all characters are decimal digits. pub(crate) fn end_dec_digits(input: &[u8]) -> usize { input.iter() .position(|b| !matches!(b, b'_' | b'0'..=b'9')) .unwrap_or(input.len()) } pub(crate) fn hex_digit_value(digit: u8) -> Option { match digit { b'0'..=b'9' => Some(digit - b'0'), b'a'..=b'f' => Some(digit - b'a' + 10), b'A'..=b'F' => Some(digit - b'A' + 10), _ => None, } }