Parse all DATE variants
This commit is contained in:
parent
99259784cf
commit
7ed4ba2372
3 changed files with 254 additions and 34 deletions
|
|
@ -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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
218
src/parse.rs
218
src/parse.rs
|
|
@ -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> {
|
||||||
|
|
|
||||||
|
|
@ -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 }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue