Skip to content

Commit

Permalink
Feat: Add indexers to the compiler and vm
Browse files Browse the repository at this point in the history
  • Loading branch information
Yag000 committed Jul 16, 2023
1 parent d08ce44 commit 8178135
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 1 deletion.
2 changes: 2 additions & 0 deletions crates/compiler/src/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ pub enum Opcode {
// Custom types
Array,
HashMap,
Index,

// Stack
Pop,
Expand Down Expand Up @@ -135,6 +136,7 @@ impl Display for Opcode {
Opcode::GetGlobal => "OpGetGlobal",
Opcode::Array => "OpArray",
Opcode::HashMap => "OpHashMap",
Opcode::Index => "OpIndex",
Opcode::Pop => "OpPop",
};
write!(f, "{op}")
Expand Down
53 changes: 53 additions & 0 deletions crates/compiler/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ impl Compiler {
}
self.emit(Opcode::HashMap, vec![len * 2]);
}
Expression::IndexExpression(index) => {
self.compile_expression(*index.left)?;
self.compile_expression(*index.index)?;
self.emit(Opcode::Index, vec![]);
}
_ => unimplemented!(),
}

Expand Down Expand Up @@ -800,4 +805,52 @@ pub mod tests {

run_compiler(tests);
}

#[test]
fn test_index_expressions() {
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: flatten_instructions(vec![
Opcode::Constant.make(vec![0]),
Opcode::Constant.make(vec![1]),
Opcode::Constant.make(vec![2]),
Opcode::Array.make(vec![3]),
Opcode::Constant.make(vec![3]),
Opcode::Constant.make(vec![4]),
Opcode::Add.make(vec![]),
Opcode::Index.make(vec![]),
Opcode::Pop.make(vec![]),
]),
},
CompilerTestCase {
input: "{1: 2}[2 - 1]".to_string(),
expected_constants: vec![
Object::INTEGER(1),
Object::INTEGER(2),
Object::INTEGER(2),
Object::INTEGER(1),
],
expected_instructions: flatten_instructions(vec![
Opcode::Constant.make(vec![0]),
Opcode::Constant.make(vec![1]),
Opcode::HashMap.make(vec![2]),
Opcode::Constant.make(vec![2]),
Opcode::Constant.make(vec![3]),
Opcode::Sub.make(vec![]),
Opcode::Index.make(vec![]),
Opcode::Pop.make(vec![]),
]),
},
];

run_compiler(tests);
}
}
92 changes: 91 additions & 1 deletion crates/vm/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ impl VM {
self.sp -= num_elements;
self.push(hashmap)?;
}
Opcode::Index => {
let index = self.pop().ok_or("Stack underflow".to_string())?;
let left = self.pop().ok_or("Stack underflow".to_string())?;
self.execute_index_expression(&left, &index)?;
}
}
ip += 1;
}
Expand Down Expand Up @@ -292,12 +297,49 @@ impl VM {
.ok_or("Unable to get element".to_string()))?)
.clone();
if !Object::is_hashable(&key) {
return Err(format!("Unusable as a hashmap key: {key:?}",));
return Ok(Rc::new(Object::ERROR(format!(
"Unusable as hashmap key: {key:?}"
))));
}
elements.insert(key, value);
}
Ok(Rc::new(Object::HASHMAP(elements)))
}

fn execute_index_expression(
&mut self,
left: &Rc<Object>,
index: &Rc<Object>,
) -> Result<(), String> {
match (&**left, &**index) {
(Object::ARRAY(elements), Object::INTEGER(i)) => {
if *i < 0 || *i >= elements.len() as i64 {
self.push(Rc::new(Object::NULL))?;
} else {
let result = elements
.get(*i as usize)
.ok_or("Index out of bounds".to_string())?;
self.push(Rc::new(result.clone()))?;
}
}
(Object::HASHMAP(elements), _) => {
if !Object::is_hashable(index) {
return Err("Unusable as hashmap key".to_string());
}
let result = elements.get(index);
if result.is_none() {
self.push(Rc::new(Object::NULL))?;
} else {
self.push(Rc::new(result.unwrap().clone()))?;
}
}
_ => {
return Err("Unsupported types for index operation".to_string());
}
}
Ok(())
}

fn native_boolean_to_boolean_object(&self, input: bool) -> Rc<Object> {
if input {
Rc::new(TRUE)
Expand Down Expand Up @@ -720,4 +762,52 @@ mod tests {

run_vm_tests(tests);
}

#[test]
fn test_index_expression() {
let tests = vec![
VmTestCase {
input: "[1, 2, 3][1]".to_string(),
expected: Object::INTEGER(2),
},
VmTestCase {
input: "[1, 2, 3][0 + 2]".to_string(),
expected: Object::INTEGER(3),
},
VmTestCase {
input: "[[1, 1, 1]][0][0]".to_string(),
expected: Object::INTEGER(1),
},
VmTestCase {
input: "[][0]".to_string(),
expected: Object::NULL,
},
VmTestCase {
input: "[1, 2, 3][99]".to_string(),
expected: Object::NULL,
},
VmTestCase {
input: "[1][-1]".to_string(),
expected: Object::NULL,
},
VmTestCase {
input: "{1: 1, 2: 2}[1]".to_string(),
expected: Object::INTEGER(1),
},
VmTestCase {
input: "{1: 1, 2: 2}[2]".to_string(),
expected: Object::INTEGER(2),
},
VmTestCase {
input: "{1: 1}[0]".to_string(),
expected: Object::NULL,
},
VmTestCase {
input: "{}[0]".to_string(),
expected: Object::NULL,
},
];

run_vm_tests(tests);
}
}

0 comments on commit 8178135

Please sign in to comment.