From 7fb93fb49008491184a7d55ccd43db846452dce0 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 17 Jan 2021 23:03:41 +0300 Subject: feat(tazjin/rlox): Bootstrap VM for Lox bytecode Change-Id: I479e20bf2087e5c4aa20e31b364c57ed0d961bcf Reviewed-on: https://cl.tvl.fyi/c/depot/+/2416 Tested-by: BuildkiteCI Reviewed-by: tazjin --- users/tazjin/rlox/Cargo.toml | 7 +-- users/tazjin/rlox/src/bytecode/chunk.rs | 27 +++------- users/tazjin/rlox/src/bytecode/errors.rs | 13 +++++ users/tazjin/rlox/src/bytecode/mod.rs | 4 +- users/tazjin/rlox/src/bytecode/vm.rs | 59 ++++++++++++++++++++++ .../rlox/src/treewalk/interpreter/builtins.rs | 2 +- users/tazjin/rlox/src/treewalk/mod.rs | 2 +- 7 files changed, 89 insertions(+), 25 deletions(-) create mode 100644 users/tazjin/rlox/src/bytecode/errors.rs create mode 100644 users/tazjin/rlox/src/bytecode/vm.rs diff --git a/users/tazjin/rlox/Cargo.toml b/users/tazjin/rlox/Cargo.toml index 1562e7ecc2..b66af6ba85 100644 --- a/users/tazjin/rlox/Cargo.toml +++ b/users/tazjin/rlox/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = ["Vincent Ambo "] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] +[features] +# Enables debugging/disassembling in the bytecode interpreter. Off by +# default as it is quite spammy. +disassemble = [] diff --git a/users/tazjin/rlox/src/bytecode/chunk.rs b/users/tazjin/rlox/src/bytecode/chunk.rs index fa3909f038..4a671c8383 100644 --- a/users/tazjin/rlox/src/bytecode/chunk.rs +++ b/users/tazjin/rlox/src/bytecode/chunk.rs @@ -10,7 +10,7 @@ use super::value; // problem. #[derive(Debug, Default)] pub struct Chunk { - code: Vec, + pub code: Vec, lines: Vec, constants: Vec, } @@ -38,6 +38,10 @@ impl Chunk { idx } + pub fn constant(&self, idx: usize) -> &value::Value { + self.constants.index(idx) + } + fn add_line(&mut self, line: usize) { match self.lines.last_mut() { Some(span) if span.line == line => span.count += 1, @@ -58,26 +62,11 @@ impl Chunk { } } -impl Index for Chunk { - type Output = OpCode; - - fn index(&self, offset: usize) -> &Self::Output { - self.code.index(offset) - } -} - // Disassembler -pub fn disassemble(chunk: &Chunk, name: &str) { - println!("== {} ==", name); - - for (idx, _) in chunk.code.iter().enumerate() { - disassemble_instruction(chunk, idx); - } -} /// Print a single disassembled instruction at the specified offset. /// Some instructions are printed "raw", others have special handling. -fn disassemble_instruction(chunk: &Chunk, offset: usize) { +pub fn disassemble_instruction(chunk: &Chunk, offset: usize) { print!("{:04} ", offset); let line = chunk.get_line(offset); @@ -87,8 +76,8 @@ fn disassemble_instruction(chunk: &Chunk, offset: usize) { print!("{:4} ", line); } - match &chunk[offset] { - OpCode::OpConstant(idx) => println!("OpConstant idx '{:?}'", chunk.constants[*idx]), + match chunk.code.index(offset) { + OpCode::OpConstant(idx) => println!("OpConstant({}) '{:?}'", *idx, chunk.constant(*idx)), op => println!("{:?}", op), } } diff --git a/users/tazjin/rlox/src/bytecode/errors.rs b/users/tazjin/rlox/src/bytecode/errors.rs new file mode 100644 index 0000000000..89ab1867a4 --- /dev/null +++ b/users/tazjin/rlox/src/bytecode/errors.rs @@ -0,0 +1,13 @@ +#[derive(Debug)] +pub enum ErrorKind { + // CompileError, + // RuntimeError, + InternalError(&'static str), +} + +#[derive(Debug)] +pub struct Error { + pub kind: ErrorKind, +} + +pub type LoxResult = Result; diff --git a/users/tazjin/rlox/src/bytecode/mod.rs b/users/tazjin/rlox/src/bytecode/mod.rs index 922b3bef44..de04d7b06f 100644 --- a/users/tazjin/rlox/src/bytecode/mod.rs +++ b/users/tazjin/rlox/src/bytecode/mod.rs @@ -3,8 +3,10 @@ //! https://craftinginterpreters.com/chunks-of-bytecode.html mod chunk; +mod errors; mod opcode; mod value; +mod vm; use chunk::Chunk; use opcode::OpCode; @@ -16,5 +18,5 @@ pub fn main() { chunk.add_op(OpCode::OpConstant(constant), 1); chunk.add_op(OpCode::OpReturn, 1); - chunk::disassemble(&chunk, "test chunk"); + vm::interpret(chunk).expect("it should work"); } diff --git a/users/tazjin/rlox/src/bytecode/vm.rs b/users/tazjin/rlox/src/bytecode/vm.rs new file mode 100644 index 0000000000..1b9c4a2359 --- /dev/null +++ b/users/tazjin/rlox/src/bytecode/vm.rs @@ -0,0 +1,59 @@ +use super::chunk; +use super::errors::*; +use super::opcode::OpCode; +use super::value::Value; + +pub struct VM { + chunk: chunk::Chunk, + + // TODO(tazjin): Accessing array elements constantly is not ideal, + // lets see if something clever can be done with iterators. + ip: usize, + + stack: Vec, +} + +impl VM { + fn push(&mut self, value: Value) { + self.stack.push(value) + } + + fn pop(&mut self) -> Value { + self.stack.pop().expect("fatal error: stack empty!") + } +} + +impl VM { + fn run(&mut self) -> LoxResult<()> { + loop { + let op = &self.chunk.code[self.ip]; + + #[cfg(feature = "disassemble")] + chunk::disassemble_instruction(&self.chunk, self.ip); + + self.ip += 1; + + match op { + OpCode::OpReturn => { + println!("{:?}", self.pop()); + return Ok(()); + } + + OpCode::OpConstant(idx) => { + let c = *self.chunk.constant(*idx); + self.push(c); + } + } + } + } +} + +pub fn interpret(chunk: chunk::Chunk) -> LoxResult<()> { + let mut vm = VM { + chunk, + ip: 0, + stack: vec![], + }; + + vm.run() +} diff --git a/users/tazjin/rlox/src/treewalk/interpreter/builtins.rs b/users/tazjin/rlox/src/treewalk/interpreter/builtins.rs index 709e53c7c0..c502d2a171 100644 --- a/users/tazjin/rlox/src/treewalk/interpreter/builtins.rs +++ b/users/tazjin/rlox/src/treewalk/interpreter/builtins.rs @@ -2,8 +2,8 @@ use std::fmt; use std::time::{SystemTime, UNIX_EPOCH}; use crate::treewalk::errors::Error; -use crate::treewalk::parser::Literal; use crate::treewalk::interpreter::Value; +use crate::treewalk::parser::Literal; pub trait Builtin: fmt::Debug { fn arity(&self) -> usize; diff --git a/users/tazjin/rlox/src/treewalk/mod.rs b/users/tazjin/rlox/src/treewalk/mod.rs index ae1049a12e..b5db454ccc 100644 --- a/users/tazjin/rlox/src/treewalk/mod.rs +++ b/users/tazjin/rlox/src/treewalk/mod.rs @@ -1,10 +1,10 @@ use crate::*; mod errors; +pub mod interpreter; mod parser; mod resolver; mod scanner; -pub mod interpreter; pub fn main() { let mut args = env::args(); -- cgit 1.4.1