Parse deltas

This commit is contained in:
Joscha 2021-11-19 19:14:14 +01:00
parent d298ffabb2
commit 8c83c0b0b9
3 changed files with 159 additions and 17 deletions

View file

@ -34,14 +34,7 @@ pub enum DeltaStep {
}
#[derive(Debug)]
pub struct Delta {
pub years: i32,
pub months: i32,
pub weeks: i32,
pub days: i32,
pub hours: i32,
pub minutes: i32,
}
pub struct Delta(pub Vec<DeltaStep>);
#[derive(Debug)]
pub struct DateEndSpec {

View file

@ -7,7 +7,8 @@ use pest::iterators::Pair;
use pest::{Parser, Span};
use crate::commands::{
Birthday, BirthdaySpec, Command, DateSpec, Done, FormulaSpec, Note, Spec, Task, WeekdaySpec,
Birthday, BirthdaySpec, Command, DateSpec, Delta, DeltaStep, Done, FormulaSpec, Note, Spec,
Task, Weekday, WeekdaySpec,
};
#[derive(pest_derive::Parser)]
@ -16,13 +17,17 @@ struct TodayfileParser;
type Result<T> = result::Result<T, Error<Rule>>;
fn fail<S: Into<String>, T>(span: Span, message: S) -> Result<T> {
Err(Error::new_from_span(
fn error<S: Into<String>>(span: Span, message: S) -> Error<Rule> {
Error::new_from_span(
ErrorVariant::CustomError {
message: message.into(),
},
span,
))
)
}
fn fail<S: Into<String>, T>(span: Span, message: S) -> Result<T> {
Err(error(span, message))
}
fn parse_title(p: Pair<Rule>) -> Result<String> {
@ -65,6 +70,138 @@ fn parse_time(p: Pair<Rule>) -> Result<NaiveTime> {
}
}
#[derive(Clone, Copy)]
pub enum Sign {
Positive,
Negative,
}
pub struct Amount {
sign: Option<Sign>,
value: i32,
}
impl Amount {
pub fn with_prev_sign(mut self, prev: Option<Sign>) -> Self {
if self.sign.is_none() {
self.sign = prev;
}
self
}
pub fn value(&self) -> Option<i32> {
match self.sign {
None => None,
Some(Sign::Positive) => Some(self.value),
Some(Sign::Negative) => Some(-self.value),
}
}
}
fn parse_amount(p: Pair<Rule>) -> Result<Amount> {
assert_eq!(p.as_rule(), Rule::amount);
let mut sign = None;
let mut value = 0;
for p in p.into_inner() {
match p.as_rule() {
Rule::amount_sign => {
sign = Some(match p.as_str() {
"+" => Sign::Positive,
"-" => Sign::Negative,
_ => unreachable!(),
})
}
Rule::amount_value => value = p.as_str().parse().unwrap(),
_ => unreachable!(),
}
}
Ok(Amount { sign, value })
}
fn parse_weekday(p: Pair<Rule>) -> Result<Weekday> {
assert_eq!(p.as_rule(), Rule::weekday);
Ok(match p.as_str() {
"mon" => Weekday::Monday,
"tue" => Weekday::Tuesday,
"wed" => Weekday::Wednesday,
"thu" => Weekday::Thursday,
"fri" => Weekday::Friday,
"sat" => Weekday::Saturday,
"sun" => Weekday::Sunday,
_ => unreachable!(),
})
}
fn parse_delta_weekdays(p: Pair<Rule>, sign: &mut Option<Sign>) -> Result<DeltaStep> {
assert_eq!(p.as_rule(), Rule::delta_weekdays);
let span = p.as_span();
let mut p = p.into_inner();
let amount = parse_amount(p.next().unwrap())?;
let weekday = parse_weekday(p.next().unwrap())?;
assert_eq!(p.next(), None);
let value = amount
.value()
.ok_or_else(|| error(span, "ambiguous sign"))?;
*sign = amount.sign;
Ok(DeltaStep::Weekday(value, weekday))
}
fn parse_delta_step(
p: Pair<Rule>,
sign: &mut Option<Sign>,
f: impl FnOnce(i32) -> DeltaStep,
) -> Result<DeltaStep> {
assert!(matches!(
p.as_rule(),
Rule::delta_years
| Rule::delta_months
| Rule::delta_months_reverse
| Rule::delta_days
| Rule::delta_weeks
| Rule::delta_hours
| Rule::delta_minutes
));
let span = p.as_span();
let amount = parse_amount(p.into_inner().next().unwrap())?.with_prev_sign(*sign);
let value = amount
.value()
.ok_or_else(|| error(span, "ambiguous sign"))?;
*sign = amount.sign;
Ok(f(value))
}
fn parse_delta(p: Pair<Rule>) -> Result<Delta> {
assert_eq!(p.as_rule(), Rule::delta);
let mut sign = None;
let mut steps = vec![];
for p in p.into_inner() {
match p.as_rule() {
Rule::delta_weekdays => steps.push(parse_delta_weekdays(p, &mut sign)?),
Rule::delta_minutes => steps.push(parse_delta_step(p, &mut sign, DeltaStep::Minute)?),
Rule::delta_years => steps.push(parse_delta_step(p, &mut sign, DeltaStep::Year)?),
Rule::delta_months => steps.push(parse_delta_step(p, &mut sign, DeltaStep::Minute)?),
Rule::delta_months_reverse => {
steps.push(parse_delta_step(p, &mut sign, DeltaStep::Minute)?)
}
Rule::delta_days => steps.push(parse_delta_step(p, &mut sign, DeltaStep::Minute)?),
Rule::delta_weeks => steps.push(parse_delta_step(p, &mut sign, DeltaStep::Minute)?),
Rule::delta_hours => steps.push(parse_delta_step(p, &mut sign, DeltaStep::Minute)?),
_ => unreachable!(),
}
}
Ok(Delta(steps))
}
fn parse_date_fixed(p: Pair<Rule>) -> Result<DateSpec> {
assert_eq!(p.as_rule(), Rule::date_fixed);
dbg!(p);

View file

@ -18,19 +18,31 @@ time = ${ hour ~ ":" ~ minute }
weekday = { "mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun" }
amount_sign = { ("+" | "-")? }
amount_value = { ASCII_DIGIT* }
amount_value = { ASCII_DIGIT{0,9} } // Fits in an i32
amount = { amount_sign ~ amount_value }
delta_weekdays = { amount ~ weekday }
delta_minutes = { amount ~ "min" }
delta_years = { amount ~ ("y" | "Y") }
delta_months = { amount ~ ("m" | "M") }
delta_years = { amount ~ "y" }
delta_months = { amount ~ "m" }
delta_months_reverse = { amount ~ "M" }
delta_days = { amount ~ "d" }
delta_weeks = { amount ~ "w" }
delta_hours = { amount ~ "h" }
delta = { (delta_weekdays | delta_minutes | delta_years | delta_months | delta_days | delta_weeks | delta_hours)+ }
delta = {
(
delta_weekdays
| delta_minutes
| delta_years
| delta_months
| delta_months_reverse
| delta_days
| delta_weeks
| delta_hours
)+
}
paren_expr = { "(" ~ expr ~ ")" }
number = @{ ASCII_DIGIT+ }
number = @{ ASCII_DIGIT{1,9} } // Fits in an i32
boolean = { "true" | "false" }
variable = {
"j"