Merge branch 'master' into equality

This commit is contained in:
Adam Kelly
2018-09-01 20:46:22 +01:00
16 changed files with 229 additions and 70 deletions

View File

@@ -25,3 +25,11 @@ assert type(2 / 3) is float
x = 1
assert type(x) is int
assert type(x - 1) is int
a = bytes([1, 2, 3])
print(a)
try:
bytes([object()])
except TypeError:
pass

View File

@@ -322,6 +322,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
dict.insert(String::from("all"), ctx.new_rustfunc(builtin_all));
dict.insert(String::from("any"), ctx.new_rustfunc(builtin_any));
dict.insert(String::from("bool"), ctx.bool_type());
dict.insert(String::from("bytes"), ctx.bytes_type());
dict.insert(String::from("chr"), ctx.new_rustfunc(builtin_chr));
dict.insert(String::from("compile"), ctx.new_rustfunc(builtin_compile));
dict.insert(String::from("dict"), ctx.dict_type());

View File

@@ -22,9 +22,6 @@ mod frame;
mod import;
mod obj;
mod objbool;
mod objfunction;
mod objobject;
mod objsequence;
pub mod pyobject;
pub mod stdlib;
mod sysmodule;

View File

@@ -1,7 +1,11 @@
pub mod objbytes;
pub mod objdict;
pub mod objfloat;
pub mod objfunction;
pub mod objint;
pub mod objlist;
pub mod objobject;
pub mod objsequence;
pub mod objstr;
pub mod objtuple;
pub mod objtype;

59
vm/src/obj/objbytes.rs Normal file
View File

@@ -0,0 +1,59 @@
use super::super::pyobject::{
AttributeProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol,
};
use super::super::vm::VirtualMachine;
use super::objint;
use super::objlist;
use super::objtype;
// Binary data support
// Fill bytes class methods:
pub fn init(context: &PyContext) {
let ref bytes_type = context.bytes_type;
bytes_type.set_attr("__init__", context.new_rustfunc(bytes_init));
bytes_type.set_attr("__str__", context.new_rustfunc(bytes_str));
}
// __init__ (store value into objectkind)
fn bytes_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(zelf, Some(vm.ctx.bytes_type())), (arg, None)]
);
let val = if objtype::isinstance(arg.clone(), vm.ctx.list_type()) {
let mut data_bytes = vec![];
for elem in objlist::get_elements(arg) {
let v = match objint::to_int(vm, &elem) {
Ok(int_ref) => int_ref,
Err(err) => return Err(err),
};
data_bytes.push(v as u8);
}
data_bytes
} else {
return Err(vm.new_type_error("Cannot construct bytes".to_string()));
};
set_value(zelf, val);
Ok(vm.get_none())
}
pub fn get_value(obj: &PyObjectRef) -> Vec<u8> {
if let PyObjectKind::Bytes { value } = &obj.borrow().kind {
value.clone()
} else {
panic!("Inner error getting int {:?}", obj);
}
}
fn set_value(obj: &PyObjectRef, value: Vec<u8>) {
obj.borrow_mut().kind = PyObjectKind::Bytes { value };
}
fn bytes_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(obj, Some(vm.ctx.bytes_type()))]);
let data = get_value(obj);
let data: Vec<String> = data.into_iter().map(|b| format!("\\x{:02x}", b)).collect();
let data = data.join("");
Ok(vm.new_str(format!("b'{}'", data)))
}

View File

@@ -1,5 +1,5 @@
use super::pyobject::{AttributeProtocol, PyContext, PyFuncArgs, PyResult};
use super::vm::VirtualMachine;
use super::super::pyobject::{AttributeProtocol, PyContext, PyFuncArgs, PyResult};
use super::super::vm::VirtualMachine;
pub fn init(context: &PyContext) {
let ref function_type = context.function_type;

View File

@@ -19,15 +19,27 @@ fn int_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
args,
required = [(zelf, Some(vm.ctx.int_type())), (arg, None)]
);
let val = if objtype::isinstance(arg.clone(), vm.ctx.int_type()) {
get_value(arg)
} else if objtype::isinstance(arg.clone(), vm.ctx.float_type()) {
objfloat::get_value(arg) as i32
// Try to cast to int:
let val = match to_int(vm, arg) {
Ok(val) => val,
Err(err) => return Err(err),
};
set_value(zelf, val);
Ok(vm.get_none())
}
// Casting function:
pub fn to_int(vm: &mut VirtualMachine, obj: &PyObjectRef) -> Result<i32, PyObjectRef> {
let val = if objtype::isinstance(obj.clone(), vm.ctx.int_type()) {
get_value(obj)
} else if objtype::isinstance(obj.clone(), vm.ctx.float_type()) {
objfloat::get_value(obj) as i32
} else {
return Err(vm.new_type_error("Cannot construct int".to_string()));
};
set_value(zelf, val);
Ok(vm.get_none())
Ok(val)
}
// Retrieve inner int value:
@@ -162,16 +174,64 @@ fn int_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
}
fn int_xor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(i, Some(vm.ctx.int_type())), (i2, None)]
);
let v1 = get_value(i);
if objtype::isinstance(i2.clone(), vm.ctx.int_type()) {
let v2 = get_value(i2);
Ok(vm.ctx.new_int(v1 ^ v2))
} else {
Err(vm.new_type_error(format!("Cannot xor {:?} and {:?}", i, i2)))
}
}
fn int_or(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(i, Some(vm.ctx.int_type())), (i2, None)]
);
let v1 = get_value(i);
if objtype::isinstance(i2.clone(), vm.ctx.int_type()) {
let v2 = get_value(i2);
Ok(vm.ctx.new_int(v1 | v2))
} else {
Err(vm.new_type_error(format!("Cannot or {:?} and {:?}", i, i2)))
}
}
fn int_and(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(i, Some(vm.ctx.int_type())), (i2, None)]
);
let v1 = get_value(i);
if objtype::isinstance(i2.clone(), vm.ctx.int_type()) {
let v2 = get_value(i2);
Ok(vm.ctx.new_int(v1 & v2))
} else {
Err(vm.new_type_error(format!("Cannot and {:?} and {:?}", i, i2)))
}
}
pub fn init(context: &PyContext) {
let ref int_type = context.int_type;
int_type.set_attr("__eq__", context.new_rustfunc(int_eq));
int_type.set_attr("__add__", context.new_rustfunc(int_add));
int_type.set_attr("__and__", context.new_rustfunc(int_and));
int_type.set_attr("__init__", context.new_rustfunc(int_init));
int_type.set_attr("__mod__", context.new_rustfunc(int_mod));
int_type.set_attr("__mul__", context.new_rustfunc(int_mul));
int_type.set_attr("__or__", context.new_rustfunc(int_or));
int_type.set_attr("__pow__", context.new_rustfunc(int_pow));
int_type.set_attr("__repr__", context.new_rustfunc(str));
int_type.set_attr("__str__", context.new_rustfunc(str));
int_type.set_attr("__sub__", context.new_rustfunc(int_sub));
int_type.set_attr("__truediv__", context.new_rustfunc(int_truediv));
int_type.set_attr("__xor__", context.new_rustfunc(int_xor));
}

View File

@@ -1,8 +1,8 @@
use super::super::objsequence::{seq_equal, PySliceableSequence};
use super::super::pyobject::{
AttributeProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol,
};
use super::super::vm::VirtualMachine;
use super::objsequence::{seq_equal, PySliceableSequence};
use super::objstr;
use super::objtype;
@@ -26,7 +26,7 @@ pub fn set_item(
}
}
pub fn get_elements(obj: PyObjectRef) -> Vec<PyObjectRef> {
pub fn get_elements(obj: &PyObjectRef) -> Vec<PyObjectRef> {
if let PyObjectKind::List { elements } = &obj.borrow().kind {
elements.to_vec()
} else {
@@ -42,8 +42,8 @@ fn list_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
);
let result = if objtype::isinstance(other.clone(), vm.ctx.list_type()) {
let zelf = get_elements(zelf.clone());
let other = get_elements(other.clone());
let zelf = get_elements(zelf);
let other = get_elements(other);
seq_equal(vm, zelf, other)?
} else {
false
@@ -59,8 +59,8 @@ fn list_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
);
if objtype::isinstance(o2.clone(), vm.ctx.list_type()) {
let e1 = get_elements(o.clone());
let e2 = get_elements(o2.clone());
let e1 = get_elements(o);
let e2 = get_elements(o2);
let elements = e1.iter().chain(e2.iter()).map(|e| e.clone()).collect();
Ok(vm.ctx.new_list(elements))
} else {
@@ -71,7 +71,7 @@ fn list_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
fn list_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(o, Some(vm.ctx.list_type()))]);
let elements = get_elements(o.clone());
let elements = get_elements(o);
let mut str_parts = vec![];
for elem in elements {
match vm.to_str(elem) {
@@ -115,12 +115,8 @@ fn clear(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
fn list_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
trace!("list.len called with: {:?}", args);
arg_check!(vm, args, required = [(list, Some(vm.ctx.list_type()))]);
let list_obj = list.borrow();
if let PyObjectKind::List { ref elements } = list_obj.kind {
Ok(vm.context().new_int(elements.len() as i32))
} else {
Err(vm.new_type_error("list.len is called with no list".to_string()))
}
let elements = get_elements(list);
Ok(vm.context().new_int(elements.len() as i32))
}
fn reverse(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {

View File

@@ -1,10 +1,10 @@
use super::obj::objdict;
use super::obj::objtype;
use super::pyobject::{
use super::super::pyobject::{
AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef,
PyResult, TypeProtocol,
};
use super::vm::VirtualMachine;
use super::super::vm::VirtualMachine;
use super::objdict;
use super::objtype;
pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
// more or less __new__ operator

View File

@@ -1,6 +1,6 @@
use super::objbool;
use super::pyobject::{PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol};
use super::vm::VirtualMachine;
use super::super::objbool;
use super::super::pyobject::{PyObject, PyObjectKind, PyObjectRef, PyResult, TypeProtocol};
use super::super::vm::VirtualMachine;
use std::marker::Sized;
pub trait PySliceableSequence {
@@ -100,14 +100,6 @@ pub fn get_item(
}
}
pub fn get_elements(obj: PyObjectRef) -> Vec<PyObjectRef> {
if let PyObjectKind::Tuple { elements } = &obj.borrow().kind {
elements.to_vec()
} else {
panic!("Cannot extract list elements from non-list");
}
}
pub fn seq_equal(
vm: &mut VirtualMachine,
zelf: Vec<PyObjectRef>,

View File

@@ -1,9 +1,9 @@
use super::super::objsequence::PySliceableSequence;
use super::super::pyobject::{
AttributeProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol,
};
use super::super::vm::VirtualMachine;
use super::objint;
use super::objsequence::PySliceableSequence;
use super::objtype;
pub fn init(context: &PyContext) {

View File

@@ -1,8 +1,8 @@
use super::super::objsequence::{get_elements, seq_equal};
use super::super::pyobject::{
AttributeProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol,
AttributeProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol,
};
use super::super::vm::VirtualMachine;
use super::objsequence::seq_equal;
use super::objstr;
use super::objtype;
@@ -14,8 +14,8 @@ fn tuple_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
);
let result = if objtype::isinstance(other.clone(), vm.ctx.tuple_type()) {
let zelf = get_elements(zelf.clone());
let other = get_elements(other.clone());
let zelf = get_elements(zelf);
let other = get_elements(other);
seq_equal(vm, zelf, other)?
} else {
false
@@ -24,21 +24,16 @@ fn tuple_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
fn tuple_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
trace!("tuple.len called with: {:?}", args);
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.tuple_type()))]);
let elements = get_elements(zelf.clone());
let elements = get_elements(zelf);
Ok(vm.context().new_int(elements.len() as i32))
}
fn tuple_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.tuple_type()))]);
let elements = get_elements(zelf.clone());
if elements.len() == 1 {
let ref part = vm.to_str(elements[0].clone())?;
let s = format!("({},)", objstr::get_value(part));
return Ok(vm.new_str(s));
}
let elements = get_elements(zelf);
let mut str_parts = vec![];
for elem in elements {
match vm.to_str(elem) {
@@ -47,10 +42,22 @@ fn tuple_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
}
let s = format!("({})", str_parts.join(", "));
let s = if str_parts.len() == 1 {
format!("({},)", str_parts[0])
} else {
format!("({})", str_parts.join(", "))
};
Ok(vm.new_str(s))
}
pub fn get_elements(obj: &PyObjectRef) -> Vec<PyObjectRef> {
if let PyObjectKind::Tuple { elements } = &obj.borrow().kind {
elements.to_vec()
} else {
panic!("Cannot extract elements from non-tuple");
}
}
pub fn init(context: &PyContext) {
let ref tuple_type = context.tuple_type;
tuple_type.set_attr("__eq__", context.new_rustfunc(tuple_eq));

View File

@@ -112,10 +112,22 @@ pub fn type_call(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
debug!("type_call: {:?}", args);
let typ = args.shift();
let new = typ.get_attr("__new__").unwrap();
let obj = vm.invoke(new, args.insert(typ.clone()))?;
let obj = match vm.invoke(new, args.insert(typ.clone())) {
Ok(res) => res,
Err(err) => return Err(err),
};
if let Some(init) = obj.typ().get_attr("__init__") {
let _ = vm.invoke(init, args.insert(obj.clone())).unwrap();
match vm.invoke(init, args.insert(obj.clone())) {
Ok(res) => {
// TODO: assert that return is none?
if !isinstance(res, vm.get_none()) {
// panic!("__init__ must return none");
// return Err(vm.new_type_error("__init__ must return None".to_string()));
}
}
Err(err) => return Err(err),
}
}
Ok(obj)
}

View File

@@ -1,15 +1,16 @@
use super::bytecode;
use super::exceptions;
use super::obj::objbytes;
use super::obj::objdict;
use super::obj::objfloat;
use super::obj::objfunction;
use super::obj::objint;
use super::obj::objlist;
use super::obj::objobject;
use super::obj::objstr;
use super::obj::objtuple;
use super::obj::objtype;
use super::objbool;
use super::objfunction;
use super::objobject;
use super::vm::VirtualMachine;
use std::cell::RefCell;
use std::cmp::Ordering;
@@ -53,6 +54,7 @@ pub struct PyContext {
pub dict_type: PyObjectRef,
pub int_type: PyObjectRef,
pub float_type: PyObjectRef,
pub bytes_type: PyObjectRef,
pub bool_type: PyObjectRef,
pub list_type: PyObjectRef,
pub tuple_type: PyObjectRef,
@@ -115,6 +117,7 @@ impl PyContext {
let list_type = create_type("list", &type_type, &object_type, &dict_type);
let int_type = create_type("int", &type_type, &object_type, &dict_type);
let float_type = create_type("float", &type_type, &object_type, &dict_type);
let bytes_type = create_type("bytes", &type_type, &object_type, &dict_type);
let tuple_type = create_type("tuple", &type_type, &object_type, &dict_type);
let bool_type = create_type("bool", &type_type, &object_type, &dict_type);
let exceptions = exceptions::ExceptionZoo::new(&type_type, &object_type, &dict_type);
@@ -127,6 +130,7 @@ impl PyContext {
let context = PyContext {
int_type: int_type,
float_type: float_type,
bytes_type: bytes_type,
list_type: list_type,
bool_type: bool_type,
tuple_type: tuple_type,
@@ -148,11 +152,11 @@ impl PyContext {
objfunction::init(&context);
objint::init(&context);
objfloat::init(&context);
objbytes::init(&context);
objstr::init(&context);
objtuple::init(&context);
objbool::init(&context);
exceptions::init(&context);
// TODO: create exception hierarchy here?
// exceptions::create_zoo(&context);
context
}
@@ -164,6 +168,10 @@ impl PyContext {
self.float_type.clone()
}
pub fn bytes_type(&self) -> PyObjectRef {
self.bytes_type.clone()
}
pub fn list_type(&self) -> PyObjectRef {
self.list_type.clone()
}
@@ -558,6 +566,9 @@ pub enum PyObjectKind {
Boolean {
value: bool,
},
Bytes {
value: Vec<u8>,
},
List {
elements: Vec<PyObjectRef>,
},
@@ -615,6 +626,7 @@ impl fmt::Debug for PyObjectKind {
&PyObjectKind::String { ref value } => write!(f, "str \"{}\"", value),
&PyObjectKind::Integer { ref value } => write!(f, "int {}", value),
&PyObjectKind::Float { ref value } => write!(f, "float {}", value),
&PyObjectKind::Bytes { ref value } => write!(f, "bytes {:?}", value),
&PyObjectKind::Boolean { ref value } => write!(f, "boolean {}", value),
&PyObjectKind::List { elements: _ } => write!(f, "list"),
&PyObjectKind::Tuple { elements: _ } => write!(f, "tuple"),
@@ -662,6 +674,7 @@ impl PyObject {
PyObjectKind::String { ref value } => value.clone(),
PyObjectKind::Integer { ref value } => format!("{:?}", value),
PyObjectKind::Float { ref value } => format!("{:?}", value),
PyObjectKind::Bytes { ref value } => format!("b'{:?}'", value),
PyObjectKind::Boolean { ref value } => format!("{:?}", value),
PyObjectKind::List { ref elements } => format!(
"[{}]",
@@ -773,11 +786,6 @@ impl PartialEq for PyObject {
(PyObjectKind::String { value: ref v1i }, PyObjectKind::String { value: ref v2i }) => {
*v2i == *v1i
}
/*
(&NativeType::Float(ref v1f), &NativeType::Float(ref v2f)) => {
curr_frame.stack.push(Rc::new(NativeType::Boolean(v2f == v1f)));
},
*/
(PyObjectKind::List { elements: ref l1 }, PyObjectKind::List { elements: ref l2 })
| (
PyObjectKind::Tuple { elements: ref l1 },

View File

@@ -6,13 +6,13 @@ use serde::de::Visitor;
use serde::ser::{SerializeMap, SerializeSeq};
use serde_json;
use super::super::obj::{objdict, objfloat, objint, objlist, objstr, objtype};
use super::super::obj::{objdict, objfloat, objint, objlist, objstr, objtuple, objtype};
use super::super::objbool;
use super::super::pyobject::{
DictProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult,
TypeProtocol,
};
use super::super::VirtualMachine;
use super::super::{objbool, objsequence};
// We need to have a VM available to serialise a PyObject based on its subclass, so we implement
// PyObject serialisation via a proxy object which holds a reference to a VM
@@ -52,10 +52,10 @@ impl<'s> serde::Serialize for PyObjectSerializer<'s> {
} else if objtype::isinstance(self.pyobject.clone(), self.vm.ctx.bool_type()) {
serializer.serialize_bool(objbool::get_value(self.pyobject))
} else if objtype::isinstance(self.pyobject.clone(), self.vm.ctx.list_type()) {
let elements = objlist::get_elements(self.pyobject.clone());
let elements = objlist::get_elements(self.pyobject);
serialize_seq_elements(serializer, elements)
} else if objtype::isinstance(self.pyobject.clone(), self.vm.ctx.tuple_type()) {
let elements = objsequence::get_elements(self.pyobject.clone());
let elements = objtuple::get_elements(self.pyobject);
serialize_seq_elements(serializer, elements)
} else if objtype::isinstance(self.pyobject.clone(), self.vm.ctx.dict_type()) {
let elements = objdict::get_elements(self.pyobject);

View File

@@ -16,10 +16,10 @@ use super::bytecode;
use super::frame::{copy_code, Block, Frame};
use super::import::import;
use super::obj::objlist;
use super::obj::objobject;
use super::obj::objstr;
use super::obj::objtype;
use super::objbool;
use super::objobject;
use super::pyobject::{
AttributeProtocol, DictProtocol, IdProtocol, ParentProtocol, PyContext, PyFuncArgs, PyObject,
PyObjectKind, PyObjectRef, PyResult, ToRust,
@@ -296,7 +296,7 @@ impl VirtualMachine {
match &a2.kind {
PyObjectKind::String { ref value } => objstr::subscript(self, value, b),
PyObjectKind::List { ref elements } | PyObjectKind::Tuple { ref elements } => {
super::objsequence::get_item(self, &a, elements, b)
super::obj::objsequence::get_item(self, &a, elements, b)
}
_ => Err(self.new_type_error(format!(
"TypeError: indexing type {:?} with index {:?} is not supported (yet?)",
@@ -367,6 +367,18 @@ impl VirtualMachine {
self.call_method(a, "__mod__", vec![b])
}
fn _xor(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult {
self.call_method(a, "__xor__", vec![b])
}
fn _or(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult {
self.call_method(a, "__or__", vec![b])
}
fn _and(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult {
self.call_method(a, "__and__", vec![b])
}
fn execute_binop(&mut self, op: &bytecode::BinaryOperator) -> Option<PyResult> {
let b_ref = self.pop_value();
let a_ref = self.pop_value();
@@ -380,6 +392,9 @@ impl VirtualMachine {
&bytecode::BinaryOperator::Divide => self._div(a_ref, b_ref),
&bytecode::BinaryOperator::Subscript => self.subscript(a_ref, b_ref),
&bytecode::BinaryOperator::Modulo => self._modulo(a_ref, b_ref),
&bytecode::BinaryOperator::Xor => self._xor(a_ref, b_ref),
&bytecode::BinaryOperator::Or => self._or(a_ref, b_ref),
&bytecode::BinaryOperator::And => self._and(a_ref, b_ref),
_ => panic!("NOT IMPL {:?}", op),
};
match result {