use crate::gql::lexer::Lexer;
use crate::gql::token::{NumberKind, NumberSuffix, Token, TokenKind};
use crate::syn::error::{SyntaxError, syntax_error};
use crate::syn::token::Span;
impl Lexer<'_> {
pub(super) fn lex_number(&mut self, start: u8) -> Token {
if start == b'0' {
let prefixed = match self.reader.peek() {
Some(b'x') => self.lex_prefixed_integer(NumberKind::Hex, |x| x.is_ascii_hexdigit()),
Some(b'o') => {
self.lex_prefixed_integer(NumberKind::Octal, |x| matches!(x, b'0'..=b'7'))
}
Some(b'b') => {
self.lex_prefixed_integer(NumberKind::Binary, |x| matches!(x, b'0' | b'1'))
}
_ => None,
};
if let Some(token) = prefixed {
return token;
}
}
if let Err(e) = self.eat_separated_digits(|x| x.is_ascii_digit()) {
return self.invalid_token(e);
}
let mut kind = NumberKind::Integer;
if self.eat(b'.') {
kind = NumberKind::Float;
if self.eat_when(|x| x.is_ascii_digit())
&& let Err(e) = self.eat_separated_digits(|x| x.is_ascii_digit())
{
return self.invalid_token(e);
}
}
self.lex_number_tail(kind)
}
pub(super) fn lex_number_starting_period(&mut self) -> Token {
if let Err(e) = self.eat_separated_digits(|x| x.is_ascii_digit()) {
return self.invalid_token(e);
}
self.lex_number_tail(NumberKind::Float)
}
fn lex_number_tail(&mut self, mut kind: NumberKind) -> Token {
if matches!(self.reader.peek(), Some(b'e' | b'E')) {
let backup = self.reader.offset();
self.reader.next();
self.eat_when(|x| matches!(x, b'+' | b'-'));
if self.eat_when(|x| x.is_ascii_digit()) {
kind = NumberKind::Scientific;
if let Err(e) = self.eat_separated_digits(|x| x.is_ascii_digit()) {
return self.invalid_token(e);
}
} else {
self.reader.backup(backup);
}
}
let suffix = match self.reader.peek() {
Some(b'm' | b'M') => Some(NumberSuffix::Exact),
Some(b'f' | b'F') => Some(NumberSuffix::Float),
Some(b'd' | b'D') => Some(NumberSuffix::Double),
_ => None,
};
if suffix.is_some() {
self.reader.next();
}
self.finish_token(TokenKind::Number {
kind,
suffix,
})
}
fn lex_prefixed_integer(
&mut self,
kind: NumberKind,
is_digit: impl Fn(u8) -> bool + Copy,
) -> Option<Token> {
let backup = self.reader.offset();
self.reader.next();
let underscore = self.eat(b'_');
if !self.eat_when(is_digit) {
if underscore {
let error = self.separator_error();
return Some(self.invalid_token(error));
}
self.reader.backup(backup);
return None;
}
if let Err(e) = self.eat_separated_digits(is_digit) {
return Some(self.invalid_token(e));
}
Some(self.finish_token(TokenKind::Number {
kind,
suffix: None,
}))
}
fn eat_separated_digits(
&mut self,
is_digit: impl Fn(u8) -> bool + Copy,
) -> Result<(), SyntaxError> {
loop {
match self.reader.peek() {
Some(x) if is_digit(x) => {
self.reader.next();
}
Some(b'_') => {
self.reader.next();
if !self.eat_when(is_digit) {
return Err(self.separator_error());
}
}
_ => return Ok(()),
}
}
}
fn separator_error(&self) -> SyntaxError {
let span = Span {
offset: self.reader.offset() - 1,
len: 1,
};
syntax_error!(
"Invalid numeric literal, underscore digit separators must be followed by a digit",
@span
)
}
fn eat_when(&mut self, f: impl FnOnce(u8) -> bool) -> bool {
let Some(x) = self.reader.peek() else {
return false;
};
if f(x) {
self.reader.next();
true
} else {
false
}
}
}