aboutsummaryrefslogtreecommitdiff
path: root/src/fexpr/grammar.lalrpop
diff options
context:
space:
mode:
Diffstat (limited to 'src/fexpr/grammar.lalrpop')
-rw-r--r--src/fexpr/grammar.lalrpop131
1 files changed, 131 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,
+
+ _
+}