diff --git a/src/obj/map.rs b/src/obj/map.rs index 2add1c0..a4cf943 100644 --- a/src/obj/map.rs +++ b/src/obj/map.rs @@ -62,6 +62,26 @@ impl Map { self.hash_builder.build_hasher() } + /// Helper function to look up values in the index. + fn index_lookup(this: ObjP, index: ObjP, vm: &mut Vm) -> Option { + let hash = map_hash_index(vm, &this, &index); + + with_obj_downcast(this.clone(), |map: &Map| { + map.table() + .find(hash, |(key, _)| { + let method = index + .borrow() + .get_vtable_attr(index.clone(), "__eq__") + .unwrap(); + let result = vm.call(method, &[key.clone()]); + let is_truthy = result.borrow().is_truthy(); + is_truthy + }) + .cloned() + }) + .map(|(_key, value)| value) + } + impl_create!(); } @@ -165,27 +185,77 @@ impl Map { List::create(list) } + pub(crate) fn eq(vm: &mut Vm) -> ObjP { + let this = vm.frame_stack()[0].clone(); + let other_ptr = vm.frame_stack()[1].clone(); + + let result = if let (Some(this), Some(other)) = ( + this.borrow().as_any().downcast_ref::(), + other_ptr.borrow().as_any().downcast_ref::(), + ) { + if this.table().len() != other.table().len() { + return Bool::create(false); + } + + let index_method = other.get_vtable_attr(other_ptr.clone(), "get").unwrap(); + let contains_method = other + .get_vtable_attr(other_ptr.clone(), "contains") + .unwrap(); + this.table().iter().all(|(key, value)| { + let contains = vm.call(contains_method.clone(), &[key.clone()]); + if contains.borrow().is_truthy() { + let other_value = vm.call(index_method.clone(), &[key.clone()]); + let eq_method = value + .borrow() + .get_vtable_attr(value.clone(), "__eq__") + .unwrap(); + let eq = vm.call(eq_method, &[other_value]); + let is_truthy = eq.borrow().is_truthy(); + + // check if this[key] == other[key] + is_truthy + } else { + // key in this, not in other + false + } + }) + } else { + // this and other are not both Map + false + }; + + Bool::create(result) + } + + pub(crate) fn contains(vm: &mut Vm) -> ObjP { + let this = vm.frame_stack()[0].clone(); + let index = vm.frame_stack()[1].clone(); + + let result = Map::index_lookup(this, index, vm); + + Bool::create(result.is_some()) + } + + pub(crate) fn get(vm: &mut Vm) -> ObjP { + let this = vm.frame_stack()[0].clone(); + let index = vm.frame_stack()[1].clone(); + + let result = Map::index_lookup(this, index, vm); + + if let Some(value) = result { + value + } else { + Nil::create() + } + } + pub(crate) fn index(vm: &mut Vm) -> ObjP { let this = vm.frame_stack()[0].clone(); let index = vm.frame_stack()[1].clone(); - let hash = map_hash_index(vm, &this, &index); + let result = Map::index_lookup(this, index, vm); - let result = with_obj_downcast(this.clone(), |map: &Map| { - map.table() - .find(hash, |(key, _)| { - let method = index - .borrow() - .get_vtable_attr(index.clone(), "__eq__") - .unwrap(); - let result = vm.call(method, &[key.clone()]); - let is_truthy = result.borrow().is_truthy(); - is_truthy - }) - .cloned() - }); - - if let Some((_key, value)) = result { + if let Some(value) = result { value } else { // TODO Map::index - throw an exception when no value is found in the index diff --git a/src/obj/ty.rs b/src/obj/ty.rs index 3d70ef7..4721b9e 100644 --- a/src/obj/ty.rs +++ b/src/obj/ty.rs @@ -221,9 +221,12 @@ pub fn init_types() { to_list => BuiltinFunction::create("to_list", Map::to_list, 1), // Operators + __eq__ => BuiltinFunction::create("__eq__", Map::eq, 2), __index__ => BuiltinFunction::create("__index__", Map::index, 2), // Methods + contains => BuiltinFunction::create("contains", Map::contains, 2), + get => BuiltinFunction::create("get", Map::get, 2), len => BuiltinFunction::create("len", Map::len, 1), insert => BuiltinFunction::create("insert", Map::insert, 3), remove => BuiltinFunction::create("remove", Map::remove, 2),