1use crate::lex::{Cursor, LexError, Scan};
2use crate::lit::Lit;
3use crate::{Span, Spanner};
4
5#[derive(Debug, Clone)]
6#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(into = "String"))]
7pub struct LitByte {
8 value: u8,
9 repr: Box<str>,
10 span: Span,
11}
12
13impl LitByte {
14 #[inline]
15 pub(crate) fn from_parts(value: u8, repr: &str, span: Span) -> Self {
16 Self {
17 value,
18 repr: repr.into(),
19 span,
20 }
21 }
22
23 #[inline]
24 pub fn value(&self) -> u8 {
25 self.value
26 }
27
28 #[inline]
29 pub fn repr(&self) -> &str {
30 &self.repr
31 }
32
33 #[inline]
34 pub fn span(&self) -> Span {
35 self.span
36 }
37
38 #[inline]
39 pub fn set_span(&mut self, span: Span) {
40 self.span = span;
41 }
42}
43
44impl PartialEq for LitByte {
45 fn eq(&self, other: &Self) -> bool {
46 self.value == other.value
47 }
48}
49
50impl Eq for LitByte {}
51
52impl std::hash::Hash for LitByte {
53 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
54 self.value.hash(state);
55 }
56}
57
58impl std::fmt::Display for LitByte {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 f.write_str(&self.repr)
61 }
62}
63
64impl Spanner for LitByte {
65 fn span(&self) -> Span {
66 self.span
67 }
68}
69
70impl Scan for LitByte {
71 fn scan(cursor: Cursor<'_>) -> Result<(Cursor<'_>, Self), LexError> {
72 let end = scan(cursor, "b'")?;
73 let len = end.offset() as usize - cursor.offset() as usize;
74 let repr = &cursor.rest()[..len];
75 let span = cursor.span_to(&end);
76 let inner = repr.strip_prefix("b'").and_then(|r| r.strip_suffix('\''));
77
78 match inner.and_then(decode_one_char) {
79 Some(value) => Ok((end, Self::from_parts(value as u8, repr, span))),
80 _ => cursor.error().into(),
81 }
82 }
83}
84
85impl From<LitByte> for Lit {
86 fn from(value: LitByte) -> Self {
87 Self::Byte(value)
88 }
89}
90
91#[cfg(feature = "serde")]
92impl From<LitByte> for String {
93 fn from(value: LitByte) -> Self {
94 value.repr.into_string()
95 }
96}
97
98fn scan<'a>(c: Cursor<'a>, open: &str) -> Result<Cursor<'a>, LexError> {
99 if !c.starts_with(open) {
100 return c.error().into();
101 }
102
103 let start = c;
104 let c = c.advance_by(open.len());
105 let c = match c.first() {
106 None | Some('\'') => return start.error().into(),
107 Some('\\') => escape(c.advance())?,
108 Some(ch) => c.advance_by(ch.len_utf8()),
109 };
110
111 if !c.starts_with("'") {
112 return start.error().into();
113 }
114
115 Ok(c.advance())
116}
117
118fn escape(c: Cursor<'_>) -> Result<Cursor<'_>, LexError> {
119 match c.first() {
120 None => c.error().into(),
121 Some('n' | 'r' | 't' | '\\' | '\'' | '"' | '0') => Ok(c.advance()),
122 Some('x') => {
123 let c = c.advance();
124 let c = hex_digit(c)?;
125 hex_digit(c)
126 }
127 Some('u') => {
128 let c = c.advance();
129
130 if !c.starts_with("{") {
131 return c.error().into();
132 }
133
134 let mut c = c.advance();
135 let mut count = 0;
136
137 loop {
138 match c.first() {
139 Some('}') if count > 0 => return Ok(c.advance()),
140 Some(ch) if ch.is_ascii_hexdigit() && count < 6 => {
141 count += 1;
142 c = c.advance();
143 }
144 _ => return c.error().into(),
145 }
146 }
147 }
148 _ => c.error().into(),
149 }
150}
151
152fn hex_digit(c: Cursor<'_>) -> Result<Cursor<'_>, LexError> {
153 match c.first() {
154 Some(ch) if ch.is_ascii_hexdigit() => Ok(c.advance()),
155 _ => c.error().into(),
156 }
157}
158
159fn decode_one_char(inner: &str) -> Option<char> {
161 let mut chars = inner.chars();
162 let value = match chars.next()? {
163 '\\' => decode_escape(&mut chars)?,
164 c => c,
165 };
166
167 if chars.next().is_some() {
168 return None;
169 }
170
171 Some(value)
172}
173
174fn decode_escape(chars: &mut std::str::Chars<'_>) -> Option<char> {
175 match chars.next()? {
176 'n' => Some('\n'),
177 'r' => Some('\r'),
178 't' => Some('\t'),
179 '\\' => Some('\\'),
180 '\'' => Some('\''),
181 '"' => Some('"'),
182 '0' => Some('\0'),
183 'x' => {
184 let hi = chars.next()?.to_digit(16)?;
185 let lo = chars.next()?.to_digit(16)?;
186 char::from_u32(hi * 16 + lo)
187 }
188 'u' => {
189 if chars.next()? != '{' {
190 return None;
191 }
192
193 let mut value = 0u32;
194 let mut count = 0;
195
196 for c in chars.by_ref() {
197 if c == '}' {
198 break;
199 }
200
201 value = value.checked_mul(16)?.checked_add(c.to_digit(16)?)?;
202 count += 1;
203
204 if count > 6 {
205 return None;
206 }
207 }
208
209 char::from_u32(value)
210 }
211 _ => None,
212 }
213}