trey/ctab/
decimal.rs

1use std::fmt::Display;
2
3use lyn::Scanner;
4
5use super::Error;
6
7#[derive(Debug, PartialEq, Clone)]
8pub struct Decimal(String);
9
10impl TryFrom<&str> for Decimal {
11    type Error = Error;
12
13    fn try_from(value: &str) -> Result<Self, Self::Error> {
14        let mut scanner = Scanner::new(value);
15
16        match decimal(&mut scanner) {
17            Ok(entered) => {
18                if entered {
19                    if scanner.is_done() {
20                        Ok(Self(value.into()))
21                    } else {
22                        Err(Error::DecimalFormat)
23                    }
24                } else {
25                    Err(Error::DecimalFormat)
26                }
27            }
28            Err(err) => Err(err),
29        }
30    }
31}
32
33impl Default for Decimal {
34    fn default() -> Self {
35        Self("0".into())
36    }
37}
38
39impl From<&Decimal> for f32 {
40    fn from(value: &Decimal) -> Self {
41        value.0.parse::<f32>().expect("f32 string")
42    }
43}
44
45impl From<f32> for Decimal {
46    fn from(value: f32) -> Self {
47        Self(value.to_string())
48    }
49}
50
51impl From<&Decimal> for f64 {
52    fn from(value: &Decimal) -> Self {
53        value.0.parse::<f64>().expect("f64 string")
54    }
55}
56
57impl Display for Decimal {
58    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59        self.0.fmt(f)
60    }
61}
62
63// Based on a formal grammar presented in:
64// https://cs.stackexchange.com/questions/99865/
65
66// <decimal> ::= <sign>?<nonnegative_decimal>
67fn decimal(scanner: &mut Scanner) -> Result<bool, Error> {
68    sign(scanner)?;
69
70    nonnegative_decimal(scanner)
71}
72
73// <sign> ::= "-"" | "+"
74fn sign(scanner: &mut Scanner) -> Result<bool, Error> {
75    Ok(match scanner.peek() {
76        Some('+' | '-') => {
77            scanner.pop();
78
79            true
80        }
81        _ => false,
82    })
83}
84
85// <nonnegative_decimal> ::= <nonnegative_integer> <fractional_part>?
86fn nonnegative_decimal(scanner: &mut Scanner) -> Result<bool, Error> {
87    Ok(if nonnegative_integer(scanner)? {
88        fractional_part(scanner)?;
89
90        true
91    } else {
92        false
93    })
94}
95
96// <nonnegative_integer> ::= <positive_integer> | <zero>
97fn nonnegative_integer(scanner: &mut Scanner) -> Result<bool, Error> {
98    Ok(positive_integer(scanner)? || zero(scanner)?)
99}
100
101// <fractional_part> ::= <dot> <digits>
102fn fractional_part(scanner: &mut Scanner) -> Result<bool, Error> {
103    if dot(scanner)? {
104        if digits(scanner)? {
105            Ok(true)
106        } else {
107            Err(Error::DecimalFormat)
108        }
109    } else {
110        Ok(false)
111    }
112}
113
114// <positive_integer> ::= <nonzero_digit> <digits>?
115fn positive_integer(scanner: &mut Scanner) -> Result<bool, Error> {
116    Ok(if nonzero_digit(scanner)? {
117        digits(scanner)?;
118
119        true
120    } else {
121        false
122    })
123}
124
125// <digits> ::= <digit> <digit>*
126fn digits(scanner: &mut Scanner) -> Result<bool, Error> {
127    Ok(if digit(scanner)? {
128        loop {
129            if !digit(scanner)? {
130                break true;
131            }
132        }
133    } else {
134        false
135    })
136}
137
138// <digit> ::= <nonzero_digit> | <zero>
139fn digit(scanner: &mut Scanner) -> Result<bool, Error> {
140    Ok(nonzero_digit(scanner)? | zero(scanner)?)
141}
142
143// <nonzero_digit> ::= 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
144fn nonzero_digit(scanner: &mut Scanner) -> Result<bool, Error> {
145    Ok(match scanner.peek() {
146        Some('1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') => {
147            scanner.pop();
148
149            true
150        }
151        _ => false,
152    })
153}
154
155// <zero> ::= "0"
156fn zero(scanner: &mut Scanner) -> Result<bool, Error> {
157    Ok(scanner.take(&'0'))
158}
159
160// <dot> ::= "."
161fn dot(scanner: &mut Scanner) -> Result<bool, Error> {
162    Ok(scanner.take(&'.'))
163}
164
165#[cfg(test)]
166pub mod decimal {
167    use super::*;
168    use pretty_assertions::assert_eq;
169
170    #[test]
171    fn alpha() {
172        let mut scanner = Scanner::new("abc");
173
174        assert_eq!(decimal(&mut scanner), Ok(false))
175    }
176
177    #[test]
178    fn zero() {
179        let mut scanner = Scanner::new("0");
180
181        assert_eq!(decimal(&mut scanner), Ok(true))
182    }
183
184    #[test]
185    fn plus_zero() {
186        let mut scanner = Scanner::new("+0");
187
188        assert_eq!(decimal(&mut scanner), Ok(true))
189    }
190
191    #[test]
192    fn minus_zero() {
193        let mut scanner = Scanner::new("-0");
194
195        assert_eq!(decimal(&mut scanner), Ok(true))
196    }
197
198    #[test]
199    fn one() {
200        let mut scanner = Scanner::new("1");
201
202        assert_eq!(decimal(&mut scanner), Ok(true))
203    }
204
205    #[test]
206    fn plus_one() {
207        let mut scanner = Scanner::new("+1");
208
209        assert_eq!(decimal(&mut scanner), Ok(true))
210    }
211
212    #[test]
213    fn minus_one() {
214        let mut scanner = Scanner::new("-1");
215
216        assert_eq!(decimal(&mut scanner), Ok(true))
217    }
218
219    #[test]
220    fn zero_one() {
221        let mut scanner = Scanner::new("01");
222
223        assert_eq!(decimal(&mut scanner), Ok(true))
224    }
225
226    #[test]
227    fn point() {
228        let mut scanner = Scanner::new(".");
229
230        assert_eq!(decimal(&mut scanner), Ok(false))
231    }
232
233    #[test]
234    fn point_zero() {
235        let mut scanner = Scanner::new(".0");
236
237        assert_eq!(decimal(&mut scanner), Ok(false))
238    }
239
240    #[test]
241    fn zero_point() {
242        let mut scanner = Scanner::new("0.");
243
244        assert_eq!(decimal(&mut scanner), Err(Error::DecimalFormat))
245    }
246
247    #[test]
248    fn zero_point_zero() {
249        let mut scanner = Scanner::new("0.0");
250
251        assert_eq!(decimal(&mut scanner), Ok(true))
252    }
253
254    #[test]
255    fn zero_point_one() {
256        let mut scanner = Scanner::new("0.1");
257
258        assert_eq!(decimal(&mut scanner), Ok(true))
259    }
260
261    #[test]
262    fn zero_point_zero_x() {
263        let mut scanner = Scanner::new("0.0x");
264
265        assert_eq!(decimal(&mut scanner), Ok(true))
266    }
267}
268
269#[cfg(test)]
270pub mod float_from_str {
271    use super::*;
272    use pretty_assertions::assert_eq;
273
274    #[test]
275    fn alpha() {
276        assert_eq!(Decimal::try_from("abc"), Err(Error::DecimalFormat))
277    }
278
279    #[test]
280    fn zero_point_zero_x() {
281        assert_eq!(Decimal::try_from("0.0x"), Err(Error::DecimalFormat))
282    }
283
284    #[test]
285    fn valid() {
286        assert_eq!(
287            Decimal::try_from("+3.14159"),
288            Ok(Decimal("+3.14159".into()))
289        )
290    }
291}
292
293#[cfg(test)]
294pub mod f32_from_float {
295    use super::*;
296    use pretty_assertions::assert_eq;
297
298    #[test]
299    fn test() {
300        let float = Decimal::try_from("1.23").unwrap();
301
302        assert_eq!(f32::from(&float), 1.23f32)
303    }
304}
305
306#[cfg(test)]
307pub mod float_from_f32 {
308    use super::*;
309    use pretty_assertions::assert_eq;
310
311    #[test]
312    fn test() {
313        assert_eq!(Decimal::from(1.23f32), Decimal::try_from("1.23").unwrap())
314    }
315}
316
317#[cfg(test)]
318pub mod f64_from_float {
319    use super::*;
320    use pretty_assertions::assert_eq;
321
322    #[test]
323    fn test() {
324        let float = Decimal::try_from("1.23").unwrap();
325
326        assert_eq!(f64::from(&float), 1.23)
327    }
328}