Files
regorus/src/rvm/vm/arithmetic.rs
Anand Krishnamoorthi 126cc12eb5 refactor: consolidate RVM instruction variants and clean up VM internals (#651)
Merge the three separate Assert* instructions (AssertNot, AssertCondition,
AssertNotUndefined) into a single `Guard { register, mode }` instruction
with a GuardMode enum. This cuts duplicated match arms across display,
listing, parser, dispatch, and all compiler emit sites.

Drop the unnecessary `#[repr(C)]` from the Instruction enum. It was never
exposed across FFI, so the C-compatible 4-byte discriminant was pure waste.
Without it Rust picks a 1-byte discriminant, shrinking every instruction
from 8 bytes to 6. A new `instruction_size` unit test locks this at 6.

While touching these files, also clean up several long-standing issues:

- Deduplicate the iteration-state setup in loops.rs by extracting a shared
  resolve_iteration_state() helper -- the stack-based and stackless paths
  had near-identical 40-line blocks.
- Collapse the ExitWithSuccess / ExitWithFailure match arms into one.
- In rules.rs, stop cloning Arc<Program> just to borrow a RuleInfo -- clone
  the small RuleInfo struct directly and extract a get_rule_info() helper.
- Move the memory check into dispatch (runs per instruction) and remove the
  now-dead enforce_memory_check() entry-point calls.
- Apply map_or_else style throughout listing.rs for consistency.
2026-04-01 05:34:33 -05:00

124 lines
4.2 KiB
Rust

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// `pattern_type_mismatch` requires explicit `&`+`ref` patterns for tuple matches
// on (&Value, &Value), which conflicts with `needless_borrowed_reference`.
// Disable both to keep patterns consistent within this file.
#![allow(clippy::pattern_type_mismatch, clippy::needless_borrowed_reference)]
use alloc::collections::BTreeSet;
use crate::number::Number;
use crate::value::Value;
use super::errors::{Result, VmError};
use super::machine::RegoVM;
impl RegoVM {
/// Add two values using interpreter's arithmetic logic
pub(super) fn add_values(&self, a: &Value, b: &Value) -> Result<Value> {
match (a, b) {
(&Value::Number(ref x), &Value::Number(ref y)) => Ok(Value::from(x.add(y)?)),
_ => Err(VmError::InvalidAddition {
left: a.clone(),
right: b.clone(),
pc: self.pc,
}),
}
}
/// Subtract two values using interpreter's arithmetic logic
pub(super) fn sub_values(&self, a: &Value, b: &Value) -> Result<Value> {
match (a, b) {
(&Value::Number(ref x), &Value::Number(ref y)) => Ok(Value::from(x.sub(y)?)),
(&Value::Set(ref left), &Value::Set(ref right)) => {
let diff: BTreeSet<Value> = left.difference(right).cloned().collect();
Ok(Value::from_set(diff))
}
_ => Err(VmError::InvalidSubtraction {
left: a.clone(),
right: b.clone(),
pc: self.pc,
}),
}
}
/// Multiply two values using interpreter's arithmetic logic
pub(super) fn mul_values(&self, a: &Value, b: &Value) -> Result<Value> {
match (a, b) {
(&Value::Number(ref x), &Value::Number(ref y)) => Ok(Value::from(x.mul(y)?)),
_ => Err(VmError::InvalidMultiplication {
left: a.clone(),
right: b.clone(),
pc: self.pc,
}),
}
}
/// Divide two values using interpreter's arithmetic logic
pub(super) fn div_values(&self, a: &Value, b: &Value) -> Result<Value> {
match (a, b) {
(&Value::Number(ref x), &Value::Number(ref y)) => {
if *y == Number::from(0_u64) {
if self.strict_builtin_errors {
return Err(VmError::InvalidDivision {
left: a.clone(),
right: b.clone(),
pc: self.pc,
});
}
return Ok(Value::Undefined);
}
Ok(Value::from(x.clone().divide(y)?))
}
_ => Err(VmError::InvalidDivision {
left: a.clone(),
right: b.clone(),
pc: self.pc,
}),
}
}
/// Modulo two values using interpreter's arithmetic logic
pub(super) fn mod_values(&self, a: &Value, b: &Value) -> Result<Value> {
match (a, b) {
(&Value::Number(ref x), &Value::Number(ref y)) => {
if *y == Number::from(0_u64) {
if self.strict_builtin_errors {
return Err(VmError::InvalidModulo {
left: a.clone(),
right: b.clone(),
pc: self.pc,
});
}
return Ok(Value::Undefined);
}
if !x.is_integer() || !y.is_integer() {
return Err(VmError::ModuloOnFloat {
left: a.clone(),
right: b.clone(),
pc: self.pc,
});
}
Ok(Value::from(x.clone().modulo(y)?))
}
_ => Err(VmError::InvalidModulo {
left: a.clone(),
right: b.clone(),
pc: self.pc,
}),
}
}
pub(super) const fn to_bool(&self, value: &Value) -> Option<bool> {
match *value {
Value::Bool(b) => Some(b),
Value::Null if !self.strict_builtin_errors => Some(true),
_ => None,
}
}
}