use num_bigint::BigInt;
use num_traits::Num;
use unescaper::unescape;
use crate::diagnostic::ParserDiagnosticKind;
pub struct ValidationError {
pub kind: ParserDiagnosticKind,
pub location: ValidationLocation,
}
impl ValidationError {
fn full(kind: ParserDiagnosticKind) -> Self {
ValidationError { kind, location: ValidationLocation::Full }
}
}
pub enum ValidationLocation {
Full,
After,
}
pub fn validate_literal_number(text: &str) -> Option<ValidationError> {
let (text, ty) = match text.split_once('_') {
Some((text, ty)) => (text, Some(ty)),
None => (text, None),
};
{
let (text, radix) = if let Some(num_no_prefix) = text.strip_prefix("0x") {
(num_no_prefix, 16)
} else if let Some(num_no_prefix) = text.strip_prefix("0o") {
(num_no_prefix, 8)
} else if let Some(num_no_prefix) = text.strip_prefix("0b") {
(num_no_prefix, 2)
} else {
(text, 10)
};
if BigInt::from_str_radix(text, radix).is_err() {
return Some(ValidationError::full(ParserDiagnosticKind::InvalidNumericLiteralValue));
}
}
if let Some(ty) = ty
&& ty.is_empty()
{
Some(ValidationError {
kind: ParserDiagnosticKind::MissingLiteralSuffix,
location: ValidationLocation::After,
})
} else {
None
}
}
pub fn validate_short_string(text: &str) -> Option<ValidationError> {
validate_any_string(
text,
'\'',
ParserDiagnosticKind::UnterminatedShortString,
ParserDiagnosticKind::ShortStringMustBeAscii,
)
}
pub fn validate_string(text: &str) -> Option<ValidationError> {
validate_any_string(
text,
'"',
ParserDiagnosticKind::UnterminatedString,
ParserDiagnosticKind::StringMustBeAscii,
)
}
fn validate_any_string(
text: &str,
delimiter: char,
unterminated_string_diagnostic_kind: ParserDiagnosticKind,
ascii_only_diagnostic_kind: ParserDiagnosticKind,
) -> Option<ValidationError> {
let (_, text) = text.split_once(delimiter).unwrap();
let Some((body, _suffix)) = text.rsplit_once(delimiter) else {
return Some(ValidationError::full(unterminated_string_diagnostic_kind));
};
validate_string_body(body, ascii_only_diagnostic_kind)
}
fn validate_string_body(
body: &str,
ascii_only_diagnostic_kind: ParserDiagnosticKind,
) -> Option<ValidationError> {
let Ok(body) = unescape(body) else {
return Some(ValidationError::full(ParserDiagnosticKind::IllegalStringEscaping));
};
if !body.is_ascii() {
return Some(ValidationError::full(ascii_only_diagnostic_kind));
}
None
}