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