diff --git a/tests/snippets/class.py b/tests/snippets/class.py index 439edcd8f..dfc622abf 100644 --- a/tests/snippets/class.py +++ b/tests/snippets/class.py @@ -1,14 +1,14 @@ class Foo: - print("Defining class") - def __init__(self): - print("initing: ", self) - self.x = 5 + def __init__(self, x): + self.x = x + + def square(self): + return self.x * self.x y = 7 -print("Done defining: ", Foo) -print("Init: ", Foo.__init__) -print("y = ", Foo.y) -foo = Foo() -print("Done initting: ", foo) -print("Foo's x: ", foo.x) +foo = Foo(5) + +assert foo.y == Foo.y +assert foo.x == 5 +assert foo.square() == 25 diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 7625802e1..a0b396482 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -12,7 +12,9 @@ pub mod eval; mod frame; mod import; mod objbool; +mod objclass; mod objdict; +mod objfunction; mod objint; mod objlist; mod objsequence; diff --git a/vm/src/objclass.rs b/vm/src/objclass.rs new file mode 100644 index 000000000..b2fac3a58 --- /dev/null +++ b/vm/src/objclass.rs @@ -0,0 +1,32 @@ +use super::pyobject::AttributeProtocol; +use super::pyobject::{PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; +use super::vm::VirtualMachine; + +pub fn get_attribute( + vm: &mut VirtualMachine, + cls: PyObjectRef, + obj: PyObjectRef, + name: &String, +) -> PyResult { + if obj.has_attr(name) { + Ok(obj.get_attr(name)) + } else if cls.has_attr(name) { + let attr = cls.get_attr(name); + let attr_class = attr.typ(); + if attr_class.has_attr(&String::from("__get__")) { + vm.invoke( + attr_class.get_attr(&String::from("__get__")), + PyFuncArgs { + args: vec![attr, obj, cls], + }, + ) + } else { + Ok(attr) + } + } else { + Err(vm.new_exception(format!( + "AttributeError: {:?} object has no attribute {}", + cls, name + ))) + } +} diff --git a/vm/src/objfunction.rs b/vm/src/objfunction.rs new file mode 100644 index 000000000..540773c37 --- /dev/null +++ b/vm/src/objfunction.rs @@ -0,0 +1,40 @@ +use super::pyobject::{PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult}; +use super::vm::VirtualMachine; +use std::collections::HashMap; + +pub fn create_type(type_type: PyObjectRef) -> PyObjectRef { + let mut dict = HashMap::new(); + dict.insert( + "__get__".to_string(), + PyObject::new( + PyObjectKind::RustFunction { + function: bind_method, + }, + type_type.clone(), + ), + ); + let typ = PyObject::new( + PyObjectKind::Class { + name: "function".to_string(), + dict: PyObject::new(PyObjectKind::Dict { elements: dict }, type_type.clone()), + }, + type_type.clone(), + ); + typ +} + +pub fn create_bound_method_type(type_type: PyObjectRef) -> PyObjectRef { + let dict = HashMap::new(); + let typ = PyObject::new( + PyObjectKind::Class { + name: "method".to_string(), + dict: PyObject::new(PyObjectKind::Dict { elements: dict }, type_type.clone()), + }, + type_type.clone(), + ); + typ +} + +fn bind_method(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + Ok(vm.new_bound_method(args.args[0].clone(), args.args[1].clone())) +} diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 892f10a39..a979284ce 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1,4 +1,5 @@ use super::bytecode; +use super::objfunction; use super::objint; use super::objtype; use super::vm::VirtualMachine; @@ -45,6 +46,8 @@ pub struct PyContext { pub list_type: PyObjectRef, pub tuple_type: PyObjectRef, pub dict_type: PyObjectRef, + pub function_type: PyObjectRef, + pub bound_method_type: PyObjectRef, } /* @@ -61,18 +64,15 @@ pub struct Scope { impl PyContext { pub fn new() -> PyContext { let type_type = objtype::create_type(); - let int_type = objint::create_type(type_type.clone()); - // TODO: How to represent builtin types? - let list_type = type_type.clone(); - let tuple_type = type_type.clone(); - let dict_type = type_type.clone(); - // let str_type = objstr::make_type(); + PyContext { + int_type: objint::create_type(type_type.clone()), + list_type: type_type.clone(), + tuple_type: type_type.clone(), + dict_type: type_type.clone(), + function_type: objfunction::create_type(type_type.clone()), + bound_method_type: objfunction::create_bound_method_type(type_type.clone()), type_type: type_type, - int_type: int_type, - list_type: list_type, - tuple_type: tuple_type, - dict_type: dict_type, } } @@ -154,6 +154,26 @@ impl PyContext { ) } + pub fn new_function(&self, code_obj: PyObjectRef, scope: PyObjectRef) -> PyObjectRef { + PyObject::new( + PyObjectKind::Function { + code: code_obj, + scope: scope, + }, + self.function_type.clone(), + ) + } + + pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef { + PyObject::new( + PyObjectKind::BoundMethod { + function: function, + object: object, + }, + self.bound_method_type.clone(), + ) + } + /* TODO: something like this? pub fn new_instance(&self, name: String) -> PyObjectRef { PyObject::new(PyObjectKind::Class { name: name }, self.type_type.clone()) @@ -187,6 +207,19 @@ impl IdProtocol for PyObjectRef { } } +pub trait TypeProtocol { + fn typ(&self) -> PyObjectRef; +} + +impl TypeProtocol for PyObjectRef { + fn typ(&self) -> PyObjectRef { + match self.borrow().typ { + Some(ref typ) => typ.clone(), + None => panic!("Object doesn't have a type!"), + } + } +} + pub trait ParentProtocol { fn has_parent(&self) -> bool; fn get_parent(&self) -> PyObjectRef; @@ -217,11 +250,13 @@ impl ParentProtocol for PyObjectRef { pub trait AttributeProtocol { fn get_attr(&self, attr_name: &String) -> PyObjectRef; fn set_attr(&self, attr_name: &String, value: PyObjectRef); + fn has_attr(&self, attr_name: &String) -> bool; } impl AttributeProtocol for PyObjectRef { fn get_attr(&self, attr_name: &String) -> PyObjectRef { - match self.borrow().kind { + let obj = self.borrow(); + match obj.kind { PyObjectKind::Module { name: _, ref dict } => dict.get_item(attr_name), PyObjectKind::Class { name: _, ref dict } => dict.get_item(attr_name), PyObjectKind::Instance { ref dict } => dict.get_item(attr_name), @@ -229,6 +264,16 @@ impl AttributeProtocol for PyObjectRef { } } + fn has_attr(&self, attr_name: &String) -> bool { + let obj = self.borrow(); + match obj.kind { + PyObjectKind::Module { name: _, ref dict } => dict.contains_key(attr_name), + PyObjectKind::Class { name: _, ref dict } => dict.contains_key(attr_name), + PyObjectKind::Instance { ref dict } => dict.contains_key(attr_name), + ref kind => unimplemented!("load_attr unimplemented for: {:?}", kind), + } + } + fn set_attr(&self, attr_name: &String, value: PyObjectRef) { match self.borrow_mut().kind { PyObjectKind::Instance { ref mut dict } => dict.set_item(attr_name, value), @@ -337,6 +382,10 @@ pub enum PyObjectKind { code: PyObjectRef, scope: PyObjectRef, }, + BoundMethod { + function: PyObjectRef, + object: PyObjectRef, + }, Scope { scope: Scope, }, @@ -379,6 +428,10 @@ impl fmt::Debug for PyObjectKind { &PyObjectKind::NameError { name: _ } => write!(f, "NameError"), &PyObjectKind::Code { ref code } => write!(f, "code: {:?}", code), &PyObjectKind::Function { code: _, scope: _ } => write!(f, "function"), + &PyObjectKind::BoundMethod { + function: _, + object: _, + } => write!(f, "bound-method"), &PyObjectKind::Module { name: _, dict: _ } => write!(f, "module"), &PyObjectKind::Scope { scope: _ } => write!(f, "scope"), &PyObjectKind::None => write!(f, "None"), @@ -449,6 +502,7 @@ impl PyObject { PyObjectKind::Instance { dict: _ } => format!(""), PyObjectKind::Code { code: _ } => format!(""), PyObjectKind::Function { code: _, scope: _ } => format!(""), + PyObjectKind::BoundMethod { .. } => format!(""), PyObjectKind::RustFunction { function: _ } => format!(""), PyObjectKind::Module { ref name, dict: _ } => format!("", name), PyObjectKind::Scope { ref scope } => format!("", scope), diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 8891c9b87..c67534594 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -12,12 +12,13 @@ use super::builtins; use super::bytecode; use super::frame::{copy_code, Block, Frame}; use super::import::import; +use super::objclass; use super::objlist; use super::objstr; use super::objtype; use super::pyobject::{ AttributeProtocol, DictProtocol, IdProtocol, ParentProtocol, PyContext, PyFuncArgs, PyObject, - PyObjectKind, PyObjectRef, PyResult, + PyObjectKind, PyObjectRef, PyResult, TypeProtocol, }; use super::sysmodule; @@ -68,6 +69,10 @@ impl VirtualMachine { self.ctx.new_bool(false) } + pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef { + self.ctx.new_bound_method(function, object) + } + pub fn get_type(&self) -> PyObjectRef { self.ctx.type_type.clone() } @@ -464,10 +469,8 @@ impl VirtualMachine { // more or less __new__ operator let dict = self.new_dict(); let obj = PyObject::new(PyObjectKind::Instance { dict: dict }, type_ref.clone()); - let init = type_ref.get_attr(&String::from("__init__")); - let mut self_args = PyFuncArgs { args: args.args }; - self_args.args.insert(0, obj.clone()); - self.invoke(init, self_args)?; + let init = objclass::get_attribute(self, type_ref, obj.clone(), &String::from("__init__"))?; + self.invoke(init, args)?; // TODO Raise TypeError if init returns not None. Ok(obj) } @@ -490,6 +493,16 @@ impl VirtualMachine { self.run_frame(frame) } PyObjectKind::Class { name: _, dict: _ } => self.new_instance(func_ref.clone(), args), + PyObjectKind::BoundMethod { + ref function, + ref object, + } => { + let mut self_args = PyFuncArgs { + args: args.args.clone(), + }; + self_args.args.insert(0, object.clone()); + self.invoke(function.clone(), self_args) + } ref kind => { unimplemented!("invoke unimplemented for: {:?}", kind); } @@ -507,11 +520,26 @@ impl VirtualMachine { None } + fn get_attribute(&mut self, obj: PyObjectRef, attr_name: &String) -> PyResult { + let typ = obj.typ(); + let typ_ref = typ.borrow(); + match typ_ref.kind { + PyObjectKind::Class { .. } => { + objclass::get_attribute(self, typ.clone(), obj.clone(), attr_name) + } + _ => panic!("It's not a class: {:?}", typ), + } + } + fn load_attr(&mut self, attr_name: &String) -> Option { let parent = self.pop_value(); - let obj = parent.get_attr(attr_name); - self.push_value(obj); - None + match self.get_attribute(parent, attr_name) { + Ok(obj) => { + self.push_value(obj); + None + } + Err(err) => Some(Err(err)), + } } fn store_attr(&mut self, attr_name: &String) -> Option { @@ -692,13 +720,7 @@ impl VirtualMachine { // pop argc arguments // argument: name, args, globals let scope = self.current_frame().locals.clone(); - let obj = PyObject::new( - PyObjectKind::Function { - code: code_obj, - scope: scope, - }, - self.get_type(), - ); + let obj = self.ctx.new_function(code_obj, scope); self.push_value(obj); None }