1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2025 Orange
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *          http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 */
18
use crate::ast::{Float, Number, I64, U64};
19
use crate::parser::primitives::try_literal;
20
use crate::parser::{ParseError, ParseErrorKind, ParseResult};
21
use crate::reader::Reader;
22
use crate::typing::ToSource;
23

            
24
/// Parses a string to a `U64`.
25
855
pub fn natural(reader: &mut Reader) -> ParseResult<U64> {
26
855
    let start = reader.cursor();
27
855
    let s = parse_natural_str(reader, "natural")?;
28
790
    match s.parse() {
29
790
        Ok(value) => {
30
790
            let source = reader.read_from(start.index).to_source();
31
790
            Ok(U64::new(value, source))
32
        }
33
        Err(_) => {
34
            let kind = ParseErrorKind::Expecting {
35
                value: String::from("natural"),
36
            };
37
            Err(ParseError::new(start.pos, false, kind))
38
        }
39
    }
40
}
41

            
42
335
pub fn integer(reader: &mut Reader) -> ParseResult<I64> {
43
335
    let start = reader.cursor();
44
335
    let sign = match try_literal("-", reader) {
45
290
        Err(_) => 1,
46
45
        Ok(_) => -1,
47
    };
48
335
    let s = parse_natural_str(reader, "integer")?;
49
235
    match s.to_string().parse::<i64>() {
50
235
        Ok(value) => {
51
235
            let value = sign * value;
52
235
            let source = reader.read_from(start.index).to_source();
53
235
            Ok(I64::new(value, source))
54
        }
55
        Err(_) => {
56
            let kind = ParseErrorKind::Expecting {
57
                value: String::from("integer"),
58
            };
59
            Err(ParseError::new(start.pos, false, kind))
60
        }
61
    }
62
}
63

            
64
/// Parses a natural string.
65
/// This is common code between natural and integer parsers.
66
1190
fn parse_natural_str(reader: &mut Reader, expecting: &str) -> ParseResult<String> {
67
1190
    let mut save = reader.cursor();
68
1190
    if reader.is_eof() {
69
        let kind = ParseErrorKind::Expecting {
70
            value: expecting.to_string(),
71
        };
72
        return Err(ParseError::new(save.pos, true, kind));
73
    }
74
1190
    let first_digit = reader.read().unwrap();
75
1190
    if !first_digit.is_ascii_digit() {
76
165
        let kind = ParseErrorKind::Expecting {
77
165
            value: expecting.to_string(),
78
165
        };
79
165
        return Err(ParseError::new(save.pos, true, kind));
80
    }
81
1025
    save.pos = reader.cursor().pos;
82
1680
    let s = reader.read_while(|c| c.is_ascii_digit());
83
1025
    // if the first digit is zero, you should not have any more digits
84
1025
    if first_digit == '0' && !s.is_empty() {
85
        let kind = ParseErrorKind::Expecting {
86
            value: expecting.to_string(),
87
        };
88
        return Err(ParseError::new(save.pos, false, kind));
89
    }
90
1025
    Ok(format!("{first_digit}{s}"))
91
}
92

            
93
16560
pub fn number(reader: &mut Reader) -> ParseResult<Number> {
94
16560
    let start = reader.cursor();
95
16560
    let sign = match try_literal("-", reader) {
96
16540
        Err(_) => "",
97
20
        Ok(_) => "-",
98
    };
99
27812
    let integer_digits = reader.read_while(|c| c.is_ascii_digit());
100
16560
    if integer_digits.is_empty() {
101
12440
        let kind = ParseErrorKind::Expecting {
102
12440
            value: "number".to_string(),
103
12440
        };
104
12440
        return Err(ParseError::new(reader.cursor().pos, true, kind));
105

            
106
        // if the first digit is zero, you should not have any more digits
107
4120
    } else if integer_digits.len() > 1 && integer_digits.starts_with('0') {
108
        let save = reader.cursor();
109
        let kind = ParseErrorKind::Expecting {
110
            value: String::from("natural"),
111
        };
112
        return Err(ParseError::new(save.pos, false, kind));
113
    }
114
4120

            
115
4120
    // Float
116
4120
    if try_literal(".", reader).is_ok() {
117
810
        let save = reader.cursor();
118
6142
        let decimal_digits = reader.read_while(|c| c.is_ascii_digit());
119
810
        if decimal_digits.is_empty() {
120
            let kind = ParseErrorKind::Expecting {
121
                value: String::from("decimal digits"),
122
            };
123
            return Err(ParseError::new(save.pos, false, kind));
124
        }
125
810
        match format!("{sign}{integer_digits}.{decimal_digits}").parse() {
126
810
            Ok(value) => {
127
810
                let source = reader.read_from(start.index).to_source();
128
810
                Ok(Number::Float(Float::new(value, source)))
129
            }
130
            Err(_) => {
131
                let kind = ParseErrorKind::Expecting {
132
                    value: String::from("float"),
133
                };
134
                Err(ParseError::new(start.pos, false, kind))
135
            }
136
        }
137

            
138
    // Integer or BigInteger
139
    } else {
140
3310
        match format!("{sign}{integer_digits}").parse::<i64>() {
141
3285
            Ok(value) => {
142
3285
                let source = reader.read_from(start.index).to_source();
143
3285
                let integer = I64::new(value, source);
144
3285
                Ok(Number::Integer(integer))
145
            }
146
25
            Err(_) => Ok(Number::BigInteger(integer_digits)),
147
        }
148
    }
149
}
150

            
151
#[cfg(test)]
152
mod tests {
153
    use super::*;
154
    use crate::reader::Pos;
155

            
156
    #[test]
157
    fn test_natural() {
158
        let mut reader = Reader::new("0");
159
        assert_eq!(natural(&mut reader).unwrap(), U64::new(0, "0".to_source()));
160
        assert_eq!(reader.cursor().index, 1);
161

            
162
        let mut reader = Reader::new("10x");
163
        assert_eq!(
164
            natural(&mut reader).unwrap(),
165
            U64::new(10, "10".to_source())
166
        );
167
        assert_eq!(reader.cursor().index, 2);
168
    }
169

            
170
    #[test]
171
    fn test_natural_error() {
172
        let mut reader = Reader::new("");
173
        let error = natural(&mut reader).err().unwrap();
174
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
175
        assert_eq!(
176
            error.kind,
177
            ParseErrorKind::Expecting {
178
                value: String::from("natural")
179
            }
180
        );
181
        assert!(error.recoverable);
182

            
183
        let mut reader = Reader::new("01");
184
        let error = natural(&mut reader).err().unwrap();
185
        assert_eq!(error.pos, Pos { line: 1, column: 2 });
186
        assert_eq!(
187
            error.kind,
188
            ParseErrorKind::Expecting {
189
                value: String::from("natural")
190
            }
191
        );
192
        assert!(!error.recoverable);
193

            
194
        let mut reader = Reader::new("x");
195
        let error = natural(&mut reader).err().unwrap();
196
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
197
        assert_eq!(
198
            error.kind,
199
            ParseErrorKind::Expecting {
200
                value: String::from("natural")
201
            }
202
        );
203
        assert!(error.recoverable);
204
    }
205

            
206
    #[test]
207
    fn test_integer() {
208
        let mut reader = Reader::new("0");
209
        assert_eq!(integer(&mut reader).unwrap(), I64::new(0, "0".to_source()));
210
        assert_eq!(reader.cursor().index, 1);
211

            
212
        let mut reader = Reader::new("-1");
213
        assert_eq!(
214
            integer(&mut reader).unwrap(),
215
            I64::new(-1, "-1".to_source())
216
        );
217
        assert_eq!(reader.cursor().index, 2);
218

            
219
        let mut reader = Reader::new("0");
220
        assert_eq!(
221
            number(&mut reader).unwrap(),
222
            Number::Integer(I64::new(0, "0".to_source()))
223
        );
224
        assert_eq!(reader.cursor().index, 1);
225

            
226
        let mut reader = Reader::new("10x");
227
        assert_eq!(
228
            number(&mut reader).unwrap(),
229
            Number::Integer(I64::new(10, "10".to_source()))
230
        );
231
        assert_eq!(reader.cursor().index, 2);
232

            
233
        let mut reader = Reader::new("-10x");
234
        assert_eq!(
235
            number(&mut reader).unwrap(),
236
            Number::Integer(I64::new(-10, "-10".to_source()))
237
        );
238
        assert_eq!(reader.cursor().index, 3);
239
    }
240

            
241
    #[test]
242
    fn test_float() {
243
        let mut reader = Reader::new("1.0");
244
        assert_eq!(
245
            number(&mut reader).unwrap(),
246
            Number::Float(Float::new(1.0, "1.0".to_source())),
247
        );
248
        assert_eq!(reader.cursor().index, 3);
249

            
250
        let mut reader = Reader::new("-1.0");
251
        assert_eq!(
252
            number(&mut reader).unwrap(),
253
            Number::Float(Float::new(-1.0, "-1.0".to_source())),
254
        );
255
        assert_eq!(reader.cursor().index, 4);
256

            
257
        let mut reader = Reader::new("1.1");
258
        assert_eq!(
259
            number(&mut reader).unwrap(),
260
            Number::Float(Float::new(1.1, "1.1".to_source())),
261
        );
262
        assert_eq!(reader.cursor().index, 3);
263

            
264
        let mut reader = Reader::new("1.100");
265
        assert_eq!(
266
            number(&mut reader).unwrap(),
267
            Number::Float(Float::new(1.1, "1.100".to_source())),
268
        );
269
        assert_eq!(reader.cursor().index, 5);
270

            
271
        let mut reader = Reader::new("1.01");
272
        assert_eq!(
273
            number(&mut reader).unwrap(),
274
            Number::Float(Float::new(1.01, "1.01".to_source())),
275
        );
276
        assert_eq!(reader.cursor().index, 4);
277

            
278
        let mut reader = Reader::new("1.010");
279
        assert_eq!(
280
            number(&mut reader).unwrap(),
281
            Number::Float(Float::new(1.01, "1.010".to_source()))
282
        );
283
        assert_eq!(reader.cursor().index, 5);
284

            
285
        // provide more digits than necessary
286
        let mut reader = Reader::new("-0.3333333333333333333");
287
        assert_eq!(
288
            number(&mut reader).unwrap(),
289
            Number::Float(Float::new(
290
                -0.3333333333333333,
291
                "-0.3333333333333333333".to_source()
292
            ))
293
        );
294
        assert_eq!(reader.cursor().index, 22);
295

            
296
        let mut reader = Reader::new("1000000000000000000000.5");
297
        assert_eq!(
298
            number(&mut reader).unwrap(),
299
            Number::Float(Float::new(
300
                1000000000000000000000.0,
301
                "1000000000000000000000.5".to_source()
302
            ))
303
        );
304
        assert_eq!(reader.cursor().index, 24);
305
    }
306

            
307
    #[test]
308
    pub fn test_number_error() {
309
        let mut reader = Reader::new("");
310
        let error = number(&mut reader).err().unwrap();
311
        assert_eq!(
312
            error.kind,
313
            ParseErrorKind::Expecting {
314
                value: String::from("number")
315
            }
316
        );
317
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
318
        assert!(error.recoverable);
319

            
320
        let mut reader = Reader::new("-");
321
        let error = number(&mut reader).err().unwrap();
322
        assert_eq!(
323
            error.kind,
324
            ParseErrorKind::Expecting {
325
                value: String::from("number")
326
            }
327
        );
328
        assert_eq!(error.pos, Pos { line: 1, column: 2 });
329
        assert!(error.recoverable);
330

            
331
        let mut reader = Reader::new("x");
332
        let error = number(&mut reader).err().unwrap();
333
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
334
        assert_eq!(
335
            error.kind,
336
            ParseErrorKind::Expecting {
337
                value: String::from("number")
338
            }
339
        );
340
        assert!(error.recoverable);
341

            
342
        let mut reader = Reader::new("1.");
343
        let error = number(&mut reader).err().unwrap();
344
        assert_eq!(error.pos, Pos { line: 1, column: 3 });
345
        assert_eq!(
346
            error.kind,
347
            ParseErrorKind::Expecting {
348
                value: String::from("decimal digits")
349
            }
350
        );
351
        assert!(!error.recoverable);
352
    }
353
}