use crate::debug::error::debug_parse_error;
use crate::debug::{restrict, DebugWidth};
use crate::spans::LocatedSpanExt;
use crate::Code;
use nom::error::ErrorKind;
use nom::{InputIter, InputLength, InputTake, Offset, Slice};
use std::error::Error;
use std::fmt;
use std::fmt::{Debug, Display};
use std::marker::PhantomData;
use std::num::NonZeroUsize;
use std::ops::{RangeFrom, RangeTo};
pub struct ParserError<C, I, Y = ()> {
pub code: C,
pub span: I,
pub hints: Vec<Hints<C, I, Y>>,
}
pub enum Hints<C, I, Y> {
Nom(Nom<C, I>),
Needed(NonZeroUsize),
Expect(SpanAndCode<C, I>),
Suggest(SpanAndCode<C, I>),
Cause(Box<dyn Error>),
UserData(Y),
}
#[derive(Clone, Copy)]
pub struct Nom<C, I> {
pub kind: ErrorKind,
pub span: I,
pub ch: Option<char>,
pub _phantom: PhantomData<C>,
}
#[derive(Clone, Copy)]
pub struct SpanAndCode<C, I> {
pub code: C,
pub span: I,
}
pub trait AppendParserError<Rhs = Self> {
type Output;
fn append(&mut self, err: Rhs) -> Self::Output;
}
impl<C, I, Y> AppendParserError<ParserError<C, I, Y>> for ParserError<C, I, Y>
where
C: Code,
I: Copy,
Y: Copy,
{
type Output = ();
fn append(&mut self, err: ParserError<C, I, Y>) {
ParserError::append(self, err);
}
}
impl<C, I, Y> AppendParserError<ParserError<C, I, Y>> for Option<ParserError<C, I, Y>>
where
C: Code,
I: Copy,
Y: Copy,
{
type Output = ();
fn append(&mut self, err: ParserError<C, I, Y>) {
match self {
None => *self = Some(err),
Some(v) => v.append(err),
}
}
}
impl<C, I, Y> AppendParserError<nom::Err<ParserError<C, I, Y>>> for Option<ParserError<C, I, Y>>
where
C: Code,
I: Copy,
Y: Copy,
{
type Output = Result<(), nom::Err<ParserError<C, I, Y>>>;
fn append(
&mut self,
err: nom::Err<ParserError<C, I, Y>>,
) -> Result<(), nom::Err<ParserError<C, I, Y>>> {
match self {
None => match err {
nom::Err::Incomplete(e) => return Err(nom::Err::Incomplete(e)),
nom::Err::Error(e) => *self = Some(e),
nom::Err::Failure(e) => *self = Some(e),
},
Some(v) => match err {
nom::Err::Incomplete(e) => return Err(nom::Err::Incomplete(e)),
nom::Err::Error(e) => v.append(e),
nom::Err::Failure(e) => v.append(e),
},
};
Ok(())
}
}
impl<C, I, Y> nom::error::ParseError<I> for ParserError<C, I, Y>
where
C: Code,
I: Copy,
Y: Copy,
{
fn from_error_kind(input: I, kind: ErrorKind) -> Self {
ParserError {
code: C::NOM_ERROR,
span: input,
hints: vec![Hints::Nom(Nom {
kind,
span: input,
ch: None,
_phantom: Default::default(),
})],
}
}
fn append(input: I, kind: ErrorKind, mut other: Self) -> Self {
other.hints.push(Hints::Nom(Nom {
kind,
span: input,
ch: None,
_phantom: Default::default(),
}));
other
}
fn from_char(input: I, ch: char) -> Self {
ParserError {
code: C::NOM_ERROR,
span: input,
hints: vec![Hints::Nom(Nom {
kind: ErrorKind::Char,
span: input,
ch: Some(ch),
_phantom: Default::default(),
})],
}
}
fn or(mut self, other: Self) -> Self {
self.append(other);
self
}
}
impl<C, I, Y> Display for ParserError<C, I, Y>
where
C: Code,
I: Copy + Debug,
I: Offset
+ InputTake
+ InputIter
+ InputLength
+ Slice<RangeFrom<usize>>
+ Slice<RangeTo<usize>>,
Y: Copy,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?} ", self.code)?;
if self.iter_expected().next().is_some() {
write!(f, " expected ")?;
}
for (i, exp) in self.iter_expected().enumerate() {
if i > 0 {
write!(f, " ")?;
}
write!(f, "{}", exp.code,)?;
}
if let Some(nom) = self.nom() {
if let Some(ch) = nom.ch {
write!(f, " errorkind {:?} {:?}", nom.kind, ch)?
} else {
write!(f, " errorkind {:?}", nom.kind)?
}
}
write!(f, " for span {:?}", restrict(DebugWidth::Short, self.span))?;
Ok(())
}
}
impl<C, I, Y> Debug for ParserError<C, I, Y>
where
C: Code,
I: Copy + Debug,
I: Offset
+ InputTake
+ InputIter
+ InputLength
+ Slice<RangeFrom<usize>>
+ Slice<RangeTo<usize>>,
Y: Copy,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
debug_parse_error(f, self)
}
}
impl<C, I, Y> Debug for Hints<C, I, Y>
where
C: Code,
I: Copy + Debug,
I: Offset
+ InputTake
+ InputIter
+ InputLength
+ Slice<RangeFrom<usize>>
+ Slice<RangeTo<usize>>,
Y: Copy + Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Hints::Nom(v) => write!(f, "Nom {:?}", v),
Hints::Needed(v) => write!(f, "Needed {:?}", v),
Hints::Expect(v) => write!(f, "Expect {:?}", v),
Hints::Suggest(v) => write!(f, "Suggest {:?}", v),
Hints::Cause(v) => write!(f, "Cause {:?}", v),
Hints::UserData(v) => write!(f, "UserData {:?}", v),
}
}
}
impl<C, I> Debug for Nom<C, I>
where
C: Code,
I: Copy + Debug,
I: Offset
+ InputTake
+ InputIter
+ InputLength
+ Slice<RangeFrom<usize>>
+ Slice<RangeTo<usize>>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let w = f.width().into();
write!(f, "{:?}:{:?}", self.kind, restrict(w, self.span))?;
Ok(())
}
}
impl<C, I> Debug for SpanAndCode<C, I>
where
C: Code,
I: Copy + Debug,
I: Offset
+ InputTake
+ InputIter
+ InputLength
+ Slice<RangeFrom<usize>>
+ Slice<RangeTo<usize>>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let w = f.width().into();
write!(f, "{}:{:?}", self.code, restrict(w, self.span))?;
Ok(())
}
}
impl<C, I, Y> Error for ParserError<C, I, Y>
where
C: Code,
I: Copy + Debug,
I: Offset
+ InputTake
+ InputIter
+ InputLength
+ Slice<RangeFrom<usize>>
+ Slice<RangeTo<usize>>,
Y: Copy,
{
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.hints
.iter()
.find(|v| matches!(v, Hints::Cause(_)))
.and_then(|v| {
if let Hints::Cause(e) = v {
Some(e.as_ref())
} else {
None
}
})
}
}
impl<C, I, Y> ParserError<C, I, Y>
where
C: Code,
I: Copy,
Y: Copy,
{
pub fn new(code: C, span: I) -> Self {
Self {
code,
span,
hints: Vec::new(),
}
}
pub fn new_suggest(code: C, span: I) -> Self {
Self {
code,
span,
hints: vec![Hints::Suggest(SpanAndCode { code, span })],
}
}
pub fn with_nom(mut self, span: I, kind: ErrorKind) -> Self {
self.hints.push(Hints::Nom(Nom {
kind,
span,
ch: None,
_phantom: Default::default(),
}));
self
}
pub fn with_cause(mut self, err: Box<dyn Error>) -> Self {
self.hints.push(Hints::Cause(err));
self
}
pub fn with_user_data(mut self, user_data: Y) -> Self {
self.hints.push(Hints::UserData(user_data));
self
}
pub fn nom(&self) -> Option<&Nom<C, I>> {
self.hints
.iter()
.find(|v| matches!(v, Hints::Nom(_)))
.and_then(|v| match v {
Hints::Nom(e) => Some(e),
_ => None,
})
}
pub fn cause(&self) -> Option<&dyn Error> {
self.hints
.iter()
.find(|v| matches!(v, Hints::Cause(_)))
.and_then(|v| match v {
Hints::Cause(e) => Some(e.as_ref()),
_ => None,
})
}
pub fn user_data(&self) -> Option<&Y> {
self.hints
.iter()
.find(|v| matches!(v, Hints::UserData(_)))
.and_then(|v| match v {
Hints::UserData(e) => Some(e),
_ => None,
})
}
pub fn append(&mut self, other: ParserError<C, I, Y>) {
if other.code != C::NOM_ERROR {
self.expect(other.code, other.span);
}
for hint in other.hints {
self.hints.push(hint);
}
}
pub fn with_code(mut self, code: C) -> Self {
if self.code != code && self.code != C::NOM_ERROR {
self.hints.push(Hints::Expect(SpanAndCode {
code: self.code,
span: self.span,
}));
}
self.code = code;
self
}
pub fn is_error_kind(&self, kind: ErrorKind) -> bool {
for n in &self.hints {
if let Hints::Nom(n) = n {
if n.kind == kind {
return true;
}
}
}
false
}
pub fn is_expected(&self, code: C) -> bool {
for exp in &self.hints {
if let Hints::Expect(exp) = exp {
if exp.code == code {
return true;
}
}
}
false
}
pub fn expect(&mut self, code: C, span: I) {
self.hints.push(Hints::Expect(SpanAndCode { code, span }))
}
pub fn append_expected(&mut self, exp: Vec<SpanAndCode<C, I>>) {
for exp in exp.into_iter() {
self.hints.push(Hints::Expect(exp));
}
}
pub fn iter_expected(&self) -> impl Iterator<Item = &SpanAndCode<C, I>> {
self.hints.iter().rev().filter_map(|v| match v {
Hints::Expect(n) => Some(n),
_ => None,
})
}
pub fn expected_grouped_by_offset(&self) -> Vec<(usize, Vec<&SpanAndCode<C, I>>)>
where
I: LocatedSpanExt,
{
let mut sorted: Vec<&SpanAndCode<C, I>> = self.iter_expected().collect();
sorted.sort_by(|a, b| b.span.location_offset().cmp(&a.span.location_offset()));
let mut grp_offset = 0;
let mut grp = Vec::new();
let mut subgrp = Vec::new();
for exp in &sorted {
if exp.span.location_offset() != grp_offset {
if !subgrp.is_empty() {
grp.push((grp_offset, subgrp));
subgrp = Vec::new();
}
grp_offset = exp.span.location_offset();
}
subgrp.push(*exp);
}
if !subgrp.is_empty() {
grp.push((grp_offset, subgrp));
}
grp
}
pub fn expected_grouped_by_line(&self) -> Vec<(u32, Vec<&SpanAndCode<C, I>>)>
where
I: LocatedSpanExt,
{
let mut sorted: Vec<&SpanAndCode<C, I>> = self.iter_expected().collect();
sorted.sort_by(|a, b| b.span.location_line().cmp(&a.span.location_line()));
let mut grp_line = 0;
let mut grp = Vec::new();
let mut subgrp = Vec::new();
for exp in &sorted {
if exp.span.location_line() != grp_line {
if !subgrp.is_empty() {
grp.push((grp_line, subgrp));
subgrp = Vec::new();
}
grp_line = exp.span.location_line();
}
subgrp.push(*exp);
}
if !subgrp.is_empty() {
grp.push((grp_line, subgrp));
}
grp
}
pub fn suggest(&mut self, code: C, span: I) {
self.hints.push(Hints::Suggest(SpanAndCode { code, span }))
}
pub fn append_suggested(&mut self, exp: Vec<SpanAndCode<C, I>>) {
for exp in exp.into_iter() {
self.hints.push(Hints::Suggest(exp));
}
}
pub fn iter_suggested(&self) -> impl Iterator<Item = &SpanAndCode<C, I>> {
self.hints.iter().rev().filter_map(|v| match v {
Hints::Suggest(n) => Some(n),
_ => None,
})
}
pub fn suggested_grouped_by_offset(&self) -> Vec<(usize, Vec<&SpanAndCode<C, I>>)>
where
I: LocatedSpanExt,
{
let mut sorted: Vec<&SpanAndCode<C, I>> = self.iter_suggested().collect();
sorted.sort_by(|a, b| b.span.location_offset().cmp(&a.span.location_offset()));
let mut grp_offset = 0;
let mut grp = Vec::new();
let mut subgrp = Vec::new();
for exp in &sorted {
if exp.span.location_offset() != grp_offset {
if !subgrp.is_empty() {
grp.push((grp_offset, subgrp));
subgrp = Vec::new();
}
grp_offset = exp.span.location_offset();
}
subgrp.push(*exp);
}
if !subgrp.is_empty() {
grp.push((grp_offset, subgrp));
}
grp
}
pub fn suggested_grouped_by_line(&self) -> Vec<(u32, Vec<&SpanAndCode<C, I>>)>
where
I: LocatedSpanExt,
{
let mut sorted: Vec<&SpanAndCode<C, I>> = self.iter_suggested().collect();
sorted.sort_by(|a, b| b.span.location_line().cmp(&a.span.location_line()));
let mut grp_line = 0;
let mut grp = Vec::new();
let mut subgrp = Vec::new();
for exp in &sorted {
if exp.span.location_line() != grp_line {
if !subgrp.is_empty() {
grp.push((grp_line, subgrp));
subgrp = Vec::new();
}
grp_line = exp.span.location_line();
}
subgrp.push(*exp);
}
if !subgrp.is_empty() {
grp.push((grp_line, subgrp));
}
grp
}
}