1 use std::fmt;
2
3 use 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)]
17 pub 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
24 impl<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.
parse(input: B) -> Result<Self, ParseError>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.
value(&self) -> char38 pub fn value(&self) -> char {
39 self.value
40 }
41
42 /// The optional suffix. Returns `""` if the suffix is empty/does not exist.
suffix(&self) -> &str43 pub fn suffix(&self) -> &str {
44 &(*self.raw)[self.start_suffix..]
45 }
46
47 /// Returns the raw input that was passed to `parse`.
raw_input(&self) -> &str48 pub fn raw_input(&self) -> &str {
49 &self.raw
50 }
51
52 /// Returns the raw input that was passed to `parse`, potentially owned.
into_raw_input(self) -> B53 pub fn into_raw_input(self) -> B {
54 self.raw
55 }
56
57 }
58
59 impl CharLit<&str> {
60 /// Makes a copy of the underlying buffer and returns the owned version of
61 /// `Self`.
to_owned(&self) -> CharLit<String>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
71 impl<B: Buffer> fmt::Display for CharLit<B> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result72 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)]
parse_impl(input: &str) -> Result<(char, usize), ParseError>79 pub(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)]
105 mod tests;
106