Parse all DATE variants

This commit is contained in:
Joscha 2021-11-19 21:23:16 +01:00
parent 99259784cf
commit 7ed4ba2372
3 changed files with 254 additions and 34 deletions

View file

@ -87,7 +87,7 @@ pub enum Var {
YearLength, YearLength,
/// `yd`, day of the year /// `yd`, day of the year
YearDay, YearDay,
/// `Yd`, day of the year starting from the end /// `yD`, day of the year starting from the end
/// ///
/// Equal to `yl - yd + 1` /// Equal to `yl - yd + 1`
YearDayReverse, YearDayReverse,
@ -95,7 +95,7 @@ pub enum Var {
/// ///
/// Equal to `((yd - 1) / 7) + 1` /// Equal to `((yd - 1) / 7) + 1`
YearWeek, YearWeek,
/// `Yw`, 1 during the last 7 days of the year, 2 during the previous etc. /// `yW`, 1 during the last 7 days of the year, 2 during the previous etc.
/// ///
/// Equal to `((yD - 1) / 7) + 1` /// Equal to `((yD - 1) / 7) + 1`
YearWeekReverse, YearWeekReverse,
@ -103,20 +103,20 @@ pub enum Var {
Month, Month,
/// `ml`, length of the current month in days /// `ml`, length of the current month in days
MonthLength, MonthLength,
/// `d` or `md`, day of the month
MonthDay,
/// `D` or `Md`, day of the month starting from the end
///
/// Equal to `ml - md + 1`
MonthDayReverse,
/// `mw`, 1 during the first 7 days of the month, 2 during the next etc. /// `mw`, 1 during the first 7 days of the month, 2 during the next etc.
/// ///
/// Equal to `((md - 1) / 7) + 1` /// Equal to `((md - 1) / 7) + 1`
MonthWeek, MonthWeek,
/// `Mw`, 1 during the last 7 days of the month, 2 during the previous etc. /// `mW`, 1 during the last 7 days of the month, 2 during the previous etc.
/// ///
/// Equal to `((mD - 1) / 7) + 1` /// Equal to `((mD - 1) / 7) + 1`
MonthWeekReverse, MonthWeekReverse,
/// `d`, day of the month
Day,
/// `D`, day of the month starting from the end
///
/// Equal to `ml - md + 1`
DayReverse,
/// `iy`, ISO 8601 year /// `iy`, ISO 8601 year
IsoYear, IsoYear,
/// `iyl`, length of current ISO 8601 year **in weeks** /// `iyl`, length of current ISO 8601 year **in weeks**
@ -180,7 +180,7 @@ pub struct FormulaSpec {
pub start: Option<Expr>, // None: * pub start: Option<Expr>, // None: *
pub start_delta: Option<Delta>, pub start_delta: Option<Delta>,
pub start_time: Option<Time>, pub start_time: Option<Time>,
pub end: Option<Delta>, pub end_delta: Option<Delta>,
pub end_time: Option<Time>, pub end_time: Option<Time>,
} }

View file

@ -4,12 +4,12 @@ use std::result;
use chrono::NaiveDate; use chrono::NaiveDate;
use pest::error::{Error, ErrorVariant}; use pest::error::{Error, ErrorVariant};
use pest::iterators::Pair; use pest::iterators::Pair;
use pest::prec_climber::PrecClimber; use pest::prec_climber::{Assoc, Operator, PrecClimber};
use pest::{Parser, Span}; use pest::{Parser, Span};
use crate::commands::{ use crate::commands::{
Birthday, BirthdaySpec, Command, DateSpec, Delta, DeltaStep, Done, Expr, FormulaSpec, Note, Birthday, BirthdaySpec, Command, DateSpec, Delta, DeltaStep, Done, Expr, FormulaSpec, Note,
Spec, Task, Time, Weekday, WeekdaySpec, Spec, Task, Time, Var, Weekday, WeekdaySpec,
}; };
#[derive(pest_derive::Parser)] #[derive(pest_derive::Parser)]
@ -275,30 +275,230 @@ fn parse_number(p: Pair<Rule>) -> i32 {
p.as_str().parse().unwrap() p.as_str().parse().unwrap()
} }
fn parse_boolean(p: Pair<Rule>) -> bool {
assert_eq!(p.as_rule(), Rule::boolean);
match p.as_str() {
"true" => true,
"false" => false,
_ => unreachable!(),
}
}
fn parse_variable(p: Pair<Rule>) -> Var {
assert_eq!(p.as_rule(), Rule::variable);
match p.as_str() {
"j" => Var::JulianDay,
"y" => Var::Year,
"yl" => Var::YearLength,
"yd" => Var::YearDay,
"yD" => Var::YearDayReverse,
"yw" => Var::YearWeek,
"yW" => Var::YearWeekReverse,
"m" => Var::Month,
"ml" => Var::MonthLength,
"mw" => Var::MonthWeek,
"mW" => Var::MonthWeekReverse,
"d" => Var::Day,
"D" => Var::DayReverse,
"iy" => Var::IsoYear,
"iyl" => Var::IsoYearLength,
"iw" => Var::IsoWeek,
"wd" => Var::Weekday,
"e" => Var::Easter,
"mon" => Var::Monday,
"tue" => Var::Tuesday,
"wed" => Var::Wednesday,
"thu" => Var::Thursday,
"fri" => Var::Friday,
"sat" => Var::Saturday,
"sun" => Var::Sunday,
"isWeekday" => Var::IsWeekday,
"isWeekend" => Var::IsWeekend,
"isLeapYear" => Var::IsLeapYear,
_ => unreachable!(),
}
}
fn parse_unop_expr(p: Pair<Rule>) -> Expr {
assert_eq!(p.as_rule(), Rule::unop_expr);
let mut p = p.into_inner();
let p_op = p.next().unwrap();
let p_expr = p.next().unwrap();
assert_eq!(p.next(), None);
let expr = parse_expr(p_expr);
match p_op.as_rule() {
Rule::unop_neg => Expr::Neg(Box::new(expr)),
Rule::unop_not => Expr::Not(Box::new(expr)),
_ => unreachable!(),
}
}
fn parse_paren_expr(p: Pair<Rule>) -> Expr {
assert_eq!(p.as_rule(), Rule::paren_expr);
let inner = parse_expr(p.into_inner().next().unwrap());
Expr::Paren(Box::new(inner))
}
fn parse_term(p: Pair<Rule>) -> Expr { fn parse_term(p: Pair<Rule>) -> Expr {
todo!() assert_eq!(p.as_rule(), Rule::term);
let p = p.into_inner().next().unwrap();
match p.as_rule() {
Rule::number => Expr::Lit(parse_number(p).into()),
Rule::boolean => Expr::Lit(if parse_boolean(p) { 1 } else { 0 }),
Rule::variable => Expr::Var(parse_variable(p)),
Rule::unop_expr => parse_unop_expr(p),
Rule::paren_expr => parse_paren_expr(p),
_ => unreachable!(),
}
} }
fn parse_op(l: Expr, p: Pair<Rule>, r: Expr) -> Expr { fn parse_op(l: Expr, p: Pair<Rule>, r: Expr) -> Expr {
todo!() match p.as_rule() {
// Integer-y operations
Rule::op_add => Expr::Add(Box::new(l), Box::new(r)),
Rule::op_sub => Expr::Sub(Box::new(l), Box::new(r)),
Rule::op_mul => Expr::Mul(Box::new(l), Box::new(r)),
Rule::op_div => Expr::Div(Box::new(l), Box::new(r)),
Rule::op_mod => Expr::Mod(Box::new(l), Box::new(r)),
// Comparisons
Rule::op_eq => Expr::Eq(Box::new(l), Box::new(r)),
Rule::op_neq => Expr::Neq(Box::new(l), Box::new(r)),
Rule::op_lt => Expr::Lt(Box::new(l), Box::new(r)),
Rule::op_lte => Expr::Lte(Box::new(l), Box::new(r)),
Rule::op_gt => Expr::Gt(Box::new(l), Box::new(r)),
Rule::op_gte => Expr::Gte(Box::new(l), Box::new(r)),
// Boolean-y operations
Rule::op_and => Expr::And(Box::new(l), Box::new(r)),
Rule::op_or => Expr::Or(Box::new(l), Box::new(r)),
Rule::op_xor => Expr::Xor(Box::new(l), Box::new(r)),
_ => unreachable!(),
}
} }
fn parse_expr(p: Pair<Rule>) -> Expr { fn parse_expr(p: Pair<Rule>) -> Expr {
assert_eq!(p.as_rule(), Rule::expr); assert_eq!(p.as_rule(), Rule::expr);
let climber = PrecClimber::new(vec![todo!()]);
fn op(rule: Rule) -> Operator<Rule> {
Operator::new(rule, Assoc::Left)
}
let climber = PrecClimber::new(vec![
// Precedence from low to high
op(Rule::op_or) | op(Rule::op_xor),
op(Rule::op_and),
op(Rule::op_eq) | op(Rule::op_neq),
op(Rule::op_lt) | op(Rule::op_lte) | op(Rule::op_gt) | op(Rule::op_gte),
op(Rule::op_mul) | op(Rule::op_div) | op(Rule::op_mod),
op(Rule::op_add) | op(Rule::op_sub),
]);
climber.climb(p.into_inner(), parse_term, parse_op) climber.climb(p.into_inner(), parse_term, parse_op)
} }
fn parse_date_expr_start(p: Pair<Rule>, spec: &mut FormulaSpec) -> Result<()> {
assert_eq!(p.as_rule(), Rule::date_expr_start);
for p in p.into_inner() {
match p.as_rule() {
Rule::paren_expr => spec.start = Some(parse_paren_expr(p)),
Rule::delta => spec.start_delta = Some(parse_delta(p)?),
Rule::time => spec.start_time = Some(parse_time(p)?),
_ => unreachable!(),
}
}
Ok(())
}
fn parse_date_expr_end(p: Pair<Rule>, spec: &mut FormulaSpec) -> Result<()> {
assert_eq!(p.as_rule(), Rule::date_expr_end);
for p in p.into_inner() {
match p.as_rule() {
Rule::delta => spec.end_delta = Some(parse_delta(p)?),
Rule::time => spec.end_time = Some(parse_time(p)?),
_ => unreachable!(),
}
}
Ok(())
}
fn parse_date_expr(p: Pair<Rule>) -> Result<FormulaSpec> { fn parse_date_expr(p: Pair<Rule>) -> Result<FormulaSpec> {
assert_eq!(p.as_rule(), Rule::date_expr); assert_eq!(p.as_rule(), Rule::date_expr);
dbg!(p);
todo!() let mut spec = FormulaSpec {
start: None,
start_delta: None,
start_time: None,
end_delta: None,
end_time: None,
};
for p in p.into_inner() {
match p.as_rule() {
Rule::date_expr_start => parse_date_expr_start(p, &mut spec)?,
Rule::date_expr_end => parse_date_expr_end(p, &mut spec)?,
_ => unreachable!(),
}
}
Ok(spec)
}
fn parse_date_weekday_start(p: Pair<Rule>, spec: &mut WeekdaySpec) -> Result<()> {
assert_eq!(p.as_rule(), Rule::date_weekday_start);
for p in p.into_inner() {
match p.as_rule() {
Rule::weekday => spec.start = parse_weekday(p),
Rule::time => spec.start_time = Some(parse_time(p)?),
_ => unreachable!(),
}
}
Ok(())
}
fn parse_date_weekday_end(p: Pair<Rule>, spec: &mut WeekdaySpec) -> Result<()> {
assert_eq!(p.as_rule(), Rule::date_weekday_end);
for p in p.into_inner() {
match p.as_rule() {
Rule::weekday => spec.end = Some(parse_weekday(p)),
Rule::delta => spec.end_delta = Some(parse_delta(p)?),
Rule::time => spec.end_time = Some(parse_time(p)?),
_ => unreachable!(),
}
}
Ok(())
} }
fn parse_date_weekday(p: Pair<Rule>) -> Result<WeekdaySpec> { fn parse_date_weekday(p: Pair<Rule>) -> Result<WeekdaySpec> {
assert_eq!(p.as_rule(), Rule::date_weekday); assert_eq!(p.as_rule(), Rule::date_weekday);
dbg!(p);
todo!() let mut spec = WeekdaySpec {
start: Weekday::Monday,
start_time: None,
end: None,
end_delta: None,
end_time: None,
};
for p in p.into_inner() {
match p.as_rule() {
Rule::date_weekday_start => parse_date_weekday_start(p, &mut spec)?,
Rule::date_weekday_end => parse_date_weekday_end(p, &mut spec)?,
_ => unreachable!(),
}
}
Ok(spec)
} }
fn parse_date(p: Pair<Rule>) -> Result<Spec> { fn parse_date(p: Pair<Rule>) -> Result<Spec> {

View file

@ -42,25 +42,46 @@ delta = {
} }
number = @{ ("+" | "-")? ~ ASCII_DIGIT{1,9} } // Fits into an i32 number = @{ ("+" | "-")? ~ ASCII_DIGIT{1,9} } // Fits into an i32
paren_expr = { "(" ~ expr ~ ")" }
boolean = { "true" | "false" } boolean = { "true" | "false" }
variable = { variable = {
"j" "mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun"
| "isWeekday" | "isWeekend" | "isLeapYear"
| "j"
| "yl" | "yd" | "yD" | "yw" | "yW" | "y" | "yl" | "yd" | "yD" | "yw" | "yW" | "y"
| "ml" | "md" | "mD" | "mw" | "mW" | "m" | "ml" | "mw" | "mW" | "m"
| "d" | "D" | "d" | "D"
| "iy" | "iyl" | "iyl" | "iy"
| "wd" | "wd"
| "e" | "e"
| "isWeekday" | "isWeekend" | "isLeapYear"
} }
term = { paren_expr | number | boolean | weekday | variable }
op = { unop_neg = { "-" }
"+" | "-" | "*" | "/" | "%" unop_not = { "!" }
| "=" | "!=" unop = _{ unop_neg | unop_not }
| "<=" | "<" | ">=" | ">"
| "&" | "|" | "^" op_add = { "+" }
op_sub = { "-" }
op_mul = { "*" }
op_div = { "/" }
op_mod = { "%" }
op_eq = { "=" }
op_neq = { "!=" }
op_lt = { "<" }
op_lte = { "<=" }
op_gt = { ">" }
op_gte = { ">=" }
op_and = { "&" }
op_or = { "|" }
op_xor = { "^" }
op = _{
op_add | op_sub | op_mul | op_div | op_mod
| op_eq | op_neq | op_lt | op_lte | op_gt | op_gte
| op_and | op_or | op_xor
} }
paren_expr = { "(" ~ expr ~ ")" }
unop_expr = { unop ~ expr }
term = { number | boolean | variable | paren_expr | unop_expr }
expr = { term ~ (op ~ term)* } expr = { term ~ (op ~ term)* }
date_fixed_start = { datum ~ delta? ~ time? } date_fixed_start = { datum ~ delta? ~ time? }
@ -68,13 +89,12 @@ date_fixed_end = { datum ~ delta? ~ time? | delta ~ time? | time }
date_fixed_repeat = { delta } date_fixed_repeat = { delta }
date_fixed = { date_fixed_start ~ ("--" ~ date_fixed_end)? ~ (";" ~ date_fixed_repeat)? } date_fixed = { date_fixed_start ~ ("--" ~ date_fixed_end)? ~ (";" ~ date_fixed_repeat)? }
wildcard_expr = { "*" } date_expr_start = { ("*" | paren_expr) ~ delta? ~ time? }
date_expr_start = { (wildcard_expr | paren_expr) ~ delta? ~ time? }
date_expr_end = { delta ~ time? | time } date_expr_end = { delta ~ time? | time }
date_expr = { date_expr_start ~ ("--" ~ date_expr_end)? } date_expr = { date_expr_start ~ ("--" ~ date_expr_end)? }
date_weekday_start = { weekday ~ delta? ~ time? } date_weekday_start = { weekday ~ time? }
date_weekday_end = { weekday ~ delta? ~ time? | delta ~ time? | time } date_weekday_end = { weekday ~ time? | delta ~ time? | time }
date_weekday = { date_weekday_start ~ ("--" ~ date_weekday_end)? } date_weekday = { date_weekday_start ~ ("--" ~ date_weekday_end)? }
date = !{ "DATE" ~ (date_fixed | date_expr | date_weekday) ~ eol } date = !{ "DATE" ~ (date_fixed | date_expr | date_weekday) ~ eol }