litrs/char/
mod.rs

1use std::fmt;
2
3use crate::{
4    Buffer, ParseError,
5    err::{perr, ParseErrorKind::*},
6    escape::unescape,
7    parse::{first_byte_or_empty, check_suffix},
8};
9
10
11/// A character literal, e.g. `'g'` or `'🦊'`.
12///
13/// See [the reference][ref] for more information.
14///
15/// [ref]: https://doc.rust-lang.org/reference/tokens.html#character-literals
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub struct CharLit<B: Buffer> {
18    raw: B,
19    /// Start index of the suffix or `raw.len()` if there is no suffix.
20    start_suffix: usize,
21    value: char,
22}
23
24impl<B: Buffer> CharLit<B> {
25    /// Parses the input as a character literal. Returns an error if the input
26    /// is invalid or represents a different kind of literal.
27    pub fn parse(input: B) -> Result<Self, ParseError> {
28        match first_byte_or_empty(&input)? {
29            b'\'' => {
30                let (value, start_suffix) = parse_impl(&input)?;
31                Ok(Self { raw: input, value, start_suffix })
32            },
33            _ => Err(perr(0, DoesNotStartWithQuote)),
34        }
35    }
36
37    /// Returns the character value that this literal represents.
38    pub fn value(&self) -> char {
39        self.value
40    }
41
42    /// The optional suffix. Returns `""` if the suffix is empty/does not exist.
43    pub fn suffix(&self) -> &str {
44        &(*self.raw)[self.start_suffix..]
45    }
46
47    /// Returns the raw input that was passed to `parse`.
48    pub fn raw_input(&self) -> &str {
49        &self.raw
50    }
51
52    /// Returns the raw input that was passed to `parse`, potentially owned.
53    pub fn into_raw_input(self) -> B {
54        self.raw
55    }
56
57}
58
59impl CharLit<&str> {
60    /// Makes a copy of the underlying buffer and returns the owned version of
61    /// `Self`.
62    pub fn to_owned(&self) -> CharLit<String> {
63        CharLit {
64            raw: self.raw.to_owned(),
65            start_suffix: self.start_suffix,
66            value: self.value,
67        }
68    }
69}
70
71impl<B: Buffer> fmt::Display for CharLit<B> {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        f.pad(&self.raw)
74    }
75}
76
77/// Precondition: first character in input must be `'`.
78#[inline(never)]
79pub(crate) fn parse_impl(input: &str) -> Result<(char, usize), ParseError> {
80    let first = input.chars().nth(1).ok_or(perr(None, UnterminatedCharLiteral))?;
81    let (c, len) = match first {
82        '\'' if input.chars().nth(2) == Some('\'') => return Err(perr(1, UnescapedSingleQuote)),
83        '\'' => return Err(perr(None, EmptyCharLiteral)),
84        '\n' | '\t' | '\r'
85            => return Err(perr(1, UnescapedSpecialWhitespace)),
86
87        '\\' => unescape::<char>(&input[1..], 1)?,
88        other => (other, other.len_utf8()),
89    };
90
91    match input[1 + len..].find('\'') {
92        Some(0) => {}
93        Some(_) => return Err(perr(None, OverlongCharLiteral)),
94        None => return Err(perr(None, UnterminatedCharLiteral)),
95    }
96
97    let start_suffix = 1 + len + 1;
98    let suffix = &input[start_suffix..];
99    check_suffix(suffix).map_err(|kind| perr(start_suffix, kind))?;
100
101    Ok((c, start_suffix))
102}
103
104#[cfg(test)]
105mod tests;