Files
crafting-interpreters/rlox/src/vm.rs

190 lines
5.0 KiB
Rust
Raw Normal View History

2023-04-14 15:27:24 +02:00
use crate::bc::{Chunk, Op, TraceInfo, Value};
2023-10-08 13:38:30 +02:00
use std::ops::Not;
use std::rc::Rc;
2023-03-30 11:49:38 +02:00
2023-03-30 20:17:00 +02:00
pub struct VM {
pub trace: bool,
2023-03-30 11:49:38 +02:00
stack: Vec<Value>,
2023-03-30 20:17:00 +02:00
pc: usize,
2023-03-30 11:49:38 +02:00
}
2023-10-08 13:38:30 +02:00
#[derive(Debug, PartialEq)]
2023-03-30 20:17:00 +02:00
pub enum VMError {
2023-03-30 11:49:38 +02:00
Compile,
2023-10-08 13:38:30 +02:00
Runtime(Rc<str>, usize),
2023-03-30 11:49:38 +02:00
}
impl VM {
2023-03-30 20:17:00 +02:00
pub fn new() -> VM {
VM {
trace: false,
stack: Vec::new(),
pc: 0,
}
}
2023-10-09 11:44:37 +02:00
pub fn set_trace(&mut self, trace: bool) {
self.trace = trace;
}
2023-06-04 19:19:48 +02:00
fn runtime_err(&self, msg: &'static str) -> VMError {
2023-10-08 13:38:30 +02:00
VMError::Runtime(msg.into(), self.pc)
}
fn type_err(&self, expected: &'static str, found: Value) -> VMError {
VMError::Runtime(
format!("Expected: {:}, Found: {:?}", expected, found).into(),
self.pc,
)
2023-06-04 19:19:48 +02:00
}
2023-03-30 11:49:38 +02:00
fn push(&mut self, value: Value) {
self.stack.push(value);
}
2023-03-30 20:17:00 +02:00
fn pop(&mut self) -> Result<Value, VMError> {
2023-10-08 11:20:00 +02:00
self.stack
.pop()
2023-10-08 13:38:30 +02:00
.ok_or(self.runtime_err("Attempt to pop of empty stack."))
}
fn pop_num(&mut self) -> Result<f64, VMError> {
let top_of_stack = self.pop()?;
top_of_stack
.as_num()
.ok_or(self.type_err("Number", top_of_stack))
2023-03-30 11:49:38 +02:00
}
2023-10-08 21:31:07 +02:00
fn pop_bool(&mut self) -> Result<bool, VMError> {
let top_of_stack = self.pop()?;
top_of_stack
.as_bool()
.ok_or(self.type_err("Boolean", top_of_stack))
}
2023-10-08 12:25:21 +02:00
pub fn run(&mut self, chunk: &Chunk) -> Result<Option<Value>, VMError> {
2023-03-30 20:17:00 +02:00
while self.pc < chunk.code.len() {
let instr = chunk.code[self.pc];
self.pc += 1;
2023-03-30 11:49:38 +02:00
if self.trace {
2023-03-30 20:17:00 +02:00
print!(" [ ");
2023-03-30 11:49:38 +02:00
for value in self.stack.iter() {
2023-03-30 20:17:00 +02:00
print!("{:?} | ", value);
2023-03-30 11:49:38 +02:00
}
2023-03-30 20:17:00 +02:00
println!("_ ]\n");
2023-04-04 19:03:57 +02:00
println!(
"{:?}\n",
TraceInfo {
offset: self.pc - 1,
op: instr,
2023-10-08 11:20:00 +02:00
chunk
2023-04-04 19:03:57 +02:00
}
);
2023-03-30 11:49:38 +02:00
}
match instr {
2023-06-04 19:19:48 +02:00
Op::Return => print!("{:?}", self.pop()?),
2023-04-04 19:03:57 +02:00
Op::Constant { offset } => self.push(chunk.constants[offset]),
Op::Nil => self.push(Value::Nil),
2023-10-08 20:44:11 +02:00
Op::True => self.push(Value::Bool(true)),
Op::False => self.push(Value::Bool(false)),
2023-03-30 20:17:00 +02:00
Op::Negate => {
2023-10-08 13:38:30 +02:00
let new_val = -self.pop_num()?;
self.push(new_val.into());
2023-03-30 11:49:38 +02:00
}
2023-10-08 21:31:07 +02:00
Op::Not => {
let new_val = !self.pop_bool()?;
self.push(new_val.into());
}
2023-04-04 19:03:57 +02:00
Op::Add | Op::Subtract | Op::Multiply | Op::Divide => {
2023-10-08 13:38:30 +02:00
let b = self.pop_num()?;
let a = self.pop_num()?;
2023-04-04 19:03:57 +02:00
let r = match instr {
2023-10-08 13:38:30 +02:00
Op::Add => Ok(a + b),
Op::Subtract => Ok(a - b),
Op::Multiply => Ok(a * b),
Op::Divide => Ok(a / b),
2023-10-08 11:20:00 +02:00
_ => Err(self.runtime_err("Op not implemented")),
2023-06-04 19:19:48 +02:00
}?;
self.push(r.into())
2023-04-04 19:03:57 +02:00
}
2023-03-30 11:49:38 +02:00
}
}
2023-10-08 13:38:30 +02:00
Ok(self
.stack
.is_empty()
.not()
.then_some(self.stack[self.stack.len() - 1]))
2023-03-30 11:49:38 +02:00
}
}
2023-04-04 20:34:36 +02:00
#[cfg(test)]
mod tests {
use super::{Chunk, Op, Value, VM};
2023-10-08 13:38:30 +02:00
use crate::vm::VMError;
2023-04-04 20:34:36 +02:00
#[test]
fn simple_arithmetic() {
2023-10-08 11:20:00 +02:00
let chunk = Chunk::new_with(
vec![
Op::Constant { offset: 0 },
Op::Constant { offset: 1 },
Op::Multiply,
Op::Constant { offset: 2 },
Op::Constant { offset: 3 },
Op::Multiply,
Op::Multiply,
Op::Negate,
Op::Constant { offset: 4 },
Op::Constant { offset: 5 },
Op::Add,
Op::Constant { offset: 6 },
Op::Subtract,
Op::Negate,
Op::Divide,
],
vec![],
vec![3., 7., 11., 17., 500., 1000., 250.]
.into_iter()
.map(Value::from)
.collect(),
);
2023-04-04 20:34:36 +02:00
2023-04-12 12:46:24 +02:00
let mut vm = VM::new();
vm.run(&chunk).unwrap();
2023-04-04 20:34:36 +02:00
2023-04-12 12:46:24 +02:00
assert_eq!(vm.stack[0], Value::from(3.1416));
2023-04-04 20:34:36 +02:00
}
2023-10-08 13:38:30 +02:00
#[test]
2023-10-08 20:44:11 +02:00
fn nil_error() {
2023-10-08 13:38:30 +02:00
let chunk = Chunk::new_with(
vec![Op::Nil, Op::Negate],
vec![],
2023-10-08 13:38:30 +02:00
vec![],
);
let mut vm = VM::new();
assert_eq!(
vm.run(&chunk).unwrap_err(),
vm.type_err("Number", Value::Nil)
);
}
2023-10-08 21:31:07 +02:00
#[test]
fn simple_booleans() {
let chunk = Chunk::new_with(
vec![Op::False, Op::Not],
vec![],
vec![],
);
let mut vm = VM::new();
vm.run(&chunk).unwrap();
assert_eq!(vm.stack[0], true.into());
}
2023-04-04 20:34:36 +02:00
}