From 9c0c6a82d9431ba51be6ecc32ddbdcdabaec1a45 Mon Sep 17 00:00:00 2001 From: Zak Farmer Date: Thu, 26 Oct 2023 18:30:20 +0100 Subject: [PATCH] feat: array indexing compilation and execution --- compiler/src/lib.rs | 8 +++++ compiler/tests/compiler_tests.rs | 55 ++++++++++++++++++++++++++++++++ vm/src/lib.rs | 28 ++++++++++++++++ vm/tests/vm_tests.rs | 22 +++++++++++++ 4 files changed, 113 insertions(+) diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index e18f991..5643601 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -250,6 +250,14 @@ impl Compiler { Ok(()) } + Expression::Index(index_expression) => { + self.compile_expression(&index_expression.left)?; + self.compile_expression(&index_expression.index)?; + + self.emit(opcode::Opcode::OpIndex, vec![]); + + Ok(()) + } Expression::Infix(infix_expression) => { self.compile_operands( &infix_expression.left, diff --git a/compiler/tests/compiler_tests.rs b/compiler/tests/compiler_tests.rs index 5ba961d..50e683c 100644 --- a/compiler/tests/compiler_tests.rs +++ b/compiler/tests/compiler_tests.rs @@ -194,6 +194,58 @@ fn test_conditionals() -> Result<(), Error> { Ok(()) } +#[test] +fn test_index_expressions() -> Result<(), Error> { + let tests = vec![ + CompilerTestCase { + input: "[1, 2, 3][1 + 1]".to_string(), + expected_constants: vec![ + Object::Integer(1), + Object::Integer(2), + Object::Integer(3), + Object::Integer(1), + Object::Integer(1), + ], + expected_instructions: vec![ + opcode::make(opcode::Opcode::OpConst, &vec![0]), + opcode::make(opcode::Opcode::OpConst, &vec![1]), + opcode::make(opcode::Opcode::OpConst, &vec![2]), + opcode::make(opcode::Opcode::OpArray, &vec![3]), + opcode::make(opcode::Opcode::OpConst, &vec![3]), + opcode::make(opcode::Opcode::OpConst, &vec![4]), + opcode::make(opcode::Opcode::OpAdd, &vec![4]), + opcode::make(opcode::Opcode::OpIndex, &vec![]), + opcode::make(opcode::Opcode::OpPop, &vec![]), + ], + }, + CompilerTestCase { + input: "[1, 2, 3][2 - 1]".to_string(), + expected_constants: vec![ + Object::Integer(1), + Object::Integer(2), + Object::Integer(3), + Object::Integer(2), + Object::Integer(1), + ], + expected_instructions: vec![ + opcode::make(opcode::Opcode::OpConst, &vec![0]), + opcode::make(opcode::Opcode::OpConst, &vec![1]), + opcode::make(opcode::Opcode::OpConst, &vec![2]), + opcode::make(opcode::Opcode::OpArray, &vec![3]), + opcode::make(opcode::Opcode::OpConst, &vec![3]), + opcode::make(opcode::Opcode::OpConst, &vec![4]), + opcode::make(opcode::Opcode::OpSub, &vec![4]), + opcode::make(opcode::Opcode::OpIndex, &vec![]), + opcode::make(opcode::Opcode::OpPop, &vec![]), + ], + }, + ]; + + run_compiler_tests(tests)?; + + Ok(()) +} + #[test] fn test_integer_arithmetic() -> Result<(), Error> { let tests = vec![ @@ -293,6 +345,9 @@ fn run_compiler_tests(tests: Vec) -> Result<(), Error> { let bytecode = compiler.compile(&Node::Program(program))?; + println!("Testing input: {}", test.input.to_string()); + println!("Constants: {:?}", bytecode.constants); + test_constants(&test.expected_constants, &bytecode.constants); test_instructions(&test.expected_instructions, &bytecode.instructions); } diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 6965a2b..cea8599 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -279,6 +279,34 @@ impl Vm { self.push(Rc::new(Object::Array(elements))); } + Opcode::OpIndex => { + let index = self.pop(); + let left = self.pop(); + + let result = match (&*left, &*index) { + (Object::Array(elements), Object::Integer(integer)) => { + let idx = *integer as usize; + + if idx >= elements.len() { + return Err(Error::msg(format!( + "index out of bounds: index={}, length={}", + idx, + elements.len() + ))); + } + + Rc::clone(&elements[idx]) + } + _ => { + return Err(Error::msg(format!( + "unsupported types for index: {}[{}]", + left, index + ))); + } + }; + + self.push(result); + } _ => { return Err(Error::msg(format!("unknown opcode: {}", op))); } diff --git a/vm/tests/vm_tests.rs b/vm/tests/vm_tests.rs index 843524a..2f50ccd 100644 --- a/vm/tests/vm_tests.rs +++ b/vm/tests/vm_tests.rs @@ -272,6 +272,28 @@ fn test_global_dollar_statements() -> Result<(), Error> { Ok(()) } +#[test] +fn test_index_expressions() -> Result<(), Error> { + let tests = vec![ + VmTestCase { + input: "[1, 2, 3][1]".to_string(), + expected: "2".to_string(), + }, + VmTestCase { + input: "[1, 2, 3][0 + 2]".to_string(), + expected: "3".to_string(), + }, + VmTestCase { + input: "[[1, 1, 1]][0][0]".to_string(), + expected: "1".to_string(), + }, + ]; + + run_vm_tests(tests)?; + + Ok(()) +} + #[test] fn test_string_expressions() -> Result<(), Error> { let tests = vec![