From d6ba84ea99721a919cedc0e6a44ecbc992e4a983 Mon Sep 17 00:00:00 2001 From: Tamo Date: Fri, 22 Oct 2021 15:09:56 +0200 Subject: [PATCH] re introduce the special error type to be able to add context to the errors --- filter_parser/src/condition.rs | 7 ++-- filter_parser/src/lib.rs | 59 ++++++++++++---------------------- filter_parser/src/value.rs | 5 ++- 3 files changed, 25 insertions(+), 46 deletions(-) diff --git a/filter_parser/src/condition.rs b/filter_parser/src/condition.rs index 75ee8c6f7..b8d0e1efc 100644 --- a/filter_parser/src/condition.rs +++ b/filter_parser/src/condition.rs @@ -12,12 +12,11 @@ use nom::branch::alt; use nom::bytes::complete::tag; -use nom::error::ParseError; use nom::sequence::tuple; use nom::IResult; use Condition::*; -use crate::{parse_value, ws, FilterCondition, Span, Token}; +use crate::{parse_value, ws, FPError, FilterCondition, Span, Token}; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Condition<'a> { @@ -47,7 +46,7 @@ impl<'a> Condition<'a> { } /// condition = value ("==" | ">" ...) value -pub fn parse_condition<'a, E: ParseError>>( +pub fn parse_condition<'a, E: FPError<'a>>( input: Span<'a>, ) -> IResult, FilterCondition, E> { let operator = alt((tag("<="), tag(">="), tag("!="), tag("<"), tag(">"), tag("="))); @@ -80,7 +79,7 @@ pub fn parse_condition<'a, E: ParseError>>( } /// to = value value TO value -pub fn parse_to<'a, E: ParseError>>(input: Span<'a>) -> IResult { +pub fn parse_to<'a, E: FPError<'a>>(input: Span<'a>) -> IResult { let (input, (key, from, _, to)) = tuple((ws(|c| parse_value(c)), ws(|c| parse_value(c)), tag("TO"), ws(|c| parse_value(c))))( input, diff --git a/filter_parser/src/lib.rs b/filter_parser/src/lib.rs index bb826872f..007817655 100644 --- a/filter_parser/src/lib.rs +++ b/filter_parser/src/lib.rs @@ -24,16 +24,22 @@ use nom::branch::alt; use nom::bytes::complete::tag; use nom::character::complete::{char, multispace0}; use nom::combinator::map; -use nom::error::{ContextError, ParseError}; +use nom::error::{ContextError, Error, VerboseError}; use nom::multi::{many0, separated_list1}; use nom::number::complete::recognize_float; use nom::sequence::{delimited, preceded, tuple}; -use nom::IResult; +use nom::{Finish, IResult}; use nom_locate::LocatedSpan; pub(crate) use value::parse_value; pub type Span<'a> = LocatedSpan<&'a str>; +pub trait FilterParserError<'a>: nom::error::ParseError> + ContextError> {} +impl<'a> FilterParserError<'a> for VerboseError> {} +impl<'a> FilterParserError<'a> for Error> {} + +use FilterParserError as FPError; + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Token<'a> { pub position: Span<'a>, @@ -82,22 +88,21 @@ impl<'a> FilterCondition<'a> { } } - pub fn parse>>(input: &'a str) -> Result { + pub fn parse>(input: &'a str) -> Result { let span = Span::new(input); - // handle error - Ok(parse_expression::<'a, E>(span).map(|(_rem, output)| output).ok().unwrap()) + parse_expression::<'a, E>(span).finish().map(|(_rem, output)| output) } } // remove OPTIONAL whitespaces before AND after the the provided parser -fn ws<'a, O, E: ParseError>>( +fn ws<'a, O, E: FPError<'a>>( inner: impl FnMut(Span<'a>) -> IResult, ) -> impl FnMut(Span<'a>) -> IResult { delimited(multispace0, inner, multispace0) } /// and = not (~ "AND" not)* -fn parse_or<'a, E: ParseError>>(input: Span<'a>) -> IResult { +fn parse_or<'a, E: FPError<'a>>(input: Span<'a>) -> IResult { let (input, lhs) = parse_and(input)?; let (input, ors) = many0(preceded(ws(tag("OR")), |c| parse_and(c)))(input)?; @@ -107,7 +112,7 @@ fn parse_or<'a, E: ParseError>>(input: Span<'a>) -> IResult>>(input: Span<'a>) -> IResult { +fn parse_and<'a, E: FPError<'a>>(input: Span<'a>) -> IResult { let (input, lhs) = parse_not(input)?; let (input, ors) = many0(preceded(ws(tag("AND")), |c| parse_not(c)))(input)?; let expr = ors @@ -117,25 +122,15 @@ fn parse_and<'a, E: ParseError>>(input: Span<'a>) -> IResult>>(input: Span<'a>) -> IResult { +fn parse_not<'a, E: FPError<'a>>(input: Span<'a>) -> IResult { alt((map(preceded(alt((tag("!"), tag("NOT"))), |c| parse_not(c)), |e| e.negate()), |c| { parse_primary(c) }))(input) } /// geoRadius = WS* ~ "_geoRadius(float ~ "," ~ float ~ "," float) -fn parse_geo_radius<'a, E: ParseError>>( - input: Span<'a>, -) -> IResult, FilterCondition, E> { - // let err_msg_args_incomplete = "_geoRadius. The `_geoRadius` filter expect three arguments: `_geoRadius(latitude, longitude, radius)`"; - /* - TODO - let err_msg_latitude_invalid = - "_geoRadius. Latitude must be contained between -90 and 90 degrees."; - - let err_msg_longitude_invalid = - "_geoRadius. Longitude must be contained between -180 and 180 degrees."; - */ +fn parse_geo_radius<'a, E: FPError<'a>>(input: Span<'a>) -> IResult, FilterCondition, E> { + let err_msg_args_incomplete = "_geoRadius. The `_geoRadius` filter expect three arguments: `_geoRadius(latitude, longitude, radius)`"; // we want to forbid space BEFORE the _geoRadius but not after let parsed = preceded::<_, _, _, _, _, _>( @@ -146,16 +141,8 @@ fn parse_geo_radius<'a, E: ParseError>>( let (input, args): (Span, Vec) = parsed?; if args.len() != 3 { - // TODO - panic!("todo"); - /* - let e = nom::error::Error::from_char(input, '('); - return Err(nom::Err::Failure(nom::error::Error::add_context( - input, - err_msg_args_incomplete, - e, - ))); - */ + let e = E::from_char(input, '('); + return Err(nom::Err::Failure(E::add_context(input, err_msg_args_incomplete, e))); } let res = FilterCondition::GeoLowerThan { @@ -166,9 +153,7 @@ fn parse_geo_radius<'a, E: ParseError>>( } /// primary = (WS* ~ "(" expression ")" ~ WS*) | condition | to | geoRadius -fn parse_primary<'a, E: ParseError>>( - input: Span<'a>, -) -> IResult { +fn parse_primary<'a, E: FPError<'a>>(input: Span<'a>) -> IResult { alt(( delimited(ws(char('(')), |c| parse_expression(c), ws(char(')'))), |c| parse_condition(c), @@ -178,16 +163,12 @@ fn parse_primary<'a, E: ParseError>>( } /// expression = or -pub fn parse_expression<'a, E: ParseError>>( - input: Span<'a>, -) -> IResult { +pub fn parse_expression<'a, E: FPError<'a>>(input: Span<'a>) -> IResult { parse_or(input) } #[cfg(test)] pub mod tests { - use nom::error::Error; - use super::*; /// Create a raw [Token]. You must specify the string that appear BEFORE your element followed by your element diff --git a/filter_parser/src/value.rs b/filter_parser/src/value.rs index 1497aaddd..5b3a8dfd1 100644 --- a/filter_parser/src/value.rs +++ b/filter_parser/src/value.rs @@ -1,14 +1,13 @@ use nom::branch::alt; use nom::bytes::complete::{take_till, take_while1}; use nom::character::complete::char; -use nom::error::ParseError; use nom::sequence::delimited; use nom::IResult; -use crate::{ws, Span, Token}; +use crate::{ws, FPError, Span, Token}; /// value = WS* ~ ( word | singleQuoted | doubleQuoted) ~ WS* -pub fn parse_value<'a, E: ParseError>>(input: Span<'a>) -> IResult, Token, E> { +pub fn parse_value<'a, E: FPError<'a>>(input: Span<'a>) -> IResult, Token, E> { // singleQuoted = "'" .* all but quotes "'" let simple_quoted_key = |input| take_till(|c: char| c == '\'')(input); // doubleQuoted = "\"" (word | spaces)* "\""