1
0
dust/dust-lang/src/vm.rs

481 lines
16 KiB
Rust
Raw Normal View History

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 {
const STACK_SIZE: usize = 256;
2024-09-06 23:27:16 +00:00
pub fn new(chunk: Chunk) -> Self {
Self {
chunk,
ip: 0,
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
}
}
2024-09-07 21:16:14 +00:00
pub fn run(&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 17:51:05 +00:00
let (index, _) = 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 17:51:05 +00:00
self.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()?;
}
2024-09-07 21:16:14 +00:00
// Variables
Instruction::DefineGlobal => {
2024-09-07 16:15:47 +00:00
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-07 17:51:05 +00:00
Instruction::GetGlobal => {
let (index, _) = self.read().copied()?;
let identifier = self.chunk.get_identifier(index as usize)?;
let value =
self.globals.get(identifier).cloned().ok_or_else(|| {
VmError::UndefinedGlobal(identifier.clone(), position)
})?;
self.push(value)?;
}
2024-09-07 21:16:14 +00:00
Instruction::SetGlobal => {
let (index, _) = self.read().copied()?;
let identifier = self.chunk.get_identifier(index as usize)?.clone();
if !self.globals.contains_key(&identifier) {
return Err(VmError::UndefinedGlobal(identifier, position));
}
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()?;
2024-09-07 17:51:05 +00:00
self.push(negated)?;
2024-09-06 23:27:16 +00:00
}
2024-09-07 16:15:47 +00:00
Instruction::Not => {
let not = self.pop()?.not()?;
2024-09-07 17:51:05 +00:00
self.push(not)?;
2024-09-07 16:15:47 +00:00
}
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
2024-09-07 17:51:05 +00:00
self.push(sum)?;
2024-09-06 23:37:12 +00:00
}
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
2024-09-07 17:51:05 +00:00
self.push(difference)?;
2024-09-06 23:37:12 +00:00
}
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
2024-09-07 17:51:05 +00:00
self.push(product)?;
2024-09-06 23:37:12 +00:00
}
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
2024-09-07 17:51:05 +00:00
self.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)?;
2024-09-07 17:51:05 +00:00
self.push(greater)?;
2024-09-07 16:15:47 +00:00
}
Instruction::Less => {
let right = self.pop()?;
let left = self.pop()?;
let less = left.less_than(&right)?;
2024-09-07 17:51:05 +00:00
self.push(less)?;
2024-09-07 16:15:47 +00:00
}
Instruction::GreaterEqual => {
let right = self.pop()?;
let left = self.pop()?;
let greater_equal = left.greater_than_or_equal(&right)?;
2024-09-07 17:51:05 +00:00
self.push(greater_equal)?;
2024-09-07 16:15:47 +00:00
}
Instruction::LessEqual => {
let right = self.pop()?;
let left = self.pop()?;
let less_equal = left.less_than_or_equal(&right)?;
2024-09-07 17:51:05 +00:00
self.push(less_equal)?;
2024-09-07 16:15:47 +00:00
}
Instruction::Equal => {
let right = self.pop()?;
let left = self.pop()?;
let equal = left.equal(&right)?;
2024-09-07 17:51:05 +00:00
self.push(equal)?;
2024-09-07 16:15:47 +00:00
}
Instruction::NotEqual => {
let right = self.pop()?;
let left = self.pop()?;
let not_equal = left.not_equal(&right)?;
2024-09-07 17:51:05 +00:00
self.push(not_equal)?;
2024-09-07 16:15:47 +00:00
}
Instruction::And => {
let right = self.pop()?;
let left = self.pop()?;
let and = left.and(&right)?;
2024-09-07 17:51:05 +00:00
self.push(and)?;
2024-09-07 16:15:47 +00:00
}
Instruction::Or => {
let right = self.pop()?;
let left = self.pop()?;
let or = left.or(&right)?;
2024-09-07 17:51:05 +00:00
self.push(or)?;
2024-09-07 16:15:47 +00:00
}
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> {
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 17:51:05 +00:00
UndefinedGlobal(Identifier, Span),
2024-09-07 10:38:12 +00:00
2024-09-07 17:51:05 +00:00
// Wrappers for foreign errors
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,
2024-09-07 21:16:14 +00:00
// Variables
DefineGlobal = 3,
2024-09-07 17:51:05 +00:00
GetGlobal = 4,
2024-09-07 21:16:14 +00:00
SetGlobal = 5,
2024-09-06 23:37:12 +00:00
// Unary
2024-09-07 21:16:14 +00:00
Negate = 6,
Not = 7,
2024-09-06 23:37:12 +00:00
// Binary
2024-09-07 21:16:14 +00:00
Add = 8,
Subtract = 9,
Multiply = 10,
Divide = 11,
Greater = 12,
Less = 13,
GreaterEqual = 14,
LessEqual = 15,
Equal = 16,
NotEqual = 17,
And = 18,
Or = 19,
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),
2024-09-07 21:16:14 +00:00
3 => Some(Instruction::DefineGlobal),
2024-09-07 17:51:05 +00:00
4 => Some(Instruction::GetGlobal),
2024-09-07 21:16:14 +00:00
5 => Some(Instruction::SetGlobal),
6 => Some(Instruction::Negate),
7 => Some(Instruction::Not),
8 => Some(Instruction::Add),
9 => Some(Instruction::Subtract),
10 => Some(Instruction::Multiply),
11 => Some(Instruction::Divide),
12 => Some(Instruction::Greater),
13 => Some(Instruction::Less),
14 => Some(Instruction::GreaterEqual),
15 => Some(Instruction::LessEqual),
16 => Some(Instruction::Equal),
17 => Some(Instruction::NotEqual),
18 => Some(Instruction::And),
19 => 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 22:48:01 +00:00
let (index_display, value_display) = if let Ok((index, _)) = chunk.read(offset + 1)
{
let index_string = index.to_string();
let value_string = chunk
.get_constant(*index as usize)
.map(|value| value.to_string())
.unwrap_or_else(|error| format!("{:?}", error));
(index_string, value_string)
} else {
let index = "ERROR".to_string();
let value = "ERROR".to_string();
(index, value)
};
format!("{offset:04} CONSTANT {index_display} {value_display}")
2024-09-06 23:27:16 +00:00
}
Instruction::Return => format!("{offset:04} RETURN"),
2024-09-07 16:15:47 +00:00
Instruction::Pop => format!("{offset:04} POP"),
2024-09-07 21:16:14 +00:00
// Variables
Instruction::DefineGlobal => {
2024-09-07 16:15:47 +00:00
let (index, _) = chunk.read(offset + 1).unwrap();
2024-09-07 21:16:14 +00:00
let index = *index as usize;
2024-09-07 22:48:01 +00:00
let identifier_display = chunk
.get_identifier(index)
.map(|identifier| identifier.to_string())
.unwrap_or_else(|error| format!("{:?}", error));
let value_display = chunk
.get_constant(index)
.map(|value| value.to_string())
.unwrap_or_else(|error| format!("{:?}", error));
2024-09-07 16:15:47 +00:00
2024-09-07 22:48:01 +00:00
format!("{offset:04} DEFINE_GLOBAL {identifier_display} {value_display}")
2024-09-07 16:15:47 +00:00
}
2024-09-07 17:51:05 +00:00
Instruction::GetGlobal => {
let (index, _) = chunk.read(offset + 1).unwrap();
2024-09-07 21:16:14 +00:00
let index = *index as usize;
2024-09-07 22:48:01 +00:00
let identifier_display = chunk
.get_identifier(index)
.map(|identifier| identifier.to_string())
.unwrap_or_else(|error| format!("{:?}", error));
let value_display = chunk
.get_constant(index)
.map(|value| value.to_string())
.unwrap_or_else(|error| format!("{:?}", error));
2024-09-07 21:16:14 +00:00
2024-09-07 22:48:01 +00:00
format!("{offset:04} GET_GLOBAL {identifier_display} {value_display}")
2024-09-07 21:16:14 +00:00
}
Instruction::SetGlobal => {
let (index, _) = chunk.read(offset + 1).unwrap();
let index = *index as usize;
2024-09-07 22:48:01 +00:00
let identifier_display = chunk
.get_identifier(index)
.map(|identifier| identifier.to_string())
.unwrap_or_else(|error| format!("{:?}", error));
let value_display = chunk
.get_constant(index)
.map(|value| value.to_string())
.unwrap_or_else(|error| format!("{:?}", error));
2024-09-07 17:51:05 +00:00
2024-09-07 22:48:01 +00:00
format!("{offset:04} SET_GLOBAL {identifier_display} {value_display}")
2024-09-07 17:51:05 +00:00
}
2024-09-06 23:37:12 +00:00
// Unary
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
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);
2024-09-07 21:16:14 +00:00
let result = vm.run();
2024-09-06 23:27:16 +00:00
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);
2024-09-07 21:16:14 +00:00
let result = vm.run();
2024-09-06 23:37:12 +00:00
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);
2024-09-07 21:16:14 +00:00
let result = vm.run();
2024-09-06 23:37:12 +00:00
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);
2024-09-07 21:16:14 +00:00
let result = vm.run();
2024-09-06 23:37:12 +00:00
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);
2024-09-07 21:16:14 +00:00
let result = vm.run();
2024-09-06 23:37:12 +00:00
assert_eq!(result, Ok(Some(Value::integer(1))));
}
2024-09-06 23:27:16 +00:00
}