1#![expect(unused_assignments)]
2
3use crate::common::*;
4
5#[derive(Debug, Diagnostic, thiserror::Error)]
6pub enum ParseNumberError {
7 #[error("expected unsigned number but found sign")]
8 #[diagnostic()]
9 UnexpectedSign {
10 #[label("occurs here")]
11 span: SourceSpan,
12 },
13 #[error("expected hexadecimal number with 0x prefix, but no prefix was present")]
14 #[diagnostic()]
15 MissingPrefix {
16 #[label("occurs here")]
17 span: SourceSpan,
18 },
19 #[error("input string has incorrect precision: expected {precision} got {actual}")]
20 #[diagnostic()]
21 PrecisionMismatch {
22 #[label("occurs here")]
23 span: SourceSpan,
24 precision: u8,
25 actual: usize,
26 },
27 #[error("input string has incorrect numeric format: {reason:?}")]
28 #[diagnostic()]
29 InvalidFormat {
30 #[label("occurs here")]
31 span: SourceSpan,
32 reason: core::num::IntErrorKind,
33 },
34 #[error("input string has incorrect numeric format: invalid casing style, expected {expected}")]
35 #[diagnostic()]
36 InvalidCasing {
37 #[label("occurs here")]
38 span: SourceSpan,
39 expected: CasingStyle,
40 },
41}
42
43#[derive(Debug, Clone)]
44pub struct Number {
45 pub span: SourceSpan,
46 pub format: Option<NumberFormat>,
47 pub value: i128,
48}
49impl Number {
50 pub fn new(span: SourceSpan, value: i128) -> Self {
51 Self {
52 span,
53 format: None,
54 value,
55 }
56 }
57
58 pub fn new_with_format(span: SourceSpan, value: i128, format: NumberFormat) -> Self {
59 Self {
60 span,
61 format: Some(format),
62 value,
63 }
64 }
65
66 pub fn infer_expression_format(&self, other: &Self) -> Option<NumberFormat> {
85 match (self.format, other.format) {
86 (format @ Some(_), None) | (None, format @ Some(_)) => format,
87 (None, None) => Some(NumberFormat::default()),
88 (
89 Some(NumberFormat::Hex {
90 require_prefix: lprefixed,
91 casing: lcasing,
92 precision: lprecision,
93 }),
94 Some(NumberFormat::Hex {
95 require_prefix: rprefixed,
96 casing: rcasing,
97 precision: rprecision,
98 }),
99 ) if lprefixed == rprefixed
100 && (lprecision == rprecision || lprecision == 0 || rprecision == 0)
101 && (lcasing == rcasing
102 || lcasing == CasingStyle::Any
103 || rcasing == CasingStyle::Any) =>
104 {
105 let precision = core::cmp::max(lprecision, rprecision);
106 Some(NumberFormat::Hex {
107 precision,
108 require_prefix: lprefixed,
109 casing: lcasing,
110 })
111 }
112 (
113 Some(NumberFormat::Signed {
114 precision: lprecision,
115 }),
116 Some(NumberFormat::Signed {
117 precision: rprecision,
118 }),
119 ) if lprecision == rprecision || lprecision == 0 || rprecision == 0 => {
120 Some(NumberFormat::Signed {
121 precision: core::cmp::max(lprecision, rprecision),
122 })
123 }
124 (
125 Some(NumberFormat::Unsigned {
126 precision: lprecision,
127 }),
128 Some(NumberFormat::Unsigned {
129 precision: rprecision,
130 }),
131 ) if lprecision == rprecision || lprecision == 0 || rprecision == 0 => {
132 Some(NumberFormat::Unsigned {
133 precision: core::cmp::max(lprecision, rprecision),
134 })
135 }
136 _ => None,
137 }
138 }
139
140 pub fn parse_with_format(
141 input: Span<&str>,
142 format: NumberFormat,
143 ) -> Result<Self, ParseNumberError> {
144 let (span, input) = input.into_parts();
145
146 match format {
147 NumberFormat::Unsigned { precision } => {
148 if input.starts_with(['-', '+']) {
149 return Err(ParseNumberError::UnexpectedSign { span });
150 }
151 let value = input
152 .parse::<i128>()
153 .map(|value| Self {
154 span,
155 format: Some(format),
156 value,
157 })
158 .map_err(|error| ParseNumberError::InvalidFormat {
159 span,
160 reason: *error.kind(),
161 })?;
162 if precision == 0 {
163 return Ok(value);
164 }
165 if input.len() < precision as usize {
166 Err(ParseNumberError::PrecisionMismatch {
167 span,
168 precision,
169 actual: input.len(),
170 })
171 } else {
172 Ok(value)
173 }
174 }
175 NumberFormat::Signed { precision } => {
176 let value = input
177 .parse::<i128>()
178 .map(|value| Self {
179 span,
180 format: Some(format),
181 value,
182 })
183 .map_err(|error| ParseNumberError::InvalidFormat {
184 span,
185 reason: *error.kind(),
186 })?;
187 if precision == 0 {
188 return Ok(value);
189 }
190 let actual = if let Some(input) = input.strip_prefix(['-', '+']) {
191 input.len()
192 } else {
193 input.len()
194 };
195 if actual < precision as usize {
196 Err(ParseNumberError::PrecisionMismatch {
197 span,
198 precision,
199 actual,
200 })
201 } else {
202 Ok(value)
203 }
204 }
205 NumberFormat::Hex {
206 require_prefix,
207 precision,
208 casing,
209 } => {
210 let input = match input.strip_prefix("0x") {
211 None if require_prefix => return Err(ParseNumberError::MissingPrefix { span }),
212 None => input,
213 Some(input) => input,
214 };
215 let is_valid_casing = match casing {
216 CasingStyle::Any => true,
217 CasingStyle::Lower => input.chars().all(|c| matches!(c, 'a'..='f' | '0'..='9')),
218 CasingStyle::Upper => input.chars().all(|c| matches!(c, 'A'..='F' | '0'..='9')),
219 };
220 let value = i128::from_str_radix(input, 16)
221 .map(|value| Self {
222 span,
223 format: Some(format),
224 value,
225 })
226 .map_err(|error| ParseNumberError::InvalidFormat {
227 span,
228 reason: *error.kind(),
229 })?;
230 if !is_valid_casing {
231 Err(ParseNumberError::InvalidCasing {
232 span,
233 expected: casing,
234 })
235 } else if precision > 0 && input.len() < precision as usize {
236 Err(ParseNumberError::PrecisionMismatch {
237 span,
238 precision,
239 actual: input.len(),
240 })
241 } else {
242 Ok(value)
243 }
244 }
245 }
246 }
247}
248impl Eq for Number {}
249impl PartialEq for Number {
250 fn eq(&self, other: &Self) -> bool {
251 self.value == other.value && self.format == other.format
252 }
253}
254impl PartialOrd for Number {
255 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
256 Some(self.cmp(other))
257 }
258}
259impl Ord for Number {
260 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
261 self.value.cmp(&other.value)
262 }
263}
264impl Spanned for Number {
265 fn span(&self) -> SourceSpan {
266 self.span
267 }
268}
269impl fmt::Display for Number {
270 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
271 let value = self.value;
272 let mut padding = 0;
273 let format = self.format.unwrap_or_default();
274 match format {
275 NumberFormat::Unsigned { precision } => {
276 if precision > 0 {
277 padding += precision as usize;
278 }
279 }
280 NumberFormat::Signed { precision } => {
281 padding += value.is_negative() as usize;
282 if precision > 0 {
283 padding += precision as usize;
284 }
285 }
286 NumberFormat::Hex {
287 precision,
288 require_prefix,
289 ..
290 } => {
291 padding += (require_prefix as usize) * 2;
292 if precision > 0 {
293 padding += precision as usize;
294 }
295 }
296 }
297 match format {
298 NumberFormat::Unsigned { precision: 0 } => write!(f, "{}", value as u64),
299 NumberFormat::Unsigned { .. } => {
300 write!(f, "{:0padding$}", value as u64)
301 }
302 NumberFormat::Signed { precision: 0 } => write!(f, "{value}"),
303 NumberFormat::Signed { .. } => write!(f, "{value:0padding$}"),
304 NumberFormat::Hex {
305 require_prefix: true,
306 precision: 0,
307 casing: CasingStyle::Any | CasingStyle::Lower,
308 } => write!(f, "{value:#x?}"),
309 NumberFormat::Hex {
310 require_prefix: true,
311 precision: 0,
312 casing: CasingStyle::Upper,
313 } => write!(f, "{value:#X?}"),
314 NumberFormat::Hex {
315 require_prefix: false,
316 precision: 0,
317 casing: CasingStyle::Any | CasingStyle::Lower,
318 } => write!(f, "{value:x?}"),
319 NumberFormat::Hex {
320 require_prefix: false,
321 precision: 0,
322 casing: CasingStyle::Upper,
323 } => write!(f, "{value:X?}"),
324 NumberFormat::Hex {
325 require_prefix: true,
326 casing: CasingStyle::Any | CasingStyle::Lower,
327 ..
328 } => write!(f, "{value:#0padding$x?}"),
329 NumberFormat::Hex {
330 require_prefix: true,
331 casing: CasingStyle::Upper,
332 ..
333 } => write!(f, "{value:#0padding$X?}"),
334 NumberFormat::Hex {
335 require_prefix: false,
336 casing: CasingStyle::Any | CasingStyle::Lower,
337 ..
338 } => write!(f, "{value:0padding$x?}"),
339 NumberFormat::Hex {
340 require_prefix: false,
341 casing: CasingStyle::Upper,
342 ..
343 } => write!(f, "{value:0padding$X?}"),
344 }
345 }
346}
347
348#[derive(Debug, Copy, Clone, PartialEq, Eq)]
349#[repr(u8)]
350pub enum NumberFormat {
351 Unsigned {
352 precision: u8,
353 },
354 Signed {
355 precision: u8,
356 },
357 Hex {
358 precision: u8,
359 require_prefix: bool,
360 casing: CasingStyle,
361 },
362}
363
364#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
365#[repr(u8)]
366pub enum CasingStyle {
367 #[default]
368 Any = 0,
369 Upper,
370 Lower,
371}
372
373impl CasingStyle {
374 const HEX_ALPHABETS: &[&str] = &["[A-Fa-f0-9]", "[A-F0-9]", "[a-f0-9]"];
375
376 pub const fn as_hex_class(self) -> &'static str {
377 Self::HEX_ALPHABETS[self as u8 as usize]
378 }
379}
380
381impl fmt::Display for CasingStyle {
382 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383 match self {
384 Self::Any => f.write_str("either upper or lowercase"),
385 Self::Upper => f.write_str("uppercase"),
386 Self::Lower => f.write_str("lowercase"),
387 }
388 }
389}
390
391impl NumberFormat {
392 pub fn describe(&self) -> Cow<'static, str> {
393 match self {
394 Self::Unsigned { precision: 0 } => Cow::Borrowed("any unsigned 128-bit integer"),
395 Self::Unsigned { precision } => {
396 Cow::Owned(format!("an unsigned {precision}-digit 128-bit integer"))
397 }
398 Self::Signed { precision: 0 } => Cow::Borrowed("any signed 128-bit integer"),
399 Self::Signed { precision } => {
400 Cow::Owned(format!("a signed {precision}-digit 128-bit integer"))
401 }
402 Self::Hex {
403 require_prefix: true,
404 precision: 0,
405 casing,
406 } => Cow::Owned(format!(
407 "any 128-bit integer in {casing} hex format, prefixed with 0x"
408 )),
409 Self::Hex {
410 require_prefix: false,
411 precision: 0,
412 casing,
413 } => Cow::Owned(format!("any 128-bit integer in {casing} hex format")),
414 Self::Hex {
415 require_prefix: true,
416 precision,
417 casing,
418 } => Cow::Owned(format!(
419 "a {precision}-digit 12864-bit integer in {casing} hex format, prefixed with 0x"
420 )),
421 Self::Hex {
422 precision, casing, ..
423 } => Cow::Owned(format!(
424 "a {precision}-digit 128-bit integer in {casing} hex format"
425 )),
426 }
427 }
428
429 pub fn is_signed(&self) -> bool {
430 matches!(self, Self::Signed { .. })
431 }
432
433 pub fn is_hex(&self) -> bool {
434 matches!(self, Self::Hex { .. })
435 }
436
437 pub fn precision(&self) -> usize {
438 match self {
439 Self::Unsigned { precision }
440 | Self::Signed { precision }
441 | Self::Hex { precision, .. } => *precision as usize,
442 }
443 }
444
445 pub fn discriminant(&self) -> u8 {
446 unsafe { *<*const _>::from(self).cast::<u8>() }
450 }
451
452 pub fn pattern_nocapture(&self) -> Cow<'static, str> {
453 match self {
457 NumberFormat::Signed { precision: 0 } => Cow::Borrowed(r"(?:[-+]?[0-9]{1,39})"),
458 NumberFormat::Signed { precision } => {
459 Cow::Owned(format!("(?:[-+]?[0-9]{{{precision},39}})"))
460 }
461 NumberFormat::Unsigned { precision: 0 } => Cow::Borrowed(r"(?:[0-9]{1,39})"),
462 NumberFormat::Unsigned { precision } => {
463 Cow::Owned(format!("(?:[0-9]{{{precision},39}})"))
464 }
465 NumberFormat::Hex {
467 require_prefix: true,
468 precision: 0,
469 casing,
470 } => Cow::Owned(format!("(?:0x{}{{1,32}})", casing.as_hex_class())),
471 NumberFormat::Hex {
472 require_prefix: true,
473 precision,
474 casing,
475 } => Cow::Owned(format!("(?:0x{}{{{precision},32}})", casing.as_hex_class())),
476 NumberFormat::Hex {
477 require_prefix: false,
478 precision: 0,
479 casing,
480 } => Cow::Owned(format!(r"(?:{}{{1,32}})", casing.as_hex_class())),
481 NumberFormat::Hex {
482 require_prefix: false,
483 precision,
484 casing,
485 } => Cow::Owned(format!("(?:{}{{{precision},32}})", casing.as_hex_class())),
486 }
487 }
488
489 pub fn pattern(&self, group_name_override: Option<&str>) -> Cow<'static, str> {
490 let group_name = group_name_override.unwrap_or("digits");
494 match self {
495 NumberFormat::Signed { precision: 0 } => match group_name_override {
496 None => Cow::Borrowed(r"(?P<digits>[-+]?[0-9]{1,39})"),
497 Some(group_name) => Cow::Owned(format!("(?P<{group_name}>[-+]?[0-9]{{1,39}})")),
498 },
499 NumberFormat::Signed { precision } => {
500 Cow::Owned(format!("(?P<{group_name}>[-+]?[0-9]{{{precision},39}})"))
501 }
502 NumberFormat::Unsigned { precision: 0 } => match group_name_override {
503 None => Cow::Borrowed(r"(?P<digits>[0-9]{1,39})"),
504 Some(group_name) => Cow::Owned(format!("(?P<{group_name}>[0-9]{{1,39}})")),
505 },
506 NumberFormat::Unsigned { precision } => {
507 Cow::Owned(format!("(?P<{group_name}>[0-9]{{{precision},39}})"))
508 }
509 NumberFormat::Hex {
511 require_prefix: true,
512 precision: 0,
513 casing,
514 } => Cow::Owned(format!(
515 "(?P<{group_name}>0x{}{{1,32}})",
516 casing.as_hex_class()
517 )),
518 NumberFormat::Hex {
519 require_prefix: true,
520 precision,
521 casing,
522 } => Cow::Owned(format!(
523 "(?P<{group_name}>0x{}{{{precision},32}})",
524 casing.as_hex_class()
525 )),
526 NumberFormat::Hex {
527 require_prefix: false,
528 precision: 0,
529 casing,
530 } => Cow::Owned(format!(
531 "(?P<{group_name}>{}{{1,32}})",
532 casing.as_hex_class()
533 )),
534 NumberFormat::Hex {
535 require_prefix: false,
536 precision,
537 casing,
538 } => Cow::Owned(format!(
539 "(?P<{group_name}>{}{{{precision},32}})",
540 casing.as_hex_class()
541 )),
542 }
543 }
544}
545
546impl fmt::Display for NumberFormat {
547 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
548 match self {
549 Self::Unsigned { precision: 0 } => f.write_str("%u"),
550 Self::Unsigned { precision } => write!(f, "%.{precision}u"),
551 Self::Signed { precision: 0 } => f.write_str("d"),
552 Self::Signed { precision } => write!(f, "%.{precision}d"),
553 Self::Hex {
554 precision: 0,
555 require_prefix: false,
556 casing: CasingStyle::Any | CasingStyle::Lower,
557 } => write!(f, "%x"),
558 Self::Hex {
559 precision: 0,
560 require_prefix: false,
561 casing: CasingStyle::Upper,
562 } => write!(f, "%X"),
563 Self::Hex {
564 precision: 0,
565 require_prefix: true,
566 casing: CasingStyle::Any | CasingStyle::Lower,
567 } => write!(f, "%#x"),
568 Self::Hex {
569 precision: 0,
570 require_prefix: true,
571 casing: CasingStyle::Upper,
572 } => write!(f, "%#X"),
573 Self::Hex {
574 precision,
575 require_prefix: false,
576 casing: CasingStyle::Any | CasingStyle::Lower,
577 } => write!(f, "%.{precision}x"),
578 Self::Hex {
579 precision,
580 require_prefix: false,
581 casing: CasingStyle::Upper,
582 } => write!(f, "%.{precision}X"),
583 Self::Hex {
584 precision,
585 require_prefix: true,
586 casing: CasingStyle::Any | CasingStyle::Lower,
587 } => write!(f, "%#.{precision}x"),
588 Self::Hex {
589 precision,
590 require_prefix: true,
591 casing: CasingStyle::Upper,
592 } => write!(f, "%#.{precision}X"),
593 }
594 }
595}
596
597impl Default for NumberFormat {
598 fn default() -> Self {
599 Self::Unsigned { precision: 0 }
600 }
601}
602
603#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
604pub enum FormatSpecifier {
605 #[default]
606 Unsigned,
607 Signed,
608 Hex(CasingStyle),
609}