wright/lexer/
integer_literal.rsuse super::{
token::{Token, TokenTy},
Lexer,
};
use std::{iter::Peekable, str::Chars};
pub fn try_consume_integer_literal(lexer: &mut Lexer) -> Option<Token> {
let mut chars: Peekable<Chars> = lexer.remaining.chars().peekable();
let next: char = chars.next().filter(char::is_ascii_digit)?;
let mut bytes_consumed: usize = next.len_utf8();
let mut radix: u32 = 10;
if next == '0' {
if let Some(prefix) = chars.next_if(|x| ['x', 'o', 'b', 'X', 'B'].contains(x)) {
bytes_consumed += 1;
radix = match prefix {
'x' | 'X' => 16,
'b' | 'B' => 2,
'o' => 8,
_ => unreachable!("the prefix byte is checked above"),
};
}
}
bytes_consumed += chars.next_if(|c| c.is_digit(radix))?.len_utf8();
bytes_consumed += chars
.take_while(|c| c.is_digit(radix) || *c == '_')
.map(char::len_utf8)
.sum::<usize>();
Some(lexer.split_token(bytes_consumed, TokenTy::IntegerLiteral))
}
#[cfg(test)]
mod tests {
use crate::lexer::integer_literal::try_consume_integer_literal;
use super::{Lexer, TokenTy};
#[test]
fn integer_literal() {
let mut lexer = Lexer::new_test("123_456_789.");
let token = lexer.next_token().unwrap();
assert_eq!(token.fragment.as_str(), "123_456_789");
assert_eq!(token.variant, TokenTy::IntegerLiteral);
assert_eq!(lexer.remaining.as_str(), ".");
}
#[test]
fn cant_start_with_underscore() {
let mut lexer = Lexer::new_test("0x__10");
assert!(try_consume_integer_literal(&mut lexer).is_none());
assert_eq!(lexer.remaining.as_str(), "0x__10");
}
}