aimx/literals/
number.rs

1//! Numeric literal parsing for AIMX expressions.
2//!
3//! Provides parsers for numeric literals (integers, floats, scientific notation, and
4//! special values) used by the expression literal layer.
5
6use nom::{
7    IResult, Parser,
8    branch::alt,
9    bytes::complete::tag,
10    character::complete::{char, digit1, one_of},
11    combinator::{map, map_res, opt, recognize},
12};
13
14/// Parse a number literal into `f64`.
15///
16/// Accepts standard decimal, fractional, scientific (`e`/`E`), and special
17/// values (`NaN`, `±Inf`) used by AIMX.
18///
19/// Returns remaining input and parsed value.
20pub fn parse_number(input: &str) -> IResult<&str, f64> {
21    // First try to parse special float values
22    if let Ok(result) = alt((
23        map(tag::<_, _, nom::error::Error<&str>>("NaN"), |_| f64::NAN),
24        map(tag::<_, _, nom::error::Error<&str>>("Inf"), |_| f64::INFINITY),
25        map(tag::<_, _, nom::error::Error<&str>>("+Inf"), |_| f64::INFINITY),
26        map(tag::<_, _, nom::error::Error<&str>>("-Inf"), |_| {
27            f64::NEG_INFINITY
28        }),
29    ))
30    .parse(input)
31    {
32        return Ok(result);
33    }
34
35    // If special values don't match, try regular numbers
36    map_res(
37        alt((parse_scientific_notation, parse_float, parse_integer)),
38        |s: &str| s.parse::<f64>(),
39    )
40    .parse(input)
41}
42
43/// Parse an unsigned 32-bit integer.
44///
45/// Consumes one or more decimal digits and returns `u32` with remaining input.
46/// Used for indices and other non-negative values.
47pub fn parse_unsigned(input: &str) -> IResult<&str, u32> {
48    map_res(digit1, |s: &str| s.parse::<u32>()).parse(input)
49}
50
51/// Parse numbers in scientific notation for `parse_number`.
52///
53/// Internal helper; not intended for direct use.
54fn parse_scientific_notation(input: &str) -> IResult<&str, &str> {
55    recognize((
56        alt((parse_float, parse_integer)),
57        one_of("eE"),
58        opt(one_of("+-")),
59        digit1,
60    ))
61    .parse(input)
62}
63
64/// Parse floating-point numbers for `parse_number`.
65///
66/// Internal helper; accepts signed and unsigned decimal forms.
67
68fn parse_float(input: &str) -> IResult<&str, &str> {
69    recognize((
70        opt(char('-')),
71        alt((
72            // Pattern: digits.digits (e.g., 123.456)
73            recognize((digit1, char('.'), digit1)),
74            // Pattern: .digits (e.g., .5)
75            recognize((char('.'), digit1)),
76            // Pattern: digits. (e.g., 123.)
77            recognize((digit1, char('.'))),
78        )),
79    ))
80    .parse(input)
81}
82
83/// Parse integer numbers for `parse_number`.
84///
85/// Internal helper; accepts optional leading `-`.
86fn parse_integer(input: &str) -> IResult<&str, &str> {
87    recognize((opt(char('-')), digit1)).parse(input)
88}