From d4a24eef7fd410c147de201d776089e0601317d5 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 12 Jun 2020 00:48:18 +0200 Subject: initial work on comparison based filters This enables filters such as -time > 2020-01-01 -time < 2020-02-03 ... for time and duration, and later possibly also for more things (such as a COUNT(...) construct). This work tries to integrate them into the existing filter system as seamless as possible, by providing a Comparator which implements LogFilter. The "type checking" is done at parse time, so nonsensical comparisons like -time > 12s flat out give a parse error. This however might be changed to a more dynamic system with run-time type checking, in which case we could do away with the type parameter on Producer and simply work with a generic Value. The comparator would then return an error if two non-identical types would be compared. Note that the system does not support arithmetic expressions, only simple comparisons to constant values. --- src/fexpr/grammar.lalrpop | 37 +++++++++++++++++++++++++++++++++++-- src/fexpr/mod.rs | 3 +++ 2 files changed, 38 insertions(+), 2 deletions(-) (limited to 'src/fexpr') diff --git a/src/fexpr/grammar.lalrpop b/src/fexpr/grammar.lalrpop index 45f4fde..c2df097 100644 --- a/src/fexpr/grammar.lalrpop +++ b/src/fexpr/grammar.lalrpop @@ -4,12 +4,15 @@ use super::{ FightOutcome, filters, PlayerClass, + + DateProducer, + DurationProducer, }; use evtclib::Boss; use std::collections::HashSet; use lalrpop_util::ParseError; -use chrono::{DateTime, Local, TimeZone, Utc, Weekday}; +use chrono::{DateTime, Local, TimeZone, Utc, Weekday, Duration}; use regex::{Regex, RegexBuilder}; grammar; @@ -68,6 +71,9 @@ LogPredicate: Box = { "any" "(" "player" ":" ")" => filters::player::any(<>), "exists" "(" "player" ":" ")" => filters::player::any(<>), + >, + >, + "(" ")", } @@ -175,6 +181,22 @@ Date: DateTime = { .map(|d| d.with_timezone(&Utc)), } +Duration: Duration = { + duration => Duration::seconds(<>[..<>.len() - 1].parse().unwrap()), +} + +CompOp: filters::values::CompOp = { + "<" => filters::values::CompOp::Less, + "<=" => filters::values::CompOp::LessEqual, + "=" => filters::values::CompOp::Equal, + ">=" => filters::values::CompOp::GreaterEqual, + ">" => filters::values::CompOp::Greater, +} + +Comparison: Box = { + => filters::values::comparison(lhs, op, rhs), +} + Comma: HashSet = { ",")*> => { let mut result = v.into_iter().collect::>(); @@ -183,6 +205,16 @@ Comma: HashSet = { }, } +DateProducer: Box = { + => filters::values::constant(<>), + "-time" => filters::values::time(), +} + +DurationProducer: Box = { + => filters::values::constant(<>), + "-duration" => filters::values::duration(), +} + match { "player" => "player", "not" => "not", @@ -194,7 +226,8 @@ 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"\d+s" => duration, + r"[[:alpha:]][\w]*" => word, r#""[^"]*""# => string, _ diff --git a/src/fexpr/mod.rs b/src/fexpr/mod.rs index c6a3a39..1738e44 100644 --- a/src/fexpr/mod.rs +++ b/src/fexpr/mod.rs @@ -11,6 +11,9 @@ use itertools::Itertools; use lalrpop_util::{lalrpop_mod, lexer::Token, ParseError}; use thiserror::Error; +trait DateProducer = filters::values::Producer>; +trait DurationProducer = filters::values::Producer; + lalrpop_mod!(#[allow(clippy::all)] pub grammar, "/fexpr/grammar.rs"); #[derive(Debug)] -- cgit v1.2.3 From 600683df83cea34f10341d7fe2a328559c578379 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 12 Jun 2020 13:03:44 +0200 Subject: allow durations with minutes as well This allows things like "2m 50s" but also "5m". humantime would allow even more, but we need a regular expression to catch them for lalrpop, so for now this are the only two supported formats (together with "50s"). Considering the usual time of fights in GW2, I doubt we'll need anything bigger than minutes. --- src/fexpr/grammar.lalrpop | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/fexpr') diff --git a/src/fexpr/grammar.lalrpop b/src/fexpr/grammar.lalrpop index c2df097..9584536 100644 --- a/src/fexpr/grammar.lalrpop +++ b/src/fexpr/grammar.lalrpop @@ -182,7 +182,7 @@ Date: DateTime = { } Duration: Duration = { - duration => Duration::seconds(<>[..<>.len() - 1].parse().unwrap()), + duration => Duration::from_std(humantime::parse_duration(<>).unwrap()).unwrap(), } CompOp: filters::values::CompOp = { @@ -226,7 +226,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"\d+s" => duration, + r"((\d+m ?)?\d+s)|(\d+m)" => duration, r"[[:alpha:]][\w]*" => word, r#""[^"]*""# => string, -- cgit v1.2.3 From e23af286b81f4c9df0e0ca9d71113caeb909cb0f Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 12 Jun 2020 13:21:35 +0200 Subject: implement count(player: ...) construct --- src/fexpr/grammar.lalrpop | 9 +++++++++ src/fexpr/mod.rs | 1 + 2 files changed, 10 insertions(+) (limited to 'src/fexpr') diff --git a/src/fexpr/grammar.lalrpop b/src/fexpr/grammar.lalrpop index 9584536..9eef21a 100644 --- a/src/fexpr/grammar.lalrpop +++ b/src/fexpr/grammar.lalrpop @@ -7,6 +7,7 @@ use super::{ DateProducer, DurationProducer, + CountProducer, }; use evtclib::Boss; use std::collections::HashSet; @@ -73,6 +74,7 @@ LogPredicate: Box = { >, >, + >, "(" ")", } @@ -215,6 +217,11 @@ DurationProducer: Box = { "-duration" => filters::values::duration(), } +CountProducer: Box = { + => filters::values::constant(<>.parse().unwrap()), + "count" "(" "player" ":" ")" => filters::values::player_count(<>), +} + match { "player" => "player", "not" => "not", @@ -223,10 +230,12 @@ match { "any" => "any", "all" => "all", "exists" => "exists", + "count" => "count", 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"((\d+m ?)?\d+s)|(\d+m)" => duration, + r"\d+" => integer, r"[[:alpha:]][\w]*" => word, r#""[^"]*""# => string, diff --git a/src/fexpr/mod.rs b/src/fexpr/mod.rs index 1738e44..f765acf 100644 --- a/src/fexpr/mod.rs +++ b/src/fexpr/mod.rs @@ -13,6 +13,7 @@ use thiserror::Error; trait DateProducer = filters::values::Producer>; trait DurationProducer = filters::values::Producer; +trait CountProducer = filters::values::Producer; lalrpop_mod!(#[allow(clippy::all)] pub grammar, "/fexpr/grammar.rs"); -- cgit v1.2.3 From a8481afdac21b8b846d5fe159678972a879b11c0 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 12 Jun 2020 13:44:19 +0200 Subject: add a comment about the trait aliases --- src/fexpr/mod.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/fexpr') diff --git a/src/fexpr/mod.rs b/src/fexpr/mod.rs index f765acf..5d12051 100644 --- a/src/fexpr/mod.rs +++ b/src/fexpr/mod.rs @@ -11,6 +11,8 @@ use itertools::Itertools; use lalrpop_util::{lalrpop_mod, lexer::Token, ParseError}; use thiserror::Error; +// Lalrpop chokes on the associated type specification (it doesn't expect the =), so we need to +// define those aliases here in Rust and then import and use them in the grammar. trait DateProducer = filters::values::Producer>; trait DurationProducer = filters::values::Producer; trait CountProducer = filters::values::Producer; -- cgit v1.2.3 From 6e52b5f2ac6154eca35355b320b7fb8bbc8f23ee Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 12 Jun 2020 15:36:19 +0200 Subject: add a count(player) to count all players --- src/fexpr/grammar.lalrpop | 1 + 1 file changed, 1 insertion(+) (limited to 'src/fexpr') diff --git a/src/fexpr/grammar.lalrpop b/src/fexpr/grammar.lalrpop index 9eef21a..700481c 100644 --- a/src/fexpr/grammar.lalrpop +++ b/src/fexpr/grammar.lalrpop @@ -220,6 +220,7 @@ DurationProducer: Box = { CountProducer: Box = { => filters::values::constant(<>.parse().unwrap()), "count" "(" "player" ":" ")" => filters::values::player_count(<>), + "count" "(" "player" ")" => filters::values::player_count(filters::constant(true)), } match { -- cgit v1.2.3 From 4a3e7137334601828f56a3ee614f01d84bada4ce Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 12 Jun 2020 15:55:19 +0200 Subject: implement -after/-before in terms of -time It makes sense to unify this implementation to avoid code duplication and bugs that might be hidden. -after and -before can stay for now, as shortcuts for -time < and -time >, the same way we have other shortcuts as well. --- src/fexpr/grammar.lalrpop | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/fexpr') diff --git a/src/fexpr/grammar.lalrpop b/src/fexpr/grammar.lalrpop index 700481c..092407e 100644 --- a/src/fexpr/grammar.lalrpop +++ b/src/fexpr/grammar.lalrpop @@ -52,8 +52,12 @@ LogPredicate: Box = { "-outcome" > => filters::log::outcome(<>), "-weekday" > => filters::log::weekday(<>), - "-before" => filters::log::before(<>), - "-after" => filters::log::after(<>), + "-before" => filters::values::comparison( + filters::values::time(), filters::values::CompOp::Less, filters::values::constant(<>) + ), + "-after" => filters::values::comparison( + filters::values::time(), filters::values::CompOp::Greater, filters::values::constant(<>) + ), "-log-before" => filters::log::log_before(<>), "-log-after" => filters::log::log_after(<>), -- cgit v1.2.3