feat(tazjin/rlox): Support trivial literals in bytecode compiler

Adds support for true, false & nil. These each come with a new
separate opcode and are pushed directly on the stack.

Change-Id: I405b5b09496dcf99d514d3411c083e0834377167
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2571
Reviewed-by: tazjin <mail@tazj.in>
Tested-by: BuildkiteCI
This commit is contained in:
Vincent Ambo 2021-02-28 15:15:46 +02:00 committed by tazjin
parent 127ef98486
commit 47ffa80711
5 changed files with 52 additions and 6 deletions

View file

@ -1,9 +1,12 @@
use super::chunk::{self, Chunk};
use super::chunk::Chunk;
use super::errors::{Error, ErrorKind, LoxResult};
use super::opcode::OpCode;
use super::value::Value;
use crate::scanner::{self, Token, TokenKind};
#[cfg(feature = "disassemble")]
use super::chunk;
struct Compiler<T: Iterator<Item = Token>> {
tokens: T,
chunk: Chunk,
@ -101,6 +104,18 @@ fn rule_for<T: Iterator<Item = Token>>(token: &TokenKind) -> ParseRule<T> {
ParseRule::new(Some(Compiler::number), None, Precedence::None)
}
TokenKind::True => {
ParseRule::new(Some(Compiler::literal), None, Precedence::None)
}
TokenKind::False => {
ParseRule::new(Some(Compiler::literal), None, Precedence::None)
}
TokenKind::Nil => {
ParseRule::new(Some(Compiler::literal), None, Precedence::None)
}
_ => ParseRule::new(None, None, Precedence::None),
}
}
@ -180,6 +195,17 @@ impl<T: Iterator<Item = Token>> Compiler<T> {
Ok(())
}
fn literal(&mut self) -> LoxResult<()> {
match self.previous().kind {
TokenKind::Nil => self.emit_op(OpCode::OpNil),
TokenKind::True => self.emit_op(OpCode::OpTrue),
TokenKind::False => self.emit_op(OpCode::OpFalse),
_ => unreachable!("only called for literal value tokens"),
}
Ok(())
}
fn parse_precedence(&mut self, precedence: Precedence) -> LoxResult<()> {
self.advance();
let rule: ParseRule<T> = rule_for(&self.previous().kind);
@ -206,7 +232,7 @@ impl<T: Iterator<Item = Token>> Compiler<T> {
}
fn consume(&mut self, expected: &TokenKind, err: ErrorKind) {
if (self.current().kind == *expected) {
if self.current().kind == *expected {
self.advance();
return;
}

View file

@ -12,7 +12,6 @@ mod vm;
#[cfg(test)]
mod tests;
use chunk::Chunk;
pub struct Interpreter {}
impl crate::Lox for Interpreter {

View file

@ -1,8 +1,13 @@
#[derive(Debug)]
pub enum OpCode {
/// Access a constant for use.
/// Push a constant onto the stack.
OpConstant(usize),
// Literal pushes
OpNil,
OpTrue,
OpFalse,
/// Return from the current function.
OpReturn,

View file

@ -1,12 +1,17 @@
use super::value::Value;
use super::*;
use crate::Lox;
fn expect_num(code: &str, value: f64) {
fn expect(code: &str, value: Value) {
let result = Interpreter::create()
.interpret(code.into())
.expect("evaluation failed");
assert_eq!(result, value::Value::Number(value));
assert_eq!(result, value);
}
fn expect_num(code: &str, value: f64) {
expect(code, Value::Number(value))
}
#[test]
@ -46,3 +51,10 @@ fn arithmetic() {
expect_num("-4 * -4 + (14 - 5)", 25.0);
expect_num("(702 + 408) - ((239 - 734) / -5) + -4", 1007.0);
}
#[test]
fn trivial_literals() {
expect("true", Value::Bool(true));
expect("false", Value::Bool(false));
expect("nil", Value::Nil);
}

View file

@ -72,6 +72,10 @@ impl VM {
self.push(c);
}
OpCode::OpNil => self.push(Value::Nil),
OpCode::OpTrue => self.push(Value::Bool(true)),
OpCode::OpFalse => self.push(Value::Bool(false)),
OpCode::OpNegate => {
let v = self.pop();
with_type!(