Skip to content

Commit

Permalink
feat: bang and minus prefix operators
Browse files Browse the repository at this point in the history
  • Loading branch information
ZakFarmer committed Oct 24, 2023
1 parent d855c39 commit 1e47790
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 0 deletions.
11 changes: 11 additions & 0 deletions compiler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,17 @@ impl Compiler {

Ok(())
}
Expression::Prefix(prefix) => {
self.compile_expression(&prefix.right)?;

match prefix.operator.as_str() {
"!" => self.emit(opcode::Opcode::OpBang, vec![]),
"-" => self.emit(opcode::Opcode::OpMinus, vec![]),
_ => return Err(Error::msg("compile_expression: unimplemented")),
};

Ok(())
}
Expression::Literal(literal) => match literal {
Literal::Boolean(boolean) => match boolean {
BooleanLiteral { value: true, .. } => {
Expand Down
17 changes: 17 additions & 0 deletions compiler/tests/compiler_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ fn test_boolean_expressions() -> Result<(), Error> {
opcode::make(opcode::Opcode::OpNotEqual, &vec![]),
],
},
CompilerTestCase {
input: "!true".to_string(),
expected_constants: vec![],
expected_instructions: vec![
opcode::make(opcode::Opcode::OpTrue, &vec![]),
opcode::make(opcode::Opcode::OpBang, &vec![]),
],
},
];

run_compiler_tests(tests)?;
Expand Down Expand Up @@ -130,6 +138,15 @@ fn test_integer_arithmetic() -> Result<(), Error> {
opcode::make(opcode::Opcode::OpPop, &vec![0])
]
},
CompilerTestCase {
input: "-1".to_string(),
expected_constants: vec![Object::Integer(1)],
expected_instructions: vec![
opcode::make(opcode::Opcode::OpConst, &vec![0]),
opcode::make(opcode::Opcode::OpMinus, &vec![]),
opcode::make(opcode::Opcode::OpPop, &vec![0])
]
},
];

run_compiler_tests(tests)?;
Expand Down
31 changes: 31 additions & 0 deletions vm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,37 @@ impl Vm {
self.stack_pointer -= 1;
self.stack[self.stack_pointer - 1] = Rc::new(result);
}
Opcode::OpBang => {
let operand = self.pop();

let result = match &*operand {
Object::Boolean(boolean) => Object::Boolean(!boolean),
Object::Integer(integer) => Object::Boolean(!integer == 0),
_ => {
return Err(Error::msg(format!(
"unsupported type for negation: !{}",
operand
)));
}
};

self.push(Rc::new(result));
}
Opcode::OpMinus => {
let operand = self.pop();

let result = match &*operand {
Object::Integer(integer) => Object::Integer(-integer),
_ => {
return Err(Error::msg(format!(
"unsupported type for negation: -{}",
operand
)));
}
};

self.push(Rc::new(result));
}
_ => {
return Err(Error::msg(format!("unknown opcode: {}", op)));
}
Expand Down
92 changes: 92 additions & 0 deletions vm/tests/vm_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,82 @@ fn test_boolean_expressions() -> Result<(), Error> {
input: "false".to_string(),
expected: "false".to_string(),
},
VmTestCase {
input: "1 < 2".to_string(),
expected: "true".to_string(),
},
VmTestCase {
input: "1 > 2".to_string(),
expected: "false".to_string(),
},
VmTestCase {
input: "1 < 1".to_string(),
expected: "false".to_string(),
},
VmTestCase {
input: "1 > 1".to_string(),
expected: "false".to_string(),
},
VmTestCase {
input: "1 == 1".to_string(),
expected: "true".to_string(),
},
VmTestCase {
input: "1 != 1".to_string(),
expected: "false".to_string(),
},
VmTestCase {
input: "1 == 2".to_string(),
expected: "false".to_string(),
},
VmTestCase {
input: "1 != 2".to_string(),
expected: "true".to_string(),
},
VmTestCase {
input: "true == true".to_string(),
expected: "true".to_string(),
},
VmTestCase {
input: "false == false".to_string(),
expected: "true".to_string(),
},
VmTestCase {
input: "true == false".to_string(),
expected: "false".to_string(),
},
VmTestCase {
input: "true != false".to_string(),
expected: "true".to_string(),
},
VmTestCase {
input: "false != true".to_string(),
expected: "true".to_string(),
},
VmTestCase {
input: "!true".to_string(),
expected: "false".to_string(),
},
VmTestCase {
input: "!false".to_string(),
expected: "true".to_string(),
},
VmTestCase {
input: "!5".to_string(),
expected: "false".to_string(),
},
VmTestCase {
input: "!!true".to_string(),
expected: "true".to_string(),
},
VmTestCase {
input: "!!false".to_string(),
expected: "false".to_string(),
},
VmTestCase {
input: "!!5".to_string(),
expected: "true".to_string(),
},
];

run_vm_tests(tests)?;
Expand Down Expand Up @@ -79,6 +155,22 @@ fn test_integer_arithmetic() -> Result<(), Error> {
input: "50 / 2 * 2 + 10 - 5".to_string(),
expected: "55".to_string(),
},
VmTestCase {
input: "-5".to_string(),
expected: "-5".to_string(),
},
VmTestCase {
input: "-10".to_string(),
expected: "-10".to_string(),
},
VmTestCase {
input: "-50 + 100 + -50".to_string(),
expected: "0".to_string(),
},
VmTestCase {
input: "(5 + 10 * 2 + 15 / 3) * 2 + -10".to_string(),
expected: "50".to_string(),
},
];

run_vm_tests(tests)?;
Expand Down

0 comments on commit 1e47790

Please sign in to comment.