1use std::fmt;
2
3use crate::{
4 err::{perr, ParseErrorKind::*},
5 escape::unescape,
6 parse::{check_suffix, first_byte_or_empty},
7 Buffer, ParseError,
8};
9
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub struct CharLit<B: Buffer> {
18 raw: B,
19 start_suffix: usize,
21 value: char,
22}
23
24impl<B: Buffer> CharLit<B> {
25 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 pub fn value(&self) -> char {
39 self.value
40 }
41
42 pub fn suffix(&self) -> &str {
44 &(*self.raw)[self.start_suffix..]
45 }
46
47 pub fn raw_input(&self) -> &str {
49 &self.raw
50 }
51
52 pub fn into_raw_input(self) -> B {
54 self.raw
55 }
56}
57
58impl CharLit<&str> {
59 pub fn to_owned(&self) -> CharLit<String> {
62 CharLit {
63 raw: self.raw.to_owned(),
64 start_suffix: self.start_suffix,
65 value: self.value,
66 }
67 }
68}
69
70impl<B: Buffer> fmt::Display for CharLit<B> {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 f.pad(&self.raw)
73 }
74}
75
76#[inline(never)]
78pub(crate) fn parse_impl(input: &str) -> Result<(char, usize), ParseError> {
79 let first = input.chars().nth(1).ok_or(perr(None, UnterminatedCharLiteral))?;
80 let (c, len) = match first {
81 '\'' if input.chars().nth(2) == Some('\'') => return Err(perr(1, UnescapedSingleQuote)),
82 '\'' => return Err(perr(None, EmptyCharLiteral)),
83 '\n' | '\t' | '\r' => return Err(perr(1, UnescapedSpecialWhitespace)),
84
85 '\\' => {
86 let (v, len) = unescape(&input[1..], true, false, true).map_err(|e| e.offset_span(1))?;
87 (v.unwrap_char(), len)
88 }
89 other => (other, other.len_utf8()),
90 };
91
92 match input[1 + len..].find('\'') {
93 Some(0) => {}
94 Some(_) => return Err(perr(None, OverlongCharLiteral)),
95 None => return Err(perr(None, UnterminatedCharLiteral)),
96 }
97
98 let start_suffix = 1 + len + 1;
99 let suffix = &input[start_suffix..];
100 check_suffix(suffix).map_err(|kind| perr(start_suffix, kind))?;
101
102 Ok((c, start_suffix))
103}
104
105#[cfg(test)]
106mod tests;