diff options
| author | Cody <cody@codyq.dev> | 2023-04-10 21:12:53 -0500 |
|---|---|---|
| committer | Cody <cody@codyq.dev> | 2023-04-10 21:12:53 -0500 |
| commit | 814fa68350cf75d5848ec6b4914e3f3bafee10fa (patch) | |
| tree | 26e598fc55e3d8e6217797fd78597987ffe3ca11 | |
| parent | c458e9f46afcd04445dcb35d1fd9e2a85a451937 (diff) | |
| download | sloth-814fa68350cf75d5848ec6b4914e3f3bafee10fa.tar.gz | |
Beginning progress on virtual machine
| -rw-r--r-- | Cargo.lock | 3 | ||||
| -rw-r--r-- | crates/sloth_bytecode/macros/src/lib.rs | 4 | ||||
| -rw-r--r-- | crates/sloth_bytecode/src/lib.rs | 23 | ||||
| -rw-r--r-- | crates/sloth_vm/Cargo.toml | 2 | ||||
| -rw-r--r-- | crates/sloth_vm/src/lib.rs | 261 |
5 files changed, 274 insertions, 19 deletions
@@ -69,6 +69,9 @@ dependencies = [ [[package]] name = "sloth_vm" version = "0.1.0" +dependencies = [ + "sloth_bytecode", +] [[package]] name = "syn" diff --git a/crates/sloth_bytecode/macros/src/lib.rs b/crates/sloth_bytecode/macros/src/lib.rs index 15a09d0..31f462f 100644 --- a/crates/sloth_bytecode/macros/src/lib.rs +++ b/crates/sloth_bytecode/macros/src/lib.rs @@ -142,12 +142,12 @@ pub fn instructions(input: proc_macro::TokenStream) -> proc_macro::TokenStream { quote! { #[repr(u8)] #[derive(Clone, Debug, Eq, PartialEq)] - enum #enum_name { + pub enum #enum_name { #( #enum_fields ),* } impl #enum_name { - fn disassemble(chunk: &[u8], offset: &mut usize) -> #enum_name { + pub fn disassemble(chunk: &[u8], offset: &mut usize) -> #enum_name { let opcode = chunk[*offset]; *offset += 1; diff --git a/crates/sloth_bytecode/src/lib.rs b/crates/sloth_bytecode/src/lib.rs index 3593f3c..e499597 100644 --- a/crates/sloth_bytecode/src/lib.rs +++ b/crates/sloth_bytecode/src/lib.rs @@ -16,7 +16,7 @@ pub enum Error { } instructions! { - Instructions; + Instruction; 0x00 Constant [u64] "Push a constant value onto the stack", 0x01 Load [u64] "Load a value from a variable", @@ -31,12 +31,13 @@ instructions! { 0x23 Div [] "Divide the last 2 values on the stack", 0x24 Mod [] "Modulo the last 2 values on the stack", - 0xF0 Print [] "[DEBUG] Pop value from stack and print it" + 0xF0 VMReturn [] "[DEBUG] Pop value from stack and return it from the program", + 0xF1 VMExit [] "[DEBUG] Exit from the VM", } #[cfg(test)] mod tests { - use crate::Instructions; + use crate::Instruction; #[test] #[rustfmt::skip] @@ -50,13 +51,13 @@ mod tests { let mut offset = 0; - assert_eq!(Instructions::disassemble(&code, &mut offset), Instructions::Constant(0)); - assert_eq!(Instructions::disassemble(&code, &mut offset), Instructions::Dup); - assert_eq!(Instructions::disassemble(&code, &mut offset), Instructions::Pop); - assert_eq!(Instructions::disassemble(&code, &mut offset), Instructions::Add); - assert_eq!(Instructions::disassemble(&code, &mut offset), Instructions::Sub); - assert_eq!(Instructions::disassemble(&code, &mut offset), Instructions::Mul); - assert_eq!(Instructions::disassemble(&code, &mut offset), Instructions::Div); - assert_eq!(Instructions::disassemble(&code, &mut offset), Instructions::Mod); + assert_eq!(Instruction::disassemble(&code, &mut offset), Instruction::Constant(0)); + assert_eq!(Instruction::disassemble(&code, &mut offset), Instruction::Dup); + assert_eq!(Instruction::disassemble(&code, &mut offset), Instruction::Pop); + assert_eq!(Instruction::disassemble(&code, &mut offset), Instruction::Add); + assert_eq!(Instruction::disassemble(&code, &mut offset), Instruction::Sub); + assert_eq!(Instruction::disassemble(&code, &mut offset), Instruction::Mul); + assert_eq!(Instruction::disassemble(&code, &mut offset), Instruction::Div); + assert_eq!(Instruction::disassemble(&code, &mut offset), Instruction::Mod); } } diff --git a/crates/sloth_vm/Cargo.toml b/crates/sloth_vm/Cargo.toml index e71c18d..1b1b6fe 100644 --- a/crates/sloth_vm/Cargo.toml +++ b/crates/sloth_vm/Cargo.toml @@ -6,3 +6,5 @@ version.workspace = true edition.workspace = true [dependencies] +sloth_bytecode = { path = "../sloth_bytecode" } + diff --git a/crates/sloth_vm/src/lib.rs b/crates/sloth_vm/src/lib.rs index 1dfb191..9240577 100644 --- a/crates/sloth_vm/src/lib.rs +++ b/crates/sloth_vm/src/lib.rs @@ -7,24 +7,273 @@ unused_lifetimes )] -const STACK_SIZE: usize = 1024; +use sloth_bytecode::Instruction; pub struct Chunk { code: Vec<u8>, - constants: Vec<u64>, + constants: Vec<Data>, } pub struct VM { - stack: [u8; STACK_SIZE], - constants: Vec<u8>, + vm_return: Option<Data>, + stack: Stack, } impl VM { - // + fn new() -> Self { + Self { + vm_return: None, + stack: Stack::default(), + } + } + + fn run(&mut self, chunk: &Chunk) { + let mut pointer = 0; + + loop { + let instruction = Instruction::disassemble(&chunk.code, &mut pointer); + + match instruction { + Instruction::Constant(idx) => { + let value = chunk.constants[idx as usize]; + self.stack.push(value); + } + Instruction::Load(_) => todo!(), + Instruction::Push(_) => todo!(), + Instruction::Dup => { + let value = self.stack.pop(); + self.stack.push(value); + self.stack.push(value); + } + Instruction::Pop => { + self.stack.pop(); + } + Instruction::Add => { + let value = match self.stack.pop2() { + (Data::Integer(lhs), Data::Integer(rhs)) => Data::Integer(lhs + rhs), + (Data::Float(lhs), Data::Float(rhs)) => Data::Float(lhs + rhs), + _ => panic!(), + }; + + self.stack.push(value); + } + Instruction::Sub => { + let value = match self.stack.pop2() { + (Data::Integer(lhs), Data::Integer(rhs)) => Data::Integer(lhs - rhs), + (Data::Float(lhs), Data::Float(rhs)) => Data::Float(lhs - rhs), + _ => panic!(), + }; + + self.stack.push(value); + } + Instruction::Mul => { + let value = match self.stack.pop2() { + (Data::Integer(lhs), Data::Integer(rhs)) => Data::Integer(lhs * rhs), + (Data::Float(lhs), Data::Float(rhs)) => Data::Float(lhs * rhs), + _ => panic!(), + }; + + self.stack.push(value); + } + Instruction::Div => { + let value = match self.stack.pop2() { + (Data::Integer(lhs), Data::Integer(rhs)) => Data::Integer(lhs / rhs), + (Data::Float(lhs), Data::Float(rhs)) => Data::Float(lhs / rhs), + _ => panic!(), + }; + + self.stack.push(value); + } + Instruction::Mod => { + let value = match self.stack.pop2() { + (Data::Integer(lhs), Data::Integer(rhs)) => Data::Integer(lhs % rhs), + (Data::Float(lhs), Data::Float(rhs)) => Data::Float(lhs % rhs), + _ => panic!(), + }; + + self.stack.push(value); + } + Instruction::VMReturn => { + let value = self.stack.pop(); + self.vm_return = Some(value); + break; + } + Instruction::VMExit => break, + } + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Data { + Integer(i128), + Float(f64), + Bool(bool), + Empty, +} + +const STACK_SIZE: usize = 1024; + +#[derive(Debug)] +pub struct Stack { + pointer: usize, + stack: [Data; STACK_SIZE], +} + +impl Default for Stack { + fn default() -> Self { + Self { + pointer: Default::default(), + stack: [Data::Empty; STACK_SIZE], + } + } +} + +impl Stack { + #[inline(always)] + pub fn push(&mut self, value: Data) { + if self.pointer >= STACK_SIZE { + panic!("Stack overflow"); + } + + self.stack[self.pointer] = value; + self.pointer += 1; + } + + #[inline(always)] + pub fn pop(&mut self) -> Data { + if self.pointer == 0 { + panic!("Stack underflow"); + } + + self.pointer -= 1; + self.stack[self.pointer] + } + + #[inline(always)] + pub fn pop2(&mut self) -> (Data, Data) { + (self.pop(), self.pop()) + } } #[cfg(test)] mod tests { + use crate::{Chunk, Data, VM}; + #[test] - fn add_program() {} + fn arithmetic_ops() { + let mut vm = VM::new(); + + // Addition + vm.run(&Chunk { + code: vec![ + 0x00, 0, 0, 0, 0, 0, 0, 0, 0, // Load constant from 0 + 0x10, // Duplicate + 0x20, // Add + 0xF0, // Return VM + ], + constants: vec![Data::Integer(7)], + }); + + let add1 = vm.vm_return; + + vm.run(&Chunk { + code: vec![ + 0x00, 0, 0, 0, 0, 0, 0, 0, 0, // Load constant from 0 + 0x00, 0, 0, 0, 0, 0, 0, 0, 1, // Load constant from 1 + 0x20, // Add + 0xF0, // Return VM + ], + constants: vec![Data::Integer(2), Data::Integer(11)], + }); + + let add2 = vm.vm_return; + + // Subtraction + vm.run(&Chunk { + code: vec![ + 0x00, 0, 0, 0, 0, 0, 0, 0, 0, // Load constant from 0 + 0x10, // Duplicate + 0x21, // Subtraction + 0xF0, // Return VM + ], + constants: vec![Data::Integer(7)], + }); + + let sub1 = vm.vm_return; + + vm.run(&Chunk { + code: vec![ + 0x00, 0, 0, 0, 0, 0, 0, 0, 0, // Load constant from 0 + 0x00, 0, 0, 0, 0, 0, 0, 0, 1, // Load constant from 1 + 0x21, // Subtraction + 0xF0, // Return VM + ], + constants: vec![Data::Integer(2), Data::Integer(11)], + }); + + let sub2 = vm.vm_return; + + // Multiplication + vm.run(&Chunk { + code: vec![ + 0x00, 0, 0, 0, 0, 0, 0, 0, 0, // Load constant from 0 + 0x10, // Duplicate + 0x22, // Multiplication + 0xF0, // Return VM + ], + constants: vec![Data::Integer(7)], + }); + + let mul1 = vm.vm_return; + + vm.run(&Chunk { + code: vec![ + 0x00, 0, 0, 0, 0, 0, 0, 0, 0, // Load constant from 0 + 0x00, 0, 0, 0, 0, 0, 0, 0, 1, // Load constant from 1 + 0x22, // Multiplication + 0xF0, // Return VM + ], + constants: vec![Data::Integer(2), Data::Integer(11)], + }); + + let mul2 = vm.vm_return; + + // Division + vm.run(&Chunk { + code: vec![ + 0x00, 0, 0, 0, 0, 0, 0, 0, 0, // Load constant from 0 + 0x10, // Duplicate + 0x23, // Division + 0xF0, // Return VM + ], + constants: vec![Data::Integer(7)], + }); + + let div1 = vm.vm_return; + + vm.run(&Chunk { + code: vec![ + 0x00, 0, 0, 0, 0, 0, 0, 0, 0, // Load constant from 0 + 0x00, 0, 0, 0, 0, 0, 0, 0, 1, // Load constant from 1 + 0x23, // Division + 0xF0, // Return VM + ], + constants: vec![Data::Integer(2), Data::Integer(11)], + }); + + let div2 = vm.vm_return; + + assert_eq!(add1, Some(Data::Integer(14))); + assert_eq!(add2, Some(Data::Integer(13))); + + assert_eq!(sub1, Some(Data::Integer(0))); + assert_eq!(sub2, Some(Data::Integer(9))); + + assert_eq!(mul1, Some(Data::Integer(49))); + assert_eq!(mul2, Some(Data::Integer(22))); + + assert_eq!(div1, Some(Data::Integer(1))); + assert_eq!(div2, Some(Data::Integer(5))); + } } |
