| 262 | } |
| 263 | |
| 264 | fn fold_binop(op: OpCode, a: Val, b: Val) -> Option<Val> { |
| 265 | if matches!(op, OpCode::Eq | OpCode::NotEq | OpCode::Lt | OpCode::Gt | OpCode::LtEq | OpCode::GtEq) { |
| 266 | let (af, bf) = if a.is_int() && b.is_int() { |
| 267 | (a.as_int() as f64, b.as_int() as f64) |
| 268 | } else if (a.is_int() || a.is_float()) && (b.is_int() || b.is_float()) { |
| 269 | let af = if a.is_int() { a.as_int() as f64 } else { a.as_float() }; |
| 270 | let bf = if b.is_int() { b.as_int() as f64 } else { b.as_float() }; |
| 271 | (af, bf) |
| 272 | } else { |
| 273 | return None; |
| 274 | }; |
| 275 | return Some(Val::bool(match op { |
| 276 | OpCode::Eq => af == bf, |
| 277 | OpCode::NotEq => af != bf, |
| 278 | OpCode::Lt => af < bf, |
| 279 | OpCode::Gt => af > bf, |
| 280 | OpCode::LtEq => af <= bf, |
| 281 | OpCode::GtEq => af >= bf, |
| 282 | _ => return None, |
| 283 | })); |
| 284 | } |
| 285 | |
| 286 | if a.is_int() && b.is_int() { |
| 287 | let (ai, bi) = (a.as_int() as i128, b.as_int() as i128); |
| 288 | let r = match op { |
| 289 | OpCode::Add => ai.checked_add(bi)?, |
| 290 | OpCode::Sub => ai.checked_sub(bi)?, |
| 291 | OpCode::Mul => ai.checked_mul(bi)?, |
| 292 | // floored mod/div (sign follows divisor), not Euclidean; matches the runtime path. |
| 293 | OpCode::Mod => if bi == 0 { return None; } else { let r = ai % bi; if r != 0 && (r < 0) != (bi < 0) { r + bi } else { r } }, |
| 294 | OpCode::FloorDiv => if bi == 0 { return None; } else { let q = ai / bi; let r = ai - q * bi; if r != 0 && (r < 0) != (bi < 0) { q - 1 } else { q } }, |
| 295 | OpCode::BitAnd => ai & bi, |
| 296 | OpCode::BitOr => ai | bi, |
| 297 | OpCode::BitXor => ai ^ bi, |
| 298 | OpCode::Shl => if !(0..63).contains(&bi) { return None; } else { ai.checked_shl(bi as u32)? }, |
| 299 | OpCode::Shr => if !(0..63).contains(&bi) { return None; } else { ai >> bi }, |
| 300 | _ => return None, |
| 301 | }; |
| 302 | if (Val::INT_MIN as i128..=Val::INT_MAX as i128).contains(&r) { |
| 303 | return Some(Val::int(r as i64)); |
| 304 | } |
| 305 | return None; |
| 306 | } |
| 307 | |
| 308 | if (a.is_int() || a.is_float()) && (b.is_int() || b.is_float()) { |
| 309 | let af = if a.is_int() { a.as_int() as f64 } else { a.as_float() }; |
| 310 | let bf = if b.is_int() { b.as_int() as f64 } else { b.as_float() }; |
| 311 | return Some(Val::float(match op { |
| 312 | OpCode::Add => af + bf, |
| 313 | OpCode::Sub => af - bf, |
| 314 | OpCode::Mul => af * bf, |
| 315 | OpCode::Div => if bf == 0.0 { return None; } else { af / bf }, |
| 316 | _ => return None, |
| 317 | })); |
| 318 | } |
| 319 | |
| 320 | None |
| 321 | } |