//! Error management //! //! Parsers are generic over their error type, requiring that it implements //! the `error::ParseError` trait. /// this trait must be implemented by the error type of a nom parser /// /// There are already implementations of it for `(Input, ErrorKind)` /// and `VerboseError`. /// /// It provides methods to create an error from some combinators, /// and combine existing errors in combinators like `alt` pub trait ParseError: 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 error. 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 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 } } impl ParseError for (I, ErrorKind) { fn from_error_kind(input: I, kind: ErrorKind) -> Self { (input, kind) } fn append(_: I, _: ErrorKind, other: Self) -> Self { other } } impl ParseError for () { fn from_error_kind(_: I, _: ErrorKind) -> Self {} fn append(_: I, _: ErrorKind, _: Self) -> Self {} } /// creates an error from the input position and an [ErrorKind] pub fn make_error>(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 pub fn append_error>(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")] #[derive(Clone, Debug, PartialEq)] pub struct VerboseError { /// 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")] #[derive(Clone, Debug, PartialEq)] /// error context for `VerboseError` pub enum 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")] impl ParseError for VerboseError { fn from_error_kind(input: I, kind: ErrorKind) -> Self { VerboseError { errors: vec![(input, VerboseErrorKind::Nom(kind))], } } fn append(input: I, kind: ErrorKind, mut other: Self) -> Self { other.errors.push((input, VerboseErrorKind::Nom(kind))); other } fn from_char(input: I, c: char) -> Self { VerboseError { errors: vec![(input, VerboseErrorKind::Char(c))], } } fn add_context(input: I, ctx: &'static str, mut other: Self) -> Self { other.errors.push((input, VerboseErrorKind::Context(ctx))); other } } use crate::internal::{Err, IResult}; /// 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, F, O>(context: &'static str, f: F) -> impl Fn(I) -> IResult where F: Fn(I) -> IResult, { move |i: I| match f(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")] pub fn convert_error(input: &str, e: VerboseError<&str>) -> crate::lib::std::string::String { use crate::lib::std::fmt::Write; use crate::traits::Offset; let mut 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) => 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\ {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(); } result } /// indicates which parser returned an error #[cfg_attr(rustfmt, rustfmt_skip)] #[derive(Debug,PartialEq,Eq,Hash,Clone,Copy)] #[allow(deprecated,missing_docs)] pub enum ErrorKind { Tag, MapRes, MapOpt, Alt, IsNot, IsA, SeparatedList, SeparatedNonEmptyList, Many0, Many1, ManyTill, Count, TakeUntil, LengthValue, TagClosure, Alpha, Digit, HexDigit, OctDigit, AlphaNumeric, Space, MultiSpace, LengthValueFn, Eof, Switch, TagBits, OneOf, NoneOf, Char, CrLf, RegexpMatch, RegexpMatches, RegexpFind, RegexpCapture, RegexpCaptures, TakeWhile1, Complete, Fix, Escaped, EscapedTransform, NonEmpty, ManyMN, Not, Permutation, Verify, TakeTill1, TakeWhileMN, ParseTo, TooLarge, Many0Count, Many1Count, Float, } #[cfg_attr(rustfmt, rustfmt_skip)] #[allow(deprecated)] /// converts an ErrorKind to a number pub fn error_to_u32(e: &ErrorKind) -> u32 { match *e { ErrorKind::Tag => 1, ErrorKind::MapRes => 2, ErrorKind::MapOpt => 3, ErrorKind::Alt => 4, ErrorKind::IsNot => 5, ErrorKind::IsA => 6, ErrorKind::SeparatedList => 7, ErrorKind::SeparatedNonEmptyList => 8, ErrorKind::Many1 => 9, ErrorKind::Count => 10, ErrorKind::TakeUntil => 12, ErrorKind::LengthValue => 15, ErrorKind::TagClosure => 16, ErrorKind::Alpha => 17, ErrorKind::Digit => 18, ErrorKind::AlphaNumeric => 19, ErrorKind::Space => 20, ErrorKind::MultiSpace => 21, ErrorKind::LengthValueFn => 22, ErrorKind::Eof => 23, ErrorKind::Switch => 27, ErrorKind::TagBits => 28, ErrorKind::OneOf => 29, ErrorKind::NoneOf => 30, ErrorKind::Char => 40, ErrorKind::CrLf => 41, ErrorKind::RegexpMatch => 42, ErrorKind::RegexpMatches => 43, ErrorKind::RegexpFind => 44, ErrorKind::RegexpCapture => 45, ErrorKind::RegexpCaptures => 46, ErrorKind::TakeWhile1 => 47, ErrorKind::Complete => 48, ErrorKind::Fix => 49, ErrorKind::Escaped => 50, ErrorKind::EscapedTransform => 51, ErrorKind::NonEmpty => 56, ErrorKind::ManyMN => 57, ErrorKind::HexDigit => 59, ErrorKind::OctDigit => 61, ErrorKind::Many0 => 62, ErrorKind::Not => 63, ErrorKind::Permutation => 64, ErrorKind::ManyTill => 65, ErrorKind::Verify => 66, ErrorKind::TakeTill1 => 67, ErrorKind::TakeWhileMN => 69, ErrorKind::ParseTo => 70, ErrorKind::TooLarge => 71, ErrorKind::Many0Count => 72, ErrorKind::Many1Count => 73, ErrorKind::Float => 74, } } impl ErrorKind { #[cfg_attr(rustfmt, rustfmt_skip)] #[allow(deprecated)] /// converts an ErrorKind to a text description pub fn description(&self) -> &str { match *self { ErrorKind::Tag => "Tag", ErrorKind::MapRes => "Map on Result", ErrorKind::MapOpt => "Map on Option", ErrorKind::Alt => "Alternative", ErrorKind::IsNot => "IsNot", ErrorKind::IsA => "IsA", ErrorKind::SeparatedList => "Separated list", ErrorKind::SeparatedNonEmptyList => "Separated non empty list", ErrorKind::Many0 => "Many0", ErrorKind::Many1 => "Many1", ErrorKind::Count => "Count", ErrorKind::TakeUntil => "Take until", ErrorKind::LengthValue => "Length followed by value", ErrorKind::TagClosure => "Tag closure", ErrorKind::Alpha => "Alphabetic", ErrorKind::Digit => "Digit", ErrorKind::AlphaNumeric => "AlphaNumeric", ErrorKind::Space => "Space", ErrorKind::MultiSpace => "Multiple spaces", ErrorKind::LengthValueFn => "LengthValueFn", ErrorKind::Eof => "End of file", ErrorKind::Switch => "Switch", ErrorKind::TagBits => "Tag on bitstream", ErrorKind::OneOf => "OneOf", ErrorKind::NoneOf => "NoneOf", ErrorKind::Char => "Char", ErrorKind::CrLf => "CrLf", ErrorKind::RegexpMatch => "RegexpMatch", ErrorKind::RegexpMatches => "RegexpMatches", ErrorKind::RegexpFind => "RegexpFind", ErrorKind::RegexpCapture => "RegexpCapture", ErrorKind::RegexpCaptures => "RegexpCaptures", ErrorKind::TakeWhile1 => "TakeWhile1", ErrorKind::Complete => "Complete", ErrorKind::Fix => "Fix", ErrorKind::Escaped => "Escaped", ErrorKind::EscapedTransform => "EscapedTransform", ErrorKind::NonEmpty => "NonEmpty", ErrorKind::ManyMN => "Many(m, n)", ErrorKind::HexDigit => "Hexadecimal Digit", ErrorKind::OctDigit => "Octal digit", ErrorKind::Not => "Negation", ErrorKind::Permutation => "Permutation", ErrorKind::ManyTill => "ManyTill", ErrorKind::Verify => "predicate verification", ErrorKind::TakeTill1 => "TakeTill1", ErrorKind::TakeWhileMN => "TakeWhileMN", ErrorKind::ParseTo => "Parse string to the specified type", ErrorKind::TooLarge => "Needed data size is too large", ErrorKind::Many0Count => "Count occurrence of >=0 patterns", ErrorKind::Many1Count => "Count occurrence of >=1 patterns", ErrorKind::Float => "Float", } } } /// 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) }); ); /* #[cfg(feature = "std")] use $crate::lib::std::any::Any; #[cfg(feature = "std")] use $crate::lib::std::{error,fmt}; #[cfg(feature = "std")] impl error::Error for Err { fn description(&self) -> &str { self.description() } } #[cfg(feature = "std")] impl fmt::Display for Err { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) } } */ //FIXME: error rewrite /// translate parser result from IResult to IResult with a custom type /// /// ``` /// # //FIXME /// # #[macro_use] extern crate nom; /// # use nom::IResult; /// # use std::convert::From; /// # use nom::Err; /// # use nom::error::ErrorKind; /// # fn main() { /// # /* /// # // will add a Custom(42) error to the error chain /// # named!(err_test, add_return_error!(ErrorKind::Custom(42u32), tag!("abcd"))); /// # /// # #[derive(Debug,Clone,PartialEq)] /// # pub struct ErrorStr(String); /// # /// # // Convert to IResult<&[u8], &[u8], ErrorStr> /// # impl From for ErrorStr { /// # fn from(i: u32) -> Self { /// # ErrorStr(format!("custom error code: {}", i)) /// # } /// # } /// # /// # named!(parser<&[u8], &[u8], ErrorStr>, /// # fix_error!(ErrorStr, err_test) /// # ); /// # /// # let a = &b"efghblah"[..]; /// # assert_eq!(parser(a), Err(Err::Error(Context::Code(a, ErrorKind::Custom(ErrorStr("custom error code: 42".to_string())))))); /// # */ /// # } /// ``` #[macro_export(local_inner_macros)] macro_rules! fix_error ( ($i:expr, $t:ty, $submac:ident!( $($args:tt)* )) => ( { use $crate::lib::std::result::Result::*; use $crate::Err; match $submac!($i, $($args)*) { Ok((i,o)) => Ok((i,o)), Err(e) => { let e2 = match e { Err::Error(err) => { Err::Error(err.into()) }, Err::Failure(err) => { Err::Failure(err.into()) }, Err::Incomplete(e) => Err::Incomplete(e), }; Err(e2) } } } ); ($i:expr, $t:ty, $f:expr) => ( fix_error!($i, $t, call!($f)); ); ); /// `flat_map!(R -> IResult, S -> IResult) => R -> IResult` /// /// combines a parser R -> IResult and /// a parser S -> IResult to return another /// parser R -> IResult /// /// ```rust /// # #[macro_use] extern crate nom; /// # use nom::{Err, 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)))); /// ``` #[macro_export(local_inner_macros)] macro_rules! flat_map( ($i:expr, $submac:ident!( $($args:tt)* ), $submac2:ident!( $($args2:tt)* )) => ( flat_map!(__impl $i, $submac!($($args)*), $submac2!($($args2)*)); ); ($i:expr, $submac:ident!( $($args:tt)* ), $g:expr) => ( flat_map!(__impl $i, $submac!($($args)*), call!($g)); ); ($i:expr, $f:expr, $submac:ident!( $($args:tt)* )) => ( flat_map!(__impl $i, call!($f), $submac!($($args)*)); ); ($i:expr, $f:expr, $g:expr) => ( flat_map!(__impl $i, call!($f), call!($g)); ); (__impl $i:expr, $submac:ident!( $($args:tt)* ), $submac2:ident!( $($args2:tt)* )) => ( $crate::combinator::map_parserc($i, move |i| {$submac!(i, $($args)*)}, move |i| {$submac2!(i, $($args2)*)}) ); ); #[cfg(test)] #[cfg(feature = "alloc")] mod tests { use super::*; use crate::character::complete::char; #[test] fn convert_error_panic() { let input = ""; let result: IResult<_, _, VerboseError<&str>> = char('x')(input); } } /* #[cfg(feature = "alloc")] use lib::std::{vec::Vec, collections::HashMap}; #[cfg(feature = "std")] use lib::std::hash::Hash; #[cfg(feature = "std")] pub fn add_error_pattern<'a, I: Clone + Hash + Eq, O, E: Clone + Hash + Eq>( h: &mut HashMap, &'a str>, e: VerboseError, message: &'a str, ) -> bool { h.insert(e, message); true } pub fn slice_to_offsets(input: &[u8], s: &[u8]) -> (usize, usize) { let start = input.as_ptr(); let off1 = s.as_ptr() as usize - start as usize; let off2 = off1 + s.len(); (off1, off2) } #[cfg(feature = "std")] pub fn prepare_errors(input: &[u8], e: VerboseError<&[u8]>) -> Option> { let mut v: Vec<(ErrorKind, usize, usize)> = Vec::new(); for (p, kind) in e.errors.drain(..) { let (o1, o2) = slice_to_offsets(input, p); v.push((kind, o1, o2)); } v.reverse(); Some(v) } #[cfg(feature = "std")] pub fn print_error(input: &[u8], res: VerboseError<&[u8]>) { if let Some(v) = prepare_errors(input, res) { let colors = generate_colors(&v); println!("parser codes: {}", print_codes(&colors, &HashMap::new())); println!("{}", print_offsets(input, 0, &v)); } else { println!("not an error"); } } #[cfg(feature = "std")] pub fn generate_colors(v: &[(ErrorKind, usize, usize)]) -> HashMap { let mut h: HashMap = HashMap::new(); let mut color = 0; for &(ref c, _, _) in v.iter() { h.insert(error_to_u32(c), color + 31); color = color + 1 % 7; } h } pub fn code_from_offset(v: &[(ErrorKind, usize, usize)], offset: usize) -> Option { let mut acc: Option<(u32, usize, usize)> = None; for &(ref ek, s, e) in v.iter() { let c = error_to_u32(ek); if s <= offset && offset <= e { if let Some((_, start, end)) = acc { if start <= s && e <= end { acc = Some((c, s, e)); } } else { acc = Some((c, s, e)); } } } if let Some((code, _, _)) = acc { return Some(code); } else { return None; } } #[cfg(feature = "alloc")] pub fn reset_color(v: &mut Vec) { v.push(0x1B); v.push(b'['); v.push(0); v.push(b'm'); } #[cfg(feature = "alloc")] pub fn write_color(v: &mut Vec, color: u8) { v.push(0x1B); v.push(b'['); v.push(1); v.push(b';'); let s = color.to_string(); let bytes = s.as_bytes(); v.extend(bytes.iter().cloned()); v.push(b'm'); } #[cfg(feature = "std")] #[cfg_attr(feature = "cargo-clippy", allow(implicit_hasher))] pub fn print_codes(colors: &HashMap, names: &HashMap) -> String { let mut v = Vec::new(); for (code, &color) in colors { if let Some(&s) = names.get(code) { let bytes = s.as_bytes(); write_color(&mut v, color); v.extend(bytes.iter().cloned()); } else { let s = code.to_string(); let bytes = s.as_bytes(); write_color(&mut v, color); v.extend(bytes.iter().cloned()); } reset_color(&mut v); v.push(b' '); } reset_color(&mut v); String::from_utf8_lossy(&v[..]).into_owned() } #[cfg(feature = "std")] pub fn print_offsets(input: &[u8], from: usize, offsets: &[(ErrorKind, usize, usize)]) -> String { let mut v = Vec::with_capacity(input.len() * 3); let mut i = from; let chunk_size = 8; let mut current_code: Option = None; let mut current_code2: Option = None; let colors = generate_colors(&offsets); for chunk in input.chunks(chunk_size) { let s = format!("{:08x}", i); for &ch in s.as_bytes().iter() { v.push(ch); } v.push(b'\t'); let mut k = i; let mut l = i; for &byte in chunk { if let Some(code) = code_from_offset(&offsets, k) { if let Some(current) = current_code { if current != code { reset_color(&mut v); current_code = Some(code); if let Some(&color) = colors.get(&code) { write_color(&mut v, color); } } } else { current_code = Some(code); if let Some(&color) = colors.get(&code) { write_color(&mut v, color); } } } v.push(CHARS[(byte >> 4) as usize]); v.push(CHARS[(byte & 0xf) as usize]); v.push(b' '); k = k + 1; } reset_color(&mut v); if chunk_size > chunk.len() { for _ in 0..(chunk_size - chunk.len()) { v.push(b' '); v.push(b' '); v.push(b' '); } } v.push(b'\t'); for &byte in chunk { if let Some(code) = code_from_offset(&offsets, l) { if let Some(current) = current_code2 { if current != code { reset_color(&mut v); current_code2 = Some(code); if let Some(&color) = colors.get(&code) { write_color(&mut v, color); } } } else { current_code2 = Some(code); if let Some(&color) = colors.get(&code) { write_color(&mut v, color); } } } if (byte >= 32 && byte <= 126) || byte >= 128 { v.push(byte); } else { v.push(b'.'); } l = l + 1; } reset_color(&mut v); v.push(b'\n'); i = i + chunk_size; } String::from_utf8_lossy(&v[..]).into_owned() } */