2023-03-29 21:15:41 +02:00
|
|
|
use std::convert::From;
|
2023-03-29 20:03:16 +02:00
|
|
|
use std::fmt;
|
|
|
|
|
|
2023-03-29 20:07:31 +02:00
|
|
|
#[repr(u8)]
|
2023-03-29 21:15:41 +02:00
|
|
|
#[derive(Copy, Clone, Debug)]
|
2023-03-29 20:07:31 +02:00
|
|
|
pub enum Op {
|
2023-03-29 21:15:41 +02:00
|
|
|
Return,
|
|
|
|
|
Constant { offset: usize },
|
2023-03-30 20:17:00 +02:00
|
|
|
Negate,
|
2023-04-04 19:03:57 +02:00
|
|
|
Add,
|
|
|
|
|
Subtract,
|
|
|
|
|
Multiply,
|
|
|
|
|
Divide,
|
2023-03-29 21:15:41 +02:00
|
|
|
}
|
|
|
|
|
|
2023-04-04 20:34:36 +02:00
|
|
|
#[derive(Copy, Clone, PartialEq)]
|
2023-03-29 21:15:41 +02:00
|
|
|
pub struct Value {
|
|
|
|
|
val: f64,
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-30 11:49:38 +02:00
|
|
|
impl fmt::Debug for Value {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
|
|
|
|
write!(f, "{}", self.val)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-29 21:15:41 +02:00
|
|
|
impl From<f64> for Value {
|
|
|
|
|
fn from(value: f64) -> Self {
|
|
|
|
|
Value { val: value }
|
|
|
|
|
}
|
2023-03-29 20:07:31 +02:00
|
|
|
}
|
|
|
|
|
|
2023-03-29 20:03:16 +02:00
|
|
|
pub struct Chunk {
|
|
|
|
|
code: Vec<Op>,
|
2023-03-29 21:15:41 +02:00
|
|
|
name: String,
|
|
|
|
|
debug_info: Vec<usize>,
|
|
|
|
|
constants: Vec<Value>,
|
2023-03-29 20:03:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Chunk {
|
2023-03-29 21:15:41 +02:00
|
|
|
pub fn new(name: String) -> Self {
|
2023-03-29 20:03:16 +02:00
|
|
|
Chunk {
|
|
|
|
|
code: Vec::new(),
|
2023-03-29 21:15:41 +02:00
|
|
|
name: name,
|
|
|
|
|
debug_info: Vec::new(),
|
|
|
|
|
constants: Vec::new(),
|
2023-03-29 20:03:16 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-29 21:15:41 +02:00
|
|
|
pub fn add_op(&mut self, op: Op, line: usize) {
|
|
|
|
|
self.code.push(op);
|
|
|
|
|
self.debug_info.push(line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn add_constant(&mut self, value: Value) {
|
|
|
|
|
self.constants.push(value);
|
2023-03-29 20:03:16 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Debug for Chunk {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
|
|
|
|
writeln!(f, "-*-*- {} -*-*-", self.name)?;
|
2023-03-29 21:15:41 +02:00
|
|
|
for (idx, op) in self.code.iter().copied().enumerate() {
|
2023-04-04 19:03:57 +02:00
|
|
|
writeln!(
|
|
|
|
|
f,
|
|
|
|
|
"{:?}",
|
|
|
|
|
TraceInfo {
|
|
|
|
|
offset: idx,
|
|
|
|
|
op: op,
|
|
|
|
|
chunk: &self
|
|
|
|
|
}
|
|
|
|
|
)?;
|
2023-03-29 20:03:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-30 11:49:38 +02:00
|
|
|
|
2023-03-30 20:17:00 +02:00
|
|
|
struct TraceInfo<'a> {
|
|
|
|
|
offset: usize,
|
|
|
|
|
op: Op,
|
2023-04-04 19:03:57 +02:00
|
|
|
chunk: &'a Chunk,
|
2023-03-30 20:17:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Debug for TraceInfo<'_> {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
|
|
|
|
let chunk = self.chunk;
|
|
|
|
|
let op = self.op;
|
|
|
|
|
let offset = self.offset;
|
|
|
|
|
|
|
|
|
|
write!(f, "{:04} ", offset)?;
|
|
|
|
|
|
|
|
|
|
let line = chunk.debug_info[offset];
|
|
|
|
|
|
2023-04-04 19:03:57 +02:00
|
|
|
if offset > 0 && chunk.debug_info[offset - 1] == line {
|
2023-03-30 20:17:00 +02:00
|
|
|
write!(f, " | ")
|
|
|
|
|
} else {
|
|
|
|
|
write!(f, "{:4} ", line)
|
|
|
|
|
}?;
|
|
|
|
|
|
|
|
|
|
match op {
|
2023-04-04 19:03:57 +02:00
|
|
|
Op::Return | Op::Negate | Op::Add | Op::Subtract | Op::Multiply | Op::Divide => {
|
|
|
|
|
write!(f, "{:?}", op)
|
|
|
|
|
}
|
2023-03-30 20:17:00 +02:00
|
|
|
Op::Constant { offset } => {
|
|
|
|
|
f.debug_struct("Constant")
|
|
|
|
|
.field("val", &chunk.constants[offset].val)
|
|
|
|
|
.finish()?;
|
|
|
|
|
write!(f, "")
|
2023-04-04 19:03:57 +02:00
|
|
|
}
|
2023-03-30 20:17:00 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
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-03-30 20:17:00 +02:00
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub enum VMError {
|
2023-03-30 11:49:38 +02:00
|
|
|
Compile,
|
2023-04-04 19:03:57 +02:00
|
|
|
Runtime,
|
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-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> {
|
|
|
|
|
self.stack.pop().ok_or(VMError::Runtime)
|
2023-03-30 11:49:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn interpret(&mut self, chunk: &Chunk) -> Result<(), 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,
|
|
|
|
|
chunk: chunk
|
|
|
|
|
}
|
|
|
|
|
);
|
2023-03-30 11:49:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match instr {
|
|
|
|
|
Op::Return => {
|
2023-03-30 20:17:00 +02:00
|
|
|
print!("{:?}", self.pop()?);
|
2023-04-04 19:03:57 +02:00
|
|
|
return Ok(());
|
2023-03-30 20:17:00 +02:00
|
|
|
}
|
2023-04-04 19:03:57 +02:00
|
|
|
Op::Constant { offset } => self.push(chunk.constants[offset]),
|
2023-03-30 20:17:00 +02:00
|
|
|
Op::Negate => {
|
|
|
|
|
let new_val = -self.pop()?.val;
|
|
|
|
|
self.push(Value::from(new_val));
|
2023-03-30 11:49:38 +02:00
|
|
|
}
|
2023-04-04 19:03:57 +02:00
|
|
|
Op::Add | Op::Subtract | Op::Multiply | Op::Divide => {
|
|
|
|
|
let b = self.pop()?;
|
|
|
|
|
let a = self.pop()?;
|
|
|
|
|
let r = match instr {
|
|
|
|
|
Op::Add => a.val + b.val,
|
|
|
|
|
Op::Subtract => a.val - b.val,
|
|
|
|
|
Op::Multiply => a.val * b.val,
|
|
|
|
|
Op::Divide => a.val / b.val,
|
|
|
|
|
_ => unreachable!()
|
|
|
|
|
};
|
|
|
|
|
self.push(Value::from(r))
|
|
|
|
|
}
|
2023-03-30 11:49:38 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-04 19:03:57 +02:00
|
|
|
return Ok(());
|
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};
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn simple_arithmetic() {
|
|
|
|
|
let mut chunk = Chunk::new("TEST".to_string());
|
|
|
|
|
chunk.add_constant(Value::from(3.));
|
|
|
|
|
chunk.add_constant(Value::from(7.));
|
|
|
|
|
chunk.add_constant(Value::from(11.));
|
|
|
|
|
chunk.add_constant(Value::from(17.));
|
|
|
|
|
chunk.add_constant(Value::from(500.));
|
|
|
|
|
chunk.add_constant(Value::from(1000.));
|
|
|
|
|
chunk.add_constant(Value::from(250.));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chunk.add_op(Op::Constant { offset: 0 }, 1);
|
|
|
|
|
chunk.add_op(Op::Constant { offset: 1 }, 1);
|
|
|
|
|
chunk.add_op(Op::Multiply, 1);
|
|
|
|
|
chunk.add_op(Op::Constant { offset: 2 }, 1);
|
|
|
|
|
chunk.add_op(Op::Constant { offset: 3 }, 1);
|
|
|
|
|
chunk.add_op(Op::Multiply, 1);
|
|
|
|
|
chunk.add_op(Op::Multiply, 1);
|
|
|
|
|
chunk.add_op(Op::Negate, 1);
|
|
|
|
|
chunk.add_op(Op::Constant { offset: 4 }, 2);
|
|
|
|
|
chunk.add_op(Op::Constant { offset: 5 }, 2);
|
|
|
|
|
chunk.add_op(Op::Add, 2);
|
|
|
|
|
chunk.add_op(Op::Constant { offset: 6 }, 2);
|
|
|
|
|
chunk.add_op(Op::Subtract, 2);
|
|
|
|
|
chunk.add_op(Op::Negate, 2);
|
|
|
|
|
chunk.add_op(Op::Divide, 2);
|
|
|
|
|
|
|
|
|
|
let mut interpreter = VM::new();
|
|
|
|
|
interpreter.interpret(&chunk).unwrap();
|
|
|
|
|
|
|
|
|
|
assert_eq!(interpreter.stack[0], Value::from(3.1416));
|
|
|
|
|
}
|
|
|
|
|
}
|