aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCody <cody@codyq.dev>2023-04-10 21:12:53 -0500
committerCody <cody@codyq.dev>2023-04-10 21:12:53 -0500
commit814fa68350cf75d5848ec6b4914e3f3bafee10fa (patch)
tree26e598fc55e3d8e6217797fd78597987ffe3ca11
parentc458e9f46afcd04445dcb35d1fd9e2a85a451937 (diff)
downloadsloth-814fa68350cf75d5848ec6b4914e3f3bafee10fa.tar.gz
Beginning progress on virtual machine
-rw-r--r--Cargo.lock3
-rw-r--r--crates/sloth_bytecode/macros/src/lib.rs4
-rw-r--r--crates/sloth_bytecode/src/lib.rs23
-rw-r--r--crates/sloth_vm/Cargo.toml2
-rw-r--r--crates/sloth_vm/src/lib.rs261
5 files changed, 274 insertions, 19 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 157650a..1684d93 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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)));
+ }
}