Alpha Chen 2 years ago
parent 7073e57123
commit 934cebc598
Signed by: alpha
SSH Key Fingerprint: SHA256:3fOT8fiYQG/aK9ntivV3Bqtg8AYQ7q4nV6ZgihOA20g

@ -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

@ -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));
}

@ -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");
}

@ -0,0 +1 @@
pub type Value = f64;

@ -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()
}
}

@ -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!()
}
}
Loading…
Cancel
Save