Add getattr and hasattr builtins

This commit is contained in:
Windel Bouwman
2018-08-14 23:39:47 +02:00
parent c9816c37de
commit 6444b50a44
3 changed files with 97 additions and 14 deletions

View File

@@ -6,7 +6,7 @@ use super::compile;
use super::objbool;
use super::pyobject::DictProtocol;
use super::pyobject::{
IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, Scope,
AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult, Scope,
};
use super::vm::VirtualMachine;
@@ -129,9 +129,39 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
// builtin_float
// builtin_format
// builtin_frozenset
// builtin_getattr
fn builtin_getattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
let args = args.args;
if args.len() == 2 {
let obj = args[0].clone();
let attr = args[1].borrow();
if let PyObjectKind::String { ref value } = attr.kind {
Ok(obj.get_attr(value))
} else {
Err(vm.new_exception("Attr can only be str for now".to_string()))
}
} else {
Err(vm.new_exception("Expected 2 arguments".to_string()))
}
}
// builtin_globals
// builtin_hasattr
fn builtin_hasattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
let args = args.args;
if args.len() == 2 {
let obj = args[0].clone();
let attr = args[1].borrow();
if let PyObjectKind::String { ref value } = attr.kind {
Ok(vm.context().new_bool(obj.has_attr(value)))
} else {
Err(vm.new_exception("Attr can only be str for now".to_string()))
}
} else {
Err(vm.new_exception("Expected 2 arguments".to_string()))
}
}
// builtin_hash
// builtin_help
// builtin_hex
@@ -154,18 +184,23 @@ fn builtin_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
if args.args.len() != 1 {
panic!("len(s) expects exactly one parameter");
}
let len = match args.args[0].borrow().kind {
PyObjectKind::Dict { ref elements } => elements.len(),
PyObjectKind::List { ref elements } => elements.len(),
PyObjectKind::Tuple { ref elements } => elements.len(),
PyObjectKind::String { ref value } => value.len(),
match args.args[0].borrow().kind {
PyObjectKind::Dict { ref elements } => Ok(vm.context().new_int(elements.len() as i32)),
PyObjectKind::List { ref elements } => Ok(vm.context().new_int(elements.len() as i32)),
PyObjectKind::Tuple { ref elements } => Ok(vm.context().new_int(elements.len() as i32)),
PyObjectKind::String { ref value } => Ok(vm.context().new_int(value.len() as i32)),
_ => {
return Err(vm
.context()
.new_str("TypeError: object of this type has no len()".to_string()))
let len_method_name = "__len__".to_string();
if args.args[0].has_attr(&len_method_name) {
let len_method = args.args[0].get_attr(&len_method_name);
vm.invoke(len_method, PyFuncArgs::default())
} else {
return Err(vm
.context()
.new_str(format!("TypeError: object of this {:?} type has no method {:?}", args.args[0], len_method_name).to_string()))
}
}
};
Ok(vm.context().new_int(len as i32))
}
}
// builtin_list
@@ -260,6 +295,8 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
dict.insert(String::from("dict"), ctx.new_rustfunc(builtin_dict));
dict.insert(String::from("dir"), ctx.new_rustfunc(builtin_dir));
dict.insert(String::from("eval"), ctx.new_rustfunc(builtin_eval));
dict.insert(String::from("getattr"), ctx.new_rustfunc(builtin_getattr));
dict.insert(String::from("hasattr"), ctx.new_rustfunc(builtin_hasattr));
dict.insert(String::from("id"), ctx.new_rustfunc(builtin_id));
dict.insert(String::from("int"), ctx.int_type.clone());
dict.insert(String::from("len"), ctx.new_rustfunc(builtin_len));

View File

@@ -40,6 +40,38 @@ fn append(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
}
fn clear(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
trace!("list.clear called with: {:?}", args);
if args.args.len() == 1 {
let l = args.args[0].clone();
let mut list_obj = l.borrow_mut();
if let PyObjectKind::List { ref mut elements } = list_obj.kind {
elements.clear();
Ok(vm.get_none())
} else {
Err(vm.new_exception("list.clear is called with no list".to_string()))
}
} else {
Err(vm.new_exception("list.clear requires one arguments".to_string()))
}
}
fn len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
trace!("list.len called with: {:?}", args);
// TODO: for this argument amount checking we could probably write some nice macro or templated function!
if args.args.len() == 1 {
let l = args.args[0].clone();
let list_obj = l.borrow();
if let PyObjectKind::List { ref elements } = list_obj.kind {
Ok(vm.context().new_int(elements.len() as i32))
} else {
Err(vm.new_exception("list.len is called with no list".to_string()))
}
} else {
Err(vm.new_exception("list.len requires one arguments".to_string()))
}
}
fn reverse(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
trace!("list.reverse called with: {:?}", args);
if args.args.len() == 1 {
@@ -58,6 +90,13 @@ fn reverse(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
pub fn create_type(type_type: PyObjectRef, method_type: PyObjectRef) -> PyObjectRef {
let mut dict = HashMap::new();
dict.insert(
"__len__".to_string(),
PyObject::new(
PyObjectKind::RustFunction { function: len },
method_type.clone(),
),
);
dict.insert(
"append".to_string(),
PyObject::new(
@@ -65,6 +104,13 @@ pub fn create_type(type_type: PyObjectRef, method_type: PyObjectRef) -> PyObject
method_type.clone(),
),
);
dict.insert(
"clear".to_string(),
PyObject::new(
PyObjectKind::RustFunction { function: clear },
method_type.clone(),
),
);
dict.insert(
"reverse".to_string(),
PyObject::new(

View File

@@ -327,7 +327,7 @@ impl fmt::Debug for PyObject {
}
}
#[derive(Debug)]
#[derive(Debug, Default)]
pub struct PyFuncArgs {
pub args: Vec<PyObjectRef>,
// TODO: add kwargs here