use crate::span::Span;
use either::Either;
use fragile::Fragile;
use std::ffi::OsString;
use std::fmt::{self, Display, Formatter};
use std::path::PathBuf;
use std::str::Utf8Error;
use std::string::FromUtf8Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(thiserror::Error, Debug)]
pub struct Error {
#[from]
pub kind: Box<ErrorKind>,
}
impl Error {
pub fn with_file<T>(error: T, file: impl Into<PathBuf>) -> Self
where
ErrorKind: From<(PathBuf, T)>,
{
Self::new((file.into(), error).into())
}
pub fn new(kind: ErrorKind) -> Self {
Self {
kind: Box::new(kind),
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.kind.fmt(f)
}
}
impl<T: Into<ErrorKind>> From<T> for Error {
fn from(t: T) -> Self {
Self::new(t.into())
}
}
#[derive(thiserror::Error, Debug)]
pub enum ErrorKind {
InternalError {
message: String,
},
IntegerTooBig {
string: String,
span: Fragile<Span>,
},
SerializationError {
error: bincode::Error,
path: PathBuf,
},
IllformedAst {
error: serde_json::Error,
path: PathBuf,
},
UnrecognisedExtension {
extension: OsString,
path: PathBuf,
},
NonUtf8Extension {
path: PathBuf,
},
NonUtf8Content {
path: PathBuf,
error: Either<Utf8Error, FromUtf8Error>,
},
GrammarNotFound {
path: PathBuf,
},
LexerGrammarSyntax {
message: String,
span: Fragile<Span>,
},
LexerGrammarDuplicateDefinition {
token: String,
span: Fragile<Span>,
},
LexerGrammarUnwantedNoDescription {
token: String,
span: Fragile<Span>,
},
LexerGrammarEofString,
LexingError {
span: Fragile<Span>,
},
UnwantedToken {
span: Fragile<Span>,
message: String,
},
GrammarDuplicateDefinition {
name: String,
span: Fragile<Span>,
old_span: Fragile<Span>,
},
GrammarDuplicateProxyItem {
span: Fragile<Span>,
old_span: Fragile<Span>,
name: String,
},
GrammarArityMismatch {
macro_name: String,
definition_arity: usize,
call_arity: usize,
definition_span: Fragile<Span>,
call_span: Fragile<Span>,
},
GrammarUndefinedNonTerminal {
name: String,
span: Fragile<Span>,
},
GrammarUndefinedMacro {
name: String,
span: Fragile<Span>,
},
GrammarNonTerminalDuplicate {
message: String,
span: Fragile<Span>,
},
GrammarTerminalInvocation {
terminal: String,
span: Fragile<Span>,
},
GrammarSyntaxError {
message: String,
span: Fragile<Span>,
},
GrammarVariantKey {
span: Fragile<Span>,
},
GrammarDuplicateMacroDefinition {
span: Fragile<Span>,
old_span: Fragile<Span>,
name: String,
},
SyntaxError {
name: String,
alternatives: Vec<String>,
span: Fragile<Span>,
},
SyntaxErrorValidPrefix {
span: Fragile<Span>,
},
IOError {
error: std::io::Error,
path: PathBuf,
},
RegexError {
span: Fragile<Span>,
message: String,
},
SameOutputAndInput,
}
impl ErrorKind {
pub fn err<T>(self) -> std::result::Result<T, Error> {
Err(Error {
kind: Box::new(self),
})
}
}
impl From<(PathBuf, std::io::Error)> for ErrorKind {
fn from((path, error): (PathBuf, std::io::Error)) -> Self {
Self::IOError { error, path }
}
}
impl From<(PathBuf, Utf8Error)> for ErrorKind {
fn from((path, error): (PathBuf, Utf8Error)) -> Self {
Self::NonUtf8Content { path, error: Either::Left(error) }
}
}
impl From<(PathBuf, bincode::Error)> for ErrorKind {
fn from((path, error): (PathBuf, bincode::Error)) -> Self {
Self::SerializationError { error, path }
}
}
impl From<(PathBuf, FromUtf8Error)> for ErrorKind {
fn from((path, error): (PathBuf, FromUtf8Error)) -> Self {
Self::NonUtf8Content { path, error: Either::Right(error) }
}
}
impl From<(PathBuf, serde_json::Error)> for ErrorKind {
fn from((path, error): (PathBuf, serde_json::Error)) -> Self {
Self::IllformedAst { error, path }
}
}
impl Display for ErrorKind {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::InternalError { message } => writeln!(f, "ICE: {message}"),
Self::SerializationError { error, path } => writeln!(
f,
"ICE: while trying to serialize {}, the following error occured\n{error}",
path.display(),
),
Self::IllformedAst { error, path } => writeln!(
f,
"File {} contains an illformed AST.\n{error}",
path.display(),
),
Self::UnrecognisedExtension { extension, path } => {
write!(
f,
"File {} has an extension that is not recognised by Beans",
path.display(),
)?;
if let Some(ext) = extension.to_str() {
writeln!(f, ": {ext}")
} else {
writeln!(f)
}
}
Self::NonUtf8Extension { path } => writeln!(
f,
"File {} has an extension that is not valid utf-8.",
path.display(),
),
Self::GrammarNotFound { path } => {
writeln!(f, "Grammar not found at {}", path.display(),)
}
Self::LexerGrammarSyntax { message, span } => {
writeln!(f, "Syntax error {span}.\n{message}")
}
Self::LexerGrammarDuplicateDefinition { token, span } => {
writeln!(f, "Found duplication definition of {token} {span}.")
}
Self::LexerGrammarUnwantedNoDescription { token, span } => {
writeln!(
f,
"{token} is tagged `unwanted` but has no description {span}."
)
}
Self::LexerGrammarEofString => {
writeln!(f, "Found EOF while reading a string.")
}
Self::LexingError { span } => {
writeln!(f, "Could not lex anything {span}.")
}
Self::UnwantedToken { span, message } => {
writeln!(f, "Lexing error {span}.\n{message}")
}
Self::GrammarDuplicateDefinition {
name: message,
span,
old_span,
} => {
writeln!(
f,
"Found two definitions of the same non-terminal {span} and {old_span}.\n{message}"
)
}
Self::GrammarDuplicateProxyItem {
span,
old_span,
name,
} => {
writeln!(
f,
"The proxy item {name} {span} was already defined {old_span}."
)
}
Self::GrammarDuplicateMacroDefinition {
span,
old_span,
name,
} => {
writeln!(f, "Macro {name} {span} was already defined {old_span}.")
}
Self::GrammarArityMismatch {
macro_name,
definition_arity,
call_arity,
definition_span,
call_span,
} => {
writeln!(f, "The macro {macro_name} is called with the wrong number of argument {call_arity} (expected {definition_arity}, {definition_span}) {call_span}.")
}
Self::GrammarNonTerminalDuplicate { message, span } => {
writeln!(
f,
"Found two definitions of the same non-terminal {span}\n.{message}"
)
}
Self::GrammarTerminalInvocation { terminal, span } => {
writeln!(
f,
"{terminal} is a terminal, not a macro, it cannot be invoked, {span}."
)
}
Self::GrammarSyntaxError { message, span } => {
writeln!(f, "Syntax error in the grammar {span}.\n{message}")
}
Self::GrammarVariantKey { span } => {
writeln!(f, "The `variant` key is reserved {span}.")
}
Self::SyntaxError {
name,
alternatives,
span,
} => {
writeln!(
f,
"Syntax error {name} {span}. You could have tried {alternatives:?}."
)
}
Self::IntegerTooBig { string, span } => {
writeln!(
f,
"Integer {string} does not fit on a 64 bit integer, {span}.\nWhy do you even need such a number?"
)
}
Self::GrammarUndefinedMacro { name, span } => {
writeln!(f, "Macro {name} is undefined {span}.")
}
Self::GrammarUndefinedNonTerminal { name, span } => {
writeln!(f, "Non-terminal {name} is undefined {span}.")
}
Self::SyntaxErrorValidPrefix { span } => {
writeln!(
f,
"Syntax error {span}: reached EOF while parsing a valid file."
)
}
Self::IOError { error, path } => {
writeln!(
f,
"While opening {}, the following error occured: {error}",
path.display(),
)
}
Self::RegexError { span, message } => {
writeln!(f, "Regex error {span}.\n{message}",)
}
Self::SameOutputAndInput => {
writeln!(f, "Beans refuses to overwrite a file it is reading.")
}
Self::NonUtf8Content { path, error } => {
writeln!(
f,
"Could not decode {} as valid utf-8.\n{error}",
path.display()
)
}
}
}
}