From e19519e155af95698807f377a5f6b525e255c4e5 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 18 Apr 2020 15:12:21 +0200 Subject: first version of the new filter pipeline --- src/fexpr/grammar.lalrpop | 131 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 src/fexpr/grammar.lalrpop (limited to 'src/fexpr/grammar.lalrpop') 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 = { + Disjunction, +} + +PlayerFilter: Box = { + Disjunction, +} + +Disjunction: T = { + > "or" > => a | b, + Conjunction, +} + +Conjunction: T = { + > "and"? > => a & b, + Negation, +} + +Negation: T = { + "not" => ! <>, + "!" => ! <>, + T, +} + +LogPredicate: Box = { + "-success" => filters::log::OutcomeFilter::success(), + "-wipe" => filters::log::OutcomeFilter::wipe(), + "-outcome" > => filters::log::OutcomeFilter::new(<>), + + "-weekday" > => filters::log::WeekdayFilter::new(<>), + "-before" => filters::log::TimeFilter::new(None, Some(<>)), + "-after" => filters::log::TimeFilter::new(Some(<>), None), + + "-boss" > => filters::log::BossFilter::new(<>), + + "all" "(" "player" ":" ")" => filters::player::all(<>), + "any" "(" "player" ":" ")" => filters::player::any(<>), + "exists" "(" "player" ":" ")" => filters::player::any(<>), + + "(" ")", +} + +PlayerPredicate: Box = { + "-character" => filters::player::NameFilter::new(SearchField::Character, <>), + "-account" => filters::player::NameFilter::new(SearchField::Account, <>), + "-name" => + filters::player::NameFilter::new(SearchField::Account, <>.clone()) + | filters::player::NameFilter::new(SearchField::Character, <>), + + "(" ")", +} + +Regex: Regex = { + =>? Regex::new(&s[1..s.len() - 1]).map_err(|_| ParseError::User { + error: FError::InvalidRegex(s.into()), + }), + =>? Regex::new(s).map_err(|e| ParseError::User { + error: FError::InvalidRegex(s.into()), + }), +} + +FightOutcome: FightOutcome = { + =>? <>.parse().map_err(|_| ParseError::User { + error: FError::InvalidFightOutcome(<>.into()), + }), +} + +Weekday: Weekday = { + =>? <>.parse().map_err(|_| ParseError::User { + error: FError::InvalidWeekday(<>.into()), + }), +} + +Boss: Boss = { + =>? <>.parse().map_err(|_| ParseError::User { + error: FError::InvalidBoss(<>.into()), + }), +} + +Date: NaiveDateTime = { + =>? NaiveDateTime::parse_from_str(<>, "%Y-%m-%d %H:%M:%S") + .map_err(|_| ParseError::User { + error: FError::InvalidTimestamp(<>.into()), + }), + =>? NaiveDateTime::parse_from_str(&format!("{} 00:00:00", <>), "%Y-%m-%d %H:%M:%S") + .map_err(|_| ParseError::User { + error: FError::InvalidTimestamp(<>.into()), + }), +} + +Comma: HashSet = { + ",")*> => { + let mut result = v.into_iter().collect::>(); + 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, + + _ +} -- cgit v1.2.3 From 569c17607297dbbd57462a9603861d9fe619fd2d Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 20 Apr 2020 13:47:00 +0200 Subject: Add -player as a shortcut to search player names --- src/fexpr/grammar.lalrpop | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/fexpr/grammar.lalrpop') diff --git a/src/fexpr/grammar.lalrpop b/src/fexpr/grammar.lalrpop index cb16153..48349a1 100644 --- a/src/fexpr/grammar.lalrpop +++ b/src/fexpr/grammar.lalrpop @@ -53,6 +53,11 @@ LogPredicate: Box = { "-boss" > => filters::log::BossFilter::new(<>), + "-player" => filters::player::any( + filters::player::NameFilter::new(SearchField::Character, <>.clone()) + | filters::player::NameFilter::new(SearchField::Account, <>) + ), + "all" "(" "player" ":" ")" => filters::player::all(<>), "any" "(" "player" ":" ")" => filters::player::any(<>), "exists" "(" "player" ":" ")" => filters::player::any(<>), -- cgit v1.2.3 From 185a5b2f802f9d05c3eb40f807c0488f168c6661 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Apr 2020 13:59:28 +0200 Subject: better error outputs --- src/fexpr/grammar.lalrpop | 62 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 16 deletions(-) (limited to 'src/fexpr/grammar.lalrpop') 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 = { } Regex: Regex = { - =>? Regex::new(&s[1..s.len() - 1]).map_err(|_| ParseError::User { - error: FError::InvalidRegex(s.into()), + =>? Regex::new(&s[1..s.len() - 1]).map_err(|error| ParseError::User { + error: FError { + location: l, + data: s.to_string(), + kind: error.into(), + } }), - =>? Regex::new(s).map_err(|e| ParseError::User { - error: FError::InvalidRegex(s.into()), + =>? Regex::new(s).map_err(|error| ParseError::User { + error: FError { + location: l, + data: s.to_string(), + kind: error.into(), + } }), } FightOutcome: FightOutcome = { - =>? <>.parse().map_err(|_| ParseError::User { - error: FError::InvalidFightOutcome(<>.into()), + =>? w.parse().map_err(|_| ParseError::User { + error: FError { + location: l, + data: w.into(), + kind: FErrorKind::InvalidFightOutcome, + } }), } Weekday: Weekday = { - =>? <>.parse().map_err(|_| ParseError::User { - error: FError::InvalidWeekday(<>.into()), + =>? w.parse().map_err(|_| ParseError::User { + error: FError { + location: l, + data: w.into(), + kind: FErrorKind::InvalidWeekday, + } }), } Boss: Boss = { - =>? <>.parse().map_err(|_| ParseError::User { - error: FError::InvalidBoss(<>.into()), + =>? w.parse().map_err(|_| ParseError::User { + error: FError { + location: l, + data: w.into(), + kind: FErrorKind::InvalidBoss, + } }), } Date: NaiveDateTime = { - =>? NaiveDateTime::parse_from_str(<>, "%Y-%m-%d %H:%M:%S") - .map_err(|_| ParseError::User { - error: FError::InvalidTimestamp(<>.into()), + =>? 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(), + } }), - =>? NaiveDateTime::parse_from_str(&format!("{} 00:00:00", <>), "%Y-%m-%d %H:%M:%S") - .map_err(|_| ParseError::User { - error: FError::InvalidTimestamp(<>.into()), + =>? 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, _ } -- cgit v1.2.3 From d1f277892ec127b1fb83ad56de59b29c32695661 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Apr 2020 14:03:13 +0200 Subject: add "or" and "and" to the list of tokens Otherwise they'd get tokenized as word and we couldn't build conjunctions/disjunctions. --- src/fexpr/grammar.lalrpop | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/fexpr/grammar.lalrpop') diff --git a/src/fexpr/grammar.lalrpop b/src/fexpr/grammar.lalrpop index 4e6ac89..f559ff1 100644 --- a/src/fexpr/grammar.lalrpop +++ b/src/fexpr/grammar.lalrpop @@ -153,6 +153,8 @@ Comma: HashSet = { match { "player" => "player", "not" => "not", + "or" => "or", + "and" => "and", "any" => "any", "all" => "all", "exists" => "exists", -- cgit v1.2.3 From 0ad7a333dc2b45f0ba658ea455284d086294a088 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Apr 2020 14:06:15 +0200 Subject: grammar: fix precendence rules If we don't allow the higher-tier on the left side, we cannot chain multiple or/and on the same level. Since or is associative, we shouldn't expect the user to write (... or (... or ...)) and instead provide the flattened version as well. --- src/fexpr/grammar.lalrpop | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/fexpr/grammar.lalrpop') diff --git a/src/fexpr/grammar.lalrpop b/src/fexpr/grammar.lalrpop index f559ff1..caaaf7f 100644 --- a/src/fexpr/grammar.lalrpop +++ b/src/fexpr/grammar.lalrpop @@ -28,18 +28,18 @@ PlayerFilter: Box = { } Disjunction: T = { - > "or" > => a | b, + > "or" > => a | b, Conjunction, } Conjunction: T = { - > "and"? > => a & b, + > "and"? > => a & b, Negation, } Negation: T = { - "not" => ! <>, - "!" => ! <>, + "not" > => ! <>, + "!" > => ! <>, T, } -- cgit v1.2.3 From 5dbea93266c3a30dac5ec6f5a7915d73a440f573 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 25 Apr 2020 13:14:30 +0200 Subject: use free functions instead of Filter::new Having a ::new on each of the filter types was a bit weird, especially because we returned Box instead of Self (and clippy rightfully complained). With this patch, we now have a bunch of normal functions, and we don't show to the outside how a filter is actually implemented (or what struct is behind it). --- src/fexpr/grammar.lalrpop | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'src/fexpr/grammar.lalrpop') diff --git a/src/fexpr/grammar.lalrpop b/src/fexpr/grammar.lalrpop index caaaf7f..d8f64fa 100644 --- a/src/fexpr/grammar.lalrpop +++ b/src/fexpr/grammar.lalrpop @@ -44,19 +44,19 @@ Negation: T = { } LogPredicate: Box = { - "-success" => filters::log::OutcomeFilter::success(), - "-wipe" => filters::log::OutcomeFilter::wipe(), - "-outcome" > => filters::log::OutcomeFilter::new(<>), + "-success" => filters::log::success(), + "-wipe" => filters::log::wipe(), + "-outcome" > => filters::log::outcome(<>), - "-weekday" > => filters::log::WeekdayFilter::new(<>), - "-before" => filters::log::TimeFilter::new(None, Some(<>)), - "-after" => filters::log::TimeFilter::new(Some(<>), None), + "-weekday" > => filters::log::weekday(<>), + "-before" => filters::log::before(<>), + "-after" => filters::log::after(<>), - "-boss" > => filters::log::BossFilter::new(<>), + "-boss" > => filters::log::boss(<>), "-player" => filters::player::any( - filters::player::NameFilter::new(SearchField::Character, <>.clone()) - | filters::player::NameFilter::new(SearchField::Account, <>) + filters::player::character(<>.clone()) + | filters::player::account(<>) ), "all" "(" "player" ":" ")" => filters::player::all(<>), @@ -67,11 +67,11 @@ LogPredicate: Box = { } PlayerPredicate: Box = { - "-character" => filters::player::NameFilter::new(SearchField::Character, <>), - "-account" => filters::player::NameFilter::new(SearchField::Account, <>), + "-character" => filters::player::character(<>), + "-account" => filters::player::account(<>), "-name" => - filters::player::NameFilter::new(SearchField::Account, <>.clone()) - | filters::player::NameFilter::new(SearchField::Character, <>), + filters::player::account(<>.clone()) + | filters::player::character(<>), "(" ")", } -- cgit v1.2.3 From 9bbd5db2a6caae10f0ab2cf2625fbc34485a4ce9 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 25 Apr 2020 13:22:00 +0200 Subject: add -include and -exclude --- src/fexpr/grammar.lalrpop | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/fexpr/grammar.lalrpop') diff --git a/src/fexpr/grammar.lalrpop b/src/fexpr/grammar.lalrpop index d8f64fa..58ec052 100644 --- a/src/fexpr/grammar.lalrpop +++ b/src/fexpr/grammar.lalrpop @@ -54,6 +54,9 @@ LogPredicate: Box = { "-boss" > => filters::log::boss(<>), + "-include" => filters::constant(true), + "-exclude" => filters::constant(false), + "-player" => filters::player::any( filters::player::character(<>.clone()) | filters::player::account(<>) -- cgit v1.2.3