/// 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`. pubtrait ParseError<I>: Sized { /// 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 /// 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 fn from_char(input: I, _: char) -> Self { Self::from_error_kind(input, ErrorKind::Char)
}
/// Combines two existing errors. This function is used to compare errors /// generated in various branches of `alt`. fn or(self, other: Self) -> Self {
other
}
}
/// This trait is required by the `context` combinator to add a static string /// to an existing error pubtrait 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 {
other
}
}
/// This trait is required by the `map_res` combinator to integrate /// error types from external functions, like [std::str::FromStr] pubtrait 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)] pubstruct 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 pubfn 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 }
}
}
// 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)
}
fn append(_: I, _: ErrorKind, other: Self) -> Self {
other
}
}
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] pubfn 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 /// position and an [ErrorKind]. This is useful when backtracking /// through a parse tree, accumulating error context on the way pubfn 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 /// 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)] pubstruct VerboseError<I> { /// 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` pubenum VerboseErrorKind { /// Static string added by the `context` function
Context(&'static str), /// Indicates which character was expected by the `char` function
Char(char), /// Error kind given by various nom parsers
Nom(ErrorKind),
}
#[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)?,
}
}
/// 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 pubfn context<I: Clone, E: ContextError<I>, F, O>(
context: &'static str, mut f: F,
) -> impl FnMut(I) -> IResult<I, O, E> where
F: Parser<I, O, E>,
{ 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))),
Err(Err::Failure(e)) => Err(Err::Failure(E::add_context(i, context, e))),
}
}
/// Transforms a `VerboseError` into a trace with input position information #[cfg(feature = "alloc")] #[cfg_attr(feature = "docsrs", doc(cfg(feature = "alloc")))] pubfn convert_error<I: core::ops::Deref<Target = str>>(
input: I,
e: VerboseError<I>,
) -> crate::lib::std::string::String { usecrate::lib::std::fmt::Write; usecrate::traits::Offset;
letmut result = crate::lib::std::string::String::new();
for (i, (substring, kind)) in e.errors.iter().enumerate() { let offset = input.offset(substring);
if input.is_empty() { match kind {
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),
}
} else { let prefix = &input.as_bytes()[..offset];
// Count the number of newlines in the first `offset` bytes of input let line_number = prefix.iter().filter(|&&b| b == b'\n').count() + 1;
// 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);
// Find the full line after that newline 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) => { iflet 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\
{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,
)
}
}
VerboseErrorKind::Context(s) => write!(
&mut result, "{i}: at line {line_number}, in {context}:\n\
{line}\n\
{caret:>column$}\n\n",
i = i,
line_number = line_number,
context = s,
line = line,
caret = '^',
column = column_number,
),
VerboseErrorKind::Nom(e) => write!(
&mut result, "{i}: at line {line_number}, in {nom_err:?}:\n\
{line}\n\
{caret:>column$}\n\n",
i = i,
line_number = line_number,
nom_err = e,
line = line,
caret = '^',
column = column_number,
),
}
} // Because `write!` to a `String` is infallible, this `unwrap` is fine.
.unwrap();
}
/// Creates a parse error from a `nom::ErrorKind` /// and the position in the input #[allow(unused_variables)] #[macro_export(local_inner_macros)]
macro_rules! error_position(
($input:expr, $code:expr) => ({
$crate::error::make_error($input, $code)
});
);
/// Creates a parse error from a `nom::ErrorKind`, /// the position in the input and the next error in /// the parsing tree #[allow(unused_variables)] #[macro_export(local_inner_macros)]
macro_rules! error_node_position(
($input:expr, $code:expr, $next:expr) => ({
$crate::error::append_error($input, $code, $next)
});
);
/// Prints a message and the input if the parser fails. /// /// The message prints the `Error` or `Incomplete` /// and the parser's calling code. /// /// It also displays the input in hexdump format /// /// ```rust /// use nom::{IResult, error::dbg_dmp, bytes::complete::tag}; /// /// fn f(i: &[u8]) -> IResult<&[u8], &[u8]> { /// dbg_dmp(tag("abcd"), "tag")(i) /// } /// /// let a = &b"efghijkl"[..]; /// /// // Will print the following message: /// // Error(Position(0, [101, 102, 103, 104, 105, 106, 107, 108])) at l.5 by ' tag ! ( "abcd" ) ' /// // 00000000 65 66 67 68 69 6a 6b 6c efghijkl /// f(a); /// ``` #[cfg(feature = "std")] #[cfg_attr(feature = "docsrs", doc(cfg(feature = "std")))] pubfn dbg_dmp<'a, F, O, E: std::fmt::Debug>(
f: F,
context: &'static str,
) -> implFn(&'a [u8]) -> IResult<&'a [u8], O, E> where
F: Fn(&'a [u8]) -> IResult<&'a [u8], O, E>,
{ usecrate::HexDisplay; move |i: &'a [u8]| match f(i) {
Err(e) => {
println!("{}: Error({:?}) at:\n{}", context, e, i.to_hex(8));
Err(e)
}
a => a,
}
}
#[cfg(test)] #[cfg(feature = "alloc")] mod tests { usesuper::*; usecrate::character::complete::char;
#[test] fn convert_error_panic() { let input = "";
let _result: IResult<_, _, VerboseError<&str>> = char('x')(input);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.