diff options
Diffstat (limited to 'src/fexpr')
| -rw-r--r-- | src/fexpr/grammar.lalrpop | 62 | ||||
| -rw-r--r-- | src/fexpr/mod.rs | 57 | 
2 files changed, 92 insertions, 27 deletions
diff --git a/src/fexpr/grammar.lalrpop b/src/fexpr/grammar.lalrpop index 48349a1..4e6ac89 100644 --- a/src/fexpr/grammar.lalrpop +++ b/src/fexpr/grammar.lalrpop @@ -1,5 +1,6 @@  use super::{      FError, +    FErrorKind,      FightOutcome,      filters,      SearchField, @@ -76,40 +77,68 @@ PlayerPredicate: Box<dyn filters::player::PlayerFilter> = {  }  Regex: Regex = { -    <s:r#""[^"]*""#> =>? Regex::new(&s[1..s.len() - 1]).map_err(|_| ParseError::User { -        error: FError::InvalidRegex(s.into()), +    <l:@L> <s:regex> =>? Regex::new(&s[1..s.len() - 1]).map_err(|error| ParseError::User { +        error: FError { +            location: l, +            data: s.to_string(), +            kind: error.into(), +        }      }), -    <s:word> =>? Regex::new(s).map_err(|e| ParseError::User { -        error: FError::InvalidRegex(s.into()), +    <l:@L> <s:word> =>? Regex::new(s).map_err(|error| ParseError::User { +        error: FError { +            location: l, +            data: s.to_string(), +            kind: error.into(), +        }      }),  }  FightOutcome: FightOutcome = { -    <word> =>? <>.parse().map_err(|_| ParseError::User { -        error: FError::InvalidFightOutcome(<>.into()), +    <l:@L> <w:word> =>? w.parse().map_err(|_| ParseError::User { +        error: FError { +            location: l, +            data: w.into(), +            kind: FErrorKind::InvalidFightOutcome, +        }      }),  }  Weekday: Weekday = { -    <word> =>? <>.parse().map_err(|_| ParseError::User { -        error: FError::InvalidWeekday(<>.into()), +    <l:@L> <w:word> =>? w.parse().map_err(|_| ParseError::User { +        error: FError { +            location: l, +            data: w.into(), +            kind: FErrorKind::InvalidWeekday, +        }      }),  }  Boss: Boss = { -    <word> =>? <>.parse().map_err(|_| ParseError::User { -        error: FError::InvalidBoss(<>.into()), +    <l:@L> <w:word> =>? w.parse().map_err(|_| ParseError::User { +        error: FError { +            location: l, +            data: w.into(), +            kind: FErrorKind::InvalidBoss, +        }      }),  }  Date: NaiveDateTime = { -    <datetime> =>? NaiveDateTime::parse_from_str(<>, "%Y-%m-%d %H:%M:%S") -        .map_err(|_| ParseError::User { -            error: FError::InvalidTimestamp(<>.into()), +    <l:@L> <d:datetime> =>? NaiveDateTime::parse_from_str(d, "%Y-%m-%d %H:%M:%S") +        .map_err(|error| ParseError::User { +            error: FError { +                location: l, +                data: d.into(), +                kind: error.into(), +            }          }), -    <date> =>? NaiveDateTime::parse_from_str(&format!("{} 00:00:00", <>), "%Y-%m-%d %H:%M:%S") -        .map_err(|_| ParseError::User { -            error: FError::InvalidTimestamp(<>.into()), +    <l:@L> <d:date> =>? NaiveDateTime::parse_from_str(&format!("{} 00:00:00", d), "%Y-%m-%d %H:%M:%S") +        .map_err(|error| ParseError::User { +            error: FError { +                location: l, +                data: d.into(), +                kind: error.into(), +            }          }),  } @@ -131,6 +160,7 @@ match {      r"\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d" => datetime,      r"\d\d\d\d-\d\d-\d\d" => date,      r"\w+" => word, +    r#""[^"]*""# => regex,      _  } diff --git a/src/fexpr/mod.rs b/src/fexpr/mod.rs index aafdea7..5754d94 100644 --- a/src/fexpr/mod.rs +++ b/src/fexpr/mod.rs @@ -4,28 +4,63 @@  //! type and convert it to a [`Filter`][super::filters::Filter].  // Make it available in the grammar mod.  use super::{filters, FightOutcome, SearchField, Weekday}; -use lalrpop_util::{lalrpop_mod, lexer::Token, ParseError}; +use std::{error, fmt}; + +use lalrpop_util::{lalrpop_mod, lexer::Token, ParseError};  use thiserror::Error; -lalrpop_mod!(pub grammar, "/fexpr/grammar.rs"); +lalrpop_mod!(#[allow(clippy::all)] pub grammar, "/fexpr/grammar.rs"); + +#[derive(Debug)] +pub struct FError { +    location: usize, +    data: String, +    kind: FErrorKind, +} + +impl fmt::Display for FError { +    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +        write!(f, "{} (at {})", self.kind, self.location) +    } +} + +impl error::Error for FError { +    fn source(&self) -> Option<&(dyn error::Error + 'static)> { +        Some(&self.kind) +    } +}  #[derive(Debug, Error)] -pub enum FError { +pub enum FErrorKind {      #[error("invalid regular expression: {0}")] -    InvalidRegex(String), -    #[error("invalid fight outcome: {0}")] -    InvalidFightOutcome(String), -    #[error("invalid weekday: {0}")] -    InvalidWeekday(String), +    InvalidRegex(#[from] regex::Error), +    #[error("invalid fight outcome")] +    InvalidFightOutcome, +    #[error("invalid weekday")] +    InvalidWeekday,      #[error("invalid timestamp: {0}")] -    InvalidTimestamp(String), -    #[error("invalid boss name: {0}")] -    InvalidBoss(String), +    InvalidTimestamp(#[from] chrono::format::ParseError), +    #[error("invalid boss name")] +    InvalidBoss,  } +/// Shortcut to create a new parser and parse the given input.  pub fn parse_logfilter(      input: &str,  ) -> Result<Box<dyn filters::log::LogFilter>, ParseError<usize, Token, FError>> {      grammar::LogFilterParser::new().parse(input)  } + +/// Extract the location from the given error. +pub fn location<T>(err: &ParseError<usize, T, FError>) -> usize { +    match *err { +        ParseError::InvalidToken { location } => location, +        ParseError::UnrecognizedEOF { location, .. } => location, +        ParseError::UnrecognizedToken { +            token: (l, _, _), .. +        } => l, +        ParseError::ExtraToken { token: (l, _, _) } => l, +        ParseError::User { ref error } => error.location, +    } +}  | 
