[rlox] Add Not op
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use crate::bc::Value::Number;
|
||||
use crate::bc::Value::{Bool, Number};
|
||||
use std::convert::From;
|
||||
use std::fmt;
|
||||
|
||||
@@ -9,6 +9,7 @@ pub enum Op {
|
||||
Nil,
|
||||
True,
|
||||
False,
|
||||
Not,
|
||||
Negate,
|
||||
Add,
|
||||
Subtract,
|
||||
@@ -30,6 +31,13 @@ impl Value {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_bool(self) -> Option<bool> {
|
||||
match self {
|
||||
Bool(val) => Some(val),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Value {
|
||||
@@ -38,6 +46,12 @@ impl From<f64> for Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Value {
|
||||
fn from(value: bool) -> Self {
|
||||
Bool(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Chunk {
|
||||
pub code: Vec<Op>,
|
||||
pub debug_info: Vec<usize>,
|
||||
@@ -139,7 +153,8 @@ impl fmt::Debug for TraceInfo<'_> {
|
||||
match op {
|
||||
Op::Return | Op::Nil | Op::True
|
||||
| Op::False | Op::Negate | Op::Add
|
||||
| Op::Subtract | Op::Multiply | Op::Divide => {
|
||||
| Op::Subtract | Op::Multiply | Op::Divide
|
||||
| Op::Not => {
|
||||
write!(f, "{:?}", op)
|
||||
}
|
||||
Op::Constant { offset } => {
|
||||
|
||||
@@ -333,11 +333,16 @@ impl<'src> Parser<'src> {
|
||||
None => panic!("Expected further tokens"),
|
||||
Some(token) => match token {
|
||||
Token {
|
||||
ttype: TokenType::Minus,
|
||||
ttype: ttype@(TokenType::Minus | TokenType::Bang),
|
||||
span: _,
|
||||
} => {
|
||||
self._expression(chunk, Precedence::Unary);
|
||||
chunk.add_op(Op::Negate, 0);
|
||||
let op = match ttype {
|
||||
TokenType::Minus => Op::Negate,
|
||||
TokenType::Bang => Op::Not,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
chunk.add_op(op, 0);
|
||||
}
|
||||
Token {
|
||||
ttype: TokenType::Number,
|
||||
@@ -467,14 +472,17 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parser() {
|
||||
let source = "1 + 1 * (2 + 1)";
|
||||
fn test_parse_expression(source: &str, expected: &Chunk) {
|
||||
let scanner = Scanner::new(source);
|
||||
let mut parser = Parser::new(scanner);
|
||||
let mut chunk = Chunk::new();
|
||||
parser.expression(&mut chunk);
|
||||
assert!(chunk.instr_eq(expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parser() {
|
||||
let source = "1 + 1 * (2 + 1)";
|
||||
use crate::bc::Op::*;
|
||||
let expected = Chunk::new_with(
|
||||
vec![
|
||||
@@ -490,17 +498,12 @@ mod tests {
|
||||
vec![1., 1., 2., 1.].into_iter().map(Value::from).collect(),
|
||||
);
|
||||
|
||||
assert!(chunk.instr_eq(&expected));
|
||||
test_parse_expression(source, &expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_nil() {
|
||||
let source = "nil + nil";
|
||||
let scanner = Scanner::new(source);
|
||||
let mut parser = Parser::new(scanner);
|
||||
let mut chunk = Chunk::new();
|
||||
parser.expression(&mut chunk);
|
||||
|
||||
use crate::bc::Op::*;
|
||||
let expected = Chunk::new_with(
|
||||
vec![Nil, Nil, Add],
|
||||
@@ -508,17 +511,12 @@ mod tests {
|
||||
vec![],
|
||||
);
|
||||
|
||||
assert!(chunk.instr_eq(&expected));
|
||||
test_parse_expression(source, &expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_bool_literals() {
|
||||
let source = "true * false";
|
||||
let scanner = Scanner::new(source);
|
||||
let mut parser = Parser::new(scanner);
|
||||
let mut chunk = Chunk::new();
|
||||
parser.expression(&mut chunk);
|
||||
|
||||
use crate::bc::Op::*;
|
||||
let expected = Chunk::new_with(
|
||||
vec![True, False, Multiply],
|
||||
@@ -526,7 +524,19 @@ mod tests {
|
||||
vec![],
|
||||
);
|
||||
|
||||
assert!(chunk.instr_eq(&expected));
|
||||
test_parse_expression(source, &expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_bool_expression() {
|
||||
let source = "!false";
|
||||
use crate::bc::Op::*;
|
||||
let expected = Chunk::new_with(
|
||||
vec![False, Not],
|
||||
vec![],
|
||||
vec![],
|
||||
);
|
||||
|
||||
test_parse_expression(source, &expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,13 @@ impl VM {
|
||||
.ok_or(self.type_err("Number", top_of_stack))
|
||||
}
|
||||
|
||||
fn pop_bool(&mut self) -> Result<bool, VMError> {
|
||||
let top_of_stack = self.pop()?;
|
||||
top_of_stack
|
||||
.as_bool()
|
||||
.ok_or(self.type_err("Boolean", top_of_stack))
|
||||
}
|
||||
|
||||
pub fn run(&mut self, chunk: &Chunk) -> Result<Option<Value>, VMError> {
|
||||
while self.pc < chunk.code.len() {
|
||||
let instr = chunk.code[self.pc];
|
||||
@@ -83,6 +90,10 @@ impl VM {
|
||||
let new_val = -self.pop_num()?;
|
||||
self.push(new_val.into());
|
||||
}
|
||||
Op::Not => {
|
||||
let new_val = !self.pop_bool()?;
|
||||
self.push(new_val.into());
|
||||
}
|
||||
Op::Add | Op::Subtract | Op::Multiply | Op::Divide => {
|
||||
let b = self.pop_num()?;
|
||||
let a = self.pop_num()?;
|
||||
@@ -158,4 +169,17 @@ mod tests {
|
||||
vm.type_err("Number", Value::Nil)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_booleans() {
|
||||
let chunk = Chunk::new_with(
|
||||
vec![Op::False, Op::Not],
|
||||
vec![],
|
||||
vec![],
|
||||
);
|
||||
let mut vm = VM::new();
|
||||
vm.run(&chunk).unwrap();
|
||||
|
||||
assert_eq!(vm.stack[0], true.into());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user