hayro_syntax/object/
number.rs1use crate::object::macros::object;
4use crate::object::{Object, ObjectLike};
5use crate::reader::{Readable, Reader, Skippable};
6use crate::xref::XRef;
7use log::debug;
8use std::fmt::Debug;
9use std::str::FromStr;
10
11#[derive(Clone, Copy, Debug, PartialEq)]
13pub struct Number(pub(crate) InternalNumber);
14
15impl Number {
16 pub fn as_f64(&self) -> f64 {
18 match self.0 {
19 InternalNumber::Real(r) => r as f64,
20 InternalNumber::Integer(i) => i as f64,
21 }
22 }
23
24 pub fn as_f32(&self) -> f32 {
26 match self.0 {
27 InternalNumber::Real(r) => r,
28 InternalNumber::Integer(i) => {
29 let converted = i as f32;
30
31 if converted as i32 != i {
33 debug!("integer {} was truncated to {}", i, converted);
34 }
35
36 converted
37 }
38 }
39 }
40
41 pub fn as_i32(&self) -> i32 {
43 match self.0 {
44 InternalNumber::Real(r) => {
45 let res = r as i32;
46
47 if !(r.trunc() == r) {
48 debug!("float {} was truncated to {}", r, res);
49 }
50
51 res
52 }
53 InternalNumber::Integer(i) => i,
54 }
55 }
56
57 pub fn from_f32(num: f32) -> Self {
59 Self(InternalNumber::Real(num))
60 }
61
62 pub fn from_i32(num: i32) -> Self {
64 Self(InternalNumber::Integer(num))
65 }
66}
67
68impl Skippable for Number {
69 fn skip<const PLAIN: bool>(r: &mut Reader<'_>) -> Option<()> {
70 r.forward_if(|b| b == b'+' || b == b'-');
71
72 match r.peek_byte()? {
73 b'.' => {
74 r.read_byte()?;
75 r.forward_while_1(is_digit)?;
76 }
77 (b'0'..=b'9') => {
78 r.forward_while_1(is_digit)?;
79 if let Some(()) = r.forward_tag(b".") {
80 r.forward_while(is_digit);
81 }
82 }
83 _ => return None,
84 }
85
86 Some(())
87 }
88}
89
90impl Readable<'_> for Number {
91 fn read<const PLAIN: bool>(r: &mut Reader<'_>, _: &XRef) -> Option<Self> {
92 let data = r.skip::<PLAIN, Number>()?;
97 let num = f32::from_str(std::str::from_utf8(data).ok()?).ok()?;
98
99 if num.fract() == 0.0 {
100 Some(Number(InternalNumber::Integer(num as i32)))
101 } else {
102 Some(Number(InternalNumber::Real(num)))
103 }
104 }
105}
106
107object!(Number, Number);
108
109#[derive(Clone, Copy, Debug, PartialEq)]
110pub(crate) enum InternalNumber {
111 Real(f32),
112 Integer(i32),
113}
114
115macro_rules! int_num {
116 ($i:ident) => {
117 impl Skippable for $i {
118 fn skip<const PLAIN: bool>(r: &mut Reader<'_>) -> Option<()> {
119 r.forward_if(|b| b == b'+' || b == b'-');
120 r.forward_while_1(is_digit)?;
121
122 if r.peek_byte() == Some(b'.') {
124 return None;
125 }
126
127 Some(())
128 }
129 }
130
131 impl<'a> Readable<'a> for $i {
132 fn read<const PLAIN: bool>(r: &mut Reader<'a>, xref: &'a XRef) -> Option<$i> {
133 r.read::<PLAIN, Number>(xref)
134 .map(|n| n.as_i32())
135 .and_then(|n| n.try_into().ok())
136 }
137 }
138
139 impl TryFrom<Object<'_>> for $i {
140 type Error = ();
141
142 fn try_from(value: Object<'_>) -> std::result::Result<Self, Self::Error> {
143 match value {
144 Object::Number(n) => n.as_i32().try_into().ok().ok_or(()),
145 _ => Err(()),
146 }
147 }
148 }
149
150 impl<'a> ObjectLike<'a> for $i {}
151 };
152}
153
154int_num!(i32);
155int_num!(u32);
156int_num!(u16);
157int_num!(usize);
158int_num!(u8);
159
160impl Skippable for f32 {
161 fn skip<const PLAIN: bool>(r: &mut Reader<'_>) -> Option<()> {
162 r.skip::<PLAIN, Number>().map(|_| {})
163 }
164}
165
166impl Readable<'_> for f32 {
167 fn read<const PLAIN: bool>(r: &mut Reader, _: &XRef) -> Option<Self> {
168 r.read_without_xref::<Number>().map(|n| n.as_f32())
169 }
170}
171
172impl TryFrom<Object<'_>> for f32 {
173 type Error = ();
174
175 fn try_from(value: Object<'_>) -> Result<Self, Self::Error> {
176 match value {
177 Object::Number(n) => Ok(n.as_f32()),
178 _ => Err(()),
179 }
180 }
181}
182
183impl ObjectLike<'_> for f32 {}
184
185impl Skippable for f64 {
186 fn skip<const PLAIN: bool>(r: &mut Reader<'_>) -> Option<()> {
187 r.skip::<PLAIN, Number>().map(|_| {})
188 }
189}
190
191impl Readable<'_> for f64 {
192 fn read<const PLAIN: bool>(r: &mut Reader, _: &XRef) -> Option<Self> {
193 r.read_without_xref::<Number>().map(|n| n.as_f64())
194 }
195}
196
197impl TryFrom<Object<'_>> for f64 {
198 type Error = ();
199
200 fn try_from(value: Object<'_>) -> Result<Self, Self::Error> {
201 match value {
202 Object::Number(n) => Ok(n.as_f64()),
203 _ => Err(()),
204 }
205 }
206}
207
208impl ObjectLike<'_> for f64 {}
209
210pub(crate) fn is_digit(byte: u8) -> bool {
211 byte >= b'0' && byte <= b'9'
212}
213
214#[cfg(test)]
215mod tests {
216 use crate::object::number::Number;
217 use crate::reader::Reader;
218
219 #[test]
220 fn int_1() {
221 assert_eq!(
222 Reader::new("0".as_bytes())
223 .read_without_xref::<i32>()
224 .unwrap(),
225 0
226 );
227 }
228
229 #[test]
230 fn int_3() {
231 assert_eq!(
232 Reader::new("+32".as_bytes())
233 .read_without_xref::<i32>()
234 .unwrap(),
235 32
236 );
237 }
238
239 #[test]
240 fn int_4() {
241 assert_eq!(
242 Reader::new("-32".as_bytes())
243 .read_without_xref::<i32>()
244 .unwrap(),
245 -32
246 );
247 }
248
249 #[test]
250 fn int_6() {
251 assert_eq!(
252 Reader::new("98349".as_bytes())
253 .read_without_xref::<i32>()
254 .unwrap(),
255 98349
256 );
257 }
258
259 #[test]
260 fn int_7() {
261 assert_eq!(
262 Reader::new("003245".as_bytes())
263 .read_without_xref::<i32>()
264 .unwrap(),
265 3245
266 );
267 }
268
269 #[test]
270 fn int_trailing() {
271 assert_eq!(
272 Reader::new("0abc".as_bytes())
273 .read_without_xref::<i32>()
274 .unwrap(),
275 0
276 );
277 }
278
279 #[test]
280 fn real_1() {
281 assert_eq!(
282 Reader::new("3".as_bytes())
283 .read_without_xref::<f32>()
284 .unwrap(),
285 3.0
286 );
287 }
288
289 #[test]
290 fn real_3() {
291 assert_eq!(
292 Reader::new("+32".as_bytes())
293 .read_without_xref::<f32>()
294 .unwrap(),
295 32.0
296 );
297 }
298
299 #[test]
300 fn real_4() {
301 assert_eq!(
302 Reader::new("-32".as_bytes())
303 .read_without_xref::<f32>()
304 .unwrap(),
305 -32.0
306 );
307 }
308
309 #[test]
310 fn real_5() {
311 assert_eq!(
312 Reader::new("-32.01".as_bytes())
313 .read_without_xref::<f32>()
314 .unwrap(),
315 -32.01
316 );
317 }
318
319 #[test]
320 fn real_6() {
321 assert_eq!(
322 Reader::new("-.345".as_bytes())
323 .read_without_xref::<f32>()
324 .unwrap(),
325 -0.345
326 );
327 }
328
329 #[test]
330 fn real_7() {
331 assert_eq!(
332 Reader::new("-.00143".as_bytes())
333 .read_without_xref::<f32>()
334 .unwrap(),
335 -0.00143
336 );
337 }
338
339 #[test]
340 fn real_8() {
341 assert_eq!(
342 Reader::new("-12.0013".as_bytes())
343 .read_without_xref::<f32>()
344 .unwrap(),
345 -12.0013
346 );
347 }
348
349 #[test]
350 fn real_9() {
351 assert_eq!(
352 Reader::new("98349.432534".as_bytes())
353 .read_without_xref::<f32>()
354 .unwrap(),
355 98349.432534
356 );
357 }
358
359 #[test]
360 fn real_10() {
361 assert_eq!(
362 Reader::new("-34534656.34".as_bytes())
363 .read_without_xref::<f32>()
364 .unwrap(),
365 -34534656.34
366 );
367 }
368
369 #[test]
370 fn real_trailing() {
371 assert_eq!(
372 Reader::new("0abc".as_bytes())
373 .read_without_xref::<f32>()
374 .unwrap(),
375 0.0
376 );
377 }
378
379 #[test]
380 fn real_failing() {
381 assert_eq!(
382 Reader::new("+abc".as_bytes())
383 .read_without_xref::<f32>()
384 .is_none(),
385 true
386 );
387 }
388
389 #[test]
390 fn number_1() {
391 assert_eq!(
392 Reader::new("+32".as_bytes())
393 .read_without_xref::<Number>()
394 .unwrap()
395 .as_f64() as f32,
396 32.0
397 );
398 }
399
400 #[test]
401 fn number_2() {
402 assert_eq!(
403 Reader::new("-32.01".as_bytes())
404 .read_without_xref::<Number>()
405 .unwrap()
406 .as_f64() as f32,
407 -32.01
408 );
409 }
410
411 #[test]
412 fn number_3() {
413 assert_eq!(
414 Reader::new("-.345".as_bytes())
415 .read_without_xref::<Number>()
416 .unwrap()
417 .as_f64() as f32,
418 -0.345
419 );
420 }
421}