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