Implement proper errors for expression evaluation

... the hard way
This commit is contained in:
Joscha 2021-12-06 22:18:57 +01:00
parent efa9b7de03
commit 5ee70bd769
4 changed files with 186 additions and 75 deletions

View file

@ -201,49 +201,35 @@ impl Var {
}
}
impl From<Weekday> for Var {
fn from(wd: Weekday) -> Self {
match wd {
Weekday::Monday => Self::Monday,
Weekday::Tuesday => Self::Tuesday,
Weekday::Wednesday => Self::Wednesday,
Weekday::Thursday => Self::Thursday,
Weekday::Friday => Self::Friday,
Weekday::Saturday => Self::Saturday,
Weekday::Sunday => Self::Sunday,
}
}
}
#[derive(Debug, Clone)]
pub enum Expr {
Lit(i64),
Var(Var),
Paren(Box<Expr>),
Paren(Box<Spanned<Expr>>),
// Integer-y operations
Neg(Box<Expr>),
Add(Box<Expr>, Box<Expr>),
Sub(Box<Expr>, Box<Expr>),
Mul(Box<Expr>, Box<Expr>),
Div(Box<Expr>, Box<Expr>),
Mod(Box<Expr>, Box<Expr>),
Neg(Box<Spanned<Expr>>),
Add(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
Sub(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
Mul(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
Div(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
Mod(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
// Comparisons
Eq(Box<Expr>, Box<Expr>),
Neq(Box<Expr>, Box<Expr>),
Lt(Box<Expr>, Box<Expr>),
Lte(Box<Expr>, Box<Expr>),
Gt(Box<Expr>, Box<Expr>),
Gte(Box<Expr>, Box<Expr>),
Eq(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
Neq(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
Lt(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
Lte(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
Gt(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
Gte(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
// Boolean-y operations
Not(Box<Expr>),
And(Box<Expr>, Box<Expr>),
Or(Box<Expr>, Box<Expr>),
Xor(Box<Expr>, Box<Expr>),
Not(Box<Spanned<Expr>>),
And(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
Or(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
Xor(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
}
#[derive(Debug)]
pub struct FormulaSpec {
pub start: Option<Expr>, // None: *
pub start: Option<Spanned<Expr>>, // None: *
pub start_delta: Option<Delta>,
pub start_time: Option<Time>,
pub end_delta: Option<Delta>,

View file

@ -350,42 +350,48 @@ fn parse_variable(p: Pair<'_, Rule>) -> Var {
}
}
fn parse_unop_expr(p: Pair<'_, Rule>) -> Expr {
fn parse_unop_expr(p: Pair<'_, Rule>) -> Spanned<Expr> {
assert_eq!(p.as_rule(), Rule::unop_expr);
let span = (&p.as_span()).into();
let mut p = p.into_inner();
let p_op = p.next().unwrap();
let p_expr = p.next().unwrap();
assert_eq!(p.next(), None);
let expr = parse_expr(p_expr);
match p_op.as_rule() {
Rule::unop_neg => Expr::Neg(Box::new(expr)),
Rule::unop_not => Expr::Not(Box::new(expr)),
let inner = parse_expr(p_expr);
let expr = match p_op.as_rule() {
Rule::unop_neg => Expr::Neg(Box::new(inner)),
Rule::unop_not => Expr::Not(Box::new(inner)),
_ => unreachable!(),
}
};
Spanned::new(span, expr)
}
fn parse_paren_expr(p: Pair<'_, Rule>) -> Expr {
fn parse_paren_expr(p: Pair<'_, Rule>) -> Spanned<Expr> {
assert_eq!(p.as_rule(), Rule::paren_expr);
let span = (&p.as_span()).into();
let inner = parse_expr(p.into_inner().next().unwrap());
Expr::Paren(Box::new(inner))
Spanned::new(span, Expr::Paren(Box::new(inner)))
}
fn parse_term(p: Pair<'_, Rule>) -> Expr {
fn parse_term(p: Pair<'_, Rule>) -> Spanned<Expr> {
assert_eq!(p.as_rule(), Rule::term);
let span = (&p.as_span()).into();
let p = p.into_inner().next().unwrap();
match p.as_rule() {
Rule::number => Expr::Lit(parse_number(p).into()),
Rule::boolean => Expr::Var(parse_boolean(p)),
Rule::variable => Expr::Var(parse_variable(p)),
Rule::number => Spanned::new(span, Expr::Lit(parse_number(p).into())),
Rule::boolean => Spanned::new(span, Expr::Var(parse_boolean(p))),
Rule::variable => Spanned::new(span, Expr::Var(parse_variable(p))),
Rule::unop_expr => parse_unop_expr(p),
Rule::paren_expr => parse_paren_expr(p),
_ => unreachable!(),
}
}
fn parse_op(l: Expr, p: Pair<'_, Rule>, r: Expr) -> Expr {
match p.as_rule() {
fn parse_op(l: Spanned<Expr>, p: Pair<'_, Rule>, r: Spanned<Expr>) -> Spanned<Expr> {
let span = l.span.join(r.span);
let expr = match p.as_rule() {
// Integer-y operations
Rule::op_add => Expr::Add(Box::new(l), Box::new(r)),
Rule::op_sub => Expr::Sub(Box::new(l), Box::new(r)),
@ -407,10 +413,11 @@ fn parse_op(l: Expr, p: Pair<'_, Rule>, r: Expr) -> Expr {
Rule::op_xor => Expr::Xor(Box::new(l), Box::new(r)),
_ => unreachable!(),
}
};
Spanned::new(span, expr)
}
fn parse_expr(p: Pair<'_, Rule>) -> Expr {
fn parse_expr(p: Pair<'_, Rule>) -> Spanned<Expr> {
assert_eq!(p.as_rule(), Rule::expr);
fn op(rule: Rule) -> Operator<Rule> {

View file

@ -1,4 +1,4 @@
use std::cmp::Ordering;
use std::cmp::{self, Ordering};
use std::fmt;
#[derive(Debug, Clone, Copy)]
@ -16,6 +16,15 @@ impl<'a> From<&pest::Span<'a>> for Span {
}
}
impl Span {
pub fn join(self, other: Self) -> Self {
Self {
start: cmp::min(self.start, other.start),
end: cmp::max(self.end, other.end),
}
}
}
#[derive(Clone, Copy)]
pub struct Spanned<T> {
pub span: Span,