castle_tokenizer/token_parsers/
parse_numbers.rs1use 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()?; },
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 }
53 },
54 b' ' => {
55 cursor.next_byte()?; }
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}