diff --git a/parser/src/ast.rs b/parser/src/ast.rs index b4757271e..591f0fa7b 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -72,6 +72,7 @@ pub enum Statement { }, ClassDef { name: String, + body: Vec, // TODO: docstring: String, }, FunctionDef { diff --git a/parser/src/parser.rs b/parser/src/parser.rs index 68e45717e..39dc0919a 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -190,4 +190,22 @@ mod tests { }) ) } + + #[test] + fn test_parse_class() { + let source = String::from("class Foo:\n def __init__(self):\n pass\n"); + assert_eq!( + parse_statement(&source), + Ok(ast::Statement::ClassDef { + name: String::from("Foo"), + body: vec![ + ast::Statement::FunctionDef { + name: String::from("__init__"), + args: vec![String::from("self")], + body: vec![ast::Statement::Pass], + } + ], + }) + ) + } } diff --git a/parser/src/python.lalrpop b/parser/src/python.lalrpop index fd4d7bdc5..f1602bdf1 100644 --- a/parser/src/python.lalrpop +++ b/parser/src/python.lalrpop @@ -178,7 +178,9 @@ TypedArgsList: Vec = { }; ClassDef: ast::Statement = { - "class" <_a:("(" ")")?> ":" <_s:Suite> => ast::Statement::ClassDef { name: n }, + "class" <_a:("(" ")")?> ":" => ast::Statement::ClassDef { + name: n, + body: s}, }; Test: ast::Expression = { diff --git a/tests/snippets/class.py b/tests/snippets/class.py new file mode 100644 index 000000000..439edcd8f --- /dev/null +++ b/tests/snippets/class.py @@ -0,0 +1,14 @@ +class Foo: + print("Defining class") + def __init__(self): + print("initing: ", self) + self.x = 5 + + 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) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index d509d3d68..3d6a6b44e 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -269,3 +269,15 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { obj } +pub fn builtin_build_class_(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let function = args.args[0].clone(); + let a1 = &*args.args[1].borrow(); + let name = match &a1.kind { + PyObjectKind::String { value: name } => name, + _ => panic!("Class name must be a string."), + }; + + let new_dict = vm.new_dict(); + &vm.invoke(function, PyFuncArgs { args: vec![ new_dict.clone() ] }); + Ok(vm.new_class(name.to_string(), new_dict)) +} diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index 906e29e26..0d9486d0a 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -38,6 +38,7 @@ pub enum Instruction { LoadName { name: String }, StoreName { name: String }, StoreSubscript, + StoreAttr { name: String }, LoadConst { value: Constant }, UnaryOperation { op: UnaryOperator }, BinaryOperation { op: BinaryOperator }, @@ -64,6 +65,8 @@ pub enum Instruction { BuildMap { size: usize }, BuildSlice { size: usize }, PrintExpr, + LoadBuildClass, + StoreLocals, } #[derive(Debug, Clone)] diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 9162f9fab..285687b62 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -229,8 +229,41 @@ impl Compiler { name: name.to_string(), }); } - ast::Statement::ClassDef { name } => { - // TODO? + ast::Statement::ClassDef { name, body } => { + self.emit(Instruction::LoadBuildClass); + self.code_object_stack.push( + CodeObject::new(vec![String::from("__locals__")])); + self.emit(Instruction::LoadName { + name: String::from("__locals__")}); + self.emit(Instruction::StoreLocals); + self.compile_statements(body); + self.emit(Instruction::LoadConst { + value: bytecode::Constant::None, + }); + self.emit(Instruction::ReturnValue); + + let code = self.code_object_stack.pop().unwrap(); + self.emit(Instruction::LoadConst { + value: bytecode::Constant::Code { code: code }, + }); + self.emit(Instruction::LoadConst { + value: bytecode::Constant::String { + value: name.clone(), + }, + }); + // Turn code object into function object: + self.emit(Instruction::MakeFunction); + + self.emit(Instruction::LoadConst { + value: bytecode::Constant::String { + value: name.clone(), + }, + }); + self.emit(Instruction::CallFunction { count: 2 }); + + self.emit(Instruction::StoreName { + name: name.to_string(), + }); } ast::Statement::Assert { test, msg } => { // TODO: if some flag, ignore all assert statements! @@ -318,8 +351,14 @@ impl Compiler { self.compile_expression(b); self.emit(Instruction::StoreSubscript); } + ast::Expression::Attribute { value, name } => { + self.compile_expression(value); + self.emit(Instruction::StoreAttr { + name: name.to_string() + }); + } _ => { - panic!("WTF"); + panic!("WTF: {:?}", target); } } } diff --git a/vm/src/objint.rs b/vm/src/objint.rs index d15f0fe65..c818ad74e 100644 --- a/vm/src/objint.rs +++ b/vm/src/objint.rs @@ -26,7 +26,7 @@ pub fn create_type(type_type: PyObjectRef) -> PyObjectRef { let typ = PyObject::new( PyObjectKind::Class { name: "int".to_string(), - // dict: PyObject::new(PyObjectKind::Dict { elements: dict }, type_type.clone() ), + dict: PyObject::new(PyObjectKind::Dict { elements: dict }, type_type.clone() ), }, type_type.clone(), ); diff --git a/vm/src/objtype.rs b/vm/src/objtype.rs index bf4792e6c..5193f1f18 100644 --- a/vm/src/objtype.rs +++ b/vm/src/objtype.rs @@ -1,5 +1,4 @@ -// use std::rc::Rc; -// use std::cell::RefCell; +use std::collections::HashMap; /* * The magical type type @@ -9,11 +8,14 @@ use super::pyobject::{PyObject, PyObjectKind, PyObjectRef}; pub fn create_type() -> PyObjectRef { let typ = PyObject::default().into_ref(); + let dict = PyObject::new( + PyObjectKind::Dict { + elements: HashMap::new(), + }, typ.clone()); (*typ.borrow_mut()).kind = PyObjectKind::Class { - name: "type".to_string(), - // dict: dict, - }; + name: String::from("type"), + dict: dict, + }; (*typ.borrow_mut()).typ = Some(typ.clone()); - // typ.borrow_mut().dict.insert("__str__".to_string(), PyObject::new(PyObjectKind::RustFunction { function: str })); typ } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 199a4b988..76d442d8c 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -141,8 +141,11 @@ impl PyContext { ) } - pub fn new_class(&self, name: String) -> PyObjectRef { - PyObject::new(PyObjectKind::Class { name: name }, self.type_type.clone()) + pub fn new_class(&self, name: String, namespace: PyObjectRef) -> PyObjectRef { + PyObject::new(PyObjectKind::Class { + name: name, + dict: namespace.clone() + }, self.type_type.clone()) } /* TODO: something like this? @@ -205,6 +208,30 @@ impl ParentProtocol for PyObjectRef { } } +pub trait AttributeProtocol { + fn get_attr(&self, attr_name: &String) -> PyObjectRef; + fn set_attr(&self, attr_name: &String, value: PyObjectRef); +} + +impl AttributeProtocol for PyObjectRef { + fn get_attr(&self, attr_name: &String) -> PyObjectRef { + match self.borrow().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), + 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), + ref kind => unimplemented!("load_attr unimplemented for: {:?}", kind), + }; + } +} + pub trait DictProtocol { fn contains_key(&self, k: &String) -> bool; fn get_item(&self, k: &String) -> PyObjectRef; @@ -312,7 +339,10 @@ pub enum PyObjectKind { None, Class { name: String, - // dict: PyObjectRef, + dict: PyObjectRef, + }, + Instance { + dict: PyObjectRef }, RustFunction { function: RustPyFunc, @@ -337,7 +367,8 @@ impl fmt::Debug for PyObjectKind { &PyObjectKind::Module { name: _, dict: _ } => write!(f, "module"), &PyObjectKind::Scope { scope: _ } => write!(f, "scope"), &PyObjectKind::None => write!(f, "None"), - &PyObjectKind::Class { name: _ } => write!(f, "class"), + &PyObjectKind::Class { name: _, dict: _ } => write!(f, "class"), + &PyObjectKind::Instance { dict: _ } => write!(f, "instance"), &PyObjectKind::RustFunction { function: _ } => write!(f, "rust function"), } } @@ -396,7 +427,10 @@ impl PyObject { .join(", ") ), PyObjectKind::None => String::from("None"), - PyObjectKind::Class { ref name } => format!("", name), + PyObjectKind::Class { ref name, dict: ref _dict } => + format!("", name), + PyObjectKind::Instance { dict: _ } => + format!(""), PyObjectKind::Code { code: _ } => format!(""), PyObjectKind::Function { code: _, scope: _ } => format!(""), PyObjectKind::RustFunction { function: _ } => format!(""), diff --git a/vm/src/vm.rs b/vm/src/vm.rs index f55a5f08b..fd4b9e100 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -14,11 +14,20 @@ use super::frame::{Block, Frame, copy_code}; use super::import::import; use super::objlist; use super::objstr; +use super::objtype; use super::pyobject::{ - DictProtocol, IdProtocol, ParentProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, - PyObjectRef, PyResult, + AttributeProtocol, + DictProtocol, + IdProtocol, + ParentProtocol, + PyContext, + PyFuncArgs, + PyObject, + PyObjectKind, + PyObjectRef, + PyResult, + Scope, }; - use super::sysmodule; // use objects::objects; @@ -57,10 +66,19 @@ impl VirtualMachine { self.ctx.new_dict() } + pub fn new_class(&self, name: String, namespace: PyObjectRef) -> PyObjectRef { + self.ctx.new_class(name, namespace) + } + pub fn new_exception(&self, msg: String) -> PyObjectRef { self.new_str(msg) } + pub fn new_scope(&mut self) -> PyObjectRef { + let parent_scope = self.current_frame().locals.clone(); + self.ctx.new_scope(Some(parent_scope)) + } + pub fn get_none(&self) -> PyObjectRef { // TODO self.ctx.new_bool(false) @@ -460,11 +478,16 @@ impl VirtualMachine { fn new_instance(&mut self, type_ref: PyObjectRef, args: PyFuncArgs) -> PyResult { // more or less __new__ operator - let obj = PyObject::new(PyObjectKind::None, type_ref.clone()); + 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).unwrap(); Ok(obj) } - fn invoke(&mut self, func_ref: PyObjectRef, args: PyFuncArgs) -> PyResult { + pub fn invoke(&mut self, func_ref: PyObjectRef, args: PyFuncArgs) -> PyResult { let f = func_ref.borrow(); match f.kind { @@ -478,7 +501,8 @@ impl VirtualMachine { let frame = Frame::new(code.clone(), scope); self.run_frame(frame) } - PyObjectKind::Class { name: _ } => self.new_instance(func_ref.clone(), args), + PyObjectKind::Class { name: _, dict: _ } => + self.new_instance(func_ref.clone(), args), ref kind => { unimplemented!("invoke unimplemented for: {:?}", kind); } @@ -498,15 +522,18 @@ impl VirtualMachine { fn load_attr(&mut self, attr_name: &String) -> Option { let parent = self.pop_value(); - // Lookup name in obj - let obj = match parent.borrow().kind { - PyObjectKind::Module { name: _, ref dict } => dict.get_item(attr_name), - ref kind => unimplemented!("load_attr unimplemented for: {:?}", kind), - }; + let obj = parent.get_attr(attr_name); self.push_value(obj); None } + fn store_attr(&mut self, attr_name: &String) -> Option { + let parent = self.pop_value(); + let value = self.pop_value(); + parent.set_attr(attr_name, value); + None + } + // Execute a single instruction: fn execute_instruction(&mut self) -> Option { let instruction = self.current_frame().fetch_instruction(); @@ -609,6 +636,7 @@ impl VirtualMachine { } bytecode::Instruction::BinaryOperation { ref op } => self.execute_binop(op), bytecode::Instruction::LoadAttr { ref name } => self.load_attr(name), + bytecode::Instruction::StoreAttr { ref name } => self.store_attr(name), bytecode::Instruction::UnaryOperation { ref op } => self.execute_unop(op), bytecode::Instruction::CompareOperation { ref op } => self.execute_compare(op), bytecode::Instruction::ReturnValue => { @@ -770,6 +798,28 @@ impl VirtualMachine { } None } + bytecode::Instruction::LoadBuildClass => { + let rustfunc = PyObject::new( + PyObjectKind::RustFunction { + function: builtins::builtin_build_class_ + }, + objtype::create_type() + ); + self.push_value(rustfunc); + None + } + bytecode::Instruction::StoreLocals => { + let locals = self.pop_value(); + let ref mut frame = self.current_frame(); + match frame.locals.borrow_mut().kind { + PyObjectKind::Scope { ref mut scope } => { + scope.locals = locals; + } + _ => + panic!("We really expect our scope to be a scope!") + } + None + }, _ => panic!("NOT IMPL {:?}", instruction), } }