use super::Lexer;
use crate::ast::Token;
use crate::error::{self, ParseError};
impl Lexer {
pub(super) fn parse_number(&mut self) -> error::Result<Token> {
let start = self.current_pos();
let mut num = self.consume_digits();
let mut is_float = false;
if self.peek() == Some('.') {
let next = self.peek_ahead(1);
let has_leading_zero = num.starts_with('0');
let has_multiple_leading_zeroes = has_leading_zero && num.len() > 1;
if next.is_some_and(|c| c.is_ascii_digit()) && !has_multiple_leading_zeroes {
is_float = true;
num.push('.');
self.advance();
num.push_str(&self.consume_digits());
} else if next.is_none_or(|c| !c.is_ascii_digit()) && !num.is_empty() && num != "0" {
is_float = true;
num.push('.');
self.advance();
}
}
if is_float {
if let Some(exp) = self.parse_exponent() {
num.push_str(&exp);
}
return Ok(Token::Float(num.into()));
}
if num.parse::<i64>().is_err() {
return Err(ParseError::invalid(
start,
format!("invalid integer '{num}'"),
Some(format!(
"integer literals must fit in a signed 64-bit range (0 to {})",
i64::MAX
)),
));
}
Ok(Token::Integer(num.into()))
}
pub(super) fn parse_exponent(&mut self) -> Option<String> {
if !matches!(self.peek(), Some('e' | 'E')) {
return None;
}
self.try_with_cursor(|this| {
let mut exponent = String::new();
exponent.push(this.advance().unwrap());
if matches!(this.peek(), Some('+' | '-')) {
exponent.push(this.advance().unwrap());
}
if this.peek().is_some_and(|c| c.is_ascii_digit()) {
exponent.push_str(&this.consume_digits());
Some(exponent)
} else {
None
}
})
}
pub(super) fn consume_digits(&mut self) -> String {
self.take_ascii_while(|b| b.is_ascii_digit()).to_owned()
}
}