use std::{
error::Error,
fmt::{self, Debug, Display, Formatter, Write},
};
use indent_write::fmt::IndentWriter;
use joinery::JoinableIterator;
use nom::{
error::{ErrorKind as NomErrorKind, FromExternalError, ParseError},
ErrorConvert, InputLength,
};
use crate::{
context::ContextError,
final_parser::{ExtractContext, RecreateContext},
tag::TagError,
};
#[non_exhaustive]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Expectation<T> {
Tag(T),
Char(char),
Alpha,
Digit,
HexDigit,
OctDigit,
AlphaNumeric,
Space,
Multispace,
CrLf,
Eof,
Something,
}
impl<T: Debug> Display for Expectation<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self {
Expectation::Tag(ref tag) => write!(f, "{:?}", tag),
Expectation::Char(c) => write!(f, "{:?}", c),
Expectation::Alpha => write!(f, "an ascii letter"),
Expectation::Digit => write!(f, "an ascii digit"),
Expectation::HexDigit => write!(f, "a hexadecimal digit"),
Expectation::OctDigit => write!(f, "an octal digit"),
Expectation::AlphaNumeric => write!(f, "an ascii alphanumeric character"),
Expectation::Space => write!(f, "a space or tab"),
Expectation::Multispace => write!(f, "whitespace"),
Expectation::Eof => write!(f, "eof"),
Expectation::CrLf => write!(f, "CRLF"),
Expectation::Something => write!(f, "not eof"),
}
}
}
#[derive(Debug)]
pub enum BaseErrorKind<T, E> {
Expected(Expectation<T>),
Kind(NomErrorKind),
External(E),
}
impl<T: Debug, E: Display> Display for BaseErrorKind<T, E> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self {
BaseErrorKind::Expected(ref expectation) => write!(f, "expected {}", expectation),
BaseErrorKind::External(ref err) => {
writeln!(f, "external error:")?;
let mut f = IndentWriter::new(" ", f);
write!(f, "{}", err)
}
BaseErrorKind::Kind(kind) => write!(f, "error in {:?}", kind),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StackContext<C> {
Kind(NomErrorKind),
Context(C),
}
impl<C: Debug> Display for StackContext<C> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self {
StackContext::Kind(kind) => write!(f, "while parsing {:?}", kind),
StackContext::Context(ref ctx) => write!(f, "in section {:?}", ctx),
}
}
}
pub type ErrorTree<I> =
GenericErrorTree<I, &'static str, &'static str, Box<dyn Error + Send + Sync + 'static>>;
#[derive(Debug)]
pub enum GenericErrorTree<Location, Tag, Context, ExternalError> {
Base {
location: Location,
kind: BaseErrorKind<Tag, ExternalError>,
},
Stack {
base: Box<Self>,
contexts: Vec<(Location, StackContext<Context>)>,
},
Alt(Vec<Self>),
}
impl<I, T, C, E> GenericErrorTree<I, T, C, E> {
fn map_locations_ref<I2>(
self,
convert_location: &mut impl FnMut(I) -> I2,
) -> GenericErrorTree<I2, T, C, E> {
match self {
GenericErrorTree::Base { location, kind } => GenericErrorTree::Base {
location: convert_location(location),
kind,
},
GenericErrorTree::Stack { base, contexts } => GenericErrorTree::Stack {
base: Box::new(base.map_locations_ref(convert_location)),
contexts: contexts
.into_iter()
.map(|(location, context)| (convert_location(location), context))
.collect(),
},
GenericErrorTree::Alt(siblings) => GenericErrorTree::Alt(
siblings
.into_iter()
.map(|err| err.map_locations_ref(convert_location))
.collect(),
),
}
}
pub fn map_locations<I2>(
self,
mut convert_location: impl FnMut(I) -> I2,
) -> GenericErrorTree<I2, T, C, E> {
self.map_locations_ref(&mut convert_location)
}
}
impl<I: Display, T: Debug, C: Debug, E: Display> Display for GenericErrorTree<I, T, C, E> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
GenericErrorTree::Base { location, kind } => write!(f, "{} at {:#}", kind, location),
GenericErrorTree::Stack { contexts, base } => {
contexts.iter().rev().try_for_each(|(location, context)| {
writeln!(f, "{} at {:#},", context, location)
})?;
base.fmt(f)
}
GenericErrorTree::Alt(siblings) => {
writeln!(f, "one of:")?;
let mut f = IndentWriter::new(" ", f);
write!(f, "{}", siblings.iter().join_with(", or\n"))
}
}
}
}
impl<I: Display + Debug, T: Debug, C: Debug, E: Display + Debug> Error
for GenericErrorTree<I, T, C, E>
{
}
impl<I: InputLength, T, C, E> ParseError<I> for GenericErrorTree<I, T, C, E> {
fn from_error_kind(location: I, kind: NomErrorKind) -> Self {
let kind = match kind {
NomErrorKind::Alpha => BaseErrorKind::Expected(Expectation::Alpha),
NomErrorKind::Digit => BaseErrorKind::Expected(Expectation::Digit),
NomErrorKind::HexDigit => BaseErrorKind::Expected(Expectation::HexDigit),
NomErrorKind::OctDigit => BaseErrorKind::Expected(Expectation::OctDigit),
NomErrorKind::AlphaNumeric => BaseErrorKind::Expected(Expectation::AlphaNumeric),
NomErrorKind::Space => BaseErrorKind::Expected(Expectation::Space),
NomErrorKind::MultiSpace => BaseErrorKind::Expected(Expectation::Multispace),
NomErrorKind::CrLf => BaseErrorKind::Expected(Expectation::CrLf),
NomErrorKind::Eof => match location.input_len() {
0 => BaseErrorKind::Expected(Expectation::Something),
_ => BaseErrorKind::Expected(Expectation::Eof),
},
kind => BaseErrorKind::Kind(kind),
};
GenericErrorTree::Base { location, kind }
}
fn append(location: I, kind: NomErrorKind, other: Self) -> Self {
let context = (location, StackContext::Kind(kind));
match other {
alt @ GenericErrorTree::Alt(..) if kind == NomErrorKind::Alt => alt,
GenericErrorTree::Stack { contexts, base } => GenericErrorTree::Stack {
base,
contexts: express!(contexts.push(context)),
},
base => GenericErrorTree::Stack {
base: Box::new(base),
contexts: vec![context],
},
}
}
fn from_char(location: I, character: char) -> Self {
GenericErrorTree::Base {
location,
kind: BaseErrorKind::Expected(Expectation::Char(character)),
}
}
fn or(self, other: Self) -> Self {
let siblings = match (self, other) {
(GenericErrorTree::Alt(siblings1), GenericErrorTree::Alt(siblings2)) => {
match siblings1.capacity() >= siblings2.capacity() {
true => express!(siblings1.extend(siblings2)),
false => express!(siblings2.extend(siblings1)),
}
}
(GenericErrorTree::Alt(siblings), err) | (err, GenericErrorTree::Alt(siblings)) => {
express!(siblings.push(err))
}
(err1, err2) => vec![err1, err2],
};
GenericErrorTree::Alt(siblings)
}
}
impl<I, T, C, E> ContextError<I, C> for GenericErrorTree<I, T, C, E> {
fn add_context(location: I, ctx: C, other: Self) -> Self {
let context = (location, StackContext::Context(ctx));
match other {
GenericErrorTree::Stack { contexts, base } => GenericErrorTree::Stack {
base,
contexts: express!(contexts.push(context)),
},
base => GenericErrorTree::Stack {
base: Box::new(base),
contexts: vec![context],
},
}
}
}
impl<I, T, E> nom::error::ContextError<I> for GenericErrorTree<I, T, &'static str, E> {
fn add_context(location: I, ctx: &'static str, other: Self) -> Self {
ContextError::add_context(location, ctx, other)
}
}
impl<I, T, C, E, E2> FromExternalError<I, E2> for GenericErrorTree<I, T, C, E>
where
E: From<E2>,
{
fn from_external_error(location: I, _kind: NomErrorKind, e: E2) -> Self {
GenericErrorTree::Base {
location,
kind: BaseErrorKind::External(e.into()),
}
}
}
impl<I, T: AsRef<[u8]>, C, E> TagError<I, T> for GenericErrorTree<I, T, C, E> {
fn from_tag(location: I, tag: T) -> Self {
GenericErrorTree::Base {
location,
kind: BaseErrorKind::Expected(match tag.as_ref() {
b"\r\n" => Expectation::CrLf,
_ => Expectation::Tag(tag),
}),
}
}
}
impl<I, T, C, E> ErrorConvert<GenericErrorTree<(I, usize), T, C, E>>
for GenericErrorTree<I, T, C, E>
{
fn convert(self) -> GenericErrorTree<(I, usize), T, C, E> {
self.map_locations(|location| (location, 0))
}
}
impl<I, T, C, E> ErrorConvert<GenericErrorTree<I, T, C, E>>
for GenericErrorTree<(I, usize), T, C, E>
{
fn convert(self) -> GenericErrorTree<I, T, C, E> {
self.map_locations(move |(location, _offset)| location)
}
}
impl<I, I2, T, C, E> ExtractContext<I, GenericErrorTree<I2, T, C, E>>
for GenericErrorTree<I, T, C, E>
where
I: Clone,
I2: RecreateContext<I>,
{
fn extract_context(self, original_input: I) -> GenericErrorTree<I2, T, C, E> {
self.map_locations(move |location| I2::recreate_context(original_input.clone(), location))
}
}
#[cfg(test)]
mod tests {
use super::*;
use nom::{
bits::{bits, complete::take},
sequence::tuple,
IResult, Parser,
};
type BitInput<'a> = (&'a [u8], usize);
fn parse_bool_bit(input: (&[u8], usize)) -> IResult<BitInput, bool, ErrorTree<BitInput>> {
take(1usize).map(|bit: u8| bit != 0).parse(input)
}
type Byte = (bool, bool, bool, bool, bool, bool, bool, bool);
fn parse_bits(input: &[u8]) -> IResult<&[u8], Byte, ErrorTree<&[u8]>> {
bits(tuple((
parse_bool_bit,
parse_bool_bit,
parse_bool_bit,
parse_bool_bit,
parse_bool_bit,
parse_bool_bit,
parse_bool_bit,
parse_bool_bit,
)))
.parse(input)
}
#[test]
fn error_tree_bits() {
let values = [0b1010_1111, 10];
let (tail, result) = parse_bits(&values).unwrap();
assert_eq!(tail, &[10]);
assert_eq!(result, (true, false, true, false, true, true, true, true));
}
}