Merge pull request #52 from cthulahoops/classes

Classes
This commit is contained in:
Daniel Watkins
2018-08-09 10:06:46 -04:00
committed by GitHub
11 changed files with 202 additions and 27 deletions

View File

@@ -72,6 +72,7 @@ pub enum Statement {
},
ClassDef {
name: String,
body: Vec<Statement>,
// TODO: docstring: String,
},
FunctionDef {

View File

@@ -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],
}
],
})
)
}
}

View File

@@ -178,7 +178,9 @@ TypedArgsList: Vec<String> = {
};
ClassDef: ast::Statement = {
"class" <n:Identifier> <_a:("(" ")")?> ":" <_s:Suite> => ast::Statement::ClassDef { name: n },
"class" <n:Identifier> <_a:("(" ")")?> ":" <s:Suite> => ast::Statement::ClassDef {
name: n,
body: s},
};
Test: ast::Expression = {

14
tests/snippets/class.py Normal file
View File

@@ -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)

View File

@@ -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))
}

View File

@@ -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)]

View File

@@ -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);
}
}
}

View File

@@ -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(),
);

View File

@@ -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
}

View File

@@ -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!("<class '{}'>", name),
PyObjectKind::Class { ref name, dict: ref _dict } =>
format!("<class '{}'>", name),
PyObjectKind::Instance { dict: _ } =>
format!("<instance>"),
PyObjectKind::Code { code: _ } => format!("<code>"),
PyObjectKind::Function { code: _, scope: _ } => format!("<func>"),
PyObjectKind::RustFunction { function: _ } => format!("<rustfunc>"),

View File

@@ -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<PyResult> {
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<PyResult> {
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<PyResult> {
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),
}
}