use crate::parser::peg::parser_core_expression::ExpressionContext;
use crate::sources::character_class::CharacterClass;
use crate::sources::span::Span;
use itertools::Itertools;
use miette::{Diagnostic, LabeledSpan, Severity, SourceCode};
use std::cmp::Ordering;
use std::fmt::{Display, Formatter};
use thiserror::Error;
#[derive(Debug, Clone, Error)]
#[error("A parse error occured!")]
pub struct PEGParseError {
pub span: Span,
pub expected: Vec<Expect>,
pub fail_left_rec: bool,
pub fail_loop: bool,
pub msgs: Vec<(String, String)>,
}
impl Diagnostic for PEGParseError
where
PEGParseError: std::error::Error,
{
fn severity(&self) -> Option<Severity> {
Some(Severity::Error)
}
fn source_code(&self) -> Option<&dyn SourceCode> {
Some(&self.span)
}
fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
if let Some(i) = self
.expected
.iter()
.find(|i| matches!(i, Expect::Custom(_)))
{
let label = LabeledSpan::new_with_span(Some(i.to_string()), self.span.clone());
return Some(Box::new(vec![label].into_iter()));
}
let expect_str = self.expected.iter().map(|exp| exp.to_string()).join(", ");
let mut labels = vec![];
if self.fail_left_rec {
labels.push(LabeledSpan::new_with_span(Some("Encountered left recursion here. This is a problem with the grammar, and may hide other errors.".to_string()), self.span.clone()));
}
if self.fail_loop {
labels.push(LabeledSpan::new_with_span(Some("Encountered an infinite loop here. This is a problem with the grammar, and may hide other errors.".to_string()), self.span.clone()));
}
match self.expected.len() {
0 => {}
1 => labels.push(LabeledSpan::new_with_span(
Some(format!("Expected {} here", expect_str)),
self.span.clone(),
)),
_ => labels.push(LabeledSpan::new_with_span(
Some(format!("Expected one of {} here", expect_str)),
self.span.clone(),
)),
}
Some(Box::new(labels.into_iter()))
}
fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
let mut helps = Vec::new();
for (sort, msg) in &self.msgs {
helps.push(format!("if a {sort} would be parsed here then {msg}"));
}
if helps.is_empty() {
None
} else {
Some(Box::new(helps.join("\n")))
}
}
}
impl PEGParseError {
pub fn expect(span: Span, expect: Expect, sort_context: &ExpressionContext) -> Self {
PEGParseError {
span,
expected: vec![expect],
fail_left_rec: false,
fail_loop: false,
msgs: if let Some(name) = sort_context.name {
sort_context
.error
.iter()
.map(|i| (name.to_string(), i.to_string()))
.collect()
} else {
vec![]
},
}
}
pub fn fail_left_recursion(span: Span) -> Self {
PEGParseError {
span,
expected: vec![],
fail_left_rec: true,
fail_loop: false,
msgs: vec![],
}
}
pub fn fail_loop(span: Span) -> Self {
PEGParseError {
span,
expected: vec![],
fail_left_rec: false,
fail_loop: true,
msgs: vec![],
}
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub enum Expect {
ExpectCharClass(CharacterClass),
ExpectString(String),
ExpectSort(String),
NotEntireInput(),
Custom(String),
}
impl Display for Expect {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Expect::ExpectCharClass(cc) => {
write!(f, "{}", cc)
}
Expect::ExpectString(s) => {
write!(f, "\'{}\'", s)
}
Expect::ExpectSort(s) => {
write!(f, "{}", s)
}
Expect::NotEntireInput() => {
write!(f, "more input")
}
Expect::Custom(e) => {
write!(f, "{e}")
}
}
}
}
impl PEGParseError {
pub fn combine(mut self, mut other: PEGParseError) -> PEGParseError {
assert_eq!(self.span.source.name(), other.span.source.name());
match self.span.position.cmp(&other.span.position) {
Ordering::Less => other,
Ordering::Greater => self,
Ordering::Equal => {
self.span.length = self.span.length.max(other.span.length);
self.expected.append(&mut other.expected);
self.fail_left_rec |= other.fail_left_rec;
self.msgs.extend(other.msgs);
self
}
}
}
pub fn combine_option_parse_error(
a: Option<PEGParseError>,
b: Option<PEGParseError>,
) -> Option<PEGParseError> {
match (a, b) {
(None, None) => None,
(None, Some(e)) => Some(e),
(Some(e), None) => Some(e),
(Some(e1), Some(e2)) => Some(e1.combine(e2)),
}
}
}