udled_tokenizers/
numeric.rs1use alloc::string::ToString;
2use udled::{
3 any,
4 token::{Digit, Opt, Spanned},
5 Error, Item, Reader, Span, StringExt, Tokenizer,
6};
7#[derive(Debug, Clone, Copy, Default)]
9pub struct Int;
10
11impl Tokenizer for Int {
12 type Token<'a> = Item<i128>;
13 fn to_token<'a>(&self, reader: &mut Reader<'_, 'a>) -> Result<Self::Token<'a>, Error> {
14 let mut val: i128 = 0;
15
16 let start = reader.position();
17
18 let sign = if reader.parse("-").is_ok() { -1 } else { 1 };
19
20 let mut base = 10;
21 if reader.parse("0x").is_ok() {
22 base = 16
23 };
24 if reader.parse("0b").is_ok() {
25 base = 2
26 };
27
28 loop {
29 let ch = reader.parse(Digit(base))?;
30
31 val = (base as i128) * val + (ch as i128);
32
33 let Some(ch) = reader.peek_ch() else {
34 break;
35 };
36
37 if ch == "_" {
39 reader.eat_ch()?;
40 continue;
41 }
42
43 if ch == "\0" {
44 break;
45 }
46
47 if !ch.is_digit(base) {
48 break;
49 }
50 }
51
52 return Ok(Item::new(sign * val, Span::new(start, reader.position())));
53 }
54
55 fn peek(&self, reader: &mut Reader<'_, '_>) -> Result<bool, Error> {
56 let Some(mut ch) = reader.peek_ch() else {
57 return Ok(false);
58 };
59
60 if ch == "-" {
61 let Some(next) = reader.peek_chn(1) else {
62 return Ok(false);
63 };
64
65 ch = next;
66 }
67
68 Ok(ch.is_digit(10))
69 }
70}
71
72pub struct Integer;
74
75impl Tokenizer for Integer {
76 type Token<'a> = Item<i128>;
77
78 fn to_token<'a>(&self, reader: &mut Reader<'_, 'a>) -> Result<Self::Token<'a>, Error> {
79 let start = reader.position();
80 let mut val: i128 = 0;
81 let base = 10;
82
83 loop {
84 let ch = reader.parse(Digit(base))?;
85
86 val = (base as i128) * val + (ch as i128);
87
88 let Some(ch) = reader.peek_ch() else {
89 break;
90 };
91
92 if ch == "_" {
94 reader.eat_ch()?;
95 continue;
96 }
97
98 if ch == "\0" {
99 break;
100 }
101
102 if !ch.is_digit(base) {
103 break;
104 }
105 }
106
107 return Ok(Item::new(val, Span::new(start, reader.position())));
108 }
109
110 fn peek(&self, reader: &mut Reader<'_, '_>) -> Result<bool, Error> {
111 reader.peek(Digit(10))
112 }
113}
114
115#[derive(Debug, Clone, Copy, Default)]
117pub struct Float;
118
119impl Tokenizer for Float {
120 type Token<'a> = Item<f64>;
121
122 fn to_token<'a>(&self, reader: &mut Reader<'_, 'a>) -> Result<Self::Token<'a>, Error> {
123 let start = reader.parse(Spanned(Integer))?;
124 reader.eat('.')?;
125 let mut end = reader.parse(Spanned(Integer))?;
126
127 if reader.peek(any!('E', 'e'))? {
128 end = reader.parse(Spanned((any!('E', 'e'), Opt('-'), Integer)))?;
129 }
130
131 let input = (start + end)
132 .slice(reader.source())
133 .ok_or_else(|| reader.error("Invalid range"))?;
134
135 let float: f64 = input
136 .parse()
137 .map_err(|err: core::num::ParseFloatError| reader.error(err.to_string()))?;
138
139 Ok(Item::new(float, start + end))
140 }
141}
142
143#[cfg(test)]
144mod test {
145 use udled::{token::Ws, Input};
146
147 use super::{Float, Int, Integer};
148
149 #[test]
150 fn integer() {
151 let mut input = Input::new("10203 0 42");
152
153 let (a, _, b, _, c) = input.parse((Integer, Ws, Integer, Ws, Integer)).unwrap();
154
155 assert_eq!(a.value, 10203);
156 assert_eq!(b.value, 0);
157 assert_eq!(c.value, 42);
158 }
159
160 #[test]
161 fn int() {
162 let mut input = Input::new("0x202 0b11 42");
163
164 let (a, _, b, _, c) = input.parse((Int, Ws, Int, Ws, Int)).unwrap();
165
166 assert_eq!(a.value, 0x202);
167 assert_eq!(b.value, 0b11);
168 assert_eq!(c.value, 42);
169 }
170
171 #[test]
172 fn float() {
173 let mut input = Input::new("1.0 2003.303 12.03e-20");
174
175 let (a, _, b, _, c) = input.parse((Float, Ws, Float, Ws, Float)).unwrap();
176
177 assert_eq!(a.value, 1.0);
178 assert_eq!(b.value, 2003.303);
179 assert_eq!(c.value, 12.03e-20);
180 }
181}