aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaniel <kingdread@gmx.de>2020-04-18 15:12:21 +0200
committerDaniel <kingdread@gmx.de>2020-04-18 15:12:21 +0200
commite19519e155af95698807f377a5f6b525e255c4e5 (patch)
treefdd80ce3675f6b0dea70239d6c4a14affff96a13 /src
parent7030224fd2a97b3551fdd47c43249e3a42341238 (diff)
downloadraidgrep-e19519e155af95698807f377a5f6b525e255c4e5.tar.gz
raidgrep-e19519e155af95698807f377a5f6b525e255c4e5.tar.bz2
raidgrep-e19519e155af95698807f377a5f6b525e255c4e5.zip
first version of the new filter pipeline
Diffstat (limited to 'src')
-rw-r--r--src/fexpr/grammar.lalrpop131
-rw-r--r--src/fexpr/mod.rs25
2 files changed, 156 insertions, 0 deletions
diff --git a/src/fexpr/grammar.lalrpop b/src/fexpr/grammar.lalrpop
new file mode 100644
index 0000000..cb16153
--- /dev/null
+++ b/src/fexpr/grammar.lalrpop
@@ -0,0 +1,131 @@
+use super::{
+ FError,
+ FightOutcome,
+ filters,
+ SearchField,
+ Weekday,
+};
+use evtclib::statistics::gamedata::Boss;
+use std::collections::HashSet;
+use lalrpop_util::ParseError;
+
+use chrono::NaiveDateTime;
+use regex::Regex;
+
+grammar;
+
+extern {
+ type Error = FError;
+}
+
+pub LogFilter: Box<dyn filters::log::LogFilter> = {
+ Disjunction<LogPredicate>,
+}
+
+PlayerFilter: Box<dyn filters::player::PlayerFilter> = {
+ Disjunction<PlayerPredicate>,
+}
+
+Disjunction<T>: T = {
+ <a:Conjunction<T>> "or" <b:Conjunction<T>> => a | b,
+ Conjunction<T>,
+}
+
+Conjunction<T>: T = {
+ <a:Negation<T>> "and"? <b:Negation<T>> => a & b,
+ Negation<T>,
+}
+
+Negation<T>: T = {
+ "not" <T> => ! <>,
+ "!" <T> => ! <>,
+ T,
+}
+
+LogPredicate: Box<dyn filters::log::LogFilter> = {
+ "-success" => filters::log::OutcomeFilter::success(),
+ "-wipe" => filters::log::OutcomeFilter::wipe(),
+ "-outcome" <Comma<FightOutcome>> => filters::log::OutcomeFilter::new(<>),
+
+ "-weekday" <Comma<Weekday>> => filters::log::WeekdayFilter::new(<>),
+ "-before" <Date> => filters::log::TimeFilter::new(None, Some(<>)),
+ "-after" <Date> => filters::log::TimeFilter::new(Some(<>), None),
+
+ "-boss" <Comma<Boss>> => filters::log::BossFilter::new(<>),
+
+ "all" "(" "player" ":" <PlayerFilter> ")" => filters::player::all(<>),
+ "any" "(" "player" ":" <PlayerFilter> ")" => filters::player::any(<>),
+ "exists" "(" "player" ":" <PlayerFilter> ")" => filters::player::any(<>),
+
+ "(" <LogFilter> ")",
+}
+
+PlayerPredicate: Box<dyn filters::player::PlayerFilter> = {
+ "-character" <Regex> => filters::player::NameFilter::new(SearchField::Character, <>),
+ "-account" <Regex> => filters::player::NameFilter::new(SearchField::Account, <>),
+ "-name" <Regex> =>
+ filters::player::NameFilter::new(SearchField::Account, <>.clone())
+ | filters::player::NameFilter::new(SearchField::Character, <>),
+
+ "(" <PlayerFilter> ")",
+}
+
+Regex: Regex = {
+ <s:r#""[^"]*""#> =>? Regex::new(&s[1..s.len() - 1]).map_err(|_| ParseError::User {
+ error: FError::InvalidRegex(s.into()),
+ }),
+ <s:word> =>? Regex::new(s).map_err(|e| ParseError::User {
+ error: FError::InvalidRegex(s.into()),
+ }),
+}
+
+FightOutcome: FightOutcome = {
+ <word> =>? <>.parse().map_err(|_| ParseError::User {
+ error: FError::InvalidFightOutcome(<>.into()),
+ }),
+}
+
+Weekday: Weekday = {
+ <word> =>? <>.parse().map_err(|_| ParseError::User {
+ error: FError::InvalidWeekday(<>.into()),
+ }),
+}
+
+Boss: Boss = {
+ <word> =>? <>.parse().map_err(|_| ParseError::User {
+ error: FError::InvalidBoss(<>.into()),
+ }),
+}
+
+Date: NaiveDateTime = {
+ <datetime> =>? NaiveDateTime::parse_from_str(<>, "%Y-%m-%d %H:%M:%S")
+ .map_err(|_| ParseError::User {
+ error: FError::InvalidTimestamp(<>.into()),
+ }),
+ <date> =>? NaiveDateTime::parse_from_str(&format!("{} 00:00:00", <>), "%Y-%m-%d %H:%M:%S")
+ .map_err(|_| ParseError::User {
+ error: FError::InvalidTimestamp(<>.into()),
+ }),
+}
+
+Comma<T>: HashSet<T> = {
+ <v:(<T> ",")*> <e:T> => {
+ let mut result = v.into_iter().collect::<HashSet<_>>();
+ result.insert(e);
+ result
+ },
+}
+
+match {
+ "player" => "player",
+ "not" => "not",
+ "any" => "any",
+ "all" => "all",
+ "exists" => "exists",
+
+ 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,
+
+ _
+}
diff --git a/src/fexpr/mod.rs b/src/fexpr/mod.rs
new file mode 100644
index 0000000..f2b1090
--- /dev/null
+++ b/src/fexpr/mod.rs
@@ -0,0 +1,25 @@
+//! Filter expression language.
+//!
+//! This module contains methods to parse a given string into an abstract filter tree, check its
+//! 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;
+
+use thiserror::Error;
+
+lalrpop_mod!(pub grammar, "/fexpr/grammar.rs");
+
+#[derive(Debug, Error)]
+pub enum FError {
+ #[error("invalid regular expression: {0}")]
+ InvalidRegex(String),
+ #[error("invalid fight outcome: {0}")]
+ InvalidFightOutcome(String),
+ #[error("invalid weekday: {0}")]
+ InvalidWeekday(String),
+ #[error("invalid timestamp: {0}")]
+ InvalidTimestamp(String),
+ #[error("invalid boss name: {0}")]
+ InvalidBoss(String),
+}