From cb3bd23cb1d79c5e9f1aabab9e7b76c45d20ef59 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sun, 12 Aug 2018 09:43:35 +0100 Subject: [PATCH 1/6] Add modules for class and function objects. --- vm/src/lib.rs | 2 ++ vm/src/objclass.rs | 29 +++++++++++++++++++++++++++++ vm/src/objfunction.rs | 28 ++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 vm/src/objclass.rs create mode 100644 vm/src/objfunction.rs 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..28f48874c --- /dev/null +++ b/vm/src/objclass.rs @@ -0,0 +1,29 @@ +use super::pyobject::AttributeProtocol; +use super::pyobject::{PyFuncArgs, PyObjectKind, 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, cls], + }, + ) + } else { + Ok(attr) + } + } else { + Err(vm.new_exception(String::from("TypeError goes here!"))) + } +} diff --git a/vm/src/objfunction.rs b/vm/src/objfunction.rs new file mode 100644 index 000000000..61d1746c4 --- /dev/null +++ b/vm/src/objfunction.rs @@ -0,0 +1,28 @@ +use super::pyobject::{PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol}; +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 +} + +fn bind_method(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + unimplemented!("not yet!"); +} From 43011d3a9c7e313303e464ef55a19029ee56e1fc Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sun, 12 Aug 2018 09:45:04 +0100 Subject: [PATCH 2/6] Create functions with type of function class. --- vm/src/pyobject.rs | 28 ++++++++++++++++++---------- vm/src/vm.rs | 8 +------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 892f10a39..ee0c147bb 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,7 @@ pub struct PyContext { pub list_type: PyObjectRef, pub tuple_type: PyObjectRef, pub dict_type: PyObjectRef, + pub function_type: PyObjectRef, } /* @@ -61,18 +63,14 @@ 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()), type_type: type_type, - int_type: int_type, - list_type: list_type, - tuple_type: tuple_type, - dict_type: dict_type, } } @@ -154,6 +152,16 @@ 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(), + ) + } + /* TODO: something like this? pub fn new_instance(&self, name: String) -> PyObjectRef { PyObject::new(PyObjectKind::Class { name: name }, self.type_type.clone()) diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 8891c9b87..82eb68af7 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -692,13 +692,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 } From 6889b3451524c1023817fc1ff92b99eb3c04a56f Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sun, 12 Aug 2018 09:46:48 +0100 Subject: [PATCH 3/6] Add Type Protocol. --- vm/src/pyobject.rs | 13 +++++++++++++ vm/src/vm.rs | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index ee0c147bb..b9c98c71f 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -195,6 +195,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; diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 82eb68af7..a476b84f4 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -17,7 +17,7 @@ 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; From 0597cff6c687749e4e0f2e4df1ab64dfd2f9f18e Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sun, 12 Aug 2018 09:47:21 +0100 Subject: [PATCH 4/6] Add has_attr to AttributeProtocol. --- vm/src/pyobject.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index b9c98c71f..1b3cee87f 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -238,11 +238,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), @@ -250,6 +252,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), From 4b52f72316cf29ead3709192ef637a9a74f8f7a8 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sun, 12 Aug 2018 09:48:32 +0100 Subject: [PATCH 5/6] load_attr to use class based attribute getting. --- tests/snippets/class.py | 20 ++++++++++---------- vm/src/objclass.rs | 9 ++++++--- vm/src/objfunction.rs | 18 +++++++++++++++--- vm/src/pyobject.rs | 21 +++++++++++++++++++++ vm/src/vm.rs | 36 +++++++++++++++++++++++++++++++++--- 5 files changed, 85 insertions(+), 19 deletions(-) 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/objclass.rs b/vm/src/objclass.rs index 28f48874c..b2fac3a58 100644 --- a/vm/src/objclass.rs +++ b/vm/src/objclass.rs @@ -1,5 +1,5 @@ use super::pyobject::AttributeProtocol; -use super::pyobject::{PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol}; +use super::pyobject::{PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use super::vm::VirtualMachine; pub fn get_attribute( @@ -17,13 +17,16 @@ pub fn get_attribute( vm.invoke( attr_class.get_attr(&String::from("__get__")), PyFuncArgs { - args: vec![attr, cls], + args: vec![attr, obj, cls], }, ) } else { Ok(attr) } } else { - Err(vm.new_exception(String::from("TypeError goes here!"))) + Err(vm.new_exception(format!( + "AttributeError: {:?} object has no attribute {}", + cls, name + ))) } } diff --git a/vm/src/objfunction.rs b/vm/src/objfunction.rs index 61d1746c4..540773c37 100644 --- a/vm/src/objfunction.rs +++ b/vm/src/objfunction.rs @@ -1,4 +1,4 @@ -use super::pyobject::{PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol}; +use super::pyobject::{PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult}; use super::vm::VirtualMachine; use std::collections::HashMap; @@ -23,6 +23,18 @@ pub fn create_type(type_type: PyObjectRef) -> PyObjectRef { typ } -fn bind_method(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - unimplemented!("not yet!"); +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 1b3cee87f..a979284ce 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -47,6 +47,7 @@ pub struct PyContext { pub tuple_type: PyObjectRef, pub dict_type: PyObjectRef, pub function_type: PyObjectRef, + pub bound_method_type: PyObjectRef, } /* @@ -70,6 +71,7 @@ impl PyContext { 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, } } @@ -162,6 +164,16 @@ impl PyContext { ) } + 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()) @@ -370,6 +382,10 @@ pub enum PyObjectKind { code: PyObjectRef, scope: PyObjectRef, }, + BoundMethod { + function: PyObjectRef, + object: PyObjectRef, + }, Scope { scope: Scope, }, @@ -412,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"), @@ -482,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 a476b84f4..80d67b722 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -12,6 +12,7 @@ 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; @@ -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() } @@ -490,6 +495,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 +522,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 { From 3a6649ddebd9df9b28f72f0f3d4e3cbf191d9ee9 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sun, 12 Aug 2018 18:26:04 +0100 Subject: [PATCH 6/6] Use standard method machinary to call __init__. --- vm/src/vm.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 80d67b722..c67534594 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -469,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) }