use std::fmt;
use crate::ast::Span;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct LexError {
pub span: Span,
pub kind: LexErrorKind,
}
impl LexError {
pub const fn new(kind: LexErrorKind, span: Span) -> Self {
Self { span, kind }
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum LexErrorKind {
UnterminatedString,
UnterminatedQuotedIdent,
UnterminatedBlockComment,
UnterminatedDollarQuote,
StrayByte,
InvalidEscapeSequence,
NulByteInString,
NulByteInIdentifier,
NulByteInComment,
ZeroLengthDelimitedIdentifier,
TrailingJunkAfterNumber,
MalformedBlobLiteral,
SourceTooLong,
}
impl LexErrorKind {
pub const fn message(&self) -> &'static str {
match self {
Self::UnterminatedString => "unterminated string literal",
Self::UnterminatedQuotedIdent => "unterminated quoted identifier",
Self::UnterminatedBlockComment => "unterminated block comment",
Self::UnterminatedDollarQuote => "unterminated dollar-quoted string",
Self::StrayByte => "stray byte",
Self::InvalidEscapeSequence => "invalid escape sequence in string literal",
Self::NulByteInString => "NUL byte in string literal",
Self::NulByteInIdentifier => "NUL byte in quoted identifier",
Self::NulByteInComment => "NUL byte in comment",
Self::ZeroLengthDelimitedIdentifier => "zero-length delimited identifier",
Self::TrailingJunkAfterNumber => "trailing junk after numeric literal",
Self::MalformedBlobLiteral => "malformed hexadecimal blob literal",
Self::SourceTooLong => "source exceeds u32::MAX bytes",
}
}
pub const fn machine_kind(&self) -> &'static str {
match self {
Self::UnterminatedString => "unterminated_string",
Self::UnterminatedQuotedIdent => "unterminated_quoted_identifier",
Self::UnterminatedBlockComment => "unterminated_block_comment",
Self::UnterminatedDollarQuote => "unterminated_dollar_quote",
Self::StrayByte => "stray_byte",
Self::InvalidEscapeSequence => "invalid_escape_sequence",
Self::NulByteInString => "nul_byte_in_string",
Self::NulByteInIdentifier => "nul_byte_in_identifier",
Self::NulByteInComment => "nul_byte_in_comment",
Self::ZeroLengthDelimitedIdentifier => "zero_length_delimited_identifier",
Self::TrailingJunkAfterNumber => "trailing_junk_after_number",
Self::MalformedBlobLiteral => "malformed_blob_literal",
Self::SourceTooLong => "source_too_long",
}
}
}
impl fmt::Display for LexError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} at bytes {}..{}",
self.kind.message(),
self.span.start(),
self.span.end()
)
}
}
impl std::error::Error for LexError {}
#[cfg(test)]
mod tests {
use super::*;
const ALL: &[LexErrorKind] = &[
LexErrorKind::UnterminatedString,
LexErrorKind::UnterminatedQuotedIdent,
LexErrorKind::UnterminatedBlockComment,
LexErrorKind::UnterminatedDollarQuote,
LexErrorKind::StrayByte,
LexErrorKind::InvalidEscapeSequence,
LexErrorKind::NulByteInString,
LexErrorKind::NulByteInIdentifier,
LexErrorKind::NulByteInComment,
LexErrorKind::ZeroLengthDelimitedIdentifier,
LexErrorKind::TrailingJunkAfterNumber,
LexErrorKind::MalformedBlobLiteral,
LexErrorKind::SourceTooLong,
];
#[test]
fn machine_kinds_are_distinct_stable_snake_case() {
let mut seen = std::collections::HashSet::new();
for kind in ALL {
let machine = kind.machine_kind();
assert!(!machine.is_empty(), "{kind:?} has an empty machine kind");
assert!(
machine.bytes().all(|b| b.is_ascii_lowercase() || b == b'_'),
"{kind:?} machine kind {machine:?} is not snake_case"
);
assert_ne!(machine, "syntax");
assert_ne!(machine, "recursion_limit_exceeded");
assert!(seen.insert(machine), "duplicate machine kind {machine:?}");
}
}
}