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 <mail@tazj.in>
This commit is contained in:
Vincent Ambo 2021-01-17 23:03:41 +03:00 committed by tazjin
parent b1d0e22b1f
commit 7fb93fb490
7 changed files with 89 additions and 25 deletions

View file

@ -4,6 +4,7 @@ version = "0.1.0"
authors = ["Vincent Ambo <mail@tazj.in>"]
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 = []

View file

@ -10,7 +10,7 @@ use super::value;
// problem.
#[derive(Debug, Default)]
pub struct Chunk {
code: Vec<OpCode>,
pub code: Vec<OpCode>,
lines: Vec<Span>,
constants: Vec<value::Value>,
}
@ -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<usize> 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),
}
}

View file

@ -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<T> = Result<T, Error>;

View file

@ -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");
}

View file

@ -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<Value>,
}
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()
}

View file

@ -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;

View file

@ -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();