1 use std::fmt;
2 
3 use crate::{
4     Buffer, ParseError,
5     err::{perr, ParseErrorKind::*},
6     escape::unescape,
7     parse::first_byte_or_empty,
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)]
17 pub struct CharLit<B: Buffer> {
18     raw: B,
19     value: char,
20 }
21 
22 impl<B: Buffer> CharLit<B> {
23     /// Parses the input as a character literal. Returns an error if the input
24     /// is invalid or represents a different kind of literal.
parse(input: B) -> Result<Self, ParseError>25     pub fn parse(input: B) -> Result<Self, ParseError> {
26         match first_byte_or_empty(&input)? {
27             b'\'' => {
28                 let value = parse_impl(&input)?;
29                 Ok(Self { raw: input, value })
30             },
31             _ => Err(perr(0, DoesNotStartWithQuote)),
32         }
33     }
34 
35     /// Returns the character value that this literal represents.
value(&self) -> char36     pub fn value(&self) -> char {
37         self.value
38     }
39 
40     /// Returns the raw input that was passed to `parse`.
raw_input(&self) -> &str41     pub fn raw_input(&self) -> &str {
42         &self.raw
43     }
44 
45     /// Returns the raw input that was passed to `parse`, potentially owned.
into_raw_input(self) -> B46     pub fn into_raw_input(self) -> B {
47         self.raw
48     }
49 
50 }
51 
52 impl CharLit<&str> {
53     /// Makes a copy of the underlying buffer and returns the owned version of
54     /// `Self`.
to_owned(&self) -> CharLit<String>55     pub fn to_owned(&self) -> CharLit<String> {
56         CharLit {
57             raw: self.raw.to_owned(),
58             value: self.value,
59         }
60     }
61 }
62 
63 impl<B: Buffer> fmt::Display for CharLit<B> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result64     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65         f.pad(&self.raw)
66     }
67 }
68 
69 /// Precondition: first character in input must be `'`.
70 #[inline(never)]
parse_impl(input: &str) -> Result<char, ParseError>71 pub(crate) fn parse_impl(input: &str) -> Result<char, ParseError> {
72     if input.len() == 1 {
73         return Err(perr(None, UnterminatedCharLiteral));
74     }
75     if *input.as_bytes().last().unwrap() != b'\'' {
76         return Err(perr(None, UnterminatedCharLiteral));
77     }
78 
79     let inner = &input[1..input.len() - 1];
80     let first = inner.chars().nth(0).ok_or(perr(None, EmptyCharLiteral))?;
81     let (c, len) = match first {
82         '\'' => return Err(perr(1, UnescapedSingleQuote)),
83         '\n' | '\t' | '\r'
84             => return Err(perr(1, UnescapedSpecialWhitespace)),
85 
86         '\\' => unescape::<char>(inner, 1)?,
87         other => (other, other.len_utf8()),
88     };
89     let rest = &inner[len..];
90 
91     if !rest.is_empty() {
92         return Err(perr(len + 1..input.len() - 1, OverlongCharLiteral));
93     }
94 
95     Ok(c)
96 }
97 
98 #[cfg(test)]
99 mod tests;
100