kalosm_sample/structured_parser/
float.rs1use crate::{CreateParserState, ParseStatus, Parser};
2use std::ops::RangeInclusive;
3
4#[derive(Debug, PartialEq, Eq, Default, Copy, Clone)]
5enum FloatParserProgress {
6 #[default]
7 Initial,
8 AfterSign,
9 AfterDigit,
10 AfterDecimalPoint {
11 digits_after_decimal_point: u32,
12 },
13}
14
15impl FloatParserProgress {
16 fn is_after_digit(&self) -> bool {
17 matches!(
18 self,
19 FloatParserProgress::AfterDigit | FloatParserProgress::AfterDecimalPoint { .. }
20 )
21 }
22}
23
24#[derive(Debug, PartialEq, Copy, Clone)]
26pub struct FloatParserState {
27 state: FloatParserProgress,
28 value: f64,
29 positive: bool,
30}
31
32impl Default for FloatParserState {
33 fn default() -> Self {
34 Self {
35 state: FloatParserProgress::Initial,
36 value: 0.0,
37 positive: true,
38 }
39 }
40}
41
42#[derive(Debug, PartialEq, Clone)]
44pub struct FloatParser {
45 range: RangeInclusive<f64>,
46}
47
48impl FloatParser {
49 pub fn new(range: RangeInclusive<f64>) -> Self {
51 if range.start() > range.end() {
52 Self {
53 range: *range.end()..=*range.start(),
54 }
55 } else {
56 Self { range }
57 }
58 }
59}
60
61impl CreateParserState for FloatParser {
62 fn create_parser_state(&self) -> <Self as Parser>::PartialState {
63 FloatParserState::default()
64 }
65}
66
67impl FloatParser {
68 fn sign_valid(&self, positive: bool) -> bool {
69 if positive {
70 *self.range.start() >= 0.0
71 } else {
72 *self.range.end() <= 0.0
73 }
74 }
75
76 fn is_number_valid(&self, value: f64) -> bool {
77 self.range.contains(&value)
78 }
79
80 fn could_number_become_valid_before_decimal(
81 &self,
82 value: f64,
83 state: FloatParserProgress,
84 ) -> bool {
85 if self.is_number_valid(value) {
86 true
87 } else {
88 let num_with_extra_digit = value * 10.;
89 if value < 0. {
90 if *self.range.start() > num_with_extra_digit {
91 return false;
92 }
93 } else if *self.range.end() < num_with_extra_digit {
94 return false;
95 }
96 let value_string = value.abs().to_string();
97 let start_value_string = self.range.start().abs().to_string();
98 let end_value_string = self.range.end().abs().to_string();
99 match state {
100 FloatParserProgress::AfterDigit | FloatParserProgress::AfterSign => {
101 let digits = value_string.chars();
103 let start_digits = start_value_string.chars();
104 let end_digits = end_value_string.chars();
105 for (digit, (start_digit, end_digit)) in
106 digits.zip(start_digits.zip(end_digits))
107 {
108 if digit < start_digit || digit > end_digit {
109 return false;
110 }
111 }
112 }
113 _ => {}
114 }
115 true
116 }
117 }
118
119 fn could_number_become_valid_after_decimal(
120 &self,
121 value: f64,
122 digits_after_decimal_point: u32,
123 ) -> bool {
124 let distance = if value < 0.0 {
125 *self.range.start() - value
126 } else {
127 *self.range.end() - value
128 };
129 println!("Distance: {}", distance);
130
131 distance < 10.0_f64.powi(-(digits_after_decimal_point as i32))
132 }
133}
134
135#[derive(Debug)]
137pub struct LeadingZeroError;
138
139impl std::fmt::Display for LeadingZeroError {
140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141 write!(
142 f,
143 "Found leading zero. Leading zeros are not allowed when parsing a number"
144 )
145 }
146}
147
148impl std::error::Error for LeadingZeroError {}
149
150#[derive(Debug)]
152pub struct OutOfRangeError;
153
154impl std::fmt::Display for OutOfRangeError {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 write!(f, "Attempted to parse a number that was out of range")
157 }
158}
159
160impl std::error::Error for OutOfRangeError {}
161
162#[derive(Debug)]
164pub struct InvalidDecimalLocation;
165
166impl std::fmt::Display for InvalidDecimalLocation {
167 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
168 write!(
169 f,
170 "Failed to parse a number with a decimal before the first digit or multiple decimals"
171 )
172 }
173}
174
175impl std::error::Error for InvalidDecimalLocation {}
176
177#[derive(Debug)]
179pub struct InvalidSignLocation;
180
181impl std::fmt::Display for InvalidSignLocation {
182 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183 write!(
184 f,
185 "Failed to parse a number with a sign after the first character"
186 )
187 }
188}
189
190impl std::error::Error for InvalidSignLocation {}
191
192#[derive(Debug)]
194pub struct EmptyNumber;
195
196impl std::fmt::Display for EmptyNumber {
197 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198 write!(f, "Failed to parse a number with no digits")
199 }
200}
201
202impl std::error::Error for EmptyNumber {}
203
204impl Parser for FloatParser {
205 type Output = f64;
206 type PartialState = FloatParserState;
207
208 fn parse<'a>(
209 &self,
210 state: &FloatParserState,
211 input: &'a [u8],
212 ) -> crate::ParseResult<ParseStatus<'a, Self::PartialState, Self::Output>> {
213 let mut value = state.value;
214 let mut positive = state.positive;
215 let mut state = state.state;
216
217 for index in 0..input.len() {
218 let input_byte = input[index];
219 let digit = match input_byte {
220 b'0'..=b'9' => {
221 if (state == FloatParserProgress::Initial
222 || state == FloatParserProgress::AfterSign)
223 && input_byte == b'0'
224 {
225 crate::bail!(LeadingZeroError);
226 }
227 input_byte - b'0'
228 }
229 b'.' => {
230 let value_digits = value.abs().log10() + 1.;
231 let start_digits = self.range.start().abs().log10() + 1.;
232 let end_digits = self.range.end().abs().log10() + 1.;
233 if positive {
234 if value_digits > end_digits {
235 crate::bail!(OutOfRangeError);
236 }
237 } else if value_digits > start_digits {
238 crate::bail!(OutOfRangeError);
239 }
240 if state == FloatParserProgress::AfterDigit {
241 state = FloatParserProgress::AfterDecimalPoint {
242 digits_after_decimal_point: 0,
243 };
244 continue;
245 } else {
246 crate::bail!(InvalidDecimalLocation);
247 }
248 }
249 b'+' | b'-' => {
250 if state == FloatParserProgress::Initial {
251 state = FloatParserProgress::AfterSign;
252 positive = input_byte == b'+';
253
254 if !self.sign_valid(positive) {
255 crate::bail!(InvalidSignLocation);
256 }
257 continue;
258 } else {
259 crate::bail!(InvalidSignLocation);
260 }
261 }
262 _ => {
263 if state.is_after_digit() {
264 let result = value * if positive { 1.0 } else { -1.0 };
265 if self.is_number_valid(result) {
266 return Ok(ParseStatus::Finished {
267 result,
268 remaining: &input[index..],
269 });
270 }
271 return Ok(ParseStatus::Finished {
272 result,
273 remaining: &input[index..],
274 });
275 } else {
276 crate::bail!(EmptyNumber)
277 }
278 }
279 };
280
281 match &mut state {
282 FloatParserProgress::Initial => {
283 state = FloatParserProgress::AfterDigit;
284 value = f64::from(digit);
285 }
286 FloatParserProgress::AfterSign => {
287 state = FloatParserProgress::AfterDigit;
288 value = f64::from(digit);
289 }
290 FloatParserProgress::AfterDigit => {
291 value = value * 10.0 + f64::from(digit);
292
293 if !self.could_number_become_valid_before_decimal(
294 value * if positive { 1.0 } else { -1.0 },
295 FloatParserProgress::AfterDigit,
296 ) {
297 crate::bail!(OutOfRangeError);
298 }
299 }
300 FloatParserProgress::AfterDecimalPoint {
301 digits_after_decimal_point,
302 } => {
303 value +=
304 f64::from(digit) / 10.0_f64.powi(*digits_after_decimal_point as i32 + 1);
305 *digits_after_decimal_point += 1;
306
307 let signed_value = value * if positive { 1.0 } else { -1.0 };
308 if !self.range.contains(&signed_value)
309 && !self.could_number_become_valid_after_decimal(
310 signed_value,
311 *digits_after_decimal_point,
312 )
313 {
314 crate::bail!(OutOfRangeError);
315 }
316 }
317 }
318 }
319
320 Ok(ParseStatus::Incomplete {
321 new_state: FloatParserState {
322 state,
323 value,
324 positive,
325 },
326 required_next: Default::default(),
327 })
328 }
329}
330
331#[test]
332fn float_parser() {
333 let parser = FloatParser {
334 range: -100.0..=200.0,
335 };
336 let state = FloatParserState::default();
337 assert_eq!(
338 parser.parse(&state, b"123").unwrap(),
339 ParseStatus::Incomplete {
340 new_state: FloatParserState {
341 state: FloatParserProgress::AfterDigit,
342 value: 123.0,
343 positive: true
344 },
345 required_next: Default::default()
346 }
347 );
348 assert_eq!(
349 parser.parse(&state, b"123.456").unwrap(),
350 ParseStatus::Incomplete {
351 new_state: FloatParserState {
352 state: FloatParserProgress::AfterDecimalPoint {
353 digits_after_decimal_point: 3
354 },
355 value: 123.456,
356 positive: true
357 },
358 required_next: Default::default()
359 }
360 );
361 assert_eq!(
362 parser
363 .parse(
364 &parser
365 .parse(&state, b"123.456")
366 .unwrap()
367 .unwrap_incomplete()
368 .0,
369 b"789x"
370 )
371 .unwrap(),
372 ParseStatus::Finished {
373 result: 123.456789,
374 remaining: b"x"
375 }
376 );
377 assert_eq!(
378 parser.parse(&state, b"123.456x").unwrap(),
379 ParseStatus::Finished {
380 result: 123.456,
381 remaining: b"x"
382 }
383 );
384 assert!(parser.parse(&state, b"abc").is_err());
385}