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 --- Cargo.toml | 5 ++ build.rs | 5 ++ src/fexpr/grammar.lalrpop | 131 ++++++++++++++++++++++++++++++++++++++++++++++ src/fexpr/mod.rs | 25 +++++++++ 4 files changed, 166 insertions(+) create mode 100644 build.rs create mode 100644 src/fexpr/grammar.lalrpop create mode 100644 src/fexpr/mod.rs diff --git a/Cargo.toml b/Cargo.toml index db1e9c5..2d64d51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,3 +24,8 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" dirs = "2.0" log = { version = "0.4", features = ["std"] } +thiserror = "1.0" +lalrpop-util = "0.18" + +[build-dependencies] +lalrpop = { version = "0.18", features = ["lexer"] } diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..23c7d3f --- /dev/null +++ b/build.rs @@ -0,0 +1,5 @@ +extern crate lalrpop; + +fn main() { + lalrpop::process_root().unwrap(); +} 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, + + _ +} 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), +} -- cgit v1.2.3