Skip to main content

moxy_token/lit/
byte.rs

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
159/// Decode a single (possibly escaped) char from a char-literal inner body.
160fn 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}