Skip to main content

moxy_token/lit/
int.rs

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