Fix panic on arithmetic error

Divide by zero and add, subtract, or multiply with overflow are all
caught now and reported as an ArithmeticError, rather than causing the
interpreter to panic.
This commit is contained in:
Alex Bethel 2021-05-30 13:26:10 -05:00
parent acfd81ead2
commit 07195d4cf6

View file

@ -110,21 +110,42 @@ impl ExecEnv {
use Expr::*; use Expr::*;
use Value::*; use Value::*;
// NOTE(Alex): This is quite nasty, and should probably be // NOTE(Alex): This is really quite horrible. I think the only
// re-done using macros or something. // real way to clean it up would be to re-engineer the AST's
// representation to be more hierarchical: rather than having
// e.g. "Expr::Add" and "Expr::Subtract" each with a "left"
// and "right" struct member, have something like
// "Expr::Binary { oper: BinOp, left: Box<Expr>, right:
// Box<Expr> }". That way we could factor out a whole bunch of
// common code here.
//
// That work should probably wait for Ondra's new parser to
// come in, however.
Ok(match expr { Ok(match expr {
Add { left, right } => { Add { left, right } => Int(i32::try_from(self.eval_expr(left)?)?
Int(i32::try_from(self.eval_expr(left)?)? + i32::try_from(self.eval_expr(right)?)?) .checked_add(i32::try_from(self.eval_expr(right)?)?)
} .ok_or(Error {
Subtract { left, right } => { kind: ErrorKind::ArithmeticError,
Int(i32::try_from(self.eval_expr(left)?)? - i32::try_from(self.eval_expr(right)?)?) position: 0..0,
} })?),
Multiply { left, right } => { Subtract { left, right } => Int(i32::try_from(self.eval_expr(left)?)?
Int(i32::try_from(self.eval_expr(left)?)? * i32::try_from(self.eval_expr(right)?)?) .checked_sub(i32::try_from(self.eval_expr(right)?)?)
} .ok_or(Error {
Divide { left, right } => { kind: ErrorKind::ArithmeticError,
Int(i32::try_from(self.eval_expr(left)?)? / i32::try_from(self.eval_expr(right)?)?) position: 0..0,
} })?),
Multiply { left, right } => Int(i32::try_from(self.eval_expr(left)?)?
.checked_mul(i32::try_from(self.eval_expr(right)?)?)
.ok_or(Error {
kind: ErrorKind::ArithmeticError,
position: 0..0,
})?),
Divide { left, right } => Int(i32::try_from(self.eval_expr(left)?)?
.checked_div(i32::try_from(self.eval_expr(right)?)?)
.ok_or(Error {
kind: ErrorKind::ArithmeticError,
position: 0..0,
})?),
Lt { left, right } => { Lt { left, right } => {
Bool(i32::try_from(self.eval_expr(left)?)? < i32::try_from(self.eval_expr(right)?)?) Bool(i32::try_from(self.eval_expr(left)?)? < i32::try_from(self.eval_expr(right)?)?)
} }