feat: execute source control flow and exact real arithmetic
This commit is contained in:
+65
-2
@@ -901,14 +901,38 @@ impl Vm {
|
||||
})
|
||||
}
|
||||
|
||||
fn number_value<'a>(
|
||||
&'a self,
|
||||
value: &'a Value,
|
||||
) -> Result<Option<&'a crate::EndorNumber>, VmError> {
|
||||
Ok(match value {
|
||||
Value::Number(n) => Some(n),
|
||||
Value::Object(o) => self
|
||||
.object(*o)?
|
||||
.payload()
|
||||
.as_any()
|
||||
.downcast_ref::<crate::EndorNumberObject>()
|
||||
.map(|n| n.value()),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn compare_values(
|
||||
&self,
|
||||
left: &Value,
|
||||
right: &Value,
|
||||
operation: CompareOp,
|
||||
) -> Result<bool, VmError> {
|
||||
if matches!(left, Value::Number(_)) || matches!(right, Value::Number(_)) {
|
||||
return Err(VmError::UnsupportedOperation("numeric comparison"));
|
||||
let (left_number, right_number) = (self.number_value(left)?, self.number_value(right)?);
|
||||
if let (Some(left), Some(right)) = (left_number, right_number) {
|
||||
return left.numeric_compare(right, operation);
|
||||
}
|
||||
if left_number.is_some() || right_number.is_some() {
|
||||
return match operation {
|
||||
CompareOp::Eq => Ok(false),
|
||||
CompareOp::Ne => Ok(true),
|
||||
_ => Err(VmError::UnsupportedOperation("mixed numeric ordering")),
|
||||
};
|
||||
}
|
||||
if let (Some(left), Some(right)) = (self.text_value(left)?, self.text_value(right)?) {
|
||||
return Ok(match operation {
|
||||
@@ -1242,6 +1266,45 @@ impl Vm {
|
||||
},
|
||||
)));
|
||||
}
|
||||
Opcode::BinaryArithmetic => {
|
||||
if decoded.instruction.argument == 0 {
|
||||
let left = Self::stack_value(frame, frame.stack.len() - 2)?;
|
||||
let right = Self::top_value(frame)?;
|
||||
if let (Value::Text(a), Value::Text(b)) = (left, right) {
|
||||
if a.len().saturating_add(b.len()) > 1_048_576 {
|
||||
return Err(VmError::InvalidArgument(
|
||||
"text concatenation exceeds operation budget",
|
||||
));
|
||||
}
|
||||
self.discard(frame, 2);
|
||||
frame.stack.push(StackEntry::Value(Value::Text(a + &b)));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let left = Self::stack_value(frame, frame.stack.len() - 2)?;
|
||||
let right = Self::top_value(frame)?;
|
||||
let left = self
|
||||
.number_value(&left)?
|
||||
.ok_or(VmError::ObjectKindMismatch("Number"))?;
|
||||
let right = self
|
||||
.number_value(&right)?
|
||||
.ok_or(VmError::ObjectKindMismatch("Number"))?;
|
||||
let result = left.arithmetic(
|
||||
right,
|
||||
crate::ArithmeticOp::from_argument(decoded.instruction.argument).unwrap(),
|
||||
)?;
|
||||
self.discard(frame, 2);
|
||||
frame.stack.push(StackEntry::Value(Value::Number(result)));
|
||||
}
|
||||
Opcode::UnaryNegate => {
|
||||
let value = Self::top_value(frame)?;
|
||||
let value = self
|
||||
.number_value(&value)?
|
||||
.ok_or(VmError::ObjectKindMismatch("Number"))?;
|
||||
let result = value.negated()?;
|
||||
self.discard(frame, 1);
|
||||
frame.stack.push(StackEntry::Value(Value::Number(result)));
|
||||
}
|
||||
Opcode::CompareOp => {
|
||||
let left = Self::stack_value(frame, frame.stack.len() - 2)?;
|
||||
let right = Self::top_value(frame)?;
|
||||
|
||||
Reference in New Issue
Block a user