1use crate::common::*;
2
3#[derive(Debug, Diagnostic, thiserror::Error)]
4pub enum ParseNumberError {
5 #[error("expected unsigned number but found sign")]
6 #[diagnostic()]
7 UnexpectedSign {
8 #[label("occurs here")]
9 span: SourceSpan,
10 },
11 #[error("expected hexadecimal number with 0x prefix, but no prefix was present")]
12 #[diagnostic()]
13 MissingPrefix {
14 #[label("occurs here")]
15 span: SourceSpan,
16 },
17 #[error("input string has incorrect precision: expected {precision} got {actual}")]
18 #[diagnostic()]
19 PrecisionMismatch {
20 #[label("occurs here")]
21 span: SourceSpan,
22 precision: u8,
23 actual: usize,
24 },
25 #[error("input string has incorrect numeric format: {reason:?}")]
26 #[diagnostic()]
27 InvalidFormat {
28 #[label("occurs here")]
29 span: SourceSpan,
30 reason: core::num::IntErrorKind,
31 },
32}
33
34#[derive(Debug, Clone)]
35pub struct Number {
36 pub span: SourceSpan,
37 pub format: NumberFormat,
38 pub value: i64,
39}
40impl Number {
41 pub fn new(span: SourceSpan, value: i64) -> Self {
42 Self {
43 span,
44 format: NumberFormat::default(),
45 value,
46 }
47 }
48
49 pub fn new_with_format(span: SourceSpan, value: i64, format: NumberFormat) -> Self {
50 Self {
51 span,
52 format,
53 value,
54 }
55 }
56
57 pub fn parse_with_format(
58 input: Span<&str>,
59 format: NumberFormat,
60 ) -> Result<Self, ParseNumberError> {
61 let (span, input) = input.into_parts();
62
63 match format {
64 NumberFormat::Unsigned { precision } => {
65 if input.starts_with(['-', '+']) {
66 return Err(ParseNumberError::UnexpectedSign { span });
67 }
68 let value = input
69 .parse::<i64>()
70 .map(|value| Self {
71 span,
72 format,
73 value,
74 })
75 .map_err(|error| ParseNumberError::InvalidFormat {
76 span,
77 reason: error.kind().clone(),
78 })?;
79 if precision == 0 {
80 return Ok(value);
81 }
82 if input.len() != precision as usize {
83 Err(ParseNumberError::PrecisionMismatch {
84 span,
85 precision,
86 actual: input.len(),
87 })
88 } else {
89 Ok(value)
90 }
91 }
92 NumberFormat::Signed { precision } => {
93 let value = input
94 .parse::<i64>()
95 .map(|value| Self {
96 span,
97 format,
98 value,
99 })
100 .map_err(|error| ParseNumberError::InvalidFormat {
101 span,
102 reason: error.kind().clone(),
103 })?;
104 if precision == 0 {
105 return Ok(value);
106 }
107 let actual = if let Some(input) = input.strip_prefix(['-', '+']) {
108 input.len()
109 } else {
110 input.len()
111 };
112 if actual != precision as usize {
113 Err(ParseNumberError::PrecisionMismatch {
114 span,
115 precision,
116 actual,
117 })
118 } else {
119 Ok(value)
120 }
121 }
122 NumberFormat::Hex {
123 require_prefix,
124 precision,
125 } => {
126 let input = match input.strip_prefix("0x") {
127 None if require_prefix => return Err(ParseNumberError::MissingPrefix { span }),
128 None => input,
129 Some(input) => input,
130 };
131 let value = i64::from_str_radix(input, 16)
132 .map(|value| Self {
133 span,
134 format,
135 value,
136 })
137 .map_err(|error| ParseNumberError::InvalidFormat {
138 span,
139 reason: error.kind().clone(),
140 })?;
141 if input.len() != precision as usize {
142 Err(ParseNumberError::PrecisionMismatch {
143 span,
144 precision,
145 actual: input.len(),
146 })
147 } else {
148 Ok(value)
149 }
150 }
151 }
152 }
153}
154impl Eq for Number {}
155impl PartialEq for Number {
156 fn eq(&self, other: &Self) -> bool {
157 self.value == other.value && self.format == other.format
158 }
159}
160impl PartialOrd for Number {
161 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
162 Some(self.value.cmp(&other.value))
163 }
164}
165impl Ord for Number {
166 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
167 self.value.cmp(&other.value)
168 }
169}
170impl Spanned for Number {
171 fn span(&self) -> SourceSpan {
172 self.span
173 }
174}
175impl fmt::Display for Number {
176 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
177 let value = self.value;
178 match self.format {
179 NumberFormat::Unsigned { precision: 0 } => write!(f, "{}", value as u64),
180 NumberFormat::Unsigned { precision: n } => {
181 write!(f, "{:0n$}", value as u64, n = n as usize)
182 }
183 NumberFormat::Signed { precision: 0 } => write!(f, "{value}"),
184 NumberFormat::Signed { precision: n } => write!(f, "{value:0n$}", n = n as usize),
185 NumberFormat::Hex {
186 require_prefix: true,
187 precision: 0,
188 } => write!(f, "{value:#x?}"),
189 NumberFormat::Hex {
190 require_prefix: false,
191 precision: 0,
192 } => write!(f, "{value:x?}"),
193 NumberFormat::Hex {
194 require_prefix: true,
195 precision: n,
196 } => write!(f, "{value:#0n$x?}", n = n as usize),
197 NumberFormat::Hex {
198 require_prefix: false,
199 precision: n,
200 } => write!(f, "{value:0n$x?}", n = n as usize),
201 }
202 }
203}
204
205#[derive(Debug, Copy, Clone, PartialEq, Eq)]
206#[repr(u8)]
207pub enum NumberFormat {
208 Unsigned { precision: u8 },
209 Signed { precision: u8 },
210 Hex { precision: u8, require_prefix: bool },
211}
212impl NumberFormat {
213 pub fn describe(&self) -> Cow<'static, str> {
214 match self {
215 Self::Unsigned { precision: 0 } => Cow::Borrowed("any unsigned 64-bit integer"),
216 Self::Unsigned { precision } => {
217 Cow::Owned(format!("an unsigned {precision}-digit 64-bit integer"))
218 }
219 Self::Signed { precision: 0 } => Cow::Borrowed("any signed 64-bit integer"),
220 Self::Signed { precision } => {
221 Cow::Owned(format!("a signed {precision}-digit 64-bit integer"))
222 }
223 Self::Hex {
224 require_prefix: true,
225 precision: 0,
226 } => Cow::Borrowed("any 64-bit integer in hex format, prefixed with 0x"),
227 Self::Hex {
228 require_prefix: false,
229 precision: 0,
230 } => Cow::Borrowed("any 64-bit integer in hex format"),
231 Self::Hex {
232 require_prefix: true,
233 precision,
234 } => Cow::Owned(format!(
235 "a {precision}-digit 64-bit integer in hex format, prefixed with 0x"
236 )),
237 Self::Hex { precision, .. } => {
238 Cow::Owned(format!("a {precision}-digit 64-bit integer in hex format"))
239 }
240 }
241 }
242
243 pub fn is_signed(&self) -> bool {
244 matches!(self, Self::Signed { .. })
245 }
246
247 pub fn is_hex(&self) -> bool {
248 matches!(self, Self::Hex { .. })
249 }
250
251 pub fn precision(&self) -> usize {
252 match self {
253 Self::Unsigned { precision }
254 | Self::Signed { precision }
255 | Self::Hex { precision, .. } => *precision as usize,
256 }
257 }
258
259 pub fn discriminant(&self) -> u8 {
260 unsafe { *<*const _>::from(self).cast::<u8>() }
264 }
265
266 pub fn pattern_nocapture(&self) -> Cow<'static, str> {
267 match self {
272 NumberFormat::Signed { precision: 0 } => Cow::Borrowed(r"(?:[-+]?[0-9]{1,19})"),
273 NumberFormat::Signed { precision } => {
274 Cow::Owned(format!("(?:[-+]?[0-9]{{{precision}}})"))
275 }
276 NumberFormat::Unsigned { precision: 0 } => Cow::Borrowed(r"(?:[0-9]{1,19})"),
277 NumberFormat::Unsigned { precision } => Cow::Owned(format!("(?:[0-9]{{{precision}}})")),
278 NumberFormat::Hex {
281 require_prefix: true,
282 precision: 0,
283 } => Cow::Borrowed(r"(?:0x[A-Fa-f0-9]{1,16})"),
284 NumberFormat::Hex {
285 require_prefix: true,
286 precision,
287 } => Cow::Owned(format!("(?:0x[A-Fa-f0-9]{{{precision}}})")),
288 NumberFormat::Hex {
289 require_prefix: false,
290 precision: 0,
291 } => Cow::Borrowed(r"(?:[A-Fa-f0-9]{1,16})"),
292 NumberFormat::Hex {
293 require_prefix: false,
294 precision,
295 } => Cow::Owned(format!("(?:[A-Fa-f0-9]{{{precision}}})")),
296 }
297 }
298
299 pub fn pattern(&self, group_name_override: Option<&str>) -> Cow<'static, str> {
300 let group_name = group_name_override.unwrap_or("digits");
305 match self {
306 NumberFormat::Signed { precision: 0 } => match group_name_override {
307 None => Cow::Borrowed(r"(?P<digits>[-+]?[0-9]{1,19})"),
308 Some(group_name) => Cow::Owned(format!("(?P<{group_name}>[-+]?[0-9]{{1,19}})")),
309 },
310 NumberFormat::Signed { precision } => {
311 Cow::Owned(format!("(?P<{group_name}>[-+]?[0-9]{{{precision}}})"))
312 }
313 NumberFormat::Unsigned { precision: 0 } => match group_name_override {
314 None => Cow::Borrowed(r"(?P<digits>[0-9]{1,19})"),
315 Some(group_name) => Cow::Owned(format!("(?P<{group_name}>[0-9]{{1,19}})")),
316 },
317 NumberFormat::Unsigned { precision } => {
318 Cow::Owned(format!("(?P<{group_name}>[0-9]{{{precision}}})"))
319 }
320 NumberFormat::Hex {
323 require_prefix: true,
324 precision: 0,
325 } => match group_name_override {
326 None => Cow::Borrowed(r"(?P<digits>0x[A-Fa-f0-9]{1,16})"),
327 Some(group_name) => Cow::Owned(format!("(?P<{group_name}>0x[A-Fa-f0-9]{{1,16}})")),
328 },
329 NumberFormat::Hex {
330 require_prefix: true,
331 precision,
332 } => Cow::Owned(format!("(?P<{group_name}>0x[A-Fa-f0-9]{{{precision}}})")),
333 NumberFormat::Hex {
334 require_prefix: false,
335 precision: 0,
336 } => match group_name_override {
337 None => Cow::Borrowed(r"(?P<digits>[A-Fa-f0-9]{1,16})"),
338 Some(group_name) => Cow::Owned(format!("(?P<{group_name}>[A-Fa-f0-9]{{1,16}})")),
339 },
340 NumberFormat::Hex {
341 require_prefix: false,
342 precision,
343 } => Cow::Owned(format!("(?P<{group_name}>[A-Fa-f0-9]{{{precision}}})")),
344 }
345 }
346}
347impl Default for NumberFormat {
348 fn default() -> Self {
349 Self::Unsigned { precision: 0 }
350 }
351}
352
353#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
354pub enum FormatSpecifier {
355 #[default]
356 Unsigned,
357 Signed,
358 Hex,
359}