From 3a219ecac25ceeb5d8ace132486c4d987e9c3c32 Mon Sep 17 00:00:00 2001 From: Joscha Date: Sat, 4 Dec 2021 22:37:01 +0100 Subject: [PATCH] Evaluate some simple statements --- src/eval/command.rs | 132 +++++++++++++++++++++++++++++++++++++++--- src/eval/date.rs | 34 ++++++++++- src/eval/error.rs | 3 + src/files/commands.rs | 22 ++++++- src/files/format.rs | 2 +- src/files/parse.rs | 3 +- 6 files changed, 184 insertions(+), 12 deletions(-) diff --git a/src/eval/command.rs b/src/eval/command.rs index c34928c..dd970ab 100644 --- a/src/eval/command.rs +++ b/src/eval/command.rs @@ -2,9 +2,10 @@ use std::collections::HashMap; use chrono::NaiveDate; -use crate::files::SourcedCommand; +use crate::files::commands::{BirthdaySpec, Command, Done, Note, Span, Spec, Statement, Task}; +use crate::files::{Source, SourcedCommand}; -use super::{DateRange, Entry, Result}; +use super::{DateRange, Entry, EntryKind, Error, Result}; pub struct CommandState<'a> { command: SourcedCommand<'a>, @@ -12,7 +13,9 @@ pub struct CommandState<'a> { from: Option, until: Option, - entries: HashMap, + + dated: HashMap, + undated: Vec, } impl<'a> CommandState<'a> { @@ -22,15 +25,130 @@ impl<'a> CommandState<'a> { command, from: None, until: None, - entries: HashMap::new(), + dated: HashMap::new(), + undated: Vec::new(), } } - pub fn eval(self) -> Result { - todo!() + pub fn eval(mut self) -> Result { + match self.command.command { + Command::Task(task) => self.eval_task(task)?, + Command::Note(note) => self.eval_note(note)?, + } + Ok(self) } pub fn entries(self) -> Vec { - self.entries.into_values().collect() + self.dated + .into_values() + .chain(self.undated.into_iter()) + .collect() + } + + // Helper functions + + fn title(&self) -> String { + self.command.command.title().to_string() + } + + fn desc(&self) -> Vec { + self.command.command.desc().to_vec() + } + + fn source(&self) -> Source { + self.command.source + } + + /// Add an entry, respecting [`Self::from`] and [`Self::until`]. Does not + /// overwrite existing entries if a root date is specified. + fn add(&mut self, entry: Entry) { + if let Some(root) = entry.root { + if let Some(from) = self.from { + if root < from { + return; + } + } + if let Some(until) = self.until { + if until < root { + return; + } + } + self.dated.entry(root).or_insert(entry); + } else { + self.undated.push(entry); + } + } + + /// Add an entry, ignoring [`Self::from`] and [`Self::until`]. Always + /// overwrites existing entries if a root date is specified. + fn add_forced(&mut self, entry: Entry) { + if let Some(root) = entry.root { + self.dated.insert(root, entry); + } else { + self.undated.push(entry); + } + } + + // Actual evaluation + + fn eval_task(&mut self, task: &Task) -> Result<()> { + for statement in &task.statements { + self.eval_statement(statement)?; + } + for done in &task.done { + self.eval_done(done); + } + Ok(()) + } + + fn eval_note(&mut self, note: &Note) -> Result<()> { + for statement in ¬e.statements { + self.eval_statement(statement)?; + } + Ok(()) + } + + fn eval_statement(&mut self, statement: &Statement) -> Result<()> { + match statement { + Statement::Date(spec) => self.eval_date(spec)?, + Statement::BDate(spec) => self.eval_bdate(spec)?, + Statement::From(date) => self.from = *date, + Statement::Until(date) => self.until = *date, + Statement::Except(date) => self.eval_except(*date), + Statement::Move { span, from, to } => self.eval_move(*span, *from, *to)?, + } + Ok(()) + } + + fn eval_date(&mut self, spec: &Spec) -> Result<()> { + todo!() + } + + fn eval_bdate(&mut self, spec: &BirthdaySpec) -> Result<()> { + todo!() + } + + fn eval_except(&mut self, date: NaiveDate) { + self.dated.remove(&date); + } + + fn eval_move(&mut self, span: Span, from: NaiveDate, to: NaiveDate) -> Result<()> { + if let Some(entry) = self.dated.remove(&from) { + self.dated.insert(to, entry); + Ok(()) + } else { + Err(Error::MoveWithoutSource { span }) + } + } + + fn eval_done(&mut self, done: &Done) { + self.add_forced(Entry { + kind: EntryKind::TaskDone(done.done_at), + title: self.title(), + desc: self.desc(), + source: self.source(), + dates: done.date.map(|date| date.into()), + root: done.date.map(|date| date.root()), + }); } } diff --git a/src/eval/date.rs b/src/eval/date.rs index ede3b46..e914691 100644 --- a/src/eval/date.rs +++ b/src/eval/date.rs @@ -1,6 +1,6 @@ use chrono::NaiveDate; -use crate::files::commands::Time; +use crate::files::commands::{DoneDate, Time}; #[derive(Debug, Clone, Copy)] pub struct Times { @@ -37,8 +37,8 @@ impl Dates { pub fn new_with_time( start: NaiveDate, - end: NaiveDate, start_time: Time, + end: NaiveDate, end_time: Time, ) -> Self { assert!(start <= end); @@ -75,3 +75,33 @@ impl Dates { self.times.as_ref().map(Times::end) } } + +impl From for Dates { + fn from(date: DoneDate) -> Self { + match date { + DoneDate::Date { root } => Self::new(root, root), + DoneDate::DateWithTime { root, root_time } => { + Self::new_with_time(root, root_time, root, root_time) + } + DoneDate::DateToDate { root, other } => { + if root <= other { + Self::new(root, other) + } else { + Self::new(other, root) + } + } + DoneDate::DateToDateWithTime { + root, + root_time, + other, + other_time, + } => { + if root < other || (root == other && root_time <= other_time) { + Self::new_with_time(root, root_time, other, other_time) + } else { + Self::new_with_time(other, other_time, root, root_time) + } + } + } + } +} diff --git a/src/eval/error.rs b/src/eval/error.rs index a53d63f..42af02d 100644 --- a/src/eval/error.rs +++ b/src/eval/error.rs @@ -18,6 +18,9 @@ pub enum Error { start: NaiveDate, prev: NaiveDate, }, + /// A `MOVE a TO b` statement was executed, but there was no entry at the + /// date `a`. + MoveWithoutSource { span: Span }, } pub type Result = result::Result; diff --git a/src/files/commands.rs b/src/files/commands.rs index 9181eb1..adbd470 100644 --- a/src/files/commands.rs +++ b/src/files/commands.rs @@ -355,7 +355,11 @@ pub enum Statement { From(Option), Until(Option), Except(NaiveDate), // TODO Allow excluding ranges - Move(NaiveDate, NaiveDate), + Move { + span: Span, + from: NaiveDate, + to: NaiveDate, + }, } #[derive(Debug, Clone, Copy)] @@ -440,6 +444,22 @@ pub enum Command { Note(Note), } +impl Command { + pub fn title(&self) -> &str { + match self { + Command::Task(task) => &task.title, + Command::Note(note) => ¬e.title, + } + } + + pub fn desc(&self) -> &[String] { + match self { + Command::Task(task) => &task.desc, + Command::Note(note) => ¬e.desc, + } + } +} + #[derive(Debug)] pub struct File { pub contents: String, diff --git a/src/files/format.rs b/src/files/format.rs index 97be489..52e777a 100644 --- a/src/files/format.rs +++ b/src/files/format.rs @@ -219,7 +219,7 @@ impl fmt::Display for Statement { Statement::Until(Some(date)) => writeln!(f, "UNTIL {}", date), Statement::Until(None) => writeln!(f, "UNTIL *"), Statement::Except(date) => writeln!(f, "EXCEPT {}", date), - Statement::Move(from, to) => writeln!(f, "MOVE {} TO {}", from, to), + Statement::Move { span, from, to } => writeln!(f, "MOVE {} TO {}", from, to), } } } diff --git a/src/files/parse.rs b/src/files/parse.rs index f4013a7..0b76479 100644 --- a/src/files/parse.rs +++ b/src/files/parse.rs @@ -605,11 +605,12 @@ fn parse_stmt_except(p: Pair<'_, Rule>) -> Result { fn parse_stmt_move(p: Pair<'_, Rule>) -> Result { assert_eq!(p.as_rule(), Rule::stmt_move); + let span = (&p.as_span()).into(); let mut p = p.into_inner(); let from = parse_datum(p.next().unwrap())?.value; let to = parse_datum(p.next().unwrap())?.value; assert_eq!(p.next(), None); - Ok(Statement::Move(from, to)) + Ok(Statement::Move { span, from, to }) } fn parse_statements(p: Pair<'_, Rule>) -> Result> {