crystal_cif_io/grammar/numeric_values/
float.rs

1use std::fmt::Display;
2
3use winnow::{
4    ascii::{digit0, digit1},
5    combinator::{alt, repeat, separated_pair, terminated},
6    error::StrContext,
7    PResult, Parser,
8};
9
10use crate::grammar::{SyntacticUnit, Value};
11
12use super::{exponent::Exponent, integer::Integer, Number, Numeric};
13
14#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
15pub struct Float(pub f32);
16
17impl Display for Float {
18    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        <f32 as Display>::fmt(&self.0, fmt)
20    }
21}
22
23impl std::ops::Deref for Float {
24    type Target = f32;
25
26    fn deref(&self) -> &Self::Target {
27        &self.0
28    }
29}
30
31fn sign_zero_or_one(input: &mut &str) -> PResult<String> {
32    repeat(0..=1, alt(('+', '-')))
33        .context(StrContext::Label("{'+'|'-'}?"))
34        .parse_next(input)
35}
36
37fn exponent_zero_or_one(input: &mut &str) -> PResult<String> {
38    repeat(0..=1, Exponent::parser.map(|e| e.formatted_output()))
39        .context(StrContext::Label("{<Exponent>}?"))
40        .map(|exps: Vec<String>| exps.concat())
41        .parse_next(input)
42}
43
44fn float_1(input: &mut &str) -> PResult<f32> {
45    (Integer::parser, Exponent::parser)
46        .map(|(i, e)| *i as f32 * 10_f32.powi(*e))
47        .context(StrContext::Label("<Integer><Exponent>"))
48        .parse_next(input)
49}
50
51fn float_2(input: &mut &str) -> PResult<f32> {
52    (
53        sign_zero_or_one,
54        alt((
55            separated_pair(digit0, '.', digit1).map(|(i, decimal)| format!("{i}.{decimal}")),
56            terminated(digit1, '.').map(|d| format!("{d}.")),
57        )),
58        exponent_zero_or_one,
59    )
60        .map(|(sign, float, exp)| {
61            format!("{sign}{float}{exp}")
62                .parse::<f32>()
63                .expect("Failed to parse as f32")
64        })
65        .parse_next(input)
66}
67
68impl SyntacticUnit for Float {
69    type ParseResult = Self;
70
71    type FormatOutput = f32;
72
73    fn parser(input: &mut &str) -> winnow::prelude::PResult<Self::ParseResult> {
74        alt((float_1, float_2)).map(Float).parse_next(input)
75    }
76
77    fn formatted_output(&self) -> Self::FormatOutput {
78        self.0
79    }
80}
81
82impl From<f64> for Float {
83    fn from(value: f64) -> Self {
84        Float(value as f32)
85    }
86}
87
88impl From<Float> for Number {
89    fn from(value: Float) -> Self {
90        Number::Float(value)
91    }
92}
93
94impl From<Float> for Numeric {
95    fn from(value: Float) -> Self {
96        Self::new(Number::from(value), None)
97    }
98}
99
100impl From<Float> for Value {
101    fn from(value: Float) -> Self {
102        Value::Numeric(Numeric::from(value))
103    }
104}
105
106impl From<Float> for f64 {
107    fn from(value: Float) -> Self {
108        *value as f64
109    }
110}
111
112#[cfg(test)]
113mod test {
114    use crate::grammar::{Float, SyntacticUnit};
115
116    #[test]
117    fn float_parsing() {
118        let mut input = "0.0033";
119        dbg!(Float::parser(&mut input).unwrap());
120        dbg!(Float::parser(&mut ".4254").unwrap());
121    }
122}