diff --git a/rvm/.gitignore b/rvm/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/rvm/.gitignore @@ -0,0 +1 @@ +/target diff --git a/rvm/Cargo.lock b/rvm/Cargo.lock new file mode 100644 index 0000000..8396fbd --- /dev/null +++ b/rvm/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "rvm" +version = "0.1.0" diff --git a/rvm/Cargo.toml b/rvm/Cargo.toml new file mode 100644 index 0000000..f0efbb2 --- /dev/null +++ b/rvm/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rvm" +version = "0.1.0" +authors = ["Jaime "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/rvm/README.md b/rvm/README.md new file mode 100644 index 0000000..d491d7b --- /dev/null +++ b/rvm/README.md @@ -0,0 +1,4 @@ +# RVM [Rust VM] +Reference Bytecode Virtual Machine implemented in Rust. + +WIP experiment based off Clox. \ No newline at end of file diff --git a/rvm/src/chunk.rs b/rvm/src/chunk.rs new file mode 100644 index 0000000..46794d6 --- /dev/null +++ b/rvm/src/chunk.rs @@ -0,0 +1,31 @@ +use crate::opcode::OpCode; +use crate::value::Value; + +#[derive(Debug)] +pub struct Chunk { + pub codes: Vec, + + // Constant Pool: Vector storing all the constant values dynamically and accessed using index stored in 'codes' vector + pub constants: Vec, + + // Optimize storing line info: https://en.wikipedia.org/wiki/Run-length_encoding + pub lines: Vec, +} + +impl Chunk { + // Associated function to create a new Chunk + pub fn new() -> Chunk { + Chunk { + // codes: Vec::::new(), + codes: Vec::::with_capacity(6), + constants: Vec::new(), + lines: Vec::new(), + } + } + + // Method to write a new OpCode + pub fn write(&mut self, byte: OpCode, line_number: usize) { + self.codes.push(byte); + self.lines.push(line_number); + } +} diff --git a/rvm/src/debug.rs b/rvm/src/debug.rs new file mode 100644 index 0000000..6dcffd4 --- /dev/null +++ b/rvm/src/debug.rs @@ -0,0 +1,58 @@ +use crate::chunk::Chunk; +use crate::opcode::OpCode; + +// pub fn disassemble_chunk(chunk: Chunk, name: &String) { +pub fn disassemble_chunk(chunk: &Chunk, name: &str) { + // println!("{:?} ", chunk); + println!("== {} ==", name); + + let mut offset: usize = 0; + while offset < chunk.codes.len() { + offset = disassemble_instruction(&chunk, offset); + } +} + +fn disassemble_instruction(chunk: &Chunk, offset: usize) -> usize { + // Prints the offset (bytecode index of a chunk) with 0 padding up to 3 digits + // https://stackoverflow.com/a/41821049 + print!("{:0width$}", offset, width = 3); + + // Print line number or | for bytecodes on the same line + if offset > 0 && chunk.lines[offset] == chunk.lines[offset - 1] { + print!(" | "); + } else { + print!("{:4} ", chunk.lines[offset]); + } + + match chunk.codes[offset] { + OpCode::RETURN => simple_instruction("RETURN", &chunk, offset), + OpCode::CONSTANT => constant_instruction("CONSTANT", &chunk, offset), + // OpCode::ConstantIndex(index) => simple_instruction("ConstantIndex", offset), + // + ref instruction => { + println!("Unknown opcode {:?}\n", instruction); + offset + 1 + } + } +} +/* + Printing should be of the format + Line number OpCode index OpCode string representation add. data if any +*/ + +// fn simple_instruction(name: &str, offset: usize) -> usize { +// println!("{}", name); +// offset + 1 +// } +fn simple_instruction(name: &str, chunk: &Chunk, offset: usize) -> usize { + println!("{:?}", chunk.codes[offset]); + offset + 1 +} + +fn constant_instruction(name: &str, chunk: &Chunk, offset: usize) -> usize { + let constant = &chunk.constants[offset]; + + // println!("{} -> {:?}", name, constant); + println!("{:?} -> {:?}", chunk.codes[offset], constant); + offset + 2 +} diff --git a/rvm/src/main.rs b/rvm/src/main.rs new file mode 100644 index 0000000..b016f6b --- /dev/null +++ b/rvm/src/main.rs @@ -0,0 +1,24 @@ +mod chunk; +mod debug; +mod opcode; +mod value; +mod vm; + +use chunk::Chunk; +use debug::disassemble_chunk; +use opcode::OpCode; +use value::Value; +use vm::VM; + +fn main() { + let mut chunk = Chunk::new(); + + chunk.write(OpCode::CONSTANT, 2); + chunk.constants.push(Value::Number(1.2)); + chunk.write(OpCode::ConstantIndex(chunk.constants.len()), 2); + + chunk.write(OpCode::RETURN, 2); + + disassemble_chunk(&chunk, "test"); + // println!("{:?}", chunk); +} diff --git a/rvm/src/opcode.rs b/rvm/src/opcode.rs new file mode 100644 index 0000000..21d1fb8 --- /dev/null +++ b/rvm/src/opcode.rs @@ -0,0 +1,13 @@ +// There are 2 types of OpCodes +// First type are the instruction OpCodes, that are denoted by their all CAPS spelling +// Secondly there are special OpCodes, that are variants with additional values like 'ConstantIndex(usize)' +// +// Clox differentiate OpCode from Data using OP_ prefix, and also all their data types is just a single byte anyways + +#[derive(Debug)] +pub enum OpCode { + RETURN, + CONSTANT, + + ConstantIndex(usize), +} diff --git a/rvm/src/value.rs b/rvm/src/value.rs new file mode 100644 index 0000000..3734a29 --- /dev/null +++ b/rvm/src/value.rs @@ -0,0 +1,9 @@ +// type Value = f64; + +#[derive(Debug)] +pub enum Value { + Number(f64), + String(String), + Bool(bool), + Null, +} diff --git a/rvm/src/vm.rs b/rvm/src/vm.rs new file mode 100644 index 0000000..822b76e --- /dev/null +++ b/rvm/src/vm.rs @@ -0,0 +1,20 @@ +use crate::chunk::Chunk; +use crate::opcode::OpCode; + +pub struct VM { + pub chunks: Vec, +} + +// Change this to error or something +pub enum InterpretResult { + Ok, + CompileError, + RuntimeError, +} + +impl VM { + // CHange this to result type + pub fn interpret(chunk: &Chunk) -> InterpretResult { + InterpretResult::Ok + } +}