From 1e477907372f1b600b5fa6b35aa0564837764ce4 Mon Sep 17 00:00:00 2001 From: Zak Farmer <zak@3sidedcube.com> Date: Tue, 24 Oct 2023 18:10:00 +0100 Subject: [PATCH] feat: bang and minus prefix operators --- compiler/src/lib.rs | 11 ++++ compiler/tests/compiler_tests.rs | 17 ++++++ vm/src/lib.rs | 31 +++++++++++ vm/tests/vm_tests.rs | 92 ++++++++++++++++++++++++++++++++ 4 files changed, 151 insertions(+) diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index 5bd8696..28f687e 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -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, .. } => { diff --git a/compiler/tests/compiler_tests.rs b/compiler/tests/compiler_tests.rs index b64edf2..764c622 100644 --- a/compiler/tests/compiler_tests.rs +++ b/compiler/tests/compiler_tests.rs @@ -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)?; @@ -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)?; diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 97288ce..7f50f50 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -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))); } diff --git a/vm/tests/vm_tests.rs b/vm/tests/vm_tests.rs index efc6458..9ccdece 100644 --- a/vm/tests/vm_tests.rs +++ b/vm/tests/vm_tests.rs @@ -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)?; @@ -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)?;