use crate::ast::{self, AstToken};
use std::{iter::Peekable, str::CharIndices};
impl ast::IntNumber {
pub fn split_into_parts(&self) -> (&str, Option<&str>) {
split_int_text_and_suffix(self.text())
}
}
impl ast::FloatNumber {
pub fn split_into_parts(&self) -> (&str, Option<&str>) {
split_float_text_and_suffix(self.text())
}
}
fn split_int_text_and_suffix(text: &str) -> (&str, Option<&str>) {
let base = match text.as_bytes() {
[b'0', b'x', ..] => 16,
[b'0', b'o', ..] => 8,
[b'0', b'b', ..] => 2,
_ => 10,
};
let mut iter = text.char_indices().peekable();
if base != 10 {
iter.next();
iter.next();
}
skip_digits(base, &mut iter);
if let Some((idx, _)) = iter.next() {
(&text[0..idx], Some(&text[idx..]))
} else {
(text, None)
}
}
fn skip_digits(base: usize, iter: &mut Peekable<CharIndices>) {
while let Some((_, c)) = iter.peek() {
if match c {
'0'..='9' => true,
'a'..='f' | 'A'..='F' if base > 10 => true,
'_' => true,
_ => false,
} {
iter.next();
} else {
break;
}
}
}
fn split_float_text_and_suffix(text: &str) -> (&str, Option<&str>) {
let mut iter = text.char_indices().peekable();
skip_digits(10, &mut iter);
if let Some((_, '.')) = iter.peek() {
iter.next();
skip_digits(10, &mut iter);
}
if let Some((_, c)) = iter.peek() {
if *c == 'e' || *c == 'E' {
iter.next();
if let Some((_, c)) = iter.peek() {
if *c == '-' || *c == '+' {
iter.next();
}
}
skip_digits(10, &mut iter);
}
}
if let Some((idx, _)) = iter.next() {
(&text[0..idx], Some(&text[idx..]))
} else {
(text, None)
}
}
#[cfg(test)]
mod tests {
use super::{split_float_text_and_suffix, split_int_text_and_suffix};
#[test]
fn split_int_and_suffix() {
assert_eq!(split_int_text_and_suffix("123"), ("123", None));
assert_eq!(split_int_text_and_suffix("0x123"), ("0x123", None));
assert_eq!(split_int_text_and_suffix("123_456"), ("123_456", None));
assert_eq!(split_int_text_and_suffix("0xfff32"), ("0xfff32", None));
assert_eq!(split_int_text_and_suffix("0xff_f32"), ("0xff_f32", None));
assert_eq!(
split_int_text_and_suffix("0xff_u32"),
("0xff_", Some("u32"))
);
assert_eq!(
split_int_text_and_suffix("0x0101u32"),
("0x0101", Some("u32"))
);
assert_eq!(
split_int_text_and_suffix("0xffffu32"),
("0xffff", Some("u32"))
);
assert_eq!(
split_int_text_and_suffix("0o71234u32"),
("0o71234", Some("u32"))
);
}
#[test]
fn split_float_and_suffix() {
assert_eq!(split_float_text_and_suffix("123.0"), ("123.0", None));
assert_eq!(
split_float_text_and_suffix("123.0f32"),
("123.0", Some("f32"))
);
assert_eq!(
split_float_text_and_suffix("123e10f32"),
("123e10", Some("f32"))
);
assert_eq!(
split_float_text_and_suffix("123E10f32"),
("123E10", Some("f32"))
);
assert_eq!(
split_float_text_and_suffix("123E+10f32"),
("123E+10", Some("f32"))
);
assert_eq!(
split_float_text_and_suffix("123E-10f32"),
("123E-10", Some("f32"))
);
assert_eq!(
split_float_text_and_suffix("123.123E10f32"),
("123.123E10", Some("f32"))
);
}
}