Skip to content

Commit

Permalink
feat: array indexing compilation and execution
Browse files Browse the repository at this point in the history
  • Loading branch information
ZakFarmer committed Oct 26, 2023
1 parent 28a8bdc commit 9c0c6a8
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 0 deletions.
8 changes: 8 additions & 0 deletions compiler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
55 changes: 55 additions & 0 deletions compiler/tests/compiler_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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![
Expand Down Expand Up @@ -293,6 +345,9 @@ fn run_compiler_tests(tests: Vec<CompilerTestCase>) -> 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);
}
Expand Down
28 changes: 28 additions & 0 deletions vm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)));
}
Expand Down
22 changes: 22 additions & 0 deletions vm/tests/vm_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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![
Expand Down

0 comments on commit 9c0c6a8

Please sign in to comment.