Skip to content
Snippets Groups Projects
value.rs 36.57 KiB
#![allow(unused)]

extern crate derive_more;
use derive_more::{From};

/* Defines semantic meaning of IR operations. */
extern crate itertools;

use crate::dyn_const_value;

use self::itertools::Itertools;
use std::clone;
use std::convert::TryInto;
use std::panic;

extern crate hercules_ir;
extern crate hercules_opt;

use self::hercules_ir::*;
use self::hercules_opt::*;

#[derive(PartialEq, Debug, Clone, Eq)]
pub enum InterpreterVal {
    Boolean(bool),
    Integer8(i8),
    Integer16(i16),
    Integer32(i32),
    Integer64(i64),
    UnsignedInteger8(u8),
    UnsignedInteger16(u16),
    UnsignedInteger32(u32),
    UnsignedInteger64(u64),

    Float32(ordered_float::OrderedFloat<f32>),
    Float64(ordered_float::OrderedFloat<f64>),

    Product(TypeID, Box<[InterpreterVal]>),
    Summation(TypeID, u32, Box<[InterpreterVal]>),
    Array(TypeID, Box<[InterpreterVal]>), // TypeID of the array Type (not the element type)
    // These can be freely? casted
    DynamicConstant(usize),
    ThreadID(usize),
}

#[derive(Clone)]
pub enum InterpreterWrapper {
    Boolean(bool),
    Integer8(i8),
    Integer16(i16),
    Integer32(i32),
    Integer64(i64),
    UnsignedInteger8(u8),
    UnsignedInteger16(u16),
    UnsignedInteger32(u32),
    UnsignedInteger64(u64),

    Float32(ordered_float::OrderedFloat<f32>),
    Float64(ordered_float::OrderedFloat<f64>),

    Product(Box<[InterpreterWrapper]>),
    Summation(u32, Box<[InterpreterWrapper]>),
    Array(Box<[InterpreterWrapper]>), // TypeID of the array Type (not the element type)
}



impl <T> From<&Vec<T>> for InterpreterWrapper 
    where T: Into<InterpreterWrapper> + Clone
{
    
    fn from(value: &Vec<T>) -> Self {
        let mut values = vec![];
        for i in 0..value.len() {
            values[i] = value[i].clone().into()
        }
        InterpreterWrapper::Array(values.into_boxed_slice())
    }
}

impl <T> From<Vec<T>> for InterpreterWrapper 
    where T: Into<InterpreterWrapper> + Clone
{
    fn from(value: Vec<T>) -> Self {
        let mut values = vec![];
        for i in 0..value.len() {
            values.push(value[i].clone().into());
        }
        InterpreterWrapper::Array(values.into_boxed_slice())
    }
}


impl <T> From<&[T]> for InterpreterWrapper 
    where T: Into<InterpreterWrapper> + Clone
{
    
    fn from(value: &[T]) -> Self {
        let mut values = vec![];
        for i in 0..value.len() {
            values[i] = value[i].clone().into()
        }
        InterpreterWrapper::Array(values.into_boxed_slice())
    }
}

// Map rust types to interpreter values. 
macro_rules! from_impl {
    ($rust:ty, $variant:tt) => {
        impl From<$rust> for InterpreterWrapper {
            fn from(value: $rust) -> Self {
                InterpreterWrapper::$variant(value)
            }
        }
    };
}

from_impl!(bool, Boolean);
from_impl!(i8, Integer8);
from_impl!(i16, Integer16);
from_impl!(i32, Integer32);
from_impl!(i64, Integer64);
from_impl!(u8, UnsignedInteger8);
from_impl!(u16, UnsignedInteger16);
from_impl!(u32, UnsignedInteger32);
from_impl!(u64, UnsignedInteger64);

impl<'a> InterpreterVal {
    pub fn from_constant(
        constant: &Constant,
        constants: &'a Vec<Constant>,
        types: &'a Vec<Type>,
        dynamic_constants: &'a [DynamicConstant],
        dynamic_constant_params: &'a [usize],
    ) -> Self {
        match *constant {
            Constant::Boolean(v) => Self::Boolean(v),
            Constant::Integer8(v) => Self::Integer8(v),
            Constant::Integer16(v) => Self::Integer16(v),
            Constant::Integer32(v) => Self::Integer32(v),
            Constant::Integer64(v) => Self::Integer64(v),
            Constant::UnsignedInteger8(v) => Self::UnsignedInteger8(v),
            Constant::UnsignedInteger16(v) => Self::UnsignedInteger16(v),
            Constant::UnsignedInteger32(v) => Self::UnsignedInteger32(v),
            Constant::UnsignedInteger64(v) => Self::UnsignedInteger64(v),
            Constant::Float32(v) => Self::Float32(v),
            Constant::Float64(v) => Self::Float64(v),

            Constant::Product(_, _) => todo!(),
            Constant::Summation(_, _, _) => todo!(),
            Constant::Array(type_id) => {
                // TODO: This is currently only implemented for arrays of primitive types, implement zero initializers for other types.
                let ty = &types[type_id.idx()];

                let extents: Vec<_> = ty
                    .try_extents()
                    .expect("PANIC: wrong type for array")
                    .into_iter()
                    .map(|extent| dyn_const_value(&dynamic_constants[extent.idx()], &dynamic_constant_params))
                    .collect();

                let size = InterpreterVal::array_size(&extents);

                // Need to recurse here, ugh.
                let element_type_id = ty.try_element_type().expect("PANIC: no element type");
                let element_type = &types[element_type_id.idx()];

                // TODO Need a seperate funciton to handle this, as arrays w/ non-primitive elements should use Constant::Zero(element_id),
                // and arrays w/ primitive elements should use the zero-initializer for those.

                let element_zeroed = InterpreterVal::zero_initialized(&element_type);
                let backing_array = vec![element_zeroed; size];
                Self::Array(type_id, backing_array.into_boxed_slice())
            }
        }
    }

    /* Zero initialized interpreter value of a hercules type, only supported for primitive types. */
    pub fn zero_initialized(ty: &'a Type) -> InterpreterVal {
        match ty {
            Type::Boolean => Self::Boolean(false),
            Type::Integer8 => Self::Integer8(0),
            Type::Integer16 => Self::Integer16(0),
            Type::Integer32 => Self::Integer32(0),
            Type::Integer64 => Self::Integer64(0),
            Type::UnsignedInteger8 => Self::UnsignedInteger8(0),
            Type::UnsignedInteger16 => Self::UnsignedInteger16(0),
            Type::UnsignedInteger32 => Self::UnsignedInteger32(0),
            Type::UnsignedInteger64 => Self::UnsignedInteger64(0),
            //Type::Float32 => Self::Float32(0.0),
            //Type::Float64 => Self::Float64(0.0),
            _ => todo!("Zero initializer not implemented yet for this type"),
        }
    }

    // TODO: Find some way to reuse this code and conditional constant prop code.
    pub fn binary_op(
        op: BinaryOperator,
        left: InterpreterVal,
        right: InterpreterVal,
    ) -> InterpreterVal {

        // Do some type conversion first.
        let left = match left {
            InterpreterVal::DynamicConstant(v) => match right {
                InterpreterVal::Integer8(_) => InterpreterVal::Integer8(v.try_into().unwrap()),
                InterpreterVal::Integer16(_) => InterpreterVal::Integer16(v.try_into().unwrap()),
                InterpreterVal::Integer32(_) => InterpreterVal::Integer32(v.try_into().unwrap()),
                InterpreterVal::Integer64(_) => InterpreterVal::Integer64(v.try_into().unwrap()),
                InterpreterVal::UnsignedInteger8(_) => {
                    InterpreterVal::UnsignedInteger8(v.try_into().unwrap())
                }
                InterpreterVal::UnsignedInteger16(_) => {
                    InterpreterVal::UnsignedInteger16(v.try_into().unwrap())
                }
                InterpreterVal::UnsignedInteger32(_) => {
                    InterpreterVal::UnsignedInteger64(v.try_into().unwrap())
                }
                InterpreterVal::UnsignedInteger64(_) => {
                    InterpreterVal::UnsignedInteger64(v.try_into().unwrap())
                }
                InterpreterVal::DynamicConstant(_) => {
                    panic!("PANIC: Some math on dynamic constants is unimplemented")
                }
                // InterpreterVal::ThreadID(_) => InterpreterVal::Boolean(v),
                _ => panic!("PANIC: Some math on dynamic constants is unimplemented"),
            },
            // InterpreterVal::ThreadID(v) => todo!(),
            _ => left,
        };

        let right = match right {
            InterpreterVal::DynamicConstant(v) => match left {
                InterpreterVal::Integer8(_) => InterpreterVal::Integer8(v.try_into().unwrap()),
                InterpreterVal::Integer16(_) => InterpreterVal::Integer16(v.try_into().unwrap()),
                InterpreterVal::Integer32(_) => InterpreterVal::Integer32(v.try_into().unwrap()),
                InterpreterVal::Integer64(_) => InterpreterVal::Integer64(v.try_into().unwrap()),
                InterpreterVal::UnsignedInteger8(_) => {
                    InterpreterVal::UnsignedInteger8(v.try_into().unwrap())
                }
                InterpreterVal::UnsignedInteger16(_) => {
                    InterpreterVal::UnsignedInteger16(v.try_into().unwrap())
                }
                InterpreterVal::UnsignedInteger32(_) => {
                    InterpreterVal::UnsignedInteger32(v.try_into().unwrap())
                }
                InterpreterVal::UnsignedInteger64(_) => {
                    InterpreterVal::UnsignedInteger64(v.try_into().unwrap())
                }
                InterpreterVal::DynamicConstant(_) => {
                    panic!("PANIC: Some math on dynamic constants is unimplemented")
                }
                _ => panic!("PANIC: Some math on dynamic constants is unimplemented"),
            },
            // InterpreterVal::ThreadID(v) => todo!(),
            _ => right,
        };

        match (op, left.clone(), right.clone()) {
            (BinaryOperator::Add, Self::Integer8(left_val), Self::Integer8(right_val)) => {
                Self::Integer8(left_val + right_val)
            }
            (BinaryOperator::Add, Self::Integer16(left_val), Self::Integer16(right_val)) => {
                Self::Integer16(left_val + right_val)
            }
            (BinaryOperator::Add, Self::Integer32(left_val), Self::Integer32(right_val)) => {
                Self::Integer32(left_val + right_val)
            }
            (BinaryOperator::Add, Self::Integer64(left_val), Self::Integer64(right_val)) => {
                Self::Integer64(left_val + right_val)
            }
            (
                BinaryOperator::Add,
                Self::UnsignedInteger8(left_val),
                Self::UnsignedInteger8(right_val),
            ) => Self::UnsignedInteger8(left_val + right_val),
            (
                BinaryOperator::Add,
                Self::UnsignedInteger16(left_val),
                Self::UnsignedInteger16(right_val),
            ) => Self::UnsignedInteger16(left_val + right_val),
            (
                BinaryOperator::Add,
                Self::UnsignedInteger32(left_val),
                Self::UnsignedInteger32(right_val),
            ) => Self::UnsignedInteger32(left_val + right_val),
            (
                BinaryOperator::Add,
                Self::UnsignedInteger64(left_val),
                Self::UnsignedInteger64(right_val),
            ) => Self::UnsignedInteger64(left_val + right_val),
            (BinaryOperator::Add, Self::Float32(left_val), Self::Float32(right_val)) => {
                Self::Float32(left_val + right_val)
            }
            (BinaryOperator::Add, Self::Float64(left_val), Self::Float64(right_val)) => {
                Self::Float64(left_val + right_val)
            }
            (BinaryOperator::Sub, Self::Integer8(left_val), Self::Integer8(right_val)) => {
                Self::Integer8(left_val - right_val)
            }
            (BinaryOperator::Sub, Self::Integer16(left_val), Self::Integer16(right_val)) => {
                Self::Integer16(left_val - right_val)
            }
            (BinaryOperator::Sub, Self::Integer32(left_val), Self::Integer32(right_val)) => {
                Self::Integer32(left_val - right_val)
            }
            (BinaryOperator::Sub, Self::Integer64(left_val), Self::Integer64(right_val)) => {
                Self::Integer64(left_val - right_val)
            }
            (
                BinaryOperator::Sub,
                Self::UnsignedInteger8(left_val),
                Self::UnsignedInteger8(right_val),
            ) => Self::UnsignedInteger8(left_val - right_val),
            (
                BinaryOperator::Sub,
                Self::UnsignedInteger16(left_val),
                Self::UnsignedInteger16(right_val),
            ) => Self::UnsignedInteger16(left_val - right_val),
            (
                BinaryOperator::Sub,
                Self::UnsignedInteger32(left_val),
                Self::UnsignedInteger32(right_val),
            ) => Self::UnsignedInteger32(left_val - right_val),
            (
                BinaryOperator::Sub,
                Self::UnsignedInteger64(left_val),
                Self::UnsignedInteger64(right_val),
            ) => Self::UnsignedInteger64(left_val - right_val),
            (BinaryOperator::Sub, Self::Float32(left_val), Self::Float32(right_val)) => {
                Self::Float32(left_val - right_val)
            }
            (BinaryOperator::Sub, Self::Float64(left_val), Self::Float64(right_val)) => {
                Self::Float64(left_val - right_val)
            }
            (BinaryOperator::Mul, Self::Integer8(left_val), Self::Integer8(right_val)) => {
                Self::Integer8(left_val * right_val)
            }
            (BinaryOperator::Mul, Self::Integer16(left_val), Self::Integer16(right_val)) => {
                Self::Integer16(left_val * right_val)
            }
            (BinaryOperator::Mul, Self::Integer32(left_val), Self::Integer32(right_val)) => {
                Self::Integer32(left_val * right_val)
            }
            (BinaryOperator::Mul, Self::Integer64(left_val), Self::Integer64(right_val)) => {
                Self::Integer64(left_val * right_val)
            }
            (
                BinaryOperator::Mul,
                Self::UnsignedInteger8(left_val),
                Self::UnsignedInteger8(right_val),
            ) => Self::UnsignedInteger8(left_val * right_val),
            (
                BinaryOperator::Mul,
                Self::UnsignedInteger16(left_val),
                Self::UnsignedInteger16(right_val),
            ) => Self::UnsignedInteger16(left_val * right_val),
            (
                BinaryOperator::Mul,
                Self::UnsignedInteger32(left_val),
                Self::UnsignedInteger32(right_val),
            ) => Self::UnsignedInteger32(left_val * right_val),
            (
                BinaryOperator::Mul,
                Self::UnsignedInteger64(left_val),
                Self::UnsignedInteger64(right_val),
            ) => Self::UnsignedInteger64(left_val * right_val),
            (BinaryOperator::Mul, Self::Float32(left_val), Self::Float32(right_val)) => {
                Self::Float32(left_val * right_val)
            }
            (BinaryOperator::Mul, Self::Float64(left_val), Self::Float64(right_val)) => {
                Self::Float64(left_val * right_val)
            }
            (BinaryOperator::Div, Self::Integer8(left_val), Self::Integer8(right_val)) => {
                Self::Integer8(left_val / right_val)
            }
            (BinaryOperator::Div, Self::Integer16(left_val), Self::Integer16(right_val)) => {
                Self::Integer16(left_val / right_val)
            }
            (BinaryOperator::Div, Self::Integer32(left_val), Self::Integer32(right_val)) => {
                Self::Integer32(left_val / right_val)
            }
            (BinaryOperator::Div, Self::Integer64(left_val), Self::Integer64(right_val)) => {
                Self::Integer64(left_val / right_val)
            }
            (
                BinaryOperator::Div,
                Self::UnsignedInteger8(left_val),
                Self::UnsignedInteger8(right_val),
            ) => Self::UnsignedInteger8(left_val / right_val),
            (
                BinaryOperator::Div,
                Self::UnsignedInteger16(left_val),
                Self::UnsignedInteger16(right_val),
            ) => Self::UnsignedInteger16(left_val / right_val),
            (
                BinaryOperator::Div,
                Self::UnsignedInteger32(left_val),
                Self::UnsignedInteger32(right_val),
            ) => Self::UnsignedInteger32(left_val / right_val),
            (
                BinaryOperator::Div,
                Self::UnsignedInteger64(left_val),
                Self::UnsignedInteger64(right_val),
            ) => Self::UnsignedInteger64(left_val / right_val),
            (BinaryOperator::Div, Self::Float32(left_val), Self::Float32(right_val)) => {
                Self::Float32(left_val / right_val)
            }
            (BinaryOperator::Div, Self::Float64(left_val), Self::Float64(right_val)) => {
                Self::Float64(left_val / right_val)
            }
            (BinaryOperator::Rem, Self::Integer8(left_val), Self::Integer8(right_val)) => {
                Self::Integer8(left_val % right_val)
            }
            (BinaryOperator::Rem, Self::Integer16(left_val), Self::Integer16(right_val)) => {
                Self::Integer16(left_val % right_val)
            }
            (BinaryOperator::Rem, Self::Integer32(left_val), Self::Integer32(right_val)) => {
                Self::Integer32(left_val % right_val)
            }
            (BinaryOperator::Rem, Self::Integer64(left_val), Self::Integer64(right_val)) => {
                Self::Integer64(left_val % right_val)
            }
            (
                BinaryOperator::Rem,
                Self::UnsignedInteger8(left_val),
                Self::UnsignedInteger8(right_val),
            ) => Self::UnsignedInteger8(left_val % right_val),
            (
                BinaryOperator::Rem,
                Self::UnsignedInteger16(left_val),
                Self::UnsignedInteger16(right_val),
            ) => Self::UnsignedInteger16(left_val % right_val),
            (
                BinaryOperator::Rem,
                Self::UnsignedInteger32(left_val),
                Self::UnsignedInteger32(right_val),
            ) => Self::UnsignedInteger32(left_val % right_val),
            (
                BinaryOperator::Rem,
                Self::UnsignedInteger64(left_val),
                Self::UnsignedInteger64(right_val),
            ) => Self::UnsignedInteger64(left_val % right_val),
            (BinaryOperator::Rem, Self::Float32(left_val), Self::Float32(right_val)) => {
                Self::Float32(left_val % right_val)
            }
            (BinaryOperator::Rem, Self::Float64(left_val), Self::Float64(right_val)) => {
                Self::Float64(left_val % right_val)
            }
            (BinaryOperator::LT, Self::Integer8(left_val), Self::Integer8(right_val)) => {
                Self::Boolean(left_val < right_val)
            }
            (BinaryOperator::LT, Self::Integer16(left_val), Self::Integer16(right_val)) => {
                Self::Boolean(left_val < right_val)
            }
            (BinaryOperator::LT, Self::Integer32(left_val), Self::Integer32(right_val)) => {
                Self::Boolean(left_val < right_val)
            }
            (BinaryOperator::LT, Self::Integer64(left_val), Self::Integer64(right_val)) => {
                Self::Boolean(left_val < right_val)
            }
            (
                BinaryOperator::LT,
                Self::UnsignedInteger8(left_val),
                Self::UnsignedInteger8(right_val),
            ) => Self::Boolean(left_val < right_val),
            (
                BinaryOperator::LT,
                Self::UnsignedInteger16(left_val),
                Self::UnsignedInteger16(right_val),
            ) => Self::Boolean(left_val < right_val),
            (
                BinaryOperator::LT,
                Self::UnsignedInteger32(left_val),
                Self::UnsignedInteger32(right_val),
            ) => Self::Boolean(left_val < right_val),
            (
                BinaryOperator::LT,
                Self::UnsignedInteger64(left_val),
                Self::UnsignedInteger64(right_val),
            ) => Self::Boolean(left_val < right_val),
            (BinaryOperator::LT, Self::Float32(left_val), Self::Float32(right_val)) => {
                Self::Boolean(*left_val < *right_val)
            }
            (BinaryOperator::LT, Self::Float64(left_val), Self::Float64(right_val)) => {
                Self::Boolean(*left_val < *right_val)
            }
            (BinaryOperator::LTE, Self::Integer8(left_val), Self::Integer8(right_val)) => {
                Self::Boolean(left_val <= right_val)
            }
            (BinaryOperator::LTE, Self::Integer16(left_val), Self::Integer16(right_val)) => {
                Self::Boolean(left_val <= right_val)
            }
            (BinaryOperator::LTE, Self::Integer32(left_val), Self::Integer32(right_val)) => {
                Self::Boolean(left_val <= right_val)
            }
            (BinaryOperator::LTE, Self::Integer64(left_val), Self::Integer64(right_val)) => {
                Self::Boolean(left_val <= right_val)
            }
            (
                BinaryOperator::LTE,
                Self::UnsignedInteger8(left_val),
                Self::UnsignedInteger8(right_val),
            ) => Self::Boolean(left_val <= right_val),
            (
                BinaryOperator::LTE,
                Self::UnsignedInteger16(left_val),
                Self::UnsignedInteger16(right_val),
            ) => Self::Boolean(left_val <= right_val),
            (
                BinaryOperator::LTE,
                Self::UnsignedInteger32(left_val),
                Self::UnsignedInteger32(right_val),
            ) => Self::Boolean(left_val <= right_val),
            (
                BinaryOperator::LTE,
                Self::UnsignedInteger64(left_val),
                Self::UnsignedInteger64(right_val),
            ) => Self::Boolean(left_val <= right_val),
            (BinaryOperator::LTE, Self::Float32(left_val), Self::Float32(right_val)) => {
                Self::Boolean(*left_val <= *right_val)
            }
            (BinaryOperator::LTE, Self::Float64(left_val), Self::Float64(right_val)) => {
                Self::Boolean(*left_val <= *right_val)
            }
            (BinaryOperator::GT, Self::Integer8(left_val), Self::Integer8(right_val)) => {
                Self::Boolean(left_val > right_val)
            }
            (BinaryOperator::GT, Self::Integer16(left_val), Self::Integer16(right_val)) => {
                Self::Boolean(left_val > right_val)
            }
            (BinaryOperator::GT, Self::Integer32(left_val), Self::Integer32(right_val)) => {
                Self::Boolean(left_val > right_val)
            }
            (BinaryOperator::GT, Self::Integer64(left_val), Self::Integer64(right_val)) => {
                Self::Boolean(left_val > right_val)
            }
            (
                BinaryOperator::GT,
                Self::UnsignedInteger8(left_val),
                Self::UnsignedInteger8(right_val),
            ) => Self::Boolean(left_val > right_val),
            (
                BinaryOperator::GT,
                Self::UnsignedInteger16(left_val),
                Self::UnsignedInteger16(right_val),
            ) => Self::Boolean(left_val > right_val),
            (
                BinaryOperator::GT,
                Self::UnsignedInteger32(left_val),
                Self::UnsignedInteger32(right_val),
            ) => Self::Boolean(left_val > right_val),
            (
                BinaryOperator::GT,
                Self::UnsignedInteger64(left_val),
                Self::UnsignedInteger64(right_val),
            ) => Self::Boolean(left_val > right_val),
            (BinaryOperator::GT, Self::Float32(left_val), Self::Float32(right_val)) => {
                Self::Boolean(*left_val > *right_val)
            }
            (BinaryOperator::GT, Self::Float64(left_val), Self::Float64(right_val)) => {
                Self::Boolean(*left_val > *right_val)
            }
            (BinaryOperator::GTE, Self::Integer8(left_val), Self::Integer8(right_val)) => {
                Self::Boolean(left_val >= right_val)
            }
            (BinaryOperator::GTE, Self::Integer16(left_val), Self::Integer16(right_val)) => {
                Self::Boolean(left_val >= right_val)
            }
            (BinaryOperator::GTE, Self::Integer32(left_val), Self::Integer32(right_val)) => {
                Self::Boolean(left_val >= right_val)
            }
            (BinaryOperator::GTE, Self::Integer64(left_val), Self::Integer64(right_val)) => {
                Self::Boolean(left_val >= right_val)
            }
            (
                BinaryOperator::GTE,
                Self::UnsignedInteger8(left_val),
                Self::UnsignedInteger8(right_val),
            ) => Self::Boolean(left_val >= right_val),
            (
                BinaryOperator::GTE,
                Self::UnsignedInteger16(left_val),
                Self::UnsignedInteger16(right_val),
            ) => Self::Boolean(left_val >= right_val),
            (
                BinaryOperator::GTE,
                Self::UnsignedInteger32(left_val),
                Self::UnsignedInteger32(right_val),
            ) => Self::Boolean(left_val >= right_val),
            (
                BinaryOperator::GTE,
                Self::UnsignedInteger64(left_val),
                Self::UnsignedInteger64(right_val),
            ) => Self::Boolean(left_val >= right_val),
            (BinaryOperator::GTE, Self::Float32(left_val), Self::Float32(right_val)) => {
                Self::Boolean(*left_val >= *right_val)
            }
            (BinaryOperator::GTE, Self::Float64(left_val), Self::Float64(right_val)) => {
                Self::Boolean(*left_val >= *right_val)
            }
            // EQ and NE can be implemented more easily, since we don't
            // need to unpack the left and right values.
            (BinaryOperator::EQ, left_val, right_val) => Self::Boolean(left_val == right_val),
            (BinaryOperator::NE, left_val, right_val) => Self::Boolean(left_val != right_val),
            (BinaryOperator::Or, Self::Boolean(left_val), Self::Boolean(right_val)) => {
                Self::Boolean(left_val || right_val)
            }
            (BinaryOperator::Or, Self::Integer8(left_val), Self::Integer8(right_val)) => {
                Self::Integer8(left_val | right_val)
            }
            (BinaryOperator::Or, Self::Integer16(left_val), Self::Integer16(right_val)) => {
                Self::Integer16(left_val | right_val)
            }
            (BinaryOperator::Or, Self::Integer32(left_val), Self::Integer32(right_val)) => {
                Self::Integer32(left_val | right_val)
            }
            (BinaryOperator::Or, Self::Integer64(left_val), Self::Integer64(right_val)) => {
                Self::Integer64(left_val | right_val)
            }
            (
                BinaryOperator::Or,
                Self::UnsignedInteger8(left_val),
                Self::UnsignedInteger8(right_val),
            ) => Self::UnsignedInteger8(left_val | right_val),
            (
                BinaryOperator::Or,
                Self::UnsignedInteger16(left_val),
                Self::UnsignedInteger16(right_val),
            ) => Self::UnsignedInteger16(left_val | right_val),
            (
                BinaryOperator::Or,
                Self::UnsignedInteger32(left_val),
                Self::UnsignedInteger32(right_val),
            ) => Self::UnsignedInteger32(left_val | right_val),
            (
                BinaryOperator::Or,
                Self::UnsignedInteger64(left_val),
                Self::UnsignedInteger64(right_val),
            ) => Self::UnsignedInteger64(left_val | right_val),
            (BinaryOperator::And, Self::Boolean(left_val), Self::Boolean(right_val)) => {
                Self::Boolean(left_val && right_val)
            }
            (BinaryOperator::And, Self::Integer8(left_val), Self::Integer8(right_val)) => {
                Self::Integer8(left_val & right_val)
            }
            (BinaryOperator::And, Self::Integer16(left_val), Self::Integer16(right_val)) => {
                Self::Integer16(left_val & right_val)
            }
            (BinaryOperator::And, Self::Integer32(left_val), Self::Integer32(right_val)) => {
                Self::Integer32(left_val & right_val)
            }
            (BinaryOperator::And, Self::Integer64(left_val), Self::Integer64(right_val)) => {
                Self::Integer64(left_val & right_val)
            }
            (
                BinaryOperator::And,
                Self::UnsignedInteger8(left_val),
                Self::UnsignedInteger8(right_val),
            ) => Self::UnsignedInteger8(left_val & right_val),
            (
                BinaryOperator::And,
                Self::UnsignedInteger16(left_val),
                Self::UnsignedInteger16(right_val),
            ) => Self::UnsignedInteger16(left_val & right_val),
            (
                BinaryOperator::And,
                Self::UnsignedInteger32(left_val),
                Self::UnsignedInteger32(right_val),
            ) => Self::UnsignedInteger32(left_val & right_val),
            (
                BinaryOperator::And,
                Self::UnsignedInteger64(left_val),
                Self::UnsignedInteger64(right_val),
            ) => Self::UnsignedInteger64(left_val & right_val),
            (BinaryOperator::Xor, Self::Boolean(left_val), Self::Boolean(right_val)) => {
                Self::Boolean(left_val ^ right_val)
            }
            (BinaryOperator::Xor, Self::Integer8(left_val), Self::Integer8(right_val)) => {
                Self::Integer8(left_val ^ right_val)
            }
            (BinaryOperator::Xor, Self::Integer16(left_val), Self::Integer16(right_val)) => {
                Self::Integer16(left_val ^ right_val)
            }
            (BinaryOperator::Xor, Self::Integer32(left_val), Self::Integer32(right_val)) => {
                Self::Integer32(left_val ^ right_val)
            }
            (BinaryOperator::Xor, Self::Integer64(left_val), Self::Integer64(right_val)) => {
                Self::Integer64(left_val ^ right_val)
            }
            (
                BinaryOperator::Xor,
                Self::UnsignedInteger8(left_val),
                Self::UnsignedInteger8(right_val),
            ) => Self::UnsignedInteger8(left_val ^ right_val),
            (
                BinaryOperator::Xor,
                Self::UnsignedInteger16(left_val),
                Self::UnsignedInteger16(right_val),
            ) => Self::UnsignedInteger16(left_val ^ right_val),
            (
                BinaryOperator::Xor,
                Self::UnsignedInteger32(left_val),
                Self::UnsignedInteger32(right_val),
            ) => Self::UnsignedInteger32(left_val ^ right_val),
            (
                BinaryOperator::Xor,
                Self::UnsignedInteger64(left_val),
                Self::UnsignedInteger64(right_val),
            ) => Self::UnsignedInteger64(left_val ^ right_val),
            (BinaryOperator::LSh, Self::Integer8(left_val), Self::Integer8(right_val)) => {
                Self::Integer8(left_val << right_val)
            }
            (BinaryOperator::LSh, Self::Integer16(left_val), Self::Integer16(right_val)) => {
                Self::Integer16(left_val << right_val)
            }
            (BinaryOperator::LSh, Self::Integer32(left_val), Self::Integer32(right_val)) => {
                Self::Integer32(left_val << right_val)
            }
            (BinaryOperator::LSh, Self::Integer64(left_val), Self::Integer64(right_val)) => {
                Self::Integer64(left_val << right_val)
            }
            (
                BinaryOperator::LSh,
                Self::UnsignedInteger8(left_val),
                Self::UnsignedInteger8(right_val),
            ) => Self::UnsignedInteger8(left_val << right_val),
            (
                BinaryOperator::LSh,
                Self::UnsignedInteger16(left_val),
                Self::UnsignedInteger16(right_val),
            ) => Self::UnsignedInteger16(left_val << right_val),
            (
                BinaryOperator::LSh,
                Self::UnsignedInteger32(left_val),
                Self::UnsignedInteger32(right_val),
            ) => Self::UnsignedInteger32(left_val << right_val),
            (
                BinaryOperator::LSh,
                Self::UnsignedInteger64(left_val),
                Self::UnsignedInteger64(right_val),
            ) => Self::UnsignedInteger64(left_val << right_val),
            (BinaryOperator::RSh, Self::Integer8(left_val), Self::Integer8(right_val)) => {
                Self::Integer8(left_val >> right_val)
            }
            (BinaryOperator::RSh, Self::Integer16(left_val), Self::Integer16(right_val)) => {
                Self::Integer16(left_val >> right_val)
            }
            (BinaryOperator::RSh, Self::Integer32(left_val), Self::Integer32(right_val)) => {
                Self::Integer32(left_val >> right_val)
            }
            (BinaryOperator::RSh, Self::Integer64(left_val), Self::Integer64(right_val)) => {
                Self::Integer64(left_val >> right_val)
            }
            (
                BinaryOperator::RSh,
                Self::UnsignedInteger8(left_val),
                Self::UnsignedInteger8(right_val),
            ) => Self::UnsignedInteger8(left_val >> right_val),
            (
                BinaryOperator::RSh,
                Self::UnsignedInteger16(left_val),
                Self::UnsignedInteger16(right_val),
            ) => Self::UnsignedInteger16(left_val >> right_val),
            (
                BinaryOperator::RSh,
                Self::UnsignedInteger32(left_val),
                Self::UnsignedInteger32(right_val),
            ) => Self::UnsignedInteger32(left_val >> right_val),
            (
                BinaryOperator::RSh,
                Self::UnsignedInteger64(left_val),
                Self::UnsignedInteger64(right_val),
            ) => Self::UnsignedInteger64(left_val >> right_val),
            _ => {
                println!("{:?}, {:?}", left, right);
                panic!("Unsupported combination of binary operation and constant values. Did typechecking succeed?")
            }
        }
    }

    pub fn unary_op(op: UnaryOperator, val: InterpreterVal) -> Self {
        match (op, val) {
            (UnaryOperator::Not, Self::Boolean(val)) => Self::Boolean(!val),
            (UnaryOperator::Not, Self::Integer8(val)) => Self::Integer8(!val),
            (UnaryOperator::Not, Self::Integer16(val)) => Self::Integer16(!val),
            (UnaryOperator::Not, Self::Integer32(val)) => Self::Integer32(!val),
            (UnaryOperator::Not, Self::Integer64(val)) => Self::Integer64(!val),
            (UnaryOperator::Neg, Self::Integer8(val)) => Self::Integer8(-val),
            (UnaryOperator::Neg, Self::Integer16(val)) => Self::Integer16(-val),
            (UnaryOperator::Neg, Self::Integer32(val)) => Self::Integer32(-val),
            (UnaryOperator::Neg, Self::Integer64(val)) => Self::Integer64(-val),
            (UnaryOperator::Neg, Self::Float32(val)) => Self::Float32(-val),
            (UnaryOperator::Neg, Self::Float64(val)) => Self::Float64(-val),
            (UnaryOperator::Cast(_), _) => todo!("Write cast impl"),
            _ => panic!("Unsupported combination of unary operation and constant value. Did typechecking succeed?")
        }
    }

    pub fn as_usize(&self) -> usize {
        match *self {
            InterpreterVal::Boolean(v) => v.try_into().unwrap(),
            InterpreterVal::Integer8(v) => v.try_into().unwrap(),
            InterpreterVal::Integer16(v) => v.try_into().unwrap(),
            InterpreterVal::Integer32(v) => v.try_into().unwrap(),
            InterpreterVal::Integer64(v) => v.try_into().unwrap(),
            InterpreterVal::UnsignedInteger8(v) => v.try_into().unwrap(),
            InterpreterVal::UnsignedInteger16(v) => v.try_into().unwrap(),
            InterpreterVal::UnsignedInteger32(v) => v.try_into().unwrap(),
            InterpreterVal::UnsignedInteger64(v) => v.try_into().unwrap(),
            InterpreterVal::DynamicConstant(v) => v,
            InterpreterVal::ThreadID(v) => v,
            _ => panic!("PANIC: Value not castable to usize"),
        }
    }

    // Defines row major / how we layout our arrays
    pub fn array_idx(extents: &[usize], indices: &[usize]) -> usize {
        let a = extents
            .into_iter()
            .enumerate()
            .map(|(i, n)| {
                extents[i + 1..]
                    .into_iter()
                    .cloned()
                    .reduce(|a, b| a * b)
                    .unwrap_or(1)
                    * indices[i]
            })
            .sum();
        a
    }

    pub fn array_size(extents: &[usize]) -> usize {
        extents
            .into_iter()
            .cloned()
            .reduce(|a, b| a * b)
            .unwrap_or(1)
    }
}

mod tests {
    use super::*;

    #[test]
    fn test_array_idx() {
        let extents = [10, 5];
        let indices = [1, 1];
        let idx = InterpreterVal::array_idx(&extents, &indices);
        assert_eq!(idx, 6)
    }
}