Skip to main content

moxy_token/lit/
int.rs

1use crate::lex::Cursor;
2use crate::lit::Lit;
3use crate::{LexError, Scan, Span, Spanner, ToTokens, TokenStream, TokenTree};
4
5/// A parsed Rust int literal token.
6#[derive(Debug, Default, Clone)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(into = "String"))]
8pub struct LitInt {
9    span: Span,
10    repr: Box<str>,
11    radix: Radix,
12    suffix: IntSuffix,
13    value: u128,
14}
15
16impl LitInt {
17    #[inline]
18    pub fn span(&self) -> Span {
19        self.span
20    }
21
22    #[inline]
23    pub fn repr(&self) -> &str {
24        &self.repr
25    }
26
27    #[inline]
28    pub fn radix(&self) -> Radix {
29        self.radix
30    }
31
32    #[inline]
33    pub fn suffix(&self) -> IntSuffix {
34        self.suffix
35    }
36
37    #[inline]
38    pub fn set_span(&mut self, span: Span) {
39        self.span = span;
40    }
41
42    #[inline]
43    pub fn value(&self) -> u128 {
44        self.value
45    }
46}
47
48impl PartialEq for LitInt {
49    fn eq(&self, other: &Self) -> bool {
50        self.value == other.value
51    }
52}
53
54impl Eq for LitInt {}
55
56impl std::hash::Hash for LitInt {
57    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
58        self.value.hash(state);
59    }
60}
61
62impl std::fmt::Display for LitInt {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        write!(f, "{}", self.repr)
65    }
66}
67
68impl std::str::FromStr for LitInt {
69    type Err = LexError;
70
71    fn from_str(s: &str) -> Result<Self, Self::Err> {
72        let cursor = Cursor::new(s, 0);
73        let (_, value) = Self::scan(cursor)?;
74        Ok(value)
75    }
76}
77
78impl Spanner for LitInt {
79    fn span(&self) -> Span {
80        self.span
81    }
82}
83
84impl ToTokens for LitInt {
85    fn to_tokens(&self, tokens: &mut TokenStream) {
86        tokens.extend_one(TokenTree::Literal(self.clone().into()));
87    }
88}
89
90impl Scan for LitInt {
91    fn scan(cursor: Cursor<'_>) -> Result<(Cursor<'_>, Self), LexError> {
92        let end = scan(cursor)?;
93        let len = end.offset() as usize - cursor.offset() as usize;
94        let repr = &cursor.rest()[..len];
95        let span = cursor.span_to(&end);
96        let stripped_repr = repr.replace("_", "");
97        let (stripped_repr, suffix) = IntSuffix::split(&stripped_repr).map_err(|msg| cursor.error().message(msg))?;
98        let (radix, stripped_repr) = Radix::split(stripped_repr).map_err(|msg| cursor.error().message(msg))?;
99        let value = u128::from_str_radix(stripped_repr, radix.into()).map_err(|err| cursor.error().message(err))?;
100
101        Ok((
102            end,
103            Self {
104                span,
105                repr: repr.into(),
106                radix,
107                suffix,
108                value,
109            },
110        ))
111    }
112}
113
114impl From<LitInt> for Lit {
115    fn from(value: LitInt) -> Self {
116        Self::Int(value)
117    }
118}
119
120#[cfg(feature = "serde")]
121impl From<LitInt> for String {
122    fn from(value: LitInt) -> Self {
123        value.repr.into_string()
124    }
125}
126
127impl Lit {
128    pub fn u8_suffixed(value: u8) -> Self {
129        Self::Int(LitInt {
130            repr: value.to_string().into(),
131            suffix: IntSuffix::U8,
132            value: value as u128,
133            ..Default::default()
134        })
135    }
136
137    pub fn u8_unsuffixed(value: u8) -> Self {
138        Self::Int(LitInt {
139            repr: value.to_string().into(),
140            value: value as u128,
141            ..Default::default()
142        })
143    }
144
145    pub fn u16_suffixed(value: u16) -> Self {
146        Self::Int(LitInt {
147            repr: value.to_string().into(),
148            suffix: IntSuffix::U16,
149            value: value as u128,
150            ..Default::default()
151        })
152    }
153
154    pub fn u16_unsuffixed(value: u16) -> Self {
155        Self::Int(LitInt {
156            repr: value.to_string().into(),
157            value: value as u128,
158            ..Default::default()
159        })
160    }
161
162    pub fn u32_suffixed(value: u32) -> Self {
163        Self::Int(LitInt {
164            repr: value.to_string().into(),
165            suffix: IntSuffix::U32,
166            value: value as u128,
167            ..Default::default()
168        })
169    }
170
171    pub fn u32_unsuffixed(value: u32) -> Self {
172        Self::Int(LitInt {
173            repr: value.to_string().into(),
174            value: value as u128,
175            ..Default::default()
176        })
177    }
178
179    pub fn u64_suffixed(value: u64) -> Self {
180        Self::Int(LitInt {
181            repr: value.to_string().into(),
182            suffix: IntSuffix::U64,
183            value: value as u128,
184            ..Default::default()
185        })
186    }
187
188    pub fn u64_unsuffixed(value: u64) -> Self {
189        Self::Int(LitInt {
190            repr: value.to_string().into(),
191            value: value as u128,
192            ..Default::default()
193        })
194    }
195
196    pub fn usize_suffixed(value: usize) -> Self {
197        Self::Int(LitInt {
198            repr: value.to_string().into(),
199            suffix: IntSuffix::USize,
200            value: value as u128,
201            ..Default::default()
202        })
203    }
204
205    pub fn usize_unsuffixed(value: usize) -> Self {
206        Self::Int(LitInt {
207            repr: value.to_string().into(),
208            value: value as u128,
209            ..Default::default()
210        })
211    }
212
213    pub fn i8_suffixed(value: i8) -> Self {
214        Self::Int(LitInt {
215            repr: value.to_string().into(),
216            suffix: IntSuffix::I8,
217            value: value as u128,
218            ..Default::default()
219        })
220    }
221
222    pub fn i8_unsuffixed(value: i8) -> Self {
223        Self::Int(LitInt {
224            repr: value.to_string().into(),
225            value: value as u128,
226            ..Default::default()
227        })
228    }
229
230    pub fn i16_suffixed(value: i16) -> Self {
231        Self::Int(LitInt {
232            repr: value.to_string().into(),
233            suffix: IntSuffix::I16,
234            value: value as u128,
235            ..Default::default()
236        })
237    }
238
239    pub fn i16_unsuffixed(value: i16) -> Self {
240        Self::Int(LitInt {
241            repr: value.to_string().into(),
242            value: value as u128,
243            ..Default::default()
244        })
245    }
246
247    pub fn i32_suffixed(value: i32) -> Self {
248        Self::Int(LitInt {
249            repr: value.to_string().into(),
250            suffix: IntSuffix::I32,
251            value: value as u128,
252            ..Default::default()
253        })
254    }
255
256    pub fn i32_unsuffixed(value: i32) -> Self {
257        Self::Int(LitInt {
258            repr: value.to_string().into(),
259            value: value as u128,
260            ..Default::default()
261        })
262    }
263
264    pub fn i64_suffixed(value: i64) -> Self {
265        Self::Int(LitInt {
266            repr: value.to_string().into(),
267            suffix: IntSuffix::I64,
268            value: value as u128,
269            ..Default::default()
270        })
271    }
272
273    pub fn i64_unsuffixed(value: i64) -> Self {
274        Self::Int(LitInt {
275            repr: value.to_string().into(),
276            value: value as u128,
277            ..Default::default()
278        })
279    }
280
281    pub fn isize_suffixed(value: isize) -> Self {
282        Self::Int(LitInt {
283            repr: value.to_string().into(),
284            suffix: IntSuffix::ISize,
285            value: value as u128,
286            ..Default::default()
287        })
288    }
289
290    pub fn isize_unsuffixed(value: isize) -> Self {
291        Self::Int(LitInt {
292            repr: value.to_string().into(),
293            value: value as u128,
294            ..Default::default()
295        })
296    }
297}
298
299/// The radix used by an integer literal.
300#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
301#[cfg_attr(feature = "serde", derive(serde::Serialize))]
302#[repr(u8)]
303pub enum Radix {
304    Binary = 2,
305    Octal = 8,
306    #[default]
307    Decimal = 10,
308    Hex = 16,
309}
310
311impl Radix {
312    pub fn split(repr: &str) -> Result<(Self, &str), String> {
313        const PREFIXES: [&str; 6] = ["0b", "0B", "0o", "0O", "0x", "0X"];
314
315        for s in PREFIXES {
316            if let Some(body) = repr.strip_prefix(s) {
317                return Ok((s.parse()?, body));
318            }
319        }
320
321        Ok((Self::Decimal, repr))
322    }
323
324    #[inline]
325    pub const fn to_u8(self) -> u8 {
326        self as u8
327    }
328}
329
330impl From<Radix> for u8 {
331    fn from(value: Radix) -> Self {
332        value.to_u8()
333    }
334}
335
336impl From<Radix> for u32 {
337    fn from(value: Radix) -> Self {
338        value as u32
339    }
340}
341
342impl std::str::FromStr for Radix {
343    type Err = String;
344
345    fn from_str(s: &str) -> Result<Self, Self::Err> {
346        match s {
347            "0b" | "0B" => Ok(Self::Binary),
348            "0o" | "0O" => Ok(Self::Octal),
349            "0x" | "0X" => Ok(Self::Hex),
350            "" => Ok(Self::Decimal),
351            v => Err(format!("expected integer literal radix, received {v}")),
352        }
353    }
354}
355
356/// The optional primitive-integer suffix of an integer literal.
357#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
358#[cfg_attr(feature = "serde", derive(serde::Serialize))]
359pub enum IntSuffix {
360    #[default]
361    None,
362    I8,
363    I16,
364    I32,
365    I64,
366    I128,
367    ISize,
368    U8,
369    U16,
370    U32,
371    U64,
372    U128,
373    USize,
374}
375
376impl IntSuffix {
377    pub fn split(repr: &str) -> Result<(&str, Self), String> {
378        const SUFFIXES: [&str; 12] = [
379            "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128", "isize",
380        ];
381
382        for s in SUFFIXES {
383            if let Some(body) = repr.strip_suffix(s) {
384                return Ok((body, s.parse()?));
385            }
386        }
387
388        Ok((repr, Self::None))
389    }
390
391    pub fn as_str(self) -> &'static str {
392        match self {
393            Self::None => "",
394            Self::I8 => "i8",
395            Self::I16 => "i16",
396            Self::I32 => "i32",
397            Self::I64 => "i64",
398            Self::I128 => "i128",
399            Self::ISize => "isize",
400            Self::U8 => "u8",
401            Self::U16 => "u16",
402            Self::U32 => "u32",
403            Self::U64 => "u64",
404            Self::U128 => "u128",
405            Self::USize => "usize",
406        }
407    }
408}
409
410impl std::fmt::Display for IntSuffix {
411    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
412        write!(f, "{}", self.as_str())
413    }
414}
415
416impl std::str::FromStr for IntSuffix {
417    type Err = String;
418
419    fn from_str(s: &str) -> Result<Self, Self::Err> {
420        match s {
421            "" => Ok(Self::None),
422            "i8" => Ok(Self::I8),
423            "i16" => Ok(Self::I16),
424            "i32" => Ok(Self::I32),
425            "i64" => Ok(Self::I64),
426            "i128" => Ok(Self::I128),
427            "isize" => Ok(Self::ISize),
428            "u8" => Ok(Self::U8),
429            "u16" => Ok(Self::U16),
430            "u32" => Ok(Self::U32),
431            "u64" => Ok(Self::U64),
432            "u128" => Ok(Self::U128),
433            "usize" => Ok(Self::USize),
434            v => Err(format!("expected integer literal suffix, received {v}")),
435        }
436    }
437}
438
439fn scan(c: Cursor<'_>) -> Result<Cursor<'_>, LexError> {
440    let first = c.first().ok_or(c.error())?;
441
442    if !first.is_ascii_digit() {
443        return c.error().into();
444    }
445
446    let mut cur = c;
447
448    if first == '0' {
449        let next = cur.advance();
450
451        match next.first() {
452            Some('x' | 'X') => {
453                cur = digits(next.advance(), |ch| ch.is_ascii_hexdigit())?;
454                return Ok(suffix(cur));
455            }
456            Some('o' | 'O') => {
457                cur = digits(next.advance(), |ch| matches!(ch, '0'..='7'))?;
458                return Ok(suffix(cur));
459            }
460            Some('b' | 'B') => {
461                cur = digits(next.advance(), |ch| matches!(ch, '0' | '1'))?;
462                return Ok(suffix(cur));
463            }
464            _ => {}
465        }
466    }
467
468    cur = digits(cur, |ch| ch.is_ascii_digit())?;
469
470    if cur.starts_with(".") {
471        let after_dot = cur.advance();
472
473        if let Some(ch) = after_dot.first() {
474            if ch.is_ascii_digit() {
475                cur = digits_opt(after_dot, |ch| ch.is_ascii_digit());
476            }
477        }
478    }
479
480    if let Some('e' | 'E') = cur.first() {
481        cur = cur.advance();
482
483        if let Some('+' | '-') = cur.first() {
484            cur = cur.advance();
485        }
486
487        cur = digits(cur, |ch| ch.is_ascii_digit())?;
488    }
489
490    Ok(suffix(cur))
491}
492
493fn digits(c: Cursor<'_>, pred: fn(char) -> bool) -> Result<Cursor<'_>, LexError> {
494    let mut cur = c;
495    let mut found = false;
496
497    loop {
498        match cur.first() {
499            Some('_') => cur = cur.advance(),
500            Some(ch) if pred(ch) => {
501                found = true;
502                cur = cur.advance_by(ch.len_utf8());
503            }
504            _ => break,
505        }
506    }
507
508    if !found {
509        return c.error().into();
510    }
511
512    Ok(cur)
513}
514
515fn digits_opt(c: Cursor<'_>, pred: fn(char) -> bool) -> Cursor<'_> {
516    digits(c, pred).unwrap_or(c)
517}
518
519fn suffix(c: Cursor<'_>) -> Cursor<'_> {
520    match c.first() {
521        Some(ch) if ch == '_' || unicode_ident::is_xid_start(ch) => {
522            c.advance_by(ch.len_utf8()).skip_while(unicode_ident::is_xid_continue)
523        }
524        _ => c,
525    }
526}