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<u8>,
- constants: Vec<Value>,
- lines: Lines,
+ pub code: Vec<Instruction>,
+ pub constants: Vec<Value>,
}
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::<usize>();
- let index_bytes = &self.code[offset + 1..offset + 1 + index_len];
-
- let (int_bytes, _) = index_bytes.split_at(std::mem::size_of::<usize>());
- 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<usize> = 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<T> {
- ptr: NonNull<T>,
- cap: usize,
- len: usize,
- _marker: PhantomData<T>,
-}
-
-unsafe impl<T: Send> Send for Vec<T> {}
-unsafe impl<T: Sync> Sync for Vec<T> {}
-
-impl<T> Vec<T> {
- pub fn new() -> Self {
- assert!(mem::size_of::<T>() != 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<T> {
- 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::<T>(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::<T>(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::<T>(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<T> Drop for Vec<T> {
- fn drop(&mut self) {
- if self.cap != 0 {
- while self.pop().is_some() {}
- let layout = Layout::array::<T>(self.cap).unwrap();
- unsafe {
- alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout);
- }
- }
- }
-}
-
-impl<T> Deref for Vec<T> {
- type Target = [T];
- fn deref(&self) -> &[T] {
- unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
- }
-}
-
-impl<T> DerefMut for Vec<T> {
- fn deref_mut(&mut self) -> &mut [T] {
- unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
- }
-}
-
-impl<T> Default for Vec<T> {
- 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!()
- }
-}