castle_tokenizer/token_parsers/
parse_numbers.rs

1use std::io::Read;
2
3use castle_input_cursor::{Position, Cursor, Span};
4use castle_types::{Primitive, CastleError};
5
6
7use crate::{Token, TokenKind};
8
9#[derive(PartialEq, Debug, Clone, Copy, Eq)]
10pub enum NumericKind {
11    UnsignedInteger,
12    Float,
13    Integer
14}
15
16pub fn parse_number(cursor: &mut Cursor<impl Read>, start_pos: Position) -> Result<Token, CastleError> {
17    let ch = if let Some(ch) = cursor.next_char()? {
18        ch as u8
19    } else {
20        return Err(CastleError::syntax("unexpected end of file", cursor.pos()));
21    };
22
23    let mut kind = match ch {
24        b'-' => NumericKind::Integer,
25        b'0'..=b'9' => NumericKind::UnsignedInteger,
26        _ => return Err(CastleError::syntax("unexpected numeric character", cursor.pos()))
27    };
28
29    let mut buf = vec![ch];
30
31    println!("buf: {:?}", buf);
32
33    loop {
34        let c = cursor.peek()?;
35
36        if let Some(ch) = c {
37            match ch {
38                b'0'..=b'9' => {
39                    buf.push(ch);
40                    cursor.next_byte()?;
41                },
42                b'_' => {
43                    cursor.next_byte()?; // ignore underscore eg: 1_000
44                },
45                b'.' => {
46                    if kind == NumericKind::Integer || kind == NumericKind::UnsignedInteger {
47                        kind = NumericKind::Float;
48                        buf.push(ch);
49                        cursor.next_byte()?;
50                    } else {
51                        break // could be a field access
52                    }
53                },
54                b' ' => {
55                    cursor.next_byte()?; // ignore whitespace
56                }
57                _ => {
58                    break;
59                }
60            }
61        }
62    }
63
64    let num_str = unsafe { std::str::from_utf8_unchecked(buf.as_slice()) };
65
66    let num = match kind {
67        NumericKind::Float => {
68            let val: f64 = fast_float::parse(num_str).expect("Failed to parse float after checks");
69            let int_val = val as i64;
70
71            if (int_val as f64) == val {
72                Primitive::Number(int_val.into())
73            } else {
74                Primitive::Number(val.into())
75            }
76        },
77        NumericKind::UnsignedInteger => {
78            Primitive::Number(u64::from_str_radix(num_str, 10).expect("Failed to parse unsigned integer").into())
79        }
80        NumericKind::Integer => {
81            Primitive::Number(i64::from_str_radix(num_str, 10).expect("Failed to parse integer").into())
82        }
83    };
84
85    Ok(Token::new(
86        TokenKind::Primitive(num),
87        Span::new(start_pos, cursor.pos())
88    ))
89}