use std::fmt::{self, Display};
use crate::TSXToken;
use source_map::Span;
use tokenizer_lib::{sized_tokens::TokenStart, Token};
#[allow(missing_docs)]
pub enum ParseErrors<'a> {
UnexpectedToken { expected: &'a [TSXToken], found: TSXToken },
UnexpectedSymbol(derive_finite_automaton::InvalidCharacter),
ClosingTagDoesNotMatch { expected: &'a str, found: &'a str },
ExpectedStringLiteral { found: TSXToken },
TypeArgumentsNotValidOnReference,
UnmatchedBrackets,
FunctionParameterOptionalAndDefaultValue,
ExpectedIdent { found: TSXToken, at_location: &'a str },
ParameterCannotHaveDefaultValueHere,
InvalidLHSAssignment,
LexingFailed,
ExpectedCatchOrFinally,
InvalidDeclareItem(&'static str),
DestructuringRequiresValue,
CannotAccessObjectLiteralDirectly,
TrailingCommaNotAllowedHere,
InvalidNumberLiteral,
ReservedIdentifier,
AwaitRequiresForOf,
CannotUseLeadingParameterHere,
ExpectedIdentifier,
ExpectedNumberLiteral,
NonStandardSyntaxUsedWithoutEnabled,
ExpectRule,
InvalidRegexFlag,
}
#[allow(missing_docs)]
#[derive(Debug)]
pub enum LexingErrors {
SecondDecimalPoint,
NumberLiteralCannotHaveDecimalPoint,
NumberLiteralBaseSpecifierMustPrecededWithZero,
InvalidCharacterInJSXTag(char),
UnbalancedJSXClosingTags,
ExpectedClosingAngleAtEndOfSelfClosingTag,
InvalidCharacterInAttributeKey(char),
UnexpectedCharacter(derive_finite_automaton::InvalidCharacter),
EmptyAttributeName,
ExpectedJSXEndTag,
NewLineInStringLiteral,
ExpectedEndToMultilineComment,
ExpectedEndToStringLiteral,
UnexpectedEndToNumberLiteral,
InvalidNumeralItemBecauseOfLiteralKind,
ExpectedEndToRegexLiteral,
ExpectedEndToJSXLiteral,
ExpectedEndToTemplateLiteral,
InvalidExponentUsage,
InvalidUnderscore,
CannotLoadLargeFile(usize),
ExpectedDashInComment,
}
impl Display for LexingErrors {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LexingErrors::SecondDecimalPoint => {
f.write_str("Second decimal point found in number literal")
}
LexingErrors::NumberLiteralCannotHaveDecimalPoint => {
f.write_str("Number literal with specified base cannot have decimal point")
}
LexingErrors::NumberLiteralBaseSpecifierMustPrecededWithZero => {
f.write_str("Number literal base character must be proceeded with a zero")
}
LexingErrors::InvalidCharacterInJSXTag(chr) => {
write!(f, "Invalid character {chr:?} in JSX tag")
}
LexingErrors::ExpectedClosingAngleAtEndOfSelfClosingTag => {
f.write_str("Expected closing angle at end of self closing JSX tag")
}
LexingErrors::InvalidCharacterInAttributeKey(chr) => {
write!(f, "Invalid character {chr:?} in JSX attribute name")
}
LexingErrors::EmptyAttributeName => f.write_str("Empty JSX attribute name"),
LexingErrors::ExpectedJSXEndTag => f.write_str("Expected JSX end tag"),
LexingErrors::NewLineInStringLiteral => {
f.write_str("String literals cannot contain new lines")
}
LexingErrors::ExpectedEndToMultilineComment => {
f.write_str("Unclosed multiline comment")
}
LexingErrors::ExpectedEndToStringLiteral => f.write_str("Unclosed string literal"),
LexingErrors::UnexpectedEndToNumberLiteral => f.write_str("Unclosed number literal"),
LexingErrors::ExpectedEndToRegexLiteral => f.write_str("Unclosed regex literal"),
LexingErrors::ExpectedEndToJSXLiteral => f.write_str("Unclosed JSX literal"),
LexingErrors::ExpectedEndToTemplateLiteral => f.write_str("Unclosed template literal"),
LexingErrors::UnexpectedCharacter(err) => Display::fmt(err, f),
LexingErrors::UnbalancedJSXClosingTags => f.write_str("Too many closing JSX tags"),
LexingErrors::InvalidExponentUsage => f.write_str("Two e in number literal"),
LexingErrors::InvalidUnderscore => f.write_str("Numeric separator in invalid place"),
LexingErrors::InvalidNumeralItemBecauseOfLiteralKind => {
f.write_str("Invalid item in binary, hex or octal literal")
}
LexingErrors::CannotLoadLargeFile(size) => {
write!(f, "Cannot parse {size:?} byte file (4GB maximum)")
}
LexingErrors::ExpectedDashInComment => {
f.write_str("JSX comments must have two dashes after `<!` start")
}
}
}
}
impl<'a> Display for ParseErrors<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParseErrors::UnexpectedToken { expected, found } => {
f.write_str("Expected ")?;
match expected {
[] => unreachable!("no expected tokens given"),
[a] => f.write_fmt(format_args!("{a:?}")),
[a, b] => f.write_fmt(format_args!("{a:?} or {b:?}")),
[head @ .., end] => {
let start = head
.iter()
.map(|token| format!("{token:?}"))
.reduce(|mut acc, token| {
acc.push_str(", ");
acc.push_str(&token);
acc
})
.unwrap();
f.write_fmt(format_args!("{start} or {end:?}"))
}
}?;
write!(f, " found {found:?}")
}
ParseErrors::UnexpectedSymbol(invalid_character) => Display::fmt(invalid_character, f),
ParseErrors::ClosingTagDoesNotMatch { expected, found } => {
write!(f, "Expected </{expected}> found </{found}>")
}
ParseErrors::NonStandardSyntaxUsedWithoutEnabled => {
write!(f, "Cannot use this syntax without flag enabled")
}
ParseErrors::ExpectedStringLiteral { found } => {
write!(f, "Expected string literal, found {found:?}")
}
ParseErrors::TypeArgumentsNotValidOnReference => {
write!(f, "Type arguments not valid on reference",)
}
ParseErrors::UnmatchedBrackets => f.write_str("Unmatched brackets"),
ParseErrors::FunctionParameterOptionalAndDefaultValue => {
f.write_str("Function parameter cannot be optional *and* have default expression")
}
ParseErrors::ExpectedIdent { found, at_location } => {
write!(f, "Expected identifier at {at_location}, found {found:?}")
}
ParseErrors::ParameterCannotHaveDefaultValueHere => {
f.write_str("Function parameter cannot be have default value here")
}
ParseErrors::InvalidLHSAssignment => f.write_str("Invalid syntax on LHS of assignment"),
ParseErrors::ExpectedCatchOrFinally => {
f.write_str("Expected 'catch' or 'finally' to follow 'try'")
}
ParseErrors::LexingFailed => {
f.write_str("Lexing issue")
}
ParseErrors::InvalidDeclareItem(item) => {
write!(f, "Declare item '{item}' must be in .d.ts file")
}
ParseErrors::DestructuringRequiresValue => {
write!(f, "RHS of destructured declaration requires expression")
}
ParseErrors::CannotAccessObjectLiteralDirectly => {
write!(f, "Cannot get property on object literal directly")
}
ParseErrors::TrailingCommaNotAllowedHere => {
write!(f, "Trailing comma not allowed here")
}
ParseErrors::InvalidNumberLiteral => {
write!(f, "Invalid number literal")
}
ParseErrors::ReservedIdentifier => {
write!(f, "Found reserved identifier")
}
ParseErrors::AwaitRequiresForOf => {
write!(f, "Can only use await on for (.. of ..)")
}
ParseErrors::CannotUseLeadingParameterHere => {
write!(f, "Cannot write this constraint in this kind of function")
}
ParseErrors::ExpectedIdentifier => {
write!(f, "Expected variable identifier")
}
ParseErrors::ExpectedNumberLiteral => {
write!(f, "Expected number literal")
}
ParseErrors::ExpectRule => {
write!(f, "'-' must be followed by a readonly rule")
}
ParseErrors::InvalidRegexFlag => {
write!(f, "Regexp flags must be one of 'd', 'g', 'i', 'm', 's', 'u' or 'y'")
}
}
}
}
impl From<Option<(TSXToken, Token<TSXToken, TokenStart>)>> for ParseError {
fn from(opt: Option<(TSXToken, Token<TSXToken, TokenStart>)>) -> Self {
if let Some((expected_type, token)) = opt {
let position = token.get_span();
let reason =
ParseErrors::UnexpectedToken { expected: &[expected_type], found: token.0 };
Self::new(reason, position)
} else {
parse_lexing_error()
}
}
}
pub(crate) fn parse_lexing_error() -> ParseError {
ParseError::new(ParseErrors::LexingFailed, source_map::Nullable::NULL)
}
pub trait ParserErrorReason: Display {}
impl<'a> ParserErrorReason for ParseErrors<'a> {}
impl ParserErrorReason for LexingErrors {}
#[derive(Debug)]
pub struct ParseError {
pub reason: String,
pub position: Span,
}
impl ParseError {
#[allow(clippy::needless_pass_by_value)]
pub fn new(reason: impl ParserErrorReason, position: Span) -> Self {
Self { reason: reason.to_string(), position }
}
}
impl std::error::Error for ParseError {}
impl std::fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("ParseError: {} @ byte indices {:?}", self.reason, self.position))
}
}
pub type ParseResult<T> = Result<T, ParseError>;