1
use std::iter::Peekable;
2

            
3
1576
#[derive(PartialEq, Eq, Debug)]
4
pub(crate) struct ParseResult {
5
    pub(crate) num_const: NumConst,
6
    pub(crate) has_overflowed: bool,
7
}
8

            
9
1576
#[derive(PartialEq, Eq, Debug)]
10
pub(crate) enum NumConst {
11
    Int(IntegerConst),
12
}
13

            
14
1576
#[derive(PartialEq, Eq, Debug)]
15
pub(crate) struct IntegerConst {
16
    // TODO: Labels for suffixes.
17
    pub(crate) value: u64,
18
    pub(crate) is_unsigned: bool,
19
}
20

            
21
1833
pub(crate) fn parse_numeric_constant(token_lexeme: &str) -> ParseResult {
22
1833
    let attrs = parse_num_const_attributes(token_lexeme);
23
1833

            
24
1833
    // TODO(feroldi): Check whether it is an integer or floating point before
25
1833
    // evaluating.
26
1833
    let (value, has_overflowed) = eval_integer_constant(attrs);
27
1833

            
28
1833
    let integer = IntegerConst {
29
1833
        value,
30
1833
        is_unsigned: false,
31
1833
    };
32
1833

            
33
1833
    ParseResult {
34
1833
        num_const: NumConst::Int(integer),
35
1833
        has_overflowed,
36
1833
    }
37
1833
}
38

            
39
struct NumConstAttrs<'a> {
40
    radix: u64,
41
    digits: &'a [u8],
42
}
43

            
44
1833
fn parse_num_const_attributes(token_lexeme: &str) -> NumConstAttrs {
45
1833
    let mut seq = Seq::new(token_lexeme);
46
1833

            
47
1833
    let radix;
48
1833
    let digits_start;
49
1833
    let digits_end;
50
1833

            
51
1833
    if seq.peek() != b'0' {
52
788
        radix = 10;
53

            
54
11873
        while seq.peek().is_ascii_digit() {
55
11085
            seq.bump();
56
11085
        }
57

            
58
788
        (digits_start, digits_end) = (0, seq.pos);
59
        // TODO: parse suffix.
60
1045
    } else if token_lexeme.len() == 1 {
61
1
        radix = 10;
62
1
        (digits_start, digits_end) = (0, 1);
63
1
    } else {
64
1044
        seq.bump();
65

            
66
1044
        if matches!(seq.peek(), b'x' | b'X') {
67
522
            seq.bump();
68
522

            
69
522
            radix = 16;
70
522
            digits_start = 2;
71

            
72
9339
            while seq.peek().is_ascii_hexdigit() {
73
8817
                seq.bump();
74
8817
            }
75
        } else {
76
522
            radix = 8;
77
522
            digits_start = 1;
78

            
79
11878
            while seq.peek().is_ascii_octdigit() {
80
11356
                seq.bump();
81
11356
            }
82
        }
83

            
84
1044
        digits_end = seq.pos;
85
    }
86

            
87
1833
    NumConstAttrs {
88
1833
        radix,
89
1833
        digits: &token_lexeme.as_bytes()[digits_start..digits_end],
90
1833
    }
91
1833
}
92

            
93
1833
fn eval_integer_constant(attrs: NumConstAttrs) -> (u64, bool) {
94
1833
    let mut value = 0u64;
95
1833
    let mut has_overflowed = false;
96

            
97
33092
    for &digit in attrs.digits {
98
31259
        let (low_mul, overflowed) = value.overflowing_mul(attrs.radix);
99
31259
        has_overflowed |= overflowed;
100
31259
        value = low_mul;
101
31259

            
102
31259
        let (low_add, overflowed) = value.overflowing_add(convert_digit_to_int(digit));
103
31259
        has_overflowed |= overflowed;
104
31259
        value = low_add;
105
31259
    }
106

            
107
1833
    (value, has_overflowed)
108
1833
}
109

            
110
struct Seq<'a> {
111
    iter: Peekable<std::slice::Iter<'a, u8>>,
112
    pos: usize,
113
}
114

            
115
impl Seq<'_> {
116
1833
    fn new(token_lexeme: &str) -> Seq {
117
1833
        Seq {
118
1833
            iter: token_lexeme.as_bytes().iter().peekable(),
119
1833
            pos: 0,
120
1833
        }
121
1833
    }
122

            
123
35967
    fn peek(&mut self) -> u8 {
124
35967
        self.iter.peek().map(|&&c| c).unwrap_or(b'\0')
125
35967
    }
126

            
127
32824
    fn bump(&mut self) -> u8 {
128
32824
        match self.iter.next() {
129
32824
            Some(&ch) => {
130
32824
                self.pos += 1;
131
32824
                ch
132
            }
133
            None => b'\0',
134
        }
135
32824
    }
136
}
137

            
138
31259
fn convert_digit_to_int(digit: u8) -> u64 {
139
31259
    match digit {
140
31259
        b'0'..=b'9' => (digit - b'0') as u64,
141
2875
        b'a'..=b'f' => (digit - b'a') as u64 + 10,
142
2868
        b'A'..=b'F' => (digit - b'A') as u64 + 10,
143
        _ => unreachable!("should not convert '{}'", digit),
144
    }
145
31259
}