Landin library reference source

core/text/numbers.ldn

1--  Parsing every integer width, the mirror of the builder's appends: what
2--  an append writes, the matching parse reads back.  Each scans from the
3--  left and checks its bound before it multiplies, so a refusal is the
4--  first one the scan meets and no byte past it is read.  A signed value
5--  is an optional `-` and digits, since no append writes a `+`.
6
7--  Decimal digits into a `u32` no greater than `limit`.
8scan_u32: (source: []u8, limit: u32)
9          -> (result: u32) ! invalid_number | number_overflow =
10    fail invalid_number when lenof source == 0
11    mut value: u32 = 0
12    for byte in source do
13        fail invalid_number when byte < 48 or byte > 57
14        digit: u32 = u32(byte - 48)
15        fail number_overflow when value > (limit - digit) / 10
16        value = value * 10 + digit
17    end for
18    result = value
19end scan_u32
20
21--  Decimal digits into a `u64` no greater than `limit`.
22scan_u64: (source: []u8, limit: u64)
23          -> (result: u64) ! invalid_number | number_overflow =
24    fail invalid_number when lenof source == 0
25    mut value: u64 = 0
26    for byte in source do
27        fail invalid_number when byte < 48 or byte > 57
28        digit: u64 = u64(byte - 48)
29        fail number_overflow when value > (limit - digit) / 10
30        value = value * 10 + digit
31    end for
32    result = value
33end scan_u64
34
35--  Decimal digits into a `usize` no greater than `limit`.
36scan_usize: (source: []u8, limit: usize)
37            -> (result: usize) ! invalid_number | number_overflow =
38    fail invalid_number when lenof source == 0
39    mut value: usize = 0
40    for byte in source do
41        fail invalid_number when byte < 48 or byte > 57
42        digit: usize = usize(byte - 48)
43        fail number_overflow when value > (limit - digit) / 10
44        value = value * 10 + digit
45    end for
46    result = value
47end scan_usize
48
49--  The digits after a leading `-`, or all of `source` without one.
50unsigned_part: (source: []u8) -> (digits: []u8 from source) =
51    digits = source
52    if lenof source > 0 and source[0] == 45 then
53        digits = source[1..<lenof source]
54    end if
55end unsigned_part
56
57--  Whether `source` begins with a `-`.
58negative: (source: []u8) -> (yes: bool) =
59    yes = lenof source > 0 and source[0] == 45
60end negative
61
62--- Parse nonempty ASCII decimal digits from bytes into `u8`, refusing as
63--- `parse_u32` does, with `number_overflow` above 255.
64public parse_u8: (source: []u8)
65                 -> (result: u8) ! invalid_number | number_overflow =
66    result = u8(try scan_u32(source, 255))
67end parse_u8
68
69--- Parse nonempty ASCII decimal digits from bytes into `u16`, refusing as
70--- `parse_u32` does, with `number_overflow` above 65,535.
71public parse_u16: (source: []u8)
72                  -> (result: u16) ! invalid_number | number_overflow =
73    result = u16(try scan_u32(source, 65535))
74end parse_u16
75
76--- Parse an `i8` from bytes: an optional `-` and nonempty ASCII decimal
77--- digits.  Refuses an empty slice, a lone `-`, a `+`, whitespace and any
78--- other byte with `invalid_number`, and a value outside -128 to 127 with
79--- `number_overflow`, whichever the scan meets first.
80public parse_i8: (source: []u8)
81                 -> (result: i8) ! invalid_number | number_overflow =
82    if negative(source) then
83        magnitude: u32 = try scan_u32(unsigned_part(source), 128)
84        result = i8(0 - i32(magnitude))
85    else
86        result = i8(try scan_u32(source, 127))
87    end if
88end parse_i8
89
90--- Parse an `i16` from bytes, as `parse_i8` does, within -32,768 to
91--- 32,767.
92public parse_i16: (source: []u8)
93                  -> (result: i16) ! invalid_number | number_overflow =
94    if negative(source) then
95        magnitude: u32 = try scan_u32(unsigned_part(source), 32768)
96        result = i16(0 - i32(magnitude))
97    else
98        result = i16(try scan_u32(source, 32767))
99    end if
100end parse_i16
101
102--- Parse an `i32` from bytes, as `parse_i8` does, within `i32`'s range.
103public parse_i32: (source: []u8)
104                  -> (result: i32) ! invalid_number | number_overflow =
105    if negative(source) then
106        magnitude: u32 = try scan_u32(unsigned_part(source), 2147483648)
107        if magnitude == 2147483648 then
108            result = 0 - 2147483647 - 1
109        else
110            result = 0 - i32(magnitude)
111        end if
112    else
113        result = i32(try scan_u32(source, 2147483647))
114    end if
115end parse_i32
116
117--- Parse an `i64` from bytes, as `parse_i8` does, within `i64`'s range.
118public parse_i64: (source: []u8)
119                  -> (result: i64) ! invalid_number | number_overflow =
120    if negative(source) then
121        magnitude: u64 = try scan_u64(unsigned_part(source),
122                                      9223372036854775808)
123        if magnitude == 9223372036854775808 then
124            result = 0 - 9223372036854775807 - 1
125        else
126            result = 0 - i64(magnitude)
127        end if
128    else
129        result = i64(try scan_u64(source, 9223372036854775807))
130    end if
131end parse_i64
132
133--- Parse an `isize` from bytes, as `parse_i8` does, within the target's
134--- `isize` range, so the same digits can parse on a 64-bit target and
135--- overflow on a 32-bit one.
136public parse_isize: (source: []u8)
137                    -> (result: isize) ! invalid_number | number_overflow =
138    largest: usize = (0 -% 1) >> 1
139    if negative(source) then
140        magnitude: usize = try scan_usize(unsigned_part(source), largest + 1)
141        if magnitude == largest + 1 then
142            result = 0 - isize(largest) - 1
143        else
144            result = 0 - isize(magnitude)
145        end if
146    else
147        result = isize(try scan_usize(source, largest))
148    end if
149end parse_isize
150
151--  A hexadecimal digit's value, or 16 for a byte that is not one.
152hex_value: (byte: u8) -> (value: u8) =
153    value = 16
154    if byte >= 48 and byte <= 57 then
155        value = byte - 48
156    elsif byte >= 97 and byte <= 102 then
157        value = byte - 87
158    elsif byte >= 65 and byte <= 70 then
159        value = byte - 55
160    end if
161end hex_value
162
163--- Parse nonempty hexadecimal digits from bytes into `u32`: `0` to `9`
164--- and `a` to `f` in either case, with no `0x`, sign or space.  Refuses
165--- any other byte or an empty slice with `invalid_number` and a value
166--- above `u32`'s maximum with `number_overflow`, whichever the scan meets
167--- first.
168public parse_hex_u32: (source: []u8)
169                      -> (result: u32) ! invalid_number | number_overflow =
170    fail invalid_number when lenof source == 0
171    mut value: u32 = 0
172    for byte in source do
173        digit: u8 = hex_value(byte)
174        fail invalid_number when digit == 16
175        fail number_overflow when value > u32(0 -% 1) >> 4
176        value = (value << 4) | u32(digit)
177    end for
178    result = value
179end parse_hex_u32
180
181--- Parse hexadecimal digits from bytes into `u64`, as `parse_hex_u32`
182--- does.
183public parse_hex_u64: (source: []u8)
184                      -> (result: u64) ! invalid_number | number_overflow =
185    fail invalid_number when lenof source == 0
186    mut value: u64 = 0
187    for byte in source do
188        digit: u8 = hex_value(byte)
189        fail invalid_number when digit == 16
190        fail number_overflow when value > u64(0 -% 1) >> 4
191        value = (value << 4) | u64(digit)
192    end for
193    result = value
194end parse_hex_u64
195
196--- Parse hexadecimal digits from bytes into `usize`, as `parse_hex_u32`
197--- does, at the target's width.
198public parse_hex_usize: (source: []u8)
199                        -> (result: usize) ! invalid_number | number_overflow =
200    fail invalid_number when lenof source == 0
201    mut value: usize = 0
202    for byte in source do
203        digit: u8 = hex_value(byte)
204        fail invalid_number when digit == 16
205        fail number_overflow when value > usize(0 -% 1) >> 4
206        value = (value << 4) | usize(digit)
207    end for
208    result = value
209end parse_hex_usize