aboutsummaryrefslogtreecommitdiff
path: root/src/error.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/error.rs')
-rw-r--r--src/error.rs281
1 files changed, 197 insertions, 84 deletions
diff --git a/src/error.rs b/src/error.rs
index 1505161..a5aa678 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -3,34 +3,41 @@
//! Parsers are generic over their error type, requiring that it implements
//! the `error::ParseError<Input>` trait.
-/// this trait must be implemented by the error type of a nom parser
+use crate::internal::Parser;
+use crate::lib::std::fmt;
+
+/// This trait must be implemented by the error type of a nom parser.
///
/// There are already implementations of it for `(Input, ErrorKind)`
/// and `VerboseError<Input>`.
///
/// It provides methods to create an error from some combinators,
-/// and combine existing errors in combinators like `alt`
+/// and combine existing errors in combinators like `alt`.
pub trait ParseError<I>: Sized {
- /// creates an error from the input position and an [ErrorKind]
+ /// Creates an error from the input position and an [ErrorKind]
fn from_error_kind(input: I, kind: ErrorKind) -> Self;
- /// combines an existing error with a new one created from the input
+ /// Combines an existing error with a new one created from the input
/// position and an [ErrorKind]. This is useful when backtracking
/// through a parse tree, accumulating error context on the way
fn append(input: I, kind: ErrorKind, other: Self) -> Self;
- /// creates an error from an input position and an expected character
+ /// Creates an error from an input position and an expected character
fn from_char(input: I, _: char) -> Self {
Self::from_error_kind(input, ErrorKind::Char)
}
- /// combines two existing error. This function is used to compare errors
+ /// Combines two existing errors. This function is used to compare errors
/// generated in various branches of [alt]
fn or(self, other: Self) -> Self {
other
}
+}
- /// create a new error from an input position, a static string and an existing error.
+/// This trait is required by the `context` combinator to add a static string
+/// to an existing error
+pub trait ContextError<I>: Sized {
+ /// Creates a new error from an input position, a static string and an existing error.
/// This is used mainly in the [context] combinator, to add user friendly information
/// to errors when backtracking through a parse tree
fn add_context(_input: I, _ctx: &'static str, other: Self) -> Self {
@@ -38,6 +45,61 @@ pub trait ParseError<I>: Sized {
}
}
+/// This trait is required by the [map_res] combinator to integrate
+/// error types from external functions, like [std::str::FromStr]
+pub trait FromExternalError<I, E> {
+ /// Creates a new error from an input position, an [ErrorKind] indicating the
+ /// wrapping parser, and an external error
+ fn from_external_error(input: I, kind: ErrorKind, e: E) -> Self;
+}
+
+/// default error type, only contains the error' location and code
+#[derive(Debug, PartialEq)]
+pub struct Error<I> {
+ /// position of the error in the input data
+ pub input: I,
+ /// nom error code
+ pub code: ErrorKind,
+}
+
+impl<I> Error<I> {
+ /// creates a new basic error
+ pub fn new(input: I, code: ErrorKind) -> Error<I> {
+ Error { input, code }
+ }
+}
+
+impl<I> ParseError<I> for Error<I> {
+ fn from_error_kind(input: I, kind: ErrorKind) -> Self {
+ Error { input, code: kind }
+ }
+
+ fn append(_: I, _: ErrorKind, other: Self) -> Self {
+ other
+ }
+}
+
+impl<I> ContextError<I> for Error<I> {}
+
+impl<I, E> FromExternalError<I, E> for Error<I> {
+ /// Create a new error from an input position and an external error
+ fn from_external_error(input: I, kind: ErrorKind, _e: E) -> Self {
+ Error { input, code: kind }
+ }
+}
+
+/// The Display implementation allows the std::error::Error implementation
+impl<I: fmt::Display> fmt::Display for Error<I> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "error {:?} at: {}", self.code, self.input)
+ }
+}
+
+#[cfg(feature = "std")]
+impl<I: fmt::Debug + fmt::Display> std::error::Error for Error<I> {}
+
+// for backward compatibility, keep those trait implementations
+// for the previously used error type
impl<I> ParseError<I> for (I, ErrorKind) {
fn from_error_kind(input: I, kind: ErrorKind) -> Self {
(input, kind)
@@ -48,48 +110,65 @@ impl<I> ParseError<I> for (I, ErrorKind) {
}
}
+impl<I> ContextError<I> for (I, ErrorKind) {}
+
+impl<I, E> FromExternalError<I, E> for (I, ErrorKind) {
+ fn from_external_error(input: I, kind: ErrorKind, _e: E) -> Self {
+ (input, kind)
+ }
+}
+
impl<I> ParseError<I> for () {
fn from_error_kind(_: I, _: ErrorKind) -> Self {}
fn append(_: I, _: ErrorKind, _: Self) -> Self {}
}
-/// creates an error from the input position and an [ErrorKind]
+impl<I> ContextError<I> for () {}
+
+impl<I, E> FromExternalError<I, E> for () {
+ fn from_external_error(_input: I, _kind: ErrorKind, _e: E) -> Self {}
+}
+
+/// Creates an error from the input position and an [ErrorKind]
pub fn make_error<I, E: ParseError<I>>(input: I, kind: ErrorKind) -> E {
E::from_error_kind(input, kind)
}
-/// combines an existing error with a new one created from the input
+/// Combines an existing error with a new one created from the input
/// position and an [ErrorKind]. This is useful when backtracking
/// through a parse tree, accumulating error context on the way
pub fn append_error<I, E: ParseError<I>>(input: I, kind: ErrorKind, other: E) -> E {
E::append(input, kind, other)
}
-/// this error type accumulates errors and their position when backtracking
+/// This error type accumulates errors and their position when backtracking
/// through a parse tree. With some post processing (cf `examples/json.rs`),
/// it can be used to display user friendly error messages
#[cfg(feature = "alloc")]
+#[cfg_attr(feature = "docsrs", doc(cfg(feature = "alloc")))]
#[derive(Clone, Debug, PartialEq)]
pub struct VerboseError<I> {
- /// list of errors accumulated by `VerboseError`, containing the affected
+ /// List of errors accumulated by `VerboseError`, containing the affected
/// part of input data, and some context
pub errors: crate::lib::std::vec::Vec<(I, VerboseErrorKind)>,
}
#[cfg(feature = "alloc")]
+#[cfg_attr(feature = "docsrs", doc(cfg(feature = "alloc")))]
#[derive(Clone, Debug, PartialEq)]
-/// error context for `VerboseError`
+/// Error context for `VerboseError`
pub enum VerboseErrorKind {
- /// static string added by the `context` function
+ /// Static string added by the `context` function
Context(&'static str),
- /// indicates which character was expected by the `char` function
+ /// Indicates which character was expected by the `char` function
Char(char),
- /// error kind given by various nom parsers
+ /// Error kind given by various nom parsers
Nom(ErrorKind),
}
#[cfg(feature = "alloc")]
+#[cfg_attr(feature = "docsrs", doc(cfg(feature = "alloc")))]
impl<I> ParseError<I> for VerboseError<I> {
fn from_error_kind(input: I, kind: ErrorKind) -> Self {
VerboseError {
@@ -107,23 +186,58 @@ impl<I> ParseError<I> for VerboseError<I> {
errors: vec![(input, VerboseErrorKind::Char(c))],
}
}
+}
+#[cfg(feature = "alloc")]
+#[cfg_attr(feature = "docsrs", doc(cfg(feature = "alloc")))]
+impl<I> ContextError<I> for VerboseError<I> {
fn add_context(input: I, ctx: &'static str, mut other: Self) -> Self {
other.errors.push((input, VerboseErrorKind::Context(ctx)));
other
}
}
+#[cfg(feature = "alloc")]
+#[cfg_attr(feature = "docsrs", doc(cfg(feature = "alloc")))]
+impl<I, E> FromExternalError<I, E> for VerboseError<I> {
+ /// Create a new error from an input position and an external error
+ fn from_external_error(input: I, kind: ErrorKind, _e: E) -> Self {
+ Self::from_error_kind(input, kind)
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl<I: fmt::Display> fmt::Display for VerboseError<I> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ writeln!(f, "Parse error:")?;
+ for (input, error) in &self.errors {
+ match error {
+ VerboseErrorKind::Nom(e) => writeln!(f, "{:?} at: {}", e, input)?,
+ VerboseErrorKind::Char(c) => writeln!(f, "expected '{}' at: {}", c, input)?,
+ VerboseErrorKind::Context(s) => writeln!(f, "in section '{}', at: {}", s, input)?,
+ }
+ }
+
+ Ok(())
+ }
+}
+
+#[cfg(feature = "std")]
+impl<I: fmt::Debug + fmt::Display> std::error::Error for VerboseError<I> {}
+
use crate::internal::{Err, IResult};
-/// create a new error from an input position, a static string and an existing error.
+/// Create a new error from an input position, a static string and an existing error.
/// This is used mainly in the [context] combinator, to add user friendly information
/// to errors when backtracking through a parse tree
-pub fn context<I: Clone, E: ParseError<I>, F, O>(context: &'static str, f: F) -> impl Fn(I) -> IResult<I, O, E>
+pub fn context<I: Clone, E: ContextError<I>, F, O>(
+ context: &'static str,
+ mut f: F,
+) -> impl FnMut(I) -> IResult<I, O, E>
where
- F: Fn(I) -> IResult<I, O, E>,
+ F: Parser<I, O, E>,
{
- move |i: I| match f(i.clone()) {
+ move |i: I| match f.parse(i.clone()) {
Ok(o) => Ok(o),
Err(Err::Incomplete(i)) => Err(Err::Incomplete(i)),
Err(Err::Error(e)) => Err(Err::Error(E::add_context(i, context, e))),
@@ -131,9 +245,13 @@ where
}
}
-/// transforms a `VerboseError` into a trace with input position information
+/// Transforms a `VerboseError` into a trace with input position information
#[cfg(feature = "alloc")]
-pub fn convert_error(input: &str, e: VerboseError<&str>) -> crate::lib::std::string::String {
+#[cfg_attr(feature = "docsrs", doc(cfg(feature = "alloc")))]
+pub fn convert_error<I: core::ops::Deref<Target = str>>(
+ input: I,
+ e: VerboseError<I>,
+) -> crate::lib::std::string::String {
use crate::lib::std::fmt::Write;
use crate::traits::Offset;
@@ -144,7 +262,9 @@ pub fn convert_error(input: &str, e: VerboseError<&str>) -> crate::lib::std::str
if input.is_empty() {
match kind {
- VerboseErrorKind::Char(c) => write!(&mut result, "{}: expected '{}', got empty input\n\n", i, c),
+ VerboseErrorKind::Char(c) => {
+ write!(&mut result, "{}: expected '{}', got empty input\n\n", i, c)
+ }
VerboseErrorKind::Context(s) => write!(&mut result, "{}: in {}, got empty input\n\n", i, s),
VerboseErrorKind::Nom(e) => write!(&mut result, "{}: in {:?}, got empty input\n\n", i, e),
}
@@ -156,45 +276,56 @@ pub fn convert_error(input: &str, e: VerboseError<&str>) -> crate::lib::std::str
// Find the line that includes the subslice:
// Find the *last* newline before the substring starts
- let line_begin = prefix.iter().rev().position(|&b| b == b'\n').map(|pos| offset - pos).unwrap_or(0);
+ let line_begin = prefix
+ .iter()
+ .rev()
+ .position(|&b| b == b'\n')
+ .map(|pos| offset - pos)
+ .unwrap_or(0);
// Find the full line after that newline
- let line = input[line_begin..].lines().next().unwrap_or(&input[line_begin..]).trim_end();
+ let line = input[line_begin..]
+ .lines()
+ .next()
+ .unwrap_or(&input[line_begin..])
+ .trim_end();
// The (1-indexed) column number is the offset of our substring into that line
let column_number = line.offset(substring) + 1;
match kind {
- VerboseErrorKind::Char(c) => if let Some(actual) = substring.chars().next() {
- write!(
- &mut result,
- "{i}: at line {line_number}:\n\
+ VerboseErrorKind::Char(c) => {
+ if let Some(actual) = substring.chars().next() {
+ write!(
+ &mut result,
+ "{i}: at line {line_number}:\n\
{line}\n\
{caret:>column$}\n\
expected '{expected}', found {actual}\n\n",
- i = i,
- line_number = line_number,
- line = line,
- caret = '^',
- column = column_number,
- expected = c,
- actual = actual,
- )
- } else {
- write!(
- &mut result,
- "{i}: at line {line_number}:\n\
+ i = i,
+ line_number = line_number,
+ line = line,
+ caret = '^',
+ column = column_number,
+ expected = c,
+ actual = actual,
+ )
+ } else {
+ write!(
+ &mut result,
+ "{i}: at line {line_number}:\n\
{line}\n\
{caret:>column$}\n\
expected '{expected}', got end of input\n\n",
- i = i,
- line_number = line_number,
- line = line,
- caret = '^',
- column = column_number,
- expected = c,
- )
- },
+ i = i,
+ line_number = line_number,
+ line = line,
+ caret = '^',
+ column = column_number,
+ expected = c,
+ )
+ }
+ }
VerboseErrorKind::Context(s) => write!(
&mut result,
"{i}: at line {line_number}, in {context}:\n\
@@ -228,8 +359,8 @@ pub fn convert_error(input: &str, e: VerboseError<&str>) -> crate::lib::std::str
result
}
-/// indicates which parser returned an error
-#[cfg_attr(rustfmt, rustfmt_skip)]
+/// Indicates which parser returned an error
+#[rustfmt::skip]
#[derive(Debug,PartialEq,Eq,Hash,Clone,Copy)]
#[allow(deprecated,missing_docs)]
pub enum ErrorKind {
@@ -285,11 +416,12 @@ pub enum ErrorKind {
Many0Count,
Many1Count,
Float,
+ Satisfy,
}
-#[cfg_attr(rustfmt, rustfmt_skip)]
+#[rustfmt::skip]
#[allow(deprecated)]
-/// converts an ErrorKind to a number
+/// Converts an ErrorKind to a number
pub fn error_to_u32(e: &ErrorKind) -> u32 {
match *e {
ErrorKind::Tag => 1,
@@ -344,13 +476,14 @@ pub fn error_to_u32(e: &ErrorKind) -> u32 {
ErrorKind::Many0Count => 72,
ErrorKind::Many1Count => 73,
ErrorKind::Float => 74,
+ ErrorKind::Satisfy => 75,
}
}
impl ErrorKind {
- #[cfg_attr(rustfmt, rustfmt_skip)]
+ #[rustfmt::skip]
#[allow(deprecated)]
- /// converts an ErrorKind to a text description
+ /// Converts an ErrorKind to a text description
pub fn description(&self) -> &str {
match *self {
ErrorKind::Tag => "Tag",
@@ -405,11 +538,12 @@ impl ErrorKind {
ErrorKind::Many0Count => "Count occurrence of >=0 patterns",
ErrorKind::Many1Count => "Count occurrence of >=1 patterns",
ErrorKind::Float => "Float",
+ ErrorKind::Satisfy => "Satisfy",
}
}
}
-/// creates a parse error from a `nom::ErrorKind`
+/// Creates a parse error from a `nom::ErrorKind`
/// and the position in the input
#[allow(unused_variables)]
#[macro_export(local_inner_macros)]
@@ -419,9 +553,9 @@ macro_rules! error_position(
});
);
-/// creates a parse error from a `nom::ErrorKind`,
+/// Creates a parse error from a `nom::ErrorKind`,
/// the position in the input and the next error in
-/// the parsing tree.
+/// the parsing tree
#[allow(unused_variables)]
#[macro_export(local_inner_macros)]
macro_rules! error_node_position(
@@ -430,27 +564,6 @@ macro_rules! error_node_position(
});
);
-/*
-
-#[cfg(feature = "std")]
-use $crate::lib::std::any::Any;
-#[cfg(feature = "std")]
-use $crate::lib::std::{error,fmt};
-#[cfg(feature = "std")]
-impl<E: fmt::Debug+Any> error::Error for Err<E> {
- fn description(&self) -> &str {
- self.description()
- }
-}
-
-#[cfg(feature = "std")]
-impl<E: fmt::Debug> fmt::Display for Err<E> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}", self.description())
- }
-}
-*/
-
//FIXME: error rewrite
/// translate parser result from IResult<I,O,u32> to IResult<I,O,E> with a custom type
///
@@ -516,19 +629,19 @@ macro_rules! fix_error (
/// `flat_map!(R -> IResult<R,S>, S -> IResult<S,T>) => R -> IResult<R, T>`
///
-/// combines a parser R -> IResult<R,S> and
-/// a parser S -> IResult<S,T> to return another
-/// parser R -> IResult<R,T>
+/// Combines a parser `R -> IResult<R,S>` and
+/// a parser `S -> IResult<S,T>` to return another
+/// parser `R -> IResult<R,T>`
///
/// ```rust
/// # #[macro_use] extern crate nom;
-/// # use nom::{Err, error::ErrorKind};
+/// # use nom::{Err, error::{Error, ErrorKind}};
/// use nom::number::complete::recognize_float;
///
/// named!(parser<&str, f64>, flat_map!(recognize_float, parse_to!(f64)));
///
/// assert_eq!(parser("123.45;"), Ok((";", 123.45)));
-/// assert_eq!(parser("abc"), Err(Err::Error(("abc", ErrorKind::Char))));
+/// assert_eq!(parser("abc"), Err(Err::Error(Error::new("abc", ErrorKind::Char))));
/// ```
#[macro_export(local_inner_macros)]
macro_rules! flat_map(
@@ -559,7 +672,7 @@ mod tests {
fn convert_error_panic() {
let input = "";
- let result: IResult<_, _, VerboseError<&str>> = char('x')(input);
+ let _result: IResult<_, _, VerboseError<&str>> = char('x')(input);
}
}