postscript/compact1/
number.rs1use crate::Result;
2
3macro_rules! reject(() => (raise!("found a malformed number")));
4
5#[derive(Clone, Copy, Debug, PartialEq)]
7pub enum Number {
8 Integer(i32),
10 Real(f32),
12}
13
14impl From<f32> for Number {
15 #[inline]
16 fn from(value: f32) -> Self {
17 Number::Real(value)
18 }
19}
20
21impl From<i32> for Number {
22 #[inline]
23 fn from(value: i32) -> Self {
24 Number::Integer(value)
25 }
26}
27
28impl crate::value::Read for Number {
29 fn read<T: crate::tape::Read>(tape: &mut T) -> Result<Self> {
30 let first = tape.take::<u8>()?;
31 Ok(match first {
32 0x20..=0xf6 => Number::Integer(first as i32 - 139),
33 0xf7..=0xfa => {
34 Number::Integer((first as i32 - 247) * 256 + tape.take::<u8>()? as i32 + 108)
35 }
36 0xfb..=0xfe => {
37 Number::Integer(-(first as i32 - 251) * 256 - tape.take::<u8>()? as i32 - 108)
38 }
39 0x1c => Number::Integer(tape.take::<u16>()? as i16 as i32),
40 0x1d => Number::Integer(tape.take::<u32>()? as i32),
41 0x1e => Number::Real(parse(tape)?),
42 _ => reject!(),
43 })
44 }
45}
46
47fn parse<T: crate::tape::Read>(tape: &mut T) -> Result<f32> {
48 let mut buffer = String::new();
49 let mut byte = 0;
50 let mut high = true;
51 loop {
52 let nibble = match high {
53 true => {
54 byte = tape.take::<u8>()?;
55 byte >> 4
56 }
57 false => byte & 0x0f,
58 };
59 high = !high;
60 match nibble {
61 0..=9 => buffer.push((b'0' + nibble) as char),
62 0x0a => buffer.push('.'),
63 0x0b => buffer.push('e'),
64 0x0c => buffer.push_str("e-"),
65 0x0e => buffer.push('-'),
66 0x0f => break,
67 _ => reject!(),
68 }
69 }
70 match buffer.parse() {
71 Ok(value) => Ok(value),
72 _ => reject!(),
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use std::io::Cursor;
79
80 use super::Number;
81 use crate::tape::Read;
82
83 #[test]
84 fn integer() {
85 macro_rules! read(
86 ($tape:expr) => (
87 match $tape.take().unwrap() {
88 Number::Integer(value) => value,
89 _ => unreachable!(),
90 }
91 )
92 );
93
94 let mut tape = Cursor::new(vec![0x8b]);
95 assert_eq!(read!(tape), 0);
96
97 let mut tape = Cursor::new(vec![0xef]);
98 assert_eq!(read!(tape), 100);
99
100 let mut tape = Cursor::new(vec![0x27]);
101 assert_eq!(read!(tape), -100);
102
103 let mut tape = Cursor::new(vec![0xfa, 0x7c]);
104 assert_eq!(read!(tape), 1000);
105
106 let mut tape = Cursor::new(vec![0xfe, 0x7c]);
107 assert_eq!(read!(tape), -1000);
108
109 let mut tape = Cursor::new(vec![0x1c, 0x27, 0x10]);
110 assert_eq!(read!(tape), 10000);
111
112 let mut tape = Cursor::new(vec![0x1c, 0xd8, 0xf0]);
113 assert_eq!(read!(tape), -10000);
114
115 let mut tape = Cursor::new(vec![0x1d, 0x00, 0x01, 0x86, 0xa0]);
116 assert_eq!(read!(tape), 100000);
117
118 let mut tape = Cursor::new(vec![0x1d, 0xff, 0xfe, 0x79, 0x60]);
119 assert_eq!(read!(tape), -100000);
120 }
121
122 #[test]
123 fn real() {
124 macro_rules! read(
125 ($tape:expr) => (
126 match $tape.take().unwrap() {
127 Number::Real(value) => value,
128 _ => unreachable!(),
129 }
130 )
131 );
132
133 let mut tape = Cursor::new(vec![0x1e, 0xe2, 0xa2, 0x5f, 0x0f]);
134 assert!((read!(tape) + 2.25).abs() < 1e-14);
135
136 let mut tape = Cursor::new(vec![0x1e, 0x0a, 0x14, 0x05, 0x41, 0xc3, 0xff, 0x0f]);
137 assert!((read!(tape) - 0.140541e-3).abs() < 1e-14);
138 }
139}