safe_bigmath/
parsing.rs

1use quoth::{
2    Parsable, ParsableExt, ParseStream, Span, Spanned,
3    parsable::{Exact, Nothing},
4};
5
6use crate::SafeInt;
7
8extern crate alloc;
9
10use alloc::vec::Vec;
11
12/// Parsed representation of a fixed-scale decimal literal.
13///
14/// Useful for inspecting the raw scaled value and source span after parsing.
15#[derive(Clone, Debug, PartialEq, Eq, Hash, ParsableExt)]
16pub struct ParsedSafeDec<const D: usize> {
17    /// The scaled integer backing the parsed decimal.
18    pub raw: SafeInt,
19    /// Number of decimal places parsed (should equal `D` when valid).
20    pub decimals: usize,
21    /// Source span of the parsed literal.
22    pub span: Span,
23}
24
25impl<const D: usize> Spanned for ParsedSafeDec<D> {
26    fn span(&self) -> Span {
27        self.span.clone()
28    }
29}
30
31impl<const D: usize> Parsable for ParsedSafeDec<D> {
32    fn parse(stream: &mut ParseStream) -> quoth::Result<Self> {
33        let start_position = stream.position;
34        let is_neg = if stream.next_char()? == '-' {
35            stream.consume(1)?;
36            true
37        } else {
38            false
39        };
40        let mut major_digits = Vec::new();
41        major_digits.push(stream.parse_digit()?);
42        while let Ok(digit) = stream.parse_digit() {
43            major_digits.push(digit);
44        }
45        stream.parse_value(Exact::from("."))?;
46        let mut minor_digits = Vec::new();
47        minor_digits.push(stream.parse_digit()?);
48        while let Ok(digit) = stream.parse_digit() {
49            minor_digits.push(digit);
50        }
51        while minor_digits.len() < D {
52            minor_digits.push(0);
53        }
54        if minor_digits.len() > D {
55            return Err(quoth::Error::new(
56                stream.current_span(),
57                "unexpected extra digit",
58            ));
59        }
60
61        let num_minor_digits = minor_digits.len();
62
63        let mut digits = Vec::new();
64        digits.append(&mut major_digits);
65        digits.append(&mut minor_digits);
66        let mut raw = SafeInt::from(0);
67        for &d in &digits {
68            raw *= 10;
69            raw += d;
70        }
71        if is_neg {
72            raw = -raw;
73        }
74        stream.parse::<Nothing>()?;
75        Ok(ParsedSafeDec {
76            raw,
77            decimals: num_minor_digits,
78            span: Span::new(stream.source().clone(), start_position..stream.position),
79        })
80    }
81}
82
83/// Parsed representation of an integer literal.
84///
85/// Useful for inspecting the parsed value and source span.
86#[derive(Clone, Debug, PartialEq, Eq, Hash, ParsableExt)]
87pub struct ParsedSafeInt {
88    /// Parsed integer value.
89    pub value: SafeInt,
90    /// Source span of the parsed literal.
91    pub span: Span,
92}
93
94impl Spanned for ParsedSafeInt {
95    fn span(&self) -> Span {
96        self.span.clone()
97    }
98}
99
100impl Parsable for ParsedSafeInt {
101    fn parse(stream: &mut ParseStream) -> quoth::Result<Self> {
102        let start_position = stream.position;
103        let is_neg = if stream.next_char()? == '-' {
104            stream.consume(1)?;
105            true
106        } else {
107            false
108        };
109        let mut digits = Vec::new();
110        digits.push(stream.parse_digit()?);
111        while let Ok(digit) = stream.parse_digit() {
112            digits.push(digit);
113        }
114        let mut raw = SafeInt::from(0);
115        for &d in &digits {
116            raw *= 10;
117            raw += d;
118        }
119        if is_neg {
120            raw = -raw;
121        }
122        stream.parse::<Nothing>()?;
123        Ok(ParsedSafeInt {
124            value: raw,
125            span: Span::new(stream.source().clone(), start_position..stream.position),
126        })
127    }
128}
129
130#[test]
131fn test_parse_safe_dec_valid_same_digits() {
132    let mut stream = ParseStream::from("-3487834.885");
133    let parsed = stream.parse::<ParsedSafeDec<3>>().unwrap();
134    assert_eq!(parsed.raw, -3487834885i64);
135}
136
137#[test]
138fn test_parse_safe_dec_invalid_extra_char() {
139    let mut stream = ParseStream::from("-3487834.885a");
140    stream.parse::<ParsedSafeDec<3>>().unwrap_err();
141    let mut stream = ParseStream::from("-3487a834.885");
142    stream.parse::<ParsedSafeDec<3>>().unwrap_err();
143    let mut stream = ParseStream::from("-a3487834.885");
144    stream.parse::<ParsedSafeDec<3>>().unwrap_err();
145    let mut stream = ParseStream::from("a-3487834.885");
146    stream.parse::<ParsedSafeDec<3>>().unwrap_err();
147}
148
149#[test]
150fn test_parse_safe_dec_valid_less_digits_a() {
151    let mut stream = ParseStream::from("7488793498789.9");
152    let parsed = stream.parse::<ParsedSafeDec<7>>().unwrap();
153    assert_eq!(parsed.raw, 74887934987899000000u128);
154}
155
156#[test]
157fn test_parse_safe_dec_valid_less_digits_b() {
158    let mut stream = ParseStream::from("123.456");
159    let parsed = stream.parse::<ParsedSafeDec<6>>().unwrap();
160    assert_eq!(parsed.raw, 123456000);
161}
162
163#[test]
164fn test_parse_safe_dec_invalid_more_digits() {
165    let mut stream = ParseStream::from("7488793498789.00487347878");
166    stream.parse::<ParsedSafeDec<3>>().unwrap_err();
167}
168
169#[test]
170fn test_parse_safe_dec_invalid_no_dot() {
171    let mut stream = ParseStream::from("748879349878900487347878");
172    stream.parse::<ParsedSafeDec<44>>().unwrap_err();
173}
174
175#[test]
176fn test_parse_safe_dec_invalid_no_minor_digits() {
177    let mut stream = ParseStream::from("7488793498789.");
178    stream.parse::<ParsedSafeDec<3>>().unwrap_err();
179    let mut stream = ParseStream::from("7488793498789.");
180    stream.parse::<ParsedSafeDec<0>>().unwrap_err();
181}
182
183#[test]
184fn test_parse_safe_int_valid_positive() {
185    assert_eq!(
186        ParseStream::from("123456")
187            .parse::<ParsedSafeInt>()
188            .unwrap()
189            .value,
190        123456
191    );
192    assert_eq!(
193        *ParseStream::from(
194            "112233445566778829879879823749798798982893947293749823798729387293849234"
195        )
196        .parse::<ParsedSafeInt>()
197        .unwrap()
198        .value
199        .raw(),
200        "112233445566778829879879823749798798982893947293749823798729387293849234"
201            .parse::<num_bigint::BigInt>()
202            .unwrap()
203    );
204    assert_eq!(
205        ParseStream::from("0")
206            .parse::<ParsedSafeInt>()
207            .unwrap()
208            .value,
209        0
210    );
211}
212
213#[test]
214fn test_parse_safe_int_valid_negative() {
215    assert_eq!(
216        ParseStream::from("-0")
217            .parse::<ParsedSafeInt>()
218            .unwrap()
219            .value,
220        0
221    );
222    assert_eq!(
223        ParseStream::from("-1")
224            .parse::<ParsedSafeInt>()
225            .unwrap()
226            .value,
227        -1
228    );
229    assert_eq!(
230        ParseStream::from("-98439874987948333035")
231            .parse::<ParsedSafeInt>()
232            .unwrap()
233            .value,
234        -98439874987948333035i128
235    );
236}
237
238#[test]
239fn test_parse_safe_int_invalid_decimal() {
240    ParseStream::from("123.456")
241        .parse::<ParsedSafeInt>()
242        .unwrap_err();
243}
244
245#[test]
246fn test_parse_safe_int_invalid_non_alpha() {
247    ParseStream::from("123a456")
248        .parse::<ParsedSafeInt>()
249        .unwrap_err();
250}