diff options
Diffstat (limited to 'src/fexpr/grammar.lalrpop')
-rw-r--r-- | src/fexpr/grammar.lalrpop | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/src/fexpr/grammar.lalrpop b/src/fexpr/grammar.lalrpop new file mode 100644 index 0000000..58ec052 --- /dev/null +++ b/src/fexpr/grammar.lalrpop @@ -0,0 +1,171 @@ +use super::{ + FError, + FErrorKind, + 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:Disjunction<T>> "or" <b:Conjunction<T>> => a | b, + Conjunction<T>, +} + +Conjunction<T>: T = { + <a:Conjunction<T>> "and"? <b:Negation<T>> => a & b, + Negation<T>, +} + +Negation<T>: T = { + "not" <Negation<T>> => ! <>, + "!" <Negation<T>> => ! <>, + T, +} + +LogPredicate: Box<dyn filters::log::LogFilter> = { + "-success" => filters::log::success(), + "-wipe" => filters::log::wipe(), + "-outcome" <Comma<FightOutcome>> => filters::log::outcome(<>), + + "-weekday" <Comma<Weekday>> => filters::log::weekday(<>), + "-before" <Date> => filters::log::before(<>), + "-after" <Date> => filters::log::after(<>), + + "-boss" <Comma<Boss>> => filters::log::boss(<>), + + "-include" => filters::constant(true), + "-exclude" => filters::constant(false), + + "-player" <Regex> => filters::player::any( + filters::player::character(<>.clone()) + | filters::player::account(<>) + ), + + "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::character(<>), + "-account" <Regex> => filters::player::account(<>), + "-name" <Regex> => + filters::player::account(<>.clone()) + | filters::player::character(<>), + + "(" <PlayerFilter> ")", +} + +Regex: Regex = { + <l:@L> <s:regex> =>? Regex::new(&s[1..s.len() - 1]).map_err(|error| ParseError::User { + error: FError { + location: l, + data: s.to_string(), + kind: error.into(), + } + }), + <l:@L> <s:word> =>? Regex::new(s).map_err(|error| ParseError::User { + error: FError { + location: l, + data: s.to_string(), + kind: error.into(), + } + }), +} + +FightOutcome: FightOutcome = { + <l:@L> <w:word> =>? w.parse().map_err(|_| ParseError::User { + error: FError { + location: l, + data: w.into(), + kind: FErrorKind::InvalidFightOutcome, + } + }), +} + +Weekday: Weekday = { + <l:@L> <w:word> =>? w.parse().map_err(|_| ParseError::User { + error: FError { + location: l, + data: w.into(), + kind: FErrorKind::InvalidWeekday, + } + }), +} + +Boss: Boss = { + <l:@L> <w:word> =>? w.parse().map_err(|_| ParseError::User { + error: FError { + location: l, + data: w.into(), + kind: FErrorKind::InvalidBoss, + } + }), +} + +Date: NaiveDateTime = { + <l:@L> <d:datetime> =>? 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(), + } + }), + <l:@L> <d:date> =>? 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(), + } + }), +} + +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", + "or" => "or", + "and" => "and", + "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, + r#""[^"]*""# => regex, + + _ +} |