Sets use python __hash__ functions when items are added - need to re-implement set creation from iterable

This commit is contained in:
Matthew Constable
2019-02-03 15:25:24 +00:00
parent 328f81a28e
commit d5801162bd
4 changed files with 71 additions and 30 deletions

View File

@@ -231,7 +231,9 @@ impl Frame {
}
bytecode::Instruction::BuildSet { size, unpack } => {
let elements = self.get_elements(vm, *size, *unpack)?;
let py_obj = vm.ctx.new_set(elements);
let py_obj = vm.ctx.new_set();
// TODO: Allow initial population of set with iterable (note: __hash__() of each object being added
// requires access to the VM. (see set_add in objset.rs)
self.push_value(py_obj);
Ok(None)
}

View File

@@ -13,8 +13,9 @@ use super::objstr;
use super::objtype;
use num_bigint::ToBigInt;
use std::collections::HashMap;
use num_bigint::BigInt;
pub fn get_elements(obj: &PyObjectRef) -> HashMap<usize, PyObjectRef> {
pub fn get_elements(obj: &PyObjectRef) -> HashMap<BigInt, PyObjectRef> {
if let PyObjectPayload::Set { elements } = &obj.borrow().payload {
elements.clone()
} else {
@@ -40,15 +41,47 @@ fn set_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
);
let mut mut_obj = s.borrow_mut();
if let PyObjectPayload::Set { ref mut elements } = mut_obj.payload {
let key = item.get_id();
elements.insert(key, item.clone());
Ok(vm.get_none())
} else {
Err(vm.new_type_error("set.add is called with no list".to_string()))
match mut_obj.payload {
PyObjectPayload::Set { ref mut elements } => {
let hash_result: PyObjectRef = vm.call_method(item, "__hash__", vec![]).unwrap();
let hash_object = hash_result.borrow();
let key: BigInt;
match hash_object.payload {
PyObjectPayload::Integer { ref value } => {
let key = value.clone();
elements.insert(key, item.clone());
Ok(vm.get_none())
},
_ => { Err(vm.new_attribute_error(format!("Expected item to implment __hash__"))) }
}
},
_ => {
Err(vm.new_type_error("set.add is called with no list".to_string()))
}
}
}
//fn set_remove(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
// trace!("set.remove called with: {:?}", args);
// arg_check!(
// vm,
// args,
// required = [(s, Some(vm.ctx.set_type())), (item, None)]
// );
// let mut mut_obj = s.borrow_mut();
//
// match mut_obj.payload {
// PyObjectPayload::Set { ref mut elements } => {
// let key = item.get_id();
// elements.remove(&key);
// Ok(vm.get_none())
// },
// _ => {
// Err(vm.new_key_error("set.remove is called with no element".to_string()))
// }
// }
//}
/* Create a new object of sub-type of set */
fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
@@ -62,27 +95,27 @@ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
return Err(vm.new_type_error(format!("{} is not a subtype of set", cls.borrow())));
}
let elements = match iterable {
None => HashMap::new(),
Some(iterable) => {
let mut elements = HashMap::new();
let iterator = objiter::get_iter(vm, iterable)?;
loop {
match vm.call_method(&iterator, "__next__", vec![]) {
Ok(v) => {
// TODO: should we use the hash function here?
let key = v.get_id();
elements.insert(key, v);
}
_ => break,
}
}
elements
}
};
// let elements = match iterable {
// None => HashMap::new(),
// Some(iterable) => {
// let mut elements = HashMap::new();
// let iterator = objiter::get_iter(vm, iterable)?;
// loop {
// match vm.call_method(&iterator, "__next__", vec![]) {
// Ok(v) => {
// // TODO: should we use the hash function here?
// let key = v.get_id();
// elements.insert(key, v);
// }
// _ => break,
// }
// }
// elements
// }
// };
Ok(PyObject::new(
PyObjectPayload::Set { elements: elements },
PyObjectPayload::Set { elements: HashMap::new() },
cls.clone(),
))
}
@@ -161,6 +194,7 @@ pub fn init(context: &PyContext) {
context.set_attr(&set_type, "__new__", context.new_rustfunc(set_new));
context.set_attr(&set_type, "__repr__", context.new_rustfunc(set_repr));
context.set_attr(&set_type, "add", context.new_rustfunc(set_add));
// context.set_attr(&set_type, "remove", context.new_rustfunc(set_remove));
let ref frozenset_type = context.frozenset_type;
context.set_attr(

View File

@@ -440,8 +440,8 @@ impl PyContext {
)
}
pub fn new_set(&self, elements: Vec<PyObjectRef>) -> PyObjectRef {
let elements = objset::sequence_to_hashmap(&elements);
pub fn new_set(&self) -> PyObjectRef {
let elements: HashMap<BigInt, PyObjectRef> = HashMap::new();
PyObject::new(PyObjectPayload::Set { elements: elements }, self.set_type())
}
@@ -871,7 +871,7 @@ pub enum PyObjectPayload {
elements: objdict::DictContentType,
},
Set {
elements: HashMap<usize, PyObjectRef>,
elements: HashMap<BigInt, PyObjectRef>,
},
Iterator {
position: usize,

View File

@@ -100,6 +100,11 @@ impl VirtualMachine {
self.new_exception(type_error, msg)
}
pub fn new_attribute_error(&mut self, msg: String) -> PyObjectRef {
let type_error = self.ctx.exceptions.attribute_error.clone();
self.new_exception(type_error, msg)
}
/// Create a new python ValueError object. Useful for raising errors from
/// python functions implemented in rust.
pub fn new_value_error(&mut self, msg: String) -> PyObjectRef {