Continue parsing the pest output
This commit is contained in:
parent
6e53446a08
commit
90f74c82bc
3 changed files with 216 additions and 15 deletions
|
|
@ -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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
217
src/parse.rs
217
src/parse.rs
|
|
@ -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_title(p: Pair<Rule>) -> Result<String> {
|
||||||
|
assert_eq!(p.as_rule(), Rule::title);
|
||||||
|
Ok(p.into_inner().next().unwrap().as_str().to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
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!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
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() {
|
match p.as_rule() {
|
||||||
Rule::task => parse_task(p).map(Command::Task),
|
Rule::task => parse_task(p).map(Command::Task),
|
||||||
Rule::note => todo!(),
|
Rule::note => parse_note(p).map(Command::Note),
|
||||||
Rule::birthday => todo!(),
|
Rule::birthday => parse_birthday(p).map(Command::Birthday),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_task(p: Pair<Rule>) -> Result<Task> {
|
pub fn parse(input: &str) -> Result<Vec<Command>> {
|
||||||
dbg!(p);
|
let mut pairs = TodayfileParser::parse(Rule::file, input)?;
|
||||||
todo!()
|
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()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue