trivet/numbers/decoder.rs
1// Trivet
2// Copyright (c) 2025 by Stacy Prowell. All rights reserved.
3// https://gitlab.com/binary-tools/trivet
4
5//! Parse numbers.
6//!
7//! A general number parser can be found in [`NumberParser`]. This is capable of parsing
8//! both integer and floating point numbers, and can do so in decimal, binary, octal, and
9//! hexadecimal.
10//!
11//! There is a standard for hexadecimal floating point numbers that, since 2008, is part
12//! of the IEEE 754 standard.
13
14use crate::errors::syntax_error;
15use crate::errors::ParseResult;
16use crate::numbers::Radix;
17use crate::parse_from_string;
18use crate::ParserCore;
19use std::fmt::Debug;
20
21use super::binary_float::parse_binary_float;
22use super::decimal_float::parse_decimal_float;
23use super::hexadecimal_float::parse_hexadecimal_float;
24use super::octal_float::parse_octal_float;
25
26/// Define the sign.
27#[derive(Debug, Clone)]
28pub enum Sign {
29 Negative,
30 None,
31 Positive,
32}
33
34/// Define a number as parts.
35#[derive(Debug, Clone)]
36pub struct NumberParts {
37 /// The radix for this number.
38 pub radix: Radix,
39 /// The sign for this number.
40 pub sign: Sign,
41 /// If true, this number is NaN. No other items are relevant.
42 pub is_nan: bool,
43 /// If true, this number is infinity. No other items except the sign are relevant.
44 pub is_inf: bool,
45 /// Any digits to the left of the decimal or exponent marker, whichever comes first. This may be
46 /// empty if there are no digits to the left of the decimal or exponent marker, or if the number is
47 /// either `inf` or `nan`.
48 pub whole: String,
49 /// Any digits to the right of the decimal and left of the exponent marker. If there
50 /// is no decimal point, then this is `None`.
51 pub fraction: Option<String>,
52 /// The sign of the exponent. If there is no exponent marker, then this is `None`.
53 pub exponent_sign: Option<Sign>,
54 /// Any digits to the right of the exponent marker. If there is no exponent marker,
55 /// then this is `None`.
56 pub exponent: Option<String>,
57}
58
59impl NumberParts {
60 /// Determine if this is likely to represent a floating point number.
61 pub fn is_apparent_float(&self) -> bool {
62 self.fraction.is_some() || self.exponent.is_some() || self.is_inf || self.is_nan
63 }
64
65 /// Convert this to a string that can subsequently be parsed. This includes the sign
66 /// unless it is a plus. If `include_radix` is `true`, then the radix prefix is included.
67 /// Otherwise it is not.
68 pub fn as_string(&self, include_radix: bool) -> String {
69 let mut result = String::new();
70 if let Sign::Negative = self.sign {
71 result.push('-')
72 }
73 if self.is_nan {
74 result.push_str("nan");
75 return result;
76 }
77 if self.is_inf {
78 result.push_str("inf");
79 return result;
80 }
81 if include_radix {
82 result.push_str(&self.radix.as_prefix());
83 }
84 result.push_str(&self.whole);
85 if let Some(ref digits) = self.fraction {
86 result.push('.');
87 result.push_str(digits);
88 }
89 if let Some(ref digits) = self.exponent {
90 if self.radix == Radix::Hexadecimal {
91 result.push('p');
92 } else {
93 result.push('e');
94 }
95 if let Some(Sign::Negative) = self.exponent_sign {
96 result.push('-');
97 }
98 result.push_str(digits);
99 }
100 result
101 }
102}
103
104impl From<u128> for NumberParts {
105 fn from(value: u128) -> Self {
106 Self {
107 radix: Radix::Decimal,
108 sign: Sign::None,
109 is_nan: false,
110 is_inf: false,
111 whole: value.to_string(),
112 fraction: None,
113 exponent_sign: None,
114 exponent: None,
115 }
116 }
117}
118
119impl From<i128> for NumberParts {
120 fn from(value: i128) -> Self {
121 let (sign, value) = if value < 0 {
122 (Sign::Negative, -value)
123 } else {
124 (Sign::None, value)
125 };
126 Self {
127 radix: Radix::Decimal,
128 sign,
129 is_nan: false,
130 is_inf: false,
131 whole: value.to_string(),
132 fraction: None,
133 exponent_sign: None,
134 exponent: None,
135 }
136 }
137}
138
139impl From<u64> for NumberParts {
140 fn from(value: u64) -> Self {
141 Self {
142 radix: Radix::Decimal,
143 sign: Sign::None,
144 is_nan: false,
145 is_inf: false,
146 whole: value.to_string(),
147 fraction: None,
148 exponent_sign: None,
149 exponent: None,
150 }
151 }
152}
153
154impl From<i64> for NumberParts {
155 fn from(value: i64) -> Self {
156 let (sign, value) = if value < 0 {
157 (Sign::Negative, -value)
158 } else {
159 (Sign::None, value)
160 };
161 Self {
162 radix: Radix::Decimal,
163 sign,
164 is_nan: false,
165 is_inf: false,
166 whole: value.to_string(),
167 fraction: None,
168 exponent_sign: None,
169 exponent: None,
170 }
171 }
172}
173
174impl From<f64> for NumberParts {
175 fn from(value: f64) -> Self {
176 if f64::is_nan(value) {
177 return Self {
178 radix: Radix::Decimal,
179 sign: Sign::None,
180 is_nan: true,
181 is_inf: false,
182 whole: "".into(),
183 fraction: None,
184 exponent_sign: None,
185 exponent: None,
186 };
187 }
188 let (sign, value) = if value < 0.0 {
189 (Sign::Negative, -value)
190 } else {
191 (Sign::None, value)
192 };
193 if value == f64::INFINITY {
194 return Self {
195 radix: Radix::Decimal,
196 sign,
197 is_nan: false,
198 is_inf: true,
199 whole: "".into(),
200 fraction: None,
201 exponent_sign: None,
202 exponent: None,
203 };
204 }
205 let sci = format!("{:e}", value);
206 let parts: Vec<&str> = sci.split('e').collect();
207 let mantissa = parts[0];
208 let exponent = parts[1];
209 let (exponent_sign, exponent) = if let Some(body) = exponent.strip_prefix('-') {
210 (Sign::Negative, body)
211 } else {
212 (Sign::None, exponent)
213 };
214 let mantissa_parts: Vec<&str> = mantissa.split('.').collect();
215 let whole = mantissa_parts[0].to_string();
216 let fraction = mantissa_parts.get(1).unwrap_or(&"0").to_string();
217 Self {
218 radix: Radix::Decimal,
219 sign,
220 is_nan: false,
221 is_inf: false,
222 whole,
223 fraction: if fraction.is_empty() {
224 None
225 } else {
226 Some(fraction)
227 },
228 exponent_sign: Some(exponent_sign),
229 exponent: Some(exponent.into()),
230 }
231 }
232}
233
234impl From<NumberParts> for i64 {
235 fn from(np: NumberParts) -> Self {
236 if np.whole.is_empty() {
237 0
238 } else {
239 let value = i64::from_str_radix(&np.whole, np.radix.value()).unwrap_or(0);
240 if let Sign::Negative = np.sign {
241 -value
242 } else {
243 value
244 }
245 }
246 }
247}
248
249impl From<NumberParts> for i128 {
250 fn from(np: NumberParts) -> Self {
251 if np.whole.is_empty() {
252 0
253 } else {
254 let value = i128::from_str_radix(&np.whole, np.radix.value()).unwrap_or(0);
255 if let Sign::Negative = np.sign {
256 -value
257 } else {
258 value
259 }
260 }
261 }
262}
263
264impl From<NumberParts> for f64 {
265 fn from(np: NumberParts) -> Self {
266 if np.is_nan {
267 return f64::NAN;
268 }
269 let mut np = np;
270 let negative = matches!(np.sign, Sign::Negative);
271 np.sign = Sign::None;
272 if np.is_inf {
273 if negative {
274 return f64::NEG_INFINITY;
275 } else {
276 return f64::INFINITY;
277 }
278 }
279 let text = np.as_string(false);
280 println!("{}", text);
281 let mut parser = parse_from_string(&text);
282 let core = parser.borrow_core();
283 let settings = &NumberParserSettings::default();
284 let result = match np.radix {
285 Radix::Binary => parse_binary_float(core, negative, settings),
286 Radix::Octal => parse_octal_float(core, negative, settings),
287 Radix::Decimal => parse_decimal_float(core, negative, settings),
288 Radix::Hexadecimal => parse_hexadecimal_float(core, negative, settings),
289 };
290 println!("{:?}", result);
291 match result {
292 Ok(value) => value,
293 Err(_) => f64::NAN,
294 }
295 }
296}
297
298/// Provide the number parser configuration. You will seldom use this directly.
299/// Instead, reference the current settings through a [`NumberParser`] instance.
300#[derive(Debug, Clone)]
301pub struct NumberParserSettings {
302 /// Allow underscores in numbers. If this is true, then arbitrary underscores can
303 /// occur in numbers, and these are dropped during parsing. If false, then
304 /// underscores are not permitted.
305 pub permit_underscores: bool,
306
307 /// If true, methods that do not explicitly specify a radix will try to detect and
308 /// parse hexadecimal numbers.
309 pub permit_hexadecimal: bool,
310
311 /// If true, methods that do not explicitly specify a radix will try to detect and
312 /// parse octal numbers.
313 pub permit_octal: bool,
314
315 /// If true, methods that do not explicitly specify a radix will try to detect and
316 /// parse binary numbers.
317 pub permit_binary: bool,
318
319 /// If true, allow a number to start with a plus sign.
320 pub permit_plus: bool,
321
322 /// Specify the characters that indicate a hexadecimal value.
323 pub hexadecimal_indicator: Vec<char>,
324
325 /// Specify the characters that indicate an octal value.
326 pub octal_indicator: Vec<char>,
327
328 /// Specify the characters that indicate a binary value.
329 pub binary_indicator: Vec<char>,
330
331 /// If true, floating point numbers can be in non-decimal radix.
332 pub decimal_only_floats: bool,
333
334 /// If true, permit an empty whole part. That is, `.15` is legal.
335 pub permit_empty_whole: bool,
336
337 /// If true, permit an empty fractional part. That is, `15.` is legal.
338 pub permit_empty_fraction: bool,
339
340 /// If true, permit a leading zero for a non-zero whole. That is, `01.5` is legal.
341 ///
342 /// **NB**: This only applies to decimal numbers, as leading zeros are common and
343 /// expected in other bases.
344 pub permit_leading_zero: bool,
345}
346
347impl Default for NumberParserSettings {
348 fn default() -> Self {
349 Self {
350 permit_binary: true,
351 permit_octal: true,
352 permit_hexadecimal: true,
353 binary_indicator: vec!['0', 'b'],
354 octal_indicator: vec!['0', 'o'],
355 hexadecimal_indicator: vec!['0', 'x'],
356 permit_plus: true,
357 permit_leading_zero: true,
358 permit_empty_whole: true,
359 permit_empty_fraction: true,
360 permit_underscores: true,
361 decimal_only_floats: false,
362 }
363 }
364}
365
366impl NumberParserSettings {
367 /// Make a new instance. The parsing rules are maximally permissive.
368 pub fn new() -> Self {
369 Self::default()
370 }
371}
372
373/// Provide for parsing of numbers in various forms.
374///
375/// To use this, make an instance and then configure it if you wish.
376///
377/// # Plus / Minus
378///
379/// (Signed) numbers can start with an optional minus or plus sign to indicate the
380/// sign. If you do not want to allow the plus sign, see [`NumberParserSettings::permit_plus`].
381///
382/// # Permitted Radices
383///
384/// The methods [`Self::parse_u128`], [`Self::parse_i128`], and [`Self::parse_f64`],
385/// can parse numbers that occur in any supported radix. You can disable parsing of
386/// numbers in hexadecimal, octal, or binary by setting the appropriate flag to false.
387/// Decimal numbers cannot be disabled.
388///
389/// # Radix Indicator
390///
391/// Different radices can be detected automatically using a leading radix indicator.
392/// This is also configurable, with the following defaults.
393///
394/// |Prefix |Radix |
395/// |-------|------|
396/// |`0b` |Binary |
397/// |`0o` |Octal |
398/// |none |Decimal |
399/// |`0x` |Hexadecimal |
400///
401/// Radix indicators can be configured if you wish, but you must be careful to avoid
402/// excessive ambiguity. The following is an example.
403///
404/// ```rust
405/// use trivet::numbers::NumberParser;
406/// use trivet::ParserCore;
407/// use trivet::errors::ParseResult;
408/// use trivet::parse_from_string;
409///
410/// # fn main() -> ParseResult<()> {
411/// let mut parser = parse_from_string("17 $ffef_1021");
412/// let mut core = parser.borrow_core();
413/// let mut numpar = NumberParser::new();
414/// numpar.settings.permit_binary = false;
415/// numpar.settings.permit_octal = false;
416/// numpar.settings.hexadecimal_indicator = vec!['$'];
417///
418/// assert_eq!(numpar.parse_u128(&mut core)?, 17);
419/// core.consume();
420/// assert_eq!(numpar.parse_u128(&mut core)?, 0xffef1021);
421/// # Ok(())
422/// # }
423/// ```
424///
425/// # Explicit Radix
426///
427/// Methods that explicitly specify a radix *do not look for* a radix indicator, and
428/// will fail if one is encountered. They also ignore the enabled/disabled status of
429/// any radix.
430///
431/// ```rust
432/// use trivet::numbers::NumberParser;
433/// use trivet::numbers::Radix;
434/// use trivet::ParserCore;
435/// use trivet::errors::ParseResult;
436/// use trivet::parse_from_string;
437///
438/// # fn main() -> ParseResult<()> {
439/// let mut parser = parse_from_string("17 ffef_1021");
440/// let mut core = parser.borrow_core();
441/// let mut numpar = NumberParser::new();
442///
443/// assert_eq!(numpar.parse_u128_radix(&mut core, Radix::Decimal)?, 17);
444/// let _ = core.consume_ws_only();
445/// assert_eq!(numpar.parse_u128_radix(&mut core, Radix::Hexadecimal)?, 0xffef1021);
446/// # Ok(())
447/// # }
448/// ```
449///
450#[derive(Debug, Clone)]
451pub struct NumberParser {
452 /// The parser settings.
453 pub settings: NumberParserSettings,
454}
455
456impl Default for NumberParser {
457 fn default() -> Self {
458 Self::new()
459 }
460}
461
462impl NumberParser {
463 /// Make a new number parser. All options are enabled by default, and the
464 /// default radix indicators are installed. This is a very permissive form
465 /// of number parsing, and can be problematic for some parsers. Check the
466 /// options carefully to ensure the ones you want are installed.
467 pub fn new() -> Self {
468 NumberParser {
469 settings: NumberParserSettings::new(),
470 }
471 }
472
473 /// Get the text of the next number in the stream. This extracts the longest string that matches
474 /// the following expression. Note that this may not be a valid number.
475 ///
476 /// ```text
477 /// [-+]? (indicator)? [0-9a-z_]* ('.' [0-9a-z_]*)? ([eEpP] [0-9a-z_]*)?
478 /// ```
479 ///
480 /// The `+` can be allowed or disallowed using options, and is discarded from the returned string.
481 ///
482 /// **The [`NumberParserSettings::decimal_only_floats`] flag is ignored.** It can be checked after
483 /// returning the result.
484 ///
485 /// In the above `indicator` is any allowed radix indicator. The returned value will be a triple
486 /// consisting of the apparent radix of the number, the text of the number, and a flag indicating
487 /// whether the number is a floating point value.
488 ///
489 /// The exponent indicator `p` is required iff the number is in hexadecimal. The exponent indicator
490 /// `p` is prohibited if hexadecimal is not allowed.
491 ///
492 /// This method honors the settings, so only permitted radices and the appropriate indicators are used,
493 /// and underscores may or may not be permitted.
494 ///
495 /// Note that even if underscores are permitted, they are discarded during parsing.
496 ///
497 /// Note that tests for leading zero, empty whole, and empty fracitonal part are not performed here.
498 /// Perform those on evaluation.
499 ///
500 /// Several methods may be useful with respect to the output of this method.
501 ///
502 /// - `i16::from_str_radix`
503 /// - `i32::from_str_radix`
504 /// - `i64::from_str_radix`
505 /// - `i128::from_str_radix`
506 /// - `u16::from_str_radix`
507 /// - `u32::from_str_radix`
508 /// - `u64::from_str_radix`
509 /// - `u128::from_str_radix`
510 /// - `f64::from_str` (must use `std::str::FromStr`)
511 ///
512 /// ```
513 /// use trivet::parse_from_string;
514 /// use trivet::numbers::NumberParser;
515 /// use trivet::numbers::Radix;
516 ///
517 /// // Parse an integer value.
518 /// let mut parser = parse_from_string("-0x21_e4");
519 /// let mut np = NumberParser::new();
520 /// let (radix, text, fp) = np.get_text(parser.borrow_core());
521 /// assert_eq!(radix, Radix::Hexadecimal);
522 /// assert_eq!(text, "-21e4".to_string());
523 /// assert_eq!(fp, false);
524 /// let value = i16::from_str_radix(&text, radix.value());
525 /// assert_eq!(value, Ok(-0x21e4));
526 /// ```
527 ///
528 /// ```
529 /// use trivet::parse_from_string;
530 /// use trivet::numbers::NumberParser;
531 /// use trivet::numbers::Radix;
532 /// use std::str::FromStr;
533 ///
534 /// // Parse a floating point value.
535 /// let mut parser = parse_from_string("-174.0210");
536 /// let mut np = NumberParser::new();
537 /// let (radix, text, fp) = np.get_text(parser.borrow_core());
538 /// assert_eq!(radix, Radix::Decimal);
539 /// assert_eq!(text, "-174.0210".to_string());
540 /// assert_eq!(fp, true);
541 /// let value = f64::from_str(&text);
542 /// assert_eq!(value, Ok(-174.0210));
543 /// ```
544 ///
545 pub fn get_text(&self, parser: &mut ParserCore) -> (Radix, String, bool) {
546 let parts = self.get_parts(parser);
547
548 // Done.
549 (
550 parts.radix,
551 parts.as_string(false),
552 parts.is_apparent_float(),
553 )
554 }
555
556 /// Parse text from the stream to generate the parts of a number.
557 pub fn get_parts(&self, parser: &mut ParserCore) -> NumberParts {
558 let mut text = String::new();
559
560 // Watch for a negative sign or a plus sign.
561 let sign = if parser.peek_and_consume('-') {
562 Sign::Negative
563 } else if parser.peek_and_consume('+') {
564 Sign::Positive
565 } else {
566 Sign::None
567 };
568
569 // Watch for inf and nan.
570 if parser.peek_and_consume_chars(&['n', 'a', 'n']) {
571 return NumberParts {
572 radix: Radix::Decimal,
573 sign: Sign::None,
574 is_inf: false,
575 is_nan: true,
576 whole: String::from(""),
577 fraction: None,
578 exponent_sign: None,
579 exponent: None,
580 };
581 }
582 if parser.peek_and_consume_chars(&['i', 'n', 'f']) {
583 let _ = parser.peek_and_consume_chars(&['i', 'n', 'i', 't', 'y']);
584 return NumberParts {
585 radix: Radix::Decimal,
586 sign,
587 is_inf: true,
588 is_nan: false,
589 whole: String::from(""),
590 fraction: None,
591 exponent_sign: None,
592 exponent: None,
593 };
594 }
595
596 // Try to match (and consume) a radix indicator.
597 let radix = self.get_radix(parser);
598
599 // Figure out the style for exponents.
600 let exponent_marker = if radix == Radix::Hexadecimal {
601 vec!['p', 'P']
602 } else if self.settings.permit_hexadecimal {
603 vec!['e', 'E', 'p', 'P']
604 } else {
605 vec!['e', 'E']
606 };
607
608 // Now consume digits. We stop when we encounter the exponent marker or a non-alphanumeric
609 // character.
610 let (whole_vec, mut last) = if self.settings.permit_underscores {
611 parser.take(
612 |ch| ch == '_',
613 |ch| exponent_marker.contains(&ch) || !ch.is_alphanumeric(),
614 )
615 } else {
616 parser.take(
617 |_| false,
618 |ch| exponent_marker.contains(&ch) || !ch.is_alphanumeric(),
619 )
620 };
621 let whole = String::from_iter(whole_vec);
622
623 // Look for a decimal point.
624 let mut fraction = None;
625 if Some('.') == last {
626 parser.consume();
627 text.push('.');
628 let (frac_vec, newlast) = if self.settings.permit_underscores {
629 parser.take(
630 |ch| ch == '_',
631 |ch| exponent_marker.contains(&ch) || !ch.is_alphanumeric(),
632 )
633 } else {
634 parser.take(
635 |_| false,
636 |ch| exponent_marker.contains(&ch) || !ch.is_alphanumeric(),
637 )
638 };
639 last = newlast;
640 fraction = Some(String::from_iter(frac_vec));
641 }
642
643 // Look for an exponent.
644 let mut exponent_sign = None;
645 let mut exponent = None;
646 if let Some(ch) = last {
647 if exponent_marker.contains(&ch) {
648 parser.consume();
649
650 // Push a p if hexadecial; otherwise push an e.
651 text.push(exponent_marker[0]);
652
653 // Watch for a negative sign or a plus sign.
654 exponent_sign = if parser.peek_and_consume('-') {
655 Some(Sign::Negative)
656 } else if parser.peek_and_consume('+') {
657 Some(Sign::Positive)
658 } else {
659 Some(Sign::None)
660 };
661 exponent = Some(if self.settings.permit_underscores {
662 parser.take_while_unless(|ch| ch.is_alphanumeric(), |ch| ch == '_')
663 } else {
664 parser.take_while(|ch| ch.is_alphanumeric())
665 });
666 }
667 }
668
669 // Done.
670 NumberParts {
671 radix,
672 sign,
673 is_inf: false,
674 is_nan: false,
675 whole,
676 fraction,
677 exponent_sign,
678 exponent,
679 }
680 }
681
682 /// Peek at the stream and guess the radix.
683 fn get_radix(&self, parser: &mut ParserCore) -> Radix {
684 if self.settings.permit_hexadecimal
685 && parser.peek_and_consume_chars(&self.settings.hexadecimal_indicator)
686 {
687 Radix::Hexadecimal
688 } else if self.settings.permit_octal
689 && parser.peek_and_consume_chars(&self.settings.octal_indicator)
690 {
691 Radix::Octal
692 } else if self.settings.permit_binary
693 && parser.peek_and_consume_chars(&self.settings.binary_indicator)
694 {
695 Radix::Binary
696 } else {
697 Radix::Decimal
698 }
699 }
700
701 /// Parse a floating point number from the stream. The number's radix is inferred from any
702 /// radix prefixes, and only the allowed integer types are checked.
703 ///
704 /// Floating point numbers can be in any supported radix, and the radix indicator is checked.
705 /// It is it not present, then decimal is assumed. The structure of a floating point number
706 /// is as follows.
707 ///
708 /// ```text
709 /// Float ::= '-'? ( Radix)? ( 'inf' | 'infinity' | 'nan' | Number )
710 /// Radix ::= ( '0x' | '0o' | '0b' )
711 /// Number ::= ( Digit+ |
712 /// Digit+ '.' Digit* |
713 /// Digit* '.' Digit+ ) Exp?
714 /// Exp ::= ('e'|'p') ('-'|'+') Digit+
715 /// Digit ::= [0-9a-zA-Z]
716 /// ```
717 ///
718 /// Empty whole and fractional parts may be permitted; refer to the
719 /// [`NumberParserSettings::permit_empty_whole`] and [`NumberParserSettings::permit_empty_fraction`]
720 /// settings. A leading zero may be allowed or not. Refer to
721 /// [`NumberParserSettings::permit_leading_zero`].
722 /// If both the whole and fractional parts are empty, this is *always* an error.
723 ///
724 /// The exponent can be indicated with either an `e` or a `p`. If the radix is hexadecimal,
725 /// then *only* `p` can be used to avoid ambiguity.
726 ///
727 /// Some examples.
728 ///
729 /// |Number |Value |
730 /// |---------|---------|
731 /// |`0,5` |0.5 |
732 /// |`0b0.1` |0.5 |
733 /// |`0b1e-1` |0.5 |
734 /// |`0o0.4` |0.5 |
735 /// |`0x0.8` |0.5 |
736 /// |`0x1.4` |1.25 |
737 /// |`0x3p4` |196,608 |
738 /// |`0b0.12e-2` |0.0029296875 |
739 ///
740 /// **Caution**: This code does not adhere to the IEEE-754 standard for non-decimal bases!
741 /// It uses a "best estimate" approach. For base 10 [`Self::parse_f64_decimal`] is used, and
742 /// is intended to be compliant.
743 ///
744 pub fn parse_f64(&self, parser: &mut ParserCore) -> ParseResult<f64> {
745 // Look for a leading minus sign.
746 let negative = parser.peek_and_consume('-');
747 if !negative {
748 parser.peek_and_consume('+');
749 }
750
751 // Look for some special cases.
752 if parser.peek_and_consume_chars(&['i', 'n', 'f']) {
753 let _ = parser.peek_and_consume_chars(&['i', 'n', 'i', 't', 'y']);
754 if negative {
755 return Ok(f64::NEG_INFINITY);
756 }
757 return Ok(f64::INFINITY);
758 }
759 if parser.peek_and_consume_chars(&['n', 'a', 'n']) {
760 return Ok(f64::NAN);
761 }
762
763 // Look for a radix indicator in the stream. If we don't find one, we will assume
764 // decimal. We skip any radix indicators that are either empty or are for a radix
765 // that is not allowed.
766 let radix = self.get_radix(parser);
767
768 // Parse based on the radix.
769 match radix {
770 Radix::Binary => parse_binary_float(parser, negative, &self.settings),
771 Radix::Hexadecimal => parse_hexadecimal_float(parser, negative, &self.settings),
772 Radix::Octal => parse_octal_float(parser, negative, &self.settings),
773 _ => parse_decimal_float(parser, negative, &self.settings),
774 }
775 }
776
777 /// Parse the body of an i128.
778 fn parse_i128_body(
779 &self,
780 parser: &mut ParserCore,
781 negative: bool,
782 radix: Radix,
783 ) -> ParseResult<i128> {
784 // Deal with any leading minus sign.
785 let mut digits = false;
786 let mut nonzero_pending = true;
787 let mut value = 0i128;
788 let mut fail_mul;
789 let mut fail_add;
790 let mut fail = false;
791
792 let accumulate = if negative {
793 i128::overflowing_sub
794 } else {
795 i128::overflowing_add
796 };
797 let base = radix.value();
798
799 // Process all alphanumerics until we encounter a non-alphanumeric character or we encounter
800 // an error.
801 while !parser.is_at_eof() {
802 // Get next potential digit and convert it to the numeric value. Note that
803 // underscores do not count as digits.
804 let ch = parser.peek();
805 if self.settings.permit_underscores && ch == '_' {
806 parser.consume();
807 continue;
808 }
809 let mut dig = ch as u32;
810 if (0x30..=0x39).contains(&dig) {
811 dig -= 0x30;
812 } else if (0x41..0x5B).contains(&(dig & 0xdf)) {
813 dig = (dig & 0xdf) - 0x37;
814 } else {
815 // Not alphanumeric.
816 break;
817 }
818 digits = true;
819
820 // If the digit is larger than the base, reject with an error.
821 if dig >= base {
822 return Err(syntax_error(
823 parser.loc(),
824 &format!("The digit '{}' is invalid for base {}.", ch, base),
825 ));
826 }
827
828 // Consume the digit now.
829 parser.consume();
830
831 // Accumulate it. The first non-zero digit is handled differently from
832 // subsequent digits. We discard leading zeros.
833 if nonzero_pending {
834 if dig > 0 {
835 // This is the first non-zero digit.
836 if negative {
837 value = -(dig as i128);
838 } else {
839 value = dig as i128;
840 }
841 nonzero_pending = false;
842 }
843 } else {
844 (value, fail_mul) = value.overflowing_mul(base as i128);
845 (value, fail_add) = accumulate(value, dig as i128);
846 fail = fail || fail_mul || fail_add;
847 }
848 }
849
850 // If we overflowed (or underflowed) during accumulation, issue an error now.
851 if fail {
852 return Err(syntax_error(
853 parser.loc(),
854 "Integer value out of bounds for i64",
855 ));
856 }
857
858 // If we didn't get any digits, then reject with an error.
859 if !digits {
860 return Err(syntax_error(
861 parser.loc(),
862 "Expected a number but found no valid digits.",
863 ));
864 }
865 Ok(value)
866 }
867
868 /// Read an unsigned from the stream at the current location and compute
869 /// and return a `u64` value. Every alphanumeric character is treated as if it is part
870 /// of the number. Underscores may be permitted in the number; that can be specified by
871 /// the `allow_underscores` flag.
872 ///
873 /// Number radices are read and processed here. The arguments allow excluding any radices
874 /// you do not want to permit. Note that we don't generate an error saying that, say,
875 /// "octal is not permitted," we just treat the 'o' as an illegal digit.
876 ///
877 /// **This method does not allow you to change the radix specifiers!** The default radix
878 /// specifiers are always used, if enabled, with the default radix being decimal. This is
879 /// *subject to change* so do not rely on this!
880 ///
881 /// |Prefix |Radix |
882 /// |--------|------------|
883 /// |`0b` |Binary |
884 /// |`0o` |Octal |
885 /// |`0x` |Hexadecimal |
886 ///
887 /// If an optional radix is provided, it must be 2, 8, 10, or 16 (it isn't checked here).
888 /// In that case no radix indicator is checked or permitted.
889 ///
890 /// Errors are generated if an unexpected alphanumeric character is encountered during
891 /// parsing. The first non-alphanumeric stops the parse.
892 fn parse_u128_body(&self, parser: &mut ParserCore, radix: Radix) -> ParseResult<u128> {
893 let mut digits = false;
894 let mut nonzero_pending = true;
895 let mut value = 0u128;
896 let mut fail_mul;
897 let mut fail_add;
898 let mut fail = false;
899 let base = radix.value();
900
901 // Process all alphanumerics until we encounter a non-alphanumeric character or we
902 // encounter an error.
903 while !parser.is_at_eof() {
904 // Get next potential digit and convert it to the numeric value. Note that
905 // underscores do not count as digits.
906 let ch = parser.peek();
907 if self.settings.permit_underscores && ch == '_' {
908 parser.consume();
909 continue;
910 }
911 let mut dig = ch as u32;
912 if (0x30..=0x39).contains(&dig) {
913 dig -= 0x30;
914 } else if (0x41..0x5B).contains(&(dig & 0xdf)) {
915 dig = (dig & 0xdf) - 0x37;
916 } else {
917 // Not alphanumeric.
918 break;
919 }
920 digits = true;
921
922 // If the digit is larger than the base, reject with an error.
923 if dig >= base {
924 return Err(syntax_error(
925 parser.loc(),
926 &format!("The digit '{}' is invalid for base {}.", ch, base),
927 ));
928 }
929
930 // Consume the digit now.
931 parser.consume();
932
933 // Accumulate it. The first non-zero digit is handled differently from
934 // subsequent digits.
935 if nonzero_pending {
936 if dig > 0 {
937 // This is the first non-zero digit.
938 value = dig as u128;
939 nonzero_pending = false;
940 }
941 } else {
942 // Accumulate the value and keep track of overflows.
943 (value, fail_mul) = value.overflowing_mul(base as u128);
944 (value, fail_add) = value.overflowing_add(dig as u128);
945 fail = fail || fail_mul || fail_add;
946 }
947 }
948
949 // Report any overflow.
950 if fail {
951 return Err(syntax_error(
952 parser.loc(),
953 "Integer value too large for u64",
954 ));
955 }
956
957 // If we didn't get any digits, then reject with an error.
958 if !digits {
959 return Err(syntax_error(
960 parser.loc(),
961 "Expected a number but found no valid digits",
962 ));
963 }
964 Ok(value)
965 }
966
967 /// Read a possibly-signed integer from the stream at the current location and compute
968 /// and return an `i64` value. Every alphanumeric character is treated as if it is part
969 /// of the number. Underscores may be permitted in the number; that can be specified by
970 /// the `allow_underscores` flag.
971 ///
972 /// Number radices are read and processed here. The arguments allow excluding any radices
973 /// you do not want to permit. Note that we don't generate an error saying that, say,
974 /// "octal is not permitted," we just treat the 'o' as an illegal digit.
975 ///
976 /// **This method does not allow you to change the radix specifiers!** The default radix
977 /// specifiers are always used, if enabled, with the default radix being decimal. This is
978 /// *subject to change* so do not rely on this!
979 ///
980 /// |Prefix |Radix |
981 /// |--------|------------|
982 /// |`0b` |Binary |
983 /// |`0o` |Octal |
984 /// |`0x` |Hexadecimal |
985 ///
986 /// If an optional radix is provided, it must be 2, 8, 10, or 16 (it isn't checked here).
987 /// In that case no radix indicator is checked or permitted.
988 ///
989 /// Errors are generated if an unexpected alphanumeric character is encountered during
990 /// parsing. The first non-alphanumeric stops the parse.
991 fn parse_i64_body(
992 &self,
993 parser: &mut ParserCore,
994 negative: bool,
995 radix: Radix,
996 ) -> ParseResult<i64> {
997 // Deal with any leading minus sign.
998 let mut digits = false;
999 let mut nonzero_pending = true;
1000 let mut value = 0i64;
1001 let mut fail_mul;
1002 let mut fail_add;
1003 let mut fail = false;
1004
1005 let accumulate = if negative {
1006 i64::overflowing_sub
1007 } else {
1008 i64::overflowing_add
1009 };
1010 let base = radix.value();
1011
1012 // Now we have the radix and we might have also processed a digit. Process all
1013 // alphanumerics until we encounter a non-alphanumeric character or we encounter
1014 // an error.
1015 while !parser.is_at_eof() {
1016 // Get next potential digit and convert it to the numeric value. Note that
1017 // underscores do not count as digits.
1018 let ch = parser.peek();
1019 if self.settings.permit_underscores && ch == '_' {
1020 parser.consume();
1021 continue;
1022 }
1023 let mut dig = ch as u32;
1024 if (0x30..=0x39).contains(&dig) {
1025 dig -= 0x30;
1026 } else if (0x41..0x5B).contains(&(dig & 0xdf)) {
1027 dig = (dig & 0xdf) - 0x37;
1028 } else {
1029 // Not alphanumeric.
1030 break;
1031 }
1032 digits = true;
1033
1034 // If the digit is larger than the base, reject with an error.
1035 if dig >= base {
1036 return Err(syntax_error(
1037 parser.loc(),
1038 &format!("The digit '{}' is invalid for base {}.", ch, base),
1039 ));
1040 }
1041
1042 // Consume the digit now.
1043 parser.consume();
1044
1045 // Accumulate it. The first non-zero digit is handled differently from
1046 // subsequent digits. We discard leading zeros.
1047 if nonzero_pending {
1048 if dig > 0 {
1049 // This is the first non-zero digit.
1050 if negative {
1051 value = -(dig as i64);
1052 } else {
1053 value = dig as i64;
1054 }
1055 nonzero_pending = false;
1056 }
1057 } else {
1058 (value, fail_mul) = value.overflowing_mul(base as i64);
1059 (value, fail_add) = accumulate(value, dig as i64);
1060 fail = fail || fail_mul || fail_add;
1061 }
1062 }
1063
1064 // If we overflowed (or underflowed) during accumulation, issue an error now.
1065 if fail {
1066 return Err(syntax_error(
1067 parser.loc(),
1068 "Integer value out of bounds for i64",
1069 ));
1070 }
1071
1072 // If we didn't get any digits, then reject with an error.
1073 if !digits {
1074 return Err(syntax_error(
1075 parser.loc(),
1076 "Expected a number but found no valid digits.",
1077 ));
1078 }
1079 Ok(value)
1080 }
1081
1082 /// Read an unsigned from the stream at the current location and compute
1083 /// and return a `u64` value. Every alphanumeric character is treated as if it is part
1084 /// of the number. Underscores may be permitted in the number; that can be specified by
1085 /// the `allow_underscores` flag.
1086 ///
1087 /// Number radices are read and processed here. The arguments allow excluding any radices
1088 /// you do not want to permit. Note that we don't generate an error saying that, say,
1089 /// "octal is not permitted," we just treat the 'o' as an illegal digit.
1090 ///
1091 /// **This method does not allow you to change the radix specifiers!** The default radix
1092 /// specifiers are always used, if enabled, with the default radix being decimal. This is
1093 /// *subject to change* so do not rely on this!
1094 ///
1095 /// |Prefix |Radix |
1096 /// |--------|------------|
1097 /// |`0b` |Binary |
1098 /// |`0o` |Octal |
1099 /// |`0x` |Hexadecimal |
1100 ///
1101 /// If an optional radix is provided, it must be 2, 8, 10, or 16 (it isn't checked here).
1102 /// In that case no radix indicator is checked or permitted.
1103 ///
1104 /// Errors are generated if an unexpected alphanumeric character is encountered during
1105 /// parsing. The first non-alphanumeric stops the parse.
1106 fn parse_u64_body(&self, parser: &mut ParserCore, radix: Radix) -> ParseResult<u64> {
1107 let mut digits = false;
1108 let mut nonzero_pending = true;
1109 let mut value = 0u64;
1110 let mut fail_mul;
1111 let mut fail_add;
1112 let mut fail = false;
1113 let base = radix.value();
1114
1115 // Now we have the radix and we might have also processed a digit. Process all
1116 // alphanumerics until we encounter a non-alphanumeric character or we encounter
1117 // an error.
1118 while !parser.is_at_eof() {
1119 // Get next potential digit and convert it to the numeric value. Note that
1120 // underscores do not count as digits.
1121 let ch = parser.peek();
1122 if self.settings.permit_underscores && ch == '_' {
1123 parser.consume();
1124 continue;
1125 }
1126 let mut dig = ch as u32;
1127 if (0x30..=0x39).contains(&dig) {
1128 dig -= 0x30;
1129 } else if (0x41..0x5B).contains(&(dig & 0xdf)) {
1130 dig = (dig & 0xdf) - 0x37;
1131 } else {
1132 // Not alphanumeric.
1133 break;
1134 }
1135 digits = true;
1136
1137 // If the digit is larger than the base, reject with an error.
1138 if dig >= base {
1139 return Err(syntax_error(
1140 parser.loc(),
1141 &format!("The digit '{}' is invalid for base {}.", ch, base),
1142 ));
1143 }
1144
1145 // Consume the digit now.
1146 parser.consume();
1147
1148 // Accumulate it. The first non-zero digit is handled differently from
1149 // subsequent digits.
1150 if nonzero_pending {
1151 if dig > 0 {
1152 // This is the first non-zero digit.
1153 value = dig as u64;
1154 nonzero_pending = false;
1155 }
1156 } else {
1157 // Accumulate the value and keep track of overflows.
1158 (value, fail_mul) = value.overflowing_mul(base as u64);
1159 (value, fail_add) = value.overflowing_add(dig as u64);
1160 fail = fail || fail_mul || fail_add;
1161 }
1162 }
1163
1164 // Report any overflow.
1165 if fail {
1166 return Err(syntax_error(
1167 parser.loc(),
1168 "Integer value too large for u64",
1169 ));
1170 }
1171
1172 // If we didn't get any digits, then reject with an error.
1173 if !digits {
1174 return Err(syntax_error(
1175 parser.loc(),
1176 "Expected a number but found no valid digits",
1177 ));
1178 }
1179 Ok(value)
1180 }
1181
1182 /// Parse a floating point number from the stream. This method assumes the number will be in
1183 /// decimal and does not look for, or accept, a radix specifier. Underscores are also not
1184 /// accepted here.
1185 ///
1186 /// The structure of a floating point number is as follows.
1187 ///
1188 /// ```text
1189 /// Float ::= '-'? ( 'inf' | 'infinity' | 'nan' | Number )
1190 /// Number ::= ( Digit+ |
1191 /// Digit+ '.' Digit* |
1192 /// Digit* '.' Digit+ ) Exp?
1193 /// Exp ::= [eEpP] ('-'|'+') Digit+
1194 /// Digit ::= [0-9]
1195 /// ```
1196 ///
1197 pub fn parse_f64_decimal(&self, parser: &mut ParserCore) -> ParseResult<f64> {
1198 let negative = parser.peek_and_consume('-');
1199 if !negative {
1200 parser.peek_and_consume('+');
1201 }
1202 parse_decimal_float(parser, negative, &self.settings)
1203 }
1204
1205 /// Read an integer from the stream at the current location and compute
1206 /// and return its value. Every alphanumeric character is treated as if it is part
1207 /// of the number. Underscores may be permitted in the number.
1208 ///
1209 /// Number radices are read and processed here, based on the configuration.
1210 ///
1211 /// Errors are generated if an unexpected alphanumeric character is encountered during
1212 /// parsing. The first non-alphanumeric stops the parse.
1213 pub fn parse_i128(&self, parser: &mut ParserCore) -> ParseResult<i128> {
1214 let negative = parser.peek_and_consume('-');
1215 if !negative {
1216 parser.peek_and_consume('+');
1217 }
1218 let radix = self.get_radix(parser);
1219 self.parse_i128_body(parser, negative, radix)
1220 }
1221
1222 /// Read an integer from the stream at the current location and compute
1223 /// and return its value. Every alphanumeric character is treated as if it is part
1224 /// of the number. Underscores may be permitted in the number.
1225 ///
1226 /// Number radices are read and processed here, based on the configuration.
1227 ///
1228 /// Errors are generated if an unexpected alphanumeric character is encountered during
1229 /// parsing. The first non-alphanumeric stops the parse.
1230 pub fn parse_u128(&self, parser: &mut ParserCore) -> ParseResult<u128> {
1231 let radix = self.get_radix(parser);
1232 self.parse_u128_body(parser, radix)
1233 }
1234
1235 /// Read an integer from the stream at the current location and compute
1236 /// and return its value. Every alphanumeric character is treated as if it is part
1237 /// of the number. Underscores may be permitted in the number.
1238 ///
1239 /// Number radices are read and processed here, based on the configuration.
1240 ///
1241 /// Errors are generated if an unexpected alphanumeric character is encountered during
1242 /// parsing. The first non-alphanumeric stops the parse.
1243 pub fn parse_i64(&self, parser: &mut ParserCore) -> ParseResult<i64> {
1244 let negative = parser.peek_and_consume('-');
1245 if !negative {
1246 parser.peek_and_consume('+');
1247 }
1248 let radix = self.get_radix(parser);
1249 self.parse_i64_body(parser, negative, radix)
1250 }
1251
1252 /// Read an integer from the stream at the current location and compute
1253 /// and return its value. Every alphanumeric character is treated as if it is part
1254 /// of the number. Underscores may be permitted in the number.
1255 ///
1256 /// Number radices are read and processed here, based on the configuration.
1257 ///
1258 /// Errors are generated if an unexpected alphanumeric character is encountered during
1259 /// parsing. The first non-alphanumeric stops the parse.
1260 pub fn parse_u64(&self, parser: &mut ParserCore) -> ParseResult<u64> {
1261 let radix = self.get_radix(parser);
1262 self.parse_u64_body(parser, radix)
1263 }
1264
1265 /// Read an integer from the stream at the current location and compute
1266 /// and return its value. Every alphanumeric character is treated as if it is part
1267 /// of the number. Underscores may be permitted in the number.
1268 ///
1269 /// The number is parsed according to the given radix. Radix specifiers are not
1270 /// permitted.
1271 ///
1272 /// Errors are generated if an unexpected alphanumeric character is encountered during
1273 /// parsing. The first non-alphanumeric stops the parse.
1274 pub fn parse_i128_radix(&self, parser: &mut ParserCore, radix: Radix) -> ParseResult<i128> {
1275 let negative = parser.peek_and_consume('-');
1276 if !negative {
1277 parser.peek_and_consume('+');
1278 }
1279 self.parse_i128_body(parser, negative, radix)
1280 }
1281
1282 /// Read an integer from the stream at the current location and compute
1283 /// and return its value. Every alphanumeric character is treated as if it is part
1284 /// of the number. Underscores may be permitted in the number.
1285 ///
1286 /// The number is parsed according to the given radix. Radix specifiers are not
1287 /// permitted.
1288 ///
1289 /// Errors are generated if an unexpected alphanumeric character is encountered during
1290 /// parsing. The first non-alphanumeric stops the parse.
1291 pub fn parse_u128_radix(&self, parser: &mut ParserCore, radix: Radix) -> ParseResult<u128> {
1292 self.parse_u128_body(parser, radix)
1293 }
1294
1295 /// Read an integer from the stream at the current location and compute
1296 /// and return its value. Every alphanumeric character is treated as if it is part
1297 /// of the number. Underscores may be permitted in the number.
1298 ///
1299 /// The number is parsed according to the given radix. Radix specifiers are not
1300 /// permitted.
1301 ///
1302 /// Errors are generated if an unexpected alphanumeric character is encountered during
1303 /// parsing. The first non-alphanumeric stops the parse.
1304 pub fn parse_i64_radix(&self, parser: &mut ParserCore, radix: Radix) -> ParseResult<i64> {
1305 let negative = parser.peek_and_consume('-');
1306 if !negative {
1307 parser.peek_and_consume('+');
1308 }
1309 self.parse_i64_body(parser, negative, radix)
1310 }
1311
1312 /// Read an integer from the stream at the current location and compute
1313 /// and return its value. Every alphanumeric character is treated as if it is part
1314 /// of the number. Underscores may be permitted in the number.
1315 ///
1316 /// The number is parsed according to the given radix. Radix specifiers are not
1317 /// permitted.
1318 ///
1319 /// Errors are generated if an unexpected alphanumeric character is encountered during
1320 /// parsing. The first non-alphanumeric stops the parse.
1321 pub fn parse_u64_radix(&self, parser: &mut ParserCore, radix: Radix) -> ParseResult<u64> {
1322 self.parse_u64_body(parser, radix)
1323 }
1324}
1325
1326#[cfg(test)]
1327mod tests {
1328 use core::f64;
1329
1330 use crate::{
1331 errors::ParseResult,
1332 numbers::{NumberParser, Radix},
1333 parse_from_string,
1334 };
1335
1336 use super::{NumberParts, Sign};
1337
1338 #[test]
1339 fn each() {
1340 let numpar = NumberParser::new();
1341 let mut parser = parse_from_string("-7563");
1342 assert_eq!(numpar.parse_i128(parser.borrow_core()).unwrap(), -7563);
1343 }
1344
1345 #[test]
1346 fn get_text_test() -> ParseResult<()> {
1347 let source = r#"
1348 0
1349 0.0
1350 -0
1351 -0.0
1352 +0
1353 +0.0
1354 0x0
1355 0o0
1356 0b0
1357 0x76_5afe
1358 0b01_0010
1359 0o14_234
1360 +0x01_24ffp12
1361 +12e-14
1362 1_123_232e-74
1363 1_123_998E+21
1364 __1_123_543__P-__12__
1365 0xfe
1366 nan
1367 inf
1368 +inf
1369 -inf
1370 infinity
1371 "#;
1372 let result = &[
1373 (Radix::Decimal, "0".to_string(), false),
1374 (Radix::Decimal, "0.0".to_string(), true),
1375 (Radix::Decimal, "-0".to_string(), false),
1376 (Radix::Decimal, "-0.0".to_string(), true),
1377 (Radix::Decimal, "0".to_string(), false),
1378 (Radix::Decimal, "0.0".to_string(), true),
1379 (Radix::Hexadecimal, "0".to_string(), false),
1380 (Radix::Octal, "0".to_string(), false),
1381 (Radix::Binary, "0".to_string(), false),
1382 (Radix::Hexadecimal, "765afe".to_string(), false),
1383 (Radix::Binary, "010010".to_string(), false),
1384 (Radix::Octal, "14234".to_string(), false),
1385 (Radix::Hexadecimal, "0124ffp12".to_string(), true),
1386 (Radix::Decimal, "12e-14".to_string(), true),
1387 (Radix::Decimal, "1123232e-74".to_string(), true),
1388 (Radix::Decimal, "1123998e21".to_string(), true),
1389 (Radix::Decimal, "1123543e-12".to_string(), true),
1390 (Radix::Hexadecimal, "fe".to_string(), false),
1391 (Radix::Decimal, "nan".to_string(), true),
1392 (Radix::Decimal, "inf".to_string(), true),
1393 (Radix::Decimal, "inf".to_string(), true),
1394 (Radix::Decimal, "-inf".to_string(), true),
1395 (Radix::Decimal, "inf".to_string(), true),
1396 ];
1397 let mut parser = parse_from_string(source);
1398 let np = NumberParser::new();
1399 let mut index = 0;
1400 parser.consume_ws();
1401 loop {
1402 if parser.is_at_eof() {
1403 break;
1404 }
1405 print!("Trying {:?}... ", result[index]);
1406 assert_eq!(result[index], np.get_text(parser.borrow_core()));
1407 println!("Done");
1408 parser.consume_ws();
1409 index += 1;
1410 }
1411 Ok(())
1412 }
1413
1414 #[test]
1415 fn get_text_no_underscores_test() -> ParseResult<()> {
1416 let source = r#"
1417 0
1418 0.0
1419 -0
1420 -0.0
1421 +0
1422 +0.0
1423 0x0
1424 0o0
1425 0b0
1426 0x765afe
1427 0b010010
1428 0o14234
1429 +0x0124ffp12
1430 +12e-14
1431 1123232e-74
1432 1123998E+21
1433 1123543P-12
1434 0xfe
1435 "#;
1436 let result = &[
1437 (Radix::Decimal, "0".to_string(), false),
1438 (Radix::Decimal, "0.0".to_string(), true),
1439 (Radix::Decimal, "-0".to_string(), false),
1440 (Radix::Decimal, "-0.0".to_string(), true),
1441 (Radix::Decimal, "0".to_string(), false),
1442 (Radix::Decimal, "0.0".to_string(), true),
1443 (Radix::Hexadecimal, "0".to_string(), false),
1444 (Radix::Octal, "0".to_string(), false),
1445 (Radix::Binary, "0".to_string(), false),
1446 (Radix::Hexadecimal, "765afe".to_string(), false),
1447 (Radix::Binary, "010010".to_string(), false),
1448 (Radix::Octal, "14234".to_string(), false),
1449 (Radix::Hexadecimal, "0124ffp12".to_string(), true),
1450 (Radix::Decimal, "12e-14".to_string(), true),
1451 (Radix::Decimal, "1123232e-74".to_string(), true),
1452 (Radix::Decimal, "1123998e21".to_string(), true),
1453 (Radix::Decimal, "1123543e-12".to_string(), true),
1454 (Radix::Hexadecimal, "fe".to_string(), false),
1455 ];
1456 let mut parser = parse_from_string(source);
1457 let mut np = NumberParser::new();
1458 np.settings.permit_underscores = false;
1459 let mut index = 0;
1460 parser.consume_ws();
1461 loop {
1462 if parser.is_at_eof() {
1463 break;
1464 }
1465 print!("Trying {:?}... ", result[index]);
1466 assert_eq!(result[index], np.get_text(parser.borrow_core()));
1467 println!("Done");
1468 parser.consume_ws();
1469 index += 1;
1470 }
1471 Ok(())
1472 }
1473
1474 #[test]
1475 fn get_text_no_hexadecimal_test() -> ParseResult<()> {
1476 let source = r#"
1477 0
1478 0.0
1479 -0
1480 -0.0
1481 +0
1482 +0.0
1483 0o0
1484 0b0
1485 0b010010
1486 0o14234
1487 +12e-14
1488 1123232e-74
1489 1123998E+21
1490 "#;
1491 let result = &[
1492 (Radix::Decimal, "0".to_string(), false),
1493 (Radix::Decimal, "0.0".to_string(), true),
1494 (Radix::Decimal, "-0".to_string(), false),
1495 (Radix::Decimal, "-0.0".to_string(), true),
1496 (Radix::Decimal, "0".to_string(), false),
1497 (Radix::Decimal, "0.0".to_string(), true),
1498 (Radix::Octal, "0".to_string(), false),
1499 (Radix::Binary, "0".to_string(), false),
1500 (Radix::Binary, "010010".to_string(), false),
1501 (Radix::Octal, "14234".to_string(), false),
1502 (Radix::Decimal, "12e-14".to_string(), true),
1503 (Radix::Decimal, "1123232e-74".to_string(), true),
1504 (Radix::Decimal, "1123998e21".to_string(), true),
1505 ];
1506 let mut parser = parse_from_string(source);
1507 let mut np = NumberParser::new();
1508 np.settings.permit_hexadecimal = false;
1509 let mut index = 0;
1510 parser.consume_ws();
1511 loop {
1512 if parser.is_at_eof() {
1513 break;
1514 }
1515 print!("Trying {:?}... ", result[index]);
1516 assert_eq!(result[index], np.get_text(parser.borrow_core()));
1517 println!("Done");
1518 parser.consume_ws();
1519 index += 1;
1520 }
1521 Ok(())
1522 }
1523
1524 #[test]
1525 fn number_parts_test() {
1526 let mut parts = NumberParts {
1527 radix: Radix::Decimal,
1528 sign: Sign::Negative,
1529 is_nan: true,
1530 is_inf: false,
1531 whole: "16383".to_string(),
1532 fraction: Some("525".to_string()),
1533 exponent_sign: Some(Sign::Negative),
1534 exponent: Some("2".to_string()),
1535 };
1536 assert_eq!(parts.as_string(true), "-nan");
1537 parts.sign = Sign::Positive;
1538 assert_eq!(parts.as_string(true), "nan");
1539 parts.is_nan = false;
1540 parts.is_inf = true;
1541 assert_eq!(parts.as_string(true), "inf");
1542 parts.sign = Sign::Negative;
1543 assert_eq!(parts.as_string(true), "-inf");
1544 parts.is_inf = false;
1545 assert_eq!(parts.as_string(true), "-16383.525e-2");
1546 parts.radix = Radix::Hexadecimal;
1547 assert_eq!(parts.as_string(false), "-16383.525p-2");
1548 assert_eq!(parts.as_string(true), "-0x16383.525p-2");
1549 parts.exponent_sign = Some(Sign::Positive);
1550 assert_eq!(parts.as_string(true), "-0x16383.525p2");
1551 }
1552
1553 #[test]
1554 fn conversions_test() {
1555 // Integer conversions. We can only convert back to signed without a possible error.
1556 let np: NumberParts = 65536000_u128.into();
1557 assert_eq!(65536000_i128, np.into());
1558 let np: NumberParts = 65536000_u64.into();
1559 assert_eq!(65536000_i64, np.into());
1560 let value = -6976363876985763_i64;
1561 let np: NumberParts = value.into();
1562 assert_eq!(-6976363876985763_i64, np.into());
1563 let value = -6976363876985763000_i128;
1564 let np: NumberParts = value.into();
1565 assert_eq!(-6976363876985763000_i128, np.into());
1566 let value = 6976363876985763_i64;
1567 let np: NumberParts = value.into();
1568 assert_eq!(6976363876985763_i64, np.into());
1569 let value = 6976363876985763000_i128;
1570 let np: NumberParts = value.into();
1571 assert_eq!(6976363876985763000_i128, np.into());
1572
1573 // Float conversions.
1574 let nan = f64::NAN;
1575 let pinf = f64::INFINITY;
1576 let ninf = f64::NEG_INFINITY;
1577 let np: NumberParts = nan.into();
1578 let back: f64 = np.into();
1579 assert!(back.is_nan());
1580 let np: NumberParts = pinf.into();
1581 assert_eq!(f64::INFINITY, np.into());
1582 let np: NumberParts = ninf.into();
1583 assert_eq!(f64::NEG_INFINITY, np.into());
1584 let np: NumberParts = 0.0_f64.into();
1585 assert_eq!(0.0_f64, np.into());
1586 let np: NumberParts = 15_f64.into();
1587 assert_eq!(15_f64, np.into());
1588 let np: NumberParts = 0.000021_f64.into();
1589 assert_eq!(0.000021_f64, np.into());
1590 let np: NumberParts = 81.743e16_f64.into();
1591 assert_eq!(81.743e16_f64, np.into());
1592 let value = -81.743e16_f64;
1593 let np: NumberParts = value.into();
1594 assert_eq!(value, np.into());
1595 let value = -81.743e-16_f64;
1596 let np: NumberParts = value.into();
1597 assert_eq!(value, np.into());
1598
1599 // Odd float conversions.
1600 let np = NumberParts {
1601 radix: Radix::Binary,
1602 sign: Sign::Negative,
1603 is_nan: false,
1604 is_inf: false,
1605 whole: "10100110".into(),
1606 fraction: Some("1".into()),
1607 exponent_sign: Some(Sign::Positive),
1608 exponent: Some("1".into()),
1609 };
1610 assert_eq!(-333.0, np.into());
1611 let np = NumberParts {
1612 radix: Radix::Octal,
1613 sign: Sign::Positive,
1614 is_nan: false,
1615 is_inf: false,
1616 whole: "0".into(),
1617 fraction: Some("3404".into()),
1618 exponent_sign: Some(Sign::Positive),
1619 exponent: Some("3".into()),
1620 };
1621 assert_eq!(224.5, np.into());
1622 let np = NumberParts {
1623 radix: Radix::Hexadecimal,
1624 sign: Sign::Negative,
1625 is_nan: false,
1626 is_inf: false,
1627 whole: "1e4".into(),
1628 fraction: Some("2".into()),
1629 exponent_sign: Some(Sign::Negative),
1630 exponent: Some("2".into()),
1631 };
1632 assert_eq!(
1633 -(256.0 + 14.0 * 16.0 + 4.0 + 2.0 / 16.0) / 16.0 / 16.0,
1634 np.into()
1635 );
1636
1637 // Failed float conversions.
1638 let np = NumberParts {
1639 radix: Radix::Octal,
1640 sign: Sign::Positive,
1641 is_nan: false,
1642 is_inf: false,
1643 whole: "0".into(),
1644 fraction: Some("384".into()),
1645 exponent_sign: Some(Sign::Positive),
1646 exponent: Some("3".into()),
1647 };
1648 let value: f64 = np.into();
1649 assert!(value.is_nan());
1650
1651 // Zeros.
1652 let np = NumberParts {
1653 radix: Radix::Octal,
1654 sign: Sign::Positive,
1655 is_nan: false,
1656 is_inf: false,
1657 whole: "".into(),
1658 fraction: None,
1659 exponent_sign: None,
1660 exponent: None,
1661 };
1662 let value: i64 = np.clone().into();
1663 assert_eq!(0, value);
1664 let value: i128 = np.into();
1665 assert_eq!(0, value);
1666 }
1667}