Carry source spans for error messages

This commit is contained in:
Joscha 2021-11-29 11:14:48 +00:00
parent 1d80ab681e
commit 1170009b4f
6 changed files with 132 additions and 59 deletions

View file

@ -1,6 +1,6 @@
use chrono::NaiveDate; use chrono::NaiveDate;
use crate::files::commands::{self, DoneDate, Time}; use crate::files::commands::{self, DoneDate, Spanned, Time};
use crate::files::Source; use crate::files::Source;
use super::delta::{Delta, DeltaStep}; use super::delta::{Delta, DeltaStep};
@ -29,7 +29,9 @@ impl From<&commands::DateSpec> for DateSpec {
.map(|delta| delta.into()) .map(|delta| delta.into())
.unwrap_or_default(); .unwrap_or_default();
if let Some(time) = spec.end_time { if let Some(time) = spec.end_time {
end_delta.steps.push(DeltaStep::Time(time)); end_delta
.steps
.push(Spanned::new(time.span, DeltaStep::Time(time.value)));
} }
let repeat: Option<Delta> = spec.repeat.as_ref().map(|repeat| (&repeat.delta).into()); let repeat: Option<Delta> = spec.repeat.as_ref().map(|repeat| (&repeat.delta).into());

View file

@ -1,6 +1,6 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use crate::files::commands::{self, Time, Weekday}; use crate::files::commands::{self, Spanned, Time, Weekday};
/// Like [`commands::DeltaStep`] but includes a new constructor, /// Like [`commands::DeltaStep`] but includes a new constructor,
/// [`DeltaStep::Time`]. /// [`DeltaStep::Time`].
@ -122,23 +122,27 @@ impl DeltaStep {
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Delta { pub struct Delta {
pub steps: Vec<DeltaStep>, pub steps: Vec<Spanned<DeltaStep>>,
} }
impl From<&commands::Delta> for Delta { impl From<&commands::Delta> for Delta {
fn from(delta: &commands::Delta) -> Self { fn from(delta: &commands::Delta) -> Self {
Self { Self {
steps: delta.0.iter().map(|&step| step.into()).collect(), steps: delta
.0
.iter()
.map(|step| Spanned::new(step.span, step.value.into()))
.collect(),
} }
} }
} }
impl Delta { impl Delta {
pub fn lower_bound(&self) -> i32 { pub fn lower_bound(&self) -> i32 {
self.steps.iter().map(DeltaStep::lower_bound).sum() self.steps.iter().map(|step| step.value.lower_bound()).sum()
} }
pub fn upper_bound(&self) -> i32 { pub fn upper_bound(&self) -> i32 {
self.steps.iter().map(DeltaStep::upper_bound).sum() self.steps.iter().map(|step| step.value.upper_bound()).sum()
} }
} }

View file

@ -1,4 +1,4 @@
use crate::files::commands::{self, DoneDate, Expr, Time, Var}; use crate::files::commands::{self, DoneDate, Expr, Spanned, Time, Var};
use crate::files::Source; use crate::files::Source;
use super::delta::{Delta, DeltaStep}; use super::delta::{Delta, DeltaStep};
@ -28,7 +28,9 @@ impl From<&commands::FormulaSpec> for FormulaSpec {
.map(|delta| delta.into()) .map(|delta| delta.into())
.unwrap_or_default(); .unwrap_or_default();
if let Some(time) = spec.end_time { if let Some(time) = spec.end_time {
end_delta.steps.push(DeltaStep::Time(time)); end_delta
.steps
.push(Spanned::new(time.span, DeltaStep::Time(time.value)));
} }
Self { Self {
@ -49,15 +51,21 @@ impl From<&commands::WeekdaySpec> for FormulaSpec {
let mut end_delta = Delta::default(); let mut end_delta = Delta::default();
if let Some(wd) = spec.end { if let Some(wd) = spec.end {
end_delta.steps.push(DeltaStep::Weekday(1, wd)); end_delta
.steps
.push(Spanned::new(wd.span, DeltaStep::Weekday(1, wd.value)));
} }
if let Some(delta) = &spec.end_delta { if let Some(delta) = &spec.end_delta {
for step in &delta.0 { for step in &delta.0 {
end_delta.steps.push((*step).into()); end_delta
.steps
.push(Spanned::new(step.span, step.value.into()));
} }
} }
if let Some(time) = spec.end_time { if let Some(time) = spec.end_time {
end_delta.steps.push(DeltaStep::Time(time)); end_delta
.steps
.push(Spanned::new(time.span, DeltaStep::Time(time.value)));
} }
Self { Self {

View file

@ -1,5 +1,48 @@
use std::fmt;
use chrono::NaiveDate; use chrono::NaiveDate;
#[derive(Debug, Clone, Copy)]
pub struct Span {
pub start: usize,
pub end: usize,
}
impl<'a> From<&pest::Span<'a>> for Span {
fn from(pspan: &pest::Span<'a>) -> Self {
Self {
start: pspan.start(),
end: pspan.end(),
}
}
}
#[derive(Clone, Copy)]
pub struct Spanned<T> {
pub span: Span,
pub value: T,
}
impl<T: fmt::Debug> fmt::Debug for Spanned<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
self.value.fmt(f)
}
}
impl<T> Spanned<T> {
pub fn new(span: Span, value: T) -> Self {
Self { span, value }
}
}
// I don't know how one would write this. It works as a polymorphic standalone
// function, but not in an impl block.
// impl<S, T: Into<S>> Spanned<T> {
// pub fn convert(&self) -> Spanned<S> {
// Self::new(self.span, self.value.into())
// }
// }
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Time { pub struct Time {
pub hour: u8, pub hour: u8,
@ -95,7 +138,7 @@ impl DeltaStep {
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Delta(pub Vec<DeltaStep>); pub struct Delta(pub Vec<Spanned<DeltaStep>>);
#[derive(Debug)] #[derive(Debug)]
pub struct Repeat { pub struct Repeat {
@ -110,9 +153,9 @@ pub struct DateSpec {
pub start: NaiveDate, pub start: NaiveDate,
pub start_delta: Option<Delta>, pub start_delta: Option<Delta>,
pub start_time: Option<Time>, pub start_time: Option<Time>,
pub end: Option<NaiveDate>, pub end: Option<Spanned<NaiveDate>>,
pub end_delta: Option<Delta>, pub end_delta: Option<Delta>,
pub end_time: Option<Time>, pub end_time: Option<Spanned<Time>>,
pub repeat: Option<Repeat>, pub repeat: Option<Repeat>,
} }
@ -120,9 +163,9 @@ pub struct DateSpec {
pub struct WeekdaySpec { pub struct WeekdaySpec {
pub start: Weekday, pub start: Weekday,
pub start_time: Option<Time>, pub start_time: Option<Time>,
pub end: Option<Weekday>, pub end: Option<Spanned<Weekday>>,
pub end_delta: Option<Delta>, pub end_delta: Option<Delta>,
pub end_time: Option<Time>, pub end_time: Option<Spanned<Time>>,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -289,7 +332,7 @@ pub struct FormulaSpec {
pub start_delta: Option<Delta>, pub start_delta: Option<Delta>,
pub start_time: Option<Time>, pub start_time: Option<Time>,
pub end_delta: Option<Delta>, pub end_delta: Option<Delta>,
pub end_time: Option<Time>, pub end_time: Option<Spanned<Time>>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -368,6 +411,7 @@ pub struct BirthdaySpec {
pub struct Birthday { pub struct Birthday {
pub title: String, pub title: String,
pub when: BirthdaySpec, pub when: BirthdaySpec,
// pub until: Option<NaiveDate>, // TODO Add UNTIL to birthday
pub desc: Vec<String>, pub desc: Vec<String>,
} }

View file

@ -4,9 +4,15 @@ use chrono::Datelike;
use super::commands::{ use super::commands::{
Birthday, BirthdaySpec, Command, DateSpec, Delta, DeltaStep, Done, DoneDate, Expr, File, Birthday, BirthdaySpec, Command, DateSpec, Delta, DeltaStep, Done, DoneDate, Expr, File,
FormulaSpec, Note, Repeat, Spec, Task, Time, Var, Weekday, WeekdaySpec, FormulaSpec, Note, Repeat, Spanned, Spec, Task, Time, Var, Weekday, WeekdaySpec,
}; };
impl<T: fmt::Display> fmt::Display for Spanned<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.value.fmt(f)
}
}
fn format_desc(f: &mut fmt::Formatter<'_>, desc: &[String]) -> fmt::Result { fn format_desc(f: &mut fmt::Formatter<'_>, desc: &[String]) -> fmt::Result {
for line in desc { for line in desc {
if line.is_empty() { if line.is_empty() {
@ -46,7 +52,7 @@ impl fmt::Display for Delta {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut sign = 0; let mut sign = 0;
for step in &self.0 { for step in &self.0 {
format_delta_step(f, step, &mut sign)?; format_delta_step(f, &step.value, &mut sign)?;
} }
Ok(()) Ok(())
} }

View file

@ -7,7 +7,7 @@ use pest::iterators::Pair;
use pest::prec_climber::{Assoc, Operator, PrecClimber}; use pest::prec_climber::{Assoc, Operator, PrecClimber};
use pest::{Parser, Span}; use pest::{Parser, Span};
use crate::files::commands::Repeat; use crate::files::commands::{Repeat, Spanned};
use super::commands::{ use super::commands::{
Birthday, BirthdaySpec, Command, DateSpec, Delta, DeltaStep, Done, DoneDate, Expr, File, Birthday, BirthdaySpec, Command, DateSpec, Delta, DeltaStep, Done, DoneDate, Expr, File,
@ -56,9 +56,10 @@ fn parse_title(p: Pair<'_, Rule>) -> String {
p.as_str().trim().to_string() p.as_str().trim().to_string()
} }
fn parse_datum(p: Pair<'_, Rule>) -> Result<NaiveDate> { fn parse_datum(p: Pair<'_, Rule>) -> Result<Spanned<NaiveDate>> {
assert_eq!(p.as_rule(), Rule::datum); assert_eq!(p.as_rule(), Rule::datum);
let span = p.as_span(); let pspan = p.as_span();
let span = (&pspan).into();
let mut p = p.into_inner(); let mut p = p.into_inner();
let year = p.next().unwrap().as_str().parse().unwrap(); let year = p.next().unwrap().as_str().parse().unwrap();
@ -68,14 +69,15 @@ fn parse_datum(p: Pair<'_, Rule>) -> Result<NaiveDate> {
assert_eq!(p.next(), None); assert_eq!(p.next(), None);
match NaiveDate::from_ymd_opt(year, month, day) { match NaiveDate::from_ymd_opt(year, month, day) {
Some(date) => Ok(date), Some(date) => Ok(Spanned::new(span, date)),
None => fail(span, "invalid date"), None => fail(pspan, "invalid date"),
} }
} }
fn parse_time(p: Pair<'_, Rule>) -> Result<Time> { fn parse_time(p: Pair<'_, Rule>) -> Result<Spanned<Time>> {
assert_eq!(p.as_rule(), Rule::time); assert_eq!(p.as_rule(), Rule::time);
let span = p.as_span(); let pspan = p.as_span();
let span = (&pspan).into();
let mut p = p.into_inner(); let mut p = p.into_inner();
let hour = p.next().unwrap().as_str().parse().unwrap(); let hour = p.next().unwrap().as_str().parse().unwrap();
@ -84,8 +86,8 @@ fn parse_time(p: Pair<'_, Rule>) -> Result<Time> {
assert_eq!(p.next(), None); assert_eq!(p.next(), None);
match Time::new(hour, min) { match Time::new(hour, min) {
Some(time) => Ok(time), Some(time) => Ok(Spanned::new(span, time)),
None => fail(span, "invalid time"), None => fail(pspan, "invalid time"),
} }
} }
@ -138,9 +140,10 @@ fn parse_amount(p: Pair<'_, Rule>) -> Amount {
Amount { sign, value } Amount { sign, value }
} }
fn parse_weekday(p: Pair<'_, Rule>) -> Weekday { fn parse_weekday(p: Pair<'_, Rule>) -> Spanned<Weekday> {
assert_eq!(p.as_rule(), Rule::weekday); assert_eq!(p.as_rule(), Rule::weekday);
match p.as_str() { let span = (&p.as_span()).into();
let wd = match p.as_str() {
"mon" => Weekday::Monday, "mon" => Weekday::Monday,
"tue" => Weekday::Tuesday, "tue" => Weekday::Tuesday,
"wed" => Weekday::Wednesday, "wed" => Weekday::Wednesday,
@ -149,32 +152,34 @@ fn parse_weekday(p: Pair<'_, Rule>) -> Weekday {
"sat" => Weekday::Saturday, "sat" => Weekday::Saturday,
"sun" => Weekday::Sunday, "sun" => Weekday::Sunday,
_ => unreachable!(), _ => unreachable!(),
} };
Spanned::new(span, wd)
} }
fn parse_delta_weekdays(p: Pair<'_, Rule>, sign: &mut Option<Sign>) -> Result<DeltaStep> { fn parse_delta_weekdays(p: Pair<'_, Rule>, sign: &mut Option<Sign>) -> Result<Spanned<DeltaStep>> {
assert_eq!(p.as_rule(), Rule::delta_weekdays); assert_eq!(p.as_rule(), Rule::delta_weekdays);
let span = p.as_span(); let pspan = p.as_span();
let span = (&pspan).into();
let mut p = p.into_inner(); let mut p = p.into_inner();
let amount = parse_amount(p.next().unwrap()).with_prev_sign(*sign); let amount = parse_amount(p.next().unwrap()).with_prev_sign(*sign);
let weekday = parse_weekday(p.next().unwrap()); let weekday = parse_weekday(p.next().unwrap()).value;
assert_eq!(p.next(), None); assert_eq!(p.next(), None);
let value = amount let value = amount
.value() .value()
.ok_or_else(|| error(span, "ambiguous sign"))?; .ok_or_else(|| error(pspan, "ambiguous sign"))?;
*sign = amount.sign; *sign = amount.sign;
Ok(DeltaStep::Weekday(value, weekday)) Ok(Spanned::new(span, DeltaStep::Weekday(value, weekday)))
} }
fn parse_delta_step( fn parse_delta_step(
p: Pair<'_, Rule>, p: Pair<'_, Rule>,
sign: &mut Option<Sign>, sign: &mut Option<Sign>,
f: impl FnOnce(i32) -> DeltaStep, f: impl FnOnce(i32) -> DeltaStep,
) -> Result<DeltaStep> { ) -> Result<Spanned<DeltaStep>> {
assert!(matches!( assert!(matches!(
p.as_rule(), p.as_rule(),
Rule::delta_years Rule::delta_years
@ -186,14 +191,15 @@ fn parse_delta_step(
| Rule::delta_minutes | Rule::delta_minutes
)); ));
let span = p.as_span(); let pspan = p.as_span();
let span = (&pspan).into();
let amount = parse_amount(p.into_inner().next().unwrap()).with_prev_sign(*sign); let amount = parse_amount(p.into_inner().next().unwrap()).with_prev_sign(*sign);
let value = amount let value = amount
.value() .value()
.ok_or_else(|| error(span, "ambiguous sign"))?; .ok_or_else(|| error(pspan, "ambiguous sign"))?;
*sign = amount.sign; *sign = amount.sign;
Ok(f(value)) Ok(Spanned::new(span, f(value)))
} }
fn parse_delta(p: Pair<'_, Rule>) -> Result<Delta> { fn parse_delta(p: Pair<'_, Rule>) -> Result<Delta> {
@ -226,9 +232,9 @@ fn parse_date_fixed_start(p: Pair<'_, Rule>, spec: &mut DateSpec) -> Result<()>
for p in p.into_inner() { for p in p.into_inner() {
match p.as_rule() { match p.as_rule() {
Rule::datum => spec.start = parse_datum(p)?, Rule::datum => spec.start = parse_datum(p)?.value,
Rule::delta => spec.start_delta = Some(parse_delta(p)?), Rule::delta => spec.start_delta = Some(parse_delta(p)?),
Rule::time => spec.start_time = Some(parse_time(p)?), Rule::time => spec.start_time = Some(parse_time(p)?.value),
_ => unreachable!(), _ => unreachable!(),
} }
} }
@ -431,7 +437,7 @@ fn parse_date_expr_start(p: Pair<'_, Rule>, spec: &mut FormulaSpec) -> Result<()
match p.as_rule() { match p.as_rule() {
Rule::paren_expr => spec.start = Some(parse_expr(p.into_inner().next().unwrap())), Rule::paren_expr => spec.start = Some(parse_expr(p.into_inner().next().unwrap())),
Rule::delta => spec.start_delta = Some(parse_delta(p)?), Rule::delta => spec.start_delta = Some(parse_delta(p)?),
Rule::time => spec.start_time = Some(parse_time(p)?), Rule::time => spec.start_time = Some(parse_time(p)?.value),
_ => unreachable!(), _ => unreachable!(),
} }
} }
@ -480,8 +486,8 @@ fn parse_date_weekday_start(p: Pair<'_, Rule>, spec: &mut WeekdaySpec) -> Result
for p in p.into_inner() { for p in p.into_inner() {
match p.as_rule() { match p.as_rule() {
Rule::weekday => spec.start = parse_weekday(p), Rule::weekday => spec.start = parse_weekday(p).value,
Rule::time => spec.start_time = Some(parse_time(p)?), Rule::time => spec.start_time = Some(parse_time(p)?.value),
_ => unreachable!(), _ => unreachable!(),
} }
} }
@ -539,17 +545,20 @@ fn parse_date(p: Pair<'_, Rule>) -> Result<Spec> {
fn parse_from(p: Pair<'_, Rule>) -> Result<NaiveDate> { fn parse_from(p: Pair<'_, Rule>) -> Result<NaiveDate> {
assert_eq!(p.as_rule(), Rule::from); assert_eq!(p.as_rule(), Rule::from);
parse_datum(p.into_inner().next().unwrap()) let datum = parse_datum(p.into_inner().next().unwrap())?;
Ok(datum.value)
} }
fn parse_until(p: Pair<'_, Rule>) -> Result<NaiveDate> { fn parse_until(p: Pair<'_, Rule>) -> Result<NaiveDate> {
assert_eq!(p.as_rule(), Rule::until); assert_eq!(p.as_rule(), Rule::until);
parse_datum(p.into_inner().next().unwrap()) let datum = parse_datum(p.into_inner().next().unwrap())?;
Ok(datum.value)
} }
fn parse_except(p: Pair<'_, Rule>) -> Result<NaiveDate> { fn parse_except(p: Pair<'_, Rule>) -> Result<NaiveDate> {
assert_eq!(p.as_rule(), Rule::except); assert_eq!(p.as_rule(), Rule::except);
parse_datum(p.into_inner().next().unwrap()) let datum = parse_datum(p.into_inner().next().unwrap())?;
Ok(datum.value)
} }
fn parse_donedate(p: Pair<'_, Rule>) -> Result<DoneDate> { fn parse_donedate(p: Pair<'_, Rule>) -> Result<DoneDate> {
@ -561,24 +570,24 @@ fn parse_donedate(p: Pair<'_, Rule>) -> Result<DoneDate> {
// the list ;-; // the list ;-;
Ok(match ps.len() { Ok(match ps.len() {
1 => DoneDate::Date { 1 => DoneDate::Date {
root: parse_datum(ps.pop().unwrap())?, root: parse_datum(ps.pop().unwrap())?.value,
}, },
2 => match ps[1].as_rule() { 2 => match ps[1].as_rule() {
Rule::time => DoneDate::DateWithTime { Rule::time => DoneDate::DateWithTime {
root_time: parse_time(ps.pop().unwrap())?, root_time: parse_time(ps.pop().unwrap())?.value,
root: parse_datum(ps.pop().unwrap())?, root: parse_datum(ps.pop().unwrap())?.value,
}, },
Rule::datum => DoneDate::DateToDate { Rule::datum => DoneDate::DateToDate {
other: parse_datum(ps.pop().unwrap())?, other: parse_datum(ps.pop().unwrap())?.value,
root: parse_datum(ps.pop().unwrap())?, root: parse_datum(ps.pop().unwrap())?.value,
}, },
_ => unreachable!(), _ => unreachable!(),
}, },
4 => DoneDate::DateToDateWithTime { 4 => DoneDate::DateToDateWithTime {
other_time: parse_time(ps.pop().unwrap())?, other_time: parse_time(ps.pop().unwrap())?.value,
other: parse_datum(ps.pop().unwrap())?, other: parse_datum(ps.pop().unwrap())?.value,
root_time: parse_time(ps.pop().unwrap())?, root_time: parse_time(ps.pop().unwrap())?.value,
root: parse_datum(ps.pop().unwrap())?, root: parse_datum(ps.pop().unwrap())?.value,
}, },
_ => unreachable!(), _ => unreachable!(),
}) })
@ -588,7 +597,7 @@ fn parse_done(p: Pair<'_, Rule>) -> Result<Done> {
assert_eq!(p.as_rule(), Rule::done); assert_eq!(p.as_rule(), Rule::done);
let mut p = p.into_inner(); let mut p = p.into_inner();
let done_at = parse_datum(p.next().unwrap())?; let done_at = parse_datum(p.next().unwrap())?.value;
let date = if let Some(p) = p.next() { let date = if let Some(p) = p.next() {
Some(parse_donedate(p)?) Some(parse_donedate(p)?)
} else { } else {