use crate::case::Case;
use crate::location::Span;
use fragile::Fragile;
use std::collections::{linked_list, LinkedList};
use std::fmt;
use std::rc::Rc;
const EMPTY_WARNING_SET: WarningSet = WarningSet::Empty;
pub type Result<T> = std::result::Result<WithWarnings<T>, Error>;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(
"Error in the Beans implementation: {message}. This should not happen."
)]
InternalError {
message: String,
},
#[error("Failed to serialize or deserialize: {0}")]
SerializationError(#[from] bincode::Error),
#[error(
"Error in the syntax of the lexer grammar: {message}, at {location}."
)]
LexerGrammarSyntax {
message: String,
location: Fragile<Span>,
},
#[error("Duplicate definition of the token {token}, at {location}.")]
LexerGrammarDuplicateDefinition {
token: String,
location: Fragile<Span>,
},
#[error("Error while lexing: {message}, at {location}")]
LexingError {
message: String,
location: Fragile<Span>,
},
#[error("Found duplicate definition of terminal in grammar: {message}, at {location}.")]
GrammarDuplicateDefinition {
message: String,
location: Fragile<Span>,
old_location: Fragile<Span>,
},
#[error("Found two references to {macro_name}, with arities {first_arity} and {second_arity}, at {location}.")]
GrammarArityMismatch {
macro_name: String,
first_arity: usize,
second_arity: usize,
location: Fragile<Span>,
},
#[error("Found duplicate definition of nonterminal in grammar: {message}, at {location}.")]
GrammarNonTerminalDuplicate {
message: String,
location: Fragile<Span>,
},
#[error("Tried to invoke terminal {terminal} as if it was a macro, at {location}.")]
GrammarTerminalInvocation {
terminal: String,
location: Fragile<Span>,
},
#[error("Syntax error in grammar: {message}, at {location}.")]
GrammarSyntaxError {
message: String,
location: Fragile<Span>,
},
#[error("The `variant` key is reserved in proxies, at {location}.")]
GrammarVariantKey { location: Fragile<Span> },
#[error("Syntax error: {message}, at {location}.")]
SyntaxError {
message: String,
location: Fragile<Span>,
},
#[error("IO error: {0}")]
IOError(#[from] std::io::Error),
#[error("Regex error: {message}, at {location}")]
RegexError {
location: Fragile<Span>,
message: String,
},
#[error("The file to be written is the same as the source file.")]
SameOutputAndInput,
}
#[derive(Debug, PartialEq, Eq)]
pub enum WarningType {
CaseConvention(Rc<str>, Case, Case),
UndefinedNonTerminal(Rc<str>, Rc<str>),
NullWarning,
}
#[derive(Debug, PartialEq, Eq)]
pub struct Warning {
location: Option<Span>,
warn_type: WarningType,
}
impl Warning {
pub fn new(warn_type: WarningType) -> Self {
Self {
warn_type,
location: None,
}
}
pub fn with_location(warn_type: WarningType, location: Span) -> Self {
Self {
warn_type,
location: Some(location),
}
}
pub fn warn_type(&self) -> &WarningType {
&self.warn_type
}
pub fn location(&self) -> Option<&Span> {
self.location.as_ref()
}
}
impl fmt::Display for Warning {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use WarningType::*;
let r#type;
let message: Box<dyn AsRef<str>>;
match &self.warn_type {
CaseConvention(msg, _case_found, _case_expected) => {
r#type = "Case convention warning";
message = Box::new(msg.as_ref());
}
UndefinedNonTerminal(origin, non_terminal) => {
r#type = "Undefined non-terminal warning";
message = Box::new(format!(
"In definition of non-terminal `{}', `{}' has been used but not defined",
origin, non_terminal
));
}
NullWarning => {
r#type = "Warning";
message = Box::new("Empty warning");
}
};
if let Some(location) = self.location.as_ref() {
write!(
f,
"{}\n @{}, from {}:{} to {}:{}\n{}",
r#type,
location.file().display(),
location.start().0,
location.start().1,
location.end().0,
location.end().1,
(*message).as_ref()
)
} else {
write!(f, "{}\n{}", r#type, (*message).as_ref())
}
}
}
#[derive(Debug)]
pub struct WarningIterator<'iter> {
warnings: Option<linked_list::Iter<'iter, Warning>>,
}
impl<'iter> WarningIterator<'iter> {
fn new(warnings: Option<linked_list::Iter<'iter, Warning>>) -> Self {
Self { warnings }
}
}
impl<'iter> Iterator for WarningIterator<'iter> {
type Item = &'iter Warning;
fn next(&mut self) -> Option<Self::Item> {
self.warnings.as_mut().and_then(|iterator| iterator.next())
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum WarningSet {
Set(LinkedList<Warning>),
Empty,
}
impl WarningSet {
#[inline]
pub fn empty() -> Self {
EMPTY_WARNING_SET
}
pub fn add(&mut self, warning: Warning) {
match self {
Self::Set(warnings) => warnings.push_back(warning),
Self::Empty => {
let mut ll = LinkedList::new();
ll.push_back(warning);
*self = Self::Set(ll);
}
}
}
pub fn iter(&self) -> WarningIterator<'_> {
WarningIterator::new(match self {
Self::Set(warnings) => Some(warnings.iter()),
Self::Empty => None,
})
}
pub fn extend(&mut self, warningset: Self) {
match self {
Self::Set(selfwarnings) => match warningset {
Self::Set(mut otherwarnings) => {
selfwarnings.append(&mut otherwarnings)
}
Self::Empty => {}
},
Self::Empty => match warningset {
Self::Set(otherwarnings) => *self = Self::Set(otherwarnings),
Self::Empty => {}
},
}
}
#[inline]
pub fn is_empty(&self) -> bool {
matches!(self, Self::Empty)
}
#[inline]
pub fn unpack<T>(
&mut self,
WithWarnings { content, warnings }: WithWarnings<T>,
) -> T {
self.extend(warnings);
content
}
#[inline]
pub fn with<T>(self, content: T) -> WithWarnings<T> {
WithWarnings {
content,
warnings: self,
}
}
#[inline]
pub fn with_ok<T, E>(
self,
content: T,
) -> std::result::Result<WithWarnings<T>, E> {
Ok(self.with(content))
}
#[inline]
pub fn empty_with<T>(content: T) -> WithWarnings<T> {
Self::empty().with(content)
}
#[inline]
pub fn on<T, F, U>(
mut self,
content: WithWarnings<T>,
f: F,
) -> WithWarnings<U>
where
F: FnOnce(T) -> U,
{
let u = f(self.unpack(content));
self.with(u)
}
}
impl Default for WarningSet {
fn default() -> Self {
Self::empty()
}
}
#[derive(Debug)]
#[must_use]
pub struct WithWarnings<T> {
content: T,
warnings: WarningSet,
}
impl<T> WithWarnings<T> {
#[inline]
pub fn unwrap(self) -> T {
self.content
}
#[inline]
pub fn chain<F, U>(self, f: F) -> WithWarnings<U>
where
F: FnOnce(T) -> WithWarnings<U>,
{
let WithWarnings {
content,
mut warnings,
} = f(self.content);
warnings.extend(self.warnings);
WithWarnings { content, warnings }
}
#[inline]
pub fn unpack_into(self, warnings: &mut WarningSet) -> T {
warnings.unpack(self)
}
#[inline]
pub fn with(mut self, warnings: WarningSet) -> Self {
self.warnings.extend(warnings);
self
}
#[inline]
pub fn with_ok<E>(
self,
warnings: WarningSet,
) -> std::result::Result<WithWarnings<T>, E> {
Ok(self.with(warnings))
}
}