basic-lang 0.7.0

The BASIC programming language as it was in the 8-bit era.
Documentation
use super::Val;
use crate::error;
use crate::lang::Error;
use std::convert::TryFrom;

type Result<T> = std::result::Result<T, Error>;

pub struct Operation {}

impl Operation {
    pub fn negate(val: Val) -> Result<Val> {
        use Val::*;
        match val {
            Integer(n) => Ok(Integer(-n)),
            Single(n) => Ok(Single(-n)),
            Double(n) => Ok(Double(-n)),
            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
        }
    }

    pub fn power(lhs: Val, rhs: Val) -> Result<Val> {
        use Val::*;
        match lhs {
            Integer(l) => match rhs {
                Integer(r) if r >= 0 => match l.checked_pow(r as u32) {
                    Some(i) => Ok(Integer(i)),
                    None => Err(error!(Overflow)),
                },
                Integer(r) => Ok(Single((l as f32).powi(r as i32))),
                Single(r) => Ok(Single((l as f32).powf(r))),
                Double(r) => Ok(Double((l as f64).powf(r))),
                _ => Err(error!(TypeMismatch)),
            },
            Single(l) => match rhs {
                Integer(r) => Ok(Single(l.powi(r as i32))),
                Single(r) => Ok(Single(l.powf(r))),
                Double(r) => Ok(Double((l as f64).powf(r))),
                _ => Err(error!(TypeMismatch)),
            },
            Double(l) => match rhs {
                Integer(r) => Ok(Double(l.powi(r as i32))),
                Single(r) => Ok(Double(l.powf(r as f64))),
                Double(r) => Ok(Double(l.powf(r))),
                _ => Err(error!(TypeMismatch)),
            },
            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
        }
    }

    pub fn multiply(lhs: Val, rhs: Val) -> Result<Val> {
        use Val::*;
        match lhs {
            Integer(l) => match rhs {
                Integer(r) => match l.checked_mul(r) {
                    Some(i) => Ok(Integer(i)),
                    None => Err(error!(Overflow)),
                },
                Single(r) => Ok(Single(l as f32 * r)),
                Double(r) => Ok(Double(l as f64 * r)),
                _ => Err(error!(TypeMismatch)),
            },
            Single(l) => match rhs {
                Integer(r) => Ok(Single(l * r as f32)),
                Single(r) => Ok(Single(l * r)),
                Double(r) => Ok(Double(l as f64 * r)),
                _ => Err(error!(TypeMismatch)),
            },
            Double(l) => match rhs {
                Integer(r) => Ok(Double(l * r as f64)),
                Single(r) => Ok(Double(l * r as f64)),
                Double(r) => Ok(Double(l * r)),
                _ => Err(error!(TypeMismatch)),
            },
            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
        }
    }

    pub fn divide(lhs: Val, rhs: Val) -> Result<Val> {
        use Val::*;
        match lhs {
            Integer(l) => match rhs {
                Integer(r) => Ok(Single(l as f32 / r as f32)),
                Single(r) => Ok(Single(l as f32 / r)),
                Double(r) => Ok(Double(l as f64 / r)),
                _ => Err(error!(TypeMismatch)),
            },
            Single(l) => match rhs {
                Integer(r) => Ok(Single(l / r as f32)),
                Single(r) => Ok(Single(l / r)),
                Double(r) => Ok(Double(l as f64 / r)),
                _ => Err(error!(TypeMismatch)),
            },
            Double(l) => match rhs {
                Integer(r) => Ok(Double(l / r as f64)),
                Single(r) => Ok(Double(l / r as f64)),
                Double(r) => Ok(Double(l / r)),
                _ => Err(error!(TypeMismatch)),
            },
            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
        }
    }

    pub fn divint(lhs: Val, rhs: Val) -> Result<Val> {
        let lhs = i16::try_from(lhs)?;
        let rhs = i16::try_from(rhs)?;
        match lhs.checked_div(rhs) {
            Some(n) => Ok(Val::Integer(n)),
            None => Err(error!(DivisionByZero)),
        }
    }

    pub fn remainder(lhs: Val, rhs: Val) -> Result<Val> {
        let lhs = i16::try_from(lhs)?;
        let rhs = i16::try_from(rhs)?;
        match lhs.checked_rem(rhs) {
            Some(n) => Ok(Val::Integer(n)),
            None => Err(error!(DivisionByZero)),
        }
    }

    pub fn sum(lhs: Val, rhs: Val) -> Result<Val> {
        use Val::*;
        match lhs {
            String(l) => match rhs {
                String(r) => Ok(String((l.to_string() + &r).into())),
                _ => Err(error!(TypeMismatch)),
            },
            Integer(l) => match rhs {
                Integer(r) => match l.checked_add(r) {
                    Some(i) => Ok(Integer(i)),
                    None => Err(error!(Overflow)),
                },
                Single(r) => Ok(Single(l as f32 + r)),
                Double(r) => Ok(Double(l as f64 + r)),
                _ => Err(error!(TypeMismatch)),
            },
            Single(l) => match rhs {
                Integer(r) => Ok(Single(l + r as f32)),
                Single(r) => Ok(Single(l + r)),
                Double(r) => Ok(Double(l as f64 + r)),
                _ => Err(error!(TypeMismatch)),
            },
            Double(l) => match rhs {
                Integer(r) => Ok(Double(l + r as f64)),
                Single(r) => Ok(Double(l + r as f64)),
                Double(r) => Ok(Double(l + r)),
                _ => Err(error!(TypeMismatch)),
            },
            Return(_) | Next(_) => Err(error!(TypeMismatch)),
        }
    }

    pub fn subtract(lhs: Val, rhs: Val) -> Result<Val> {
        use Val::*;
        match lhs {
            Integer(l) => match rhs {
                Integer(r) => match l.checked_sub(r) {
                    Some(i) => Ok(Integer(i)),
                    None => Err(error!(Overflow)),
                },
                Single(r) => Ok(Single(l as f32 - r)),
                Double(r) => Ok(Double(l as f64 - r)),
                _ => Err(error!(TypeMismatch)),
            },
            Single(l) => match rhs {
                Integer(r) => Ok(Single(l - r as f32)),
                Single(r) => Ok(Single(l - r)),
                Double(r) => Ok(Double(l as f64 - r)),
                _ => Err(error!(TypeMismatch)),
            },
            Double(l) => match rhs {
                Integer(r) => Ok(Double(l - r as f64)),
                Single(r) => Ok(Double(l - r as f64)),
                Double(r) => Ok(Double(l - r)),
                _ => Err(error!(TypeMismatch)),
            },
            String(_) | Return(_) | Next(_) => Err(error!(TypeMismatch)),
        }
    }

    pub fn equal(lhs: Val, rhs: Val) -> Result<Val> {
        if Operation::equal_bool(lhs, rhs)? {
            Ok(Val::Integer(-1))
        } else {
            Ok(Val::Integer(0))
        }
    }

    pub fn not_equal(lhs: Val, rhs: Val) -> Result<Val> {
        if Operation::equal_bool(lhs, rhs)? {
            Ok(Val::Integer(0))
        } else {
            Ok(Val::Integer(-1))
        }
    }

    fn equal_bool(lhs: Val, rhs: Val) -> Result<bool> {
        use Val::*;
        match lhs {
            Integer(l) => match rhs {
                Integer(r) => Ok(l == r),
                Single(r) => Ok((l as f32 - r).abs() <= std::f32::EPSILON),
                Double(r) => Ok((l as f64 - r).abs() <= std::f64::EPSILON),
                _ => Err(error!(TypeMismatch)),
            },
            Single(l) => match rhs {
                Integer(r) => Ok((l - r as f32).abs() <= std::f32::EPSILON),
                Single(r) => Ok((l - r).abs() <= std::f32::EPSILON),
                Double(r) => Ok((l as f64 - r).abs() <= std::f64::EPSILON),
                _ => Err(error!(TypeMismatch)),
            },
            Double(l) => match rhs {
                Integer(r) => Ok((l - r as f64).abs() <= std::f64::EPSILON),
                Single(r) => Ok((l - r as f64).abs() <= std::f64::EPSILON),
                Double(r) => Ok((l - r).abs() <= std::f64::EPSILON),
                _ => Err(error!(TypeMismatch)),
            },
            String(l) => match rhs {
                String(r) => Ok(l == r),
                _ => Err(error!(TypeMismatch)),
            },
            Return(_) | Next(_) => Err(error!(TypeMismatch)),
        }
    }

    pub fn greater(lhs: Val, rhs: Val) -> Result<Val> {
        if Operation::less_bool(rhs, lhs)? {
            Ok(Val::Integer(-1))
        } else {
            Ok(Val::Integer(0))
        }
    }

    pub fn less(lhs: Val, rhs: Val) -> Result<Val> {
        if Operation::less_bool(lhs, rhs)? {
            Ok(Val::Integer(-1))
        } else {
            Ok(Val::Integer(0))
        }
    }

    pub fn less_bool(lhs: Val, rhs: Val) -> Result<bool> {
        use Val::*;
        match lhs {
            Integer(l) => match rhs {
                Integer(r) => Ok(l < r),
                Single(r) => Ok((l as f32) < r),
                Double(r) => Ok((l as f64) < r),
                _ => Err(error!(TypeMismatch)),
            },
            Single(l) => match rhs {
                Integer(r) => Ok(l < r as f32),
                Single(r) => Ok(l < r),
                Double(r) => Ok((l as f64) < r),
                _ => Err(error!(TypeMismatch)),
            },
            Double(l) => match rhs {
                Integer(r) => Ok(l < r as f64),
                Single(r) => Ok(l < r as f64),
                Double(r) => Ok(l < r),
                _ => Err(error!(TypeMismatch)),
            },
            String(l) => match rhs {
                String(r) => Ok(l < r),
                _ => Err(error!(TypeMismatch)),
            },
            Return(_) | Next(_) => Err(error!(TypeMismatch)),
        }
    }

    pub fn greater_equal(lhs: Val, rhs: Val) -> Result<Val> {
        if Operation::less_equal_bool(rhs, lhs)? {
            Ok(Val::Integer(-1))
        } else {
            Ok(Val::Integer(0))
        }
    }

    pub fn less_equal(lhs: Val, rhs: Val) -> Result<Val> {
        if Operation::less_equal_bool(lhs, rhs)? {
            Ok(Val::Integer(-1))
        } else {
            Ok(Val::Integer(0))
        }
    }

    pub fn less_equal_bool(lhs: Val, rhs: Val) -> Result<bool> {
        use Val::*;
        match lhs {
            Integer(l) => match rhs {
                Integer(r) => Ok(l <= r),
                Single(r) => Ok((l as f32) <= r),
                Double(r) => Ok((l as f64) <= r),
                _ => Err(error!(TypeMismatch)),
            },
            Single(l) => match rhs {
                Integer(r) => Ok(l <= r as f32),
                Single(r) => Ok(l <= r),
                Double(r) => Ok((l as f64) <= r),
                _ => Err(error!(TypeMismatch)),
            },
            Double(l) => match rhs {
                Integer(r) => Ok(l <= r as f64),
                Single(r) => Ok(l <= r as f64),
                Double(r) => Ok(l <= r),
                _ => Err(error!(TypeMismatch)),
            },
            String(l) => match rhs {
                String(r) => Ok(l <= r),
                _ => Err(error!(TypeMismatch)),
            },
            Return(_) | Next(_) => Err(error!(TypeMismatch)),
        }
    }

    pub fn and(lhs: Val, rhs: Val) -> Result<Val> {
        let lhs = i16::try_from(lhs)?;
        let rhs = i16::try_from(rhs)?;
        Ok(Val::Integer(lhs & rhs))
    }

    pub fn not(val: Val) -> Result<Val> {
        Ok(Val::Integer(!i16::try_from(val)?))
    }

    pub fn or(lhs: Val, rhs: Val) -> Result<Val> {
        let lhs = i16::try_from(lhs)?;
        let rhs = i16::try_from(rhs)?;
        Ok(Val::Integer(lhs | rhs))
    }

    pub fn xor(lhs: Val, rhs: Val) -> Result<Val> {
        let lhs = i16::try_from(lhs)?;
        let rhs = i16::try_from(rhs)?;
        Ok(Val::Integer(lhs ^ rhs))
    }

    pub fn imp(lhs: Val, rhs: Val) -> Result<Val> {
        let lhs = i16::try_from(lhs)?;
        let rhs = i16::try_from(rhs)?;
        Ok(Val::Integer(!lhs | rhs))
    }

    pub fn eqv(lhs: Val, rhs: Val) -> Result<Val> {
        let lhs = i16::try_from(lhs)?;
        let rhs = i16::try_from(rhs)?;
        Ok(Val::Integer(!(lhs ^ rhs)))
    }
}