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    pub fn split(repr: &str) -> Result<(Self, &str), String> {
307        const PREFIXES: [&str; 6] = ["0b", "0B", "0o", "0O", "0x", "0X"];
308
309        for s in PREFIXES {
310            if let Some(body) = repr.strip_prefix(s) {
311                return Ok((s.parse()?, body));
312            }
313        }
314
315        Ok((Self::Decimal, repr))
316    }
317
318    #[inline]
319    pub const fn to_u8(self) -> u8 {
320        self as u8
321    }
322}
323
324impl From<Radix> for u8 {
325    fn from(value: Radix) -> Self {
326        value.to_u8()
327    }
328}
329
330impl From<Radix> for u32 {
331    fn from(value: Radix) -> Self {
332        value as u32
333    }
334}
335
336impl std::str::FromStr for Radix {
337    type Err = String;
338
339    fn from_str(s: &str) -> Result<Self, Self::Err> {
340        match s {
341            "0b" | "0B" => Ok(Self::Binary),
342            "0o" | "0O" => Ok(Self::Octal),
343            "0x" | "0X" => Ok(Self::Hex),
344            "" => Ok(Self::Decimal),
345            v => Err(format!("expected integer literal radix, received {v}")),
346        }
347    }
348}
349
350/// The optional primitive-integer suffix of an integer literal.
351#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
352#[cfg_attr(feature = "serde", derive(serde::Serialize))]
353pub enum IntSuffix {
354    #[default]
355    None,
356    I8,
357    I16,
358    I32,
359    I64,
360    I128,
361    ISize,
362    U8,
363    U16,
364    U32,
365    U64,
366    U128,
367    USize,
368}
369
370impl IntSuffix {
371    pub fn split(repr: &str) -> Result<(&str, Self), String> {
372        const SUFFIXES: [&str; 12] = [
373            "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128", "isize",
374        ];
375
376        for s in SUFFIXES {
377            if let Some(body) = repr.strip_suffix(s) {
378                return Ok((body, s.parse()?));
379            }
380        }
381
382        Ok((repr, Self::None))
383    }
384
385    pub fn as_str(self) -> &'static str {
386        match self {
387            Self::None => "",
388            Self::I8 => "i8",
389            Self::I16 => "i16",
390            Self::I32 => "i32",
391            Self::I64 => "i64",
392            Self::I128 => "i128",
393            Self::ISize => "isize",
394            Self::U8 => "u8",
395            Self::U16 => "u16",
396            Self::U32 => "u32",
397            Self::U64 => "u64",
398            Self::U128 => "u128",
399            Self::USize => "usize",
400        }
401    }
402}
403
404impl std::fmt::Display for IntSuffix {
405    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
406        write!(f, "{}", self.as_str())
407    }
408}
409
410impl std::str::FromStr for IntSuffix {
411    type Err = String;
412
413    fn from_str(s: &str) -> Result<Self, Self::Err> {
414        match s {
415            "" => Ok(Self::None),
416            "i8" => Ok(Self::I8),
417            "i16" => Ok(Self::I16),
418            "i32" => Ok(Self::I32),
419            "i64" => Ok(Self::I64),
420            "i128" => Ok(Self::I128),
421            "isize" => Ok(Self::ISize),
422            "u8" => Ok(Self::U8),
423            "u16" => Ok(Self::U16),
424            "u32" => Ok(Self::U32),
425            "u64" => Ok(Self::U64),
426            "u128" => Ok(Self::U128),
427            "usize" => Ok(Self::USize),
428            v => Err(format!("expected integer literal suffix, received {v}")),
429        }
430    }
431}
432
433fn scan(c: Cursor<'_>) -> Result<Cursor<'_>, LexError> {
434    let first = c.first().ok_or(c.error())?;
435
436    if !first.is_ascii_digit() {
437        return c.error().into();
438    }
439
440    let mut cur = c;
441
442    if first == '0' {
443        let next = cur.advance();
444
445        match next.first() {
446            Some('x' | 'X') => {
447                cur = digits(next.advance(), |ch| ch.is_ascii_hexdigit())?;
448                return Ok(suffix(cur));
449            }
450            Some('o' | 'O') => {
451                cur = digits(next.advance(), |ch| matches!(ch, '0'..='7'))?;
452                return Ok(suffix(cur));
453            }
454            Some('b' | 'B') => {
455                cur = digits(next.advance(), |ch| matches!(ch, '0' | '1'))?;
456                return Ok(suffix(cur));
457            }
458            _ => {}
459        }
460    }
461
462    cur = digits(cur, |ch| ch.is_ascii_digit())?;
463
464    if cur.starts_with(".") {
465        let after_dot = cur.advance();
466
467        if let Some(ch) = after_dot.first() {
468            if ch.is_ascii_digit() {
469                cur = digits_opt(after_dot, |ch| ch.is_ascii_digit());
470            }
471        }
472    }
473
474    if let Some('e' | 'E') = cur.first() {
475        cur = cur.advance();
476
477        if let Some('+' | '-') = cur.first() {
478            cur = cur.advance();
479        }
480
481        cur = digits(cur, |ch| ch.is_ascii_digit())?;
482    }
483
484    Ok(suffix(cur))
485}
486
487fn digits(c: Cursor<'_>, pred: fn(char) -> bool) -> Result<Cursor<'_>, LexError> {
488    let mut cur = c;
489    let mut found = false;
490
491    loop {
492        match cur.first() {
493            Some('_') => cur = cur.advance(),
494            Some(ch) if pred(ch) => {
495                found = true;
496                cur = cur.advance_by(ch.len_utf8());
497            }
498            _ => break,
499        }
500    }
501
502    if !found {
503        return c.error().into();
504    }
505
506    Ok(cur)
507}
508
509fn digits_opt(c: Cursor<'_>, pred: fn(char) -> bool) -> Cursor<'_> {
510    digits(c, pred).unwrap_or(c)
511}
512
513fn suffix(c: Cursor<'_>) -> Cursor<'_> {
514    match c.first() {
515        Some(ch) if ch == '_' || unicode_ident::is_xid_start(ch) => {
516            c.advance_by(ch.len_utf8()).skip_while(unicode_ident::is_xid_continue)
517        }
518        _ => c,
519    }
520}