Continue parsing the pest output

This commit is contained in:
Joscha 2021-11-18 23:46:41 +01:00
parent 6e53446a08
commit 90f74c82bc
3 changed files with 216 additions and 15 deletions

View file

@ -209,6 +209,9 @@ pub struct Done {
pub struct Task { pub struct Task {
pub title: String, pub title: String,
pub when: Vec<Spec>, pub when: Vec<Spec>,
pub from: Option<NaiveDate>,
pub until: Option<NaiveDate>,
pub except: Vec<NaiveDate>,
pub done: Vec<Done>, pub done: Vec<Done>,
pub desc: Option<String>, pub desc: Option<String>,
} }
@ -217,6 +220,9 @@ pub struct Task {
pub struct Note { pub struct Note {
pub title: String, pub title: String,
pub when: Vec<Spec>, // Should not be empty? pub when: Vec<Spec>, // Should not be empty?
pub from: Option<NaiveDate>,
pub until: Option<NaiveDate>,
pub except: Vec<NaiveDate>,
pub desc: Option<String>, pub desc: Option<String>,
} }

View file

@ -1,10 +1,11 @@
use std::result; use std::result;
use pest::error::Error; use chrono::NaiveDate;
use pest::error::{Error, ErrorVariant};
use pest::iterators::Pair; use pest::iterators::Pair;
use pest::Parser; use pest::{Parser, Span};
use crate::commands::{Command, Task}; use crate::commands::{Birthday, BirthdaySpec, Command, Done, Note, Spec, Task};
#[derive(pest_derive::Parser)] #[derive(pest_derive::Parser)]
#[grammar = "parse/todayfile.pest"] #[grammar = "parse/todayfile.pest"]
@ -12,23 +13,213 @@ struct TodayfileParser;
type Result<T> = result::Result<T, Error<Rule>>; type Result<T> = result::Result<T, Error<Rule>>;
pub fn parse(input: &str) -> Result<Vec<Command>> { fn fail<S: Into<String>, T>(span: Span, message: S) -> Result<T> {
let mut pairs = TodayfileParser::parse(Rule::file, input)?; Err(Error::new_from_span(
let file = pairs.next().unwrap(); ErrorVariant::CustomError {
let commands = file.into_inner(); message: message.into(),
commands.map(parse_command).collect() },
span,
))
} }
fn parse_command(p: Pair<Rule>) -> Result<Command> { fn parse_title(p: Pair<Rule>) -> Result<String> {
match p.as_rule() { assert_eq!(p.as_rule(), Rule::title);
Rule::task => parse_task(p).map(Command::Task), Ok(p.into_inner().next().unwrap().as_str().to_string())
Rule::note => todo!(), }
Rule::birthday => todo!(),
fn parse_datum(p: Pair<Rule>) -> Result<NaiveDate> {
assert_eq!(p.as_rule(), Rule::datum);
let date_span = p.as_span();
let mut p = p.into_inner();
let year = p.next().unwrap().as_str().parse().unwrap();
let month = p.next().unwrap().as_str().parse().unwrap();
let day = p.next().unwrap().as_str().parse().unwrap();
assert_eq!(p.next(), None);
match NaiveDate::from_ymd_opt(year, month, day) {
Some(date) => Ok(date),
None => fail(date_span, "invalid date"),
}
}
#[derive(Default)]
struct Options {
when: Vec<Spec>,
from: Option<NaiveDate>,
until: Option<NaiveDate>,
except: Vec<NaiveDate>,
done: Vec<Done>,
}
fn parse_date(p: Pair<Rule>, opts: &mut Options) -> Result<()> {
todo!()
}
fn parse_from(p: Pair<Rule>, opts: &mut Options) -> Result<()> {
todo!()
}
fn parse_until(p: Pair<Rule>, opts: &mut Options) -> Result<()> {
todo!()
}
fn parse_except(p: Pair<Rule>, opts: &mut Options) -> Result<()> {
todo!()
}
fn parse_done(p: Pair<Rule>, opts: &mut Options) -> Result<()> {
todo!()
}
fn parse_options(p: Pair<Rule>) -> Result<Options> {
assert!(matches!(
p.as_rule(),
Rule::task_options | Rule::note_options
));
let mut opts = Options::default();
for opt in p.into_inner() {
match opt.as_rule() {
Rule::date => parse_date(opt, &mut opts)?,
Rule::from => parse_from(opt, &mut opts)?,
Rule::until => parse_until(opt, &mut opts)?,
Rule::except => parse_except(opt, &mut opts)?,
Rule::done => parse_done(opt, &mut opts)?,
_ => unreachable!(), _ => unreachable!(),
} }
} }
fn parse_task(p: Pair<Rule>) -> Result<Task> { Ok(opts)
dbg!(p); }
todo!()
fn parse_indented_line(p: Pair<Rule>) -> Result<String> {
assert_eq!(p.as_rule(), Rule::indented_line);
Ok(match p.into_inner().next() {
Some(rest) => {
assert_eq!(rest.as_rule(), Rule::rest);
rest.as_str().to_string()
}
None => "".to_string(),
})
}
fn parse_description(p: Pair<Rule>) -> Result<Option<String>> {
assert_eq!(p.as_rule(), Rule::description);
let lines = p
.into_inner()
.map(parse_indented_line)
.collect::<Result<Vec<String>>>()?;
// TODO Strip whitespace prefix
let desc = lines.join("\n");
Ok(Some(desc).filter(|s| !s.is_empty()))
}
fn parse_task(p: Pair<Rule>) -> Result<Task> {
assert_eq!(p.as_rule(), Rule::task);
let mut p = p.into_inner();
let title = parse_title(p.next().unwrap())?;
let opts = parse_options(p.next().unwrap())?;
let desc = parse_description(p.next().unwrap())?;
assert_eq!(p.next(), None);
Ok(Task {
title,
when: opts.when,
from: opts.from,
until: opts.until,
except: opts.except,
done: opts.done,
desc,
})
}
fn parse_note(p: Pair<Rule>) -> Result<Note> {
assert_eq!(p.as_rule(), Rule::task);
let mut p = p.into_inner();
let title = parse_title(p.next().unwrap())?;
let opts = parse_options(p.next().unwrap())?;
let desc = parse_description(p.next().unwrap())?;
assert_eq!(p.next(), None);
assert!(opts.done.is_empty());
Ok(Note {
title,
when: opts.when,
from: opts.from,
until: opts.until,
except: opts.except,
desc,
})
}
fn parse_bdatum(p: Pair<Rule>) -> Result<BirthdaySpec> {
assert_eq!(p.as_rule(), Rule::bdatum);
let span = p.as_span();
let p = p.into_inner().collect::<Vec<_>>();
assert!(p.len() == 2 || p.len() == 3);
let (y, m, d, year_known) = if p.len() == 3 {
let y = p[0].as_str().parse().unwrap();
let m = p[1].as_str().parse().unwrap();
let d = p[2].as_str().parse().unwrap();
(y, m, d, true)
} else {
let m = p[0].as_str().parse().unwrap();
let d = p[1].as_str().parse().unwrap();
(0, m, d, false)
};
let date = match NaiveDate::from_ymd_opt(y, m, d) {
Some(date) => Ok(date),
None => fail(span, "invalid date"),
}?;
Ok(BirthdaySpec { date, year_known })
}
fn parse_bdate(p: Pair<Rule>) -> Result<BirthdaySpec> {
assert_eq!(p.as_rule(), Rule::bdate);
parse_bdatum(p.into_inner().next().unwrap())
}
fn parse_birthday(p: Pair<Rule>) -> Result<Birthday> {
assert_eq!(p.as_rule(), Rule::birthday);
let mut p = p.into_inner();
let title = parse_title(p.next().unwrap())?;
let when = parse_bdate(p.next().unwrap())?;
let desc = parse_description(p.next().unwrap())?;
Ok(Birthday { title, when, desc })
}
fn parse_command(p: Pair<Rule>) -> Result<Command> {
assert_eq!(p.as_rule(), Rule::command);
let p = p.into_inner().next().unwrap();
match p.as_rule() {
Rule::task => parse_task(p).map(Command::Task),
Rule::note => parse_note(p).map(Command::Note),
Rule::birthday => parse_birthday(p).map(Command::Birthday),
_ => unreachable!(),
}
}
pub fn parse(input: &str) -> Result<Vec<Command>> {
let mut pairs = TodayfileParser::parse(Rule::file, input)?;
let file = pairs.next().unwrap();
file.into_inner()
// For some reason, the EOI in `file` always gets captured
.take_while(|p| p.as_rule() == Rule::command)
.map(parse_command)
.collect()
} }

View file

@ -79,17 +79,21 @@ indented_line = { NEWLINE | WHITESPACE ~ rest ~ eol }
description = { indented_line* } description = { indented_line* }
task_options = { (date | from | until | except | done)* }
task = { task = {
"TASK" "TASK"
~ title ~ title
~ (date | from | until | except | done)* ~ task_options
~ description ~ description
} }
note_options = { (date | from | until | except)* }
note = { note = {
"NOTE" "NOTE"
~ title ~ title
~ (date | from | until | except | done)* ~ note_options
~ description ~ description
} }