Skip to main content

moxy_token/lit/
int.rs

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