use crate::parser::types::Value;
use super::Interpreter;
#[derive(Debug, Clone, Copy)]
enum Num {
I(i32),
L(i64),
F(f64),
}
impl Interpreter {
fn unify_numeric(a: Value, b: Value) -> Result<(Num, Num), String> {
let rank_a = Interpreter::numeric_rank(&a);
let rank_b = Interpreter::numeric_rank(&b);
if rank_a == 0 || rank_b == 0 {
return Err("Not numeric".to_string());
}
let top = std::cmp::max(rank_a, rank_b);
let na = Interpreter::coerce_value(a, top)?;
let nb = Interpreter::coerce_value(b, top)?;
Ok((na, nb))
}
fn numeric_rank(v: &Value) -> i32 {
match v {
Value::Int(_) => 1,
Value::Long(_) => 2,
Value::Float(_) => 3,
_ => 0,
}
}
fn coerce_value(v: Value, rank: i32) -> Result<Num, String> {
match v {
Value::Int(i) => match rank {
3 => Ok(Num::F(i as f64)),
2 => Ok(Num::L(i as i64)),
1 => Ok(Num::I(i)),
_ => Err("coerce => invalid rank".to_string()),
},
Value::Long(l) => match rank {
3 => Ok(Num::F(l as f64)),
2 => Ok(Num::L(l)),
1 => {
if l < i64::from(std::i32::MIN) || l > i64::from(std::i32::MAX) {
return Err("Long->Int overflow".to_string());
}
Ok(Num::I(l as i32))
}
_ => Err("coerce => invalid rank".to_string()),
},
Value::Float(ff) => match rank {
3 => Ok(Num::F(ff)),
2 => Ok(Num::L(ff as i64)),
1 => Ok(Num::I(ff as i32)),
_ => Err("coerce_value => invalid rank".to_string()),
},
_ => Err("coerce_value => not numeric".to_string()),
}
}
pub fn eval_gt(&self, lv: Value, rv: Value) -> Result<Value, String> {
let (lt, rt) = Interpreter::unify_numeric(lv, rv)?;
let res = match (lt, rt) {
(Num::I(a), Num::I(b)) => a > b,
(Num::L(a), Num::L(b)) => a > b,
(Num::F(a), Num::F(b)) => a > b,
_ => unreachable!("unify_numeric => same rank => no cross combos"),
};
Ok(Value::Bool(res))
}
pub fn eval_lt(&self, lv: Value, rv: Value) -> Result<Value, String> {
let (lt, rt) = Interpreter::unify_numeric(lv, rv)?;
let res = match (lt, rt) {
(Num::I(a), Num::I(b)) => a < b,
(Num::L(a), Num::L(b)) => a < b,
(Num::F(a), Num::F(b)) => a < b,
_ => unreachable!("unify_numeric => same rank => no cross combos"),
};
Ok(Value::Bool(res))
}
pub fn eval_le(&self, lv: Value, rv: Value) -> Result<Value, String> {
let (lt, rt) = Interpreter::unify_numeric(lv, rv)?;
let res = match (lt, rt) {
(Num::I(a), Num::I(b)) => a <= b,
(Num::L(a), Num::L(b)) => a <= b,
(Num::F(a), Num::F(b)) => a <= b,
_ => unreachable!("unify_numeric => same rank => no cross combos"),
};
Ok(Value::Bool(res))
}
pub fn eval_ge(&self, lv: Value, rv: Value) -> Result<Value, String> {
let (lt, rt) = Interpreter::unify_numeric(lv, rv)?;
let res = match (lt, rt) {
(Num::I(a), Num::I(b)) => a >= b,
(Num::L(a), Num::L(b)) => a >= b,
(Num::F(a), Num::F(b)) => a >= b,
_ => unreachable!("unify_numeric => same rank => no cross combos"),
};
Ok(Value::Bool(res))
}
pub fn eval_eqeq(&self, lv: Value, rv: Value) -> Result<Value, String> {
if let Ok((lt, rt)) = Interpreter::unify_numeric(lv.clone(), rv.clone()) {
return match (lt, rt) {
(Num::I(a), Num::I(b)) => Ok(Value::Bool(a == b)),
(Num::L(a), Num::L(b)) => Ok(Value::Bool(a == b)),
(Num::F(a), Num::F(b)) => Ok(Value::Bool(a == b)),
_ => unreachable!("unify_numeric => same rank => no cross combos"),
};
}
Ok(Value::Bool(lv == rv))
}
pub fn eval_ne(&self, lv: Value, rv: Value) -> Result<Value, String> {
if let Ok((lt, rt)) = Interpreter::unify_numeric(lv.clone(), rv.clone()) {
return match (lt, rt) {
(Num::I(a), Num::I(b)) => Ok(Value::Bool(a != b)),
(Num::L(a), Num::L(b)) => Ok(Value::Bool(a != b)),
(Num::F(a), Num::F(b)) => Ok(Value::Bool((a - b).abs() > std::f64::EPSILON)),
_ => unreachable!("unify_numeric => same rank => no cross combos"),
};
}
Ok(Value::Bool(lv != rv))
}
pub fn eval_add(&self, lv: Value, rv: Value) -> Result<Value, String> {
let (lt, rt) = Interpreter::unify_numeric(lv, rv)?;
match (lt, rt) {
(Num::I(a), Num::I(b)) => {
a.checked_add(b).map(Value::Int).ok_or_else(|| "Overflow in i32 add".to_string())
}
(Num::L(a), Num::L(b)) => {
a.checked_add(b).map(Value::Long).ok_or_else(|| "Overflow in i64 add".to_string())
}
(Num::F(a), Num::F(b)) => Ok(Value::Float(a + b)),
_ => unreachable!("unify_numeric => same rank => no cross combos"),
}
}
pub fn eval_sub(&self, lv: Value, rv: Value) -> Result<Value, String> {
let (lt, rt) = Interpreter::unify_numeric(lv, rv)?;
match (lt, rt) {
(Num::I(a), Num::I(b)) => {
a.checked_sub(b).map(Value::Int).ok_or_else(|| "Overflow in i32 sub".to_string())
}
(Num::L(a), Num::L(b)) => {
a.checked_sub(b).map(Value::Long).ok_or_else(|| "Overflow in i64 sub".to_string())
}
(Num::F(a), Num::F(b)) => Ok(Value::Float(a - b)),
_ => unreachable!("unify_numeric => same rank => no cross combos"),
}
}
pub fn eval_mul(&self, lv: Value, rv: Value) -> Result<Value, String> {
let (lt, rt) = Interpreter::unify_numeric(lv, rv)?;
match (lt, rt) {
(Num::I(a), Num::I(b)) => {
a.checked_mul(b).map(Value::Int).ok_or_else(|| "Overflow in i32 mul".to_string())
}
(Num::L(a), Num::L(b)) => {
a.checked_mul(b).map(Value::Long).ok_or_else(|| "Overflow in i64 mul".to_string())
}
(Num::F(a), Num::F(b)) => Ok(Value::Float(a * b)),
_ => unreachable!("unify_numeric => same rank => no cross combos"),
}
}
pub fn eval_div(&self, lv: Value, rv: Value) -> Result<Value, String> {
let (lt, rt) = Interpreter::unify_numeric(lv, rv)?;
match (lt, rt) {
(Num::I(_), Num::I(0)) => Err("Division by zero".to_string()),
(Num::L(_), Num::L(0)) => Err("Division by zero".to_string()),
(Num::F(_), Num::F(bf)) if bf == 0.0 => Err("Division by zero".to_string()),
(Num::I(a), Num::I(b)) => Ok(Value::Int(a / b)),
(Num::L(a), Num::L(b)) => Ok(Value::Long(a / b)),
(Num::F(a), Num::F(b)) => Ok(Value::Float(a / b)),
_ => unreachable!("unify_numeric => same rank => no cross combos"),
}
}
pub fn eval_mod(&self, lv: Value, rv: Value) -> Result<Value, String> {
let (lt, rt) = Interpreter::unify_numeric(lv, rv)?;
match (lt, rt) {
(Num::I(_), Num::I(0)) => Err("Mod by zero".to_string()),
(Num::L(_), Num::L(0)) => Err("Mod by zero".to_string()),
(Num::F(_), Num::F(bf)) if bf == 0.0 => Err("Mod by zero".to_string()),
(Num::I(a), Num::I(b)) => Ok(Value::Int(a % b)),
(Num::L(a), Num::L(b)) => Ok(Value::Long(a % b)),
(Num::F(a), Num::F(b)) => Ok(Value::Float(a % b)),
_ => unreachable!("unify_numeric => same rank => no cross combos"),
}
}
pub fn eval_bitand(&self, lv: Value, rv: Value) -> Result<Value, String> {
let (lt, rt) = Interpreter::unify_numeric(lv, rv)?;
match (lt, rt) {
(Num::I(a), Num::I(b)) => Ok(Value::Int(a & b)),
(Num::L(a), Num::L(b)) => Ok(Value::Long(a & b)),
_ => Err("bitwise & is not defined for floats".to_string()),
}
}
pub fn eval_shl(&self, lv: Value, rv: Value) -> Result<Value, String> {
let (lt, rt) = Interpreter::unify_numeric(lv, rv)?;
match (lt, rt) {
(Num::I(a), Num::I(b)) => {
if b < 0 || b >= 32 {
return Err(format!("invalid shift amount: {}", b));
}
Ok(Value::Int(a << b))
}
(Num::L(a), Num::L(b)) => {
if b < 0 || b >= 64 {
return Err(format!("invalid shift amount: {}", b));
}
Ok(Value::Long(a << b))
}
_ => Err("Cannot shift a float or shift by float".to_string()),
}
}
pub fn eval_shr(&self, lv: Value, rv: Value) -> Result<Value, String> {
let (lt, rt) = Interpreter::unify_numeric(lv, rv)?;
match (lt, rt) {
(Num::I(a), Num::I(b)) => {
if b < 0 || b >= 32 {
return Err(format!("invalid shift amount: {}", b));
}
Ok(Value::Int((a as u32 >> b) as i32))
}
(Num::L(a), Num::L(b)) => {
if b < 0 || b >= 64 {
return Err(format!("invalid shift amount: {}", b));
}
Ok(Value::Long((a as u64 >> b) as i64))
}
_ => Err("Cannot shift a float or shift by float".to_string()),
}
}
pub fn eval_bitxor(&self, lv: Value, rv: Value) -> Result<Value, String> {
let (lt, rt) = Interpreter::unify_numeric(lv, rv)?;
match (lt, rt) {
(Num::I(a), Num::I(b)) => Ok(Value::Int(a ^ b)),
(Num::L(a), Num::L(b)) => Ok(Value::Long(a ^ b)),
_ => Err("bitwise ^ is not defined for floats".to_string()),
}
}
pub fn eval_bittilde(&self, lv: Value, rv: Value) -> Result<Value, String> {
let (lt, rt) = Interpreter::unify_numeric(lv, rv)?;
match (lt, rt) {
(Num::I(a), Num::I(b)) => {
let nand = !(a & b);
Ok(Value::Int(nand))
}
(Num::L(a), Num::L(b)) => {
let nand = !(a & b);
Ok(Value::Long(nand))
}
_ => Err("bitwise ~ is not defined for floats".to_string()),
}
}
pub fn eval_bitor(&self, lv: Value, rv: Value) -> Result<Value, String> {
let (lt, rt) = Interpreter::unify_numeric(lv, rv)?;
match (lt, rt) {
(Num::I(a), Num::I(b)) => Ok(Value::Int(a | b)),
(Num::L(a), Num::L(b)) => Ok(Value::Long(a | b)),
_ => Err("bitwise | is not defined for floats".to_string()),
}
}
}