Cleanups and comments in src/eval

This commit is contained in:
2023-04-17 17:41:55 -07:00
parent e648f3b31b
commit 9e3cce5076
3 changed files with 52 additions and 4 deletions

View File

@@ -1,5 +1,6 @@
use crate::eval::value::Value;
/// Errors that can occur running primitive operations in the evaluators.
#[derive(Clone, Debug, PartialEq, thiserror::Error)]
pub enum PrimOpError {
#[error("Math error (underflow or overflow) computing {0} operator")]
@@ -14,6 +15,16 @@ pub enum PrimOpError {
UnknownPrimOp(String),
}
// Implementing primitives in an interpreter like this is *super* tedious,
// and the only way to make it even somewhat manageable is to use macros.
// This particular macro works for binary operations, and assumes that
// you've already worked out that the `calculate` call provided two arguments.
//
// In those cases, it will rul the operations we know about, and error if
// it doesn't.
//
// This macro then needs to be instantiated for every type, which is super
// fun.
macro_rules! run_op {
($op: ident, $left: expr, $right: expr) => {
match $op {
@@ -23,15 +34,15 @@ macro_rules! run_op {
.map(Into::into),
"-" => $left
.checked_sub($right)
.ok_or(PrimOpError::MathFailure("+"))
.ok_or(PrimOpError::MathFailure("-"))
.map(Into::into),
"*" => $left
.checked_mul($right)
.ok_or(PrimOpError::MathFailure("+"))
.ok_or(PrimOpError::MathFailure("*"))
.map(Into::into),
"/" => $left
.checked_div($right)
.ok_or(PrimOpError::MathFailure("+"))
.ok_or(PrimOpError::MathFailure("/"))
.map(Into::into),
_ => Err(PrimOpError::UnknownPrimOp($op.to_string())),
}
@@ -41,6 +52,8 @@ macro_rules! run_op {
impl Value {
fn binary_op(operation: &str, left: &Value, right: &Value) -> Result<Value, PrimOpError> {
match left {
// for now we only have one type, but in the future this is
// going to be very irritating.
Value::I64(x) => match right {
Value::I64(y) => run_op!(operation, x, *y),
// _ => Err(PrimOpError::TypeMismatch(
@@ -52,6 +65,10 @@ impl Value {
}
}
/// Calculate the result of running the given primitive on the given arguments.
///
/// This can cause errors in a whole mess of ways, so be careful about your
/// inputs.
pub fn calculate(operation: &str, values: Vec<Value>) -> Result<Value, PrimOpError> {
if values.len() == 2 {
Value::binary_op(operation, &values[0], &values[1])