feat(tazjin/rlox): Bootstrap rough shape of bytecode compiler
This one necessarily has to diverge more from the book than the treewalk interpreter did, so some of this is expected to change, but I'm happy with the rough shape. Since we're reusing the old scanner, the compiler/parser struct owns an iterator over all tokens with which the pull-scanner from the bytecode chapters is simulated. Change-Id: Icfa0bd4729d9df786e08f7e49a25cba1b9989a91 Reviewed-on: https://cl.tvl.fyi/c/depot/+/2556 Tested-by: BuildkiteCI Reviewed-by: tazjin <mail@tazj.in>
This commit is contained in:
parent
da2dfb42c6
commit
ee974b3edd
3 changed files with 87 additions and 1 deletions
84
users/tazjin/rlox/src/bytecode/compiler.rs
Normal file
84
users/tazjin/rlox/src/bytecode/compiler.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
use super::chunk::Chunk;
|
||||
use super::errors::{Error, ErrorKind, LoxResult};
|
||||
use super::opcode::OpCode;
|
||||
use crate::scanner;
|
||||
|
||||
struct Compiler<T: Iterator<Item = scanner::Token>> {
|
||||
// panic: bool,
|
||||
errors: Vec<Error>,
|
||||
tokens: T,
|
||||
chunk: Chunk,
|
||||
|
||||
// TODO(tazjin): Restructure so that these don't need to be Option?
|
||||
current: Option<scanner::Token>,
|
||||
previous: Option<scanner::Token>,
|
||||
}
|
||||
|
||||
impl<T: Iterator<Item = scanner::Token>> Compiler<T> {
|
||||
fn compile(&mut self) -> LoxResult<()> {
|
||||
self.advance();
|
||||
self.expression();
|
||||
self.consume(
|
||||
&scanner::TokenKind::Eof,
|
||||
ErrorKind::ExpectedToken("Expected end of expression"),
|
||||
)?;
|
||||
|
||||
self.end_compiler()
|
||||
}
|
||||
|
||||
fn advance(&mut self) {
|
||||
self.previous = self.current.take();
|
||||
self.current = self.tokens.next();
|
||||
}
|
||||
|
||||
fn expression(&mut self) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn consume(
|
||||
&mut self,
|
||||
expected: &scanner::TokenKind,
|
||||
err: ErrorKind,
|
||||
) -> LoxResult<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn current_chunk(&mut self) -> &mut Chunk {
|
||||
&mut self.chunk
|
||||
}
|
||||
|
||||
fn end_compiler(&mut self) -> LoxResult<()> {
|
||||
let line = self.previous().line;
|
||||
self.current_chunk().add_op(OpCode::OpReturn, line);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn previous(&self) -> &scanner::Token {
|
||||
self.previous
|
||||
.as_ref()
|
||||
.expect("invalid internal compiler state: missing previous token")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile(code: &str) -> Result<Chunk, Vec<Error>> {
|
||||
let chars = code.chars().collect::<Vec<char>>();
|
||||
let tokens = scanner::scan(&chars).map_err(|errors| {
|
||||
errors.into_iter().map(Into::into).collect::<Vec<Error>>()
|
||||
})?;
|
||||
|
||||
let mut compiler = Compiler {
|
||||
tokens: tokens.into_iter().peekable(),
|
||||
errors: vec![],
|
||||
current: None,
|
||||
previous: None,
|
||||
chunk: Default::default(),
|
||||
};
|
||||
|
||||
compiler.compile()?;
|
||||
|
||||
if compiler.errors.is_empty() {
|
||||
Ok(unimplemented!())
|
||||
} else {
|
||||
Err(compiler.errors)
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ use std::fmt;
|
|||
pub enum ErrorKind {
|
||||
UnexpectedChar(char),
|
||||
UnterminatedString,
|
||||
ExpectedToken(&'static str),
|
||||
InternalError(&'static str),
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
//! https://craftinginterpreters.com/chunks-of-bytecode.html
|
||||
|
||||
mod chunk;
|
||||
mod compiler;
|
||||
mod errors;
|
||||
mod opcode;
|
||||
mod value;
|
||||
|
@ -20,7 +21,7 @@ impl crate::Lox for Interpreter {
|
|||
}
|
||||
|
||||
fn interpret(&mut self, code: String) -> Result<Self::Value, Vec<Self::Error>> {
|
||||
let chunk: Chunk = Default::default();
|
||||
let chunk = compiler::compile(&code)?;
|
||||
vm::interpret(chunk).map_err(|e| vec![e])
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue