2024-09-07 16:15:47 +00:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
2024-09-06 23:27:16 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
2024-09-07 16:15:47 +00:00
|
|
|
use crate::{Chunk, ChunkError, Identifier, Span, Value, ValueError};
|
2024-09-06 23:27:16 +00:00
|
|
|
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
|
|
pub struct Vm {
|
|
|
|
chunk: Chunk,
|
|
|
|
ip: usize,
|
|
|
|
stack: Vec<Value>,
|
2024-09-07 16:15:47 +00:00
|
|
|
globals: HashMap<Identifier, Value>,
|
2024-09-06 23:27:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Vm {
|
2024-09-07 08:34:03 +00:00
|
|
|
const STACK_SIZE: usize = 256;
|
|
|
|
|
2024-09-06 23:27:16 +00:00
|
|
|
pub fn new(chunk: Chunk) -> Self {
|
|
|
|
Self {
|
|
|
|
chunk,
|
|
|
|
ip: 0,
|
2024-09-07 08:34:03 +00:00
|
|
|
stack: Vec::with_capacity(Self::STACK_SIZE),
|
2024-09-07 16:15:47 +00:00
|
|
|
globals: HashMap::new(),
|
2024-09-06 23:27:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn interpret(&mut self) -> Result<Option<Value>, VmError> {
|
2024-09-07 16:15:47 +00:00
|
|
|
while let Ok((byte, position)) = self.read().copied() {
|
2024-09-07 03:30:43 +00:00
|
|
|
let instruction = Instruction::from_byte(byte)
|
|
|
|
.ok_or_else(|| VmError::InvalidInstruction(byte, position))?;
|
2024-09-06 23:27:16 +00:00
|
|
|
|
|
|
|
match instruction {
|
2024-09-07 03:30:43 +00:00
|
|
|
Instruction::Constant => {
|
2024-09-07 16:15:47 +00:00
|
|
|
let (index, position) = self.read().copied()?;
|
2024-09-07 10:38:12 +00:00
|
|
|
let value = self.read_constant(index as usize)?;
|
2024-09-06 23:27:16 +00:00
|
|
|
|
2024-09-07 08:34:03 +00:00
|
|
|
self.stack.push(value);
|
2024-09-06 23:27:16 +00:00
|
|
|
}
|
2024-09-06 23:37:12 +00:00
|
|
|
Instruction::Return => {
|
|
|
|
let value = self.pop()?;
|
|
|
|
|
|
|
|
return Ok(Some(value));
|
|
|
|
}
|
2024-09-07 16:15:47 +00:00
|
|
|
Instruction::Pop => {
|
|
|
|
self.pop()?;
|
|
|
|
}
|
|
|
|
Instruction::DefineGlobal => {
|
|
|
|
let (index, _) = self.read().copied()?;
|
|
|
|
let identifier = self.chunk.get_identifier(index as usize)?.clone();
|
|
|
|
let value = self.pop()?;
|
|
|
|
|
|
|
|
self.globals.insert(identifier, value);
|
|
|
|
}
|
2024-09-06 23:37:12 +00:00
|
|
|
|
|
|
|
// Unary
|
2024-09-06 23:27:16 +00:00
|
|
|
Instruction::Negate => {
|
|
|
|
let negated = self.pop()?.negate()?;
|
|
|
|
|
|
|
|
self.stack.push(negated);
|
|
|
|
}
|
2024-09-07 16:15:47 +00:00
|
|
|
Instruction::Not => {
|
|
|
|
let not = self.pop()?.not()?;
|
|
|
|
|
|
|
|
self.stack.push(not);
|
|
|
|
}
|
2024-09-06 23:27:16 +00:00
|
|
|
|
2024-09-06 23:37:12 +00:00
|
|
|
// Binary
|
|
|
|
Instruction::Add => {
|
2024-09-07 16:15:47 +00:00
|
|
|
let right = self.pop()?;
|
|
|
|
let left = self.pop()?;
|
|
|
|
let sum = left.add(&right)?;
|
2024-09-06 23:37:12 +00:00
|
|
|
|
|
|
|
self.stack.push(sum);
|
|
|
|
}
|
|
|
|
Instruction::Subtract => {
|
2024-09-07 16:15:47 +00:00
|
|
|
let right = self.pop()?;
|
|
|
|
let left = self.pop()?;
|
|
|
|
let difference = left.subtract(&right)?;
|
2024-09-06 23:37:12 +00:00
|
|
|
|
|
|
|
self.stack.push(difference);
|
|
|
|
}
|
|
|
|
Instruction::Multiply => {
|
2024-09-07 16:15:47 +00:00
|
|
|
let right = self.pop()?;
|
|
|
|
let left = self.pop()?;
|
|
|
|
let product = left.multiply(&right)?;
|
2024-09-06 23:37:12 +00:00
|
|
|
|
|
|
|
self.stack.push(product);
|
|
|
|
}
|
|
|
|
Instruction::Divide => {
|
2024-09-07 16:15:47 +00:00
|
|
|
let right = self.pop()?;
|
|
|
|
let left = self.pop()?;
|
|
|
|
let quotient = left.divide(&right)?;
|
2024-09-06 23:37:12 +00:00
|
|
|
|
|
|
|
self.stack.push(quotient);
|
2024-09-06 23:27:16 +00:00
|
|
|
}
|
2024-09-07 16:15:47 +00:00
|
|
|
Instruction::Greater => {
|
|
|
|
let right = self.pop()?;
|
|
|
|
let left = self.pop()?;
|
|
|
|
let greater = left.greater_than(&right)?;
|
|
|
|
|
|
|
|
self.stack.push(greater);
|
|
|
|
}
|
|
|
|
Instruction::Less => {
|
|
|
|
let right = self.pop()?;
|
|
|
|
let left = self.pop()?;
|
|
|
|
let less = left.less_than(&right)?;
|
|
|
|
|
|
|
|
self.stack.push(less);
|
|
|
|
}
|
|
|
|
Instruction::GreaterEqual => {
|
|
|
|
let right = self.pop()?;
|
|
|
|
let left = self.pop()?;
|
|
|
|
let greater_equal = left.greater_than_or_equal(&right)?;
|
|
|
|
|
|
|
|
self.stack.push(greater_equal);
|
|
|
|
}
|
|
|
|
Instruction::LessEqual => {
|
|
|
|
let right = self.pop()?;
|
|
|
|
let left = self.pop()?;
|
|
|
|
let less_equal = left.less_than_or_equal(&right)?;
|
|
|
|
|
|
|
|
self.stack.push(less_equal);
|
|
|
|
}
|
|
|
|
Instruction::Equal => {
|
|
|
|
let right = self.pop()?;
|
|
|
|
let left = self.pop()?;
|
|
|
|
let equal = left.equal(&right)?;
|
|
|
|
|
|
|
|
self.stack.push(equal);
|
|
|
|
}
|
|
|
|
Instruction::NotEqual => {
|
|
|
|
let right = self.pop()?;
|
|
|
|
let left = self.pop()?;
|
|
|
|
let not_equal = left.not_equal(&right)?;
|
|
|
|
|
|
|
|
self.stack.push(not_equal);
|
|
|
|
}
|
|
|
|
Instruction::And => {
|
|
|
|
let right = self.pop()?;
|
|
|
|
let left = self.pop()?;
|
|
|
|
let and = left.and(&right)?;
|
|
|
|
|
|
|
|
self.stack.push(and);
|
|
|
|
}
|
|
|
|
Instruction::Or => {
|
|
|
|
let right = self.pop()?;
|
|
|
|
let left = self.pop()?;
|
|
|
|
let or = left.or(&right)?;
|
|
|
|
|
|
|
|
self.stack.push(or);
|
|
|
|
}
|
2024-09-06 23:27:16 +00:00
|
|
|
}
|
|
|
|
}
|
2024-09-07 16:15:47 +00:00
|
|
|
|
|
|
|
Ok(self.stack.pop())
|
2024-09-06 23:27:16 +00:00
|
|
|
}
|
|
|
|
|
2024-09-07 16:15:47 +00:00
|
|
|
fn push(&mut self, value: Value) -> Result<(), VmError> {
|
2024-09-07 08:34:03 +00:00
|
|
|
if self.stack.len() == Self::STACK_SIZE {
|
2024-09-06 23:27:16 +00:00
|
|
|
Err(VmError::StackOverflow)
|
|
|
|
} else {
|
|
|
|
self.stack.push(value);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-07 16:15:47 +00:00
|
|
|
fn pop(&mut self) -> Result<Value, VmError> {
|
2024-09-06 23:27:16 +00:00
|
|
|
if let Some(value) = self.stack.pop() {
|
|
|
|
Ok(value)
|
|
|
|
} else {
|
|
|
|
Err(VmError::StackUnderflow)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-07 16:15:47 +00:00
|
|
|
fn read(&mut self) -> Result<&(u8, Span), VmError> {
|
|
|
|
let current = self.chunk.read(self.ip)?;
|
|
|
|
|
2024-09-07 03:30:43 +00:00
|
|
|
self.ip += 1;
|
2024-09-06 23:27:16 +00:00
|
|
|
|
2024-09-07 16:15:47 +00:00
|
|
|
Ok(current)
|
2024-09-06 23:27:16 +00:00
|
|
|
}
|
|
|
|
|
2024-09-07 16:15:47 +00:00
|
|
|
fn read_constant(&self, index: usize) -> Result<Value, VmError> {
|
2024-09-07 10:38:12 +00:00
|
|
|
Ok(self.chunk.get_constant(index)?.clone())
|
2024-09-06 23:27:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
|
|
pub enum VmError {
|
2024-09-07 03:30:43 +00:00
|
|
|
InvalidInstruction(u8, Span),
|
2024-09-06 23:27:16 +00:00
|
|
|
StackUnderflow,
|
|
|
|
StackOverflow,
|
2024-09-07 10:38:12 +00:00
|
|
|
|
|
|
|
Chunk(ChunkError),
|
2024-09-06 23:27:16 +00:00
|
|
|
Value(ValueError),
|
|
|
|
}
|
|
|
|
|
2024-09-07 10:38:12 +00:00
|
|
|
impl From<ChunkError> for VmError {
|
|
|
|
fn from(error: ChunkError) -> Self {
|
|
|
|
Self::Chunk(error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-06 23:27:16 +00:00
|
|
|
impl From<ValueError> for VmError {
|
|
|
|
fn from(error: ValueError) -> Self {
|
|
|
|
Self::Value(error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
|
|
|
|
pub enum Instruction {
|
2024-09-07 03:30:43 +00:00
|
|
|
Constant = 0,
|
|
|
|
Return = 1,
|
2024-09-07 16:15:47 +00:00
|
|
|
Pop = 2,
|
|
|
|
DefineGlobal = 3,
|
2024-09-06 23:37:12 +00:00
|
|
|
|
|
|
|
// Unary
|
2024-09-07 16:15:47 +00:00
|
|
|
Negate = 4,
|
|
|
|
Not = 5,
|
2024-09-06 23:37:12 +00:00
|
|
|
|
|
|
|
// Binary
|
2024-09-07 16:15:47 +00:00
|
|
|
Add = 6,
|
|
|
|
Subtract = 7,
|
|
|
|
Multiply = 8,
|
|
|
|
Divide = 9,
|
|
|
|
Greater = 10,
|
|
|
|
Less = 11,
|
|
|
|
GreaterEqual = 12,
|
|
|
|
LessEqual = 13,
|
|
|
|
Equal = 14,
|
|
|
|
NotEqual = 15,
|
|
|
|
And = 16,
|
|
|
|
Or = 17,
|
2024-09-06 23:27:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Instruction {
|
2024-09-07 03:30:43 +00:00
|
|
|
pub fn from_byte(byte: u8) -> Option<Self> {
|
|
|
|
match byte {
|
2024-09-07 16:15:47 +00:00
|
|
|
0 => Some(Instruction::Constant),
|
|
|
|
1 => Some(Instruction::Return),
|
|
|
|
2 => Some(Instruction::Pop),
|
|
|
|
3 => Some(Instruction::DefineGlobal),
|
|
|
|
4 => Some(Instruction::Negate),
|
|
|
|
5 => Some(Instruction::Not),
|
|
|
|
6 => Some(Instruction::Add),
|
|
|
|
7 => Some(Instruction::Subtract),
|
|
|
|
8 => Some(Instruction::Multiply),
|
|
|
|
9 => Some(Instruction::Divide),
|
|
|
|
10 => Some(Instruction::Greater),
|
|
|
|
11 => Some(Instruction::Less),
|
|
|
|
12 => Some(Instruction::GreaterEqual),
|
|
|
|
13 => Some(Instruction::LessEqual),
|
|
|
|
14 => Some(Instruction::Equal),
|
|
|
|
15 => Some(Instruction::NotEqual),
|
|
|
|
16 => Some(Instruction::And),
|
|
|
|
17 => Some(Instruction::Or),
|
2024-09-07 03:30:43 +00:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-06 23:27:16 +00:00
|
|
|
pub fn disassemble(&self, chunk: &Chunk, offset: usize) -> String {
|
|
|
|
match self {
|
2024-09-07 03:30:43 +00:00
|
|
|
Instruction::Constant => {
|
2024-09-07 16:15:47 +00:00
|
|
|
let (index, _) = chunk.read(offset + 1).unwrap();
|
2024-09-07 10:38:12 +00:00
|
|
|
let value_display = chunk
|
2024-09-07 16:15:47 +00:00
|
|
|
.get_constant(*index as usize)
|
2024-09-07 10:38:12 +00:00
|
|
|
.map(|value| value.to_string())
|
|
|
|
.unwrap_or_else(|error| format!("{:?}", error));
|
2024-09-06 23:27:16 +00:00
|
|
|
|
2024-09-07 10:38:12 +00:00
|
|
|
format!("{offset:04} CONSTANT {index} {value_display}")
|
2024-09-06 23:27:16 +00:00
|
|
|
}
|
2024-09-07 08:34:03 +00:00
|
|
|
Instruction::Return => format!("{offset:04} RETURN"),
|
2024-09-07 16:15:47 +00:00
|
|
|
Instruction::Pop => format!("{offset:04} POP"),
|
|
|
|
Instruction::DefineGlobal => {
|
|
|
|
let (index, _) = chunk.read(offset + 1).unwrap();
|
|
|
|
let identifier = chunk.get_identifier(*index as usize).unwrap();
|
|
|
|
|
|
|
|
format!("{offset:04} DEFINE_GLOBAL {identifier}")
|
|
|
|
}
|
2024-09-06 23:37:12 +00:00
|
|
|
|
|
|
|
// Unary
|
2024-09-07 08:34:03 +00:00
|
|
|
Instruction::Negate => format!("{offset:04} NEGATE"),
|
2024-09-07 16:15:47 +00:00
|
|
|
Instruction::Not => format!("{offset:04} NOT"),
|
2024-09-06 23:37:12 +00:00
|
|
|
|
|
|
|
// Binary
|
2024-09-07 08:34:03 +00:00
|
|
|
Instruction::Add => format!("{offset:04} ADD"),
|
|
|
|
Instruction::Subtract => format!("{offset:04} SUBTRACT"),
|
|
|
|
Instruction::Multiply => format!("{offset:04} MULTIPLY"),
|
|
|
|
Instruction::Divide => format!("{offset:04} DIVIDE"),
|
2024-09-07 16:15:47 +00:00
|
|
|
Instruction::Greater => format!("{offset:04} GREATER"),
|
|
|
|
Instruction::Less => format!("{offset:04} LESS"),
|
|
|
|
Instruction::GreaterEqual => format!("{offset:04} GREATER_EQUAL"),
|
|
|
|
Instruction::LessEqual => format!("{offset:04} LESS_EQUAL"),
|
|
|
|
Instruction::Equal => format!("{offset:04} EQUAL"),
|
|
|
|
Instruction::NotEqual => format!("{offset:04} NOT_EQUAL"),
|
|
|
|
Instruction::And => format!("{offset:04} AND"),
|
|
|
|
Instruction::Or => format!("{offset:04} OR"),
|
2024-09-06 23:27:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
pub mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn negation() {
|
|
|
|
let mut chunk = Chunk::new();
|
2024-09-07 03:30:43 +00:00
|
|
|
let constant = chunk.push_constant(Value::integer(42)).unwrap();
|
2024-09-06 23:27:16 +00:00
|
|
|
|
2024-09-07 03:30:43 +00:00
|
|
|
chunk.write(Instruction::Constant as u8, Span(0, 1));
|
|
|
|
chunk.write(constant, Span(2, 3));
|
|
|
|
chunk.write(Instruction::Negate as u8, Span(4, 5));
|
|
|
|
chunk.write(Instruction::Return as u8, Span(2, 3));
|
2024-09-06 23:27:16 +00:00
|
|
|
|
|
|
|
let mut vm = Vm::new(chunk);
|
|
|
|
let result = vm.interpret();
|
|
|
|
|
|
|
|
assert_eq!(result, Ok(Some(Value::integer(-42))));
|
|
|
|
}
|
2024-09-06 23:37:12 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn addition() {
|
|
|
|
let mut chunk = Chunk::new();
|
2024-09-07 03:30:43 +00:00
|
|
|
let left = chunk.push_constant(Value::integer(42)).unwrap();
|
|
|
|
let right = chunk.push_constant(Value::integer(23)).unwrap();
|
2024-09-06 23:37:12 +00:00
|
|
|
|
2024-09-07 03:30:43 +00:00
|
|
|
chunk.write(Instruction::Constant as u8, Span(0, 1));
|
|
|
|
chunk.write(left, Span(2, 3));
|
|
|
|
chunk.write(Instruction::Constant as u8, Span(4, 5));
|
|
|
|
chunk.write(right, Span(6, 7));
|
|
|
|
chunk.write(Instruction::Add as u8, Span(8, 9));
|
|
|
|
chunk.write(Instruction::Return as u8, Span(10, 11));
|
2024-09-06 23:37:12 +00:00
|
|
|
|
|
|
|
let mut vm = Vm::new(chunk);
|
|
|
|
let result = vm.interpret();
|
|
|
|
|
|
|
|
assert_eq!(result, Ok(Some(Value::integer(65))));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn subtraction() {
|
|
|
|
let mut chunk = Chunk::new();
|
2024-09-07 03:30:43 +00:00
|
|
|
let left = chunk.push_constant(Value::integer(42)).unwrap();
|
|
|
|
let right = chunk.push_constant(Value::integer(23)).unwrap();
|
2024-09-06 23:37:12 +00:00
|
|
|
|
2024-09-07 03:30:43 +00:00
|
|
|
chunk.write(Instruction::Constant as u8, Span(0, 1));
|
|
|
|
chunk.write(left, Span(2, 3));
|
|
|
|
chunk.write(Instruction::Constant as u8, Span(4, 5));
|
|
|
|
chunk.write(right, Span(6, 7));
|
|
|
|
chunk.write(Instruction::Subtract as u8, Span(8, 9));
|
|
|
|
chunk.write(Instruction::Return as u8, Span(10, 11));
|
2024-09-06 23:37:12 +00:00
|
|
|
|
|
|
|
let mut vm = Vm::new(chunk);
|
|
|
|
let result = vm.interpret();
|
|
|
|
|
|
|
|
assert_eq!(result, Ok(Some(Value::integer(19))));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn multiplication() {
|
|
|
|
let mut chunk = Chunk::new();
|
2024-09-07 03:30:43 +00:00
|
|
|
let left = chunk.push_constant(Value::integer(42)).unwrap();
|
|
|
|
let right = chunk.push_constant(Value::integer(23)).unwrap();
|
2024-09-06 23:37:12 +00:00
|
|
|
|
2024-09-07 03:30:43 +00:00
|
|
|
chunk.write(Instruction::Constant as u8, Span(0, 1));
|
|
|
|
chunk.write(left, Span(2, 3));
|
|
|
|
chunk.write(Instruction::Constant as u8, Span(4, 5));
|
|
|
|
chunk.write(right, Span(6, 7));
|
|
|
|
chunk.write(Instruction::Multiply as u8, Span(8, 9));
|
|
|
|
chunk.write(Instruction::Return as u8, Span(10, 11));
|
2024-09-06 23:37:12 +00:00
|
|
|
|
|
|
|
let mut vm = Vm::new(chunk);
|
|
|
|
let result = vm.interpret();
|
|
|
|
|
|
|
|
assert_eq!(result, Ok(Some(Value::integer(966))));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
|
|
fn division() {
|
|
|
|
let mut chunk = Chunk::new();
|
2024-09-07 03:30:43 +00:00
|
|
|
let left = chunk.push_constant(Value::integer(42)).unwrap();
|
|
|
|
let right = chunk.push_constant(Value::integer(23)).unwrap();
|
|
|
|
|
|
|
|
chunk.write(Instruction::Constant as u8, Span(0, 1));
|
|
|
|
chunk.write(left, Span(2, 3));
|
|
|
|
chunk.write(Instruction::Constant as u8, Span(4, 5));
|
|
|
|
chunk.write(right, Span(6, 7));
|
|
|
|
chunk.write(Instruction::Divide as u8, Span(8, 9));
|
|
|
|
chunk.write(Instruction::Return as u8, Span(10, 11));
|
2024-09-06 23:37:12 +00:00
|
|
|
|
|
|
|
let mut vm = Vm::new(chunk);
|
|
|
|
let result = vm.interpret();
|
|
|
|
|
|
|
|
assert_eq!(result, Ok(Some(Value::integer(1))));
|
|
|
|
}
|
2024-09-06 23:27:16 +00:00
|
|
|
}
|