Move builtin types to own directory

This commit is contained in:
Windel Bouwman
2018-08-31 20:31:25 +02:00
parent c687db079a
commit f453c749ec
15 changed files with 51 additions and 49 deletions

6
vm/src/obj/mod.rs Normal file
View File

@@ -0,0 +1,6 @@
pub mod objdict;
pub mod objfloat;
pub mod objint;
pub mod objlist;
pub mod objstr;
pub mod objtype;

80
vm/src/obj/objdict.rs Normal file
View File

@@ -0,0 +1,80 @@
use super::super::pyobject::{
AttributeProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult,
TypeProtocol,
};
use super::super::vm::VirtualMachine;
use super::objstr;
use super::objtype;
use std::collections::HashMap;
pub fn _set_item(
vm: &mut VirtualMachine,
_d: PyObjectRef,
_idx: PyObjectRef,
_obj: PyObjectRef,
) -> PyResult {
// TODO: Implement objdict::set_item
Ok(vm.get_none())
}
pub fn new(dict_type: PyObjectRef) -> PyObjectRef {
PyObject::new(
PyObjectKind::Dict {
elements: HashMap::new(),
},
dict_type.clone(),
)
}
pub fn get_elements(obj: &PyObjectRef) -> HashMap<String, PyObjectRef> {
if let PyObjectKind::Dict { elements } = &obj.borrow().kind {
elements.clone()
} else {
panic!("Cannot extract dict elements");
}
}
fn dict_new(_vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
Ok(new(args.args[0].clone()))
}
fn dict_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(o, Some(vm.ctx.dict_type()))]);
let elements = get_elements(o);
Ok(vm.ctx.new_int(elements.len() as i32))
}
fn dict_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(o, Some(vm.ctx.dict_type()))]);
let elements = get_elements(o);
let mut str_parts = vec![];
for elem in elements {
match vm.to_str(elem.1) {
Ok(s) => {
let value_str = objstr::get_value(&s);
str_parts.push(format!("{}: {}", elem.0, value_str));
}
Err(err) => return Err(err),
}
}
let s = format!("{{ {} }}", str_parts.join(", "));
Ok(vm.new_str(s))
}
pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) {
(*dict_type.borrow_mut()).kind = PyObjectKind::Class {
name: String::from("dict"),
dict: new(dict_type.clone()),
mro: vec![object_type],
};
(*dict_type.borrow_mut()).typ = Some(type_type.clone());
}
pub fn init(context: &PyContext) {
let ref dict_type = context.dict_type;
dict_type.set_attr("__len__", context.new_rustfunc(dict_len));
dict_type.set_attr("__new__", context.new_rustfunc(dict_new));
dict_type.set_attr("__str__", context.new_rustfunc(dict_str));
}

83
vm/src/obj/objfloat.rs Normal file
View File

@@ -0,0 +1,83 @@
use super::super::pyobject::{
AttributeProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol,
};
use super::super::vm::VirtualMachine;
use super::objint;
use super::objtype;
fn str(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result<PyObjectRef, PyObjectRef> {
arg_check!(vm, args, required = [(float, Some(vm.ctx.float_type()))]);
let v = get_value(float.clone());
Ok(vm.new_str(v.to_string()))
}
// Retrieve inner float value:
pub fn get_value(obj: PyObjectRef) -> f64 {
if let PyObjectKind::Float { value } = &obj.borrow().kind {
*value
} else {
panic!("Inner error getting float");
}
}
fn float_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(i, Some(vm.ctx.float_type())), (i2, None)]
);
let v1 = get_value(i.clone());
if objtype::isinstance(i2.clone(), vm.ctx.float_type()) {
Ok(vm.ctx.new_float(v1 + get_value(i2.clone())))
} else if objtype::isinstance(i2.clone(), vm.ctx.int_type()) {
Ok(vm.ctx.new_float(v1 + objint::get_value(i2.clone()) as f64))
} else {
Err(vm.new_type_error(format!("Cannot add {:?} and {:?}", i, i2)))
}
}
fn float_sub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(i, Some(vm.ctx.float_type())), (i2, None)]
);
let v1 = get_value(i.clone());
if objtype::isinstance(i2.clone(), vm.ctx.float_type()) {
Ok(vm.ctx.new_float(v1 - get_value(i2.clone())))
} else if objtype::isinstance(i2.clone(), vm.ctx.int_type()) {
Ok(vm.ctx.new_float(v1 - objint::get_value(i2.clone()) as f64))
} else {
Err(vm.new_type_error(format!("Cannot add {:?} and {:?}", i, i2)))
}
}
fn float_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(i, Some(vm.ctx.float_type())), (i2, None)]
);
let v1 = get_value(i.clone());
if objtype::isinstance(i2.clone(), vm.ctx.float_type()) {
let result = v1.powf(get_value(i2.clone()));
Ok(vm.ctx.new_float(result))
} else if objtype::isinstance(i2.clone(), vm.ctx.int_type()) {
let result = v1.powf(objint::get_value(i2.clone()) as f64);
Ok(vm.ctx.new_float(result))
} else {
Err(vm.new_type_error(format!("Cannot add {:?} and {:?}", i, i2)))
}
}
pub fn init(context: &PyContext) {
let ref float_type = context.float_type;
float_type.set_attr("__add__", context.new_rustfunc(float_add));
float_type.set_attr("__pow__", context.new_rustfunc(float_pow));
float_type.set_attr("__str__", context.new_rustfunc(str));
float_type.set_attr("__sub__", context.new_rustfunc(float_sub));
float_type.set_attr("__repr__", context.new_rustfunc(str));
}

126
vm/src/obj/objint.rs Normal file
View File

@@ -0,0 +1,126 @@
use super::super::pyobject::{
AttributeProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol,
};
use super::super::vm::VirtualMachine;
use super::objfloat;
use super::objtype;
fn str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(int, Some(vm.ctx.int_type()))]);
let v = get_value(int.clone());
Ok(vm.new_str(v.to_string()))
}
// Retrieve inner int value:
pub fn get_value(obj: PyObjectRef) -> i32 {
if let PyObjectKind::Integer { value } = &obj.borrow().kind {
*value
} else {
panic!("Inner error getting int");
}
}
fn int_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(i, Some(vm.ctx.int_type())), (i2, None)]
);
if objtype::isinstance(i2.clone(), vm.ctx.int_type()) {
Ok(vm.ctx.new_int(get_value(i.clone()) + get_value(i2.clone())))
} else if objtype::isinstance(i2.clone(), vm.ctx.float_type()) {
Ok(vm
.ctx
.new_float(get_value(i.clone()) as f64 + objfloat::get_value(i2.clone())))
} else {
Err(vm.new_type_error(format!("Cannot add {:?} and {:?}", i, i2)))
}
}
fn int_sub(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(i, Some(vm.ctx.int_type())), (i2, None)]
);
if objtype::isinstance(i2.clone(), vm.ctx.int_type()) {
Ok(vm.ctx.new_int(get_value(i.clone()) - get_value(i2.clone())))
} else {
Err(vm.new_type_error(format!("Cannot substract {:?} and {:?}", i, i2)))
}
}
fn int_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(i, Some(vm.ctx.int_type())), (i2, None)]
);
if objtype::isinstance(i2.clone(), vm.ctx.int_type()) {
Ok(vm.ctx.new_int(get_value(i.clone()) * get_value(i2.clone())))
} else {
Err(vm.new_type_error(format!("Cannot multiply {:?} and {:?}", i, i2)))
}
}
fn int_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(i, Some(vm.ctx.int_type())), (i2, None)]
);
if objtype::isinstance(i2.clone(), vm.ctx.int_type()) {
Ok(vm
.ctx
.new_float(get_value(i.clone()) as f64 / get_value(i2.clone()) as f64))
} else if objtype::isinstance(i2.clone(), vm.ctx.float_type()) {
Ok(vm
.ctx
.new_float(get_value(i.clone()) as f64 / objfloat::get_value(i2.clone())))
} else {
Err(vm.new_type_error(format!("Cannot multiply {:?} and {:?}", i, i2)))
}
}
fn int_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(i, Some(vm.ctx.int_type())), (i2, None)]
);
if objtype::isinstance(i2.clone(), vm.ctx.int_type()) {
Ok(vm.ctx.new_int(get_value(i.clone()) % get_value(i2.clone())))
} else {
Err(vm.new_type_error(format!("Cannot modulo {:?} and {:?}", i, i2)))
}
}
fn int_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(i, Some(vm.ctx.int_type())), (i2, None)]
);
let v1 = get_value(i.clone());
if objtype::isinstance(i2.clone(), vm.ctx.int_type()) {
let v2 = get_value(i2.clone());
Ok(vm.ctx.new_int(v1.pow(v2 as u32)))
} else if objtype::isinstance(i2.clone(), vm.ctx.float_type()) {
let v2 = objfloat::get_value(i2.clone());
Ok(vm.ctx.new_float((v1 as f64).powf(v2)))
} else {
Err(vm.new_type_error(format!("Cannot modulo {:?} and {:?}", i, i2)))
}
}
pub fn init(context: &PyContext) {
let ref int_type = context.int_type;
int_type.set_attr("__add__", context.new_rustfunc(int_add));
int_type.set_attr("__mod__", context.new_rustfunc(int_mod));
int_type.set_attr("__mul__", context.new_rustfunc(int_mul));
int_type.set_attr("__pow__", context.new_rustfunc(int_pow));
int_type.set_attr("__repr__", context.new_rustfunc(str));
int_type.set_attr("__str__", context.new_rustfunc(str));
int_type.set_attr("__sub__", context.new_rustfunc(int_sub));
int_type.set_attr("__truediv__", context.new_rustfunc(int_truediv));
}

129
vm/src/obj/objlist.rs Normal file
View File

@@ -0,0 +1,129 @@
use super::super::objsequence::PySliceableSequence;
use super::super::pyobject::{
AttributeProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol,
};
use super::super::vm::VirtualMachine;
use super::objstr;
use super::objtype;
// set_item:
pub fn set_item(
vm: &mut VirtualMachine,
l: &mut Vec<PyObjectRef>,
idx: PyObjectRef,
obj: PyObjectRef,
) -> PyResult {
match &(idx.borrow()).kind {
PyObjectKind::Integer { value } => {
let pos_index = l.get_pos(*value);
l[pos_index] = obj;
Ok(vm.get_none())
}
_ => panic!(
"TypeError: indexing type {:?} with index {:?} is not supported (yet?)",
l, idx
),
}
}
pub fn get_elements(obj: PyObjectRef) -> Vec<PyObjectRef> {
if let PyObjectKind::List { elements } = &obj.borrow().kind {
elements.to_vec()
} else {
panic!("Cannot extract list elements from non-list");
}
}
fn list_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(o, Some(vm.ctx.list_type())), (o2, None)]
);
if objtype::isinstance(o2.clone(), vm.ctx.list_type()) {
let e1 = get_elements(o.clone());
let e2 = get_elements(o2.clone());
let elements = e1.iter().chain(e2.iter()).map(|e| e.clone()).collect();
Ok(vm.ctx.new_list(elements))
} else {
Err(vm.new_type_error(format!("Cannot add {:?} and {:?}", o, o2)))
}
}
fn list_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(o, Some(vm.ctx.list_type()))]);
let elements = get_elements(o.clone());
let mut str_parts = vec![];
for elem in elements {
match vm.to_str(elem) {
Ok(s) => str_parts.push(objstr::get_value(&s)),
Err(err) => return Err(err),
}
}
let s = format!("[{}]", str_parts.join(", "));
Ok(vm.new_str(s))
}
pub fn append(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
trace!("list.append called with: {:?}", args);
arg_check!(
vm,
args,
required = [(list, Some(vm.ctx.list_type())), (x, None)]
);
let mut list_obj = list.borrow_mut();
if let PyObjectKind::List { ref mut elements } = list_obj.kind {
elements.push(x.clone());
Ok(vm.get_none())
} else {
Err(vm.new_type_error("list.append is called with no list".to_string()))
}
}
fn clear(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
trace!("list.clear called with: {:?}", args);
arg_check!(vm, args, required = [(list, Some(vm.ctx.list_type()))]);
let mut list_obj = list.borrow_mut();
if let PyObjectKind::List { ref mut elements } = list_obj.kind {
elements.clear();
Ok(vm.get_none())
} else {
Err(vm.new_type_error("list.clear is called with no list".to_string()))
}
}
fn len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
trace!("list.len called with: {:?}", args);
arg_check!(vm, args, required = [(list, Some(vm.ctx.list_type()))]);
let list_obj = list.borrow();
if let PyObjectKind::List { ref elements } = list_obj.kind {
Ok(vm.context().new_int(elements.len() as i32))
} else {
Err(vm.new_type_error("list.len is called with no list".to_string()))
}
}
fn reverse(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
trace!("list.reverse called with: {:?}", args);
arg_check!(vm, args, required = [(list, Some(vm.ctx.list_type()))]);
let mut list_obj = list.borrow_mut();
if let PyObjectKind::List { ref mut elements } = list_obj.kind {
elements.reverse();
Ok(vm.get_none())
} else {
Err(vm.new_type_error("list.reverse is called with no list".to_string()))
}
}
pub fn init(context: &PyContext) {
let ref list_type = context.list_type;
list_type.set_attr("__add__", context.new_rustfunc(list_add));
list_type.set_attr("__len__", context.new_rustfunc(len));
list_type.set_attr("__str__", context.new_rustfunc(list_str));
list_type.set_attr("append", context.new_rustfunc(append));
list_type.set_attr("clear", context.new_rustfunc(clear));
list_type.set_attr("reverse", context.new_rustfunc(reverse));
}

115
vm/src/obj/objstr.rs Normal file
View File

@@ -0,0 +1,115 @@
use super::super::objsequence::PySliceableSequence;
use super::super::pyobject::{
AttributeProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol,
};
use super::super::vm::VirtualMachine;
use super::objint;
use super::objtype;
pub fn init(context: &PyContext) {
let ref str_type = context.str_type;
str_type.set_attr("__add__", context.new_rustfunc(str_add));
str_type.set_attr("__len__", context.new_rustfunc(str_len));
str_type.set_attr("__mul__", context.new_rustfunc(str_mul));
str_type.set_attr("__new__", context.new_rustfunc(str_new));
str_type.set_attr("__str__", context.new_rustfunc(str_str));
}
pub fn get_value(obj: &PyObjectRef) -> String {
if let PyObjectKind::String { value } = &obj.borrow().kind {
value.to_string()
} else {
panic!("Inner error getting str");
}
}
fn str_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
Ok(s.clone())
}
fn str_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(s, Some(vm.ctx.str_type())), (s2, None)]
);
if objtype::isinstance(s2.clone(), vm.ctx.str_type()) {
Ok(vm
.ctx
.new_str(format!("{}{}", get_value(&s), get_value(&s2))))
} else {
Err(vm.new_type_error(format!("Cannot add {:?} and {:?}", s, s2)))
}
}
fn str_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
let sv = get_value(s);
Ok(vm.ctx.new_int(sv.len() as i32))
}
fn str_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(s, Some(vm.ctx.str_type())), (s2, None)]
);
if objtype::isinstance(s2.clone(), vm.ctx.int_type()) {
let value1 = get_value(&s);
let value2 = objint::get_value(s2.clone());
let mut result = String::new();
for _x in 0..value2 {
result.push_str(value1.as_str());
}
Ok(vm.ctx.new_str(result))
} else {
Err(vm.new_type_error(format!("Cannot multiply {:?} and {:?}", s, s2)))
}
}
// TODO: should with following format
// class str(object='')
// class str(object=b'', encoding='utf-8', errors='strict')
fn str_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
if args.args.len() == 1 {
return Ok(vm.new_str("".to_string()));
}
if args.args.len() > 2 {
panic!("str expects exactly one parameter");
};
vm.to_str(args.args[1].clone())
}
impl PySliceableSequence for String {
fn do_slice(&self, start: usize, stop: usize) -> Self {
self[start..stop].to_string()
}
fn do_stepped_slice(&self, start: usize, stop: usize, step: usize) -> Self {
self[start..stop].chars().step_by(step).collect()
}
fn len(&self) -> usize {
self.len()
}
}
pub fn subscript(vm: &mut VirtualMachine, value: &String, b: PyObjectRef) -> PyResult {
// let value = a
match &(*b.borrow()).kind {
&PyObjectKind::Integer { value: ref pos } => {
let idx = value.get_pos(*pos);
Ok(vm.new_str(value[idx..idx + 1].to_string()))
}
&PyObjectKind::Slice {
start: _,
stop: _,
step: _,
} => Ok(vm.new_str(value.get_slice_items(&b))),
_ => panic!(
"TypeError: indexing type {:?} with index {:?} is not supported (yet?)",
value, b
),
}
}

269
vm/src/obj/objtype.rs Normal file
View File

@@ -0,0 +1,269 @@
use super::super::pyobject::{
AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef,
PyResult, ToRust, TypeProtocol,
};
use super::super::vm::VirtualMachine;
use super::objdict;
use super::objtype; // Required for arg_check! to use isinstance
/*
* The magical type type
*/
pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) {
(*type_type.borrow_mut()).kind = PyObjectKind::Class {
name: String::from("type"),
dict: objdict::new(dict_type),
mro: vec![object_type],
};
(*type_type.borrow_mut()).typ = Some(type_type.clone());
}
pub fn init(context: &PyContext) {
let ref type_type = context.type_type;
type_type.set_attr("__call__", context.new_rustfunc(type_call));
type_type.set_attr("__new__", context.new_rustfunc(type_new));
type_type.set_attr("__mro__", context.new_member_descriptor(type_mro));
type_type.set_attr("__class__", context.new_member_descriptor(type_new));
type_type.set_attr("__str__", context.new_rustfunc(type_str));
}
fn type_mro(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
println!("{:?}", args);
arg_check!(
vm,
args,
required = [
(cls, Some(vm.ctx.type_type())),
(_typ, Some(vm.ctx.type_type()))
]
);
match _mro(cls.clone()) {
Some(mro) => Ok(vm.context().new_tuple(mro)),
None => Err(vm.new_type_error("Only classes have an MRO.".to_string())),
}
}
fn _mro(cls: PyObjectRef) -> Option<Vec<PyObjectRef>> {
match cls.borrow().kind {
PyObjectKind::Class { ref mro, .. } => {
let mut mro = mro.clone();
mro.insert(0, cls.clone());
Some(mro)
}
_ => None,
}
}
pub fn isinstance(obj: PyObjectRef, cls: PyObjectRef) -> bool {
let mro = _mro(obj.typ()).unwrap();
mro.into_iter().any(|c| c.is(&cls))
}
pub fn issubclass(typ: PyObjectRef, cls: PyObjectRef) -> bool {
let mro = _mro(typ).unwrap();
mro.into_iter().any(|c| c.is(&cls))
}
pub fn get_type_name(typ: &PyObjectRef) -> String {
if let PyObjectKind::Class {
name,
dict: _,
mro: _,
} = &typ.borrow().kind
{
name.clone()
} else {
panic!("Cannot get type_name of non-type type");
}
}
pub fn type_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
debug!("type.__new__{:?}", args);
if args.args.len() == 2 {
arg_check!(
vm,
args,
required = [(_typ, Some(vm.ctx.type_type())), (obj, None)]
);
Ok(obj.typ())
} else if args.args.len() == 4 {
arg_check!(
vm,
args,
required = [
(typ, Some(vm.ctx.type_type())),
(name, Some(vm.ctx.str_type())),
// bases needs to be mutable, which arg_check! doesn't support, so we just check
// the type and extract it again below
// TODO: arg_check! should support specifying iterables
(_bases, None),
(dict, Some(vm.ctx.dict_type()))
]
);
let mut bases = args.args[2].to_vec().unwrap();
bases.push(vm.context().object());
new(typ.clone(), &name.to_str().unwrap(), bases, dict.clone())
} else {
Err(vm.new_type_error(format!(": type_new: {:?}", args)))
}
}
pub fn type_call(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
debug!("type_call: {:?}", args);
let typ = args.shift();
let new = typ.get_attr("__new__").unwrap();
let obj = vm.invoke(new, args.insert(typ.clone()))?;
if let Some(init) = obj.typ().get_attr("__init__") {
let _ = vm.invoke(init, args.insert(obj.clone())).unwrap();
}
Ok(obj)
}
pub fn get_attribute(vm: &mut VirtualMachine, obj: PyObjectRef, name: &String) -> PyResult {
let cls = obj.typ();
trace!("get_attribute: {:?}, {:?}, {:?}", cls, obj, name);
if let Some(attr) = cls.get_attr(name) {
let attr_class = attr.typ();
if let Some(descriptor) = attr_class.get_attr("__get__") {
return vm.invoke(
descriptor,
PyFuncArgs {
args: vec![attr, obj, cls],
},
);
}
}
if let Some(obj_attr) = obj.get_attr(name) {
Ok(obj_attr)
} else if let Some(cls_attr) = cls.get_attr(name) {
Ok(cls_attr)
} else {
let attribute_error = vm.context().exceptions.attribute_error.clone();
Err(vm.new_exception(
attribute_error,
format!("{:?} object has no attribute {}", cls, name),
))
}
}
fn take_next_base(
mut bases: Vec<Vec<PyObjectRef>>,
) -> Option<(PyObjectRef, Vec<Vec<PyObjectRef>>)> {
let mut next = None;
bases = bases.into_iter().filter(|x| !x.is_empty()).collect();
for base in &bases {
let head = base[0].clone();
if !(&bases)
.into_iter()
.any(|x| x[1..].into_iter().any(|x| x.get_id() == head.get_id()))
{
next = Some(head);
break;
}
}
if let Some(head) = next {
for ref mut item in &mut bases {
if item[0].get_id() == head.get_id() {
item.remove(0);
}
}
return Some((head, bases));
}
None
}
fn linearise_mro(mut bases: Vec<Vec<PyObjectRef>>) -> Option<Vec<PyObjectRef>> {
debug!("Linearising MRO: {:?}", bases);
let mut result = vec![];
loop {
if (&bases).into_iter().all(|x| x.is_empty()) {
break;
}
match take_next_base(bases) {
Some((head, new_bases)) => {
result.push(head);
bases = new_bases;
}
None => return None,
}
}
Some(result)
}
pub fn new(typ: PyObjectRef, name: &str, bases: Vec<PyObjectRef>, dict: PyObjectRef) -> PyResult {
let mros = bases.into_iter().map(|x| _mro(x).unwrap()).collect();
let mro = linearise_mro(mros).unwrap();
Ok(PyObject::new(
PyObjectKind::Class {
name: String::from(name),
dict: dict,
mro: mro,
},
typ,
))
}
fn type_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(obj, Some(vm.ctx.type_type()))]);
let type_name = get_type_name(&obj);
Ok(vm.new_str(format!("<class '{}'>", type_name)))
}
pub fn call(vm: &mut VirtualMachine, typ: PyObjectRef, args: PyFuncArgs) -> PyResult {
let function = get_attribute(vm, typ, &String::from("__call__"))?;
vm.invoke(function, args)
}
#[cfg(test)]
mod tests {
use super::{linearise_mro, new};
use super::{IdProtocol, PyContext, PyObjectRef};
fn map_ids(obj: Option<Vec<PyObjectRef>>) -> Option<Vec<usize>> {
match obj {
Some(vec) => Some(vec.into_iter().map(|x| x.get_id()).collect()),
None => None,
}
}
#[test]
fn test_linearise() {
let context = PyContext::new();
let object = context.object;
let type_type = context.type_type;
let a = new(
type_type.clone(),
"A",
vec![object.clone()],
type_type.clone(),
).unwrap();
let b = new(
type_type.clone(),
"B",
vec![object.clone()],
type_type.clone(),
).unwrap();
assert_eq!(
map_ids(linearise_mro(vec![
vec![object.clone()],
vec![object.clone()]
])),
map_ids(Some(vec![object.clone()]))
);
assert_eq!(
map_ids(linearise_mro(vec![
vec![a.clone(), object.clone()],
vec![b.clone(), object.clone()],
])),
map_ids(Some(vec![a.clone(), b.clone(), object.clone()]))
);
}
}