[rlox] Make nil falsey + add equal and comparisons
This commit is contained in:
@@ -15,9 +15,12 @@ pub enum Op {
|
||||
Subtract,
|
||||
Multiply,
|
||||
Divide,
|
||||
Equal,
|
||||
Greater,
|
||||
Less,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
|
||||
pub enum Value {
|
||||
Nil,
|
||||
Bool(bool),
|
||||
@@ -112,7 +115,7 @@ impl fmt::Debug for Chunk {
|
||||
TraceInfo {
|
||||
offset: idx,
|
||||
op,
|
||||
chunk: self
|
||||
chunk: self,
|
||||
}
|
||||
)?;
|
||||
}
|
||||
@@ -151,18 +154,13 @@ 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::Not => {
|
||||
write!(f, "{:?}", op)
|
||||
}
|
||||
Op::Constant { offset } => {
|
||||
f.debug_struct("Constant")
|
||||
.field("val", &chunk.constants[offset])
|
||||
.finish()?;
|
||||
write!(f, "")
|
||||
}
|
||||
_ => write!(f, "{:?}", op)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,22 +286,6 @@ enum Precedence {
|
||||
Primary,
|
||||
}
|
||||
|
||||
type ParseInfo = (Associativity, Precedence, Op, Option<Op>);
|
||||
fn get_info(ttype: TokenType) -> ParseInfo {
|
||||
match ttype {
|
||||
TokenType::Plus => (Associativity::Left, Precedence::Term, Op::Add, None),
|
||||
TokenType::Minus => (
|
||||
Associativity::Left,
|
||||
Precedence::Term,
|
||||
Op::Subtract,
|
||||
Some(Op::Negate),
|
||||
),
|
||||
TokenType::Slash => (Associativity::Left, Precedence::Factor, Op::Divide, None),
|
||||
TokenType::Star => (Associativity::Left, Precedence::Factor, Op::Multiply, None),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'src> Parser<'src> {
|
||||
fn new(sc: Scanner<'src>) -> Self {
|
||||
Parser {
|
||||
@@ -314,6 +298,8 @@ impl<'src> Parser<'src> {
|
||||
match ttype {
|
||||
Plus | Minus => Precedence::Term,
|
||||
Star | Slash => Precedence::Factor,
|
||||
EqualEqual | BangEqual => Precedence::Equality,
|
||||
Greater | GreaterEqual | Less | LessEqual => Precedence::Comparison,
|
||||
RightParen => Precedence::None,
|
||||
_ => panic!("{:?}", ttype),
|
||||
}
|
||||
@@ -322,8 +308,9 @@ impl<'src> Parser<'src> {
|
||||
fn associativity(prec: Precedence) -> Associativity {
|
||||
use Precedence::*;
|
||||
match prec {
|
||||
Term | Factor => Associativity::Left,
|
||||
Term | Factor | Equality | Comparison => Associativity::Left,
|
||||
None => Associativity::Left,
|
||||
Unary => Associativity::Right,
|
||||
_ => Associativity::NonAssoc,
|
||||
}
|
||||
}
|
||||
@@ -393,15 +380,19 @@ impl<'src> Parser<'src> {
|
||||
// Generates code for rhs
|
||||
self._expression(chunk, Self::precedence(op.ttype));
|
||||
|
||||
let op_decoded = match op.ttype {
|
||||
TokenType::Plus => Op::Add,
|
||||
TokenType::Minus => Op::Subtract,
|
||||
TokenType::Star => Op::Multiply,
|
||||
TokenType::Slash => Op::Divide,
|
||||
match op.ttype {
|
||||
TokenType::Plus => chunk.add_op(Op::Add, 0),
|
||||
TokenType::Minus => chunk.add_op(Op::Subtract, 0),
|
||||
TokenType::Star => chunk.add_op(Op::Multiply, 0),
|
||||
TokenType::Slash => chunk.add_op(Op::Divide, 0),
|
||||
TokenType::EqualEqual => chunk.add_op(Op::Equal, 0),
|
||||
TokenType::Greater => chunk.add_op(Op::Greater, 0),
|
||||
TokenType::Less => chunk.add_op(Op::Less, 0),
|
||||
TokenType::BangEqual => chunk.add_op(Op::Equal, 0).add_op(Op::Not, 0),
|
||||
TokenType::GreaterEqual => chunk.add_op(Op::Less, 0).add_op(Op::Not, 0),
|
||||
TokenType::LessEqual => chunk.add_op(Op::Greater, 0).add_op(Op::Not, 0),
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
chunk.add_op(op_decoded, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -529,10 +520,16 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn parse_bool_expression() {
|
||||
let source = "!false";
|
||||
let source = "!false == !true >= true <= false > true < false != true";
|
||||
use crate::bc::Op::*;
|
||||
let expected = Chunk::new_with(
|
||||
vec![False, Not],
|
||||
vec![
|
||||
False, Not, True, Not, True, Less, Not,
|
||||
False, Greater, Not,
|
||||
True, Greater,
|
||||
False, Less,
|
||||
Equal,
|
||||
True, Equal, Not],
|
||||
vec![],
|
||||
vec![],
|
||||
);
|
||||
|
||||
@@ -95,19 +95,35 @@ impl VM {
|
||||
self.push(new_val.into());
|
||||
}
|
||||
Op::Not => {
|
||||
let new_val = !self.pop_bool()?;
|
||||
let top_of_stack = self.pop()?;
|
||||
let new_val = match top_of_stack {
|
||||
Value::Nil => Ok(true),
|
||||
Value::Bool(val) => Ok(!val),
|
||||
_ => Err(self.type_err("Boolean or Nil", top_of_stack)),
|
||||
}?;
|
||||
self.push(new_val.into());
|
||||
}
|
||||
Op::Add | Op::Subtract | Op::Multiply | Op::Divide => {
|
||||
let b = self.pop_num()?;
|
||||
let a = self.pop_num()?;
|
||||
let r = match instr {
|
||||
Op::Add => Ok(a + b),
|
||||
Op::Subtract => Ok(a - b),
|
||||
Op::Multiply => Ok(a * b),
|
||||
Op::Divide => Ok(a / b),
|
||||
_ => Err(self.runtime_err("Op not implemented")),
|
||||
}?;
|
||||
Op::Add => a + b,
|
||||
Op::Subtract => a - b,
|
||||
Op::Multiply => a * b,
|
||||
Op::Divide => a / b,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.push(r.into())
|
||||
}
|
||||
Op::Equal | Op::Greater | Op::Less => {
|
||||
let b = self.pop()?;
|
||||
let a = self.pop()?;
|
||||
let r = match instr {
|
||||
Op::Equal => a == b,
|
||||
Op::Greater => a > b,
|
||||
Op::Less => a < b,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.push(r.into())
|
||||
}
|
||||
}
|
||||
@@ -123,6 +139,7 @@ impl VM {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::bc::Op::Equal;
|
||||
use super::{Chunk, Op, Value, VM};
|
||||
use crate::vm::VMError;
|
||||
|
||||
@@ -177,7 +194,20 @@ mod tests {
|
||||
#[test]
|
||||
fn simple_booleans() {
|
||||
let chunk = Chunk::new_with(
|
||||
vec![Op::False, Op::Not],
|
||||
vec![Op::False, Op::Not, Op::False, Op::Not, Op::Equal],
|
||||
vec![],
|
||||
vec![],
|
||||
);
|
||||
let mut vm = VM::new();
|
||||
vm.run(&chunk).unwrap();
|
||||
|
||||
assert_eq!(vm.stack[0], true.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_nil_is_true() {
|
||||
let chunk = Chunk::new_with(
|
||||
vec![Op::Nil, Op::Not],
|
||||
vec![],
|
||||
vec![],
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user