From 934cebc598dcc00e123da1c761584dc88e05e571 Mon Sep 17 00:00:00 2001 From: Alpha Chen Date: Tue, 11 Oct 2022 19:49:52 -0700 Subject: [PATCH] 14 --- rust/.rustfmt.toml | 6 ++ rust/src/chunk.rs | 156 ++++++++++----------------------------------- rust/src/main.rs | 22 ++----- rust/src/value.rs | 1 + rust/src/vec.rs | 118 ---------------------------------- rust/src/vm.rs | 33 ---------- 6 files changed, 47 insertions(+), 289 deletions(-) create mode 100644 rust/.rustfmt.toml create mode 100644 rust/src/value.rs delete mode 100644 rust/src/vec.rs delete mode 100644 rust/src/vm.rs diff --git a/rust/.rustfmt.toml b/rust/.rustfmt.toml new file mode 100644 index 0000000..c2c8317 --- /dev/null +++ b/rust/.rustfmt.toml @@ -0,0 +1,6 @@ +condense_wildcard_suffixes = true +format_strings = true +reorder_impl_items = true +group_imports = "StdExternalCrate" +use_field_init_shorthand = true +wrap_comments = true diff --git a/rust/src/chunk.rs b/rust/src/chunk.rs index 688764f..c57fa76 100644 --- a/rust/src/chunk.rs +++ b/rust/src/chunk.rs @@ -1,148 +1,62 @@ -use std::mem; +use std::fmt; -use vec::Vec; +use crate::value::Value; -use crate::vec; - -#[repr(u8)] +#[derive(Debug)] pub enum OpCode { - Constant, - LongConstant, + Constant(usize), Return, } -type Value = f32; +#[derive(Debug)] +pub struct Instruction { + op_code: OpCode, + line: usize, +} -#[derive(Default)] +#[derive(Debug, Default)] pub struct Chunk { - pub code: Vec, - constants: Vec, - lines: Lines, + pub code: Vec, + pub constants: Vec, } impl Chunk { - pub fn write(&mut self, byte: u8, line: usize) { - self.code.push(byte); - self.lines.add(line); + pub fn push(&mut self, op_code: OpCode, line: usize) { + self.code.push(Instruction { op_code, line }); } - pub fn write_constant(&mut self, value: Value, line: usize) { + pub fn push_constant(&mut self, value: Value, line: usize) { self.constants.push(value); - let index = self.constants.len() - 1; - - if let Ok(index) = index.try_into() { - self.write(OpCode::Constant as u8, line); - self.write(index, line); - } else { - self.write(OpCode::LongConstant as u8, line); - for byte in index.to_ne_bytes() { - self.write(byte, line); - } - } + self.push(OpCode::Constant(self.constants.len() - 1), line) } pub fn disassemble(&self, name: &str) { - println!("== {} ==", name); - - let mut offset = 0; - while offset < self.code.len() { - offset = self.disassemble_instruction(offset); - } - } - - fn disassemble_instruction(&self, offset: usize) -> usize { - print!("{:04} ", offset); - - let (line, is_first) = self.lines.get(offset); - if is_first { - print!("{:>4} ", line); - } else { - print!(" | "); - } - - match self.code[offset] { - 0 => self.constant_instruction("OP_CONSTANT", offset), - 1 => self.constant_long_instruction("OP_CONSTANT_LONG", offset), - 2 => self.simple_instruction("OP_RETURN", offset), - _ => unreachable!(), - } - } - - fn simple_instruction(&self, name: &str, offset: usize) -> usize { - println!("{}", name); - offset + 1 - } - - fn constant_instruction(&self, name: &str, offset: usize) -> usize { - let constant_index = self.code[offset + 1]; - let value = self.constants[constant_index as usize]; - println!("{:<16} {:>4} '{}'", name, constant_index, value); - offset + 2 + println!("== {} ==\n{}", name, self); } - - fn constant_long_instruction(&self, name: &str, offset: usize) -> usize { - let index_len = mem::size_of::(); - let index_bytes = &self.code[offset + 1..offset + 1 + index_len]; - - let (int_bytes, _) = index_bytes.split_at(std::mem::size_of::()); - let constant_index = usize::from_ne_bytes(int_bytes.try_into().unwrap()); - - let value = self.constants[constant_index as usize]; - println!("{:<16} {:>4} '{}'", name, constant_index, value); - offset + 1 + index_len - } -} - -#[test] -fn test_constant_long() { - let mut chunk = Chunk::default(); - - for i in 0..=u8::MAX { - chunk.write_constant(i.into(), 123); - } - - chunk.write_constant(0.0, 123); - - // TODO Make the disassembler testable } -// Lines are stored using run-length encoding, where the first element is the line and the second -// element the number of instructions that are associated with that line -#[derive(Debug, Default)] -struct Lines(std::vec::Vec<(usize, usize)>); - -impl Lines { - fn add(&mut self, line: usize) { - if let Some(last) = self.0.last_mut() { - last.1 += 1; - } else { - self.0.push((line, 1)); - } - } +impl fmt::Display for Chunk { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut last_line: Option = None; - fn get(&self, offset: usize) -> (usize, bool) { - let mut offset = offset; - for (line, run) in self.0.iter() { - if offset == 0 { - return (*line, true); + for (i, instruction) in self.code.iter().enumerate() { + write!(f, "{:04} ", i)?; + if last_line == Some(instruction.line) { + write!(f, " | ")?; + } else { + write!(f, "{:>4} ", instruction.line)?; } - - if offset < *run { - return (*line, false); + last_line = Some(instruction.line); + + match instruction.op_code { + OpCode::Constant(constant) => { + let value = self.constants[constant]; + writeln!(f, "{:<16} {:4} '{}'", "OP_CONSTANT", constant, value)?; + } + OpCode::Return => writeln!(f, "OP_RETURN")?, } - - offset -= run; } - unreachable!() + Ok(()) } } - -#[test] -fn test_get_line() { - let lines = Lines(vec![(1_usize, 2_usize), (2_usize, 2_usize)]); - assert_eq!(lines.get(0), (1, true)); - assert_eq!(lines.get(1), (1, false)); - assert_eq!(lines.get(2), (2, true)); - assert_eq!(lines.get(3), (2, false)); -} diff --git a/rust/src/main.rs b/rust/src/main.rs index 2ec0252..71cc773 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -1,25 +1,13 @@ use chunk::{Chunk, OpCode}; -use vm::VM; -pub mod chunk; -pub mod vec; -pub mod vm; +mod chunk; +mod value; fn main() { let mut chunk = Chunk::default(); - chunk.write_constant(1.2, 123); + chunk.push_constant(1.2, 123); + chunk.push(OpCode::Return, 123); - chunk.write(OpCode::Return as u8, 123); - - // for i in 0..=u8::MAX { - // chunk.write_constant(i.into(), 123); - // } - - // chunk.write_constant(0.0, 123); - - // chunk.disassemble("test chunk"); - - let vm = VM::new(chunk); - vm.interpret(); + chunk.disassemble("test chunk"); } diff --git a/rust/src/value.rs b/rust/src/value.rs new file mode 100644 index 0000000..343d142 --- /dev/null +++ b/rust/src/value.rs @@ -0,0 +1 @@ +pub type Value = f64; diff --git a/rust/src/vec.rs b/rust/src/vec.rs deleted file mode 100644 index 195e207..0000000 --- a/rust/src/vec.rs +++ /dev/null @@ -1,118 +0,0 @@ -// https://doc.rust-lang.org/nomicon/vec/vec.html - -use std::alloc::{self, Layout}; -use std::marker::PhantomData; -use std::mem; -use std::ops::{Deref, DerefMut}; -use std::ptr::{self, NonNull}; - -pub struct Vec { - ptr: NonNull, - cap: usize, - len: usize, - _marker: PhantomData, -} - -unsafe impl Send for Vec {} -unsafe impl Sync for Vec {} - -impl Vec { - pub fn new() -> Self { - assert!(mem::size_of::() != 0, "We're not ready to handle ZSTs"); - Vec { - ptr: NonNull::dangling(), - len: 0, - cap: 0, - _marker: PhantomData, - } - } - - pub fn push(&mut self, elem: T) { - if self.len == self.cap { - self.grow(); - } - - unsafe { - ptr::write(self.ptr.as_ptr().add(self.len), elem); - } - - // Can't fail, we'll OOM first. - self.len += 1; - } - - pub fn pop(&mut self) -> Option { - if self.len == 0 { - None - } else { - self.len -= 1; - unsafe { Some(ptr::read(self.ptr.as_ptr().add(self.len))) } - } - } - - fn grow(&mut self) { - let (new_cap, new_layout) = if self.cap == 0 { - (1, Layout::array::(1).unwrap()) - } else { - // This can't overflow since self.cap <= isize::MAX. - let new_cap = 2 * self.cap; - - // `Layout::array` checks that the number of bytes is <= usize::MAX, - // but this is redundant since old_layout.size() <= isize::MAX, - // so the `unwrap` should never fail. - let new_layout = Layout::array::(new_cap).unwrap(); - (new_cap, new_layout) - }; - - // Ensure that the new allocation doesn't exceed `isize::MAX` bytes. - assert!( - new_layout.size() <= isize::MAX as usize, - "Allocation too large" - ); - - let new_ptr = if self.cap == 0 { - unsafe { alloc::alloc(new_layout) } - } else { - let old_layout = Layout::array::(self.cap).unwrap(); - let old_ptr = self.ptr.as_ptr() as *mut u8; - unsafe { alloc::realloc(old_ptr, old_layout, new_layout.size()) } - }; - - // If allocation fails, `new_ptr` will be null, in which case we abort. - self.ptr = match NonNull::new(new_ptr as *mut T) { - Some(p) => p, - None => alloc::handle_alloc_error(new_layout), - }; - self.cap = new_cap; - } -} - -impl Drop for Vec { - fn drop(&mut self) { - if self.cap != 0 { - while self.pop().is_some() {} - let layout = Layout::array::(self.cap).unwrap(); - unsafe { - alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout); - } - } - } -} - -impl Deref for Vec { - type Target = [T]; - fn deref(&self) -> &[T] { - unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) } - } -} - -impl DerefMut for Vec { - fn deref_mut(&mut self) -> &mut [T] { - unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) } - } -} - -impl Default for Vec { - fn default() -> Self { - Self::new() - } -} diff --git a/rust/src/vm.rs b/rust/src/vm.rs deleted file mode 100644 index 8b61340..0000000 --- a/rust/src/vm.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::ptr; - -use anyhow::Result; -use thiserror::Error; - -use crate::chunk::Chunk; - -pub struct VM { - chunk: Chunk, - ip: *const u8, -} - -#[derive(Error, Debug)] -pub enum DataStoreError { - #[error("")] - CompileError, - #[error("")] - RuntimeError, -} - -impl VM { - pub fn new(chunk: Chunk) -> Self { - VM { chunk, ip: &chunk.code.ptr } - } - - pub fn interpret(&self) -> Result<()> { - self.run() - } - - fn run(&self) -> Result<()> { - todo!() - } -}