## Coverage Report

#### Created: 2025-09-08 21:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /home/noah/src/ruchy/src/runtime/binary_ops.rs

    Line
    Count
    Source
    1
    //! Shared binary operation evaluation
    2
    //! Extracted to reduce duplication across interpreter and REPL
    3
    4
    use crate::frontend::ast::BinaryOp;
    5
    use crate::runtime::Value;
    6
    use anyhow::{Result, bail};
    7
    8
    /// Evaluate a binary operation on two values
    9
    ///
    10
    /// # Examples
    11
    ///
    12
    /// ```
    13
    /// use ruchy::runtime::binary_ops::evaluate_binary_op;
    14
    /// use ruchy::runtime::Value;
    15
    /// use ruchy::frontend::ast::BinaryOp;
    16
    ///
    17
    /// let lhs = Value::Int(5);
    18
    /// let rhs = Value::Int(3);
    19
    /// let result = evaluate_binary_op(&BinaryOp::Add, &lhs, &rhs).unwrap();
    20
    /// assert_eq!(result, Value::Int(8));
    21
    /// ```
    22
    0
    pub fn evaluate_binary_op(op: &BinaryOp, lhs: &Value, rhs: &Value) -> Result<Value> {
    23
    0
        match op {
    24
    0
            BinaryOp::Add => evaluate_add(lhs, rhs),
    25
    0
            BinaryOp::Subtract => evaluate_subtract(lhs, rhs),
    26
    0
            BinaryOp::Multiply => evaluate_multiply(lhs, rhs),
    27
    0
            BinaryOp::Divide => evaluate_divide(lhs, rhs),
    28
    0
            BinaryOp::Modulo => evaluate_modulo(lhs, rhs),
    29
    0
            BinaryOp::Power => evaluate_power(lhs, rhs),
    30
    31
    0
            BinaryOp::Equal => Ok(Value::Bool(values_equal(lhs, rhs))),
    32
    0
            BinaryOp::NotEqual => Ok(Value::Bool(!values_equal(lhs, rhs))),
    33
    0
            BinaryOp::Less => evaluate_less(lhs, rhs),
    34
    0
            BinaryOp::LessEqual => evaluate_less_equal(lhs, rhs),
    35
    0
            BinaryOp::Greater => evaluate_greater(lhs, rhs),
    36
    0
            BinaryOp::GreaterEqual => evaluate_greater_equal(lhs, rhs),
    37
    38
    0
            BinaryOp::And => evaluate_and(lhs, rhs),
    39
    0
            BinaryOp::Or => evaluate_or(lhs, rhs),
    40
    41
    0
            BinaryOp::BitwiseAnd => evaluate_bitwise_and(lhs, rhs),
    42
    0
            BinaryOp::BitwiseOr => evaluate_bitwise_or(lhs, rhs),
    43
    0
            BinaryOp::BitwiseXor => evaluate_bitwise_xor(lhs, rhs),
    44
    0
            BinaryOp::LeftShift => evaluate_left_shift(lhs, rhs),
    45
    46
    0
            BinaryOp::NullCoalesce => Ok(if matches!(lhs, Value::Unit) {
    47
    0
                rhs.clone()
    48
            } else {
    49
    0
                lhs.clone()
    50
            }),
    51
        }
    52
    0
    }
    53
    54
    0
    fn evaluate_add(lhs: &Value, rhs: &Value) -> Result<Value> {
    55
    0
        match (lhs, rhs) {
    56
    0
            (Value::Int(a), Value::Int(b)) => Ok(Value::Int(a + b)),
    57
    0
            (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a + b)),
    58
    0
            (Value::String(a), Value::String(b)) => {
    59
    0
                let mut result = String::with_capacity(a.len() + b.len());
    60
    0
                result.push_str(a);
    61
    0
                result.push_str(b);
    62
    0
                Ok(Value::String(result))
    63
            }
    64
    0
            (Value::List(a), Value::List(b)) => {
    65
    0
                let mut result = Vec::with_capacity(a.len() + b.len());
    66
    0
                result.extend(a.iter().cloned());
    67
    0
                result.extend(b.iter().cloned());
    68
    0
                Ok(Value::List(result))
    69
            }
    70
    0
            _ => bail!("Cannot add {:?} and {:?}", lhs, rhs),
    71
        }
    72
    0
    }
    73
    74
    0
    fn evaluate_subtract(lhs: &Value, rhs: &Value) -> Result<Value> {
    75
    0
        match (lhs, rhs) {
    76
    0
            (Value::Int(a), Value::Int(b)) => Ok(Value::Int(a - b)),
    77
    0
            (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a - b)),
    78
    0
            _ => bail!("Cannot subtract {:?} and {:?}", lhs, rhs),
    79
        }
    80
    0
    }
    81
    82
    0
    fn evaluate_multiply(lhs: &Value, rhs: &Value) -> Result<Value> {
    83
    0
        match (lhs, rhs) {
    84
    0
            (Value::Int(a), Value::Int(b)) => Ok(Value::Int(a * b)),
    85
    0
            (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a * b)),
    86
    0
            (Value::String(s), Value::Int(n)) | (Value::Int(n), Value::String(s)) => {
    87
    0
                if *n < 0 {
    88
    0
                    bail!("Cannot repeat string negative times");
    89
    0
                }
    90
    0
                Ok(Value::String(s.repeat(*n as usize)))
    91
            }
    92
    0
            _ => bail!("Cannot multiply {:?} and {:?}", lhs, rhs),
    93
        }
    94
    0
    }
    95
    96
    0
    fn evaluate_divide(lhs: &Value, rhs: &Value) -> Result<Value> {
    97
    0
        match (lhs, rhs) {
    98
    0
            (Value::Int(a), Value::Int(b)) => {
    99
    0
                if *b == 0 {
    100
    0
                    bail!("Division by zero");
    101
    0
                }
    102
    0
                Ok(Value::Int(a / b))
    103
            }
    104
    0
            (Value::Float(a), Value::Float(b)) => {
    105
    0
                if b.abs() < f64::EPSILON {
    106
    0
                    bail!("Division by zero");
    107
    0
                }
    108
    0
                Ok(Value::Float(a / b))
    109
            }
    110
    0
            _ => bail!("Cannot divide {:?} and {:?}", lhs, rhs),
    111
        }
    112
    0
    }
    113
    114
    0
    fn evaluate_modulo(lhs: &Value, rhs: &Value) -> Result<Value> {
    115
    0
        match (lhs, rhs) {
    116
    0
            (Value::Int(a), Value::Int(b)) => {
    117
    0
                if *b == 0 {
    118
    0
                    bail!("Modulo by zero");
    119
    0
                }
    120
    0
                Ok(Value::Int(a % b))
    121
            }
    122
    0
            _ => bail!("Cannot modulo {:?} and {:?}", lhs, rhs),
    123
        }
    124
    0
    }
    125
    126
    0
    fn evaluate_power(lhs: &Value, rhs: &Value) -> Result<Value> {
    127
    0
        match (lhs, rhs) {
    128
    0
            (Value::Int(a), Value::Int(b)) => {
    129
    0
                if *b < 0 {
    130
    0
                    bail!("Negative exponent not supported for integers");
    131
    0
                }
    132
    0
                Ok(Value::Int(a.pow(*b as u32)))
    133
            }
    134
    0
            (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.powf(*b))),
    135
    0
            _ => bail!("Cannot exponentiate {:?} and {:?}", lhs, rhs),
    136
        }
    137
    0
    }
    138
    139
    0
    fn evaluate_less(lhs: &Value, rhs: &Value) -> Result<Value> {
    140
    0
        match (lhs, rhs) {
    141
    0
            (Value::Int(a), Value::Int(b)) => Ok(Value::Bool(a < b)),
    142
    0
            (Value::Float(a), Value::Float(b)) => Ok(Value::Bool(a < b)),
    143
    0
            (Value::String(a), Value::String(b)) => Ok(Value::Bool(a < b)),
    144
    0
            _ => bail!("Cannot compare {:?} and {:?}", lhs, rhs),
    145
        }
    146
    0
    }
    147
    148
    0
    fn evaluate_less_equal(lhs: &Value, rhs: &Value) -> Result<Value> {
    149
    0
        match (lhs, rhs) {
    150
    0
            (Value::Int(a), Value::Int(b)) => Ok(Value::Bool(a <= b)),
    151
    0
            (Value::Float(a), Value::Float(b)) => Ok(Value::Bool(a <= b)),
    152
    0
            (Value::String(a), Value::String(b)) => Ok(Value::Bool(a <= b)),
    153
    0
            _ => bail!("Cannot compare {:?} and {:?}", lhs, rhs),
    154
        }
    155
    0
    }
    156
    157
    0
    fn evaluate_greater(lhs: &Value, rhs: &Value) -> Result<Value> {
    158
    0
        match (lhs, rhs) {
    159
    0
            (Value::Int(a), Value::Int(b)) => Ok(Value::Bool(a > b)),
    160
    0
            (Value::Float(a), Value::Float(b)) => Ok(Value::Bool(a > b)),
    161
    0
            (Value::String(a), Value::String(b)) => Ok(Value::Bool(a > b)),
    162
    0
            _ => bail!("Cannot compare {:?} and {:?}", lhs, rhs),
    163
        }
    164
    0
    }
    165
    166
    0
    fn evaluate_greater_equal(lhs: &Value, rhs: &Value) -> Result<Value> {
    167
    0
        match (lhs, rhs) {
    168
    0
            (Value::Int(a), Value::Int(b)) => Ok(Value::Bool(a >= b)),
    169
    0
            (Value::Float(a), Value::Float(b)) => Ok(Value::Bool(a >= b)),
    170
    0
            (Value::String(a), Value::String(b)) => Ok(Value::Bool(a >= b)),
    171
    0
            _ => bail!("Cannot compare {:?} and {:?}", lhs, rhs),
    172
        }
    173
    0
    }
    174
    175
    0
    fn evaluate_and(lhs: &Value, rhs: &Value) -> Result<Value> {
    176
    0
        match (lhs, rhs) {
    177
    0
            (Value::Bool(a), Value::Bool(b)) => Ok(Value::Bool(*a && *b)),
    178
    0
            _ => bail!("Cannot AND {:?} and {:?}", lhs, rhs),
    179
        }
    180
    0
    }
    181
    182
    0
    fn evaluate_or(lhs: &Value, rhs: &Value) -> Result<Value> {
    183
    0
        match (lhs, rhs) {
    184
    0
            (Value::Bool(a), Value::Bool(b)) => Ok(Value::Bool(*a || *b)),
    185
    0
            _ => bail!("Cannot OR {:?} and {:?}", lhs, rhs),
    186
        }
    187
    0
    }
    188
    189
    0
    fn evaluate_bitwise_and(lhs: &Value, rhs: &Value) -> Result<Value> {
    190
    0
        match (lhs, rhs) {
    191
    0
            (Value::Int(a), Value::Int(b)) => Ok(Value::Int(a & b)),
    192
    0
            _ => bail!("Cannot bitwise AND {:?} and {:?}", lhs, rhs),
    193
        }
    194
    0
    }
    195
    196
    0
    fn evaluate_bitwise_or(lhs: &Value, rhs: &Value) -> Result<Value> {
    197
    0
        match (lhs, rhs) {
    198
    0
            (Value::Int(a), Value::Int(b)) => Ok(Value::Int(a | b)),
    199
    0
            _ => bail!("Cannot bitwise OR {:?} and {:?}", lhs, rhs),
    200
        }
    201
    0
    }
    202
    203
    0
    fn evaluate_bitwise_xor(lhs: &Value, rhs: &Value) -> Result<Value> {
    204
    0
        match (lhs, rhs) {
    205
    0
            (Value::Int(a), Value::Int(b)) => Ok(Value::Int(a ^ b)),
    206
    0
            _ => bail!("Cannot bitwise XOR {:?} and {:?}", lhs, rhs),
    207
        }
    208
    0
    }
    209
    210
    0
    fn evaluate_left_shift(lhs: &Value, rhs: &Value) -> Result<Value> {
    211
    0
        match (lhs, rhs) {
    212
    0
            (Value::Int(a), Value::Int(b)) => {
    213
    0
                if *b < 0 || *b >= 64 {
    214
    0
                    bail!("Invalid shift amount: {}", b);
    215
    0
                }
    216
    0
                Ok(Value::Int(a << b))
    217
            }
    218
    0
            _ => bail!("Cannot left shift {:?} by {:?}", lhs, rhs),
    219
        }
    220
    0
    }
    221
    222
    /// Check if two values are equal
    223
    0
    fn values_equal(v1: &Value, v2: &Value) -> bool {
    224
    0
        match (v1, v2) {
    225
    0
            (Value::Unit, Value::Unit) => true,
    226
    0
            (Value::Int(a), Value::Int(b)) => a == b,
    227
    0
            (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
    228
    0
            (Value::String(a), Value::String(b)) => a == b,
    229
    0
            (Value::Bool(a), Value::Bool(b)) => a == b,
    230
    0
            (Value::Char(a), Value::Char(b)) => a == b,
    231
    0
            (Value::List(a), Value::List(b)) => {
    232
    0
                a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
    233
            }
    234
    0
            (Value::Tuple(a), Value::Tuple(b)) => {
    235
    0
                a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
    236
            }
    237
    0
            _ => false,
    238
        }
    239
    0
    }