Skip to main content

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}