Add Map __eq__, contains, and get methods

* __eq__ recursively checks equality (need a way to check for cycles)
* contains checks if the map contains a given index
* get will get an item from the map, but return nil if it doesn't exist
  as a key

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2024-10-14 20:13:43 -07:00
parent 4cdc48537c
commit 3263656668
2 changed files with 89 additions and 16 deletions

View File

@@ -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<ObjP> {
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::<Map>(),
other_ptr.borrow().as_any().downcast_ref::<Map>(),
) {
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

View File

@@ -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),