Use a lookup table instead of matching operation codes in the VM
This commit is contained in:
parent
20f451fe6c
commit
395f0af213
@ -17,7 +17,7 @@ use std::io::Write;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::{ConcreteValue, DustString, FunctionType, Instruction, Span, Type};
|
use crate::{ConcreteValue, DustString, FunctionType, Instruction, Span};
|
||||||
|
|
||||||
/// In-memory representation of a Dust program or function.
|
/// In-memory representation of a Dust program or function.
|
||||||
///
|
///
|
||||||
|
@ -154,7 +154,6 @@ pub struct Instruction(u32);
|
|||||||
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct InstructionData {
|
pub struct InstructionData {
|
||||||
pub operation: Operation,
|
|
||||||
pub a: u8,
|
pub a: u8,
|
||||||
pub b: u8,
|
pub b: u8,
|
||||||
pub c: u8,
|
pub c: u8,
|
||||||
@ -226,16 +225,18 @@ impl Instruction {
|
|||||||
self.0 = (self.0 & 0xFF00FFFF) | ((bits as u32) << 24);
|
self.0 = (self.0 & 0xFF00FFFF) | ((bits as u32) << 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode(self) -> InstructionData {
|
pub fn decode(self) -> (Operation, InstructionData) {
|
||||||
|
(
|
||||||
|
self.operation(),
|
||||||
InstructionData {
|
InstructionData {
|
||||||
operation: self.operation(),
|
|
||||||
a: self.a_field(),
|
a: self.a_field(),
|
||||||
b: self.b_field(),
|
b: self.b_field(),
|
||||||
c: self.c_field(),
|
c: self.c_field(),
|
||||||
b_is_constant: self.b_is_constant(),
|
b_is_constant: self.b_is_constant(),
|
||||||
c_is_constant: self.c_is_constant(),
|
c_is_constant: self.c_is_constant(),
|
||||||
d: self.d_field(),
|
d: self.d_field(),
|
||||||
}
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn r#move(from: u8, to: u8) -> Instruction {
|
pub fn r#move(from: u8, to: u8) -> Instruction {
|
||||||
@ -498,7 +499,6 @@ impl Instruction {
|
|||||||
Operation::LoadBoolean
|
Operation::LoadBoolean
|
||||||
| Operation::LoadConstant
|
| Operation::LoadConstant
|
||||||
| Operation::LoadList
|
| Operation::LoadList
|
||||||
| Operation::LoadMap
|
|
||||||
| Operation::LoadSelf
|
| Operation::LoadSelf
|
||||||
| Operation::GetLocal
|
| Operation::GetLocal
|
||||||
| Operation::Add
|
| Operation::Add
|
||||||
@ -506,7 +506,6 @@ impl Instruction {
|
|||||||
| Operation::Multiply
|
| Operation::Multiply
|
||||||
| Operation::Divide
|
| Operation::Divide
|
||||||
| Operation::Modulo
|
| Operation::Modulo
|
||||||
| Operation::Power
|
|
||||||
| Operation::Negate
|
| Operation::Negate
|
||||||
| Operation::Not
|
| Operation::Not
|
||||||
| Operation::Equal
|
| Operation::Equal
|
||||||
|
@ -9,27 +9,25 @@ pub const CLOSE_BYTE: u8 = 1;
|
|||||||
pub const LOAD_BOOLEAN_BYTE: u8 = 2;
|
pub const LOAD_BOOLEAN_BYTE: u8 = 2;
|
||||||
pub const LOAD_CONSTANT_BYTE: u8 = 3;
|
pub const LOAD_CONSTANT_BYTE: u8 = 3;
|
||||||
pub const LOAD_LIST_BYTE: u8 = 4;
|
pub const LOAD_LIST_BYTE: u8 = 4;
|
||||||
pub const LOAD_MAP_BYTE: u8 = 5;
|
pub const LOAD_SELF_BYTE: u8 = 5;
|
||||||
pub const LOAD_SELF_BYTE: u8 = 6;
|
pub const GET_LOCAL_BYTE: u8 = 6;
|
||||||
pub const GET_LOCAL_BYTE: u8 = 7;
|
pub const SET_LOCAL_BYTE: u8 = 7;
|
||||||
pub const SET_LOCAL_BYTE: u8 = 8;
|
pub const ADD_BYTE: u8 = 8;
|
||||||
pub const ADD_BYTE: u8 = 9;
|
pub const SUBTRACT_BYTE: u8 = 9;
|
||||||
pub const SUBTRACT_BYTE: u8 = 10;
|
pub const MULTIPLY_BYTE: u8 = 10;
|
||||||
pub const MULTIPLY_BYTE: u8 = 11;
|
pub const DIVIDE_BYTE: u8 = 11;
|
||||||
pub const DIVIDE_BYTE: u8 = 12;
|
pub const MODULO_BYTE: u8 = 12;
|
||||||
pub const MODULO_BYTE: u8 = 13;
|
pub const TEST_BYTE: u8 = 13;
|
||||||
pub const POWER_BYTE: u8 = 14;
|
pub const TEST_SET_BYTE: u8 = 14;
|
||||||
pub const TEST_BYTE: u8 = 15;
|
pub const EQUAL_BYTE: u8 = 15;
|
||||||
pub const TEST_SET_BYTE: u8 = 16;
|
pub const LESS_BYTE: u8 = 16;
|
||||||
pub const EQUAL_BYTE: u8 = 17;
|
pub const LESS_EQUAL_BYTE: u8 = 17;
|
||||||
pub const LESS_BYTE: u8 = 18;
|
pub const NEGATE_BYTE: u8 = 18;
|
||||||
pub const LESS_EQUAL_BYTE: u8 = 19;
|
pub const NOT_BYTE: u8 = 19;
|
||||||
pub const NEGATE_BYTE: u8 = 20;
|
pub const CALL_BYTE: u8 = 20;
|
||||||
pub const NOT_BYTE: u8 = 21;
|
pub const CALL_NATIVE_BYTE: u8 = 21;
|
||||||
pub const CALL_BYTE: u8 = 22;
|
pub const JUMP_BYTE: u8 = 22;
|
||||||
pub const CALL_NATIVE_BYTE: u8 = 23;
|
pub const RETURN_BYTE: u8 = 23;
|
||||||
pub const JUMP_BYTE: u8 = 24;
|
|
||||||
pub const RETURN_BYTE: u8 = 25;
|
|
||||||
|
|
||||||
/// Part of an [Instruction][crate::Instruction] that is encoded as a single byte.
|
/// Part of an [Instruction][crate::Instruction] that is encoded as a single byte.
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
@ -40,7 +38,6 @@ pub enum Operation {
|
|||||||
LoadBoolean = LOAD_BOOLEAN_BYTE,
|
LoadBoolean = LOAD_BOOLEAN_BYTE,
|
||||||
LoadConstant = LOAD_CONSTANT_BYTE,
|
LoadConstant = LOAD_CONSTANT_BYTE,
|
||||||
LoadList = LOAD_LIST_BYTE,
|
LoadList = LOAD_LIST_BYTE,
|
||||||
LoadMap = LOAD_MAP_BYTE,
|
|
||||||
LoadSelf = LOAD_SELF_BYTE,
|
LoadSelf = LOAD_SELF_BYTE,
|
||||||
GetLocal = GET_LOCAL_BYTE,
|
GetLocal = GET_LOCAL_BYTE,
|
||||||
SetLocal = SET_LOCAL_BYTE,
|
SetLocal = SET_LOCAL_BYTE,
|
||||||
@ -49,7 +46,6 @@ pub enum Operation {
|
|||||||
Multiply = MULTIPLY_BYTE,
|
Multiply = MULTIPLY_BYTE,
|
||||||
Divide = DIVIDE_BYTE,
|
Divide = DIVIDE_BYTE,
|
||||||
Modulo = MODULO_BYTE,
|
Modulo = MODULO_BYTE,
|
||||||
Power = POWER_BYTE,
|
|
||||||
Test = TEST_BYTE,
|
Test = TEST_BYTE,
|
||||||
TestSet = TEST_SET_BYTE,
|
TestSet = TEST_SET_BYTE,
|
||||||
Equal = EQUAL_BYTE,
|
Equal = EQUAL_BYTE,
|
||||||
@ -71,7 +67,6 @@ impl From<u8> for Operation {
|
|||||||
LOAD_BOOLEAN_BYTE => Self::LoadBoolean,
|
LOAD_BOOLEAN_BYTE => Self::LoadBoolean,
|
||||||
LOAD_CONSTANT_BYTE => Self::LoadConstant,
|
LOAD_CONSTANT_BYTE => Self::LoadConstant,
|
||||||
LOAD_LIST_BYTE => Self::LoadList,
|
LOAD_LIST_BYTE => Self::LoadList,
|
||||||
LOAD_MAP_BYTE => Self::LoadMap,
|
|
||||||
LOAD_SELF_BYTE => Self::LoadSelf,
|
LOAD_SELF_BYTE => Self::LoadSelf,
|
||||||
GET_LOCAL_BYTE => Self::GetLocal,
|
GET_LOCAL_BYTE => Self::GetLocal,
|
||||||
SET_LOCAL_BYTE => Self::SetLocal,
|
SET_LOCAL_BYTE => Self::SetLocal,
|
||||||
@ -80,7 +75,6 @@ impl From<u8> for Operation {
|
|||||||
MULTIPLY_BYTE => Self::Multiply,
|
MULTIPLY_BYTE => Self::Multiply,
|
||||||
DIVIDE_BYTE => Self::Divide,
|
DIVIDE_BYTE => Self::Divide,
|
||||||
MODULO_BYTE => Self::Modulo,
|
MODULO_BYTE => Self::Modulo,
|
||||||
POWER_BYTE => Self::Power,
|
|
||||||
TEST_BYTE => Self::Test,
|
TEST_BYTE => Self::Test,
|
||||||
TEST_SET_BYTE => Self::TestSet,
|
TEST_SET_BYTE => Self::TestSet,
|
||||||
EQUAL_BYTE => Self::Equal,
|
EQUAL_BYTE => Self::Equal,
|
||||||
@ -111,7 +105,6 @@ impl Operation {
|
|||||||
Self::LoadBoolean => "LOAD_BOOLEAN",
|
Self::LoadBoolean => "LOAD_BOOLEAN",
|
||||||
Self::LoadConstant => "LOAD_CONSTANT",
|
Self::LoadConstant => "LOAD_CONSTANT",
|
||||||
Self::LoadList => "LOAD_LIST",
|
Self::LoadList => "LOAD_LIST",
|
||||||
Self::LoadMap => "LOAD_MAP",
|
|
||||||
Self::LoadSelf => "LOAD_SELF",
|
Self::LoadSelf => "LOAD_SELF",
|
||||||
Self::GetLocal => "GET_LOCAL",
|
Self::GetLocal => "GET_LOCAL",
|
||||||
Self::SetLocal => "SET_LOCAL",
|
Self::SetLocal => "SET_LOCAL",
|
||||||
@ -120,7 +113,6 @@ impl Operation {
|
|||||||
Self::Multiply => "MULTIPLY",
|
Self::Multiply => "MULTIPLY",
|
||||||
Self::Divide => "DIVIDE",
|
Self::Divide => "DIVIDE",
|
||||||
Self::Modulo => "MODULO",
|
Self::Modulo => "MODULO",
|
||||||
Self::Power => "POWER",
|
|
||||||
Self::Test => "TEST",
|
Self::Test => "TEST",
|
||||||
Self::TestSet => "TEST_SET",
|
Self::TestSet => "TEST_SET",
|
||||||
Self::Equal => "EQUAL",
|
Self::Equal => "EQUAL",
|
||||||
@ -152,13 +144,12 @@ impl Display for Operation {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const ALL_OPERATIONS: [Operation; 26] = [
|
const ALL_OPERATIONS: [Operation; 24] = [
|
||||||
Operation::Move,
|
Operation::Move,
|
||||||
Operation::Close,
|
Operation::Close,
|
||||||
Operation::LoadBoolean,
|
Operation::LoadBoolean,
|
||||||
Operation::LoadConstant,
|
Operation::LoadConstant,
|
||||||
Operation::LoadList,
|
Operation::LoadList,
|
||||||
Operation::LoadMap,
|
|
||||||
Operation::LoadSelf,
|
Operation::LoadSelf,
|
||||||
Operation::GetLocal,
|
Operation::GetLocal,
|
||||||
Operation::SetLocal,
|
Operation::SetLocal,
|
||||||
@ -167,7 +158,6 @@ mod tests {
|
|||||||
Operation::Multiply,
|
Operation::Multiply,
|
||||||
Operation::Divide,
|
Operation::Divide,
|
||||||
Operation::Modulo,
|
Operation::Modulo,
|
||||||
Operation::Power,
|
|
||||||
Operation::Test,
|
Operation::Test,
|
||||||
Operation::TestSet,
|
Operation::TestSet,
|
||||||
Operation::Equal,
|
Operation::Equal,
|
||||||
|
@ -154,7 +154,7 @@ impl ValueRef<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn less_than(&self, other: ValueRef) -> Result<Value, ValueError> {
|
pub fn less(&self, other: ValueRef) -> Result<Value, ValueError> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(ValueRef::Concrete(left), ValueRef::Concrete(right)) => {
|
(ValueRef::Concrete(left), ValueRef::Concrete(right)) => {
|
||||||
left.less_than(right).map(Value::Concrete)
|
left.less_than(right).map(Value::Concrete)
|
||||||
@ -163,7 +163,7 @@ impl ValueRef<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn less_than_or_equal(&self, other: ValueRef) -> Result<Value, ValueError> {
|
pub fn less_equal(&self, other: ValueRef) -> Result<Value, ValueError> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(ValueRef::Concrete(left), ValueRef::Concrete(right)) => {
|
(ValueRef::Concrete(left), ValueRef::Concrete(right)) => {
|
||||||
left.less_than_or_equal(right).map(Value::Concrete)
|
left.less_than_or_equal(right).map(Value::Concrete)
|
||||||
|
@ -8,7 +8,7 @@ use smallvec::SmallVec;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
compile, instruction::*, AbstractValue, AnnotatedError, Chunk, ConcreteValue, DustError,
|
compile, instruction::*, AbstractValue, AnnotatedError, Chunk, ConcreteValue, DustError,
|
||||||
Instruction, NativeFunctionError, Operation, Span, Value, ValueError, ValueRef,
|
Instruction, NativeFunction, NativeFunctionError, Operation, Span, Value, ValueError, ValueRef,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> {
|
pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> {
|
||||||
@ -18,6 +18,34 @@ pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> {
|
|||||||
vm.run().map_err(|error| DustError::runtime(error, source))
|
vm.run().map_err(|error| DustError::runtime(error, source))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Runner = fn(&mut Vm, InstructionData) -> Result<(), VmError>;
|
||||||
|
|
||||||
|
const RUNNERS: [Runner; 23] = [
|
||||||
|
Vm::r#move,
|
||||||
|
Vm::close,
|
||||||
|
Vm::load_boolean,
|
||||||
|
Vm::load_constant,
|
||||||
|
Vm::load_list,
|
||||||
|
Vm::load_self,
|
||||||
|
Vm::get_local,
|
||||||
|
Vm::set_local,
|
||||||
|
Vm::add,
|
||||||
|
Vm::subtract,
|
||||||
|
Vm::multiply,
|
||||||
|
Vm::divide,
|
||||||
|
Vm::modulo,
|
||||||
|
Vm::test,
|
||||||
|
Vm::test_set,
|
||||||
|
Vm::equal,
|
||||||
|
Vm::less,
|
||||||
|
Vm::less_equal,
|
||||||
|
Vm::negate,
|
||||||
|
Vm::not,
|
||||||
|
Vm::call,
|
||||||
|
Vm::call_native,
|
||||||
|
Vm::jump,
|
||||||
|
];
|
||||||
|
|
||||||
/// Dust virtual machine.
|
/// Dust virtual machine.
|
||||||
///
|
///
|
||||||
/// See the [module-level documentation](index.html) for more information.
|
/// See the [module-level documentation](index.html) for more information.
|
||||||
@ -62,262 +90,320 @@ impl<'a> Vm<'a> {
|
|||||||
position
|
position
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) -> Result<Option<ConcreteValue>, VmError> {
|
fn r#move<'b, 'c>(
|
||||||
loop {
|
vm: &'b mut Vm<'c>,
|
||||||
let instruction = self.read();
|
instruction_data: InstructionData,
|
||||||
|
) -> Result<(), VmError> {
|
||||||
|
let InstructionData { b, c, .. } = instruction_data;
|
||||||
|
let from_register_has_value = vm
|
||||||
|
.stack
|
||||||
|
.get(b as usize)
|
||||||
|
.is_some_and(|register| !matches!(register, Register::Empty));
|
||||||
|
let register = Register::Pointer(Pointer::Stack(b));
|
||||||
|
|
||||||
|
if from_register_has_value {
|
||||||
|
vm.set_register(c, register)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) -> Result<(), VmError> {
|
||||||
|
let InstructionData { b, c, .. } = instruction_data;
|
||||||
|
|
||||||
|
if vm.stack.len() < c as usize {
|
||||||
|
return Err(VmError::StackUnderflow {
|
||||||
|
position: vm.current_position(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for register_index in b..c {
|
||||||
|
vm.stack[register_index as usize] = Register::Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_boolean<'b, 'c>(
|
||||||
|
vm: &'b mut Vm<'c>,
|
||||||
|
instruction_data: InstructionData,
|
||||||
|
) -> Result<(), VmError> {
|
||||||
|
let InstructionData { a, b, c, .. } = instruction_data;
|
||||||
|
let boolean = ConcreteValue::Boolean(b != 0).to_value();
|
||||||
|
let register = Register::Value(boolean);
|
||||||
|
|
||||||
|
vm.set_register(a, register)?;
|
||||||
|
|
||||||
|
if c != 0 {
|
||||||
|
vm.jump_instructions(1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_constant<'b, 'c>(
|
||||||
|
vm: &'b mut Vm<'c>,
|
||||||
|
instruction_data: InstructionData,
|
||||||
|
) -> Result<(), VmError> {
|
||||||
|
let InstructionData { a, b, c, .. } = instruction_data;
|
||||||
|
let register = Register::Pointer(Pointer::Constant(b));
|
||||||
|
|
||||||
|
vm.set_register(a, register)?;
|
||||||
|
|
||||||
|
if c != 0 {
|
||||||
|
vm.jump_instructions(1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_list<'b, 'c>(
|
||||||
|
vm: &'b mut Vm<'c>,
|
||||||
|
instruction_data: InstructionData,
|
||||||
|
) -> Result<(), VmError> {
|
||||||
|
let InstructionData { a, b, .. } = instruction_data;
|
||||||
|
let mut item_pointers = Vec::new();
|
||||||
|
let stack = vm.stack.as_slice();
|
||||||
|
|
||||||
|
for register_index in b..a {
|
||||||
|
if let Register::Empty = stack[register_index as usize] {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pointer = Pointer::Stack(register_index);
|
||||||
|
|
||||||
|
item_pointers.push(pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
let list_value = AbstractValue::List { item_pointers }.to_value();
|
||||||
|
let register = Register::Value(list_value);
|
||||||
|
|
||||||
|
vm.set_register(a, register)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_self<'b, 'c>(
|
||||||
|
vm: &'b mut Vm<'c>,
|
||||||
|
instruction_data: InstructionData,
|
||||||
|
) -> Result<(), VmError> {
|
||||||
|
let InstructionData { a, .. } = instruction_data;
|
||||||
|
let register = Register::Value(AbstractValue::FunctionSelf.to_value());
|
||||||
|
|
||||||
|
vm.set_register(a, register)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_local<'b, 'c>(
|
||||||
|
vm: &'b mut Vm<'c>,
|
||||||
|
instruction_data: InstructionData,
|
||||||
|
) -> Result<(), VmError> {
|
||||||
|
let InstructionData { a, b, .. } = instruction_data;
|
||||||
|
let local_register_index = vm.get_local_register(b)?;
|
||||||
|
let register = Register::Pointer(Pointer::Stack(local_register_index));
|
||||||
|
|
||||||
|
vm.set_register(a, register)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_local<'b, 'c>(
|
||||||
|
vm: &'b mut Vm<'c>,
|
||||||
|
instruction_data: InstructionData,
|
||||||
|
) -> Result<(), VmError> {
|
||||||
|
let InstructionData { b, c, .. } = instruction_data;
|
||||||
|
let local_register_index = vm.get_local_register(c)?;
|
||||||
|
let register = Register::Pointer(Pointer::Stack(b));
|
||||||
|
|
||||||
|
vm.set_register(local_register_index, register)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) -> Result<(), VmError> {
|
||||||
let InstructionData {
|
let InstructionData {
|
||||||
operation,
|
|
||||||
a,
|
a,
|
||||||
b,
|
b,
|
||||||
c,
|
c,
|
||||||
b_is_constant,
|
b_is_constant,
|
||||||
c_is_constant,
|
c_is_constant,
|
||||||
d,
|
..
|
||||||
} = instruction.decode();
|
} = instruction_data;
|
||||||
|
let left = vm.get_argument(b, b_is_constant)?;
|
||||||
log::info!(
|
let right = vm.get_argument(c, c_is_constant)?;
|
||||||
"{} | {} | {} | {}",
|
|
||||||
self.ip - 1,
|
|
||||||
self.current_position(),
|
|
||||||
instruction.operation(),
|
|
||||||
instruction.disassembly_info()
|
|
||||||
);
|
|
||||||
|
|
||||||
match operation {
|
|
||||||
Operation::Move => {
|
|
||||||
let Move { from, to } = Move::from(&instruction);
|
|
||||||
let from_register_has_value = self
|
|
||||||
.stack
|
|
||||||
.get(from as usize)
|
|
||||||
.is_some_and(|register| !matches!(register, Register::Empty));
|
|
||||||
let register = Register::Pointer(Pointer::Stack(from));
|
|
||||||
|
|
||||||
if from_register_has_value {
|
|
||||||
self.set_register(to, register)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Operation::Close => {
|
|
||||||
let Close { from, to } = Close::from(&instruction);
|
|
||||||
|
|
||||||
if self.stack.len() < to as usize {
|
|
||||||
return Err(VmError::StackUnderflow {
|
|
||||||
position: self.current_position(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for register_index in from..to {
|
|
||||||
self.stack[register_index as usize] = Register::Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Operation::LoadBoolean => {
|
|
||||||
let LoadBoolean {
|
|
||||||
destination,
|
|
||||||
value,
|
|
||||||
jump_next,
|
|
||||||
} = LoadBoolean::from(&instruction);
|
|
||||||
let boolean = ConcreteValue::Boolean(value).to_value();
|
|
||||||
let register = Register::Value(boolean);
|
|
||||||
|
|
||||||
self.set_register(destination, register)?;
|
|
||||||
|
|
||||||
if jump_next {
|
|
||||||
self.jump(1, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Operation::LoadConstant => {
|
|
||||||
let register = Register::Pointer(Pointer::Constant(b));
|
|
||||||
|
|
||||||
self.set_register(a, register)?;
|
|
||||||
|
|
||||||
if c != 0 {
|
|
||||||
self.jump(1, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Operation::LoadList => {
|
|
||||||
let LoadList {
|
|
||||||
destination,
|
|
||||||
start_register,
|
|
||||||
} = LoadList::from(&instruction);
|
|
||||||
let mut pointers = Vec::new();
|
|
||||||
|
|
||||||
for register in start_register..destination {
|
|
||||||
if let Some(Register::Empty) = self.stack.get(register as usize) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let pointer = Pointer::Stack(register);
|
|
||||||
|
|
||||||
pointers.push(pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
let register = Register::Value(
|
|
||||||
AbstractValue::List {
|
|
||||||
item_pointers: pointers,
|
|
||||||
}
|
|
||||||
.to_value(),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.set_register(destination, register)?;
|
|
||||||
}
|
|
||||||
Operation::LoadSelf => {
|
|
||||||
let LoadSelf { destination } = LoadSelf::from(&instruction);
|
|
||||||
let register = Register::Value(AbstractValue::FunctionSelf.to_value());
|
|
||||||
|
|
||||||
self.set_register(destination, register)?;
|
|
||||||
}
|
|
||||||
Operation::GetLocal => {
|
|
||||||
let GetLocal {
|
|
||||||
destination,
|
|
||||||
local_index,
|
|
||||||
} = GetLocal::from(&instruction);
|
|
||||||
let local_register = self.get_local_register(local_index)?;
|
|
||||||
let register = Register::Pointer(Pointer::Stack(local_register));
|
|
||||||
|
|
||||||
self.set_register(destination, register)?;
|
|
||||||
}
|
|
||||||
Operation::SetLocal => {
|
|
||||||
let SetLocal {
|
|
||||||
register_index,
|
|
||||||
local_index,
|
|
||||||
} = SetLocal::from(&instruction);
|
|
||||||
let local_register_index = self.get_local_register(local_index)?;
|
|
||||||
let register = Register::Pointer(Pointer::Stack(register_index));
|
|
||||||
|
|
||||||
self.set_register(local_register_index, register)?;
|
|
||||||
}
|
|
||||||
Operation::Add => {
|
|
||||||
let left = if b_is_constant {
|
|
||||||
self.get_constant(b).to_value_ref()
|
|
||||||
} else {
|
|
||||||
self.open_register(b)?
|
|
||||||
};
|
|
||||||
let right = if c_is_constant {
|
|
||||||
self.get_constant(c).to_value_ref()
|
|
||||||
} else {
|
|
||||||
self.open_register(c)?
|
|
||||||
};
|
|
||||||
let sum_result = left.add(right);
|
let sum_result = left.add(right);
|
||||||
let sum = match sum_result {
|
let sum = match sum_result {
|
||||||
Ok(sum) => sum,
|
Ok(sum) => sum,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(VmError::Value {
|
return Err(VmError::Value {
|
||||||
error,
|
error,
|
||||||
position: self.current_position(),
|
position: vm.current_position(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let register = Register::Value(sum);
|
||||||
|
|
||||||
self.set_register(a, Register::Value(sum))?;
|
vm.set_register(a, register)
|
||||||
}
|
}
|
||||||
Operation::Subtract => {
|
|
||||||
let left = self.get_argument(b, b_is_constant)?;
|
fn subtract<'b, 'c>(
|
||||||
let right = self.get_argument(c, c_is_constant)?;
|
vm: &'b mut Vm<'c>,
|
||||||
|
instruction_data: InstructionData,
|
||||||
|
) -> Result<(), VmError> {
|
||||||
|
let InstructionData {
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
b_is_constant,
|
||||||
|
c_is_constant,
|
||||||
|
..
|
||||||
|
} = instruction_data;
|
||||||
|
let left = vm.get_argument(b, b_is_constant)?;
|
||||||
|
let right = vm.get_argument(c, c_is_constant)?;
|
||||||
let subtraction_result = left.subtract(right);
|
let subtraction_result = left.subtract(right);
|
||||||
let difference = match subtraction_result {
|
let difference = match subtraction_result {
|
||||||
Ok(difference) => difference,
|
Ok(difference) => difference,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(VmError::Value {
|
return Err(VmError::Value {
|
||||||
error,
|
error,
|
||||||
position: self.current_position(),
|
position: vm.current_position(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let register = Register::Value(difference);
|
||||||
|
|
||||||
self.set_register(a, Register::Value(difference))?;
|
vm.set_register(a, register)
|
||||||
}
|
}
|
||||||
Operation::Multiply => {
|
|
||||||
let left = self.get_argument(b, b_is_constant)?;
|
fn multiply<'b, 'c>(
|
||||||
let right = self.get_argument(c, c_is_constant)?;
|
vm: &'b mut Vm<'c>,
|
||||||
|
instruction_data: InstructionData,
|
||||||
|
) -> Result<(), VmError> {
|
||||||
|
let InstructionData {
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
b_is_constant,
|
||||||
|
c_is_constant,
|
||||||
|
..
|
||||||
|
} = instruction_data;
|
||||||
|
let left = vm.get_argument(b, b_is_constant)?;
|
||||||
|
let right = vm.get_argument(c, c_is_constant)?;
|
||||||
let multiplication_result = left.multiply(right);
|
let multiplication_result = left.multiply(right);
|
||||||
let product = match multiplication_result {
|
let product = match multiplication_result {
|
||||||
Ok(product) => product,
|
Ok(product) => product,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(VmError::Value {
|
return Err(VmError::Value {
|
||||||
error,
|
error,
|
||||||
position: self.current_position(),
|
position: vm.current_position(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let register = Register::Value(product);
|
||||||
|
|
||||||
self.set_register(a, Register::Value(product))?;
|
vm.set_register(a, register)
|
||||||
}
|
}
|
||||||
Operation::Divide => {
|
|
||||||
let Divide {
|
fn divide<'b, 'c>(
|
||||||
destination,
|
vm: &'b mut Vm<'c>,
|
||||||
left,
|
instruction_data: InstructionData,
|
||||||
right,
|
) -> Result<(), VmError> {
|
||||||
} = Divide::from(&instruction);
|
let InstructionData {
|
||||||
let left = self.get_argument(b, b_is_constant)?;
|
a,
|
||||||
let right = self.get_argument(c, c_is_constant)?;
|
b,
|
||||||
|
c,
|
||||||
|
b_is_constant,
|
||||||
|
c_is_constant,
|
||||||
|
..
|
||||||
|
} = instruction_data;
|
||||||
|
let left = vm.get_argument(b, b_is_constant)?;
|
||||||
|
let right = vm.get_argument(c, c_is_constant)?;
|
||||||
let division_result = left.divide(right);
|
let division_result = left.divide(right);
|
||||||
let quotient = match division_result {
|
let quotient = match division_result {
|
||||||
Ok(quotient) => quotient,
|
Ok(quotient) => quotient,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(VmError::Value {
|
return Err(VmError::Value {
|
||||||
error,
|
error,
|
||||||
position: self.current_position(),
|
position: vm.current_position(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let register = Register::Value(quotient);
|
||||||
|
|
||||||
self.set_register(destination, Register::Value(quotient))?;
|
vm.set_register(a, register)
|
||||||
}
|
}
|
||||||
Operation::Modulo => {
|
|
||||||
let Modulo {
|
fn modulo<'b, 'c>(
|
||||||
destination,
|
vm: &'b mut Vm<'c>,
|
||||||
left,
|
instruction_data: InstructionData,
|
||||||
right,
|
) -> Result<(), VmError> {
|
||||||
} = Modulo::from(&instruction);
|
let InstructionData {
|
||||||
let left = self.get_argument(b, b_is_constant)?;
|
a,
|
||||||
let right = self.get_argument(c, c_is_constant)?;
|
b,
|
||||||
|
c,
|
||||||
|
b_is_constant,
|
||||||
|
c_is_constant,
|
||||||
|
..
|
||||||
|
} = instruction_data;
|
||||||
|
let left = vm.get_argument(b, b_is_constant)?;
|
||||||
|
let right = vm.get_argument(c, c_is_constant)?;
|
||||||
let modulo_result = left.modulo(right);
|
let modulo_result = left.modulo(right);
|
||||||
let remainder = match modulo_result {
|
let remainder = match modulo_result {
|
||||||
Ok(remainder) => remainder,
|
Ok(remainder) => remainder,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(VmError::Value {
|
return Err(VmError::Value {
|
||||||
error,
|
error,
|
||||||
position: self.current_position(),
|
position: vm.current_position(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let register = Register::Value(remainder);
|
||||||
|
|
||||||
self.set_register(destination, Register::Value(remainder))?;
|
vm.set_register(a, register)
|
||||||
}
|
}
|
||||||
Operation::Test => {
|
|
||||||
let value = if b_is_constant {
|
fn test<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) -> Result<(), VmError> {
|
||||||
self.get_constant(b).to_value_ref()
|
let InstructionData {
|
||||||
} else {
|
b,
|
||||||
self.open_register(b)?
|
b_is_constant,
|
||||||
};
|
c,
|
||||||
let boolean = if let ValueRef::Concrete(ConcreteValue::Boolean(boolean)) = value
|
..
|
||||||
{
|
} = instruction_data;
|
||||||
|
let value = vm.get_argument(b, b_is_constant)?;
|
||||||
|
let boolean = if let ValueRef::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
||||||
*boolean
|
*boolean
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedBoolean {
|
panic!(
|
||||||
found: value.to_owned(),
|
"VM Error: Expected boolean value for TEST operation at {}",
|
||||||
position: self.current_position(),
|
vm.current_position()
|
||||||
});
|
);
|
||||||
};
|
|
||||||
|
|
||||||
if boolean == (c != 0) {
|
|
||||||
self.jump(1, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Operation::TestSet => {
|
|
||||||
let value = self.get_argument(b, b_is_constant)?;
|
|
||||||
let boolean = if let ValueRef::Concrete(ConcreteValue::Boolean(boolean)) = value
|
|
||||||
{
|
|
||||||
*boolean
|
|
||||||
} else {
|
|
||||||
return Err(VmError::ExpectedBoolean {
|
|
||||||
found: value.to_owned(),
|
|
||||||
position: self.current_position(),
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
let test_value = c != 0;
|
let test_value = c != 0;
|
||||||
|
|
||||||
if boolean == test_value {
|
if boolean == test_value {
|
||||||
self.jump(1, true);
|
vm.jump_instructions(1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_set<'b, 'c>(
|
||||||
|
vm: &'b mut Vm<'c>,
|
||||||
|
instruction_data: InstructionData,
|
||||||
|
) -> Result<(), VmError> {
|
||||||
|
let InstructionData {
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
b_is_constant,
|
||||||
|
..
|
||||||
|
} = instruction_data;
|
||||||
|
let value = vm.get_argument(b, b_is_constant)?;
|
||||||
|
let boolean = if let ValueRef::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
||||||
|
*boolean
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"VM Error: Expected boolean value for TEST_SET operation at {}",
|
||||||
|
vm.current_position()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
let test_value = c != 0;
|
||||||
|
|
||||||
|
if boolean == test_value {
|
||||||
|
vm.jump_instructions(1, true);
|
||||||
} else {
|
} else {
|
||||||
let pointer = if b_is_constant {
|
let pointer = if b_is_constant {
|
||||||
Pointer::Constant(b)
|
Pointer::Constant(b)
|
||||||
@ -326,148 +412,179 @@ impl<'a> Vm<'a> {
|
|||||||
};
|
};
|
||||||
let register = Register::Pointer(pointer);
|
let register = Register::Pointer(pointer);
|
||||||
|
|
||||||
self.set_register(a, register)?;
|
vm.set_register(a, register)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Operation::Equal => {
|
|
||||||
let left = self.get_argument(b, b_is_constant)?;
|
fn equal<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) -> Result<(), VmError> {
|
||||||
let right = self.get_argument(c, c_is_constant)?;
|
let InstructionData {
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
b_is_constant,
|
||||||
|
c_is_constant,
|
||||||
|
d,
|
||||||
|
..
|
||||||
|
} = instruction_data;
|
||||||
|
let left = vm.get_argument(b, b_is_constant)?;
|
||||||
|
let right = vm.get_argument(c, c_is_constant)?;
|
||||||
let equal_result = left.equal(right).map_err(|error| VmError::Value {
|
let equal_result = left.equal(right).map_err(|error| VmError::Value {
|
||||||
error,
|
error,
|
||||||
position: self.current_position(),
|
position: vm.current_position(),
|
||||||
})?;
|
})?;
|
||||||
let is_equal =
|
let is_equal = if let Value::Concrete(ConcreteValue::Boolean(is_equal)) = equal_result {
|
||||||
if let Value::Concrete(ConcreteValue::Boolean(boolean)) = equal_result {
|
is_equal
|
||||||
boolean
|
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedBoolean {
|
panic!(
|
||||||
found: equal_result,
|
"VM Error: Expected boolean value for EQUAL operation at {}",
|
||||||
position: self.current_position(),
|
vm.current_position()
|
||||||
});
|
);
|
||||||
};
|
};
|
||||||
let comparison = is_equal == d;
|
let comparison = is_equal == d;
|
||||||
let register =
|
let register = Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison)));
|
||||||
Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison)));
|
|
||||||
|
|
||||||
self.set_register(a, register)?;
|
vm.set_register(a, register)
|
||||||
}
|
}
|
||||||
Operation::Less => {
|
|
||||||
let left = if b_is_constant {
|
fn less<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) -> Result<(), VmError> {
|
||||||
self.get_constant(b).to_value_ref()
|
let InstructionData {
|
||||||
} else {
|
a,
|
||||||
self.open_register(b)?
|
b,
|
||||||
};
|
c,
|
||||||
let right = if c_is_constant {
|
b_is_constant,
|
||||||
self.get_constant(c).to_value_ref()
|
c_is_constant,
|
||||||
} else {
|
d,
|
||||||
self.open_register(c)?
|
..
|
||||||
};
|
} = instruction_data;
|
||||||
let less_result = left.less_than(right);
|
let left = vm.get_argument(b, b_is_constant)?;
|
||||||
let less_than_value = match less_result {
|
let right = vm.get_argument(c, c_is_constant)?;
|
||||||
Ok(value) => value,
|
let less_result = left.less(right).map_err(|error| VmError::Value {
|
||||||
Err(error) => {
|
|
||||||
return Err(VmError::Value {
|
|
||||||
error,
|
error,
|
||||||
position: self.current_position(),
|
position: vm.current_position(),
|
||||||
});
|
})?;
|
||||||
}
|
let is_less = if let Value::Concrete(ConcreteValue::Boolean(is_less)) = less_result {
|
||||||
|
is_less
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"VM Error: Expected boolean value for LESS operation at {}",
|
||||||
|
vm.current_position()
|
||||||
|
);
|
||||||
};
|
};
|
||||||
let is_less_than = match less_than_value {
|
let comparison = is_less == d;
|
||||||
Value::Concrete(ConcreteValue::Boolean(boolean)) => boolean,
|
let register = Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison)));
|
||||||
_ => {
|
|
||||||
return Err(VmError::ExpectedBoolean {
|
|
||||||
found: less_than_value,
|
|
||||||
position: self.current_position(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let comparison = is_less_than == d;
|
|
||||||
let register =
|
|
||||||
Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison)));
|
|
||||||
|
|
||||||
self.set_register(a, register)?;
|
vm.set_register(a, register)
|
||||||
}
|
}
|
||||||
Operation::LessEqual => {
|
|
||||||
let left = if b_is_constant {
|
fn less_equal<'b, 'c>(
|
||||||
self.get_constant(b).to_value_ref()
|
vm: &'b mut Vm<'c>,
|
||||||
} else {
|
instruction_data: InstructionData,
|
||||||
self.open_register(b)?
|
) -> Result<(), VmError> {
|
||||||
};
|
let InstructionData {
|
||||||
let right = if c_is_constant {
|
a,
|
||||||
self.get_constant(c).to_value_ref()
|
b,
|
||||||
} else {
|
c,
|
||||||
self.open_register(c)?
|
b_is_constant,
|
||||||
};
|
c_is_constant,
|
||||||
let less_or_equal_result = left.less_than_or_equal(right);
|
d,
|
||||||
let less_or_equal_value = match less_or_equal_result {
|
..
|
||||||
Ok(value) => value,
|
} = instruction_data;
|
||||||
Err(error) => {
|
let left = vm.get_argument(b, b_is_constant)?;
|
||||||
return Err(VmError::Value {
|
let right = vm.get_argument(c, c_is_constant)?;
|
||||||
|
let less_or_equal_result = left.less_equal(right).map_err(|error| VmError::Value {
|
||||||
error,
|
error,
|
||||||
position: self.current_position(),
|
position: vm.current_position(),
|
||||||
});
|
})?;
|
||||||
}
|
let is_less_or_equal = if let Value::Concrete(ConcreteValue::Boolean(is_less_or_equal)) =
|
||||||
|
less_or_equal_result
|
||||||
|
{
|
||||||
|
is_less_or_equal
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"VM Error: Expected boolean value for LESS_EQUAl operation at {}",
|
||||||
|
vm.current_position()
|
||||||
|
);
|
||||||
};
|
};
|
||||||
let is_less_than_or_equal = match less_or_equal_value {
|
let comparison = is_less_or_equal == d;
|
||||||
Value::Concrete(ConcreteValue::Boolean(boolean)) => boolean,
|
let register = Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison)));
|
||||||
_ => {
|
|
||||||
return Err(VmError::ExpectedBoolean {
|
|
||||||
found: less_or_equal_value,
|
|
||||||
position: self.current_position(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let comparison = is_less_than_or_equal == d;
|
|
||||||
let register =
|
|
||||||
Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison)));
|
|
||||||
|
|
||||||
self.set_register(a, register)?;
|
vm.set_register(a, register)
|
||||||
}
|
}
|
||||||
Operation::Negate => {
|
|
||||||
let value = self.get_argument(b, b_is_constant)?;
|
fn negate<'b, 'c>(
|
||||||
|
vm: &'b mut Vm<'c>,
|
||||||
|
instruction_data: InstructionData,
|
||||||
|
) -> Result<(), VmError> {
|
||||||
|
let InstructionData {
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
b_is_constant,
|
||||||
|
..
|
||||||
|
} = instruction_data;
|
||||||
|
let value = vm.get_argument(b, b_is_constant)?;
|
||||||
let negated = value.negate().map_err(|error| VmError::Value {
|
let negated = value.negate().map_err(|error| VmError::Value {
|
||||||
error,
|
error,
|
||||||
position: self.current_position(),
|
position: vm.current_position(),
|
||||||
})?;
|
})?;
|
||||||
let register = Register::Value(negated);
|
let register = Register::Value(negated);
|
||||||
|
|
||||||
self.set_register(a, register)?;
|
vm.set_register(a, register)
|
||||||
}
|
}
|
||||||
Operation::Not => {
|
|
||||||
let value = self.get_argument(b, b_is_constant)?;
|
fn not<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) -> Result<(), VmError> {
|
||||||
|
let InstructionData {
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
b_is_constant,
|
||||||
|
..
|
||||||
|
} = instruction_data;
|
||||||
|
let value = vm.get_argument(b, b_is_constant)?;
|
||||||
let not = value.not().map_err(|error| VmError::Value {
|
let not = value.not().map_err(|error| VmError::Value {
|
||||||
error,
|
error,
|
||||||
position: self.current_position(),
|
position: vm.current_position(),
|
||||||
})?;
|
})?;
|
||||||
let register = Register::Value(not);
|
let register = Register::Value(not);
|
||||||
|
|
||||||
self.set_register(a, register)?;
|
vm.set_register(a, register)
|
||||||
}
|
}
|
||||||
Operation::Jump => {
|
|
||||||
self.jump(b as usize, c != 0);
|
fn jump<'b, 'c>(vm: &mut Vm<'c>, instruction_data: InstructionData) -> Result<(), VmError> {
|
||||||
|
let InstructionData { b, c, .. } = instruction_data;
|
||||||
|
let is_positive = c != 0;
|
||||||
|
|
||||||
|
vm.jump_instructions(b as usize, is_positive);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Operation::Call => {
|
|
||||||
let function = self.get_argument(b, b_is_constant)?;
|
fn call<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) -> Result<(), VmError> {
|
||||||
let chunk = if let ValueRef::Concrete(ConcreteValue::Function(chunk)) = function
|
let InstructionData {
|
||||||
{
|
a,
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
b_is_constant,
|
||||||
|
..
|
||||||
|
} = instruction_data;
|
||||||
|
let function = vm.get_argument(b, b_is_constant)?;
|
||||||
|
let chunk = if let ValueRef::Concrete(ConcreteValue::Function(chunk)) = function {
|
||||||
chunk
|
chunk
|
||||||
} else if let ValueRef::Abstract(AbstractValue::FunctionSelf) = function {
|
} else if let ValueRef::Abstract(AbstractValue::FunctionSelf) = function {
|
||||||
self.chunk
|
vm.chunk
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedFunction {
|
return Err(VmError::ExpectedFunction {
|
||||||
found: function.into_concrete_owned(self)?,
|
found: function.into_concrete_owned(vm)?,
|
||||||
position: self.current_position(),
|
position: vm.current_position(),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
let mut function_vm = Vm::new(self.source, chunk, Some(self));
|
let mut function_vm = Vm::new(vm.source, chunk, Some(vm));
|
||||||
let first_argument_index = a - c;
|
let first_argument_index = a - c;
|
||||||
let mut argument_index = 0;
|
let mut argument_index = 0;
|
||||||
|
|
||||||
for argument_register_index in first_argument_index..a {
|
for argument_register_index in first_argument_index..a {
|
||||||
let target_register_is_empty = matches!(
|
let target_register_is_empty =
|
||||||
self.stack[argument_register_index as usize],
|
matches!(vm.stack[argument_register_index as usize], Register::Empty);
|
||||||
Register::Empty
|
|
||||||
);
|
|
||||||
|
|
||||||
if target_register_is_empty {
|
if target_register_is_empty {
|
||||||
continue;
|
continue;
|
||||||
@ -486,25 +603,27 @@ impl<'a> Vm<'a> {
|
|||||||
if let Some(concrete_value) = return_value {
|
if let Some(concrete_value) = return_value {
|
||||||
let register = Register::Value(concrete_value.to_value());
|
let register = Register::Value(concrete_value.to_value());
|
||||||
|
|
||||||
self.set_register(a, register)?;
|
vm.set_register(a, register)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Operation::CallNative => {
|
|
||||||
let CallNative {
|
fn call_native<'b, 'c>(
|
||||||
destination,
|
vm: &'b mut Vm<'c>,
|
||||||
function,
|
instruction_data: InstructionData,
|
||||||
argument_count,
|
) -> Result<(), VmError> {
|
||||||
} = CallNative::from(&instruction);
|
let InstructionData { a, b, c, .. } = instruction_data;
|
||||||
let first_argument_index = (destination - argument_count) as usize;
|
let first_argument_index = (a - c) as usize;
|
||||||
let argument_range = first_argument_index..destination as usize;
|
let argument_range = first_argument_index..a as usize;
|
||||||
let mut arguments: SmallVec<[ValueRef; 4]> = SmallVec::new();
|
let mut arguments: SmallVec<[ValueRef; 4]> = SmallVec::new();
|
||||||
|
|
||||||
for register_index in argument_range {
|
for register_index in argument_range {
|
||||||
let register = &self.stack[register_index];
|
let register = &vm.stack[register_index];
|
||||||
let value = match register {
|
let value = match register {
|
||||||
Register::Value(value) => value.to_ref(),
|
Register::Value(value) => value.to_ref(),
|
||||||
Register::Pointer(pointer) => {
|
Register::Pointer(pointer) => {
|
||||||
let value_option = self.follow_pointer_allow_empty(*pointer)?;
|
let value_option = vm.follow_pointer_allow_empty(*pointer)?;
|
||||||
|
|
||||||
match value_option {
|
match value_option {
|
||||||
Some(value) => value,
|
Some(value) => value,
|
||||||
@ -517,7 +636,8 @@ impl<'a> Vm<'a> {
|
|||||||
arguments.push(value);
|
arguments.push(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
let call_result = function.call(self, arguments);
|
let function = NativeFunction::from(b);
|
||||||
|
let call_result = function.call(vm, arguments);
|
||||||
let return_value = match call_result {
|
let return_value = match call_result {
|
||||||
Ok(value_option) => value_option,
|
Ok(value_option) => value_option,
|
||||||
Err(error) => return Err(VmError::NativeFunction(error)),
|
Err(error) => return Err(VmError::NativeFunction(error)),
|
||||||
@ -526,21 +646,35 @@ impl<'a> Vm<'a> {
|
|||||||
if let Some(value) = return_value {
|
if let Some(value) = return_value {
|
||||||
let register = Register::Value(value);
|
let register = Register::Value(value);
|
||||||
|
|
||||||
self.set_register(destination, register)?;
|
vm.set_register(a, register)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Operation::Return => {
|
|
||||||
let Return {
|
pub fn run(&mut self) -> Result<Option<ConcreteValue>, VmError> {
|
||||||
should_return_value,
|
loop {
|
||||||
} = Return::from(&instruction);
|
let instruction = self.read();
|
||||||
|
let (operation, instruction_data) = instruction.decode();
|
||||||
|
|
||||||
|
log::info!(
|
||||||
|
"{} | {} | {} | {}",
|
||||||
|
self.ip - 1,
|
||||||
|
self.current_position(),
|
||||||
|
instruction.operation(),
|
||||||
|
instruction.disassembly_info()
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Operation::Return = operation {
|
||||||
|
let should_return_value = instruction_data.b != 0;
|
||||||
|
|
||||||
if !should_return_value {
|
if !should_return_value {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
return if let Some(register_index) = self.last_assigned_register {
|
return if let Some(register_index) = &self.last_assigned_register {
|
||||||
let return_value = self
|
let return_value = self
|
||||||
.open_register(register_index)?
|
.open_register(*register_index)?
|
||||||
.into_concrete_owned(self)?;
|
.into_concrete_owned(self)?;
|
||||||
|
|
||||||
Ok(Some(return_value))
|
Ok(Some(return_value))
|
||||||
@ -549,8 +683,10 @@ impl<'a> Vm<'a> {
|
|||||||
position: self.current_position(),
|
position: self.current_position(),
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
} else {
|
||||||
_ => unreachable!(),
|
let runner = RUNNERS[operation as usize];
|
||||||
|
|
||||||
|
runner(self, instruction_data).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -659,7 +795,7 @@ impl<'a> Vm<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// DRY helper for handling JUMP instructions
|
/// DRY helper for handling JUMP instructions
|
||||||
fn jump(&mut self, offset: usize, is_positive: bool) {
|
fn jump_instructions(&mut self, offset: usize, is_positive: bool) {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
"Jumping {}",
|
"Jumping {}",
|
||||||
if is_positive {
|
if is_positive {
|
||||||
@ -901,3 +1037,48 @@ impl AnnotatedError for VmError {
|
|||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const ALL_OPERATIONS: [(Operation, Runner); 23] = [
|
||||||
|
(Operation::Move, Vm::r#move),
|
||||||
|
(Operation::Close, Vm::close),
|
||||||
|
(Operation::LoadBoolean, Vm::load_boolean),
|
||||||
|
(Operation::LoadConstant, Vm::load_constant),
|
||||||
|
(Operation::LoadList, Vm::load_list),
|
||||||
|
(Operation::LoadSelf, Vm::load_self),
|
||||||
|
(Operation::GetLocal, Vm::get_local),
|
||||||
|
(Operation::SetLocal, Vm::set_local),
|
||||||
|
(Operation::Add, Vm::add),
|
||||||
|
(Operation::Subtract, Vm::subtract),
|
||||||
|
(Operation::Multiply, Vm::multiply),
|
||||||
|
(Operation::Divide, Vm::divide),
|
||||||
|
(Operation::Modulo, Vm::modulo),
|
||||||
|
(Operation::Test, Vm::test),
|
||||||
|
(Operation::TestSet, Vm::test_set),
|
||||||
|
(Operation::Equal, Vm::equal),
|
||||||
|
(Operation::Less, Vm::less),
|
||||||
|
(Operation::LessEqual, Vm::less_equal),
|
||||||
|
(Operation::Negate, Vm::negate),
|
||||||
|
(Operation::Not, Vm::not),
|
||||||
|
(Operation::Call, Vm::call),
|
||||||
|
(Operation::CallNative, Vm::call_native),
|
||||||
|
(Operation::Jump, Vm::jump),
|
||||||
|
];
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn operations_map_to_the_correct_runner() {
|
||||||
|
for (operation, expected_runner) in ALL_OPERATIONS {
|
||||||
|
let actual_runner = RUNNERS[operation as usize];
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
expected_runner, actual_runner,
|
||||||
|
"{operation} runner is incorrect"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user