From b13b4741add7bd7d2cad3bf3469f5bf62dc57004 Mon Sep 17 00:00:00 2001 From: ben Date: Sat, 2 Feb 2019 17:40:17 +1300 Subject: [PATCH 001/153] Add tuple.__add__. Also stop rustfmt from trying to format generated parser. --- parser/src/lib.rs | 1 + tests/snippets/tuple.py | 2 ++ vm/src/obj/objtuple.rs | 22 ++++++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/parser/src/lib.rs b/parser/src/lib.rs index cefb3938b..e6372ce7c 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -7,6 +7,7 @@ extern crate num_traits; pub mod ast; pub mod lexer; pub mod parser; +#[cfg_attr(rustfmt, rustfmt_skip)] mod python; pub mod token; diff --git a/tests/snippets/tuple.py b/tests/snippets/tuple.py index 9227f9591..bf0188230 100644 --- a/tests/snippets/tuple.py +++ b/tests/snippets/tuple.py @@ -5,3 +5,5 @@ assert x[0] == 1 y = (1,) assert y[0] == 1 + +assert x + y == (1, 2, 1) diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 9657328a1..8c15e7e2d 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -10,6 +10,27 @@ use super::objtype; use num_bigint::ToBigInt; use std::hash::{Hash, Hasher}; +fn tuple_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] + ); + + if objtype::isinstance(other, &vm.ctx.tuple_type()) { + let e1 = get_elements(zelf); + let e2 = get_elements(other); + let elements = e1.iter().chain(e2.iter()).map(|e| e.clone()).collect(); + Ok(vm.ctx.new_tuple(elements)) + } else { + Err(vm.new_type_error(format!( + "Cannot add {} and {}", + zelf.borrow(), + other.borrow() + ))) + } +} + fn tuple_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -150,6 +171,7 @@ pub fn tuple_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let ref tuple_type = context.tuple_type; + context.set_attr(&tuple_type, "__add__", context.new_rustfunc(tuple_add)); context.set_attr(&tuple_type, "__eq__", context.new_rustfunc(tuple_eq)); context.set_attr( &tuple_type, From e0b41d1386955be9391a281c2db86c943caad8a4 Mon Sep 17 00:00:00 2001 From: Michael Recachinas Date: Sat, 2 Feb 2019 21:25:14 +0000 Subject: [PATCH 002/153] Add bool.__doc__ (addresses #260) --- tests/snippets/bools.py | 2 ++ vm/src/obj/objbool.rs | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/tests/snippets/bools.py b/tests/snippets/bools.py index aeec25639..8c6ec8c57 100644 --- a/tests/snippets/bools.py +++ b/tests/snippets/bools.py @@ -40,6 +40,8 @@ assert (True and True) assert not (False and fake) assert (True and 5) == 5 +assert bool.__doc__ == "bool(x) -> bool\n\nReturns True when the argument x is true, False otherwise.\nThe builtins True and False are the only two instances of the class bool.\nThe class bool is a subclass of the class int, and cannot be subclassed." + # Bools are also ints. assert isinstance(True, int) assert True + True == 2 diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index 85814be1d..bf53b858d 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -33,6 +33,7 @@ pub fn init(context: &PyContext) { let ref bool_type = context.bool_type; context.set_attr(&bool_type, "__new__", context.new_rustfunc(bool_new)); context.set_attr(&bool_type, "__repr__", context.new_rustfunc(bool_repr)); + context.set_attr(&bool_type, "__doc__", context.new_property(bool_doc)); } pub fn not(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult { @@ -79,3 +80,13 @@ fn bool_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { None => vm.context().new_bool(false), }) } + +fn bool_doc(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result { + arg_check!(vm, args, required = [(obj, None)]); + let s = "bool(x) -> bool + +Returns True when the argument x is true, False otherwise. +The builtins True and False are the only two instances of the class bool. +The class bool is a subclass of the class int, and cannot be subclassed."; + Ok(vm.new_str(s.to_string())) +} From f0abcff53d302c67557f2897663f2367e00cba11 Mon Sep 17 00:00:00 2001 From: Michael Recachinas Date: Sat, 2 Feb 2019 21:37:49 +0000 Subject: [PATCH 003/153] Add int.__doc__ (addresses #261) --- tests/snippets/numbers.py | 2 ++ vm/src/obj/objint.rs | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/tests/snippets/numbers.py b/tests/snippets/numbers.py index eae26b102..979dc6fdc 100644 --- a/tests/snippets/numbers.py +++ b/tests/snippets/numbers.py @@ -2,6 +2,8 @@ x = 5 x.__init__(6) assert x == 5 +assert int.__doc__ == "int(x=0) -> integer\nint(x, base=10) -> integer\n\nConvert a number or string to an integer, or return 0 if no arguments\nare given. If x is a number, return x.__int__(). For floating point\nnumbers, this truncates towards zero.\n\nIf x is not a number or if base is given, then x must be a string,\nbytes, or bytearray instance representing an integer literal in the\ngiven base. The literal can be preceded by '+' or '-' and be surrounded\nby whitespace. The base defaults to 10. Valid bases are 0 and 2-36.\nBase 0 means to interpret the base from the string as an integer literal.\n>>> int('0b100', base=0)\n4" + class A(int): pass diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index c42ddd999..7fbb81221 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -425,6 +425,25 @@ fn int_bit_length(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_int(bits.to_bigint().unwrap())) } +fn int_doc(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result { + arg_check!(vm, args, required = [(_zelf, None)]); + let s = "int(x=0) -> integer +int(x, base=10) -> integer + +Convert a number or string to an integer, or return 0 if no arguments +are given. If x is a number, return x.__int__(). For floating point +numbers, this truncates towards zero. + +If x is not a number or if base is given, then x must be a string, +bytes, or bytearray instance representing an integer literal in the +given base. The literal can be preceded by '+' or '-' and be surrounded +by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. +Base 0 means to interpret the base from the string as an integer literal. +>>> int('0b100', base=0) +4"; + Ok(vm.ctx.new_str(s.to_string())) +} + pub fn init(context: &PyContext) { let ref int_type = context.int_type; context.set_attr(&int_type, "__eq__", context.new_rustfunc(int_eq)); @@ -460,4 +479,5 @@ pub fn init(context: &PyContext) { "bit_length", context.new_rustfunc(int_bit_length), ); + context.set_attr(&int_type, "__doc__", context.new_property(int_doc)); } From e557ff290aafbad6373e046dff0c6f40393a5862 Mon Sep 17 00:00:00 2001 From: Michael Recachinas Date: Sat, 2 Feb 2019 21:39:06 +0000 Subject: [PATCH 004/153] Suppress warning and rename obj to _zelf in arg_check --- vm/src/obj/objbool.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index bf53b858d..eda7e74bc 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -82,7 +82,7 @@ fn bool_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn bool_doc(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result { - arg_check!(vm, args, required = [(obj, None)]); + arg_check!(vm, args, required = [(_zelf, None)]); let s = "bool(x) -> bool Returns True when the argument x is true, False otherwise. From 46b939721fd82edb2aa9d71f9f3faeea86856a6d Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Sun, 3 Feb 2019 01:51:47 +0300 Subject: [PATCH 005/153] str: Added the isdecimal method --- vm/src/obj/objstr.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 707ce42ba..3887a3f33 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -53,6 +53,7 @@ pub fn init(context: &PyContext) { context.set_attr(&str_type, "isalnum", context.new_rustfunc(str_isalnum)); context.set_attr(&str_type, "isnumeric", context.new_rustfunc(str_isnumeric)); context.set_attr(&str_type, "isdigit", context.new_rustfunc(str_isdigit)); + context.set_attr(&str_type, "isdecimal", context.new_rustfunc(str_isdecimal)); context.set_attr(&str_type, "title", context.new_rustfunc(str_title)); context.set_attr(&str_type, "swapcase", context.new_rustfunc(str_swapcase)); context.set_attr(&str_type, "isalpha", context.new_rustfunc(str_isalpha)); @@ -938,6 +939,20 @@ fn str_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(is_digit)) } +fn str_isdecimal(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); + + let value = get_value(&s); + + let is_decimal = if !value.is_empty() { + value.chars().all(|c| c.is_ascii_digit()) + } else { + false + }; + + Ok(vm.ctx.new_bool(is_decimal)) +} + fn str_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, From 23de5581bdcbab7d2b80406fe098c021e80da07c Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Sun, 3 Feb 2019 02:05:57 +0300 Subject: [PATCH 006/153] Added tests for the isdecimal method --- tests/snippets/strings.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/snippets/strings.py b/tests/snippets/strings.py index b6c735cc4..db0aad96e 100644 --- a/tests/snippets/strings.py +++ b/tests/snippets/strings.py @@ -35,6 +35,7 @@ assert not a.endswith('on') assert a.zfill(8) == '000Hallo' assert a.isalnum() assert not a.isdigit() +assert not a.isdecimal() assert not a.isnumeric() assert a.istitle() assert a.isalpha() @@ -65,6 +66,9 @@ assert '-'.join(['1', '2', '3']) == '1-2-3' assert 'HALLO'.isupper() assert "hello, my name is".partition("my ") == ('hello, ', 'my ', 'name is') assert "hello, my name is".rpartition("is") == ('hello, my name ', 'is', '') +assert not ''.isdecimal() +assert '123'.isdecimal() +assert not '\u00B2'.isdecimal() # String Formatting assert "{} {}".format(1,2) == "1 2" From b55150c7bd30ceae380d27fa544564aee1ccafba Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Sun, 3 Feb 2019 02:15:57 +0300 Subject: [PATCH 007/153] README: Added the 'pipenv install' command to the 'Testing' section --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 92623e8db..27691fd12 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ To test rustpython, there is a collection of python snippets located in the ```shell $ cd tests +$ pipenv install $ pipenv shell $ pytest -v ``` From 8558b84b162d93ef5560254a095ab39534fefc1c Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Sun, 3 Feb 2019 02:17:46 +0300 Subject: [PATCH 008/153] README: Replaced the 'pipenv shell' and the 'pytest -v' commands with the single 'pipenv run pytest -v' command in the 'Testing' section --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 27691fd12..adb06a4a2 100644 --- a/README.md +++ b/README.md @@ -82,8 +82,7 @@ To test rustpython, there is a collection of python snippets located in the ```shell $ cd tests $ pipenv install -$ pipenv shell -$ pytest -v +$ pipenv run pytest -v ``` There also are some unittests, you can run those will cargo: From e8d1e10226b05267b9161258977f9b5913dbd98f Mon Sep 17 00:00:00 2001 From: holygits Date: Sun, 3 Feb 2019 12:38:38 +1300 Subject: [PATCH 009/153] Implement __rshift__ and __lshift__ for integer --- tests/snippets/test_bitwise.py | 37 +++++++++++++++++++ vm/src/obj/objint.rs | 66 ++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 tests/snippets/test_bitwise.py diff --git a/tests/snippets/test_bitwise.py b/tests/snippets/test_bitwise.py new file mode 100644 index 000000000..18b90a197 --- /dev/null +++ b/tests/snippets/test_bitwise.py @@ -0,0 +1,37 @@ +def assert_raises(expr, exc_type): + """ + Helper function to assert `expr` raises an exception of type `exc_type` + Args: + expr: Callable + exec_type: Exception + Returns: + None + Raises: + Assertion error on failure + """ + try: + expr(None) + except exc_type: + assert True + else: + assert False + +# +# Tests +# +assert 8 >> 3 == 1 +assert 8 << 3 == 64 + +# Left shift raises type error +assert_raises(lambda _: 1 << 0.1, TypeError) +assert_raises(lambda _: 1 << "abc", TypeError) + +# Right shift raises type error +assert_raises(lambda _: 1 >> 0.1, TypeError) +assert_raises(lambda _: 1 >> "abc", TypeError) + +# Left shift raises value error on negative +assert_raises(lambda _: 1 << -1, ValueError) + +# Right shift raises value error on negative +assert_raises(lambda _: 1 >> -1, ValueError) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index c42ddd999..44569710f 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -170,6 +170,70 @@ fn int_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } +fn int_lshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(i, Some(vm.ctx.int_type())), (i2, None)] + ); + + if !objtype::isinstance(i2, &vm.ctx.int_type()) { + return Err(vm.new_type_error(format!( + "unsupported operand type(s) for << '{}' and '{}'", + objtype::get_type_name(&i.typ()), + objtype::get_type_name(&i2.typ()) + ))); + } + + if let Some(n_bits) = get_value(i2).to_usize() { + return Ok(vm.ctx.new_int(get_value(i) << n_bits)); + } + + // i2 failed `to_usize()` conversion + match get_value(i2) { + ref v if *v < BigInt::zero() => { + return Err(vm.new_value_error("negative shift count".to_string())); + } + ref v if *v > BigInt::from(usize::max_value()) => { + // TODO: raise OverflowError + panic!("Failed converting {} to rust usize", get_value(i2)); + } + _ => panic!("Failed converting {} to rust usize", get_value(i2)), + } +} + +fn int_rshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(i, Some(vm.ctx.int_type())), (i2, None)] + ); + + if !objtype::isinstance(i2, &vm.ctx.int_type()) { + return Err(vm.new_type_error(format!( + "unsupported operand type(s) for >> '{}' and '{}'", + objtype::get_type_name(&i.typ()), + objtype::get_type_name(&i2.typ()) + ))); + } + + if let Some(n_bits) = get_value(i2).to_usize() { + return Ok(vm.ctx.new_int(get_value(i) >> n_bits)); + } + + // i2 failed `to_usize()` conversion + match get_value(i2) { + ref v if *v < BigInt::zero() => { + return Err(vm.new_value_error("negative shift count".to_string())); + } + ref v if *v > BigInt::from(usize::max_value()) => { + // TODO: raise OverflowError + panic!("Failed converting {} to rust usize", get_value(i2)); + } + _ => panic!("Failed converting {} to rust usize", get_value(i2)), + } +} + fn int_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.int_type()))]); let value = BigInt::from_pyobj(zelf); @@ -443,6 +507,8 @@ pub fn init(context: &PyContext) { context.new_rustfunc(int_floordiv), ); context.set_attr(&int_type, "__hash__", context.new_rustfunc(int_hash)); + context.set_attr(&int_type, "__lshift__", context.new_rustfunc(int_lshift)); + context.set_attr(&int_type, "__rshift__", context.new_rustfunc(int_rshift)); context.set_attr(&int_type, "__new__", context.new_rustfunc(int_new)); context.set_attr(&int_type, "__mod__", context.new_rustfunc(int_mod)); context.set_attr(&int_type, "__mul__", context.new_rustfunc(int_mul)); From 8f9b733a7799323412280f7824ef388832ececbb Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 3 Feb 2019 13:59:43 +1300 Subject: [PATCH 010/153] Add tuple.__mul__ --- tests/snippets/list.py | 1 + tests/snippets/tuple.py | 5 +++++ vm/src/obj/objlist.rs | 12 ++---------- vm/src/obj/objsequence.rs | 15 +++++++++++++++ vm/src/obj/objtuple.rs | 18 +++++++++++++++++- 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/tests/snippets/list.py b/tests/snippets/list.py index 85010a755..22b7dec82 100644 --- a/tests/snippets/list.py +++ b/tests/snippets/list.py @@ -10,6 +10,7 @@ y.extend(x) assert y == [2, 1, 2, 3, 1, 2, 3] assert x * 0 == [], "list __mul__ by 0 failed" +assert x * -1 == [], "list __mul__ by -1 failed" assert x * 2 == [1, 2, 3, 1, 2, 3], "list __mul__ by 2 failed" assert ['a', 'b', 'c'].index('b') == 1 diff --git a/tests/snippets/tuple.py b/tests/snippets/tuple.py index 9227f9591..781a56ee5 100644 --- a/tests/snippets/tuple.py +++ b/tests/snippets/tuple.py @@ -5,3 +5,8 @@ assert x[0] == 1 y = (1,) assert y[0] == 1 + +assert x * 3 == (1, 2, 1, 2, 1, 2) +# assert 3 * x == (1, 2, 1, 2, 1, 2) +assert x * 0 == () +assert x * -1 == () # integers less than zero treated as 0 diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 641bb9aa6..30df25ab5 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -5,7 +5,7 @@ use super::super::vm::VirtualMachine; use super::objbool; use super::objint; use super::objsequence::{ - get_elements, get_item, get_mut_elements, seq_equal, PySliceableSequence, + get_elements, get_item, get_mut_elements, seq_equal, seq_mul, PySliceableSequence, }; use super::objstr; use super::objtype; @@ -259,15 +259,7 @@ fn list_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ] ); - let counter = objint::get_value(&product).to_usize().unwrap(); - - let elements = get_elements(list); - let current_len = elements.len(); - let mut new_elements = Vec::with_capacity(counter * current_len); - - for _ in 0..counter { - new_elements.extend(elements.clone()); - } + let new_elements = seq_mul(&get_elements(list), product); Ok(vm.ctx.new_list(new_elements)) } diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index cd26391a9..8dc63b51c 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -1,6 +1,7 @@ use super::super::pyobject::{PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol}; use super::super::vm::VirtualMachine; use super::objbool; +use super::objint; use num_traits::ToPrimitive; use std::cell::{Ref, RefMut}; use std::marker::Sized; @@ -120,6 +121,20 @@ pub fn seq_equal( } } +pub fn seq_mul(elements: &Vec, product: &PyObjectRef) -> Vec { + let counter = objint::get_value(&product).to_isize().unwrap(); + + let current_len = elements.len(); + let new_len = counter.max(0) as usize * current_len; + let mut new_elements = Vec::with_capacity(new_len); + + for _ in 0..counter { + new_elements.extend(elements.clone()); + } + + new_elements +} + pub fn get_elements<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a { Ref::map(obj.borrow(), |x| { if let PyObjectPayload::Sequence { ref elements } = x.payload { diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 9657328a1..8a0e92d0c 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -4,7 +4,7 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objbool; use super::objint; -use super::objsequence::{get_elements, get_item, seq_equal}; +use super::objsequence::{get_elements, get_item, seq_equal, seq_mul}; use super::objstr; use super::objtype; use num_bigint::ToBigInt; @@ -119,6 +119,21 @@ fn tuple_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_str(s)) } +fn tuple_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [ + (zelf, Some(vm.ctx.tuple_type())), + (product, Some(vm.ctx.int_type())) + ] + ); + + let new_elements = seq_mul(&get_elements(zelf), product); + + Ok(vm.ctx.new_tuple(new_elements)) +} + fn tuple_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -165,6 +180,7 @@ pub fn init(context: &PyContext) { context.set_attr(&tuple_type, "__iter__", context.new_rustfunc(tuple_iter)); context.set_attr(&tuple_type, "__len__", context.new_rustfunc(tuple_len)); context.set_attr(&tuple_type, "__new__", context.new_rustfunc(tuple_new)); + context.set_attr(&tuple_type, "__mul__", context.new_rustfunc(tuple_mul)); context.set_attr(&tuple_type, "__repr__", context.new_rustfunc(tuple_repr)); context.set_attr(&tuple_type, "count", context.new_rustfunc(tuple_count)); } From dae5b146ff152b3a6972fdc62c815c819426dd99 Mon Sep 17 00:00:00 2001 From: 0xflotus <0xflotus@gmail.com> Date: Sun, 3 Feb 2019 02:30:58 +0100 Subject: [PATCH 011/153] fixed converter --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 92623e8db..89256cfd8 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ If you wish to update the online documentation. Push directly to the `release` b - `obj`: python builtin types - `src`: using the other subcrates to bring rustpython to life. - `docs`: documentation (work in progress) -- `py_code_object`: CPython bytecode to rustpython bytecode convertor (work in progress) +- `py_code_object`: CPython bytecode to rustpython bytecode converter (work in progress) - `wasm`: Binary crate and resources for WebAssembly build - `tests`: integration test snippets From 167aefa8296837b8c50272b010adef1daf752e52 Mon Sep 17 00:00:00 2001 From: 0xflotus <0xflotus@gmail.com> Date: Sun, 3 Feb 2019 02:35:05 +0100 Subject: [PATCH 012/153] Update builtins.md --- docs/builtins.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/builtins.md b/docs/builtins.md index d918f0861..2daa2b275 100644 --- a/docs/builtins.md +++ b/docs/builtins.md @@ -1,6 +1,6 @@ Byterun -* Builtins are exposted to frame.f_builtins +* Builtins are exposed to frame.f_builtins * f_builtins is assigned during frame creation, self.f_builtins = f_locals['__builtins__'] if hasattr(self.f_builtins, '__dict__'): @@ -21,10 +21,10 @@ TODO: * Implement a new type NativeFunction * Wrap a function pointer in NativeFunction * Refactor the CALL_FUNCTION case so it can call both python function and native function -* During frame creation, force push a nativefunction `print` into the namespace +* During frame creation, force push a native function `print` into the namespace * Modify LOAD_* so they can search for names in builtins * Create a module type * In VM initialization, load the builtins module into locals -* During frame creation, create a field that conatins the builtins dict +* During frame creation, create a field that contains the builtins dict From 8854430651c8b34be6b6cdfdf0a66078fdedc18a Mon Sep 17 00:00:00 2001 From: holygits Date: Sun, 3 Feb 2019 14:53:04 +1300 Subject: [PATCH 013/153] Better error handling for file io Add FileNotFoundError Add PermissionError Minor idomatic clean ups --- vm/src/exceptions.rs | 8 ++++++++ vm/src/stdlib/io.rs | 35 +++++++++++++---------------------- vm/src/stdlib/os.rs | 40 +++++++++++++++++++++++++--------------- 3 files changed, 46 insertions(+), 37 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 72d640521..52bac927b 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -96,6 +96,8 @@ pub struct ExceptionZoo { pub value_error: PyObjectRef, pub import_error: PyObjectRef, pub module_not_found_error: PyObjectRef, + pub file_not_found_error: PyObjectRef, + pub permission_error: PyObjectRef, } impl ExceptionZoo { @@ -138,6 +140,10 @@ impl ExceptionZoo { let import_error = create_type("ImportError", &type_type, &exception_type, &dict_type); let module_not_found_error = create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type); + let file_not_found_error = + create_type("FileNotFoundError", &type_type, &import_error, &dict_type); + let permission_error = + create_type("PermissionError", &type_type, &import_error, &dict_type); ExceptionZoo { base_exception_type: base_exception_type, @@ -155,6 +161,8 @@ impl ExceptionZoo { value_error: value_error, import_error: import_error, module_not_found_error: module_not_found_error, + file_not_found_error: file_not_found_error, + permission_error: permission_error, } } } diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index c639195ec..6831b8bbc 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -81,10 +81,8 @@ fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //bytes are returned (when the end of the file is reached). while length == buff_size { let raw_read = vm.get_method(raw.clone(), &"readinto".to_string()).unwrap(); - match vm.invoke(raw_read, PyFuncArgs::new(vec![buffer.clone()], vec![])) { - Ok(_) => {} - Err(_) => return Err(vm.new_value_error("IO Error".to_string())), - } + vm.invoke(raw_read, PyFuncArgs::new(vec![buffer.clone()], vec![])) + .map_err(|_| vm.new_value_error("IO Error".to_string()))?; //Copy bytes from the buffer vector into the results vector match buffer.borrow_mut().payload { @@ -110,18 +108,15 @@ fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { optional = [(mode, Some(vm.ctx.str_type()))] ); - let rust_mode = match mode { - Some(m) => objstr::get_value(m), - None => "r".to_string(), - }; + let rust_mode = mode.map_or("r".to_string(), |m| objstr::get_value(m)); match compute_c_flag(&rust_mode).to_bigint() { Some(os_mode) => { let args = vec![name.clone(), vm.ctx.new_int(os_mode)]; - let fileno = os::os_open(vm, PyFuncArgs::new(args, vec![])); + let file_no = os::os_open(vm, PyFuncArgs::new(args, vec![]))?; vm.ctx.set_attr(&file_io, "name", name.clone()); - vm.ctx.set_attr(&file_io, "fileno", fileno.unwrap()); + vm.ctx.set_attr(&file_io, "file_no", file_no); Ok(vm.get_none()) } None => Err(vm.new_type_error(format!("invalid mode {}", rust_mode))), @@ -166,8 +161,8 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let py_length = vm.invoke(len_method.unwrap(), PyFuncArgs::default()); let length = objint::get_value(&py_length.unwrap()).to_u64().unwrap(); - let fileno = file_io.get_attr("fileno").unwrap(); - let raw_fd = objint::get_value(&fileno).to_i32().unwrap(); + let file_no = file_io.get_attr("file_no").unwrap(); + let raw_fd = objint::get_value(&file_no).to_i32().unwrap(); //unsafe block - creates file handle from the UNIX file descriptor //raw_fd is supported on UNIX only. This will need to be extended @@ -189,7 +184,7 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let new_handle = f.into_inner().into_raw_fd().to_bigint(); vm.ctx - .set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); + .set_attr(&file_io, "file_no", vm.ctx.new_int(new_handle.unwrap())); Ok(vm.get_none()) } @@ -200,8 +195,8 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(file_io, None), (obj, Some(vm.ctx.bytes_type()))] ); - let fileno = file_io.get_attr("fileno").unwrap(); - let raw_fd = objint::get_value(&fileno).to_i32().unwrap(); + let file_no = file_io.get_attr("file_no").unwrap(); + let raw_fd = objint::get_value(&file_no).to_i32().unwrap(); //unsafe block - creates file handle from the UNIX file descriptor //raw_fd is supported on UNIX only. This will need to be extended @@ -215,7 +210,7 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //reset raw fd on the FileIO object let new_handle = handle.into_raw_fd().to_bigint(); vm.ctx - .set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); + .set_attr(&file_io, "file_no", vm.ctx.new_int(new_handle.unwrap())); //return number of bytes written Ok(vm.ctx.new_int(len.to_bigint().unwrap())) @@ -280,11 +275,7 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let module = mk_module(&vm.ctx); //mode is optional: 'rt' is the default mode (open from reading text) - let rust_mode = if let Some(m) = mode { - objstr::get_value(m) - } else { - "rt".to_string() - }; + let rust_mode = mode.map_or("rt".to_string(), |m| objstr::get_value(m)); let mut raw_modes = HashSet::new(); @@ -322,7 +313,7 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vec![file.clone(), vm.ctx.new_str(modes[0].to_string())], vec![], ); - let file_io = vm.invoke(file_io_class, file_args).unwrap(); + let file_io = vm.invoke(file_io_class, file_args)?; //Create Buffered class to consume FileIO. The type of buffered class depends on //the operation in the mode. diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 443952cb0..0c6bab2ee 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -1,4 +1,5 @@ use std::fs::OpenOptions; +use std::io::ErrorKind; use std::os::unix::io::IntoRawFd; use num_bigint::ToBigInt; @@ -22,25 +23,34 @@ pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ] ); + let fname = objstr::get_value(&name); + let handle = match objint::get_value(mode).to_u16().unwrap() { - 0 => OpenOptions::new().read(true).open(objstr::get_value(&name)), - 1 => OpenOptions::new() - .write(true) - .open(objstr::get_value(&name)), - 512 => OpenOptions::new() - .write(true) - .create(true) - .open(objstr::get_value(&name)), - _ => OpenOptions::new().read(true).open(objstr::get_value(&name)), - }; + 0 => OpenOptions::new().read(true).open(&fname), + 1 => OpenOptions::new().write(true).open(&fname), + 512 => OpenOptions::new().write(true).create(true).open(&fname), + _ => OpenOptions::new().read(true).open(&fname), + } + .map_err(|err| match err.kind() { + ErrorKind::NotFound => { + let exc_type = vm.ctx.exceptions.file_not_found_error.clone(); + vm.new_exception(exc_type, format!("No such file or directory: {}", &fname)) + } + ErrorKind::PermissionDenied => { + let exc_type = vm.ctx.exceptions.permission_error.clone(); + vm.new_exception(exc_type, format!("Permission denied: {}", &fname)) + } + _ => vm.new_value_error("Unhandled file IO error".to_string()), + })?; //raw_fd is supported on UNIX only. This will need to be extended //to support windows - i.e. raw file_handles - if let Ok(f) = handle { - Ok(vm.ctx.new_int(f.into_raw_fd().to_bigint().unwrap())) - } else { - Err(vm.new_value_error("Bad file descriptor".to_string())) - } + Ok(vm.ctx.new_int( + handle + .into_raw_fd() + .to_bigint() + .expect("Invalid file descriptor"), + )) } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { From ea1a7bb39525b0693cb77d36743fb61097fd6bf9 Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sat, 2 Feb 2019 22:04:26 -0500 Subject: [PATCH 014/153] Add list.{__lt__, __gt__, __le__, __ge__} --- tests/snippets/list.py | 8 ++++ vm/src/obj/objlist.rs | 94 +++++++++++++++++++++++++++++++++++++- vm/src/obj/objsequence.rs | 95 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+), 1 deletion(-) diff --git a/tests/snippets/list.py b/tests/snippets/list.py index 85010a755..52dd60f0c 100644 --- a/tests/snippets/list.py +++ b/tests/snippets/list.py @@ -20,3 +20,11 @@ except ValueError: pass else: assert False, "ValueError was not raised" + +x = [[1,2,3], 'a', 1] +y = [[3,1,1], 'z', 2] +assert x < y, "list __lt__ failed" + +x = [5, -3, 1] +y = [1, 10, 29] +assert x > y, "list __gt__ failed" \ No newline at end of file diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 641bb9aa6..b4f7eb5df 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -5,7 +5,7 @@ use super::super::vm::VirtualMachine; use super::objbool; use super::objint; use super::objsequence::{ - get_elements, get_item, get_mut_elements, seq_equal, PySliceableSequence, + get_elements, get_item, get_mut_elements, seq_equal, seq_gt, seq_lt, seq_le, seq_ge, PySliceableSequence, }; use super::objstr; use super::objtype; @@ -73,6 +73,94 @@ fn list_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } +fn list_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.list_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.list_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_lt(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + +fn list_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.list_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.list_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_gt(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + +fn list_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.list_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.list_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_ge(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + +fn list_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.list_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.list_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_le(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + fn list_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -281,6 +369,10 @@ pub fn init(context: &PyContext) { context.new_rustfunc(list_contains), ); context.set_attr(&list_type, "__eq__", context.new_rustfunc(list_eq)); + context.set_attr(&list_type, "__lt__", context.new_rustfunc(list_lt)); + context.set_attr(&list_type, "__gt__", context.new_rustfunc(list_gt)); + context.set_attr(&list_type, "__le__", context.new_rustfunc(list_le)); + context.set_attr(&list_type, "__ge__", context.new_rustfunc(list_ge)); context.set_attr( &list_type, "__getitem__", diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index cd26391a9..705bdb750 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -120,6 +120,101 @@ pub fn seq_equal( } } +pub fn seq_lt( + vm: &mut VirtualMachine, + zelf: &Vec, + other: &Vec, +) -> Result { + if zelf.len() == other.len() { + for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { + let lt = vm.call_method(&a.clone(), "__lt__", vec![b.clone()])?; + let value = objbool::boolval(vm, lt)?; + if !value { + return Ok(false); + } + } + Ok(true) + } else { + // This case is more complicated because it can still return true if + // `zelf` is the head of `other` e.g. [1,2,3] < [1,2,3,4] should return true + let mut head = true; // true if `zelf` is the head of `other` + + for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { + let lt = vm.call_method(&a.clone(), "__lt__", vec![b.clone()])?; + let eq = vm.call_method(&a.clone(), "__eq__", vec![b.clone()])?; + let lt_value = objbool::boolval(vm, lt)?; + let eq_value = objbool::boolval(vm, eq)?; + + if !lt_value && !eq_value { + return Ok(false); + } else if !eq_value { + head = false; + } + } + + if head { + Ok(zelf.len() < other.len()) + } else { + Ok(true) + } + } +} + +pub fn seq_gt( + vm: &mut VirtualMachine, + zelf: &Vec, + other: &Vec, +) -> Result { + if zelf.len() == other.len() { + for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { + let gt = vm.call_method(&a.clone(), "__gt__", vec![b.clone()])?; + let value = objbool::boolval(vm, gt)?; + if !value { + return Ok(false); + } + } + Ok(true) + } else { + let mut head = true; // true if `other` is the head of `zelf` + for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { + // This case is more complicated because it can still return true if + // `other` is the head of `zelf` e.g. [1,2,3,4] > [1,2,3] should return true + let gt = vm.call_method(&a.clone(), "__gt__", vec![b.clone()])?; + let eq = vm.call_method(&a.clone(), "__eq__", vec![b.clone()])?; + let gt_value = objbool::boolval(vm, gt)?; + let eq_value = objbool::boolval(vm, eq)?; + + if !gt_value && !eq_value { + return Ok(false); + } else if !eq_value { + head = false; + } + } + + if head { + Ok(zelf.len() > other.len()) + } else { + Ok(true) + } + } +} + +pub fn seq_ge( + vm: &mut VirtualMachine, + zelf: &Vec, + other: &Vec, +) -> Result { + Ok(seq_gt(vm, zelf, other)? || seq_equal(vm, zelf, other)?) +} + +pub fn seq_le( + vm: &mut VirtualMachine, + zelf: &Vec, + other: &Vec, +) -> Result { + Ok(seq_lt(vm, zelf, other)? || seq_equal(vm, zelf, other)?) +} + pub fn get_elements<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a { Ref::map(obj.borrow(), |x| { if let PyObjectPayload::Sequence { ref elements } = x.payload { From 2b747277835f5c746caa2e45e6224176a8783c5c Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sat, 2 Feb 2019 22:41:34 -0500 Subject: [PATCH 015/153] Fix broken test --- tests/snippets/list.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/snippets/list.py b/tests/snippets/list.py index 52dd60f0c..abb31be3d 100644 --- a/tests/snippets/list.py +++ b/tests/snippets/list.py @@ -21,10 +21,10 @@ except ValueError: else: assert False, "ValueError was not raised" -x = [[1,2,3], 'a', 1] -y = [[3,1,1], 'z', 2] +x = [[1,0,-3], 'a', 1] +y = [[3,2,1], 'z', 2] assert x < y, "list __lt__ failed" -x = [5, -3, 1] +x = [5, 13, 31] y = [1, 10, 29] -assert x > y, "list __gt__ failed" \ No newline at end of file +assert x > y, "list __gt__ failed" From 8852435a3d017ba476eaa551306bd1eca06ab294 Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sat, 2 Feb 2019 23:05:32 -0500 Subject: [PATCH 016/153] Run cargo fmt --- vm/src/obj/objlist.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index b4f7eb5df..75cd0ab88 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -5,7 +5,8 @@ use super::super::vm::VirtualMachine; use super::objbool; use super::objint; use super::objsequence::{ - get_elements, get_item, get_mut_elements, seq_equal, seq_gt, seq_lt, seq_le, seq_ge, PySliceableSequence, + get_elements, get_item, get_mut_elements, seq_equal, seq_ge, seq_gt, seq_le, seq_lt, + PySliceableSequence, }; use super::objstr; use super::objtype; From 561eb8ed4fed89cc0009c25b1550161a3c965ae4 Mon Sep 17 00:00:00 2001 From: Yu Wu Date: Sat, 2 Feb 2019 21:45:24 -0800 Subject: [PATCH 017/153] Use call_or_unsupported for operators. --- .gitignore | 1 + vm/src/vm.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index e965f0242..ee7b4d3ee 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ __pycache__ **/*.pytest_cache .*sw* .repl_history.txt +.vscode wasm-pack.log diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 2dea87aa2..30e186150 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -549,27 +549,27 @@ impl VirtualMachine { } pub fn _add(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(&a, "__add__", vec![b]) + self.call_or_unsupported(a, b, "__add__", "__radd__", "+") } pub fn _mul(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(&a, "__mul__", vec![b]) + self.call_or_unsupported(a, b, "__mul__", "__rmul__", "*") } pub fn _div(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(&a, "__truediv__", vec![b]) + self.call_or_unsupported(a, b, "__truediv__", "__truediv__", "/") } pub fn _pow(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(&a, "__pow__", vec![b]) + self.call_or_unsupported(a, b, "__pow__", "__rpow__", "**") } pub fn _modulo(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(&a, "__mod__", vec![b]) + self.call_or_unsupported(a, b, "__mod__", "__rmod__", "%") } pub fn _xor(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_method(&a, "__xor__", vec![b]) + self.call_or_unsupported(a, b, "__xor__", "__rxor__", "^") } pub fn _or(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { From 7b27fbf36907f34c5865e72a09e1713f5c260def Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sun, 3 Feb 2019 00:49:52 -0500 Subject: [PATCH 018/153] Add list.pop Also include IndexError in the __builtin__ module. --- tests/snippets/list.py | 8 ++++++++ vm/src/builtins.rs | 1 + vm/src/obj/objlist.rs | 12 ++++++++++++ 3 files changed, 21 insertions(+) diff --git a/tests/snippets/list.py b/tests/snippets/list.py index 85010a755..709b6ff71 100644 --- a/tests/snippets/list.py +++ b/tests/snippets/list.py @@ -20,3 +20,11 @@ except ValueError: pass else: assert False, "ValueError was not raised" + +assert [1,2,'a'].pop() == 'a', "list pop failed" +try: + [].pop() +except IndexError: + pass +else: + assert False, "IndexError was not raised" diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index f1067d81e..d823e9205 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -822,6 +822,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ); ctx.set_attr(&py_mod, "TypeError", ctx.exceptions.type_error.clone()); ctx.set_attr(&py_mod, "ValueError", ctx.exceptions.value_error.clone()); + ctx.set_attr(&py_mod, "IndexError", ctx.exceptions.index_error.clone()); py_mod } diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 641bb9aa6..9f69792bc 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -272,6 +272,17 @@ fn list_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_list(new_elements)) } +fn list_pop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.list_type()))]); + + let mut elements = get_mut_elements(zelf); + if let Some(result) = elements.pop() { + Ok(result) + } else { + Err(vm.new_index_error("pop from empty list".to_string())) + } +} + pub fn init(context: &PyContext) { let ref list_type = context.list_type; context.set_attr(&list_type, "__add__", context.new_rustfunc(list_add)); @@ -303,4 +314,5 @@ pub fn init(context: &PyContext) { context.set_attr(&list_type, "index", context.new_rustfunc(list_index)); context.set_attr(&list_type, "reverse", context.new_rustfunc(list_reverse)); context.set_attr(&list_type, "sort", context.new_rustfunc(list_sort)); + context.set_attr(&list_type, "pop", context.new_rustfunc(list_pop)); } From a72dbf1d0ce72f7eaf5a3b1b9f2f670a3c62d08f Mon Sep 17 00:00:00 2001 From: Tom Forbes Date: Sun, 3 Feb 2019 10:07:50 +0100 Subject: [PATCH 019/153] Use XDG paths to store Python history --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/main.rs | 10 ++++++---- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ddc013a58..6f226d279 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -684,6 +684,7 @@ dependencies = [ "rustpython_parser 0.0.1", "rustpython_vm 0.1.0", "rustyline 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1128,6 +1129,11 @@ dependencies = [ "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "xdg" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [metadata] "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" @@ -1263,3 +1269,4 @@ dependencies = [ "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767" "checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" +"checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" diff --git a/Cargo.toml b/Cargo.toml index 23b9e02a5..92213e8cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ clap = "2.31.2" rustpython_parser = {path = "parser"} rustpython_vm = {path = "vm"} rustyline = "2.1.0" +xdg = "2.2.0" [profile.release] opt-level = "s" diff --git a/src/main.rs b/src/main.rs index 12b79873c..0a59c883b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ extern crate log; extern crate rustpython_parser; extern crate rustpython_vm; extern crate rustyline; +extern crate xdg; use clap::{App, Arg}; use rustpython_parser::parser; @@ -154,9 +155,10 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { let mut input = String::new(); let mut rl = Editor::<()>::new(); - // TODO: Store the history in a proper XDG directory - let repl_history_path = ".repl_history.txt"; - if rl.load_history(repl_history_path).is_err() { + let xdg_dirs = xdg::BaseDirectories::with_prefix("rustpython").unwrap(); + let repl_history_path = xdg_dirs.place_cache_file("repl_history.txt").unwrap(); + let repl_history_path_str = repl_history_path.to_str().unwrap(); + if rl.load_history(repl_history_path_str).is_err() { println!("No previous history."); } @@ -220,7 +222,7 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { } }; } - rl.save_history(repl_history_path).unwrap(); + rl.save_history(repl_history_path_str).unwrap(); Ok(vm.get_none()) } From 334d78704ac3c066834e71ef2a0dc7dbf78a6a56 Mon Sep 17 00:00:00 2001 From: Takanori Ishibashi Date: Sun, 3 Feb 2019 21:41:04 +0900 Subject: [PATCH 020/153] distuingish -> distinguish --- parser/src/ast.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index d0ed2b3fe..6a4a6f409 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -221,7 +221,7 @@ pub enum Expression { /* * In cpython this is called arguments, but we choose parameters to - * distuingish between function parameters and actual call arguments. + * distinguish between function parameters and actual call arguments. */ #[derive(Debug, PartialEq, Default)] pub struct Parameters { From 38ee8aeed679619968eddc03479d97214bc168a7 Mon Sep 17 00:00:00 2001 From: Yu Wu Date: Sun, 3 Feb 2019 07:02:36 -0800 Subject: [PATCH 021/153] Fix reverse method for calling div. --- vm/src/vm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 30e186150..db4396dc9 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -557,7 +557,7 @@ impl VirtualMachine { } pub fn _div(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__truediv__", "__truediv__", "/") + self.call_or_unsupported(a, b, "__truediv__", "__rtruediv__", "/") } pub fn _pow(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { From 353d881bacf8b15f554a0dacd2d75ce76935309c Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sun, 3 Feb 2019 11:33:11 -0500 Subject: [PATCH 022/153] Fix missing seq_mul import --- vm/src/obj/objlist.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index f77b8b408..0df789385 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -5,7 +5,7 @@ use super::super::vm::VirtualMachine; use super::objbool; use super::objint; use super::objsequence::{ - get_elements, get_item, get_mut_elements, seq_equal, seq_ge, seq_gt, seq_le, seq_lt, + get_elements, get_item, get_mut_elements, seq_equal, seq_ge, seq_gt, seq_le, seq_lt, seq_mul, PySliceableSequence, }; use super::objstr; From a95747f161e852736cb2dfb162c9bd5f48af7706 Mon Sep 17 00:00:00 2001 From: Alexander Gude Date: Sun, 3 Feb 2019 11:18:23 -0800 Subject: [PATCH 023/153] Add .conjugate() method to int type Also add tests for for the int type, and a commented out one for the bool type. --- tests/snippets/basic_types.py | 2 ++ tests/snippets/bools.py | 1 + vm/src/obj/objint.rs | 6 ++++++ 3 files changed, 9 insertions(+) diff --git a/tests/snippets/basic_types.py b/tests/snippets/basic_types.py index 4ed164f30..1298c4ed3 100644 --- a/tests/snippets/basic_types.py +++ b/tests/snippets/basic_types.py @@ -45,6 +45,8 @@ a = complex(2, 4) assert type(a) is complex assert type(a + a) is complex +a = 1 +assert a.conjugate() == a a = 12345 diff --git a/tests/snippets/bools.py b/tests/snippets/bools.py index aeec25639..426b1605a 100644 --- a/tests/snippets/bools.py +++ b/tests/snippets/bools.py @@ -46,3 +46,4 @@ assert True + True == 2 assert False * 7 == 0 assert True > 0 assert int(True) == 1 +#assert True.conjugate() == 1 diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 44569710f..9005a631b 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -489,6 +489,11 @@ fn int_bit_length(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_int(bits.to_bigint().unwrap())) } +fn int_conjugate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(i, Some(vm.ctx.int_type()))]); + Ok(i.clone()) +} + pub fn init(context: &PyContext) { let ref int_type = context.int_type; context.set_attr(&int_type, "__eq__", context.new_rustfunc(int_eq)); @@ -526,4 +531,5 @@ pub fn init(context: &PyContext) { "bit_length", context.new_rustfunc(int_bit_length), ); + context.set_attr(&int_type, "conjugate", context.new_rustfunc(int_conjugate)); } From 33523d492c103da2da9c5e881653822373ae1543 Mon Sep 17 00:00:00 2001 From: Alexander Gude Date: Sun, 3 Feb 2019 13:27:57 -0800 Subject: [PATCH 024/153] Change int.conjugate() to return a new int This results in the correct behavior (that is, matching CPython) for both int and bool types. Also adding a bool conjugate test. --- tests/snippets/bools.py | 3 ++- vm/src/obj/objint.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/snippets/bools.py b/tests/snippets/bools.py index 426b1605a..2aa817ca4 100644 --- a/tests/snippets/bools.py +++ b/tests/snippets/bools.py @@ -46,4 +46,5 @@ assert True + True == 2 assert False * 7 == 0 assert True > 0 assert int(True) == 1 -#assert True.conjugate() == 1 +assert True.conjugate() == 1 +assert isinstance(True.conjugate(), int) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 9005a631b..7f0321683 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -491,7 +491,8 @@ fn int_bit_length(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn int_conjugate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(i, Some(vm.ctx.int_type()))]); - Ok(i.clone()) + let v = get_value(i); + Ok(vm.ctx.new_int(v)) } pub fn init(context: &PyContext) { From 19557e83447ccc672aaa687d6b977626aa4d3845 Mon Sep 17 00:00:00 2001 From: idbentley Date: Sun, 3 Feb 2019 16:36:25 -0500 Subject: [PATCH 025/153] Alphebetize exception related code --- vm/src/exceptions.rs | 56 ++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 52bac927b..b9e2496b3 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -81,23 +81,23 @@ fn exception_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { #[derive(Debug)] pub struct ExceptionZoo { - pub base_exception_type: PyObjectRef, - pub exception_type: PyObjectRef, - pub syntax_error: PyObjectRef, pub assertion_error: PyObjectRef, pub attribute_error: PyObjectRef, + pub base_exception_type: PyObjectRef, + pub exception_type: PyObjectRef, + pub file_not_found_error: PyObjectRef, + pub import_error: PyObjectRef, pub index_error: PyObjectRef, pub key_error: PyObjectRef, + pub module_not_found_error: PyObjectRef, pub name_error: PyObjectRef, - pub runtime_error: PyObjectRef, pub not_implemented_error: PyObjectRef, + pub permission_error: PyObjectRef, + pub runtime_error: PyObjectRef, pub stop_iteration: PyObjectRef, + pub syntax_error: PyObjectRef, pub type_error: PyObjectRef, pub value_error: PyObjectRef, - pub import_error: PyObjectRef, - pub module_not_found_error: PyObjectRef, - pub file_not_found_error: PyObjectRef, - pub permission_error: PyObjectRef, } impl ExceptionZoo { @@ -106,11 +106,16 @@ impl ExceptionZoo { object_type: &PyObjectRef, dict_type: &PyObjectRef, ) -> Self { + // Sorted By Hierarchy then alphabetized. + let base_exception_type = create_type("BaseException", &type_type, &object_type, &dict_type); let exception_type = create_type("Exception", &type_type, &base_exception_type, &dict_type); - let syntax_error = create_type("SyntaxError", &type_type, &exception_type, &dict_type); + + let runtime_error = create_type("RuntimeError", &type_type, &exception_type, &dict_type); + let import_error = create_type("ImportError", &type_type, &exception_type, &dict_type); + let assertion_error = create_type("AssertionError", &type_type, &exception_type, &dict_type); let attribute_error = create_type( @@ -119,6 +124,8 @@ impl ExceptionZoo { &exception_type.clone(), &dict_type, ); + let file_not_found_error = + create_type("FileNotFoundError", &type_type, &import_error, &dict_type); let index_error = create_type( "IndexError", &type_type, @@ -126,43 +133,40 @@ impl ExceptionZoo { &dict_type, ); let key_error = create_type("KeyError", &type_type, &exception_type.clone(), &dict_type); + let module_not_found_error = + create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type); let name_error = create_type("NameError", &type_type, &exception_type.clone(), &dict_type); - let runtime_error = create_type("RuntimeError", &type_type, &exception_type, &dict_type); let not_implemented_error = create_type( "NotImplementedError", &type_type, &runtime_error, &dict_type, ); - let stop_iteration = create_type("StopIteration", &type_type, &exception_type, &dict_type); - let type_error = create_type("TypeError", &type_type, &exception_type, &dict_type); - let value_error = create_type("ValueError", &type_type, &exception_type, &dict_type); - let import_error = create_type("ImportError", &type_type, &exception_type, &dict_type); - let module_not_found_error = - create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type); - let file_not_found_error = - create_type("FileNotFoundError", &type_type, &import_error, &dict_type); let permission_error = create_type("PermissionError", &type_type, &import_error, &dict_type); + let stop_iteration = create_type("StopIteration", &type_type, &exception_type, &dict_type); + let syntax_error = create_type("SyntaxError", &type_type, &exception_type, &dict_type); + let type_error = create_type("TypeError", &type_type, &exception_type, &dict_type); + let value_error = create_type("ValueError", &type_type, &exception_type, &dict_type); ExceptionZoo { + attribute_error: attribute_error, + assertion_error: assertion_error, base_exception_type: base_exception_type, exception_type: exception_type, - syntax_error: syntax_error, - assertion_error: assertion_error, - attribute_error: attribute_error, + file_not_found_error: file_not_found_error, + import_error: import_error, index_error: index_error, key_error: key_error, + module_not_found_error: module_not_found_error, name_error: name_error, - runtime_error: runtime_error, not_implemented_error: not_implemented_error, + permission_error: permission_error, + runtime_error: runtime_error, stop_iteration: stop_iteration, + syntax_error: syntax_error, type_error: type_error, value_error: value_error, - import_error: import_error, - module_not_found_error: module_not_found_error, - file_not_found_error: file_not_found_error, - permission_error: permission_error, } } } From 055e5774241c0f3a4ed431f741e2e5413f8a9adf Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Sun, 3 Feb 2019 16:38:04 -0500 Subject: [PATCH 026/153] Fix panic on out of bounds negative index (addresses #295) Also an out of bounds index now raises an IndexError rather than a ValueError --- vm/src/obj/objsequence.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 3bb6933fc..65bff64a6 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -13,7 +13,12 @@ pub trait PySliceableSequence { fn len(&self) -> usize; fn get_pos(&self, p: i32) -> usize { if p < 0 { - self.len() - ((-p) as usize) + if -p as usize > self.len() { + // return something that is out of bounds so get_item raises an IndexError + self.len() + 1 + } else { + self.len() - ((-p) as usize) + } } else if p as usize > self.len() { // This is for the slicing case where the end element is greater than the length of the // sequence @@ -78,8 +83,8 @@ pub fn get_item( let obj = elements[pos_index].clone(); Ok(obj) } else { - let value_error = vm.context().exceptions.value_error.clone(); - Err(vm.new_exception(value_error, "Index out of bounds!".to_string())) + let index_error = vm.context().exceptions.index_error.clone(); + Err(vm.new_exception(index_error, "Index out of bounds!".to_string())) } } PyObjectPayload::Slice { From f2db23bb3561bb910d4c0be19851f7c2bd44bace Mon Sep 17 00:00:00 2001 From: idbentley Date: Sun, 3 Feb 2019 16:40:14 -0500 Subject: [PATCH 027/153] correct order of errors. --- vm/src/exceptions.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index b9e2496b3..3cb61a4a2 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -113,9 +113,6 @@ impl ExceptionZoo { let exception_type = create_type("Exception", &type_type, &base_exception_type, &dict_type); - let runtime_error = create_type("RuntimeError", &type_type, &exception_type, &dict_type); - let import_error = create_type("ImportError", &type_type, &exception_type, &dict_type); - let assertion_error = create_type("AssertionError", &type_type, &exception_type, &dict_type); let attribute_error = create_type( @@ -124,8 +121,7 @@ impl ExceptionZoo { &exception_type.clone(), &dict_type, ); - let file_not_found_error = - create_type("FileNotFoundError", &type_type, &import_error, &dict_type); + let import_error = create_type("ImportError", &type_type, &exception_type, &dict_type); let index_error = create_type( "IndexError", &type_type, @@ -133,9 +129,17 @@ impl ExceptionZoo { &dict_type, ); let key_error = create_type("KeyError", &type_type, &exception_type.clone(), &dict_type); + let name_error = create_type("NameError", &type_type, &exception_type.clone(), &dict_type); + let runtime_error = create_type("RuntimeError", &type_type, &exception_type, &dict_type); + let stop_iteration = create_type("StopIteration", &type_type, &exception_type, &dict_type); + let syntax_error = create_type("SyntaxError", &type_type, &exception_type, &dict_type); + let type_error = create_type("TypeError", &type_type, &exception_type, &dict_type); + let value_error = create_type("ValueError", &type_type, &exception_type, &dict_type); + + let file_not_found_error = + create_type("FileNotFoundError", &type_type, &import_error, &dict_type); let module_not_found_error = create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type); - let name_error = create_type("NameError", &type_type, &exception_type.clone(), &dict_type); let not_implemented_error = create_type( "NotImplementedError", &type_type, @@ -144,14 +148,10 @@ impl ExceptionZoo { ); let permission_error = create_type("PermissionError", &type_type, &import_error, &dict_type); - let stop_iteration = create_type("StopIteration", &type_type, &exception_type, &dict_type); - let syntax_error = create_type("SyntaxError", &type_type, &exception_type, &dict_type); - let type_error = create_type("TypeError", &type_type, &exception_type, &dict_type); - let value_error = create_type("ValueError", &type_type, &exception_type, &dict_type); ExceptionZoo { - attribute_error: attribute_error, assertion_error: assertion_error, + attribute_error: attribute_error, base_exception_type: base_exception_type, exception_type: exception_type, file_not_found_error: file_not_found_error, From 33a3ec8832f50b2a0b6ef0b861ddc2e1f654e983 Mon Sep 17 00:00:00 2001 From: holygits Date: Mon, 4 Feb 2019 15:40:30 +1300 Subject: [PATCH 028/153] Handle unicode string slicing with graphemes --- Cargo.lock | 1 + tests/snippets/unicode_slicing.py | 33 +++++++++++++++++++++++++++ vm/Cargo.toml | 3 ++- vm/src/obj/objsequence.rs | 13 +++++++---- vm/src/obj/objstr.rs | 38 ++++++++++++++++++++++++++----- 5 files changed, 77 insertions(+), 11 deletions(-) create mode 100644 tests/snippets/unicode_slicing.py diff --git a/Cargo.lock b/Cargo.lock index 6f226d279..b1342cd72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -717,6 +717,7 @@ dependencies = [ "serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", "statrs 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/tests/snippets/unicode_slicing.py b/tests/snippets/unicode_slicing.py new file mode 100644 index 000000000..de4184513 --- /dev/null +++ b/tests/snippets/unicode_slicing.py @@ -0,0 +1,33 @@ +def test_slice_bounds(s): + # End out of range + assert s[0:100] == s + assert s[0:-100] == '' + # Start out of range + assert s[100:1] == '' + # Out of range both sides + # This is the behaviour in cpython + # assert s[-100:100] == s + +def expect_index_error(s, index): + try: + s[index] + except IndexError: + pass + else: + assert False + +unicode_str = "∀∂" +assert unicode_str[0] == "∀" +assert unicode_str[1] == "∂" +assert unicode_str[-1] == "∂" + +test_slice_bounds(unicode_str) +expect_index_error(unicode_str, 100) +expect_index_error(unicode_str, -100) + +ascii_str = "hello world" +test_slice_bounds(ascii_str) +assert ascii_str[0] == "h" +assert ascii_str[1] == "e" +assert ascii_str[-1] == "d" + diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 7f1aed7ab..59e10da64 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -17,4 +17,5 @@ serde_json = "1.0.26" byteorder = "1.2.6" regex = "1" statrs = "0.10.0" -caseless = "0.2.1" \ No newline at end of file +caseless = "0.2.1" +unicode-segmentation = "1.2.1" diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 3bb6933fc..4dbd0026f 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -13,7 +13,12 @@ pub trait PySliceableSequence { fn len(&self) -> usize; fn get_pos(&self, p: i32) -> usize { if p < 0 { - self.len() - ((-p) as usize) + if -p as usize > self.len() { + // return something that is out of bounds so `get_item` raises an IndexError + self.len() + 1 + } else { + self.len() - ((-p) as usize) + } } else if p as usize > self.len() { // This is for the slicing case where the end element is greater than the length of the // sequence @@ -52,7 +57,7 @@ pub trait PySliceableSequence { } } -impl PySliceableSequence for Vec { +impl PySliceableSequence for Vec { fn do_slice(&self, start: usize, stop: usize) -> Self { self[start..stop].to_vec() } @@ -78,8 +83,8 @@ pub fn get_item( let obj = elements[pos_index].clone(); Ok(obj) } else { - let value_error = vm.context().exceptions.value_error.clone(); - Err(vm.new_exception(value_error, "Index out of bounds!".to_string())) + let index_error = vm.context().exceptions.index_error.clone(); + Err(vm.new_exception(index_error, "Index out of bounds!".to_string())) } } PyObjectPayload::Slice { diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 3887a3f33..ce55bb940 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -11,6 +11,9 @@ use num_traits::ToPrimitive; use std::hash::{Hash, Hasher}; // rust's builtin to_lowercase isn't sufficient for casefold extern crate caseless; +extern crate unicode_segmentation; + +use self::unicode_segmentation::UnicodeSegmentation; pub fn init(context: &PyContext) { let ref str_type = context.str_type; @@ -980,22 +983,45 @@ fn str_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { impl PySliceableSequence for String { fn do_slice(&self, start: usize, stop: usize) -> Self { - self[start..stop].to_string() + to_graphemes(self) + .get(start..stop) + .map_or(String::default(), |c| c.join("")) } + fn do_stepped_slice(&self, start: usize, stop: usize, step: usize) -> Self { - self[start..stop].chars().step_by(step).collect() + if let Some(s) = to_graphemes(self).get(start..stop) { + return s + .iter() + .cloned() + .step_by(step) + .collect::>() + .join(""); + } + String::default() } + fn len(&self) -> usize { - self.len() + to_graphemes(self).len() } } +/// Convert a string-able `value` to a vec of graphemes +/// represents the string according to user perceived characters +fn to_graphemes>(value: S) -> Vec { + UnicodeSegmentation::graphemes(value.as_ref(), true) + .map(String::from) + .collect() +} + pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResult { - // let value = a if objtype::isinstance(&b, &vm.ctx.int_type()) { let pos = objint::get_value(&b).to_i32().unwrap(); - let idx = value.to_string().get_pos(pos); - Ok(vm.new_str(value[idx..idx + 1].to_string())) + let graphemes = to_graphemes(value); + let idx = graphemes.get_pos(pos); + graphemes + .get(idx) + .map(|c| vm.new_str(c.to_string())) + .ok_or(vm.new_index_error("string index out of range".to_string())) } else { match &(*b.borrow()).payload { &PyObjectPayload::Slice { From 0146e5f51ce9136b5317dd5d61dd9d7ec71f5645 Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Mon, 4 Feb 2019 08:15:22 +0100 Subject: [PATCH 029/153] Remove unnecessary dereferences/references in compiler It's not necessary to dereference a reference to then take a reference again, at least not in these cases, so we can remove them. --- vm/src/compile.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 149bb85a9..9699ce2e0 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -838,21 +838,21 @@ impl Compiler { self.compile_test(expression, None, None, EvalContext::Expression)? } ast::Expression::Binop { a, op, b } => { - self.compile_expression(&*a)?; - self.compile_expression(&*b)?; + self.compile_expression(a)?; + self.compile_expression(b)?; // Perform operation: self.compile_op(op); } ast::Expression::Subscript { a, b } => { - self.compile_expression(&*a)?; - self.compile_expression(&*b)?; + self.compile_expression(a)?; + self.compile_expression(b)?; self.emit(Instruction::BinaryOperation { op: bytecode::BinaryOperator::Subscript, }); } ast::Expression::Unop { op, a } => { - self.compile_expression(&*a)?; + self.compile_expression(a)?; // Perform operation: let i = match op { @@ -865,14 +865,14 @@ impl Compiler { self.emit(i); } ast::Expression::Attribute { value, name } => { - self.compile_expression(&*value)?; + self.compile_expression(value)?; self.emit(Instruction::LoadAttr { name: name.to_string(), }); } ast::Expression::Compare { a, op, b } => { - self.compile_expression(&*a)?; - self.compile_expression(&*b)?; + self.compile_expression(a)?; + self.compile_expression(b)?; let i = match op { ast::Comparison::Equal => bytecode::ComparisonOperator::Equal, From 7096434f95c60d29bfe0bdda984af38a52040de3 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Mon, 4 Feb 2019 17:17:54 +0100 Subject: [PATCH 030/153] Implement some clippy tips --- src/main.rs | 4 +- vm/src/builtins.rs | 10 +++-- vm/src/compile.rs | 24 +++++----- vm/src/eval.rs | 2 +- vm/src/exceptions.rs | 38 ++++++++-------- vm/src/import.rs | 2 +- vm/src/pyobject.rs | 104 +++++++++++++++++++++---------------------- wasm/lib/src/lib.rs | 2 +- 8 files changed, 95 insertions(+), 91 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0a59c883b..afb49832e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -70,7 +70,7 @@ fn main() { } fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: Option) -> PyResult { - let code_obj = compile::compile(vm, source, compile::Mode::Exec, source_path)?; + let code_obj = compile::compile(vm, source, &compile::Mode::Exec, source_path)?; // trace!("Code object: {:?}", code_obj.borrow()); let builtins = vm.get_builtin_scope(); let vars = vm.context().new_scope(Some(builtins)); // Keep track of local variables @@ -115,7 +115,7 @@ fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult { } fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool { - match compile::compile(vm, source, compile::Mode::Single, None) { + match compile::compile(vm, source, &compile::Mode::Single, None) { Ok(code) => { match vm.run_code_obj(code, scope) { Ok(_value) => { diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index d823e9205..1c67b474c 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -151,7 +151,7 @@ fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let filename = objstr::get_value(filename); - compile::compile(vm, &source, mode, Some(filename)) + compile::compile(vm, &source, &mode, Some(filename)) } fn builtin_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -203,6 +203,8 @@ fn builtin_enumerate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_list(new_items)) } +/// Implements `eval`. +/// See also: https://docs.python.org/3/library/functions.html#eval fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -222,7 +224,7 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let source = objstr::get_value(source); // TODO: fix this newline bug: let source = format!("{}\n", source); - compile::compile(vm, &source, mode, None)? + compile::compile(vm, &source, &mode, None)? } else { return Err(vm.new_type_error("code argument must be str or code object".to_string())); }; @@ -249,6 +251,8 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm.run_code_obj(code_obj.clone(), scope) } +/// Implements `exec` +/// https://docs.python.org/3/library/functions.html#exec fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -266,7 +270,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let source = objstr::get_value(source); // TODO: fix this newline bug: let source = format!("{}\n", source); - compile::compile(vm, &source, mode, None)? + compile::compile(vm, &source, &mode, None)? } else if objtype::isinstance(source, &vm.ctx.code_type()) { source.clone() } else { diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 9699ce2e0..71439463d 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -23,7 +23,7 @@ struct Compiler { pub fn compile( vm: &mut VirtualMachine, source: &str, - mode: Mode, + mode: &Mode, source_path: Option, ) -> PyResult { let mut compiler = Compiler::new(); @@ -112,7 +112,7 @@ impl Compiler { fn compile_program_single(&mut self, program: &ast::Program) -> Result<(), String> { for statement in &program.statements { - if let &ast::Statement::Expression { ref expression } = &statement.node { + if let ast::Statement::Expression { ref expression } = statement.node { self.compile_expression(expression)?; self.emit(Instruction::PrintExpr); } else { @@ -128,7 +128,7 @@ impl Compiler { // Compile statement in eval mode: fn compile_statement_eval(&mut self, statement: &ast::LocatedStatement) -> Result<(), String> { - if let &ast::Statement::Expression { ref expression } = &statement.node { + if let ast::Statement::Expression { ref expression } = statement.node { self.compile_expression(expression)?; self.emit(Instruction::ReturnValue); Ok(()) @@ -500,7 +500,7 @@ impl Compiler { self.compile_expression(base)?; } - if keywords.len() > 0 { + if !keywords.is_empty() { let mut kwarg_names = vec![]; for keyword in keywords { if let Some(name) = &keyword.name { @@ -627,7 +627,7 @@ impl Compiler { self.emit(Instruction::DeleteSubscript); } _ => { - return Err(format!("Invalid delete statement")); + return Err("Invalid delete statement".to_string()); } } } @@ -641,10 +641,10 @@ impl Compiler { fn enter_function( &mut self, - name: &String, + name: &str, args: &ast::Parameters, ) -> Result { - let have_kwargs = args.defaults.len() > 0; + let have_kwargs = !args.defaults.is_empty(); if have_kwargs { // Construct a tuple: let size = args.defaults.len(); @@ -663,7 +663,7 @@ impl Compiler { args.kwonlyargs.clone(), args.kwarg.clone(), self.source_path.clone(), - name.clone(), + name.to_string(), )); let mut flags = bytecode::FunctionOpArg::empty(); @@ -780,7 +780,7 @@ impl Compiler { let f = false_label.unwrap_or_else(|| self.new_label()); self.compile_test(a, None, Some(f), context)?; self.compile_test(b, true_label, false_label, context)?; - if let None = false_label { + if false_label.is_none() { self.set_label(f); } } @@ -788,7 +788,7 @@ impl Compiler { let t = true_label.unwrap_or_else(|| self.new_label()); self.compile_test(a, Some(t), None, context)?; self.compile_test(b, true_label, false_label, context)?; - if let None = true_label { + if true_label.is_none() { self.set_label(t); } } @@ -1054,7 +1054,7 @@ impl Compiler { }); // Create an optional map with kw-args: - if keywords.len() > 0 { + if !keywords.is_empty() { for keyword in keywords { if let Some(name) = &keyword.name { self.emit(Instruction::LoadConst { @@ -1090,7 +1090,7 @@ impl Compiler { } } else { // Keyword arguments: - if keywords.len() > 0 { + if !keywords.is_empty() { let mut kwarg_names = vec![]; for keyword in keywords { if let Some(name) = &keyword.name { diff --git a/vm/src/eval.rs b/vm/src/eval.rs index d1facd26e..f5f690e95 100644 --- a/vm/src/eval.rs +++ b/vm/src/eval.rs @@ -5,7 +5,7 @@ use super::pyobject::{PyObjectRef, PyResult}; use super::vm::VirtualMachine; pub fn eval(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> PyResult { - match compile::compile(vm, source, compile::Mode::Eval, None) { + match compile::compile(vm, source, &compile::Mode::Eval, None) { Ok(bytecode) => { debug!("Code object: {:?}", bytecode); vm.run_code_obj(bytecode, scope) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 3cb61a4a2..1b3857645 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -150,35 +150,35 @@ impl ExceptionZoo { create_type("PermissionError", &type_type, &import_error, &dict_type); ExceptionZoo { - assertion_error: assertion_error, - attribute_error: attribute_error, - base_exception_type: base_exception_type, - exception_type: exception_type, - file_not_found_error: file_not_found_error, - import_error: import_error, - index_error: index_error, - key_error: key_error, - module_not_found_error: module_not_found_error, - name_error: name_error, - not_implemented_error: not_implemented_error, - permission_error: permission_error, - runtime_error: runtime_error, - stop_iteration: stop_iteration, - syntax_error: syntax_error, - type_error: type_error, - value_error: value_error, + assertion_error, + attribute_error, + base_exception_type, + exception_type, + file_not_found_error, + import_error, + index_error, + key_error, + module_not_found_error, + name_error, + not_implemented_error, + permission_error, + runtime_error, + stop_iteration, + syntax_error, + type_error, + value_error, } } } pub fn init(context: &PyContext) { - let ref base_exception_type = context.exceptions.base_exception_type; + let base_exception_type = &context.exceptions.base_exception_type; context.set_attr( &base_exception_type, "__init__", context.new_rustfunc(exception_init), ); - let ref exception_type = context.exceptions.exception_type; + let exception_type = &context.exceptions.exception_type; context.set_attr( &exception_type, "__str__", diff --git a/vm/src/import.rs b/vm/src/import.rs index 0edd4626b..f0f331c14 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -34,7 +34,7 @@ fn import_uncached_module( let code_obj = compile::compile( vm, &source, - compile::Mode::Exec, + &compile::Mode::Exec, Some(filepath.to_str().unwrap().to_string()), )?; // trace!("Code object: {:?}", code_obj); diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 7f4c7af5e..1527e40b4 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -219,37 +219,37 @@ impl PyContext { bool_type.clone(), ); let context = PyContext { - bool_type: bool_type, - memoryview_type: memoryview_type, - bytearray_type: bytearray_type, - bytes_type: bytes_type, - code_type: code_type, - complex_type: complex_type, - classmethod_type: classmethod_type, - int_type: int_type, - float_type: float_type, - frame_type: frame_type, - staticmethod_type: staticmethod_type, - list_type: list_type, - set_type: set_type, - frozenset_type: frozenset_type, - true_value: true_value, - false_value: false_value, - tuple_type: tuple_type, - iter_type: iter_type, - dict_type: dict_type, + bool_type, + memoryview_type, + bytearray_type, + bytes_type, + code_type, + complex_type, + classmethod_type, + int_type, + float_type, + frame_type, + staticmethod_type, + list_type, + set_type, + frozenset_type, + true_value, + false_value, + tuple_type, + iter_type, + dict_type, none: none, - str_type: str_type, + str_type, object: object_type, - function_type: function_type, - super_type: super_type, - property_type: property_type, - generator_type: generator_type, - module_type: module_type, - bound_method_type: bound_method_type, - member_descriptor_type: member_descriptor_type, - type_type: type_type, - exceptions: exceptions, + function_type, + super_type, + property_type, + generator_type, + module_type, + bound_method_type, + member_descriptor_type, + type_type, + exceptions, }; objtype::init(&context); objlist::init(&context); @@ -930,43 +930,43 @@ pub enum PyObjectPayload { impl fmt::Debug for PyObjectPayload { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - &PyObjectPayload::String { ref value } => write!(f, "str \"{}\"", value), - &PyObjectPayload::Integer { ref value } => write!(f, "int {}", value), - &PyObjectPayload::Float { ref value } => write!(f, "float {}", value), - &PyObjectPayload::Complex { ref value } => write!(f, "complex {}", value), - &PyObjectPayload::Bytes { ref value } => write!(f, "bytes/bytearray {:?}", value), - &PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), - &PyObjectPayload::Sequence { elements: _ } => write!(f, "list or tuple"), - &PyObjectPayload::Dict { elements: _ } => write!(f, "dict"), - &PyObjectPayload::Set { elements: _ } => write!(f, "set"), - &PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), - &PyObjectPayload::Iterator { + PyObjectPayload::String { ref value } => write!(f, "str \"{}\"", value), + PyObjectPayload::Integer { ref value } => write!(f, "int {}", value), + PyObjectPayload::Float { ref value } => write!(f, "float {}", value), + PyObjectPayload::Complex { ref value } => write!(f, "complex {}", value), + PyObjectPayload::Bytes { ref value } => write!(f, "bytes/bytearray {:?}", value), + PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), + PyObjectPayload::Sequence { elements: _ } => write!(f, "list or tuple"), + PyObjectPayload::Dict { elements: _ } => write!(f, "dict"), + PyObjectPayload::Set { elements: _ } => write!(f, "set"), + PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), + PyObjectPayload::Iterator { position: _, iterated_obj: _, } => write!(f, "iterator"), - &PyObjectPayload::Slice { + PyObjectPayload::Slice { start: _, stop: _, step: _, } => write!(f, "slice"), - &PyObjectPayload::Code { ref code } => write!(f, "code: {:?}", code), - &PyObjectPayload::Function { .. } => write!(f, "function"), - &PyObjectPayload::Generator { .. } => write!(f, "generator"), - &PyObjectPayload::BoundMethod { + PyObjectPayload::Code { ref code } => write!(f, "code: {:?}", code), + PyObjectPayload::Function { .. } => write!(f, "function"), + PyObjectPayload::Generator { .. } => write!(f, "generator"), + PyObjectPayload::BoundMethod { ref function, ref object, } => write!(f, "bound-method: {:?} of {:?}", function, object), - &PyObjectPayload::Module { name: _, dict: _ } => write!(f, "module"), - &PyObjectPayload::Scope { scope: _ } => write!(f, "scope"), - &PyObjectPayload::None => write!(f, "None"), - &PyObjectPayload::Class { + PyObjectPayload::Module { name: _, dict: _ } => write!(f, "module"), + PyObjectPayload::Scope { scope: _ } => write!(f, "scope"), + PyObjectPayload::None => write!(f, "None"), + PyObjectPayload::Class { ref name, dict: _, mro: _, } => write!(f, "class {:?}", name), - &PyObjectPayload::Instance { dict: _ } => write!(f, "instance"), - &PyObjectPayload::RustFunction { function: _ } => write!(f, "rust function"), - &PyObjectPayload::Frame { .. } => write!(f, "frame"), + PyObjectPayload::Instance { dict: _ } => write!(f, "instance"), + PyObjectPayload::RustFunction { function: _ } => write!(f, "rust function"), + PyObjectPayload::Frame { .. } => write!(f, "frame"), } } } diff --git a/wasm/lib/src/lib.rs b/wasm/lib/src/lib.rs index 4bc0891b2..01e524c9d 100644 --- a/wasm/lib/src/lib.rs +++ b/wasm/lib/src/lib.rs @@ -123,7 +123,7 @@ fn eval(vm: &mut VirtualMachine, source: &str, vars: PyObjectRef) -> PyResult { source.push('\n'); } - let code_obj = compile::compile(vm, &source, compile::Mode::Exec, None)?; + let code_obj = compile::compile(vm, &source, &compile::Mode::Exec, None)?; vm.run_code_obj(code_obj, vars) } From 2b22cd443e92d94c4ca88f5e80b796ecb91cc790 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Mon, 4 Feb 2019 18:18:40 +0100 Subject: [PATCH 031/153] Implement clippy thoughts into rustpython_parser --- parser/src/ast.rs | 4 +-- parser/src/lexer.rs | 51 +++++++++++++++------------------------ parser/src/python.lalrpop | 6 ++++- 3 files changed, 27 insertions(+), 34 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 6a4a6f409..d28f69e2f 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -65,9 +65,9 @@ pub enum Statement { value: Expression, }, AugAssign { - target: Expression, + target: Box, op: Operator, - value: Expression, + value: Box, }, Expression { expression: Expression, diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index cf611e8a1..0d721f7d1 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -64,10 +64,7 @@ pub struct Location { impl Location { pub fn new(row: usize, column: usize) -> Self { - Location { - row: row, - column: column, - } + Location { row, column } } pub fn get_row(&self) -> usize { @@ -126,8 +123,7 @@ pub type Spanned = Result<(Location, Tok, Location), LexicalError>; pub fn make_tokenizer<'a>(source: &'a str) -> impl Iterator> + 'a { let nlh = NewlineHandler::new(source.chars()); let lch = LineContinationHandler::new(nlh); - let lexer = Lexer::new(lch); - lexer + Lexer::new(lch) } // The newline handler is an iterator which collapses different newline @@ -144,7 +140,7 @@ where { pub fn new(source: T) -> Self { let mut nlh = NewlineHandler { - source: source, + source, chr0: None, chr1: None, }; @@ -200,7 +196,7 @@ where { pub fn new(source: T) -> Self { let mut nlh = LineContinationHandler { - source: source, + source, chr0: None, chr1: None, }; @@ -313,7 +309,7 @@ where if keywords.contains_key(&name) { Ok((start_pos, keywords.remove(&name).unwrap(), end_pos)) } else { - Ok((start_pos, Tok::Name { name: name }, end_pos)) + Ok((start_pos, Tok::Name { name }, end_pos)) } } @@ -358,7 +354,7 @@ where let end_pos = self.get_pos(); let value = BigInt::from_str_radix(&value_text, radix).unwrap(); - Ok((start_pos, Tok::Int { value: value }, end_pos)) + Ok((start_pos, Tok::Int { value }, end_pos)) } fn lex_normal_number(&mut self) -> Spanned { @@ -410,7 +406,7 @@ where )) } else { let end_pos = self.get_pos(); - Ok((start_pos, Tok::Float { value: value }, end_pos)) + Ok((start_pos, Tok::Float { value }, end_pos)) } } else { // Parse trailing 'j': @@ -418,18 +414,11 @@ where self.next_char(); let end_pos = self.get_pos(); let imag = f64::from_str(&value_text).unwrap(); - Ok(( - start_pos, - Tok::Complex { - real: 0.0, - imag: imag, - }, - end_pos, - )) + Ok((start_pos, Tok::Complex { real: 0.0, imag }, end_pos)) } else { let end_pos = self.get_pos(); let value = value_text.parse::().unwrap(); - Ok((start_pos, Tok::Int { value: value }, end_pos)) + Ok((start_pos, Tok::Int { value }, end_pos)) } } } @@ -548,33 +537,33 @@ where } }; - return Ok((start_pos, tok, end_pos)); + Ok((start_pos, tok, end_pos)) } fn is_char(&self) -> bool { match self.chr0 { - Some('a'...'z') | Some('A'...'Z') | Some('_') | Some('0'...'9') => return true, - _ => return false, + Some('a'...'z') | Some('A'...'Z') | Some('_') | Some('0'...'9') => true, + _ => false, } } fn is_number(&self, radix: u32) -> bool { match radix { 2 => match self.chr0 { - Some('0'...'1') => return true, - _ => return false, + Some('0'...'1') => true, + _ => false, }, 8 => match self.chr0 { - Some('0'...'7') => return true, - _ => return false, + Some('0'...'7') => true, + _ => false, }, 10 => match self.chr0 { - Some('0'...'9') => return true, - _ => return false, + Some('0'...'9') => true, + _ => false, }, 16 => match self.chr0 { - Some('0'...'9') | Some('a'...'f') | Some('A'...'F') => return true, - _ => return false, + Some('0'...'9') | Some('a'...'f') | Some('A'...'F') => true, + _ => false, }, x => unimplemented!("Radix not implemented: {}", x), } diff --git a/parser/src/python.lalrpop b/parser/src/python.lalrpop index 4d126ee17..07b6ec3b6 100644 --- a/parser/src/python.lalrpop +++ b/parser/src/python.lalrpop @@ -105,7 +105,11 @@ ExpressionStatement: ast::LocatedStatement = { let rhs = e2.into_iter().next().unwrap(); ast::LocatedStatement { location: loc, - node: ast::Statement::AugAssign { target: expr, op: op, value: rhs }, + node: ast::Statement::AugAssign { + target: Box::new(expr), + op, + value: Box::new(rhs) + }, } }, }; From b25aab006ed0013e2bbeeb59ede940039e9423c3 Mon Sep 17 00:00:00 2001 From: Timur Date: Mon, 4 Feb 2019 22:16:49 +0300 Subject: [PATCH 032/153] Add type conversion to functions calls in `math` --- tests/snippets/math.py | 12 +++++- vm/src/stdlib/math.rs | 94 +++++++++++++++--------------------------- 2 files changed, 44 insertions(+), 62 deletions(-) diff --git a/tests/snippets/math.py b/tests/snippets/math.py index 9b31c1860..2270ff058 100644 --- a/tests/snippets/math.py +++ b/tests/snippets/math.py @@ -17,5 +17,13 @@ assert a - 3 == 1 assert -a == -4 assert +a == 4 -# import math -# print(math.cos(1.2)) +import math +assert(math.exp(2) == math.exp(2.0)) +assert(math.exp(True) == math.exp(1.0)) + +class Conversible(): + def __float__(self): + print("Converting to float now!") + return 1.1111 + +assert math.log(1.1111) == math.log(Conversible()) diff --git a/vm/src/stdlib/math.rs b/vm/src/stdlib/math.rs index 50e7dbde9..6067f98dc 100644 --- a/vm/src/stdlib/math.rs +++ b/vm/src/stdlib/math.rs @@ -14,8 +14,8 @@ use std; macro_rules! make_math_func { ( $fname:ident, $fun:ident ) => { fn $fname(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let value = objfloat::get_value(value); + arg_check!(vm, args, required = [(value, None)]); + let value = objfloat::make_float(vm, value)?; let value = value.$fun(); let value = vm.ctx.new_float(value); Ok(value) @@ -27,20 +27,20 @@ macro_rules! make_math_func { make_math_func!(math_fabs, abs); fn math_isfinite(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let value = objfloat::get_value(value).is_finite(); + arg_check!(vm, args, required = [(value, None)]); + let value = objfloat::make_float(vm, value)?.is_finite(); Ok(vm.ctx.new_bool(value)) } fn math_isinf(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let value = objfloat::get_value(value).is_infinite(); + arg_check!(vm, args, required = [(value, None)]); + let value = objfloat::make_float(vm, value)?.is_infinite(); Ok(vm.ctx.new_bool(value)) } fn math_isnan(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let value = objfloat::get_value(value).is_nan(); + arg_check!(vm, args, required = [(value, None)]); + let value = objfloat::make_float(vm, value)?.is_nan(); Ok(vm.ctx.new_bool(value)) } @@ -49,25 +49,20 @@ make_math_func!(math_exp, exp); make_math_func!(math_expm1, exp_m1); fn math_log(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(x, Some(vm.ctx.float_type()))], - optional = [(base, Some(vm.ctx.float_type()))] - ); - let x = objfloat::get_value(x); + arg_check!(vm, args, required = [(x, None)], optional = [(base, None)]); + let x = objfloat::make_float(vm, x)?; match base { None => Ok(vm.ctx.new_float(x.ln())), Some(base) => { - let base = objfloat::get_value(base); + let base = objfloat::make_float(vm, base)?; Ok(vm.ctx.new_float(x.log(base))) } } } fn math_log1p(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(x, Some(vm.ctx.float_type()))]); - let x = objfloat::get_value(x); + arg_check!(vm, args, required = [(x, None)]); + let x = objfloat::make_float(vm, x)?; Ok(vm.ctx.new_float((x + 1.0).ln())) } @@ -75,16 +70,9 @@ make_math_func!(math_log2, log2); make_math_func!(math_log10, log10); fn math_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (x, Some(vm.ctx.float_type())), - (y, Some(vm.ctx.float_type())) - ] - ); - let x = objfloat::get_value(x); - let y = objfloat::get_value(y); + arg_check!(vm, args, required = [(x, None), (y, None)]); + let x = objfloat::make_float(vm, x)?; + let y = objfloat::make_float(vm, y)?; Ok(vm.ctx.new_float(x.powf(y))) } @@ -96,32 +84,18 @@ make_math_func!(math_asin, asin); make_math_func!(math_atan, atan); fn math_atan2(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (y, Some(vm.ctx.float_type())), - (x, Some(vm.ctx.float_type())) - ] - ); - let y = objfloat::get_value(y); - let x = objfloat::get_value(x); + arg_check!(vm, args, required = [(y, None), (x, None)]); + let y = objfloat::make_float(vm, y)?; + let x = objfloat::make_float(vm, x)?; Ok(vm.ctx.new_float(y.atan2(x))) } make_math_func!(math_cos, cos); fn math_hypot(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (x, Some(vm.ctx.float_type())), - (y, Some(vm.ctx.float_type())) - ] - ); - let x = objfloat::get_value(x); - let y = objfloat::get_value(y); + arg_check!(vm, args, required = [(x, None), (y, None)]); + let x = objfloat::make_float(vm, x)?; + let y = objfloat::make_float(vm, y)?; Ok(vm.ctx.new_float(x.hypot(y))) } @@ -129,14 +103,14 @@ make_math_func!(math_sin, sin); make_math_func!(math_tan, tan); fn math_degrees(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let x = objfloat::get_value(value); + arg_check!(vm, args, required = [(value, None)]); + let x = objfloat::make_float(vm, value)?; Ok(vm.ctx.new_float(x * (180.0 / std::f64::consts::PI))) } fn math_radians(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let x = objfloat::get_value(value); + arg_check!(vm, args, required = [(value, None)]); + let x = objfloat::make_float(vm, value)?; Ok(vm.ctx.new_float(x * (std::f64::consts::PI / 180.0))) } @@ -150,8 +124,8 @@ make_math_func!(math_tanh, tanh); // Special functions: fn math_erf(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let x = objfloat::get_value(value); + arg_check!(vm, args, required = [(value, None)]); + let x = objfloat::make_float(vm, value)?; if x.is_nan() { Ok(vm.ctx.new_float(x)) @@ -161,8 +135,8 @@ fn math_erf(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn math_erfc(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let x = objfloat::get_value(value); + arg_check!(vm, args, required = [(value, None)]); + let x = objfloat::make_float(vm, value)?; if x.is_nan() { Ok(vm.ctx.new_float(x)) @@ -172,8 +146,8 @@ fn math_erfc(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn math_gamma(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let x = objfloat::get_value(value); + arg_check!(vm, args, required = [(value, None)]); + let x = objfloat::make_float(vm, value)?; if x.is_finite() { Ok(vm.ctx.new_float(gamma(x))) @@ -187,8 +161,8 @@ fn math_gamma(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn math_lgamma(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]); - let x = objfloat::get_value(value); + arg_check!(vm, args, required = [(value, None)]); + let x = objfloat::make_float(vm, value)?; if x.is_finite() { Ok(vm.ctx.new_float(ln_gamma(x))) From 804def1522af2795802b69fb8c33058c6c318165 Mon Sep 17 00:00:00 2001 From: Michael Recachinas Date: Mon, 4 Feb 2019 19:37:24 +0000 Subject: [PATCH 033/153] Replace context.new_property with context.new_str for bool.__doc__ --- vm/src/obj/objbool.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index eda7e74bc..594361532 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -31,9 +31,15 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result PyResult { @@ -80,13 +86,3 @@ fn bool_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { None => vm.context().new_bool(false), }) } - -fn bool_doc(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result { - arg_check!(vm, args, required = [(_zelf, None)]); - let s = "bool(x) -> bool - -Returns True when the argument x is true, False otherwise. -The builtins True and False are the only two instances of the class bool. -The class bool is a subclass of the class int, and cannot be subclassed."; - Ok(vm.new_str(s.to_string())) -} From 9dded0cb7ba396d3b9c2721d1d758a7a6ec7759c Mon Sep 17 00:00:00 2001 From: Michael Recachinas Date: Mon, 4 Feb 2019 19:46:58 +0000 Subject: [PATCH 034/153] Replace context.new_property with context.new_str for int.__doc__ --- vm/src/obj/objint.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 7fbb81221..793e84eca 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -425,9 +425,9 @@ fn int_bit_length(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_int(bits.to_bigint().unwrap())) } -fn int_doc(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result { - arg_check!(vm, args, required = [(_zelf, None)]); - let s = "int(x=0) -> integer +pub fn init(context: &PyContext) { + let ref int_type = context.int_type; + let int_doc = "int(x=0) -> integer int(x, base=10) -> integer Convert a number or string to an integer, or return 0 if no arguments @@ -441,11 +441,6 @@ by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal. >>> int('0b100', base=0) 4"; - Ok(vm.ctx.new_str(s.to_string())) -} - -pub fn init(context: &PyContext) { - let ref int_type = context.int_type; context.set_attr(&int_type, "__eq__", context.new_rustfunc(int_eq)); context.set_attr(&int_type, "__lt__", context.new_rustfunc(int_lt)); context.set_attr(&int_type, "__le__", context.new_rustfunc(int_le)); @@ -479,5 +474,5 @@ pub fn init(context: &PyContext) { "bit_length", context.new_rustfunc(int_bit_length), ); - context.set_attr(&int_type, "__doc__", context.new_property(int_doc)); + context.set_attr(&int_type, "__doc__", context.new_str(int_doc.to_string())); } From 64bd33e78767e34587facf970e30b06b15809fd3 Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Mon, 4 Feb 2019 15:08:13 -0500 Subject: [PATCH 035/153] Add built in range type (addresses #294) --- vm/src/builtins.rs | 11 +-- vm/src/obj/mod.rs | 1 + vm/src/obj/objiter.rs | 17 +++- vm/src/obj/objrange.rs | 191 +++++++++++++++++++++++++++++++++++++++++ vm/src/pyobject.rs | 14 +++ 5 files changed, 222 insertions(+), 12 deletions(-) create mode 100644 vm/src/obj/objrange.rs diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index d823e9205..c5292437b 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -655,15 +655,6 @@ pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } -fn builtin_range(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(range, Some(vm.ctx.int_type()))]); - let value = objint::get_value(range); - let range_elements: Vec = (0..value.to_i32().unwrap()) - .map(|num| vm.context().new_int(num.to_bigint().unwrap())) - .collect(); - Ok(vm.context().new_list(range_elements)) -} - fn builtin_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, None)]); vm.to_repr(obj) @@ -780,7 +771,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "pow", ctx.new_rustfunc(builtin_pow)); ctx.set_attr(&py_mod, "print", ctx.new_rustfunc(builtin_print)); ctx.set_attr(&py_mod, "property", ctx.property_type()); - ctx.set_attr(&py_mod, "range", ctx.new_rustfunc(builtin_range)); + ctx.set_attr(&py_mod, "range", ctx.range_type()); ctx.set_attr(&py_mod, "repr", ctx.new_rustfunc(builtin_repr)); ctx.set_attr(&py_mod, "set", ctx.set_type()); ctx.set_attr(&py_mod, "setattr", ctx.new_rustfunc(builtin_setattr)); diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index 16670fa5d..03017a30b 100644 --- a/vm/src/obj/mod.rs +++ b/vm/src/obj/mod.rs @@ -16,6 +16,7 @@ pub mod objlist; pub mod objmemory; pub mod objobject; pub mod objproperty; +pub mod objrange; pub mod objsequence; pub mod objset; pub mod objstr; diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 9844c2dfb..3796c043e 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -9,6 +9,7 @@ use super::super::vm::VirtualMachine; use super::objbool; // use super::objstr; use super::objtype; // Required for arg_check! to use isinstance +use num_bigint::ToBigInt; /* * This helper function is called at multiple places. First, it is called @@ -101,10 +102,10 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if let PyObjectPayload::Iterator { ref mut position, - iterated_obj: ref iterated_obj_ref, + iterated_obj: ref mut iterated_obj_ref, } = iter.borrow_mut().payload { - let iterated_obj = &*iterated_obj_ref.borrow_mut(); + let iterated_obj = iterated_obj_ref.borrow_mut(); match iterated_obj.payload { PyObjectPayload::Sequence { ref elements } => { if *position < elements.len() { @@ -118,6 +119,18 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(stop_iteration) } } + + PyObjectPayload::Range { ref range } => { + if let Some(int) = range.get(*position as i64) { + *position += 1; + Ok(vm.ctx.new_int(int.to_bigint().unwrap())) + } else { + let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); + let stop_iteration = + vm.new_exception(stop_iteration_type, "End of iterator".to_string()); + Err(stop_iteration) + } + } _ => { panic!("NOT IMPL"); } diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs new file mode 100644 index 000000000..c066470bd --- /dev/null +++ b/vm/src/obj/objrange.rs @@ -0,0 +1,191 @@ +use super::super::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, +}; +use super::super::vm::VirtualMachine; +use super::objint; +use super::objtype; +use num_bigint::ToBigInt; +use num_traits::ToPrimitive; + +#[derive(Debug, Copy, Clone)] +pub struct RangeType { + // Unfortunately Rust's built in range type doesn't support things like indexing + // or ranges where start > end so we need to roll our own. + pub start: i64, + pub end: i64, + pub step: i64, +} + +impl RangeType { + #[inline] + pub fn len(&self) -> usize { + ((self.end - self.start) / self.step).abs() as usize + } + + #[inline] + pub fn is_empty(&self) -> bool { + (self.start <= self.end && self.step < 0) || (self.start >= self.end && self.step > 0) + } + + #[inline] + pub fn forward(&self) -> bool { + self.start < self.end + } + + #[inline] + pub fn get(&self, index: i64) -> Option { + let result = self.start + self.step * index; + + if self.forward() && !self.is_empty() && result < self.end { + Some(result) + } else if !self.forward() && !self.is_empty() && result > self.end { + Some(result) + } else { + None + } + } +} + +pub fn init(context: &PyContext) { + let ref range_type = context.range_type; + context.set_attr(&range_type, "__new__", context.new_rustfunc(range_new)); + context.set_attr(&range_type, "__iter__", context.new_rustfunc(range_iter)); + context.set_attr(&range_type, "__len__", context.new_rustfunc(range_len)); + context.set_attr( + &range_type, + "__getitem__", + context.new_rustfunc(range_getitem), + ); +} + +fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(cls, None), (first, Some(vm.ctx.int_type()))], + optional = [ + (second, Some(vm.ctx.int_type())), + (step, Some(vm.ctx.int_type())) + ] + ); + + let start = if let Some(_) = second { + objint::get_value(first).to_i64().unwrap() + } else { + 0i64 + }; + + let end = if let Some(pyint) = second { + objint::get_value(pyint).to_i64().unwrap() + } else { + objint::get_value(first).to_i64().unwrap() + }; + + let step = if let Some(pyint) = step { + objint::get_value(pyint).to_i64().unwrap() + } else { + 1i64 + }; + + if step == 0 { + Err(vm.new_value_error("range with 0 step size".to_string())) + } else { + Ok(PyObject::new( + PyObjectPayload::Range { + range: RangeType { start, end, step }, + }, + cls.clone(), + )) + } +} + +fn range_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(range, Some(vm.ctx.range_type()))]); + + Ok(PyObject::new( + PyObjectPayload::Iterator { + position: 0, + iterated_obj: range.clone(), + }, + vm.ctx.iter_type(), + )) +} + +fn range_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); + + let len = match zelf.borrow().payload { + PyObjectPayload::Range { ref range } => range.len(), + _ => unreachable!(), + }; + + Ok(vm.ctx.new_int(len.to_bigint().unwrap())) +} + +fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.range_type())), (subscript, None)] + ); + let zrange = if let PyObjectPayload::Range { range } = zelf.borrow().payload { + range.clone() + } else { + unreachable!() + }; + + match subscript.borrow().payload { + PyObjectPayload::Integer { ref value } => { + if let Some(int) = zrange.get(value.to_i64().unwrap()) { + Ok(PyObject::new( + PyObjectPayload::Integer { + value: int.to_bigint().unwrap(), + }, + vm.ctx.int_type(), + )) + } else { + Err(vm.new_index_error("range object index out of range".to_string())) + } + } + PyObjectPayload::Slice { start, stop, step } => { + let new_start = if let Some(int) = start { + if let Some(i) = zrange.get(int.into()) { + i as i64 + } else { + zrange.start + } + } else { + zrange.start + }; + + let new_end = if let Some(int) = stop { + if let Some(i) = zrange.get(int.into()) { + i as i64 + } else { + zrange.end + } + } else { + zrange.end + }; + + let new_step = if let Some(int) = step { + (int as i64) * zrange.step + } else { + zrange.step + }; + + Ok(PyObject::new( + PyObjectPayload::Range { + range: RangeType { + start: new_start, + end: new_end, + step: new_step, + }, + }, + vm.ctx.range_type(), + )) + } + + _ => Err(vm.new_type_error("range indices must be integer or slice".to_string())), + } +} diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 7f4c7af5e..a8583b85e 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -17,6 +17,7 @@ use super::obj::objlist; use super::obj::objmemory; use super::obj::objobject; use super::obj::objproperty; +use super::obj::objrange; use super::obj::objset; use super::obj::objstr; use super::obj::objsuper; @@ -122,6 +123,7 @@ pub struct PyContext { pub staticmethod_type: PyObjectRef, pub super_type: PyObjectRef, pub str_type: PyObjectRef, + pub range_type: PyObjectRef, pub type_type: PyObjectRef, pub function_type: PyObjectRef, pub property_type: PyObjectRef, @@ -201,6 +203,7 @@ impl PyContext { let bool_type = create_type("bool", &type_type, &int_type, &dict_type); let memoryview_type = create_type("memoryview", &type_type, &object_type, &dict_type); let code_type = create_type("code", &type_type, &int_type, &dict_type); + let range_type = create_type("range", &type_type, &object_type, &dict_type); let exceptions = exceptions::ExceptionZoo::new(&type_type, &object_type, &dict_type); let none = PyObject::new( @@ -240,6 +243,7 @@ impl PyContext { dict_type: dict_type, none: none, str_type: str_type, + range_type: range_type, object: object_type, function_type: function_type, super_type: super_type, @@ -267,6 +271,7 @@ impl PyContext { objproperty::init(&context); objmemory::init(&context); objstr::init(&context); + objrange::init(&context); objsuper::init(&context); objtuple::init(&context); objiter::init(&context); @@ -317,6 +322,10 @@ impl PyContext { self.set_type.clone() } + pub fn range_type(&self) -> PyObjectRef { + self.range_type.clone() + } + pub fn frozenset_type(&self) -> PyObjectRef { self.frozenset_type.clone() } @@ -882,6 +891,9 @@ pub enum PyObjectPayload { stop: Option, step: Option, }, + Range { + range: objrange::RangeType, + }, MemoryView { obj: PyObjectRef, }, @@ -949,6 +961,7 @@ impl fmt::Debug for PyObjectPayload { stop: _, step: _, } => write!(f, "slice"), + &PyObjectPayload::Range { range: _ } => write!(f, "range"), &PyObjectPayload::Code { ref code } => write!(f, "code: {:?}", code), &PyObjectPayload::Function { .. } => write!(f, "function"), &PyObjectPayload::Generator { .. } => write!(f, "generator"), @@ -1038,6 +1051,7 @@ impl PyObject { ref stop, ref step, } => format!("", start, stop, step), + PyObjectPayload::Range { ref range } => format!("", range), PyObjectPayload::Iterator { ref position, ref iterated_obj, From c8f2515f4baa1fda0f46b176ec9e7b9a4a7f42a0 Mon Sep 17 00:00:00 2001 From: veera venky Date: Tue, 5 Feb 2019 01:42:32 +0530 Subject: [PATCH 036/153] Added object expected methods to left to implement list --- tests/snippets/whats_left_to_implement.py | 33 +++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/snippets/whats_left_to_implement.py b/tests/snippets/whats_left_to_implement.py index c67cef580..6d42679b9 100644 --- a/tests/snippets/whats_left_to_implement.py +++ b/tests/snippets/whats_left_to_implement.py @@ -848,6 +848,32 @@ tuple_expected_methods = [ 'index', ] +object_expected_methods = [ + '__repr__', + '__hash__', + '__str__', + '__getattribute__', + '__setattr__', + '__delattr__', + '__lt__', + '__le__', + '__eq__', + '__ne__', + '__gt__', + '__ge__', + '__init__', + '__new__', + '__reduce_ex__', + '__reduce__', + '__subclasshook__', + '__init_subclass__', + '__format__', + '__sizeof__', + '__dir__', + '__class__', + '__doc__' +] + not_implemented = [] for method in bool_expected_methods: @@ -957,6 +983,13 @@ for method in tuple_expected_methods: except NameError: not_implemented.append(("tuple", method)) +for method in object_expected_methods: + try: + if not hasattr(bool, method): + not_implemented.append(("object", method)) + except NameError: + not_implemented.append(("object", method)) + for r in not_implemented: print(r[0], ".", r[1]) else: From d3fedecd6178f45e973be2991deb5d6a186f2a2a Mon Sep 17 00:00:00 2001 From: veera venky Date: Tue, 5 Feb 2019 00:33:09 +0530 Subject: [PATCH 037/153] Adding 'int.__bool__' method --- tests/snippets/numbers.py | 4 ++++ vm/src/obj/objint.rs | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/tests/snippets/numbers.py b/tests/snippets/numbers.py index eae26b102..7b01f6473 100644 --- a/tests/snippets/numbers.py +++ b/tests/snippets/numbers.py @@ -8,3 +8,7 @@ class A(int): x = A(7) assert x == 7 assert type(x) is A + +assert int(2).__bool__() == True +assert int(0.5).__bool__() == False +assert int(-1).__bool__() == True diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 7f0321683..a00f1d5b0 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -90,6 +90,11 @@ impl FromPyObjectRef for BigInt { } } +fn int_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.int_type()))]); + let result = !BigInt::from_pyobj(zelf).is_zero(); + Ok(vm.ctx.new_bool(result)) +} fn int_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -527,6 +532,7 @@ pub fn init(context: &PyContext) { context.set_attr(&int_type, "__format__", context.new_rustfunc(int_format)); context.set_attr(&int_type, "__truediv__", context.new_rustfunc(int_truediv)); context.set_attr(&int_type, "__xor__", context.new_rustfunc(int_xor)); + context.set_attr(&int_type, "__bool__", context.new_rustfunc(int_bool)); context.set_attr( &int_type, "bit_length", From 9a37825f7bc4279e6cf8e801f6ff2d04b4c470bc Mon Sep 17 00:00:00 2001 From: holygits Date: Tue, 5 Feb 2019 10:29:36 +1300 Subject: [PATCH 038/153] Add OSError Fix heirarchy or existing os errors --- vm/src/exceptions.rs | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 1b3857645..305297919 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -92,6 +92,7 @@ pub struct ExceptionZoo { pub module_not_found_error: PyObjectRef, pub name_error: PyObjectRef, pub not_implemented_error: PyObjectRef, + pub os_error: PyObjectRef, pub permission_error: PyObjectRef, pub runtime_error: PyObjectRef, pub stop_iteration: PyObjectRef, @@ -107,7 +108,6 @@ impl ExceptionZoo { dict_type: &PyObjectRef, ) -> Self { // Sorted By Hierarchy then alphabetized. - let base_exception_type = create_type("BaseException", &type_type, &object_type, &dict_type); @@ -115,29 +115,19 @@ impl ExceptionZoo { let assertion_error = create_type("AssertionError", &type_type, &exception_type, &dict_type); - let attribute_error = create_type( - "AttributeError", - &type_type, - &exception_type.clone(), - &dict_type, - ); + let attribute_error = + create_type("AttributeError", &type_type, &exception_type, &dict_type); let import_error = create_type("ImportError", &type_type, &exception_type, &dict_type); - let index_error = create_type( - "IndexError", - &type_type, - &exception_type.clone(), - &dict_type, - ); - let key_error = create_type("KeyError", &type_type, &exception_type.clone(), &dict_type); - let name_error = create_type("NameError", &type_type, &exception_type.clone(), &dict_type); + let index_error = create_type("IndexError", &type_type, &exception_type, &dict_type); + let key_error = create_type("KeyError", &type_type, &exception_type, &dict_type); + let name_error = create_type("NameError", &type_type, &exception_type, &dict_type); + let os_error = create_type("OSError", &type_type, &exception_type, &dict_type); let runtime_error = create_type("RuntimeError", &type_type, &exception_type, &dict_type); let stop_iteration = create_type("StopIteration", &type_type, &exception_type, &dict_type); let syntax_error = create_type("SyntaxError", &type_type, &exception_type, &dict_type); let type_error = create_type("TypeError", &type_type, &exception_type, &dict_type); let value_error = create_type("ValueError", &type_type, &exception_type, &dict_type); - let file_not_found_error = - create_type("FileNotFoundError", &type_type, &import_error, &dict_type); let module_not_found_error = create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type); let not_implemented_error = create_type( @@ -146,8 +136,10 @@ impl ExceptionZoo { &runtime_error, &dict_type, ); - let permission_error = - create_type("PermissionError", &type_type, &import_error, &dict_type); + + let file_not_found_error = + create_type("FileNotFoundError", &type_type, &os_error, &dict_type); + let permission_error = create_type("PermissionError", &type_type, &os_error, &dict_type); ExceptionZoo { assertion_error, @@ -161,6 +153,7 @@ impl ExceptionZoo { module_not_found_error, name_error, not_implemented_error, + os_error, permission_error, runtime_error, stop_iteration, From 9ac8e55695f20e3019c8a893a8dbdffddeeffd73 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Mon, 4 Feb 2019 17:38:23 -0800 Subject: [PATCH 039/153] Represent range with BigInts --- tests/snippets/builtin_range.py | 1 + vm/src/obj/objiter.rs | 4 +-- vm/src/obj/objrange.rs | 50 ++++++++++++++++++--------------- 3 files changed, 30 insertions(+), 25 deletions(-) create mode 100644 tests/snippets/builtin_range.py diff --git a/tests/snippets/builtin_range.py b/tests/snippets/builtin_range.py new file mode 100644 index 000000000..3284fa2b4 --- /dev/null +++ b/tests/snippets/builtin_range.py @@ -0,0 +1 @@ +assert range(2**63+1)[2**63] == 9223372036854775808 diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 3796c043e..49497e9e9 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -9,7 +9,7 @@ use super::super::vm::VirtualMachine; use super::objbool; // use super::objstr; use super::objtype; // Required for arg_check! to use isinstance -use num_bigint::ToBigInt; +use num_bigint::{BigInt, ToBigInt}; /* * This helper function is called at multiple places. First, it is called @@ -121,7 +121,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } PyObjectPayload::Range { ref range } => { - if let Some(int) = range.get(*position as i64) { + if let Some(int) = range.get(BigInt::from(*position)) { *position += 1; Ok(vm.ctx.new_int(int.to_bigint().unwrap())) } else { diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index c066470bd..5924cd048 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -4,27 +4,31 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objint; use super::objtype; -use num_bigint::ToBigInt; -use num_traits::ToPrimitive; +use num_bigint::{BigInt, ToBigInt}; +use num_traits::{One, Signed, ToPrimitive, Zero}; -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub struct RangeType { // Unfortunately Rust's built in range type doesn't support things like indexing // or ranges where start > end so we need to roll our own. - pub start: i64, - pub end: i64, - pub step: i64, + pub start: BigInt, + pub end: BigInt, + pub step: BigInt, } impl RangeType { #[inline] pub fn len(&self) -> usize { - ((self.end - self.start) / self.step).abs() as usize + ((self.end.clone() - self.start.clone()) / self.step.clone()) + .abs() + .to_usize() + .unwrap() } #[inline] pub fn is_empty(&self) -> bool { - (self.start <= self.end && self.step < 0) || (self.start >= self.end && self.step > 0) + (self.start <= self.end && self.step.is_negative()) + || (self.start >= self.end && self.step.is_positive()) } #[inline] @@ -33,8 +37,8 @@ impl RangeType { } #[inline] - pub fn get(&self, index: i64) -> Option { - let result = self.start + self.step * index; + pub fn get(&self, index: BigInt) -> Option { + let result = self.start.clone() + self.step.clone() * index; if self.forward() && !self.is_empty() && result < self.end { Some(result) @@ -70,24 +74,24 @@ fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let start = if let Some(_) = second { - objint::get_value(first).to_i64().unwrap() + objint::get_value(first) } else { - 0i64 + BigInt::zero() }; let end = if let Some(pyint) = second { - objint::get_value(pyint).to_i64().unwrap() + objint::get_value(pyint) } else { - objint::get_value(first).to_i64().unwrap() + objint::get_value(first) }; let step = if let Some(pyint) = step { - objint::get_value(pyint).to_i64().unwrap() + objint::get_value(pyint) } else { - 1i64 + BigInt::one() }; - if step == 0 { + if step.is_zero() { Err(vm.new_value_error("range with 0 step size".to_string())) } else { Ok(PyObject::new( @@ -128,7 +132,7 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(zelf, Some(vm.ctx.range_type())), (subscript, None)] ); - let zrange = if let PyObjectPayload::Range { range } = zelf.borrow().payload { + let zrange = if let PyObjectPayload::Range { ref range } = zelf.borrow().payload { range.clone() } else { unreachable!() @@ -136,7 +140,7 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match subscript.borrow().payload { PyObjectPayload::Integer { ref value } => { - if let Some(int) = zrange.get(value.to_i64().unwrap()) { + if let Some(int) = zrange.get(value.clone()) { Ok(PyObject::new( PyObjectPayload::Integer { value: int.to_bigint().unwrap(), @@ -150,17 +154,17 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { PyObjectPayload::Slice { start, stop, step } => { let new_start = if let Some(int) = start { if let Some(i) = zrange.get(int.into()) { - i as i64 + i } else { - zrange.start + zrange.start.clone() } } else { - zrange.start + zrange.start.clone() }; let new_end = if let Some(int) = stop { if let Some(i) = zrange.get(int.into()) { - i as i64 + i } else { zrange.end } From 5cad1b0215b580da0119149129200733916ef355 Mon Sep 17 00:00:00 2001 From: holygits Date: Tue, 5 Feb 2019 16:39:22 +1300 Subject: [PATCH 040/153] Revert 'file_no' -> 'fileno' attr naming --- vm/src/stdlib/io.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 6831b8bbc..49bf7e994 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -116,7 +116,7 @@ fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let file_no = os::os_open(vm, PyFuncArgs::new(args, vec![]))?; vm.ctx.set_attr(&file_io, "name", name.clone()); - vm.ctx.set_attr(&file_io, "file_no", file_no); + vm.ctx.set_attr(&file_io, "fileno", file_no); Ok(vm.get_none()) } None => Err(vm.new_type_error(format!("invalid mode {}", rust_mode))), @@ -161,7 +161,7 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let py_length = vm.invoke(len_method.unwrap(), PyFuncArgs::default()); let length = objint::get_value(&py_length.unwrap()).to_u64().unwrap(); - let file_no = file_io.get_attr("file_no").unwrap(); + let file_no = file_io.get_attr("fileno").unwrap(); let raw_fd = objint::get_value(&file_no).to_i32().unwrap(); //unsafe block - creates file handle from the UNIX file descriptor @@ -184,7 +184,7 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let new_handle = f.into_inner().into_raw_fd().to_bigint(); vm.ctx - .set_attr(&file_io, "file_no", vm.ctx.new_int(new_handle.unwrap())); + .set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); Ok(vm.get_none()) } @@ -195,7 +195,7 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(file_io, None), (obj, Some(vm.ctx.bytes_type()))] ); - let file_no = file_io.get_attr("file_no").unwrap(); + let file_no = file_io.get_attr("fileno").unwrap(); let raw_fd = objint::get_value(&file_no).to_i32().unwrap(); //unsafe block - creates file handle from the UNIX file descriptor @@ -210,7 +210,7 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //reset raw fd on the FileIO object let new_handle = handle.into_raw_fd().to_bigint(); vm.ctx - .set_attr(&file_io, "file_no", vm.ctx.new_int(new_handle.unwrap())); + .set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); //return number of bytes written Ok(vm.ctx.new_int(len.to_bigint().unwrap())) From 608a13eabbe6be33811be6fa261cba30ad27688d Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Mon, 4 Feb 2019 23:26:16 -0500 Subject: [PATCH 041/153] Add comparison operations for tuple --- vm/src/obj/objtuple.rs | 94 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index e3128e99d..864ce00fb 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -4,12 +4,100 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objbool; use super::objint; -use super::objsequence::{get_elements, get_item, seq_equal, seq_mul}; +use super::objsequence::{get_elements, get_item, seq_equal, seq_mul, seq_ge, seq_gt, seq_le, seq_lt}; use super::objstr; use super::objtype; use num_bigint::ToBigInt; use std::hash::{Hash, Hasher}; +fn tuple_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_lt(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + +fn tuple_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_gt(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + +fn tuple_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_ge(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + +fn tuple_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)] + ); + + let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) { + let zelf = get_elements(zelf); + let other = get_elements(other); + seq_le(vm, &zelf, &other)? + } else { + return Err(vm.new_type_error(format!( + "Cannot compare {} and {} using '<'", + zelf.borrow(), + other.borrow() + ))); + }; + + Ok(vm.ctx.new_bool(result)) +} + fn tuple_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -205,4 +293,8 @@ pub fn init(context: &PyContext) { context.set_attr(&tuple_type, "__mul__", context.new_rustfunc(tuple_mul)); context.set_attr(&tuple_type, "__repr__", context.new_rustfunc(tuple_repr)); context.set_attr(&tuple_type, "count", context.new_rustfunc(tuple_count)); + context.set_attr(&tuple_type, "__lt__", context.new_rustfunc(tuple_lt)); + context.set_attr(&tuple_type, "__le__", context.new_rustfunc(tuple_le)); + context.set_attr(&tuple_type, "__gt__", context.new_rustfunc(tuple_gt)); + context.set_attr(&tuple_type, "__ge__", context.new_rustfunc(tuple_ge)); } From 696bf8e9a93f96461ca663c01fe97c6581fdd3d5 Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Mon, 4 Feb 2019 23:27:45 -0500 Subject: [PATCH 042/153] rustfmt --- vm/src/obj/objtuple.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 864ce00fb..233faad20 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -4,7 +4,9 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objbool; use super::objint; -use super::objsequence::{get_elements, get_item, seq_equal, seq_mul, seq_ge, seq_gt, seq_le, seq_lt}; +use super::objsequence::{ + get_elements, get_item, seq_equal, seq_ge, seq_gt, seq_le, seq_lt, seq_mul, +}; use super::objstr; use super::objtype; use num_bigint::ToBigInt; From fb58ca3e6cf784203ec7a7f766f59667d679d61c Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Mon, 4 Feb 2019 23:31:39 -0500 Subject: [PATCH 043/153] Add comparison tests for tuple --- tests/snippets/tuple.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/snippets/tuple.py b/tests/snippets/tuple.py index 572ecb5dd..56a61f827 100644 --- a/tests/snippets/tuple.py +++ b/tests/snippets/tuple.py @@ -12,3 +12,6 @@ assert x * 3 == (1, 2, 1, 2, 1, 2) # assert 3 * x == (1, 2, 1, 2, 1, 2) assert x * 0 == () assert x * -1 == () # integers less than zero treated as 0 + +assert y < x, "tuple __lt__ failed" +assert x > y, "tuple __gt__ failed" From 7446a52c1847d6386b9b3a4fa5f2b4279bd34e01 Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Tue, 5 Feb 2019 00:27:36 -0500 Subject: [PATCH 044/153] Fix error messages in tuple/list comparisons --- vm/src/obj/objlist.rs | 6 +++--- vm/src/obj/objtuple.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 0df789385..4175b6005 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -109,7 +109,7 @@ fn list_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { seq_gt(vm, &zelf, &other)? } else { return Err(vm.new_type_error(format!( - "Cannot compare {} and {} using '<'", + "Cannot compare {} and {} using '>'", zelf.borrow(), other.borrow() ))); @@ -131,7 +131,7 @@ fn list_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { seq_ge(vm, &zelf, &other)? } else { return Err(vm.new_type_error(format!( - "Cannot compare {} and {} using '<'", + "Cannot compare {} and {} using '>='", zelf.borrow(), other.borrow() ))); @@ -153,7 +153,7 @@ fn list_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { seq_le(vm, &zelf, &other)? } else { return Err(vm.new_type_error(format!( - "Cannot compare {} and {} using '<'", + "Cannot compare {} and {} using '<='", zelf.borrow(), other.borrow() ))); diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 233faad20..dc10020f4 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -47,7 +47,7 @@ fn tuple_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { seq_gt(vm, &zelf, &other)? } else { return Err(vm.new_type_error(format!( - "Cannot compare {} and {} using '<'", + "Cannot compare {} and {} using '>'", zelf.borrow(), other.borrow() ))); @@ -69,7 +69,7 @@ fn tuple_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { seq_ge(vm, &zelf, &other)? } else { return Err(vm.new_type_error(format!( - "Cannot compare {} and {} using '<'", + "Cannot compare {} and {} using '>='", zelf.borrow(), other.borrow() ))); @@ -91,7 +91,7 @@ fn tuple_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { seq_le(vm, &zelf, &other)? } else { return Err(vm.new_type_error(format!( - "Cannot compare {} and {} using '<'", + "Cannot compare {} and {} using '<='", zelf.borrow(), other.borrow() ))); From ebbd3d9f2264d74c7873385f305eb408a2b71643 Mon Sep 17 00:00:00 2001 From: Vladimir Karamyshev Date: Tue, 5 Feb 2019 10:17:43 +0300 Subject: [PATCH 045/153] Add error handling for objsequence and objstr index overflow --- tests/snippets/index_overflow.py | 26 ++++++++++++++++++++++++++ vm/src/obj/objsequence.rs | 23 +++++++++++++---------- vm/src/obj/objstr.rs | 20 +++++++++++++------- 3 files changed, 52 insertions(+), 17 deletions(-) create mode 100644 tests/snippets/index_overflow.py diff --git a/tests/snippets/index_overflow.py b/tests/snippets/index_overflow.py new file mode 100644 index 000000000..fa0d80b9a --- /dev/null +++ b/tests/snippets/index_overflow.py @@ -0,0 +1,26 @@ +import sys + + +def expect_cannot_fit_index_error(s, index): + try: + s[index] + except IndexError: + pass + # TODO: Replace current except block with commented + # after solving https://github.com/RustPython/RustPython/issues/322 + # except IndexError as error: + # assert str(error) == "cannot fit 'int' into an index-sized integer" + else: + assert False + + +MAX_INDEX = sys.maxsize + 1 +MIN_INDEX = -(MAX_INDEX + 1) + +test_str = "test" +expect_cannot_fit_index_error(test_str, MIN_INDEX) +expect_cannot_fit_index_error(test_str, MAX_INDEX) + +test_list = [0, 1, 2, 3] +expect_cannot_fit_index_error(test_list, MIN_INDEX) +expect_cannot_fit_index_error(test_list, MAX_INDEX) diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 4dbd0026f..ff3ed6a01 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -76,17 +76,20 @@ pub fn get_item( subscript: PyObjectRef, ) -> PyResult { match &(subscript.borrow()).payload { - PyObjectPayload::Integer { value } => { - let value = value.to_i32().unwrap(); - let pos_index = elements.to_vec().get_pos(value); - if pos_index < elements.len() { - let obj = elements[pos_index].clone(); - Ok(obj) - } else { - let index_error = vm.context().exceptions.index_error.clone(); - Err(vm.new_exception(index_error, "Index out of bounds!".to_string())) + PyObjectPayload::Integer { value } => match value.to_i32() { + Some(value) => { + let pos_index = elements.to_vec().get_pos(value); + if pos_index < elements.len() { + let obj = elements[pos_index].clone(); + Ok(obj) + } else { + Err(vm.new_index_error("Index out of bounds!".to_string())) + } } - } + None => { + Err(vm.new_index_error("cannot fit 'int' into an index-sized integer".to_string())) + } + }, PyObjectPayload::Slice { start: _, stop: _, diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index ce55bb940..a20e5b517 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -1015,13 +1015,19 @@ fn to_graphemes>(value: S) -> Vec { pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResult { if objtype::isinstance(&b, &vm.ctx.int_type()) { - let pos = objint::get_value(&b).to_i32().unwrap(); - let graphemes = to_graphemes(value); - let idx = graphemes.get_pos(pos); - graphemes - .get(idx) - .map(|c| vm.new_str(c.to_string())) - .ok_or(vm.new_index_error("string index out of range".to_string())) + match objint::get_value(&b).to_i32() { + Some(pos) => { + let graphemes = to_graphemes(value); + let idx = graphemes.get_pos(pos); + graphemes + .get(idx) + .map(|c| vm.new_str(c.to_string())) + .ok_or(vm.new_index_error("string index out of range".to_string())) + } + None => { + Err(vm.new_index_error("cannot fit 'int' into an index-sized integer".to_string())) + } + } } else { match &(*b.borrow()).payload { &PyObjectPayload::Slice { From ca815baeaf5710c9ac407eba7b9bd159d2cdd5f0 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Tue, 5 Feb 2019 20:57:06 +1100 Subject: [PATCH 046/153] Factored out unix dependency --- vm/src/exceptions.rs | 3 +++ vm/src/stdlib/io.rs | 35 ++++++++++++++-------------- vm/src/stdlib/os.rs | 54 +++++++++++++++++++++++++++++++++++++++----- vm/src/vm.rs | 5 ++++ 4 files changed, 74 insertions(+), 23 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 1b3857645..53f1e8a92 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -92,6 +92,7 @@ pub struct ExceptionZoo { pub module_not_found_error: PyObjectRef, pub name_error: PyObjectRef, pub not_implemented_error: PyObjectRef, + pub os_error: PyObjectRef, pub permission_error: PyObjectRef, pub runtime_error: PyObjectRef, pub stop_iteration: PyObjectRef, @@ -135,6 +136,7 @@ impl ExceptionZoo { let syntax_error = create_type("SyntaxError", &type_type, &exception_type, &dict_type); let type_error = create_type("TypeError", &type_type, &exception_type, &dict_type); let value_error = create_type("ValueError", &type_type, &exception_type, &dict_type); + let os_error = create_type("OSError", &type_type, &exception_type.clone(), &dict_type); let file_not_found_error = create_type("FileNotFoundError", &type_type, &import_error, &dict_type); @@ -167,6 +169,7 @@ impl ExceptionZoo { syntax_error, type_error, value_error, + os_error, } } } diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 49bf7e994..631ba5c64 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -2,23 +2,23 @@ * I/O core tools. */ +//library imports use std::collections::HashSet; - -use std::io::prelude::*; -use std::os::unix::io::{FromRawFd, IntoRawFd}; - use std::fs::File; +use std::io::prelude::*; use std::io::BufReader; +//3rd party imports +use num_bigint::ToBigInt; +use num_traits::ToPrimitive; + +//custom imports use super::super::obj::objbytes; use super::super::obj::objint; use super::super::obj::objstr; use super::super::obj::objtype; use super::os; -use num_bigint::ToBigInt; -use num_traits::ToPrimitive; - use super::super::pyobject::{ AttributeProtocol, BufferProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, @@ -117,6 +117,9 @@ fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm.ctx.set_attr(&file_io, "name", name.clone()); vm.ctx.set_attr(&file_io, "fileno", file_no); + vm.ctx.set_attr(&file_io, "closefd", vm.new_bool(false)); + vm.ctx.set_attr(&file_io, "closed", vm.new_bool(false)); + Ok(vm.get_none()) } None => Err(vm.new_type_error(format!("invalid mode {}", rust_mode))), @@ -164,10 +167,8 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let file_no = file_io.get_attr("fileno").unwrap(); let raw_fd = objint::get_value(&file_no).to_i32().unwrap(); - //unsafe block - creates file handle from the UNIX file descriptor - //raw_fd is supported on UNIX only. This will need to be extended - //to support windows - i.e. raw file_handles - let handle = unsafe { File::from_raw_fd(raw_fd) }; + //extract unix file descriptor. + let handle = os::rust_file(raw_fd); let mut f = handle.take(length); match obj.borrow_mut().payload { @@ -182,9 +183,9 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { _ => {} }; - let new_handle = f.into_inner().into_raw_fd().to_bigint(); + let updated = os::raw_file_number(f.into_inner()).to_bigint(); vm.ctx - .set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); + .set_attr(&file_io, "fileno", vm.ctx.new_int(updated.unwrap())); Ok(vm.get_none()) } @@ -201,16 +202,16 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //unsafe block - creates file handle from the UNIX file descriptor //raw_fd is supported on UNIX only. This will need to be extended //to support windows - i.e. raw file_handles - let mut handle = unsafe { File::from_raw_fd(raw_fd) }; + let mut handle = os::rust_file(raw_fd); match obj.borrow_mut().payload { PyObjectPayload::Bytes { ref mut value } => { match handle.write(&value[..]) { Ok(len) => { //reset raw fd on the FileIO object - let new_handle = handle.into_raw_fd().to_bigint(); + let updated = os::raw_file_number(handle).to_bigint(); vm.ctx - .set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap())); + .set_attr(&file_io, "fileno", vm.ctx.new_int(updated.unwrap())); //return number of bytes written Ok(vm.ctx.new_int(len.to_bigint().unwrap())) @@ -279,7 +280,7 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut raw_modes = HashSet::new(); - // Add some books. + //add raw modes raw_modes.insert("a".to_string()); raw_modes.insert("r".to_string()); raw_modes.insert("x".to_string()); diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 0c6bab2ee..ded5adc7a 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -1,18 +1,61 @@ +//library imports +use std::fs::File; use std::fs::OpenOptions; use std::io::ErrorKind; -use std::os::unix::io::IntoRawFd; +//3rd party imports use num_bigint::ToBigInt; use num_traits::cast::ToPrimitive; +//custom imports use super::super::obj::objint; use super::super::obj::objstr; use super::super::obj::objtype; use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; - use super::super::vm::VirtualMachine; +#[cfg(target_family = "unix")] +pub fn raw_file_number(handle: File) -> i32 { + use std::os::unix::io::IntoRawFd; + + handle.into_raw_fd() +} + +#[cfg(target_family = "unix")] +pub fn rust_file(raw_fileno: i32) -> File { + use std::os::unix::io::FromRawFd; + + unsafe { File::from_raw_fd(raw_fileno) } +} + +#[cfg(target_family = "windows")] +pub fn rust_file(handle: File) -> i32 { + use std::os::windows::io::IntoRawHandle; + + handle.into_raw_handle() +} + +#[cfg(target_family = "windows")] +pub fn rust_file(raw_fileno: i32) -> File { + use std::os::unix::io::FromRawHandle; + + unsafe { File::from_raw_handle(raw_fileno) } +} + +pub fn os_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(fileno, Some(vm.ctx.int_type()))]); + + let raw_fileno = objint::get_value(&fileno); + + //The File type automatically closes when it goes out of scope. + //To enable us to close these file desciptors (and hence prevent leaks) + //we seek to create the relevant File and simply let it pass out of scope! + rust_file(raw_fileno.to_i32().unwrap()); + + Ok(vm.get_none()) +} + pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -43,11 +86,8 @@ pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { _ => vm.new_value_error("Unhandled file IO error".to_string()), })?; - //raw_fd is supported on UNIX only. This will need to be extended - //to support windows - i.e. raw file_handles Ok(vm.ctx.new_int( - handle - .into_raw_fd() + raw_file_number(handle) .to_bigint() .expect("Invalid file descriptor"), )) @@ -56,6 +96,8 @@ pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let py_mod = ctx.new_module(&"io".to_string(), ctx.new_scope(None)); ctx.set_attr(&py_mod, "open", ctx.new_rustfunc(os_open)); + ctx.set_attr(&py_mod, "close", ctx.new_rustfunc(os_close)); + ctx.set_attr(&py_mod, "O_RDONLY", ctx.new_int(0.to_bigint().unwrap())); ctx.set_attr(&py_mod, "O_WRONLY", ctx.new_int(1.to_bigint().unwrap())); ctx.set_attr(&py_mod, "O_RDWR", ctx.new_int(2.to_bigint().unwrap())); diff --git a/vm/src/vm.rs b/vm/src/vm.rs index db4396dc9..061c99605 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -100,6 +100,11 @@ impl VirtualMachine { self.new_exception(type_error, msg) } + pub fn new_os_error(&mut self, msg: String) -> PyObjectRef { + let os_error = self.ctx.exceptions.os_error.clone(); + self.new_exception(os_error, msg) + } + /// Create a new python ValueError object. Useful for raising errors from /// python functions implemented in rust. pub fn new_value_error(&mut self, msg: String) -> PyObjectRef { From ea4059a18fd93de86dcd1f8534e30d0c5edf4cd9 Mon Sep 17 00:00:00 2001 From: veera venky Date: Tue, 5 Feb 2019 16:04:01 +0530 Subject: [PATCH 047/153] Refactored: whats_left_to_implement.py --- tests/snippets/whats_left_to_implement.py | 202 +++++----------------- 1 file changed, 42 insertions(+), 160 deletions(-) diff --git a/tests/snippets/whats_left_to_implement.py b/tests/snippets/whats_left_to_implement.py index 6d42679b9..b6c8cbe42 100644 --- a/tests/snippets/whats_left_to_implement.py +++ b/tests/snippets/whats_left_to_implement.py @@ -1,4 +1,7 @@ -bool_expected_methods = [ +expected_methods = [] + +# TODO: using tuple could have been better +expected_methods.append({'name': 'bool', 'methods': [ '__abs__', '__add__', '__and__', @@ -69,9 +72,8 @@ bool_expected_methods = [ 'numerator', 'real', 'to_bytes', -] - -bytearray_expected_methods = [ +], 'type': bool}) +expected_methods.append({'name': 'bytearray', 'type': bytearray, 'methods': [ '__add__', '__alloc__', '__class__', @@ -157,9 +159,8 @@ bytearray_expected_methods = [ 'translate', 'upper', 'zfill', -] - -bytes_expected_methods = [ +]}) +expected_methods.append({'name': 'bytes', 'type': bytes, 'methods': [ '__add__', '__class__', '__contains__', @@ -233,9 +234,8 @@ bytes_expected_methods = [ 'translate', 'upper', 'zfill', -] - -complex_expected_methods = [ +]}) +expected_methods.append({'name': 'complex', 'type': complex, 'methods': [ '__abs__', '__add__', '__bool__', @@ -285,9 +285,8 @@ complex_expected_methods = [ 'conjugate', 'imag', 'real', -] - -dict_expected_methods = [ +]}) +expected_methods.append({'name': 'dict', 'type': dict, 'methods': [ '__class__', '__contains__', '__delattr__', @@ -328,9 +327,8 @@ dict_expected_methods = [ 'setdefault', 'update', 'values', -] - -float_expected_methods = [ +]}) +expected_methods.append({'name': 'float','type':float,'methods':[ '__abs__', '__add__', '__bool__', @@ -388,9 +386,8 @@ float_expected_methods = [ 'imag', 'is_integer', 'real', -] - -frozenset_expected_methods = [ +]}) +expected_methods.append({'name': 'frozenset','type':frozenset, 'methods': [ '__and__', '__class__', '__contains__', @@ -433,9 +430,8 @@ frozenset_expected_methods = [ 'issuperset', 'symmetric_difference', 'union', -] - -int_expected_methods = [ +]}) +expected_methods.append({'name': 'int', 'type':int, 'methods': [ '__abs__', '__add__', '__and__', @@ -506,9 +502,8 @@ int_expected_methods = [ 'numerator', 'real', 'to_bytes', -] - -iter_expected_methods = [ +]}) +expected_methods.append({'name': 'iter','type':iter,'methods':[ '__class__', '__delattr__', '__dir__', @@ -536,9 +531,8 @@ iter_expected_methods = [ '__sizeof__', '__str__', '__subclasshook__', -] - -list_expected_methods = [ +]}) +expected_methods.append({'name': 'list','type':list,'methods':[ '__add__', '__class__', '__contains__', @@ -585,9 +579,8 @@ list_expected_methods = [ 'remove', 'reverse', 'sort', -] - -memoryview_expected_methods = [ +]}) +expected_methods.append({'name': 'memoryview','type':memoryview,'methods':[ '__class__', '__delattr__', '__delitem__', @@ -634,9 +627,8 @@ memoryview_expected_methods = [ 'suboffsets', 'tobytes', 'tolist', -] - -range_expected_methods = [ +]}) +expected_methods.append({'name': 'range','type':range,'methods':[ '__bool__', '__class__', '__contains__', @@ -671,9 +663,8 @@ range_expected_methods = [ 'start', 'step', 'stop', -] - -set_expected_methods = [ +]}) +expected_methods.append({'name': 'set','type':set,'methods':[ '__and__', '__class__', '__contains__', @@ -729,9 +720,8 @@ set_expected_methods = [ 'symmetric_difference_update', 'union', 'update', -] - -string_expected_methods = [ +]}) +expected_methods.append({'name': 'string','type':str,'methods':[ '__add__', '__class__', '__contains__', @@ -810,9 +800,8 @@ string_expected_methods = [ 'translate', 'upper', 'zfill' -] - -tuple_expected_methods = [ +]}) +expected_methods.append({'name': 'tuple','type':tuple, 'methods': [ '__add__', '__class__', '__contains__', @@ -846,9 +835,8 @@ tuple_expected_methods = [ '__subclasshook__', 'count', 'index', -] - -object_expected_methods = [ +]}) +expected_methods.append({'name': 'object', 'type':object, 'methods':[ '__repr__', '__hash__', '__str__', @@ -872,123 +860,17 @@ object_expected_methods = [ '__dir__', '__class__', '__doc__' -] +]}) not_implemented = [] -for method in bool_expected_methods: - try: - if not hasattr(bool, method): - not_implemented.append(("bool", method)) - except NameError: - not_implemented.append(("bool", method)) - -for method in bytearray_expected_methods: - try: - if not hasattr(bytearray(), method): - not_implemented.append(("bytearray", method)) - except NameError: - not_implemented.append(("bytearray", method)) - -for method in bytes_expected_methods: - try: - if not hasattr(bytes, method): - not_implemented.append(("bytes", method)) - except NameError: - not_implemented.append(("bytes", method)) - -for method in complex_expected_methods: - try: - if not hasattr(complex, method): - not_implemented.append(("complex", method)) - except NameError: - not_implemented.append(("complex", method)) - -for method in dict_expected_methods: - try: - if not hasattr(dict, method): - not_implemented.append(("dict", method)) - except NameError: - not_implemented.append(("dict", method)) - -for method in float_expected_methods: - try: - if not hasattr(float, method): - not_implemented.append(("float", method)) - except NameError: - not_implemented.append(("float", method)) - -for method in frozenset_expected_methods: -# TODO: uncomment this when frozenset is implemented -# try: -# if not hasattr(frozenset, method): -# not_implemented.append(("frozenset", method)) -# except NameError: - not_implemented.append(("frozenset", method)) - -for method in int_expected_methods: - try: - if not hasattr(int, method): - not_implemented.append(("int", method)) - except NameError: - not_implemented.append(("int", method)) - -for method in iter_expected_methods: - try: - if not hasattr(iter([]), method): - not_implemented.append(("iter", method)) - except NameError: - not_implemented.append(("iter", method)) - -for method in list_expected_methods: - try: - if not hasattr(list, method): - not_implemented.append(("list", method)) - except NameError: - not_implemented.append(("list", method)) - -for method in memoryview_expected_methods: -# TODO: uncomment this when memoryview is implemented -# try: -# if not hasattr(memoryview, method): -# not_implemented.append(("memoryview", method)) -# except NameError: - not_implemented.append(("memoryview", method)) - -for method in range_expected_methods: - try: - if not hasattr(range, method): - not_implemented.append(("range", method)) - except NameError: - not_implemented.append(("range", method)) - -for method in set_expected_methods: - try: - if not hasattr(set, method): - not_implemented.append(("set", method)) - except NameError: - not_implemented.append(("set", method)) - -for method in string_expected_methods: - try: - if not hasattr(str, method): - not_implemented.append(("string", method)) - except NameError: - not_implemented.append(("string", method)) - -for method in tuple_expected_methods: - try: - if not hasattr(tuple, method): - not_implemented.append(("tuple", method)) - except NameError: - not_implemented.append(("tuple", method)) - -for method in object_expected_methods: - try: - if not hasattr(bool, method): - not_implemented.append(("object", method)) - except NameError: - not_implemented.append(("object", method)) +for item in expected_methods: + for method in item['methods']: + try: + if not hasattr(item['type'], method): + not_implemented.append((item['name'], method)) + except NameError: + not_implemented.append((item['name'], method)) for r in not_implemented: print(r[0], ".", r[1]) From 0daa03875c4c7b2cfd538e2ebe4e7dcf017c28a7 Mon Sep 17 00:00:00 2001 From: Ryan Liddle Date: Tue, 5 Feb 2019 22:14:14 +1100 Subject: [PATCH 048/153] windows fixes for os module --- vm/src/stdlib/os.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index ded5adc7a..41d9a84c6 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -30,17 +30,18 @@ pub fn rust_file(raw_fileno: i32) -> File { } #[cfg(target_family = "windows")] -pub fn rust_file(handle: File) -> i32 { +pub fn raw_file_number(handle: File) -> i32 { use std::os::windows::io::IntoRawHandle; - handle.into_raw_handle() + handle.into_raw_handle() as i32 } #[cfg(target_family = "windows")] pub fn rust_file(raw_fileno: i32) -> File { - use std::os::unix::io::FromRawHandle; + use std::os::windows::io::FromRawHandle; + use std::ffi::c_void; - unsafe { File::from_raw_handle(raw_fileno) } + unsafe { File::from_raw_handle(raw_fileno as *mut c_void)} } pub fn os_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From eca75b36113eb5de2b30ccef74ce0084366d350e Mon Sep 17 00:00:00 2001 From: rmliddle Date: Tue, 5 Feb 2019 22:18:24 +1100 Subject: [PATCH 049/153] Comment and formatted on windows rust_file call --- vm/src/stdlib/os.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 41d9a84c6..499ae197b 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -38,10 +38,14 @@ pub fn raw_file_number(handle: File) -> i32 { #[cfg(target_family = "windows")] pub fn rust_file(raw_fileno: i32) -> File { + use std::ffi::c_void; use std::os::windows::io::FromRawHandle; - use std::ffi::c_void; - unsafe { File::from_raw_handle(raw_fileno as *mut c_void)} + //TODO: This is untested and (very) unsafe handling or + //raw pointers - This should be patched as a matter of + //urgently patched by comparison to the cpython handling of + //the equivalent fileno fields for windows + unsafe { File::from_raw_handle(raw_fileno as *mut c_void) } } pub fn os_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From 73ae085ed845100e82ee64641f38da46790edeaa Mon Sep 17 00:00:00 2001 From: lausek Date: Tue, 5 Feb 2019 12:47:30 +0100 Subject: [PATCH 050/153] implemented rounding funcs for int (#304) --- vm/src/obj/objint.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index a00f1d5b0..0eda177cf 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -294,6 +294,21 @@ fn int_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } +fn int_round(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(i, Some(vm.ctx.int_type()))], + optional = [(_precision, None)] + ); + Ok(vm.ctx.new_int(get_value(i))) +} + +fn int_ceil_floor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(i, Some(vm.ctx.int_type()))]); + Ok(vm.ctx.new_int(get_value(i))) +} + fn int_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -512,6 +527,9 @@ pub fn init(context: &PyContext) { context.set_attr(&int_type, "__and__", context.new_rustfunc(int_and)); context.set_attr(&int_type, "__divmod__", context.new_rustfunc(int_divmod)); context.set_attr(&int_type, "__float__", context.new_rustfunc(int_float)); + context.set_attr(&int_type, "__round__", context.new_rustfunc(int_round)); + context.set_attr(&int_type, "__ceil__", context.new_rustfunc(int_ceil_floor)); + context.set_attr(&int_type, "__floor__", context.new_rustfunc(int_ceil_floor)); context.set_attr( &int_type, "__floordiv__", From 86e94f587b62f22a072c0c7c6f2619ae0f62fb78 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Tue, 5 Feb 2019 22:47:33 +1100 Subject: [PATCH 051/153] Comment fix and use of i64 instead of i32 --- vm/src/stdlib/io.rs | 4 ++-- vm/src/stdlib/os.rs | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 631ba5c64..765e899a8 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -165,7 +165,7 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let length = objint::get_value(&py_length.unwrap()).to_u64().unwrap(); let file_no = file_io.get_attr("fileno").unwrap(); - let raw_fd = objint::get_value(&file_no).to_i32().unwrap(); + let raw_fd = objint::get_value(&file_no).to_i64().unwrap(); //extract unix file descriptor. let handle = os::rust_file(raw_fd); @@ -197,7 +197,7 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let file_no = file_io.get_attr("fileno").unwrap(); - let raw_fd = objint::get_value(&file_no).to_i32().unwrap(); + let raw_fd = objint::get_value(&file_no).to_i64().unwrap(); //unsafe block - creates file handle from the UNIX file descriptor //raw_fd is supported on UNIX only. This will need to be extended diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 499ae197b..18a043407 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -16,35 +16,35 @@ use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeP use super::super::vm::VirtualMachine; #[cfg(target_family = "unix")] -pub fn raw_file_number(handle: File) -> i32 { +pub fn raw_file_number(handle: File) -> i64 { use std::os::unix::io::IntoRawFd; - handle.into_raw_fd() + handle.into_raw_fd() as i64 } #[cfg(target_family = "unix")] -pub fn rust_file(raw_fileno: i32) -> File { +pub fn rust_file(raw_fileno: i64) -> File { use std::os::unix::io::FromRawFd; - unsafe { File::from_raw_fd(raw_fileno) } + unsafe { File::from_raw_fd(raw_fileno as i32) } } #[cfg(target_family = "windows")] -pub fn raw_file_number(handle: File) -> i32 { +pub fn raw_file_number(handle: File) -> i64 { use std::os::windows::io::IntoRawHandle; - handle.into_raw_handle() as i32 + handle.into_raw_handle() as i64 } #[cfg(target_family = "windows")] -pub fn rust_file(raw_fileno: i32) -> File { +pub fn rust_file(raw_fileno: i64) -> File { use std::ffi::c_void; use std::os::windows::io::FromRawHandle; //TODO: This is untested and (very) unsafe handling or - //raw pointers - This should be patched as a matter of - //urgently patched by comparison to the cpython handling of - //the equivalent fileno fields for windows + //raw pointers - This should be patched urgently by + //comparison to the cpython handling of the equivalent fileno + //fields for windows unsafe { File::from_raw_handle(raw_fileno as *mut c_void) } } @@ -56,7 +56,7 @@ pub fn os_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //The File type automatically closes when it goes out of scope. //To enable us to close these file desciptors (and hence prevent leaks) //we seek to create the relevant File and simply let it pass out of scope! - rust_file(raw_fileno.to_i32().unwrap()); + rust_file(raw_fileno.to_i64().unwrap()); Ok(vm.get_none()) } From aa2dff4bd9ba36981c7ac9645159f507d6fcb136 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Tue, 5 Feb 2019 23:08:45 +1100 Subject: [PATCH 052/153] Format os.rs --- vm/src/stdlib/os.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 18a043407..4c54f5847 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -42,8 +42,8 @@ pub fn rust_file(raw_fileno: i64) -> File { use std::os::windows::io::FromRawHandle; //TODO: This is untested and (very) unsafe handling or - //raw pointers - This should be patched urgently by - //comparison to the cpython handling of the equivalent fileno + //raw pointers - This should be patched urgently by + //comparison to the cpython handling of the equivalent fileno //fields for windows unsafe { File::from_raw_handle(raw_fileno as *mut c_void) } } From 5db2025decf75f4c072841867c9d1ea016a64013 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 15:20:17 +0300 Subject: [PATCH 053/153] int type: Added __invert__ --- vm/src/obj/objint.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index a00f1d5b0..8822fe5a1 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -95,6 +95,15 @@ fn int_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let result = !BigInt::from_pyobj(zelf).is_zero(); Ok(vm.ctx.new_bool(result)) } + +fn int_invert(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.int_type()))]); + + let result = !BigInt::from_pyobj(zelf); + + Ok(vm.ctx.new_int(result)) +} + fn int_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -533,6 +542,7 @@ pub fn init(context: &PyContext) { context.set_attr(&int_type, "__truediv__", context.new_rustfunc(int_truediv)); context.set_attr(&int_type, "__xor__", context.new_rustfunc(int_xor)); context.set_attr(&int_type, "__bool__", context.new_rustfunc(int_bool)); + context.set_attr(&int_type, "__invert__", context.new_rustfunc(int_invert)); context.set_attr( &int_type, "bit_length", From 8c469a64662e051bd6c0396d3875d572c3e73e47 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 15:25:07 +0300 Subject: [PATCH 054/153] int type: Added tests for the __invert__ --- tests/snippets/numbers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/snippets/numbers.py b/tests/snippets/numbers.py index 7b01f6473..bf3402bc3 100644 --- a/tests/snippets/numbers.py +++ b/tests/snippets/numbers.py @@ -12,3 +12,7 @@ assert type(x) is A assert int(2).__bool__() == True assert int(0.5).__bool__() == False assert int(-1).__bool__() == True + +assert int(0).__invert__() == -1 +assert int(-3).__invert__() == 2 +assert int(4).__invert__() == -5 From 3d07ecdd1c6762d1eec870611b23f597b7933f1d Mon Sep 17 00:00:00 2001 From: lausek Date: Tue, 5 Feb 2019 14:08:08 +0100 Subject: [PATCH 055/153] int type: trunc and index --- tests/snippets/numbers.py | 7 +++++++ vm/src/obj/objint.rs | 8 +++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/snippets/numbers.py b/tests/snippets/numbers.py index 7b01f6473..c13470eda 100644 --- a/tests/snippets/numbers.py +++ b/tests/snippets/numbers.py @@ -9,6 +9,13 @@ x = A(7) assert x == 7 assert type(x) is A +assert int(2).__index__() == 2 +assert int(2).__trunc__() == 2 +assert int(2).__ceil__() == 2 +assert int(2).__floor__() == 2 +assert int(2).__round__() == 2 +assert int(2).__round__(3) == 2 + assert int(2).__bool__() == True assert int(0.5).__bool__() == False assert int(-1).__bool__() == True diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 0eda177cf..8598e68e8 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -304,7 +304,7 @@ fn int_round(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_int(get_value(i))) } -fn int_ceil_floor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn int_pass_value(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(i, Some(vm.ctx.int_type()))]); Ok(vm.ctx.new_int(get_value(i))) } @@ -528,8 +528,10 @@ pub fn init(context: &PyContext) { context.set_attr(&int_type, "__divmod__", context.new_rustfunc(int_divmod)); context.set_attr(&int_type, "__float__", context.new_rustfunc(int_float)); context.set_attr(&int_type, "__round__", context.new_rustfunc(int_round)); - context.set_attr(&int_type, "__ceil__", context.new_rustfunc(int_ceil_floor)); - context.set_attr(&int_type, "__floor__", context.new_rustfunc(int_ceil_floor)); + context.set_attr(&int_type, "__ceil__", context.new_rustfunc(int_pass_value)); + context.set_attr(&int_type, "__floor__", context.new_rustfunc(int_pass_value)); + context.set_attr(&int_type, "__index__", context.new_rustfunc(int_pass_value)); + context.set_attr(&int_type, "__trunc__", context.new_rustfunc(int_pass_value)); context.set_attr( &int_type, "__floordiv__", From fc779c2e3ab78cf43ca9a88c05e45d92b3a2086d Mon Sep 17 00:00:00 2001 From: ichyo Date: Tue, 5 Feb 2019 23:18:38 +0900 Subject: [PATCH 056/153] Use the number of characters instead of bytes for str.__len__ --- tests/snippets/strings.py | 3 +++ vm/src/obj/objstr.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/snippets/strings.py b/tests/snippets/strings.py index db0aad96e..7b6541c42 100644 --- a/tests/snippets/strings.py +++ b/tests/snippets/strings.py @@ -8,6 +8,9 @@ assert "\n" == """ """ assert len(""" " \" """) == 5 +assert len("\u00E9") == 1 +assert len("\u0065\u0301") == 2 +assert len("\u3042") == 1 assert type("") is str assert type(b"") is bytes diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index ce55bb940..793f0b2d7 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -314,7 +314,7 @@ fn str_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn str_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); let sv = get_value(s); - Ok(vm.ctx.new_int(sv.len().to_bigint().unwrap())) + Ok(vm.ctx.new_int(sv.chars().count().to_bigint().unwrap())) } fn str_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From f8db0f1c94b818122a9ccee9ad09e6806147c2f9 Mon Sep 17 00:00:00 2001 From: lausek Date: Tue, 5 Feb 2019 15:42:39 +0100 Subject: [PATCH 057/153] raise ImportError in import list --- tests/snippets/import.py | 6 ++++++ vm/src/import.rs | 16 +++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/snippets/import.py b/tests/snippets/import.py index 1a5130269..2699002d0 100644 --- a/tests/snippets/import.py +++ b/tests/snippets/import.py @@ -16,6 +16,12 @@ assert import_target.Y == aliased_other_func() assert STAR_IMPORT == '123' +try: + from import_target import func, unknown_name + raise AssertionError('`unknown_name` does not cause an exception') +except: + pass + # TODO: Once we can determine current directory, use that to construct this # path: #import sys diff --git a/vm/src/import.rs b/vm/src/import.rs index f0f331c14..e560d3b18 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -71,11 +71,17 @@ pub fn import( let module = import_module(vm, current_path, module_name)?; // If we're importing a symbol, look it up and use it, otherwise construct a module and return // that - let obj = match symbol { - Some(symbol) => module.get_item(symbol).unwrap(), - None => module, - }; - Ok(obj) + if let Some(symbol) = symbol { + module.get_item(symbol).map_or_else( + || { + let import_error = vm.context().exceptions.import_error.clone(); + Err(vm.new_exception(import_error, format!("cannot import name '{}'", symbol))) + }, + |obj| Ok(obj), + ) + } else { + Ok(module) + } } fn find_source(vm: &VirtualMachine, current_path: PathBuf, name: &str) -> Result { From d1b034e3b9f88d6d7ab29448e789bb0a8eb20cdc Mon Sep 17 00:00:00 2001 From: ichyo Date: Tue, 5 Feb 2019 23:54:39 +0900 Subject: [PATCH 058/153] Avoid using \xhh for string --- tests/snippets/strings.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/snippets/strings.py b/tests/snippets/strings.py index 7b6541c42..66157f533 100644 --- a/tests/snippets/strings.py +++ b/tests/snippets/strings.py @@ -8,9 +8,9 @@ assert "\n" == """ """ assert len(""" " \" """) == 5 -assert len("\u00E9") == 1 -assert len("\u0065\u0301") == 2 -assert len("\u3042") == 1 +assert len("é") == 1 +assert len("é") == 2 +assert len("あ") == 1 assert type("") is str assert type(b"") is bytes From c7e6935e1c9766766b766ac8ba9a05eab70af725 Mon Sep 17 00:00:00 2001 From: Timur Makarchuk Date: Tue, 5 Feb 2019 19:21:31 +0300 Subject: [PATCH 059/153] Get rid of `exp` usages Assuming `exp` function was missing for platform-dependant reasons, let's try to test it with `sin` instead --- tests/snippets/math.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/snippets/math.py b/tests/snippets/math.py index 2270ff058..c238ee667 100644 --- a/tests/snippets/math.py +++ b/tests/snippets/math.py @@ -18,8 +18,8 @@ assert -a == -4 assert +a == 4 import math -assert(math.exp(2) == math.exp(2.0)) -assert(math.exp(True) == math.exp(1.0)) +assert(math.sin(2) == math.sin(2.0)) +assert(math.sin(True) == math.sin(1.0)) class Conversible(): def __float__(self): From f4af59916793b1435f278db1f1a233d12e9d03a8 Mon Sep 17 00:00:00 2001 From: lausek Date: Tue, 5 Feb 2019 17:41:05 +0100 Subject: [PATCH 060/153] added ImportError as builtin --- tests/snippets/import.py | 2 +- vm/src/builtins.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/snippets/import.py b/tests/snippets/import.py index 2699002d0..0a36e6342 100644 --- a/tests/snippets/import.py +++ b/tests/snippets/import.py @@ -19,7 +19,7 @@ assert STAR_IMPORT == '123' try: from import_target import func, unknown_name raise AssertionError('`unknown_name` does not cause an exception') -except: +except ImportError: pass # TODO: Once we can determine current directory, use that to construct this diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index a08e70fb7..8b8c8690b 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -818,6 +818,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "TypeError", ctx.exceptions.type_error.clone()); ctx.set_attr(&py_mod, "ValueError", ctx.exceptions.value_error.clone()); ctx.set_attr(&py_mod, "IndexError", ctx.exceptions.index_error.clone()); + ctx.set_attr(&py_mod, "ImportError", ctx.exceptions.import_error.clone()); py_mod } From e481063ca6a169625d04834969783e28b2185011 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Tue, 5 Feb 2019 18:13:08 +0100 Subject: [PATCH 061/153] Fixed duplicate os error. --- vm/src/exceptions.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 4d3930fd8..305297919 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -127,7 +127,6 @@ impl ExceptionZoo { let syntax_error = create_type("SyntaxError", &type_type, &exception_type, &dict_type); let type_error = create_type("TypeError", &type_type, &exception_type, &dict_type); let value_error = create_type("ValueError", &type_type, &exception_type, &dict_type); - let os_error = create_type("OSError", &type_type, &exception_type.clone(), &dict_type); let module_not_found_error = create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type); @@ -161,7 +160,6 @@ impl ExceptionZoo { syntax_error, type_error, value_error, - os_error, } } } From 57e2beef3aa0f8f35bc775b1f5abce25beb784c3 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Mon, 4 Feb 2019 22:33:25 +0300 Subject: [PATCH 062/153] Fixed the 'useless_format' clippy warning This replaces all the occurrences of the format!(<&str>) with the <&str>.to_string() Relevant clippy warning: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format --- vm/src/obj/objcode.rs | 4 ++-- vm/src/obj/objframe.rs | 4 ++-- vm/src/obj/objsuper.rs | 6 +++--- vm/src/pyobject.rs | 14 +++++++------- vm/src/stdlib/pystruct.rs | 8 ++++---- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 757e1aca3..f8cdcb22a 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -27,7 +27,7 @@ pub fn copy_code(code_obj: &PyObjectRef) -> bytecode::CodeObject { fn code_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(_cls, None)]); - Err(vm.new_type_error(format!("Cannot directly create code object"))) + Err(vm.new_type_error("Cannot directly create code object".to_string())) } fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -43,7 +43,7 @@ fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; // TODO: fetch proper line info from code object - let line = format!(", line 1"); + let line = ", line 1".to_string(); let repr = format!("", file, line); Ok(vm.new_str(repr)) diff --git a/vm/src/obj/objframe.rs b/vm/src/obj/objframe.rs index c9aa366b1..e0e0d8d15 100644 --- a/vm/src/obj/objframe.rs +++ b/vm/src/obj/objframe.rs @@ -19,12 +19,12 @@ pub fn init(context: &PyContext) { fn frame_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(_cls, None)]); - Err(vm.new_type_error(format!("Cannot directly create frame object"))) + Err(vm.new_type_error("Cannot directly create frame object".to_string())) } fn frame_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(_frame, Some(vm.ctx.frame_type()))]); - let repr = format!(""); + let repr = "".to_string(); Ok(vm.new_str(repr)) } diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 850f64c1a..4929042bb 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -53,9 +53,9 @@ fn super_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // Check obj type: if !(objtype::isinstance(&py_obj, &py_type) || objtype::issubclass(&py_obj, &py_type)) { - return Err(vm.new_type_error(format!( - "super(type, obj): obj must be an instance or subtype of type" - ))); + return Err(vm.new_type_error( + "super(type, obj): obj must be an instance or subtype of type".to_string(), + )); } // TODO: how to store those types? diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 69fc083a5..b91b519c9 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1037,13 +1037,13 @@ impl PyObject { dict: ref _dict, mro: _, } => format!("", name), - PyObjectPayload::Instance { dict: _ } => format!(""), - PyObjectPayload::Code { code: _ } => format!(""), - PyObjectPayload::Function { .. } => format!(""), - PyObjectPayload::Generator { .. } => format!(""), - PyObjectPayload::Frame { .. } => format!(""), - PyObjectPayload::BoundMethod { .. } => format!(""), - PyObjectPayload::RustFunction { function: _ } => format!(""), + PyObjectPayload::Instance { dict: _ } => "".to_string(), + PyObjectPayload::Code { code: _ } => "".to_string(), + PyObjectPayload::Function { .. } => "".to_string(), + PyObjectPayload::Generator { .. } => "".to_string(), + PyObjectPayload::Frame { .. } => "".to_string(), + PyObjectPayload::BoundMethod { .. } => "".to_string(), + PyObjectPayload::RustFunction { function: _ } => "".to_string(), PyObjectPayload::Module { ref name, dict: _ } => format!("", name), PyObjectPayload::Scope { ref scope } => format!("", scope), PyObjectPayload::Slice { diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index fda1854f4..4640af759 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -76,7 +76,7 @@ fn pack_bool( data.write_u8(v).unwrap(); Ok(()) } else { - Err(vm.new_type_error(format!("Expected boolean"))) + Err(vm.new_type_error("Expected boolean".to_string())) } } @@ -150,7 +150,7 @@ fn pack_f32( data.write_f32::(v).unwrap(); Ok(()) } else { - Err(vm.new_type_error(format!("Expected float"))) + Err(vm.new_type_error("Expected float".to_string())) } } @@ -164,7 +164,7 @@ fn pack_f64( data.write_f64::(v).unwrap(); Ok(()) } else { - Err(vm.new_type_error(format!("Expected float"))) + Err(vm.new_type_error("Expected float".to_string())) } } @@ -216,7 +216,7 @@ fn struct_pack(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ))) } } else { - Err(vm.new_type_error(format!("First argument must be of str type"))) + Err(vm.new_type_error("First argument must be of str type".to_string())) } } } From 6fb91ddfed4d7de81d2b12515c19c3cb39d3b2c9 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Mon, 4 Feb 2019 23:13:11 +0300 Subject: [PATCH 063/153] Fixed the 'len_zero' clippy warnings This replaces all the occurrences of the .len() == 0 with the .is_empty() and the occurrences of the .len() > 0 with the !.is_empty() Relevant clippy warning: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero --- vm/src/builtins.rs | 4 ++-- vm/src/compile.rs | 5 ++--- vm/src/format.rs | 10 +++++----- vm/src/obj/objset.rs | 4 ++-- vm/src/obj/objstr.rs | 2 +- vm/src/stdlib/io.rs | 2 +- vm/src/stdlib/pystruct.rs | 2 +- 7 files changed, 14 insertions(+), 15 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 8b8c8690b..83893ee83 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -465,7 +465,7 @@ fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return Err(vm.new_type_error("Expected 1 or more arguments".to_string())); }; - if candidates.len() == 0 { + if candidates.is_empty() { let default = args.get_optional_kwarg("default"); if default.is_none() { return Err(vm.new_value_error("max() arg is an empty sequence".to_string())); @@ -516,7 +516,7 @@ fn builtin_min(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return Err(vm.new_type_error("Expected 1 or more arguments".to_string())); }; - if candidates.len() == 0 { + if candidates.is_empty() { let default = args.get_optional_kwarg("default"); if default.is_none() { return Err(vm.new_value_error("min() arg is an empty sequence".to_string())); diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 71439463d..caccfb65c 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -1156,7 +1156,7 @@ impl Compiler { generators: &Vec, ) -> Result<(), String> { // We must have at least one generator: - assert!(generators.len() > 0); + assert!(!generators.is_empty()); let name = match kind { ast::ComprehensionKind::GeneratorExpression { .. } => "", @@ -1201,8 +1201,7 @@ impl Compiler { let mut loop_labels = vec![]; for generator in generators { - let first = loop_labels.len() == 0; - if first { + if loop_labels.is_empty() { // Load iterator onto stack (passed as first argument): self.emit(Instruction::LoadName { name: String::from(".0"), diff --git a/vm/src/format.rs b/vm/src/format.rs index 37f8bfa57..5b4035ef7 100644 --- a/vm/src/format.rs +++ b/vm/src/format.rs @@ -87,7 +87,7 @@ fn parse_align(text: &str) -> (Option, &str) { fn parse_fill_and_align(text: &str) -> (Option, Option, &str) { let char_indices: Vec<(usize, char)> = text.char_indices().take(3).collect(); - if char_indices.len() == 0 { + if char_indices.is_empty() { (None, None, text) } else if char_indices.len() == 1 { let (maybe_align, remaining) = parse_align(text); @@ -438,14 +438,14 @@ impl FormatString { fn parse_literal(text: &str) -> Result<(FormatPart, &str), FormatParseError> { let mut cur_text = text; let mut result_string = String::new(); - while cur_text.len() > 0 { + while !cur_text.is_empty() { match FormatString::parse_literal_single(cur_text) { Ok((next_char, remaining)) => { result_string.push(next_char); cur_text = remaining; } Err(err) => { - if result_string.len() > 0 { + if !result_string.is_empty() { return Ok((FormatPart::Literal(result_string.to_string()), cur_text)); } else { return Err(err); @@ -467,7 +467,7 @@ impl FormatString { String::new() }; - if arg_part.len() == 0 { + if arg_part.is_empty() { return Ok(FormatPart::AutoSpec(format_spec)); } @@ -500,7 +500,7 @@ impl FormatString { pub fn from_str(text: &str) -> Result { let mut cur_text: &str = text; let mut parts: Vec = Vec::new(); - while cur_text.len() > 0 { + while !cur_text.is_empty() { // Try to parse both literals and bracketed format parts util we // run out of text cur_text = FormatString::parse_literal(cur_text) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 18791cf78..507b59f0a 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -98,7 +98,7 @@ fn set_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.set_type()))]); let elements = get_elements(o); - let s = if elements.len() == 0 { + let s = if elements.is_empty() { "set()".to_string() } else { let mut str_parts = vec![]; @@ -136,7 +136,7 @@ fn frozenset_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.frozenset_type()))]); let elements = get_elements(o); - let s = if elements.len() == 0 { + let s = if elements.is_empty() { "frozenset()".to_string() } else { let mut str_parts = vec![]; diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 4eea5a966..fee556e03 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -208,7 +208,7 @@ fn str_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn str_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - if args.args.len() == 0 { + if args.args.is_empty() { return Err( vm.new_type_error("descriptor 'format' of 'str' object needs an argument".to_string()) ); diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 765e899a8..38d2b3ce7 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -295,7 +295,7 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .filter(|a| raw_modes.contains(&a.to_string())) .collect(); - if modes.len() == 0 || modes.len() > 1 { + if modes.is_empty() || modes.len() > 1 { return Err(vm.new_value_error("Invalid Mode".to_string())); } diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index fda1854f4..3c60e7976 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -169,7 +169,7 @@ fn pack_f64( } fn struct_pack(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - if args.args.len() < 1 { + if args.args.is_empty() { Err(vm.new_type_error(format!( "Expected at least 1 argument (got: {})", args.args.len() From 1a6840280b8f27ffd7f5972347419e5230233911 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 00:37:06 +0300 Subject: [PATCH 064/153] Fixed the 'ptr_arg' clippy warnings This replaces all the occurrences of & with the &[T]. Relevant clippy warning: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg --- vm/src/compile.rs | 12 ++++++------ vm/src/obj/objsequence.rs | 24 ++++++++++++------------ vm/src/obj/objset.rs | 2 +- vm/src/obj/objstr.rs | 4 ++-- vm/src/stdlib/ast.rs | 2 +- vm/src/stdlib/io.rs | 2 +- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 71439463d..06f62663d 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -674,14 +674,14 @@ impl Compiler { Ok(flags) } - fn prepare_decorators(&mut self, decorator_list: &Vec) -> Result<(), String> { + fn prepare_decorators(&mut self, decorator_list: &[ast::Expression]) -> Result<(), String> { for decorator in decorator_list { self.compile_expression(decorator)?; } Ok(()) } - fn apply_decorators(&mut self, decorator_list: &Vec) { + fn apply_decorators(&mut self, decorator_list: &[ast::Expression]) { // Apply decorators: for _ in decorator_list { self.emit(Instruction::CallFunction { @@ -1036,8 +1036,8 @@ impl Compiler { fn compile_call( &mut self, function: &ast::Expression, - args: &Vec, - keywords: &Vec, + args: &[ast::Expression], + keywords: &[ast::Keyword], ) -> Result<(), String> { self.compile_expression(function)?; let count = args.len() + keywords.len(); @@ -1123,7 +1123,7 @@ impl Compiler { // Given a vector of expr / star expr generate code which gives either // a list of expressions on the stack, or a list of tuples. - fn gather_elements(&mut self, elements: &Vec) -> Result { + fn gather_elements(&mut self, elements: &[ast::Expression]) -> Result { // First determine if we have starred elements: let has_stars = elements.iter().any(|e| { if let ast::Expression::Starred { .. } = e { @@ -1153,7 +1153,7 @@ impl Compiler { fn compile_comprehension( &mut self, kind: &ast::ComprehensionKind, - generators: &Vec, + generators: &[ast::Comprehension], ) -> Result<(), String> { // We must have at least one generator: assert!(generators.len() > 0); diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index ff3ed6a01..5c580e823 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -112,8 +112,8 @@ pub fn get_item( pub fn seq_equal( vm: &mut VirtualMachine, - zelf: &Vec, - other: &Vec, + zelf: &[PyObjectRef], + other: &[PyObjectRef], ) -> Result { if zelf.len() == other.len() { for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { @@ -131,8 +131,8 @@ pub fn seq_equal( pub fn seq_lt( vm: &mut VirtualMachine, - zelf: &Vec, - other: &Vec, + zelf: &[PyObjectRef], + other: &[PyObjectRef], ) -> Result { if zelf.len() == other.len() { for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { @@ -171,8 +171,8 @@ pub fn seq_lt( pub fn seq_gt( vm: &mut VirtualMachine, - zelf: &Vec, - other: &Vec, + zelf: &[PyObjectRef], + other: &[PyObjectRef], ) -> Result { if zelf.len() == other.len() { for (a, b) in Iterator::zip(zelf.iter(), other.iter()) { @@ -210,21 +210,21 @@ pub fn seq_gt( pub fn seq_ge( vm: &mut VirtualMachine, - zelf: &Vec, - other: &Vec, + zelf: &[PyObjectRef], + other: &[PyObjectRef], ) -> Result { Ok(seq_gt(vm, zelf, other)? || seq_equal(vm, zelf, other)?) } pub fn seq_le( vm: &mut VirtualMachine, - zelf: &Vec, - other: &Vec, + zelf: &[PyObjectRef], + other: &[PyObjectRef], ) -> Result { Ok(seq_lt(vm, zelf, other)? || seq_equal(vm, zelf, other)?) } -pub fn seq_mul(elements: &Vec, product: &PyObjectRef) -> Vec { +pub fn seq_mul(elements: &[PyObjectRef], product: &PyObjectRef) -> Vec { let counter = objint::get_value(&product).to_isize().unwrap(); let current_len = elements.len(); @@ -232,7 +232,7 @@ pub fn seq_mul(elements: &Vec, product: &PyObjectRef) -> Vec HashMap { } } -pub fn sequence_to_hashmap(iterable: &Vec) -> HashMap { +pub fn sequence_to_hashmap(iterable: &[PyObjectRef]) -> HashMap { let mut elements = HashMap::new(); for item in iterable { let key = item.get_id(); diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 4eea5a966..b5012e21e 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -238,9 +238,9 @@ fn str_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn call_object_format( vm: &mut VirtualMachine, argument: PyObjectRef, - format_spec: &String, + format_spec: &str, ) -> PyResult { - let returned_type = vm.ctx.new_str(format_spec.clone()); + let returned_type = vm.ctx.new_str(format_spec.to_string()); let result = vm.call_method(&argument, "__format__", vec![returned_type])?; if !objtype::isinstance(&result, &vm.ctx.str_type()) { let result_type = result.typ(); diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index 107cc0ac9..cf64a24e2 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -239,7 +239,7 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj node } -fn expressions_to_ast(ctx: &PyContext, expressions: &Vec) -> PyObjectRef { +fn expressions_to_ast(ctx: &PyContext, expressions: &[ast::Expression]) -> PyObjectRef { let mut py_expression_nodes = vec![]; for expression in expressions { py_expression_nodes.push(expression_to_ast(ctx, expression)); diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 765e899a8..8013abbce 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -26,7 +26,7 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; -fn compute_c_flag(mode: &String) -> u16 { +fn compute_c_flag(mode: &str) -> u16 { match mode.as_ref() { "w" => 512, "x" => 512, From d445e3082a64766d2d0266828c4b6f7d2eb9a199 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 01:32:18 +0300 Subject: [PATCH 065/153] Fixed the 'unneeded_field_pattern' clippy warnings This replaces all the occurrences of the Struct {field: _} with the Struct { .. }. Relevant clippy warning: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern --- vm/src/frame.rs | 12 ++-- vm/src/obj/objsequence.rs | 9 +-- vm/src/obj/objstr.rs | 8 +-- vm/src/obj/objtype.rs | 14 +---- vm/src/pyobject.rs | 112 ++++++++++++++------------------------ vm/src/stdlib/ast.rs | 9 +-- vm/src/vm.rs | 8 +-- 7 files changed, 58 insertions(+), 114 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 2c8ada87b..b8914a66b 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -522,7 +522,7 @@ impl Frame { bytecode::Instruction::Break => { let block = self.unwind_loop(vm); - if let Block::Loop { start: _, end } = block { + if let Block::Loop { end, .. } = block { self.jump(end); } Ok(None) @@ -533,7 +533,7 @@ impl Frame { } bytecode::Instruction::Continue => { let block = self.unwind_loop(vm); - if let Block::Loop { start, end: _ } = block { + if let Block::Loop { start, .. } = block { self.jump(start); } else { assert!(false); @@ -708,8 +708,7 @@ impl Frame { // TODO: execute finally handler } Some(Block::With { - end: _, - context_manager, + context_manager, .. }) => { match self.with_exit(vm, &context_manager, None) { Ok(..) => {} @@ -728,13 +727,12 @@ impl Frame { loop { let block = self.pop_block(); match block { - Some(Block::Loop { start: _, end: __ }) => break block.unwrap(), + Some(Block::Loop { .. }) => break block.unwrap(), Some(Block::TryExcept { .. }) => { // TODO: execute finally handler } Some(Block::With { - end: _, - context_manager, + context_manager, .. }) => match self.with_exit(vm, &context_manager, None) { Ok(..) => {} Err(exc) => { diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index ff3ed6a01..bf7a653af 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -90,13 +90,10 @@ pub fn get_item( Err(vm.new_index_error("cannot fit 'int' into an index-sized integer".to_string())) } }, - PyObjectPayload::Slice { - start: _, - stop: _, - step: _, - } => Ok(PyObject::new( + + PyObjectPayload::Slice { .. } => Ok(PyObject::new( match &(sequence.borrow()).payload { - PyObjectPayload::Sequence { elements: _ } => PyObjectPayload::Sequence { + PyObjectPayload::Sequence { .. } => PyObjectPayload::Sequence { elements: elements.to_vec().get_slice_items(&subscript), }, ref payload => panic!("sequence get_item called for non-sequence: {:?}", payload), diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 4eea5a966..ed6888350 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -1030,11 +1030,9 @@ pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResu } } else { match &(*b.borrow()).payload { - &PyObjectPayload::Slice { - start: _, - stop: _, - step: _, - } => Ok(vm.new_str(value.to_string().get_slice_items(&b).to_string())), + &PyObjectPayload::Slice { .. } => { + Ok(vm.new_str(value.to_string().get_slice_items(&b).to_string())) + } _ => panic!( "TypeError: indexing type {:?} with index {:?} is not supported (yet?)", value, b diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 059b59ff4..2accef02a 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -89,12 +89,7 @@ pub fn issubclass(typ: &PyObjectRef, cls: &PyObjectRef) -> bool { } pub fn get_type_name(typ: &PyObjectRef) -> String { - if let PyObjectPayload::Class { - name, - dict: _, - mro: _, - } = &typ.borrow().payload - { + if let PyObjectPayload::Class { name, .. } = &typ.borrow().payload { name.clone() } else { panic!("Cannot get type_name of non-type type {:?}", typ); @@ -219,12 +214,7 @@ pub fn get_attributes(obj: &PyObjectRef) -> HashMap { let mut base_classes = objtype::base_classes(obj); base_classes.reverse(); for bc in base_classes { - if let PyObjectPayload::Class { - name: _, - dict, - mro: _, - } = &bc.borrow().payload - { + if let PyObjectPayload::Class { dict, .. } = &bc.borrow().payload { let elements = objdict::get_key_value_pairs(dict); for (name, value) in elements.iter() { let name = objstr::get_value(name); diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 69fc083a5..307660d45 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -436,17 +436,11 @@ impl PyContext { } pub fn new_tuple(&self, elements: Vec) -> PyObjectRef { - PyObject::new( - PyObjectPayload::Sequence { elements: elements }, - self.tuple_type(), - ) + PyObject::new(PyObjectPayload::Sequence { elements }, self.tuple_type()) } pub fn new_list(&self, elements: Vec) -> PyObjectRef { - PyObject::new( - PyObjectPayload::Sequence { elements: elements }, - self.list_type(), - ) + PyObject::new(PyObjectPayload::Sequence { elements }, self.list_type()) } pub fn new_set(&self, elements: Vec) -> PyObjectRef { @@ -469,12 +463,11 @@ impl PyContext { pub fn new_scope(&self, parent: Option) -> PyObjectRef { let locals = self.new_dict(); - let scope = Scope { - locals: locals, - parent: parent, - }; + + let scope = Scope { locals, parent }; + PyObject { - payload: PyObjectPayload::Scope { scope: scope }, + payload: PyObjectPayload::Scope { scope }, typ: None, } .into_ref() @@ -553,10 +546,7 @@ impl PyContext { pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef { PyObject::new( - PyObjectPayload::BoundMethod { - function: function, - object: object, - }, + PyObjectPayload::BoundMethod { function, object }, self.bound_method_type(), ) } @@ -581,10 +571,7 @@ impl PyContext { let key = self.new_str(key.to_string()); objdict::set_item_in_content(elements, &key, &v); } - PyObjectPayload::Module { - name: _, - ref mut dict, - } => self.set_item(dict, key, v), + PyObjectPayload::Module { ref mut dict, .. } => self.set_item(dict, key, v), PyObjectPayload::Scope { ref mut scope } => { self.set_item(&scope.locals, key, v); } @@ -601,13 +588,9 @@ impl PyContext { pub fn set_attr(&self, obj: &PyObjectRef, attr_name: &str, value: PyObjectRef) { match obj.borrow().payload { - PyObjectPayload::Module { name: _, ref dict } => self.set_item(dict, attr_name, value), + PyObjectPayload::Module { ref dict, .. } => self.set_item(dict, attr_name, value), PyObjectPayload::Instance { ref dict } => self.set_item(dict, attr_name, value), - PyObjectPayload::Class { - name: _, - ref dict, - mro: _, - } => self.set_item(dict, attr_name, value), + PyObjectPayload::Class { ref dict, .. } => self.set_item(dict, attr_name, value), ref payload => unimplemented!("set_attr unimplemented for: {:?}", payload), }; } @@ -732,7 +715,7 @@ impl AttributeProtocol for PyObjectRef { fn has_attr(&self, attr_name: &str) -> bool { let obj = self.borrow(); match obj.payload { - PyObjectPayload::Module { name: _, ref dict } => dict.contains_key(attr_name), + PyObjectPayload::Module { ref dict, .. } => dict.contains_key(attr_name), PyObjectPayload::Class { ref mro, .. } => { class_has_item(self, attr_name) || mro.into_iter().any(|d| class_has_item(d, attr_name)) @@ -755,7 +738,7 @@ impl DictProtocol for PyObjectRef { PyObjectPayload::Dict { ref elements } => { objdict::content_contains_key_str(elements, k) } - PyObjectPayload::Module { name: _, ref dict } => dict.contains_key(k), + PyObjectPayload::Module { ref dict, .. } => dict.contains_key(k), PyObjectPayload::Scope { ref scope } => scope.locals.contains_key(k), ref payload => unimplemented!("TODO {:?}", payload), } @@ -764,7 +747,7 @@ impl DictProtocol for PyObjectRef { fn get_item(&self, k: &str) -> Option { match self.borrow().payload { PyObjectPayload::Dict { ref elements } => objdict::content_get_key_str(elements, k), - PyObjectPayload::Module { name: _, ref dict } => dict.get_item(k), + PyObjectPayload::Module { ref dict, .. } => dict.get_item(k), PyObjectPayload::Scope { ref scope } => scope.locals.get_item(k), _ => panic!("TODO"), } @@ -772,8 +755,8 @@ impl DictProtocol for PyObjectRef { fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> { match self.borrow().payload { - PyObjectPayload::Dict { elements: _ } => objdict::get_key_value_pairs(self), - PyObjectPayload::Module { name: _, ref dict } => dict.get_key_value_pairs(), + PyObjectPayload::Dict { .. } => objdict::get_key_value_pairs(self), + PyObjectPayload::Module { ref dict, .. } => dict.get_key_value_pairs(), PyObjectPayload::Scope { ref scope } => scope.locals.get_key_value_pairs(), _ => panic!("TODO"), } @@ -815,10 +798,7 @@ impl PyFuncArgs { for name in kwarg_names.iter().rev() { kwargs.push((name.clone(), args.pop().unwrap())); } - PyFuncArgs { - args: args, - kwargs: kwargs, - } + PyFuncArgs { args, kwargs } } pub fn insert(&self, item: PyObjectRef) -> PyFuncArgs { @@ -948,37 +928,26 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::Complex { ref value } => write!(f, "complex {}", value), PyObjectPayload::Bytes { ref value } => write!(f, "bytes/bytearray {:?}", value), PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj), - PyObjectPayload::Sequence { elements: _ } => write!(f, "list or tuple"), - PyObjectPayload::Dict { elements: _ } => write!(f, "dict"), - PyObjectPayload::Set { elements: _ } => write!(f, "set"), + PyObjectPayload::Sequence { .. } => write!(f, "list or tuple"), + PyObjectPayload::Dict { .. } => write!(f, "dict"), + PyObjectPayload::Set { .. } => write!(f, "set"), PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), - PyObjectPayload::Iterator { - position: _, - iterated_obj: _, - } => write!(f, "iterator"), - PyObjectPayload::Slice { - start: _, - stop: _, - step: _, - } => write!(f, "slice"), - &PyObjectPayload::Range { range: _ } => write!(f, "range"), - &PyObjectPayload::Code { ref code } => write!(f, "code: {:?}", code), - &PyObjectPayload::Function { .. } => write!(f, "function"), - &PyObjectPayload::Generator { .. } => write!(f, "generator"), - &PyObjectPayload::BoundMethod { + PyObjectPayload::Range { .. } => write!(f, "range"), + PyObjectPayload::Iterator { .. } => write!(f, "iterator"), + PyObjectPayload::Slice { .. } => write!(f, "slice"), + PyObjectPayload::Code { ref code } => write!(f, "code: {:?}", code), + PyObjectPayload::Function { .. } => write!(f, "function"), + PyObjectPayload::Generator { .. } => write!(f, "generator"), + PyObjectPayload::BoundMethod { ref function, ref object, } => write!(f, "bound-method: {:?} of {:?}", function, object), - PyObjectPayload::Module { name: _, dict: _ } => write!(f, "module"), - PyObjectPayload::Scope { scope: _ } => write!(f, "scope"), + PyObjectPayload::Module { .. } => write!(f, "module"), + PyObjectPayload::Scope { .. } => write!(f, "scope"), PyObjectPayload::None => write!(f, "None"), - PyObjectPayload::Class { - ref name, - dict: _, - mro: _, - } => write!(f, "class {:?}", name), - PyObjectPayload::Instance { dict: _ } => write!(f, "instance"), - PyObjectPayload::RustFunction { function: _ } => write!(f, "rust function"), + PyObjectPayload::Class { ref name, .. } => write!(f, "class {:?}", name), + PyObjectPayload::Instance { .. } => write!(f, "instance"), + PyObjectPayload::RustFunction { .. } => write!(f, "rust function"), PyObjectPayload::Frame { .. } => write!(f, "frame"), } } @@ -1035,16 +1004,17 @@ impl PyObject { PyObjectPayload::Class { ref name, dict: ref _dict, - mro: _, + .. } => format!("", name), - PyObjectPayload::Instance { dict: _ } => format!(""), - PyObjectPayload::Code { code: _ } => format!(""), - PyObjectPayload::Function { .. } => format!(""), - PyObjectPayload::Generator { .. } => format!(""), - PyObjectPayload::Frame { .. } => format!(""), - PyObjectPayload::BoundMethod { .. } => format!(""), - PyObjectPayload::RustFunction { function: _ } => format!(""), - PyObjectPayload::Module { ref name, dict: _ } => format!("", name), + + PyObjectPayload::Instance { .. } => "".to_string(), + PyObjectPayload::Code { .. } => "".to_string(), + PyObjectPayload::Function { .. } => "".to_string(), + PyObjectPayload::Generator { .. } => "".to_string(), + PyObjectPayload::Frame { .. } => "".to_string(), + PyObjectPayload::BoundMethod { .. } => "".to_string(), + PyObjectPayload::RustFunction { .. } => "".to_string(), + PyObjectPayload::Module { ref name, .. } => format!("", name), PyObjectPayload::Scope { ref scope } => format!("", scope), PyObjectPayload::Slice { ref start, diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index 107cc0ac9..baf860990 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -64,9 +64,8 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj ast::Statement::ClassDef { name, body, - bases: _, - keywords: _, decorator_list, + .. } => { let node = create_node(ctx, "ClassDef"); @@ -249,11 +248,7 @@ fn expressions_to_ast(ctx: &PyContext, expressions: &Vec) -> Py fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectRef { let node = match &expression { - ast::Expression::Call { - function, - args, - keywords: _, - } => { + ast::Expression::Call { function, args, .. } => { let node = create_node(ctx, "Call"); let py_func_ast = expression_to_ast(ctx, function); diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 061c99605..3ec2af335 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -164,7 +164,7 @@ impl VirtualMachine { pub fn get_builtin_scope(&mut self) -> PyObjectRef { let a2 = &*self.builtins.borrow(); match a2.payload { - PyObjectPayload::Module { name: _, ref dict } => dict.clone(), + PyObjectPayload::Module { ref dict, .. } => dict.clone(), _ => { panic!("OMG"); } @@ -250,11 +250,7 @@ impl VirtualMachine { ref scope, ref defaults, } => self.invoke_python_function(code, scope, defaults, args), - PyObjectPayload::Class { - name: _, - dict: _, - mro: _, - } => self.call_method_pyargs(&func_ref, "__call__", args), + PyObjectPayload::Class { .. } => self.call_method_pyargs(&func_ref, "__call__", args), PyObjectPayload::BoundMethod { ref function, ref object, From 7134885775e4b512ab58361a0f20c0122f738e7a Mon Sep 17 00:00:00 2001 From: stratusjerry Date: Tue, 5 Feb 2019 13:39:16 -0500 Subject: [PATCH 066/153] Spellcheck --- wasm/demo/src/index.html | 6 +++--- wasm/lib/README.md | 2 +- wasm/lib/src/lib.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/wasm/demo/src/index.html b/wasm/demo/src/index.html index d87853bde..639bf1f7d 100644 --- a/wasm/demo/src/index.html +++ b/wasm/demo/src/index.html @@ -7,7 +7,7 @@

RustPython Demo

- RustPython is a Python interpreter writter in Rust. This demo is + RustPython is a Python interpreter written in Rust. This demo is compiled from Rust to WebAssembly so it runs in the browser.
Please input your Python code below and click Run, or you can open up your browser's devtools and play with @@ -44,7 +44,7 @@ while count < until:

  • stdout: either a string with a css selector - to a textarea element or a function that recieves a + to a textarea element or a function that receives a string when the print function is called in python. The default value is console.log.
  • @@ -57,7 +57,7 @@ while count < until:
  • - JS functions that get passed to python will recieve positional + JS functions that get passed to python will receive positional args as positional args and kwargs as the this argument
  • diff --git a/wasm/lib/README.md b/wasm/lib/README.md index b278e8cb9..6d2f89d93 100644 --- a/wasm/lib/README.md +++ b/wasm/lib/README.md @@ -28,7 +28,7 @@ pyEval(code, options?); - `vars?`: `{ [key: string]: any }`: Variables passed to the VM that can be accessed in Python with the variable `js_vars`. Functions do work, and - recieve the Python kwargs as the `this` argument. + receive the Python kwargs as the `this` argument. - `stdout?`: `(out: string) => void`: A function to replace the native print function, by default `console.log`. diff --git a/wasm/lib/src/lib.rs b/wasm/lib/src/lib.rs index 01e524c9d..f7da7edd1 100644 --- a/wasm/lib/src/lib.rs +++ b/wasm/lib/src/lib.rs @@ -11,7 +11,7 @@ use rustpython_vm::pyobject::{self, PyFuncArgs, PyObjectRef, PyResult}; use rustpython_vm::VirtualMachine; use wasm_bindgen::{prelude::*, JsCast}; -// Hack to comment out wasm-bindgen's typescript definitons +// Hack to comment out wasm-bindgen's typescript definitions #[wasm_bindgen(typescript_custom_section)] const TS_CMT_START: &'static str = "/*"; @@ -141,7 +141,7 @@ fn eval(vm: &mut VirtualMachine, source: &str, vars: PyObjectRef) -> PyResult { /// /// - `vars?`: `{ [key: string]: any }`: Variables passed to the VM that can be /// accessed in Python with the variable `js_vars`. Functions do work, and -/// recieve the Python kwargs as the `this` argument. +/// receive the Python kwargs as the `this` argument. /// - `stdout?`: `(out: string) => void`: A function to replace the native print /// function, by default `console.log`. pub fn eval_py(source: &str, options: Option) -> Result { From 526ed98d93b840b66bc95d87c06a9cc00b11c238 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 01:40:34 +0300 Subject: [PATCH 067/153] Fixed the 'needless_return' clippy warnings This replaces all the occurrences of the 'return var;' at the end of the functions with the 'var'. Relevant clippy warning: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return --- vm/src/obj/objint.rs | 8 ++------ vm/src/obj/objstr.rs | 2 +- vm/src/pyobject.rs | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 083741023..51e58a7d6 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -205,9 +205,7 @@ fn int_lshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // i2 failed `to_usize()` conversion match get_value(i2) { - ref v if *v < BigInt::zero() => { - return Err(vm.new_value_error("negative shift count".to_string())); - } + ref v if *v < BigInt::zero() => Err(vm.new_value_error("negative shift count".to_string())), ref v if *v > BigInt::from(usize::max_value()) => { // TODO: raise OverflowError panic!("Failed converting {} to rust usize", get_value(i2)); @@ -237,9 +235,7 @@ fn int_rshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // i2 failed `to_usize()` conversion match get_value(i2) { - ref v if *v < BigInt::zero() => { - return Err(vm.new_value_error("negative shift count".to_string())); - } + ref v if *v < BigInt::zero() => Err(vm.new_value_error("negative shift count".to_string())), ref v if *v > BigInt::from(usize::max_value()) => { // TODO: raise OverflowError panic!("Failed converting {} to rust usize", get_value(i2)); diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 4eea5a966..2b840aa11 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -1081,5 +1081,5 @@ fn make_title(s: &str) -> String { capitalize_char = true; } } - return titled_str; + titled_str } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 69fc083a5..9fffb14d5 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -827,7 +827,7 @@ impl PyFuncArgs { kwargs: self.kwargs.clone(), }; args.args.insert(0, item); - return args; + args } pub fn shift(&mut self) -> PyObjectRef { From 0d3b2182378ef33588c242f330f8f24b5710a162 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 01:57:13 +0300 Subject: [PATCH 068/153] Fixed the 'toplevel_ref_arg' clippy warning This replaces all the occurrences of the 'let ref var = another_var' with the 'let var = &another_var' Relevant clippy warning: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg --- vm/src/obj/objbool.rs | 2 +- vm/src/obj/objbytearray.rs | 2 +- vm/src/obj/objbytes.rs | 2 +- vm/src/obj/objcode.rs | 2 +- vm/src/obj/objcomplex.rs | 2 +- vm/src/obj/objdict.rs | 2 +- vm/src/obj/objfloat.rs | 2 +- vm/src/obj/objframe.rs | 2 +- vm/src/obj/objfunction.rs | 8 ++++---- vm/src/obj/objgenerator.rs | 2 +- vm/src/obj/objint.rs | 3 ++- vm/src/obj/objiter.rs | 2 +- vm/src/obj/objlist.rs | 2 +- vm/src/obj/objmemory.rs | 2 +- vm/src/obj/objobject.rs | 2 +- vm/src/obj/objproperty.rs | 2 +- vm/src/obj/objset.rs | 4 ++-- vm/src/obj/objstr.rs | 2 +- vm/src/obj/objsuper.rs | 2 +- vm/src/obj/objtuple.rs | 2 +- vm/src/obj/objtype.rs | 4 ++-- vm/src/pyobject.rs | 2 +- 22 files changed, 28 insertions(+), 27 deletions(-) diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index 594361532..f3a2fafb8 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -30,13 +30,13 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result PyResult { } pub fn init(context: &PyContext) { - let ref float_type = context.float_type; + let float_type = &context.float_type; context.set_attr(&float_type, "__eq__", context.new_rustfunc(float_eq)); context.set_attr(&float_type, "__lt__", context.new_rustfunc(float_lt)); context.set_attr(&float_type, "__le__", context.new_rustfunc(float_le)); diff --git a/vm/src/obj/objframe.rs b/vm/src/obj/objframe.rs index c9aa366b1..83417bb8e 100644 --- a/vm/src/obj/objframe.rs +++ b/vm/src/obj/objframe.rs @@ -10,7 +10,7 @@ use super::super::vm::VirtualMachine; use super::objtype; pub fn init(context: &PyContext) { - let ref frame_type = context.frame_type; + let frame_type = &context.frame_type; context.set_attr(&frame_type, "__new__", context.new_rustfunc(frame_new)); context.set_attr(&frame_type, "__repr__", context.new_rustfunc(frame_repr)); context.set_attr(&frame_type, "f_locals", context.new_property(frame_flocals)); diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index 925bb7123..b665e581a 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -6,17 +6,17 @@ use super::super::vm::VirtualMachine; use super::objtype; pub fn init(context: &PyContext) { - let ref function_type = context.function_type; + let function_type = &context.function_type; context.set_attr(&function_type, "__get__", context.new_rustfunc(bind_method)); - let ref member_descriptor_type = context.member_descriptor_type; + let member_descriptor_type = &context.member_descriptor_type; context.set_attr( &member_descriptor_type, "__get__", context.new_rustfunc(member_get), ); - let ref classmethod_type = context.classmethod_type; + let classmethod_type = &context.classmethod_type; context.set_attr( &classmethod_type, "__get__", @@ -28,7 +28,7 @@ pub fn init(context: &PyContext) { context.new_rustfunc(classmethod_new), ); - let ref staticmethod_type = context.staticmethod_type; + let staticmethod_type = &context.staticmethod_type; context.set_attr( staticmethod_type, "__get__", diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index c98dcadf2..79c4830fa 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -10,7 +10,7 @@ use super::super::vm::VirtualMachine; use super::objtype; pub fn init(context: &PyContext) { - let ref generator_type = context.generator_type; + let generator_type = &context.generator_type; context.set_attr( &generator_type, "__iter__", diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 083741023..7e23df333 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -510,7 +510,6 @@ fn int_conjugate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn init(context: &PyContext) { - let ref int_type = context.int_type; let int_doc = "int(x=0) -> integer int(x, base=10) -> integer @@ -525,6 +524,8 @@ by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal. >>> int('0b100', base=0) 4"; + let int_type = &context.int_type; + context.set_attr(&int_type, "__eq__", context.new_rustfunc(int_eq)); context.set_attr(&int_type, "__lt__", context.new_rustfunc(int_lt)); context.set_attr(&int_type, "__le__", context.new_rustfunc(int_le)); diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 49497e9e9..53ed76804 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -141,7 +141,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn init(context: &PyContext) { - let ref iter_type = context.iter_type; + let iter_type = &context.iter_type; context.set_attr( &iter_type, "__contains__", diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 4175b6005..a4f68d1bf 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -365,7 +365,7 @@ fn list_pop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn init(context: &PyContext) { - let ref list_type = context.list_type; + let list_type = &context.list_type; context.set_attr(&list_type, "__add__", context.new_rustfunc(list_add)); context.set_attr( &list_type, diff --git a/vm/src/obj/objmemory.rs b/vm/src/obj/objmemory.rs index f9b20e441..6005ba62d 100644 --- a/vm/src/obj/objmemory.rs +++ b/vm/src/obj/objmemory.rs @@ -17,7 +17,7 @@ pub fn new_memory_view(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn init(ctx: &PyContext) { - let ref memoryview_type = ctx.memoryview_type; + let memoryview_type = &ctx.memoryview_type; ctx.set_attr( &memoryview_type, "__new__", diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 5c425087e..37e9f2bff 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -86,7 +86,7 @@ fn object_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn init(context: &PyContext) { - let ref object = context.object; + let object = &context.object; context.set_attr(&object, "__new__", context.new_rustfunc(new_instance)); context.set_attr(&object, "__init__", context.new_rustfunc(object_init)); context.set_attr(&object, "__eq__", context.new_rustfunc(object_eq)); diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index 71c1b5ab9..1d146889b 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -9,7 +9,7 @@ use super::super::vm::VirtualMachine; use super::objtype; pub fn init(context: &PyContext) { - let ref property_type = context.property_type; + let property_type = &context.property_type; context.set_attr( &property_type, "__get__", diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 18791cf78..da2f00630 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -151,7 +151,7 @@ fn frozenset_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn init(context: &PyContext) { - let ref set_type = context.set_type; + let set_type = &context.set_type; context.set_attr( &set_type, "__contains__", @@ -162,7 +162,7 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__repr__", context.new_rustfunc(set_repr)); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); - let ref frozenset_type = context.frozenset_type; + let frozenset_type = &context.frozenset_type; context.set_attr( &frozenset_type, "__contains__", diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 4eea5a966..fa8b7bb27 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -16,7 +16,7 @@ extern crate unicode_segmentation; use self::unicode_segmentation::UnicodeSegmentation; pub fn init(context: &PyContext) { - let ref str_type = context.str_type; + let str_type = &context.str_type; context.set_attr(&str_type, "__add__", context.new_rustfunc(str_add)); context.set_attr(&str_type, "__eq__", context.new_rustfunc(str_eq)); context.set_attr( diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 850f64c1a..377bfe604 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -11,7 +11,7 @@ use super::super::vm::VirtualMachine; use super::objtype; pub fn init(context: &PyContext) { - let ref super_type = context.super_type; + let super_type = &context.super_type; context.set_attr(&super_type, "__init__", context.new_rustfunc(super_init)); } diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index dc10020f4..fedbf85e3 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -275,7 +275,7 @@ pub fn tuple_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn init(context: &PyContext) { - let ref tuple_type = context.tuple_type; + let tuple_type = &context.tuple_type; context.set_attr(&tuple_type, "__add__", context.new_rustfunc(tuple_add)); context.set_attr(&tuple_type, "__eq__", context.new_rustfunc(tuple_eq)); context.set_attr( diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 059b59ff4..95bcf9f42 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -22,7 +22,7 @@ pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: } pub fn init(context: &PyContext) { - let ref type_type = context.type_type; + let type_type = &context.type_type; context.set_attr(&type_type, "__call__", context.new_rustfunc(type_call)); context.set_attr(&type_type, "__new__", context.new_rustfunc(type_new)); context.set_attr( @@ -263,7 +263,7 @@ fn take_next_base( } if let Some(head) = next { - for ref mut item in &mut bases { + for item in &mut bases { if item[0].get_id() == head.get_id() { item.remove(0); } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 69fc083a5..07bfaeb50 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -717,7 +717,7 @@ impl AttributeProtocol for PyObjectRef { if let Some(item) = class_get_item(self, attr_name) { return Some(item); } - for ref class in mro { + for class in mro { if let Some(item) = class_get_item(class, attr_name) { return Some(item); } From b0cbb23b4319623431418b7a9f3456f5152ef1cc Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 02:09:17 +0300 Subject: [PATCH 069/153] Fixed the 'collapsible_if' clippy warnings Relevant clippy warning: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if --- vm/src/obj/objobject.rs | 28 +++++++++++++--------------- vm/src/obj/objstr.rs | 8 ++------ vm/src/obj/objtype.rs | 28 +++++++++++++--------------- vm/src/stdlib/math.rs | 16 ++++++---------- 4 files changed, 34 insertions(+), 46 deletions(-) diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 5c425087e..cb834e573 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -151,21 +151,19 @@ fn object_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(obj_attr) } else if let Some(attr) = cls.get_attr(&name) { vm.call_get_descriptor(attr, obj.clone()) + } else if let Some(getter) = cls.get_attr("__getattr__") { + vm.invoke( + getter, + PyFuncArgs { + args: vec![cls, name_str.clone()], + kwargs: vec![], + }, + ) } else { - if let Some(getter) = cls.get_attr("__getattr__") { - vm.invoke( - getter, - PyFuncArgs { - args: vec![cls, name_str.clone()], - kwargs: vec![], - }, - ) - } else { - let attribute_error = vm.context().exceptions.attribute_error.clone(); - Err(vm.new_exception( - attribute_error, - format!("{} has no attribute '{}'", obj.borrow(), name), - )) - } + let attribute_error = vm.context().exceptions.attribute_error.clone(); + Err(vm.new_exception( + attribute_error, + format!("{} has no attribute '{}'", obj.borrow(), name), + )) } } diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 4eea5a966..54b833a05 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -450,12 +450,8 @@ fn str_isidentifier(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { && !value.chars().nth(0).unwrap().is_digit(10) { for c in value.chars() { - if c != "_".chars().nth(0).unwrap() { - if !c.is_digit(10) { - if !c.is_alphabetic() { - is_identifier = false; - } - } + if c != "_".chars().nth(0).unwrap() && !c.is_digit(10) && !c.is_alphabetic() { + is_identifier = false; } } } else { diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 059b59ff4..5c7e9e672 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -192,22 +192,20 @@ pub fn type_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult Ok(cls_attr) } else if let Some(attr) = mcl.get_attr(&name) { vm.call_get_descriptor(attr, cls.clone()) + } else if let Some(getter) = cls.get_attr("__getattr__") { + vm.invoke( + getter, + PyFuncArgs { + args: vec![mcl, name_str.clone()], + kwargs: vec![], + }, + ) } else { - if let Some(getter) = cls.get_attr("__getattr__") { - vm.invoke( - getter, - PyFuncArgs { - args: vec![mcl, name_str.clone()], - kwargs: vec![], - }, - ) - } else { - let attribute_error = vm.context().exceptions.attribute_error.clone(); - Err(vm.new_exception( - attribute_error, - format!("{} has no attribute '{}'", cls.borrow(), name), - )) - } + let attribute_error = vm.context().exceptions.attribute_error.clone(); + Err(vm.new_exception( + attribute_error, + format!("{} has no attribute '{}'", cls.borrow(), name), + )) } } diff --git a/vm/src/stdlib/math.rs b/vm/src/stdlib/math.rs index 50e7dbde9..012d11ace 100644 --- a/vm/src/stdlib/math.rs +++ b/vm/src/stdlib/math.rs @@ -177,12 +177,10 @@ fn math_gamma(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if x.is_finite() { Ok(vm.ctx.new_float(gamma(x))) + } else if x.is_nan() || x.is_sign_positive() { + Ok(vm.ctx.new_float(x)) } else { - if x.is_nan() || x.is_sign_positive() { - Ok(vm.ctx.new_float(x)) - } else { - Ok(vm.ctx.new_float(std::f64::NAN)) - } + Ok(vm.ctx.new_float(std::f64::NAN)) } } @@ -192,12 +190,10 @@ fn math_lgamma(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if x.is_finite() { Ok(vm.ctx.new_float(ln_gamma(x))) + } else if x.is_nan() { + Ok(vm.ctx.new_float(x)) } else { - if x.is_nan() { - Ok(vm.ctx.new_float(x)) - } else { - Ok(vm.ctx.new_float(std::f64::INFINITY)) - } + Ok(vm.ctx.new_float(std::f64::INFINITY)) } } From b43c51154222e376be658032e2de1f3bdc90d085 Mon Sep 17 00:00:00 2001 From: silmeth Date: Tue, 5 Feb 2019 19:49:14 +0100 Subject: [PATCH 070/153] fix range len() for negative and non-divisible steps --- vm/src/obj/objrange.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 5924cd048..7e25fd058 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -4,7 +4,7 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objint; use super::objtype; -use num_bigint::{BigInt, ToBigInt}; +use num_bigint::{BigInt, ToBigInt, Sign}; use num_traits::{One, Signed, ToPrimitive, Zero}; #[derive(Debug, Clone)] @@ -19,10 +19,13 @@ pub struct RangeType { impl RangeType { #[inline] pub fn len(&self) -> usize { - ((self.end.clone() - self.start.clone()) / self.step.clone()) - .abs() - .to_usize() - .unwrap() + match self.step.sign() { + Sign::Plus if self.start < self.end => + ((&self.end - &self.start - 1usize) / &self.step).to_usize().unwrap() + 1, + Sign::Minus if self.start > self.end => + ((&self.start - &self.end - 1usize) / (-&self.step)).to_usize().unwrap() + 1, + _ => 0, + } } #[inline] From 4b2787ea1d695cac7eb30fb3284a2a7a15c8174e Mon Sep 17 00:00:00 2001 From: Timur Date: Tue, 5 Feb 2019 21:54:13 +0300 Subject: [PATCH 071/153] Comment out math from tests due to Travis errors --- tests/snippets/math.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/snippets/math.py b/tests/snippets/math.py index 2270ff058..09f3ed3b3 100644 --- a/tests/snippets/math.py +++ b/tests/snippets/math.py @@ -17,13 +17,13 @@ assert a - 3 == 1 assert -a == -4 assert +a == 4 -import math -assert(math.exp(2) == math.exp(2.0)) -assert(math.exp(True) == math.exp(1.0)) - -class Conversible(): - def __float__(self): - print("Converting to float now!") - return 1.1111 - -assert math.log(1.1111) == math.log(Conversible()) +# import math +# assert(math.exp(2) == math.exp(2.0)) +# assert(math.exp(True) == math.exp(1.0)) +# +# class Conversible(): +# def __float__(self): +# print("Converting to float now!") +# return 1.1111 +# +# assert math.log(1.1111) == math.log(Conversible()) From fb9fa27555976fbf7ac5f1bec3aa3e9bf11ba38f Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 01:01:11 +0300 Subject: [PATCH 072/153] Fixed the 'redundant_field_names' clippy warnings This replaces all the occurrences of the Struct {field : field} with the Struct {field}. Relevant clippy warning: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names --- vm/src/builtins.rs | 4 ++-- vm/src/bytecode.rs | 12 +++++----- vm/src/compile.rs | 24 ++++++++++---------- vm/src/frame.rs | 4 ++-- vm/src/obj/objbytearray.rs | 5 +---- vm/src/obj/objbytes.rs | 5 +---- vm/src/obj/objgenerator.rs | 2 +- vm/src/obj/objlist.rs | 2 +- vm/src/obj/objobject.rs | 2 +- vm/src/obj/objset.rs | 2 +- vm/src/obj/objtuple.rs | 2 +- vm/src/obj/objtype.rs | 4 ++-- vm/src/pyobject.rs | 45 +++++++++++++------------------------- vm/src/stdlib/weakref.rs | 2 +- vm/src/vm.rs | 8 +++---- 15 files changed, 51 insertions(+), 72 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 8b8c8690b..d4144ffae 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -238,7 +238,7 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // TODO: handle optional globals // Construct new scope: let scope_inner = Scope { - locals: locals, + locals, parent: None, }; let scope = PyObject { @@ -288,7 +288,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // Construct new scope: let scope_inner = Scope { - locals: locals, + locals, parent: None, }; let scope = PyObject { diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index 0c08d0d1b..77b12917a 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -40,12 +40,12 @@ impl CodeObject { instructions: Vec::new(), label_map: HashMap::new(), locations: Vec::new(), - arg_names: arg_names, - varargs: varargs, - kwonlyarg_names: kwonlyarg_names, - varkeywords: varkeywords, - source_path: source_path, - obj_name: obj_name, + arg_names, + varargs, + kwonlyarg_names, + varkeywords, + source_path, + obj_name, is_generator: false, } } diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 71439463d..607e99548 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -53,7 +53,7 @@ pub fn compile( let code = compiler.pop_code_object(); trace!("Compilation completed: {:?}", code); Ok(PyObject::new( - PyObjectPayload::Code { code: code }, + PyObjectPayload::Code { code }, vm.ctx.code_type(), )) } @@ -432,7 +432,7 @@ impl Compiler { self.prepare_decorators(decorator_list)?; self.emit(Instruction::LoadConst { - value: bytecode::Constant::Code { code: code }, + value: bytecode::Constant::Code { code }, }); self.emit(Instruction::LoadConst { value: bytecode::Constant::String { @@ -441,7 +441,7 @@ impl Compiler { }); // Turn code object into function object: - self.emit(Instruction::MakeFunction { flags: flags }); + self.emit(Instruction::MakeFunction { flags }); self.apply_decorators(decorator_list); self.emit(Instruction::StoreName { @@ -477,7 +477,7 @@ impl Compiler { let code = self.pop_code_object(); self.emit(Instruction::LoadConst { - value: bytecode::Constant::Code { code: code }, + value: bytecode::Constant::Code { code }, }); self.emit(Instruction::LoadConst { value: bytecode::Constant::String { @@ -905,7 +905,7 @@ impl Compiler { let size = elements.len(); let must_unpack = self.gather_elements(elements)?; self.emit(Instruction::BuildList { - size: size, + size, unpack: must_unpack, }); } @@ -913,7 +913,7 @@ impl Compiler { let size = elements.len(); let must_unpack = self.gather_elements(elements)?; self.emit(Instruction::BuildTuple { - size: size, + size, unpack: must_unpack, }); } @@ -921,7 +921,7 @@ impl Compiler { let size = elements.len(); let must_unpack = self.gather_elements(elements)?; self.emit(Instruction::BuildSet { - size: size, + size, unpack: must_unpack, }); } @@ -932,7 +932,7 @@ impl Compiler { self.compile_expression(value)?; } self.emit(Instruction::BuildMap { - size: size, + size, unpack: false, }); } @@ -941,7 +941,7 @@ impl Compiler { for element in elements { self.compile_expression(element)?; } - self.emit(Instruction::BuildSlice { size: size }); + self.emit(Instruction::BuildSlice { size }); } ast::Expression::Yield { value } => { self.mark_generator(); @@ -1003,13 +1003,13 @@ impl Compiler { self.emit(Instruction::ReturnValue); let code = self.pop_code_object(); self.emit(Instruction::LoadConst { - value: bytecode::Constant::Code { code: code }, + value: bytecode::Constant::Code { code }, }); self.emit(Instruction::LoadConst { value: bytecode::Constant::String { value: name }, }); // Turn code object into function object: - self.emit(Instruction::MakeFunction { flags: flags }); + self.emit(Instruction::MakeFunction { flags }); } ast::Expression::Comprehension { kind, generators } => { self.compile_comprehension(kind, generators)?; @@ -1287,7 +1287,7 @@ impl Compiler { // List comprehension code: self.emit(Instruction::LoadConst { - value: bytecode::Constant::Code { code: code }, + value: bytecode::Constant::Code { code }, }); // List comprehension function name: diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 2c8ada87b..f230fbc43 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -76,7 +76,7 @@ impl Frame { blocks: vec![], // save the callargs as locals // globals: locals.clone(), - locals: locals, + locals, lasti: 0, } } @@ -440,7 +440,7 @@ impl Frame { bytecode::CallType::Positional(count) => { let args: Vec = self.pop_multiple(*count); PyFuncArgs { - args: args, + args, kwargs: vec![], } } diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 0f7e8d8da..d7f732e24 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -63,10 +63,7 @@ fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } else { vec![] }; - Ok(PyObject::new( - PyObjectPayload::Bytes { value: value }, - cls.clone(), - )) + Ok(PyObject::new(PyObjectPayload::Bytes { value }, cls.clone())) } fn bytesarray_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 55ca04c9b..f2e0e101d 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -47,10 +47,7 @@ fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vec![] }; - Ok(PyObject::new( - PyObjectPayload::Bytes { value: value }, - cls.clone(), - )) + Ok(PyObject::new(PyObjectPayload::Bytes { value }, cls.clone())) } fn bytes_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index c98dcadf2..bf800647d 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -30,7 +30,7 @@ pub fn init(context: &PyContext) { pub fn new_generator(vm: &mut VirtualMachine, frame: Frame) -> PyResult { let g = PyObject::new( - PyObjectPayload::Generator { frame: frame }, + PyObjectPayload::Generator { frame }, vm.ctx.generator_type.clone(), ); Ok(g) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 4175b6005..b1a8dc097 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -52,7 +52,7 @@ fn list_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectPayload::Sequence { elements: elements }, + PyObjectPayload::Sequence { elements }, cls.clone(), )) } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 5c425087e..3006de790 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -12,7 +12,7 @@ pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { // more or less __new__ operator let type_ref = args.shift(); let dict = vm.new_dict(); - let obj = PyObject::new(PyObjectPayload::Instance { dict: dict }, type_ref.clone()); + let obj = PyObject::new(PyObjectPayload::Instance { dict }, type_ref.clone()); Ok(obj) } diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 18791cf78..1b93af66a 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -82,7 +82,7 @@ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectPayload::Set { elements: elements }, + PyObjectPayload::Set { elements }, cls.clone(), )) } diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index dc10020f4..a3b4cadb7 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -206,7 +206,7 @@ fn tuple_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; Ok(PyObject::new( - PyObjectPayload::Sequence { elements: elements }, + PyObjectPayload::Sequence { elements }, cls.clone(), )) } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 059b59ff4..4efae42cb 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -297,8 +297,8 @@ pub fn new(typ: PyObjectRef, name: &str, bases: Vec, dict: PyObject Ok(PyObject::new( PyObjectPayload::Class { name: String::from(name), - dict: dict, - mro: mro, + dict, + mro, }, typ, )) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 69fc083a5..8e78ca56c 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -241,9 +241,9 @@ impl PyContext { tuple_type, iter_type, dict_type, - none: none, - str_type: str_type, - range_type: range_type, + none, + str_type, + range_type, object: object_type, function_type, super_type, @@ -436,22 +436,16 @@ impl PyContext { } pub fn new_tuple(&self, elements: Vec) -> PyObjectRef { - PyObject::new( - PyObjectPayload::Sequence { elements: elements }, - self.tuple_type(), - ) + PyObject::new(PyObjectPayload::Sequence { elements }, self.tuple_type()) } pub fn new_list(&self, elements: Vec) -> PyObjectRef { - PyObject::new( - PyObjectPayload::Sequence { elements: elements }, - self.list_type(), - ) + PyObject::new(PyObjectPayload::Sequence { elements }, self.list_type()) } pub fn new_set(&self, elements: Vec) -> PyObjectRef { let elements = objset::sequence_to_hashmap(&elements); - PyObject::new(PyObjectPayload::Set { elements: elements }, self.set_type()) + PyObject::new(PyObjectPayload::Set { elements }, self.set_type()) } pub fn new_dict(&self) -> PyObjectRef { @@ -469,12 +463,9 @@ impl PyContext { pub fn new_scope(&self, parent: Option) -> PyObjectRef { let locals = self.new_dict(); - let scope = Scope { - locals: locals, - parent: parent, - }; + let scope = Scope { locals, parent }; PyObject { - payload: PyObjectPayload::Scope { scope: scope }, + payload: PyObjectPayload::Scope { scope }, typ: None, } .into_ref() @@ -513,7 +504,7 @@ impl PyContext { } pub fn new_frame(&self, frame: Frame) -> PyObjectRef { - PyObject::new(PyObjectPayload::Frame { frame: frame }, self.frame_type()) + PyObject::new(PyObjectPayload::Frame { frame }, self.frame_type()) } pub fn new_property PyResult>( @@ -544,8 +535,8 @@ impl PyContext { PyObject::new( PyObjectPayload::Function { code: code_obj, - scope: scope, - defaults: defaults, + scope, + defaults, }, self.function_type(), ) @@ -553,10 +544,7 @@ impl PyContext { pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef { PyObject::new( - PyObjectPayload::BoundMethod { - function: function, - object: object, - }, + PyObjectPayload::BoundMethod { function, object }, self.bound_method_type(), ) } @@ -571,7 +559,7 @@ impl PyContext { } pub fn new_instance(&self, dict: PyObjectRef, class: PyObjectRef) -> PyObjectRef { - PyObject::new(PyObjectPayload::Instance { dict: dict }, class) + PyObject::new(PyObjectPayload::Instance { dict }, class) } // Item set/get: @@ -815,10 +803,7 @@ impl PyFuncArgs { for name in kwarg_names.iter().rev() { kwargs.push((name.clone(), args.pop().unwrap())); } - PyFuncArgs { - args: args, - kwargs: kwargs, - } + PyFuncArgs { args, kwargs } } pub fn insert(&self, item: PyObjectRef) -> PyFuncArgs { @@ -990,7 +975,7 @@ impl PyObject { /* dict: PyObjectRef,*/ typ: PyObjectRef, ) -> PyObjectRef { PyObject { - payload: payload, + payload, typ: Some(typ), // dict: HashMap::new(), // dict, } diff --git a/vm/src/stdlib/weakref.rs b/vm/src/stdlib/weakref.rs index f1294b3c0..8f407675b 100644 --- a/vm/src/stdlib/weakref.rs +++ b/vm/src/stdlib/weakref.rs @@ -28,7 +28,7 @@ fn ref_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(cls, None), (referent, None)]); let referent = Rc::downgrade(referent); Ok(PyObject::new( - PyObjectPayload::WeakRef { referent: referent }, + PyObjectPayload::WeakRef { referent }, cls.clone(), )) } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 061c99605..6cdf3805f 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -53,10 +53,10 @@ impl VirtualMachine { let stdlib_inits = stdlib::get_module_inits(); VirtualMachine { - builtins: builtins, + builtins, sys_module: sysmod, stdlib_inits, - ctx: ctx, + ctx, current_frame: None, } } @@ -86,7 +86,7 @@ impl VirtualMachine { let pymsg = self.new_str(msg); let args: Vec = vec![pymsg]; let args = PyFuncArgs { - args: args, + args, kwargs: vec![], }; @@ -211,7 +211,7 @@ impl VirtualMachine { obj, method_name, PyFuncArgs { - args: args, + args, kwargs: vec![], }, ) From 0a1eb4b91bb4ea1f796189b2a70cb8711d345b23 Mon Sep 17 00:00:00 2001 From: silmeth Date: Tue, 5 Feb 2019 20:58:38 +0100 Subject: [PATCH 073/153] make len(range) throw OverflowError on too big ints + impl __repr__ --- vm/src/exceptions.rs | 14 +++++++++++++ vm/src/obj/objrange.rs | 46 +++++++++++++++++++++++++++++++++--------- vm/src/vm.rs | 9 +++++++-- 3 files changed, 58 insertions(+), 11 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 305297919..63e17e7c6 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -81,6 +81,7 @@ fn exception_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { #[derive(Debug)] pub struct ExceptionZoo { + pub arithmetic_error: PyObjectRef, pub assertion_error: PyObjectRef, pub attribute_error: PyObjectRef, pub base_exception_type: PyObjectRef, @@ -93,12 +94,14 @@ pub struct ExceptionZoo { pub name_error: PyObjectRef, pub not_implemented_error: PyObjectRef, pub os_error: PyObjectRef, + pub overflow_error: PyObjectRef, pub permission_error: PyObjectRef, pub runtime_error: PyObjectRef, pub stop_iteration: PyObjectRef, pub syntax_error: PyObjectRef, pub type_error: PyObjectRef, pub value_error: PyObjectRef, + pub zero_division_error: PyObjectRef, } impl ExceptionZoo { @@ -113,6 +116,8 @@ impl ExceptionZoo { let exception_type = create_type("Exception", &type_type, &base_exception_type, &dict_type); + let arithmetic_error = + create_type("ArithmeticError", &type_type, &exception_type, &dict_type); let assertion_error = create_type("AssertionError", &type_type, &exception_type, &dict_type); let attribute_error = @@ -128,8 +133,14 @@ impl ExceptionZoo { let type_error = create_type("TypeError", &type_type, &exception_type, &dict_type); let value_error = create_type("ValueError", &type_type, &exception_type, &dict_type); + let overflow_error = + create_type("OverflowError", &type_type, &arithmetic_error, &dict_type); + let zero_division_error = + create_type("ZeroDivisionError", &type_type, &arithmetic_error, &dict_type); + let module_not_found_error = create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type); + let not_implemented_error = create_type( "NotImplementedError", &type_type, @@ -142,6 +153,7 @@ impl ExceptionZoo { let permission_error = create_type("PermissionError", &type_type, &os_error, &dict_type); ExceptionZoo { + arithmetic_error, assertion_error, attribute_error, base_exception_type, @@ -154,12 +166,14 @@ impl ExceptionZoo { name_error, not_implemented_error, os_error, + overflow_error, permission_error, runtime_error, stop_iteration, syntax_error, type_error, value_error, + zero_division_error, } } } diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 7e25fd058..c79a26492 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -18,16 +18,21 @@ pub struct RangeType { impl RangeType { #[inline] - pub fn len(&self) -> usize { + pub fn try_len(&self) -> Option { match self.step.sign() { Sign::Plus if self.start < self.end => - ((&self.end - &self.start - 1usize) / &self.step).to_usize().unwrap() + 1, + ((&self.end - &self.start - 1usize) / &self.step).to_usize().map(|sz| sz + 1), Sign::Minus if self.start > self.end => - ((&self.start - &self.end - 1usize) / (-&self.step)).to_usize().unwrap() + 1, - _ => 0, + ((&self.start - &self.end - 1usize) / (-&self.step)).to_usize().map(|sz| sz + 1), + _ => Some(0), } } + #[inline] + pub fn len(&self) -> usize { + self.try_len().unwrap() + } + #[inline] pub fn is_empty(&self) -> bool { (self.start <= self.end && self.step.is_negative()) @@ -51,6 +56,15 @@ impl RangeType { None } } + + #[inline] + pub fn repr(&self) -> String { + if self.step == BigInt::one() { + format!("range({}, {})", self.start, self.end) + } else { + format!("range({}, {}, {})", self.start, self.end, self.step) + } + } } pub fn init(context: &PyContext) { @@ -63,6 +77,7 @@ pub fn init(context: &PyContext) { "__getitem__", context.new_rustfunc(range_getitem), ); + context.set_attr(&range_type, "__repr__", context.new_rustfunc(range_repr)); } fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -121,12 +136,14 @@ fn range_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn range_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - let len = match zelf.borrow().payload { - PyObjectPayload::Range { ref range } => range.len(), + if let Some(len) = match zelf.borrow().payload { + PyObjectPayload::Range { ref range } => range.try_len(), _ => unreachable!(), - }; - - Ok(vm.ctx.new_int(len.to_bigint().unwrap())) + } { + Ok(vm.ctx.new_int(len.to_bigint().unwrap())) + } else { + Err(vm.new_overflow_error("Python int too large to convert to Rust usize".to_string())) + } } fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -196,3 +213,14 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { _ => Err(vm.new_type_error("range indices must be integer or slice".to_string())), } } + +fn range_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); + + let s = match zelf.borrow().payload { + PyObjectPayload::Range { ref range } => range.repr(), + _ => unreachable!(), + }; + + Ok(vm.ctx.new_str(s)) +} diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 061c99605..56004271f 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -105,6 +105,11 @@ impl VirtualMachine { self.new_exception(os_error, msg) } + pub fn new_overflow_error(&mut self, msg: String) -> PyObjectRef { + let overflow_error = self.ctx.exceptions.overflow_error.clone(); + self.new_exception(overflow_error, msg) + } + /// Create a new python ValueError object. Useful for raising errors from /// python functions implemented in rust. pub fn new_value_error(&mut self, msg: String) -> PyObjectRef { @@ -123,8 +128,8 @@ impl VirtualMachine { } pub fn new_not_implemented_error(&mut self, msg: String) -> PyObjectRef { - let value_error = self.ctx.exceptions.not_implemented_error.clone(); - self.new_exception(value_error, msg) + let not_implemented_error = self.ctx.exceptions.not_implemented_error.clone(); + self.new_exception(not_implemented_error, msg) } pub fn new_scope(&mut self, parent_scope: Option) -> PyObjectRef { From d96e0ecf40bfda9288bf30a3f1be1b66250147c5 Mon Sep 17 00:00:00 2001 From: silmeth Date: Tue, 5 Feb 2019 21:28:48 +0100 Subject: [PATCH 074/153] make division and modulo by 0 throw ZeroDivisionError --- vm/src/obj/objint.rs | 32 +++++++++++++++++++++++--------- vm/src/vm.rs | 5 +++++ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 083741023..e0e275dce 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -370,17 +370,25 @@ fn int_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); - let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.int_type()) { - Ok(vm - .ctx - .new_float(v1.to_f64().unwrap() / get_value(i2).to_f64().unwrap())) + + let v1 = get_value(i).to_f64() + .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))?; + + let v2 = if objtype::isinstance(i2, &vm.ctx.int_type()) { + get_value(i2).to_f64() + .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? } else if objtype::isinstance(i2, &vm.ctx.float_type()) { + objfloat::get_value(i2) + } else { + return Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow()))); + }; + + if v2 == 0.0 { + Err(vm.new_zero_division_error("integer division by zero".to_string())) + } else { Ok(vm .ctx - .new_float(v1.to_f64().unwrap() / objfloat::get_value(i2))) - } else { - Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow()))) + .new_float(v1 / v2)) } } @@ -392,7 +400,13 @@ fn int_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); if objtype::isinstance(i2, &vm.ctx.int_type()) { - Ok(vm.ctx.new_int(v1 % get_value(i2))) + let v2 = get_value(i2); + + if v2 != BigInt::zero() { + Ok(vm.ctx.new_int(v1 % get_value(i2))) + } else { + Err(vm.new_zero_division_error("integer modulo by zero".to_string())) + } } else { Err(vm.new_type_error(format!("Cannot modulo {} and {}", i.borrow(), i2.borrow()))) } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 56004271f..3d56818c6 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -132,6 +132,11 @@ impl VirtualMachine { self.new_exception(not_implemented_error, msg) } + pub fn new_zero_division_error(&mut self, msg: String) -> PyObjectRef { + let zero_division_error = self.ctx.exceptions.zero_division_error.clone(); + self.new_exception(zero_division_error, msg) + } + pub fn new_scope(&mut self, parent_scope: Option) -> PyObjectRef { // let parent_scope = self.current_frame_mut().locals.clone(); self.ctx.new_scope(parent_scope) From 30c8e477e45a8d333556c64d03a9397ac024482c Mon Sep 17 00:00:00 2001 From: silmeth Date: Tue, 5 Feb 2019 21:54:03 +0100 Subject: [PATCH 075/153] style: rustfmt --- vm/src/exceptions.rs | 8 ++++++-- vm/src/obj/objint.rs | 10 +++++----- vm/src/obj/objrange.rs | 16 ++++++++++------ 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 63e17e7c6..be3652a73 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -135,8 +135,12 @@ impl ExceptionZoo { let overflow_error = create_type("OverflowError", &type_type, &arithmetic_error, &dict_type); - let zero_division_error = - create_type("ZeroDivisionError", &type_type, &arithmetic_error, &dict_type); + let zero_division_error = create_type( + "ZeroDivisionError", + &type_type, + &arithmetic_error, + &dict_type, + ); let module_not_found_error = create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type); diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index e0e275dce..c31709595 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -371,11 +371,13 @@ fn int_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); - let v1 = get_value(i).to_f64() + let v1 = get_value(i) + .to_f64() .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))?; let v2 = if objtype::isinstance(i2, &vm.ctx.int_type()) { - get_value(i2).to_f64() + get_value(i2) + .to_f64() .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? } else if objtype::isinstance(i2, &vm.ctx.float_type()) { objfloat::get_value(i2) @@ -386,9 +388,7 @@ fn int_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if v2 == 0.0 { Err(vm.new_zero_division_error("integer division by zero".to_string())) } else { - Ok(vm - .ctx - .new_float(v1 / v2)) + Ok(vm.ctx.new_float(v1 / v2)) } } diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index c79a26492..cd255b29a 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -4,7 +4,7 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objint; use super::objtype; -use num_bigint::{BigInt, ToBigInt, Sign}; +use num_bigint::{BigInt, Sign, ToBigInt}; use num_traits::{One, Signed, ToPrimitive, Zero}; #[derive(Debug, Clone)] @@ -20,10 +20,14 @@ impl RangeType { #[inline] pub fn try_len(&self) -> Option { match self.step.sign() { - Sign::Plus if self.start < self.end => - ((&self.end - &self.start - 1usize) / &self.step).to_usize().map(|sz| sz + 1), - Sign::Minus if self.start > self.end => - ((&self.start - &self.end - 1usize) / (-&self.step)).to_usize().map(|sz| sz + 1), + Sign::Plus if self.start < self.end => ((&self.end - &self.start - 1usize) + / &self.step) + .to_usize() + .map(|sz| sz + 1), + Sign::Minus if self.start > self.end => ((&self.start - &self.end - 1usize) + / (-&self.step)) + .to_usize() + .map(|sz| sz + 1), _ => Some(0), } } @@ -56,7 +60,7 @@ impl RangeType { None } } - + #[inline] pub fn repr(&self) -> String { if self.step == BigInt::one() { From b0a2f6f87d9af4b49dbe45b70ed7cb0d224a6638 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Tue, 5 Feb 2019 13:20:32 -0800 Subject: [PATCH 076/153] Add range.index --- Cargo.lock | 1 + tests/snippets/builtin_range.py | 30 ++++++++++++++++++++++++ vm/Cargo.toml | 1 + vm/src/lib.rs | 1 + vm/src/obj/objrange.rs | 41 +++++++++++++++++++++++++++++++++ 5 files changed, 74 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index b1342cd72..25a156c8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -709,6 +709,7 @@ dependencies = [ "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/tests/snippets/builtin_range.py b/tests/snippets/builtin_range.py index 3284fa2b4..5e58ae069 100644 --- a/tests/snippets/builtin_range.py +++ b/tests/snippets/builtin_range.py @@ -1 +1,31 @@ +def assert_raises(expr, exc_type): + """ + Helper function to assert `expr` raises an exception of type `exc_type` + Args: + expr: Callable + exec_type: Exception + Returns: + None + Raises: + Assertion error on failure + """ + try: + expr(None) + except exc_type: + assert True + else: + assert False + assert range(2**63+1)[2**63] == 9223372036854775808 + +# index tests +assert range(10).index(6) == 6 +assert range(4, 10).index(6) == 2 +assert range(4, 10, 2).index(6) == 1 + +# index raises value error on out of bounds +assert_raises(lambda _: range(10).index(-1), ValueError) +assert_raises(lambda _: range(10).index(10), ValueError) + +# index raises value error if out of step +assert_raises(lambda _: range(4, 10, 2).index(5), ValueError) diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 59e10da64..fe278edb7 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -8,6 +8,7 @@ bitflags = "1.0.4" num-complex = "0.2" num-bigint = "0.2.1" num-traits = "0.2" +num-integer = "0.1.39" rand = "0.5" log = "0.3" rustpython_parser = {path = "../parser"} diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 7c1df250e..107ac3896 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -12,6 +12,7 @@ extern crate log; // extern crate env_logger; extern crate num_bigint; extern crate num_complex; +extern crate num_integer; extern crate num_traits; extern crate serde; extern crate serde_json; diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 5924cd048..b6ca2c291 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -5,6 +5,7 @@ use super::super::vm::VirtualMachine; use super::objint; use super::objtype; use num_bigint::{BigInt, ToBigInt}; +use num_integer::Integer; use num_traits::{One, Signed, ToPrimitive, Zero}; #[derive(Debug, Clone)] @@ -25,6 +26,25 @@ impl RangeType { .unwrap() } + #[inline] + pub fn contains(&self, value: &BigInt) -> bool { + value >= &self.start && value < &self.end + } + + #[inline] + pub fn index_of(&self, value: &BigInt) -> Option { + if !self.contains(value) { + return None; + } + + let offset = value - &self.start; + if offset.is_multiple_of(&self.step) { + Some(offset / &self.step) + } else { + None + } + } + #[inline] pub fn is_empty(&self) -> bool { (self.start <= self.end && self.step.is_negative()) @@ -60,6 +80,7 @@ pub fn init(context: &PyContext) { "__getitem__", context.new_rustfunc(range_getitem), ); + context.set_attr(&range_type, "index", context.new_rustfunc(range_index)); } fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -193,3 +214,23 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { _ => Err(vm.new_type_error("range indices must be integer or slice".to_string())), } } + +fn range_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.range_type())), (needle, None)] + ); + + if let PyObjectPayload::Range { ref range } = zelf.borrow().payload { + match needle.borrow().payload { + PyObjectPayload::Integer { ref value } => match range.index_of(value) { + Some(idx) => Ok(vm.ctx.new_int(idx)), + None => Err(vm.new_value_error(format!("{} is not in range", value))), + }, + _ => Err(vm.new_value_error("sequence.index(x): x not in sequence".to_string())), + } + } else { + unreachable!() + } +} From 8621f3ff2b24ec29ee14d3489c4ad79d85af1960 Mon Sep 17 00:00:00 2001 From: silmeth Date: Tue, 5 Feb 2019 22:48:33 +0100 Subject: [PATCH 077/153] fix: register arithmetic errors in builtins module --- vm/src/builtins.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 8b8c8690b..fb1d84c32 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -794,6 +794,11 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.exceptions.base_exception_type.clone(), ); ctx.set_attr(&py_mod, "Exception", ctx.exceptions.exception_type.clone()); + ctx.set_attr( + &py_mod, + "ArithmeticError", + ctx.exceptions.arithmetic_error.clone(), + ); ctx.set_attr( &py_mod, "AssertionError", @@ -805,6 +810,11 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.exceptions.attribute_error.clone(), ); ctx.set_attr(&py_mod, "NameError", ctx.exceptions.name_error.clone()); + ctx.set_attr( + &py_mod, + "OverflowError", + ctx.exceptions.overflow_error.clone(), + ); ctx.set_attr( &py_mod, "RuntimeError", @@ -819,6 +829,11 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "ValueError", ctx.exceptions.value_error.clone()); ctx.set_attr(&py_mod, "IndexError", ctx.exceptions.index_error.clone()); ctx.set_attr(&py_mod, "ImportError", ctx.exceptions.import_error.clone()); + ctx.set_attr( + &py_mod, + "ZeroDivisionError", + ctx.exceptions.zero_division_error.clone(), + ); py_mod } From 77ae6621e91dbb1a6f572cc1400165bda3ed1fd0 Mon Sep 17 00:00:00 2001 From: silmeth Date: Tue, 5 Feb 2019 22:50:52 +0100 Subject: [PATCH 078/153] add tests for range length, and for division by zero --- tests/snippets/builtin_range.py | 4 ++++ tests/snippets/division_by_zero.py | 34 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 tests/snippets/division_by_zero.py diff --git a/tests/snippets/builtin_range.py b/tests/snippets/builtin_range.py index 3284fa2b4..d1df0871b 100644 --- a/tests/snippets/builtin_range.py +++ b/tests/snippets/builtin_range.py @@ -1 +1,5 @@ assert range(2**63+1)[2**63] == 9223372036854775808 + +assert len(range(10, 5)) == 0, 'Range with no elements should have length = 0' +assert len(range(10, 5, -2)) == 3, 'Expected length 3, for elements: 10, 8, 6' +assert len(range(5, 10, 2)) == 3, 'Expected length 3, for elements: 5, 7, 9' diff --git a/tests/snippets/division_by_zero.py b/tests/snippets/division_by_zero.py new file mode 100644 index 000000000..b4b036c84 --- /dev/null +++ b/tests/snippets/division_by_zero.py @@ -0,0 +1,34 @@ +try: + 5 / 0 +except ZeroDivisionError: + pass +except: + assert False, 'Expected ZeroDivisionError' + +try: + 5 / -0.0 +except ZeroDivisionError: + pass +except: + assert False, 'Expected ZeroDivisionError' + +try: + 5 / (3-2) +except ZeroDivisionError: + pass +except: + assert False, 'Expected ZeroDivisionError' + +try: + 5 % 0 +except ZeroDivisionError: + pass +except: + assert False, 'Expected ZeroDivisionError' + +try: + raise ZeroDivisionError('Is an ArithmeticError subclass?') +except ArithmeticError: + pass +except: + assert False, 'Expected ZeroDivisionError to be a subclass of ArithmeticError' From 8cc6821c448f10943c3a79e167f2e189d9786e4f Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 6 Feb 2019 08:52:44 +1300 Subject: [PATCH 079/153] Make filter lazy --- tests/snippets/builtin_filter.py | 21 +++++ tests/snippets/builtins.py | 2 - vm/src/builtins.rs | 19 +---- vm/src/obj/objiter.rs | 129 +++++++++++++++++++++++-------- vm/src/pyobject.rs | 12 +++ 5 files changed, 129 insertions(+), 54 deletions(-) create mode 100644 tests/snippets/builtin_filter.py diff --git a/tests/snippets/builtin_filter.py b/tests/snippets/builtin_filter.py new file mode 100644 index 000000000..ca7cddf0c --- /dev/null +++ b/tests/snippets/builtin_filter.py @@ -0,0 +1,21 @@ + +assert list(filter(lambda x: ((x % 2) == 0), [0, 1, 2])) == [0, 2] + +assert list(filter(None, [0, 1, 2])) == [0, 1, 2] + + +# test infinite iterator +class Counter(object): + counter = 0 + + def __next__(self): + self.counter += 1 + return self.counter + + def __iter__(self): + return self + + +it = filter(lambda x: ((x % 2) == 0), Counter()) +assert next(it) == 2 +assert next(it) == 4 diff --git a/tests/snippets/builtins.py b/tests/snippets/builtins.py index bbf116abc..ee6ae6865 100644 --- a/tests/snippets/builtins.py +++ b/tests/snippets/builtins.py @@ -15,8 +15,6 @@ assert type(frozenset) is type assert list(zip(['a', 'b', 'c'], range(3), [9, 8, 7, 99])) == [('a', 0, 9), ('b', 1, 8), ('c', 2, 7)] -assert list(filter(lambda x: ((x % 2) == 0), [0, 1, 2])) == [0, 2] - assert 3 == eval('1+2') code = compile('5+3', 'x.py', 'eval') diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 83893ee83..16c184c19 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -304,24 +304,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn builtin_filter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(function, None), (iterable, None)]); - // TODO: process one element at a time from iterators. - let iterable = vm.extract_elements(iterable)?; - - let mut new_items = vec![]; - for element in iterable { - // apply function: - let args = PyFuncArgs { - args: vec![element.clone()], - kwargs: vec![], - }; - let result = vm.invoke(function.clone(), args)?; - let result = objbool::boolval(vm, result)?; - if result { - new_items.push(element); - } - } - - Ok(vm.ctx.new_list(new_items)) + objiter::create_filter(vm, function, iterable) } fn builtin_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 53ed76804..c0ff2b771 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -3,7 +3,8 @@ */ use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, + TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objbool; @@ -23,6 +24,10 @@ pub fn get_iter(vm: &mut VirtualMachine, iter_target: &PyObjectRef) -> PyResult // return Err(type_error); } +pub fn call_next(vm: &mut VirtualMachine, iter_obj: &PyObjectRef) -> PyResult { + vm.call_method(iter_obj, "__next__", vec![]) +} + /* * Helper function to retrieve the next object (or none) from an iterator. */ @@ -30,7 +35,7 @@ pub fn get_next_object( vm: &mut VirtualMachine, iter_obj: &PyObjectRef, ) -> Result, PyObjectRef> { - let next_obj: PyResult = vm.call_method(iter_obj, "__next__", vec![]); + let next_obj: PyResult = call_next(vm, iter_obj); match next_obj { Ok(value) => Ok(Some(value)), @@ -61,6 +66,38 @@ pub fn get_all( Ok(elements) } +// Should filter/map have their own class? +pub fn create_filter( + vm: &mut VirtualMachine, + predicate: &PyObjectRef, + iterable: &PyObjectRef, +) -> PyResult { + let iterator = get_iter(vm, iterable)?; + let iter_obj = PyObject::new( + PyObjectPayload::FilterIterator { + predicate: predicate.clone(), + iterator, + }, + vm.ctx.iter_type(), + ); + + Ok(iter_obj) +} + +//pub fn create_map(vm: &mut VirtualMachine, +// mapper: &PyObjectRef, +// iterators: &PyObjectRef) -> PyResult { +// let iter_obj = PyObject::new( +// PyObjectPayload::MapIterator { +// predicate: predicate.clone(), +// iterator: iterator.clone(), +// }, +// vm.ctx.iter_type(), +// ); +// +// Ok(iter_obj) +//} + // Sequence iterator: fn iter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iter_target, None)]); @@ -100,43 +137,67 @@ fn iter_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iter, Some(vm.ctx.iter_type()))]); - if let PyObjectPayload::Iterator { - ref mut position, - iterated_obj: ref mut iterated_obj_ref, - } = iter.borrow_mut().payload - { - let iterated_obj = iterated_obj_ref.borrow_mut(); - match iterated_obj.payload { - PyObjectPayload::Sequence { ref elements } => { - if *position < elements.len() { - let obj_ref = elements[*position].clone(); - *position += 1; - Ok(obj_ref) - } else { - let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); - let stop_iteration = - vm.new_exception(stop_iteration_type, "End of iterator".to_string()); - Err(stop_iteration) + let ref mut payload = iter.borrow_mut().payload; + match payload { + PyObjectPayload::Iterator { + ref mut position, + iterated_obj: ref mut iterated_obj_ref, + } => { + let iterated_obj = iterated_obj_ref.borrow_mut(); + match iterated_obj.payload { + PyObjectPayload::Sequence { ref elements } => { + if *position < elements.len() { + let obj_ref = elements[*position].clone(); + *position += 1; + Ok(obj_ref) + } else { + let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); + let stop_iteration = + vm.new_exception(stop_iteration_type, "End of iterator".to_string()); + Err(stop_iteration) + } } - } - PyObjectPayload::Range { ref range } => { - if let Some(int) = range.get(BigInt::from(*position)) { - *position += 1; - Ok(vm.ctx.new_int(int.to_bigint().unwrap())) - } else { - let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); - let stop_iteration = - vm.new_exception(stop_iteration_type, "End of iterator".to_string()); - Err(stop_iteration) + PyObjectPayload::Range { ref range } => { + if let Some(int) = range.get(BigInt::from(*position)) { + *position += 1; + Ok(vm.ctx.new_int(int.to_bigint().unwrap())) + } else { + let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); + let stop_iteration = + vm.new_exception(stop_iteration_type, "End of iterator".to_string()); + Err(stop_iteration) + } + } + _ => { + panic!("NOT IMPL"); } - } - _ => { - panic!("NOT IMPL"); } } - } else { - panic!("NOT IMPL"); + PyObjectPayload::FilterIterator { + ref mut predicate, + ref mut iterator, + } => { + loop { + let next_obj = call_next(vm, iterator)?; + if predicate.is(&vm.get_none()) { + return Ok(next_obj); + } + let predicate_value = vm.invoke( + predicate.clone(), + PyFuncArgs { + args: vec![next_obj.clone()], + kwargs: vec![], + }, + )?; // What happens if the predicate raise StopIteration + if objbool::boolval(vm, predicate_value)? { + return Ok(next_obj); + } + } + } + _ => { + panic!("NOT IMPL"); + } } } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index fec9578be..558dfcd6e 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -866,6 +866,14 @@ pub enum PyObjectPayload { position: usize, iterated_obj: PyObjectRef, }, + FilterIterator { + predicate: PyObjectRef, + iterator: PyObjectRef, + }, + MapIterator { + mapper: PyObjectRef, + iterators: Vec, + }, Slice { start: Option, stop: Option, @@ -934,6 +942,8 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), PyObjectPayload::Range { .. } => write!(f, "range"), PyObjectPayload::Iterator { .. } => write!(f, "iterator"), + PyObjectPayload::FilterIterator { .. } => write!(f, "filter"), + PyObjectPayload::MapIterator { .. } => write!(f, "map"), PyObjectPayload::Slice { .. } => write!(f, "slice"), PyObjectPayload::Code { ref code } => write!(f, "code: {:?}", code), PyObjectPayload::Function { .. } => write!(f, "function"), @@ -1030,6 +1040,8 @@ impl PyObject { position, iterated_obj.borrow_mut().str() ), + PyObjectPayload::FilterIterator { .. } => format!(""), + PyObjectPayload::MapIterator { .. } => format!(""), } } From 91fcd7782e81a08e44fa44d4e4dfe2d09f87be1c Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 6 Feb 2019 09:56:45 +1300 Subject: [PATCH 080/153] Make map lazy and accept multiple iterables --- tests/snippets/builtin_filter.py | 1 - tests/snippets/builtin_map.py | 23 +++++++++++++++ tests/snippets/builtins.py | 4 --- vm/src/builtins.rs | 30 +++++-------------- vm/src/obj/objiter.rs | 49 +++++++++++++++++++++++--------- 5 files changed, 66 insertions(+), 41 deletions(-) create mode 100644 tests/snippets/builtin_map.py diff --git a/tests/snippets/builtin_filter.py b/tests/snippets/builtin_filter.py index ca7cddf0c..1f654ad84 100644 --- a/tests/snippets/builtin_filter.py +++ b/tests/snippets/builtin_filter.py @@ -1,4 +1,3 @@ - assert list(filter(lambda x: ((x % 2) == 0), [0, 1, 2])) == [0, 2] assert list(filter(None, [0, 1, 2])) == [0, 1, 2] diff --git a/tests/snippets/builtin_map.py b/tests/snippets/builtin_map.py new file mode 100644 index 000000000..955351a67 --- /dev/null +++ b/tests/snippets/builtin_map.py @@ -0,0 +1,23 @@ +a = list(map(str, [1, 2, 3])) +assert a == ['1', '2', '3'] + + +b = list(map(lambda x, y: x + y, [1, 2, 4], [3, 5])) +assert b == [4, 7] + + +# test infinite iterator +class Counter(object): + counter = 0 + + def __next__(self): + self.counter += 1 + return self.counter + + def __iter__(self): + return self + + +it = map(lambda x: x+1, Counter()) +assert next(it) == 2 +assert next(it) == 3 diff --git a/tests/snippets/builtins.py b/tests/snippets/builtins.py index ee6ae6865..1a3dbb1ba 100644 --- a/tests/snippets/builtins.py +++ b/tests/snippets/builtins.py @@ -1,7 +1,3 @@ - -a = list(map(str, [1, 2, 3])) -assert a == ['1', '2', '3'] - x = sum(map(int, a)) assert x == 6 diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 16c184c19..f64609d54 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -412,30 +412,14 @@ fn builtin_locals(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn builtin_map(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(function, None), (iter_target, None)]); - let iterator = objiter::get_iter(vm, iter_target)?; - let mut elements = vec![]; - loop { - match vm.call_method(&iterator, "__next__", vec![]) { - Ok(v) => { - // Now apply function: - let mapped_value = vm.invoke( - function.clone(), - PyFuncArgs { - args: vec![v], - kwargs: vec![], - }, - )?; - elements.push(mapped_value); - } - Err(_) => break, - } + no_kwargs!(vm, args); + if args.args.len() < 2 { + Err(vm.new_type_error("map() must have at least two arguments.".to_owned())) + } else { + let function = &args.args[0]; + let iterables = &args.args[1..]; + objiter::create_map(vm, function, iterables) } - - trace!("Mapped elements: {:?}", elements); - - // TODO: when iterators are implemented, we can improve this function. - Ok(vm.ctx.new_list(elements)) } fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index c0ff2b771..b9811f47e 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -84,19 +84,25 @@ pub fn create_filter( Ok(iter_obj) } -//pub fn create_map(vm: &mut VirtualMachine, -// mapper: &PyObjectRef, -// iterators: &PyObjectRef) -> PyResult { -// let iter_obj = PyObject::new( -// PyObjectPayload::MapIterator { -// predicate: predicate.clone(), -// iterator: iterator.clone(), -// }, -// vm.ctx.iter_type(), -// ); -// -// Ok(iter_obj) -//} +pub fn create_map( + vm: &mut VirtualMachine, + mapper: &PyObjectRef, + iterables: &[PyObjectRef], +) -> PyResult { + let iterators = iterables + .into_iter() + .map(|iterable| get_iter(vm, iterable)) + .collect::, _>>()?; + let iter_obj = PyObject::new( + PyObjectPayload::MapIterator { + mapper: mapper.clone(), + iterators, + }, + vm.ctx.iter_type(), + ); + + Ok(iter_obj) +} // Sequence iterator: fn iter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -195,6 +201,23 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } } + PyObjectPayload::MapIterator { + ref mut mapper, + ref mut iterators, + } => { + let next_objs = iterators + .iter() + .map(|iterator| call_next(vm, iterator)) + .collect::, _>>()?; + + vm.invoke( + mapper.clone(), + PyFuncArgs { + args: next_objs, + kwargs: vec![], + }, + ) + } _ => { panic!("NOT IMPL"); } From f85bc13ded235c21102d1668b6a9bb2cfb6a1800 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 6 Feb 2019 10:06:39 +1300 Subject: [PATCH 081/153] Fix misunderstanding about behaviour if filter function is None --- tests/snippets/builtin_filter.py | 3 ++- tests/snippets/builtins.py | 2 +- vm/src/obj/objiter.rs | 22 ++++++++++++---------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/tests/snippets/builtin_filter.py b/tests/snippets/builtin_filter.py index 1f654ad84..b2ccbf251 100644 --- a/tests/snippets/builtin_filter.py +++ b/tests/snippets/builtin_filter.py @@ -1,6 +1,7 @@ assert list(filter(lambda x: ((x % 2) == 0), [0, 1, 2])) == [0, 2] -assert list(filter(None, [0, 1, 2])) == [0, 1, 2] +# None implies identity +assert list(filter(None, [0, 1, 2])) == [1, 2] # test infinite iterator diff --git a/tests/snippets/builtins.py b/tests/snippets/builtins.py index 1a3dbb1ba..539b49ef7 100644 --- a/tests/snippets/builtins.py +++ b/tests/snippets/builtins.py @@ -1,4 +1,4 @@ -x = sum(map(int, a)) +x = sum(map(int, ['1', '2', '3'])) assert x == 6 assert callable(type) diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index b9811f47e..a671d2c97 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -186,16 +186,18 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } => { loop { let next_obj = call_next(vm, iterator)?; - if predicate.is(&vm.get_none()) { - return Ok(next_obj); - } - let predicate_value = vm.invoke( - predicate.clone(), - PyFuncArgs { - args: vec![next_obj.clone()], - kwargs: vec![], - }, - )?; // What happens if the predicate raise StopIteration + let predicate_value = if predicate.is(&vm.get_none()) { + next_obj.clone() + } else { + // What should happen if the predicate raise StopIteration + vm.invoke( + predicate.clone(), + PyFuncArgs { + args: vec![next_obj.clone()], + kwargs: vec![], + }, + )? + }; if objbool::boolval(vm, predicate_value)? { return Ok(next_obj); } From 6e99ad32db83390ecc270e16575aad601a5a189d Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 6 Feb 2019 10:39:08 +1300 Subject: [PATCH 082/153] Added tests for when function in filter/map raises StopIteration --- tests/snippets/builtin_filter.py | 9 +++++++++ tests/snippets/builtin_map.py | 9 +++++++++ vm/src/builtins.rs | 1 + vm/src/obj/objiter.rs | 4 +++- 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/snippets/builtin_filter.py b/tests/snippets/builtin_filter.py index b2ccbf251..45b55d5dd 100644 --- a/tests/snippets/builtin_filter.py +++ b/tests/snippets/builtin_filter.py @@ -19,3 +19,12 @@ class Counter(object): it = filter(lambda x: ((x % 2) == 0), Counter()) assert next(it) == 2 assert next(it) == 4 + + +def predicate(x): + if x == 0: + raise StopIteration() + return True + + +assert list(filter(predicate, [1, 2, 0, 4, 5])) == [1, 2] diff --git a/tests/snippets/builtin_map.py b/tests/snippets/builtin_map.py index 955351a67..59be6ba6a 100644 --- a/tests/snippets/builtin_map.py +++ b/tests/snippets/builtin_map.py @@ -21,3 +21,12 @@ class Counter(object): it = map(lambda x: x+1, Counter()) assert next(it) == 2 assert next(it) == 3 + + +def mapping(x): + if x == 0: + raise StopIteration() + return x + + +assert list(map(mapping, [1, 2, 0, 4, 5])) == [1, 2] diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index f64609d54..e62bc52be 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -786,6 +786,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "ValueError", ctx.exceptions.value_error.clone()); ctx.set_attr(&py_mod, "IndexError", ctx.exceptions.index_error.clone()); ctx.set_attr(&py_mod, "ImportError", ctx.exceptions.import_error.clone()); + ctx.set_attr(&py_mod, "StopIteration", ctx.exceptions.stop_iteration.clone()); py_mod } diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index a671d2c97..02d0c91d5 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -189,7 +189,8 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let predicate_value = if predicate.is(&vm.get_none()) { next_obj.clone() } else { - // What should happen if the predicate raise StopIteration + // the predicate itself can raise StopIteration which does stop the filter + // iteration vm.invoke( predicate.clone(), PyFuncArgs { @@ -212,6 +213,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .map(|iterator| call_next(vm, iterator)) .collect::, _>>()?; + // the mapper itself can raise StopIteration which does stop the map iteration vm.invoke( mapper.clone(), PyFuncArgs { From 3a81fd1ef4649810c21b93b66e840f013e881472 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 6 Feb 2019 11:15:07 +1300 Subject: [PATCH 083/153] style: rustfmt --- vm/src/builtins.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index e62bc52be..e17a88e3f 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -786,7 +786,11 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "ValueError", ctx.exceptions.value_error.clone()); ctx.set_attr(&py_mod, "IndexError", ctx.exceptions.index_error.clone()); ctx.set_attr(&py_mod, "ImportError", ctx.exceptions.import_error.clone()); - ctx.set_attr(&py_mod, "StopIteration", ctx.exceptions.stop_iteration.clone()); + ctx.set_attr( + &py_mod, + "StopIteration", + ctx.exceptions.stop_iteration.clone(), + ); py_mod } From 6c8ec398827e71bad61a79d023c4c3e708ade152 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 6 Feb 2019 12:45:14 +1300 Subject: [PATCH 084/153] Make map and filter into proper types --- tests/snippets/builtin_filter.py | 2 + tests/snippets/builtin_map.py | 2 + vm/src/builtins.rs | 21 +--- vm/src/obj/mod.rs | 2 + vm/src/obj/objfilter.rs | 83 +++++++++++++++ vm/src/obj/objiter.rs | 177 ++++++++----------------------- vm/src/obj/objmap.rs | 81 ++++++++++++++ vm/src/pyobject.rs | 18 ++++ 8 files changed, 237 insertions(+), 149 deletions(-) create mode 100644 vm/src/obj/objfilter.rs create mode 100644 vm/src/obj/objmap.rs diff --git a/tests/snippets/builtin_filter.py b/tests/snippets/builtin_filter.py index 45b55d5dd..d0b5ccd5c 100644 --- a/tests/snippets/builtin_filter.py +++ b/tests/snippets/builtin_filter.py @@ -3,6 +3,8 @@ assert list(filter(lambda x: ((x % 2) == 0), [0, 1, 2])) == [0, 2] # None implies identity assert list(filter(None, [0, 1, 2])) == [1, 2] +assert type(filter(None, [])) == filter + # test infinite iterator class Counter(object): diff --git a/tests/snippets/builtin_map.py b/tests/snippets/builtin_map.py index 59be6ba6a..0de8d2c59 100644 --- a/tests/snippets/builtin_map.py +++ b/tests/snippets/builtin_map.py @@ -5,6 +5,8 @@ assert a == ['1', '2', '3'] b = list(map(lambda x, y: x + y, [1, 2, 4], [3, 5])) assert b == [4, 7] +assert type(map(lambda x: x, [])) == map + # test infinite iterator class Counter(object): diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index e17a88e3f..afd7efc43 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -301,12 +301,6 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm.run_code_obj(code_obj, scope) } -fn builtin_filter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(function, None), (iterable, None)]); - - objiter::create_filter(vm, function, iterable) -} - fn builtin_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -411,17 +405,6 @@ fn builtin_locals(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_locals()) } -fn builtin_map(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - no_kwargs!(vm, args); - if args.args.len() < 2 { - Err(vm.new_type_error("map() must have at least two arguments.".to_owned())) - } else { - let function = &args.args[0]; - let iterables = &args.args[1..]; - objiter::create_map(vm, function, iterables) - } -} - fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let candidates = if args.args.len() > 1 { args.args.clone() @@ -716,7 +699,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "exec", ctx.new_rustfunc(builtin_exec)); ctx.set_attr(&py_mod, "float", ctx.float_type()); ctx.set_attr(&py_mod, "frozenset", ctx.frozenset_type()); - ctx.set_attr(&py_mod, "filter", ctx.new_rustfunc(builtin_filter)); + ctx.set_attr(&py_mod, "filter", ctx.filter_type()); ctx.set_attr(&py_mod, "format", ctx.new_rustfunc(builtin_format)); ctx.set_attr(&py_mod, "getattr", ctx.new_rustfunc(builtin_getattr)); ctx.set_attr(&py_mod, "hasattr", ctx.new_rustfunc(builtin_hasattr)); @@ -730,7 +713,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "len", ctx.new_rustfunc(builtin_len)); ctx.set_attr(&py_mod, "list", ctx.list_type()); ctx.set_attr(&py_mod, "locals", ctx.new_rustfunc(builtin_locals)); - ctx.set_attr(&py_mod, "map", ctx.new_rustfunc(builtin_map)); + ctx.set_attr(&py_mod, "map", ctx.map_type()); ctx.set_attr(&py_mod, "max", ctx.new_rustfunc(builtin_max)); ctx.set_attr(&py_mod, "memoryview", ctx.memoryview_type()); ctx.set_attr(&py_mod, "min", ctx.new_rustfunc(builtin_min)); diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index 03017a30b..a60ce79ff 100644 --- a/vm/src/obj/mod.rs +++ b/vm/src/obj/mod.rs @@ -6,6 +6,7 @@ pub mod objbytes; pub mod objcode; pub mod objcomplex; pub mod objdict; +pub mod objfilter; pub mod objfloat; pub mod objframe; pub mod objfunction; @@ -13,6 +14,7 @@ pub mod objgenerator; pub mod objint; pub mod objiter; pub mod objlist; +pub mod objmap; pub mod objmemory; pub mod objobject; pub mod objproperty; diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs new file mode 100644 index 000000000..009d2ad2e --- /dev/null +++ b/vm/src/obj/objfilter.rs @@ -0,0 +1,83 @@ +use super::super::pyobject::{ + IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, + TypeProtocol, +}; +use super::super::vm::VirtualMachine; +use super::objbool; +use super::objiter; +use super::objtype; // Required for arg_check! to use isinstance + +pub fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(cls, None), (function, None), (iterable, None)] + ); + let iterator = objiter::get_iter(vm, iterable)?; + Ok(PyObject::new( + PyObjectPayload::FilterIterator { + predicate: function.clone(), + iterator, + }, + cls.clone(), + )) +} + +fn filter_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(filter, Some(vm.ctx.filter_type()))]); + // Return self: + Ok(filter.clone()) +} + +fn filter_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(filter, Some(vm.ctx.filter_type())), (needle, None)] + ); + objiter::contains(vm, filter, needle) +} + +fn filter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(filter, Some(vm.ctx.filter_type()))]); + + if let PyObjectPayload::FilterIterator { + ref mut predicate, + ref mut iterator, + } = filter.borrow_mut().payload + { + loop { + let next_obj = objiter::call_next(vm, iterator)?; + let predicate_value = if predicate.is(&vm.get_none()) { + next_obj.clone() + } else { + // the predicate itself can raise StopIteration which does stop the filter + // iteration + vm.invoke( + predicate.clone(), + PyFuncArgs { + args: vec![next_obj.clone()], + kwargs: vec![], + }, + )? + }; + if objbool::boolval(vm, predicate_value)? { + return Ok(next_obj); + } + } + } else { + panic!("filter doesn't have correct payload"); + } +} + +pub fn init(context: &PyContext) { + let filter_type = &context.filter_type; + context.set_attr( + &filter_type, + "__contains__", + context.new_rustfunc(filter_contains), + ); + context.set_attr(&filter_type, "__iter__", context.new_rustfunc(filter_iter)); + context.set_attr(&filter_type, "__new__", context.new_rustfunc(filter_new)); + context.set_attr(&filter_type, "__next__", context.new_rustfunc(filter_next)); +} diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 02d0c91d5..19382bc28 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -3,8 +3,7 @@ */ use super::super::pyobject::{ - IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, - TypeProtocol, + PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objbool; @@ -66,42 +65,19 @@ pub fn get_all( Ok(elements) } -// Should filter/map have their own class? -pub fn create_filter( - vm: &mut VirtualMachine, - predicate: &PyObjectRef, - iterable: &PyObjectRef, -) -> PyResult { - let iterator = get_iter(vm, iterable)?; - let iter_obj = PyObject::new( - PyObjectPayload::FilterIterator { - predicate: predicate.clone(), - iterator, - }, - vm.ctx.iter_type(), - ); - - Ok(iter_obj) -} - -pub fn create_map( - vm: &mut VirtualMachine, - mapper: &PyObjectRef, - iterables: &[PyObjectRef], -) -> PyResult { - let iterators = iterables - .into_iter() - .map(|iterable| get_iter(vm, iterable)) - .collect::, _>>()?; - let iter_obj = PyObject::new( - PyObjectPayload::MapIterator { - mapper: mapper.clone(), - iterators, - }, - vm.ctx.iter_type(), - ); - - Ok(iter_obj) +pub fn contains(vm: &mut VirtualMachine, iter: &PyObjectRef, needle: &PyObjectRef) -> PyResult { + loop { + if let Some(element) = get_next_object(vm, iter)? { + let equal = vm.call_method(needle, "__eq__", vec![element.clone()])?; + if objbool::get_value(&equal) { + return Ok(vm.new_bool(true)); + } else { + continue; + } + } else { + return Ok(vm.new_bool(false)); + } + } } // Sequence iterator: @@ -123,108 +99,49 @@ fn iter_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(iter, Some(vm.ctx.iter_type())), (needle, None)] ); - loop { - match vm.call_method(&iter, "__next__", vec![]) { - Ok(element) => match vm.call_method(needle, "__eq__", vec![element.clone()]) { - Ok(value) => { - if objbool::get_value(&value) { - return Ok(vm.new_bool(true)); - } else { - continue; - } - } - Err(_) => return Err(vm.new_type_error("".to_string())), - }, - Err(_) => return Ok(vm.new_bool(false)), - } - } + contains(vm, iter, needle) } fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iter, Some(vm.ctx.iter_type()))]); - let ref mut payload = iter.borrow_mut().payload; - match payload { - PyObjectPayload::Iterator { - ref mut position, - iterated_obj: ref mut iterated_obj_ref, - } => { - let iterated_obj = iterated_obj_ref.borrow_mut(); - match iterated_obj.payload { - PyObjectPayload::Sequence { ref elements } => { - if *position < elements.len() { - let obj_ref = elements[*position].clone(); - *position += 1; - Ok(obj_ref) - } else { - let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); - let stop_iteration = - vm.new_exception(stop_iteration_type, "End of iterator".to_string()); - Err(stop_iteration) - } - } - - PyObjectPayload::Range { ref range } => { - if let Some(int) = range.get(BigInt::from(*position)) { - *position += 1; - Ok(vm.ctx.new_int(int.to_bigint().unwrap())) - } else { - let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); - let stop_iteration = - vm.new_exception(stop_iteration_type, "End of iterator".to_string()); - Err(stop_iteration) - } - } - _ => { - panic!("NOT IMPL"); - } - } - } - PyObjectPayload::FilterIterator { - ref mut predicate, - ref mut iterator, - } => { - loop { - let next_obj = call_next(vm, iterator)?; - let predicate_value = if predicate.is(&vm.get_none()) { - next_obj.clone() + if let PyObjectPayload::Iterator { + ref mut position, + iterated_obj: ref mut iterated_obj_ref, + } = iter.borrow_mut().payload + { + let iterated_obj = iterated_obj_ref.borrow_mut(); + match iterated_obj.payload { + PyObjectPayload::Sequence { ref elements } => { + if *position < elements.len() { + let obj_ref = elements[*position].clone(); + *position += 1; + Ok(obj_ref) } else { - // the predicate itself can raise StopIteration which does stop the filter - // iteration - vm.invoke( - predicate.clone(), - PyFuncArgs { - args: vec![next_obj.clone()], - kwargs: vec![], - }, - )? - }; - if objbool::boolval(vm, predicate_value)? { - return Ok(next_obj); + let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); + let stop_iteration = + vm.new_exception(stop_iteration_type, "End of iterator".to_string()); + Err(stop_iteration) } } - } - PyObjectPayload::MapIterator { - ref mut mapper, - ref mut iterators, - } => { - let next_objs = iterators - .iter() - .map(|iterator| call_next(vm, iterator)) - .collect::, _>>()?; - // the mapper itself can raise StopIteration which does stop the map iteration - vm.invoke( - mapper.clone(), - PyFuncArgs { - args: next_objs, - kwargs: vec![], - }, - ) - } - _ => { - panic!("NOT IMPL"); + PyObjectPayload::Range { ref range } => { + if let Some(int) = range.get(BigInt::from(*position)) { + *position += 1; + Ok(vm.ctx.new_int(int.to_bigint().unwrap())) + } else { + let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); + let stop_iteration = + vm.new_exception(stop_iteration_type, "End of iterator".to_string()); + Err(stop_iteration) + } + } + _ => { + panic!("NOT IMPL"); + } } + } else { + panic!("NOT IMPL"); } } diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs new file mode 100644 index 000000000..ed6130643 --- /dev/null +++ b/vm/src/obj/objmap.rs @@ -0,0 +1,81 @@ +use super::super::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, +}; +use super::super::vm::VirtualMachine; +use super::objiter; +use super::objtype; // Required for arg_check! to use isinstance + +pub fn map_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + no_kwargs!(vm, args); + let cls = &args.args[0]; + if args.args.len() < 3 { + Err(vm.new_type_error("map() must have at least two arguments.".to_owned())) + } else { + let function = &args.args[1]; + let iterables = &args.args[2..]; + let iterators = iterables + .into_iter() + .map(|iterable| objiter::get_iter(vm, iterable)) + .collect::, _>>()?; + Ok(PyObject::new( + PyObjectPayload::MapIterator { + mapper: function.clone(), + iterators, + }, + cls.clone(), + )) + } +} + +fn map_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(map, Some(vm.ctx.map_type()))]); + // Return self: + Ok(map.clone()) +} + +fn map_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(map, Some(vm.ctx.map_type())), (needle, None)] + ); + objiter::contains(vm, map, needle) +} + +fn map_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(map, Some(vm.ctx.map_type()))]); + + if let PyObjectPayload::MapIterator { + ref mut mapper, + ref mut iterators, + } = map.borrow_mut().payload + { + let next_objs = iterators + .iter() + .map(|iterator| objiter::call_next(vm, iterator)) + .collect::, _>>()?; + + // the mapper itself can raise StopIteration which does stop the map iteration + vm.invoke( + mapper.clone(), + PyFuncArgs { + args: next_objs, + kwargs: vec![], + }, + ) + } else { + panic!("map doesn't have correct payload"); + } +} + +pub fn init(context: &PyContext) { + let map_type = &context.map_type; + context.set_attr( + &map_type, + "__contains__", + context.new_rustfunc(map_contains), + ); + context.set_attr(&map_type, "__iter__", context.new_rustfunc(map_iter)); + context.set_attr(&map_type, "__new__", context.new_rustfunc(map_new)); + context.set_attr(&map_type, "__next__", context.new_rustfunc(map_next)); +} diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 558dfcd6e..245314093 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -7,6 +7,7 @@ use super::obj::objbytes; use super::obj::objcode; use super::obj::objcomplex; use super::obj::objdict; +use super::obj::objfilter; use super::obj::objfloat; use super::obj::objframe; use super::obj::objfunction; @@ -14,6 +15,7 @@ use super::obj::objgenerator; use super::obj::objint; use super::obj::objiter; use super::obj::objlist; +use super::obj::objmap; use super::obj::objmemory; use super::obj::objobject; use super::obj::objproperty; @@ -106,6 +108,7 @@ pub struct PyContext { pub classmethod_type: PyObjectRef, pub code_type: PyObjectRef, pub dict_type: PyObjectRef, + pub filter_type: PyObjectRef, pub float_type: PyObjectRef, pub frame_type: PyObjectRef, pub frozenset_type: PyObjectRef, @@ -116,6 +119,7 @@ pub struct PyContext { pub true_value: PyObjectRef, pub false_value: PyObjectRef, pub list_type: PyObjectRef, + pub map_type: PyObjectRef, pub memoryview_type: PyObjectRef, pub none: PyObjectRef, pub tuple_type: PyObjectRef, @@ -200,6 +204,8 @@ impl PyContext { let bytearray_type = create_type("bytearray", &type_type, &object_type, &dict_type); let tuple_type = create_type("tuple", &type_type, &object_type, &dict_type); let iter_type = create_type("iter", &type_type, &object_type, &dict_type); + let filter_type = create_type("filter", &type_type, &object_type, &dict_type); + let map_type = create_type("map", &type_type, &object_type, &dict_type); let bool_type = create_type("bool", &type_type, &int_type, &dict_type); let memoryview_type = create_type("memoryview", &type_type, &object_type, &dict_type); let code_type = create_type("code", &type_type, &int_type, &dict_type); @@ -240,6 +246,8 @@ impl PyContext { false_value, tuple_type, iter_type, + filter_type, + map_type, dict_type, none: none, str_type: str_type, @@ -275,6 +283,8 @@ impl PyContext { objsuper::init(&context); objtuple::init(&context); objiter::init(&context); + objfilter::init(&context); + objmap::init(&context); objbool::init(&context); objcode::init(&context); objframe::init(&context); @@ -346,6 +356,14 @@ impl PyContext { self.iter_type.clone() } + pub fn filter_type(&self) -> PyObjectRef { + self.filter_type.clone() + } + + pub fn map_type(&self) -> PyObjectRef { + self.map_type.clone() + } + pub fn str_type(&self) -> PyObjectRef { self.str_type.clone() } From 49a23a8483a37c9fd8cb9b63764fb0300f2f7a87 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Tue, 5 Feb 2019 16:26:12 -0800 Subject: [PATCH 085/153] remove misleading contains method --- vm/src/obj/objrange.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index b6ca2c291..647298ea2 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -26,14 +26,9 @@ impl RangeType { .unwrap() } - #[inline] - pub fn contains(&self, value: &BigInt) -> bool { - value >= &self.start && value < &self.end - } - #[inline] pub fn index_of(&self, value: &BigInt) -> Option { - if !self.contains(value) { + if value < &self.start || value >= &self.end { return None; } From 35a06bc42803b53da607d73ed8b0b0cf976fe233 Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Tue, 5 Feb 2019 21:44:14 -0500 Subject: [PATCH 086/153] Add bytes.__iter__ --- vm/src/obj/objbytes.rs | 15 +++++++++++++++ vm/src/obj/objiter.rs | 14 ++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index f700b8bdb..8b0f60bd5 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -20,6 +20,7 @@ pub fn init(context: &PyContext) { context.set_attr(bytes_type, "__new__", context.new_rustfunc(bytes_new)); context.set_attr(bytes_type, "__repr__", context.new_rustfunc(bytes_repr)); context.set_attr(bytes_type, "__len__", context.new_rustfunc(bytes_len)); + context.set_attr(bytes_type, "__iter__", context.new_rustfunc(bytes_iter)) } fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -101,3 +102,17 @@ fn bytes_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let data = String::from_utf8(value.to_vec()).unwrap(); Ok(vm.new_str(format!("b'{}'", data))) } + +fn bytes_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(obj, Some(vm.ctx.bytes_type()))]); + + let iter_obj = PyObject::new( + PyObjectPayload::Iterator { + position: 0, + iterated_obj: obj.clone(), + }, + vm.ctx.iter_type(), + ); + + Ok(iter_obj) +} diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 53ed76804..86eb79c77 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -131,6 +131,20 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(stop_iteration) } } + + PyObjectPayload::Bytes { ref value } => { + if *position < value.len() { + let obj_ref = vm.ctx.new_int(value[*position].to_bigint().unwrap()); + *position += 1; + Ok(obj_ref) + } else { + let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); + let stop_iteration = + vm.new_exception(stop_iteration_type, "End of iterator".to_string()); + Err(stop_iteration) + } + } + _ => { panic!("NOT IMPL"); } From 12e6de0503996e4a6e70bf96a949672f4d1564cb Mon Sep 17 00:00:00 2001 From: Nathan Date: Tue, 5 Feb 2019 20:59:41 -0600 Subject: [PATCH 087/153] repr() of complex numbers is compliant with cpython --- tests/snippets/basic_types.py | 3 +++ vm/src/obj/objcomplex.rs | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/snippets/basic_types.py b/tests/snippets/basic_types.py index 1298c4ed3..006669b3f 100644 --- a/tests/snippets/basic_types.py +++ b/tests/snippets/basic_types.py @@ -44,6 +44,9 @@ assert int() == 0 a = complex(2, 4) assert type(a) is complex assert type(a + a) is complex +assert repr(a) == '(2+4j)' +a = 10j +assert repr(a) == '10j' a = 1 assert a.conjugate() == a diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index 7c4c54990..bc624d50f 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -85,5 +85,10 @@ fn complex_conjugate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn complex_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.complex_type()))]); let v = get_value(obj); - Ok(vm.new_str(v.to_string())) + let repr = if v.re == 0. { + format!("{}j", v.im) + } else { + format!("({}+{}j)", v.re, v.im) + }; + Ok(vm.new_str(repr)) } From 3de17b908389f9534360791035d85333e40ea2ab Mon Sep 17 00:00:00 2001 From: veera venky Date: Wed, 6 Feb 2019 08:35:57 +0530 Subject: [PATCH 088/153] Added __doc__ atrribute for object --- vm/src/obj/objobject.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 0bcfe7785..f2668d23e 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -87,6 +87,8 @@ fn object_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let object = &context.object; + let object_doc = "The most base type"; + context.set_attr(&object, "__new__", context.new_rustfunc(new_instance)); context.set_attr(&object, "__init__", context.new_rustfunc(object_init)); context.set_attr(&object, "__eq__", context.new_rustfunc(object_eq)); @@ -105,6 +107,7 @@ pub fn init(context: &PyContext) { "__getattribute__", context.new_rustfunc(object_getattribute), ); + context.set_attr(&object, "__doc__", context.new_str(object_doc.to_string())); } fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { From 1270aee62815fcc849fc20e8c1e4276789e3d18a Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Wed, 6 Feb 2019 01:22:23 -0500 Subject: [PATCH 089/153] Add is* methods to bytearray --- vm/src/obj/objbytearray.rs | 121 +++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index de873dca5..71b6cc18c 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -37,6 +37,46 @@ pub fn init(context: &PyContext) { "__len__", context.new_rustfunc(bytesarray_len), ); + context.set_attr( + &bytearray_type, + "isalnum", + context.new_rustfunc(bytearray_isalnum), + ); + context.set_attr( + &bytearray_type, + "isalpha", + context.new_rustfunc(bytearray_isalpha), + ); + context.set_attr( + &bytearray_type, + "isascii", + context.new_rustfunc(bytearray_isascii), + ); + context.set_attr( + &bytearray_type, + "isdigit", + context.new_rustfunc(bytearray_isdigit), + ); + context.set_attr( + &bytearray_type, + "islower", + context.new_rustfunc(bytearray_islower), + ); + context.set_attr( + &bytearray_type, + "isspace", + context.new_rustfunc(bytearray_isspace), + ); + context.set_attr( + &bytearray_type, + "isupper", + context.new_rustfunc(bytearray_isupper), + ); + context.set_attr( + &bytearray_type, + "istitle", + context.new_rustfunc(bytearray_istitle), + ); } fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -92,6 +132,87 @@ fn bytearray_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_bool(result)) } +fn bytearray_isalnum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); + let bytes = get_value(zelf); + Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_alphanumeric()))) +} + +fn bytearray_isalpha(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); + let bytes = get_value(zelf); + Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_alphabetic()))) +} + +fn bytearray_isascii(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); + let bytes = get_value(zelf); + Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_ascii()))) +} + +fn bytearray_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); + let bytes = get_value(zelf); + Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_digit(10)))) +} + +fn bytearray_islower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); + let bytes = get_value(zelf); + Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_lowercase()))) +} + +fn bytearray_isspace(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); + let bytes = get_value(zelf); + Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_whitespace()))) +} + +fn bytearray_isupper(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); + let bytes = get_value(zelf); + Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_uppercase()))) +} + +fn bytearray_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); + let bytes = get_value(zelf); + + if bytes.is_empty() { + Ok(vm.new_bool(false)) + } else { + let mut iter = bytes.iter().peekable(); + let mut prev_cased = false; + + while let Some(c) = iter.next() { + let current = char::from(*c); + let next = if let Some(k) = iter.peek() { + char::from(**k) + } else { + if current.is_uppercase() { + return Ok(vm.new_bool(!prev_cased)); + } else { + return Ok(vm.new_bool(prev_cased)); + } + }; + + if is_cased(current) && next.is_uppercase() && !prev_cased { + return Ok(vm.new_bool(false)); + } else if !is_cased(current) && next.is_lowercase() { + return Ok(vm.new_bool(false)); + } + prev_cased = is_cased(current); + } + + Ok(vm.new_bool(true)) + } +} + +// helper function for istitle +fn is_cased(c: char) -> bool { + c.to_uppercase().next().unwrap() != c || c.to_lowercase().next().unwrap() != c +} + /* fn bytearray_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( From 0085470c83b26090de779bc4c1ccc6bdac6b63a8 Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Wed, 6 Feb 2019 01:26:51 -0500 Subject: [PATCH 090/153] Add tests for bytearray --- tests/snippets/bytearray.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/snippets/bytearray.py diff --git a/tests/snippets/bytearray.py b/tests/snippets/bytearray.py new file mode 100644 index 000000000..297bf667e --- /dev/null +++ b/tests/snippets/bytearray.py @@ -0,0 +1,30 @@ +#__getitem__ not implemented yet +#a = bytearray(b'abc') +#assert a[0] == b'a' +#assert a[1] == b'b' + +assert len(bytearray([1,2,3])) == 3 + +assert bytearray(b'1a23').isalnum() +assert not bytearray(b'1%a23').isalnum() + +assert bytearray(b'abc').isalpha() +assert not bytearray(b'abc1').isalpha() + +assert bytearray(b'xyz').isascii() +assert not bytearray([128, 157, 32]).isascii() + +assert bytearray(b'1234567890').isdigit() +assert not bytearray(b'12ab').isdigit() + +assert bytearray(b'lower').islower() +assert not bytearray(b'Super Friends').islower() + +assert bytearray(b' \n\t').isspace() +assert not bytearray(b'\td\n').isspace() + +assert bytearray(b'UPPER').isupper() +assert not bytearray(b'tuPpEr').isupper() + +assert bytearray(b'Is Title Case').istitle() +assert not bytearray(b'is Not title casE').istitle() From 94dc6ece645d598407a88f54e6a86f971ec0270c Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Wed, 6 Feb 2019 02:42:44 -0500 Subject: [PATCH 091/153] Fix python tests that travis didn't like --- tests/snippets/bytearray.py | 5 +++-- vm/src/obj/objbytearray.rs | 26 +++++++++++++++++++------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/tests/snippets/bytearray.py b/tests/snippets/bytearray.py index 297bf667e..2fffe3111 100644 --- a/tests/snippets/bytearray.py +++ b/tests/snippets/bytearray.py @@ -11,8 +11,9 @@ assert not bytearray(b'1%a23').isalnum() assert bytearray(b'abc').isalpha() assert not bytearray(b'abc1').isalpha() -assert bytearray(b'xyz').isascii() -assert not bytearray([128, 157, 32]).isascii() +# travis doesn't like this +#assert bytearray(b'xyz').isascii() +#assert not bytearray([128, 157, 32]).isascii() assert bytearray(b'1234567890').isdigit() assert not bytearray(b'12ab').isdigit() diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 71b6cc18c..78e48dcc3 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -135,43 +135,55 @@ fn bytearray_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn bytearray_isalnum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); let bytes = get_value(zelf); - Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_alphanumeric()))) + Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_alphanumeric()))) } fn bytearray_isalpha(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); let bytes = get_value(zelf); - Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_alphabetic()))) + Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_alphabetic()))) } fn bytearray_isascii(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); let bytes = get_value(zelf); - Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_ascii()))) + Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_ascii()))) } fn bytearray_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); let bytes = get_value(zelf); - Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_digit(10)))) + Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_digit(10)))) } fn bytearray_islower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); let bytes = get_value(zelf); - Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_lowercase()))) + Ok(vm.new_bool( + !bytes.is_empty() + && bytes + .iter() + .filter(|x| char::from(**x).is_whitespace()) + .all(|x| char::from(*x).is_lowercase()), + )) } fn bytearray_isspace(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); let bytes = get_value(zelf); - Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_whitespace()))) + Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_whitespace()))) } fn bytearray_isupper(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]); let bytes = get_value(zelf); - Ok(vm.new_bool(bytes.iter().all(|x| char::from(*x).is_uppercase()))) + Ok(vm.new_bool( + !bytes.is_empty() + && bytes + .iter() + .filter(|x| !char::from(**x).is_whitespace()) + .all(|x| char::from(*x).is_uppercase()), + )) } fn bytearray_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From 00ef668022c9f90d8c5bbd3467eff83dbb88185d Mon Sep 17 00:00:00 2001 From: Homer McMillan Date: Wed, 6 Feb 2019 03:15:03 -0500 Subject: [PATCH 092/153] String is* methods return false on empty string #363 --- vm/src/obj/objstr.rs | 93 ++++++++++++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 33 deletions(-) diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index f6e0fffd8..c33f70326 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -464,26 +464,34 @@ fn str_isidentifier(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { // which is why isspace is using is_ascii_whitespace. Same for isupper & islower fn str_isspace(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let is_whitespace = get_value(&s).chars().all(|c| c.is_ascii_whitespace()); - Ok(vm.ctx.new_bool(is_whitespace)) + let value = get_value(&s); + Ok(vm + .ctx + .new_bool(!value.is_empty() && value.chars().all(|c| c.is_ascii_whitespace()))) } fn str_isupper(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let is_upper = get_value(&s) - .chars() - .filter(|x| !x.is_ascii_whitespace()) - .all(|c| c.is_uppercase()); - Ok(vm.ctx.new_bool(is_upper)) + let value = get_value(&s); + Ok(vm.ctx.new_bool( + !value.is_empty() + && value + .chars() + .filter(|x| !x.is_ascii_whitespace()) + .all(|c| c.is_uppercase()), + )) } fn str_islower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let is_lower = get_value(&s) - .chars() - .filter(|x| !x.is_ascii_whitespace()) - .all(|c| c.is_lowercase()); - Ok(vm.ctx.new_bool(is_lower)) + let value = get_value(&s); + Ok(vm.ctx.new_bool( + !value.is_empty() + && value + .chars() + .filter(|x| !x.is_ascii_whitespace()) + .all(|c| c.is_lowercase()), + )) } // doesn't implement keep new line delimeter just yet @@ -785,10 +793,15 @@ fn str_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); let value = get_value(&s); let mut is_titled = true; - for word in value.split(" ") { - if word != make_title(&word) { - is_titled = false; - break; + + if value.is_empty() { + is_titled = false; + } else { + for word in value.split(" ") { + if word != make_title(&word) { + is_titled = false; + break; + } } } Ok(vm.ctx.new_bool(is_titled)) @@ -845,14 +858,18 @@ fn str_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn str_isalnum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let is_alnum = get_value(&s).chars().all(|c| c.is_alphanumeric()); - Ok(vm.ctx.new_bool(is_alnum)) + let value = get_value(&s); + Ok(vm + .ctx + .new_bool(!value.is_empty() && value.chars().all(|c| c.is_alphanumeric()))) } fn str_isascii(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let is_ascii = get_value(&s).chars().all(|c| c.is_ascii()); - Ok(vm.ctx.new_bool(is_ascii)) + let value = get_value(&s); + Ok(vm + .ctx + .new_bool(!value.is_empty() && value.chars().all(|c| c.is_ascii()))) } fn str_rindex(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -905,14 +922,18 @@ fn str_rfind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn str_isnumeric(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let is_numeric = get_value(&s).chars().all(|c| c.is_numeric()); - Ok(vm.ctx.new_bool(is_numeric)) + let value = get_value(&s); + Ok(vm + .ctx + .new_bool(!value.is_empty() && value.chars().all(|c| c.is_numeric()))) } fn str_isalpha(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); - let is_alpha = get_value(&s).chars().all(|c| c.is_alphanumeric()); - Ok(vm.ctx.new_bool(is_alpha)) + let value = get_value(&s); + Ok(vm + .ctx + .new_bool(!value.is_empty() && value.chars().all(|c| c.is_alphanumeric()))) } fn str_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -923,18 +944,24 @@ fn str_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { 0x2070, 0x00B9, 0x00B2, 0x00B3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, ]; let mut is_digit: bool = true; - for c in value.chars() { - if !c.is_digit(10) { - // checking if char is exponent - let char_as_uni: u16 = c as u16; - if valid_unicodes.contains(&char_as_uni) { - continue; - } else { - is_digit = false; - break; + + if value.is_empty() { + is_digit = false; + } else { + for c in value.chars() { + if !c.is_digit(10) { + // checking if char is exponent + let char_as_uni: u16 = c as u16; + if valid_unicodes.contains(&char_as_uni) { + continue; + } else { + is_digit = false; + break; + } } } } + Ok(vm.ctx.new_bool(is_digit)) } From b682eb939e1b7de2ba4a887b2808e6fd6ec188a5 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Wed, 6 Feb 2019 12:04:47 +0300 Subject: [PATCH 093/153] int type: Added __rxor__ --- vm/src/obj/objint.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index d0fbca709..646dd13f9 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -462,6 +462,23 @@ fn int_xor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } +fn int_rxor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(i, Some(vm.ctx.int_type())), (i2, None)] + ); + + if objtype::isinstance(i2, &vm.ctx.int_type()) { + let right_val = get_value(i); + let left_val = get_value(i2); + + Ok(vm.ctx.new_int(left_val ^ right_val)) + } else { + Err(vm.new_type_error(format!("Cannot rxor {} and {}", i.borrow(), i2.borrow()))) + } +} + fn int_or(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -552,6 +569,7 @@ Base 0 means to interpret the base from the string as an integer literal. context.set_attr(&int_type, "__format__", context.new_rustfunc(int_format)); context.set_attr(&int_type, "__truediv__", context.new_rustfunc(int_truediv)); context.set_attr(&int_type, "__xor__", context.new_rustfunc(int_xor)); + context.set_attr(&int_type, "__rxor__", context.new_rustfunc(int_rxor)); context.set_attr(&int_type, "__bool__", context.new_rustfunc(int_bool)); context.set_attr(&int_type, "__invert__", context.new_rustfunc(int_invert)); context.set_attr( From dfd0ea23c05aff45f98daecd5dc01048f48b6b67 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Wed, 6 Feb 2019 12:08:26 +0300 Subject: [PATCH 094/153] int type: Added tests for the __rxor__ --- tests/snippets/numbers.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/snippets/numbers.py b/tests/snippets/numbers.py index 01d8b815b..12e892385 100644 --- a/tests/snippets/numbers.py +++ b/tests/snippets/numbers.py @@ -4,9 +4,11 @@ assert x == 5 assert int.__doc__ == "int(x=0) -> integer\nint(x, base=10) -> integer\n\nConvert a number or string to an integer, or return 0 if no arguments\nare given. If x is a number, return x.__int__(). For floating point\nnumbers, this truncates towards zero.\n\nIf x is not a number or if base is given, then x must be a string,\nbytes, or bytearray instance representing an integer literal in the\ngiven base. The literal can be preceded by '+' or '-' and be surrounded\nby whitespace. The base defaults to 10. Valid bases are 0 and 2-36.\nBase 0 means to interpret the base from the string as an integer literal.\n>>> int('0b100', base=0)\n4" + class A(int): pass + x = A(7) assert x == 7 assert type(x) is A @@ -18,3 +20,10 @@ assert int(-1).__bool__() == True assert int(0).__invert__() == -1 assert int(-3).__invert__() == 2 assert int(4).__invert__() == -5 + +assert int(0).__rxor__(0) == 0 +assert int(1).__rxor__(0) == 1 +assert int(0).__rxor__(1) == 1 +assert int(1).__rxor__(1) == 0 +assert int(3).__rxor__(-3) == -2 +assert int(3).__rxor__(4) == 7 From af0fdcb9e33bde89d83275311c3989431ccfbafd Mon Sep 17 00:00:00 2001 From: silmeth Date: Wed, 6 Feb 2019 10:10:20 +0100 Subject: [PATCH 095/153] fix zero-division tests --- tests/snippets/division_by_zero.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/snippets/division_by_zero.py b/tests/snippets/division_by_zero.py index b4b036c84..7cb68cd76 100644 --- a/tests/snippets/division_by_zero.py +++ b/tests/snippets/division_by_zero.py @@ -2,33 +2,33 @@ try: 5 / 0 except ZeroDivisionError: pass -except: +else: assert False, 'Expected ZeroDivisionError' try: 5 / -0.0 except ZeroDivisionError: pass -except: +else: assert False, 'Expected ZeroDivisionError' try: - 5 / (3-2) + 5 / (2-2) except ZeroDivisionError: pass -except: +else: assert False, 'Expected ZeroDivisionError' try: 5 % 0 except ZeroDivisionError: pass -except: +else: assert False, 'Expected ZeroDivisionError' try: raise ZeroDivisionError('Is an ArithmeticError subclass?') except ArithmeticError: pass -except: - assert False, 'Expected ZeroDivisionError to be a subclass of ArithmeticError' +else: + assert False, 'Expected ZeroDivisionError' From 2f0647e847f1e8a2cbf0a052def49af14cf0831d Mon Sep 17 00:00:00 2001 From: Ryan Liddle Date: Wed, 6 Feb 2019 20:59:54 +1100 Subject: [PATCH 096/153] factored out xdg to cfg functions to allow windows builds to succeed --- src/main.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index afb49832e..453f3eb9c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,6 @@ extern crate log; extern crate rustpython_parser; extern crate rustpython_vm; extern crate rustyline; -extern crate xdg; use clap::{App, Arg}; use rustpython_parser::parser; @@ -143,6 +142,23 @@ fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool true } +#[cfg(target_family = "windows")] +fn get_history_path() -> String { + // + String::from(".repl_history.txt") +} + +#[cfg(target_family = "unix")] +fn get_history_path() -> String { + //work around for windows dependent builds. The xdg crate is unix specific + //so access to the BaseDirectories struct breaks builds on python. + extern crate xdg; + + let xdg_dirs = xdg::BaseDirectories::with_prefix("rustpython").unwrap(); + let repl_history_path = xdg_dirs.place_cache_file("repl_history.txt").unwrap(); + repl_history_path.to_str().unwrap() +} + fn run_shell(vm: &mut VirtualMachine) -> PyResult { println!( "Welcome to the magnificent Rust Python {} interpreter", @@ -155,9 +171,8 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { let mut input = String::new(); let mut rl = Editor::<()>::new(); - let xdg_dirs = xdg::BaseDirectories::with_prefix("rustpython").unwrap(); - let repl_history_path = xdg_dirs.place_cache_file("repl_history.txt").unwrap(); - let repl_history_path_str = repl_history_path.to_str().unwrap(); + //retrieve a history_path_str dependent to the os + let repl_history_path_str = &get_history_path(); if rl.load_history(repl_history_path_str).is_err() { println!("No previous history."); } From 401f477fc8c70631449f7926e22cffd3ae1425e7 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Wed, 6 Feb 2019 21:01:55 +1100 Subject: [PATCH 097/153] Format main.rs --- src/main.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index 453f3eb9c..18c0c679c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -144,19 +144,19 @@ fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool #[cfg(target_family = "windows")] fn get_history_path() -> String { - // - String::from(".repl_history.txt") + // + String::from(".repl_history.txt") } #[cfg(target_family = "unix")] fn get_history_path() -> String { - //work around for windows dependent builds. The xdg crate is unix specific - //so access to the BaseDirectories struct breaks builds on python. - extern crate xdg; + //work around for windows dependent builds. The xdg crate is unix specific + //so access to the BaseDirectories struct breaks builds on python. + extern crate xdg; let xdg_dirs = xdg::BaseDirectories::with_prefix("rustpython").unwrap(); let repl_history_path = xdg_dirs.place_cache_file("repl_history.txt").unwrap(); - repl_history_path.to_str().unwrap() + repl_history_path.to_str().unwrap() } fn run_shell(vm: &mut VirtualMachine) -> PyResult { @@ -171,8 +171,8 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { let mut input = String::new(); let mut rl = Editor::<()>::new(); - //retrieve a history_path_str dependent to the os - let repl_history_path_str = &get_history_path(); + //retrieve a history_path_str dependent to the os + let repl_history_path_str = &get_history_path(); if rl.load_history(repl_history_path_str).is_err() { println!("No previous history."); } From 346482849956a9abba23ecb43f8af492fe4991da Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 02:52:24 +0300 Subject: [PATCH 098/153] Fixed various small clippy warnings --- vm/src/builtins.rs | 6 ++-- vm/src/compile.rs | 9 +++--- vm/src/format.rs | 2 +- vm/src/frame.rs | 64 +++++++++++++++++++-------------------- vm/src/import.rs | 2 +- vm/src/obj/objbool.rs | 3 +- vm/src/obj/objdict.rs | 7 ++--- vm/src/obj/objlist.rs | 4 +-- vm/src/obj/objsequence.rs | 18 +++++------ vm/src/obj/objset.rs | 13 +++----- vm/src/obj/objstr.rs | 23 +++++++------- vm/src/obj/objtuple.rs | 4 +-- vm/src/obj/objtype.rs | 6 ++-- vm/src/pyobject.rs | 8 ++--- vm/src/stdlib/ast.rs | 33 +++++--------------- vm/src/stdlib/io.rs | 27 +++++++---------- vm/src/vm.rs | 3 +- 17 files changed, 97 insertions(+), 135 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 2e789383e..f1ccfd83a 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -136,11 +136,11 @@ fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mode = { let mode = objstr::get_value(mode); - if mode == String::from("exec") { + if mode == "exec" { compile::Mode::Exec - } else if mode == "eval".to_string() { + } else if mode == "eval" { compile::Mode::Eval - } else if mode == "single".to_string() { + } else if mode == "single" { compile::Mode::Single } else { return Err( diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 11def8b15..ebe4e826e 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -45,9 +45,8 @@ pub fn compile( }, }; - match result { - Err(msg) => return Err(vm.new_exception(syntax_error.clone(), msg)), - _ => {} + if let Err(msg) = result { + return Err(vm.new_exception(syntax_error.clone(), msg)); } let code = compiler.pop_code_object(); @@ -592,7 +591,7 @@ impl Compiler { ast::Statement::Assign { targets, value } => { self.compile_expression(value)?; - for (i, target) in targets.into_iter().enumerate() { + for (i, target) in targets.iter().enumerate() { if i + 1 != targets.len() { self.emit(Instruction::Duplicate); } @@ -668,7 +667,7 @@ impl Compiler { let mut flags = bytecode::FunctionOpArg::empty(); if have_kwargs { - flags = flags | bytecode::FunctionOpArg::HAS_DEFAULTS; + flags |= bytecode::FunctionOpArg::HAS_DEFAULTS; } Ok(flags) diff --git a/vm/src/format.rs b/vm/src/format.rs index 5b4035ef7..6dfa77c7e 100644 --- a/vm/src/format.rs +++ b/vm/src/format.rs @@ -314,7 +314,7 @@ impl FormatSpec { } None => Ok(magnitude.to_str_radix(10)), }; - if !raw_magnitude_string_result.is_ok() { + if raw_magnitude_string_result.is_err() { return raw_magnitude_string_result; } let magnitude_string = format!( diff --git a/vm/src/frame.rs b/vm/src/frame.rs index d4ce64da8..c1abb9112 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -611,7 +611,7 @@ impl Frame { .iter() .skip(*before) .take(middle) - .map(|x| x.clone()) + .cloned() .collect(); let t = vm.ctx.new_list(middle_elements); self.push_value(t); @@ -887,25 +887,25 @@ impl Frame { ) -> FrameResult { let b_ref = self.pop_value(); let a_ref = self.pop_value(); - let value = match op { - &bytecode::BinaryOperator::Subtract => vm._sub(a_ref, b_ref), - &bytecode::BinaryOperator::Add => vm._add(a_ref, b_ref), - &bytecode::BinaryOperator::Multiply => vm._mul(a_ref, b_ref), - &bytecode::BinaryOperator::MatrixMultiply => { + let value = match *op { + bytecode::BinaryOperator::Subtract => vm._sub(a_ref, b_ref), + bytecode::BinaryOperator::Add => vm._add(a_ref, b_ref), + bytecode::BinaryOperator::Multiply => vm._mul(a_ref, b_ref), + bytecode::BinaryOperator::MatrixMultiply => { vm.call_method(&a_ref, "__matmul__", vec![b_ref]) } - &bytecode::BinaryOperator::Power => vm._pow(a_ref, b_ref), - &bytecode::BinaryOperator::Divide => vm._div(a_ref, b_ref), - &bytecode::BinaryOperator::FloorDivide => { + bytecode::BinaryOperator::Power => vm._pow(a_ref, b_ref), + bytecode::BinaryOperator::Divide => vm._div(a_ref, b_ref), + bytecode::BinaryOperator::FloorDivide => { vm.call_method(&a_ref, "__floordiv__", vec![b_ref]) } - &bytecode::BinaryOperator::Subscript => self.subscript(vm, a_ref, b_ref), - &bytecode::BinaryOperator::Modulo => vm._modulo(a_ref, b_ref), - &bytecode::BinaryOperator::Lshift => vm.call_method(&a_ref, "__lshift__", vec![b_ref]), - &bytecode::BinaryOperator::Rshift => vm.call_method(&a_ref, "__rshift__", vec![b_ref]), - &bytecode::BinaryOperator::Xor => vm._xor(a_ref, b_ref), - &bytecode::BinaryOperator::Or => vm._or(a_ref, b_ref), - &bytecode::BinaryOperator::And => vm._and(a_ref, b_ref), + bytecode::BinaryOperator::Subscript => self.subscript(vm, a_ref, b_ref), + bytecode::BinaryOperator::Modulo => vm._modulo(a_ref, b_ref), + bytecode::BinaryOperator::Lshift => vm.call_method(&a_ref, "__lshift__", vec![b_ref]), + bytecode::BinaryOperator::Rshift => vm.call_method(&a_ref, "__rshift__", vec![b_ref]), + bytecode::BinaryOperator::Xor => vm._xor(a_ref, b_ref), + bytecode::BinaryOperator::Or => vm._or(a_ref, b_ref), + bytecode::BinaryOperator::And => vm._and(a_ref, b_ref), }?; self.push_value(value); @@ -918,11 +918,11 @@ impl Frame { op: &bytecode::UnaryOperator, ) -> FrameResult { let a = self.pop_value(); - let value = match op { - &bytecode::UnaryOperator::Minus => vm.call_method(&a, "__neg__", vec![])?, - &bytecode::UnaryOperator::Plus => vm.call_method(&a, "__pos__", vec![])?, - &bytecode::UnaryOperator::Invert => vm.call_method(&a, "__invert__", vec![])?, - &bytecode::UnaryOperator::Not => { + let value = match *op { + bytecode::UnaryOperator::Minus => vm.call_method(&a, "__neg__", vec![])?, + bytecode::UnaryOperator::Plus => vm.call_method(&a, "__pos__", vec![])?, + bytecode::UnaryOperator::Invert => vm.call_method(&a, "__invert__", vec![])?, + bytecode::UnaryOperator::Not => { let value = objbool::boolval(vm, a)?; vm.ctx.new_bool(!value) } @@ -995,17 +995,17 @@ impl Frame { ) -> FrameResult { let b = self.pop_value(); let a = self.pop_value(); - let value = match op { - &bytecode::ComparisonOperator::Equal => vm._eq(&a, b)?, - &bytecode::ComparisonOperator::NotEqual => vm._ne(&a, b)?, - &bytecode::ComparisonOperator::Less => vm._lt(&a, b)?, - &bytecode::ComparisonOperator::LessOrEqual => vm._le(&a, b)?, - &bytecode::ComparisonOperator::Greater => vm._gt(&a, b)?, - &bytecode::ComparisonOperator::GreaterOrEqual => vm._ge(&a, b)?, - &bytecode::ComparisonOperator::Is => vm.ctx.new_bool(self._is(a, b)), - &bytecode::ComparisonOperator::IsNot => self._is_not(vm, a, b)?, - &bytecode::ComparisonOperator::In => self._in(vm, a, b)?, - &bytecode::ComparisonOperator::NotIn => self._not_in(vm, a, b)?, + let value = match *op { + bytecode::ComparisonOperator::Equal => vm._eq(&a, b)?, + bytecode::ComparisonOperator::NotEqual => vm._ne(&a, b)?, + bytecode::ComparisonOperator::Less => vm._lt(&a, b)?, + bytecode::ComparisonOperator::LessOrEqual => vm._le(&a, b)?, + bytecode::ComparisonOperator::Greater => vm._gt(&a, b)?, + bytecode::ComparisonOperator::GreaterOrEqual => vm._ge(&a, b)?, + bytecode::ComparisonOperator::Is => vm.ctx.new_bool(self._is(a, b)), + bytecode::ComparisonOperator::IsNot => self._is_not(vm, a, b)?, + bytecode::ComparisonOperator::In => self._in(vm, a, b)?, + bytecode::ComparisonOperator::NotIn => self._not_in(vm, a, b)?, }; self.push_value(value); diff --git a/vm/src/import.rs b/vm/src/import.rs index e560d3b18..be8807881 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -103,7 +103,7 @@ fn find_source(vm: &VirtualMachine, current_path: PathBuf, name: &str) -> Result } } - match filepaths.iter().filter(|p| p.exists()).next() { + match filepaths.iter().find(|p| p.exists()) { Some(path) => Ok(path.to_path_buf()), None => Err(format!("No module named '{}'", name)), } diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index f3a2fafb8..dbec65a7c 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -20,6 +20,7 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result !value.is_zero(), _ => return Err(vm.new_type_error(String::from("TypeError"))), }; + v } else { true @@ -81,7 +82,7 @@ fn bool_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(match val { Some(val) => { let bv = boolval(vm, val.clone())?; - vm.new_bool(bv.clone()) + vm.new_bool(bv) } None => vm.context().new_bool(false), }) diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index b547eb906..614a41b78 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -106,10 +106,7 @@ pub fn contains_key_str(dict: &PyObjectRef, key: &str) -> bool { pub fn content_contains_key_str(elements: &DictContentType, key: &str) -> bool { // TODO: let hash: usize = key; - match elements.get(key) { - Some(_) => true, - None => false, - } + elements.get(key).is_some() } // Python dict methods: @@ -140,7 +137,7 @@ fn dict_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let elem_iter = objiter::get_iter(vm, &element)?; let needle = objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?; let value = objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?; - if let Some(_) = objiter::get_next_object(vm, &elem_iter)? { + if objiter::get_next_object(vm, &elem_iter)?.is_some() { return Err(err(vm)); } set_item(&dict, &needle, &value); diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 1bda7f11d..49e668a6a 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -172,7 +172,7 @@ fn list_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if objtype::isinstance(o2, &vm.ctx.list_type()) { let e1 = get_elements(o); let e2 = get_elements(o2); - let elements = e1.iter().chain(e2.iter()).map(|e| e.clone()).collect(); + let elements = e1.iter().chain(e2.iter()).cloned().collect(); Ok(vm.ctx.new_list(elements)) } else { Err(vm.new_type_error(format!("Cannot add {} and {}", o.borrow(), o2.borrow()))) @@ -224,7 +224,7 @@ fn list_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { for element in elements.iter() { let is_eq = vm._eq(element, value.clone())?; if objbool::boolval(vm, is_eq)? { - count = count + 1; + count += 1; } } Ok(vm.context().new_int(count.to_bigint().unwrap())) diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index c5504b22d..d2fbb2a96 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -34,17 +34,17 @@ pub trait PySliceableSequence { // TODO: we could potentially avoid this copy and use slice match &(slice.borrow()).payload { PyObjectPayload::Slice { start, stop, step } => { - let start = match start { - &Some(start) => self.get_pos(start), - &None => 0, + let start = match *start { + Some(start) => self.get_pos(start), + None => 0, }; - let stop = match stop { - &Some(stop) => self.get_pos(stop), - &None => self.len() as usize, + let stop = match *stop { + Some(stop) => self.get_pos(stop), + None => self.len() as usize, }; - match step { - &None | &Some(1) => self.do_slice(start, stop), - &Some(num) => { + match *step { + None | Some(1) => self.do_slice(start, stop), + Some(num) => { if num < 0 { unimplemented!("negative step indexing not yet supported") }; diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index b22213dd0..7e50070cd 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -67,15 +67,10 @@ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Some(iterable) => { let mut elements = HashMap::new(); let iterator = objiter::get_iter(vm, iterable)?; - loop { - match vm.call_method(&iterator, "__next__", vec![]) { - Ok(v) => { - // TODO: should we use the hash function here? - let key = v.get_id(); - elements.insert(key, v); - } - _ => break, - } + while let Ok(v) = vm.call_method(&iterator, "__next__", vec![]) { + // TODO: should we use the hash function here? + let key = v.get_id(); + elements.insert(key, v); } elements } diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index c33f70326..0673237aa 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -512,12 +512,11 @@ fn str_zfill(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let value = get_value(&s); let len = objint::get_value(&len).to_usize().unwrap(); - let new_str: String; - if len <= value.len() { - new_str = value; + let new_str = if len <= value.len() { + value } else { - new_str = format!("{}{}", "0".repeat(len - value.len()), value); - } + format!("{}{}", "0".repeat(len - value.len()), value) + }; Ok(vm.ctx.new_str(new_str)) } @@ -573,7 +572,7 @@ fn str_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok((start, end)) => (start, end), Err(e) => return Err(vm.new_index_error(e)), }; - let ind: usize = match value[start..end + 1].find(&sub) { + let ind: usize = match value[start..=end].find(&sub) { Some(num) => num, None => { return Err(vm.new_value_error("substring not found".to_string())); @@ -598,7 +597,7 @@ fn str_find(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok((start, end)) => (start, end), Err(e) => return Err(vm.new_index_error(e)), }; - let ind: i128 = match value[start..end + 1].find(&sub) { + let ind: i128 = match value[start..=end].find(&sub) { Some(num) => num as i128, None => -1 as i128, }; @@ -797,7 +796,7 @@ fn str_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if value.is_empty() { is_titled = false; } else { - for word in value.split(" ") { + for word in value.split(' ') { if word != make_title(&word) { is_titled = false; break; @@ -888,7 +887,7 @@ fn str_rindex(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok((start, end)) => (start, end), Err(e) => return Err(vm.new_index_error(e)), }; - let ind: i64 = match value[start..end + 1].rfind(&sub) { + let ind: i64 = match value[start..=end].rfind(&sub) { Some(num) => num as i64, None => { return Err(vm.new_value_error("substring not found".to_string())); @@ -913,7 +912,7 @@ fn str_rfind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok((start, end)) => (start, end), Err(e) => return Err(vm.new_index_error(e)), }; - let ind = match value[start..end + 1].rfind(&sub) { + let ind = match value[start..=end].rfind(&sub) { Some(num) => num as i128, None => -1 as i128, }; @@ -1052,8 +1051,8 @@ pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResu } } } else { - match &(*b.borrow()).payload { - &PyObjectPayload::Slice { .. } => { + match (*b.borrow()).payload { + PyObjectPayload::Slice { .. } => { Ok(vm.new_str(value.to_string().get_slice_items(&b).to_string())) } _ => panic!( diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index b969142cc..b5267e090 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -110,7 +110,7 @@ fn tuple_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { if objtype::isinstance(other, &vm.ctx.tuple_type()) { let e1 = get_elements(zelf); let e2 = get_elements(other); - let elements = e1.iter().chain(e2.iter()).map(|e| e.clone()).collect(); + let elements = e1.iter().chain(e2.iter()).cloned().collect(); Ok(vm.ctx.new_tuple(elements)) } else { Err(vm.new_type_error(format!( @@ -132,7 +132,7 @@ fn tuple_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { for element in elements.iter() { let is_eq = vm._eq(element, value.clone())?; if objbool::boolval(vm, is_eq)? { - count = count + 1; + count += 1; } } Ok(vm.context().new_int(count.to_bigint().unwrap())) diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 7b2ed48c5..b72526eb2 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -242,8 +242,8 @@ fn take_next_base( for base in &bases { let head = base[0].clone(); if !(&bases) - .into_iter() - .any(|x| x[1..].into_iter().any(|x| x.get_id() == head.get_id())) + .iter() + .any(|x| x[1..].iter().any(|x| x.get_id() == head.get_id())) { next = Some(head); break; @@ -265,7 +265,7 @@ fn linearise_mro(mut bases: Vec>) -> Option> { debug!("Linearising MRO: {:?}", bases); let mut result = vec![]; loop { - if (&bases).into_iter().all(|x| x.is_empty()) { + if (&bases).iter().all(|x| x.is_empty()) { break; } match take_next_base(bases) { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 072ebb6bc..8ef13c9d1 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -667,10 +667,7 @@ pub trait ParentProtocol { impl ParentProtocol for PyObjectRef { fn has_parent(&self) -> bool { match self.borrow().payload { - PyObjectPayload::Scope { ref scope } => match scope.parent { - Some(_) => true, - None => false, - }, + PyObjectPayload::Scope { ref scope } => scope.parent.is_some(), _ => panic!("Only scopes have parent (not {:?}", self), } } @@ -733,8 +730,7 @@ impl AttributeProtocol for PyObjectRef { match obj.payload { PyObjectPayload::Module { ref dict, .. } => dict.contains_key(attr_name), PyObjectPayload::Class { ref mro, .. } => { - class_has_item(self, attr_name) - || mro.into_iter().any(|d| class_has_item(d, attr_name)) + class_has_item(self, attr_name) || mro.iter().any(|d| class_has_item(d, attr_name)) } PyObjectPayload::Instance { ref dict } => dict.contains_key(attr_name), _ => false, diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index 6b9eb66c9..8ae3b72c7 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -47,8 +47,7 @@ fn program_to_ast(ctx: &PyContext, program: &ast::Program) -> PyObjectRef { fn create_node(ctx: &PyContext, _name: &str) -> PyObjectRef { // TODO: instantiate a class of type given by name // TODO: lookup in the current module? - let node = ctx.new_object(); - node + ctx.new_object() } fn statements_to_ast(ctx: &PyContext, statements: &[ast::LocatedStatement]) -> PyObjectRef { @@ -101,18 +100,9 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj ctx.set_attr(&node, "decorator_list", py_decorator_list); node } - ast::Statement::Continue => { - let node = create_node(ctx, "Continue"); - node - } - ast::Statement::Break => { - let node = create_node(ctx, "Break"); - node - } - ast::Statement::Pass => { - let node = create_node(ctx, "Pass"); - node - } + ast::Statement::Continue => create_node(ctx, "Continue"), + ast::Statement::Break => create_node(ctx, "Break"), + ast::Statement::Pass => create_node(ctx, "Pass"), ast::Statement::Assert { test, msg } => { let node = create_node(ctx, "Pass"); @@ -129,12 +119,8 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj ast::Statement::Delete { targets } => { let node = create_node(ctx, "Delete"); - let py_targets = ctx.new_tuple( - targets - .into_iter() - .map(|v| expression_to_ast(ctx, v)) - .collect(), - ); + let py_targets = + ctx.new_tuple(targets.iter().map(|v| expression_to_ast(ctx, v)).collect()); ctx.set_attr(&node, "targets", py_targets); node @@ -143,12 +129,7 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj let node = create_node(ctx, "Return"); let py_value = if let Some(value) = value { - ctx.new_tuple( - value - .into_iter() - .map(|v| expression_to_ast(ctx, v)) - .collect(), - ) + ctx.new_tuple(value.iter().map(|v| expression_to_ast(ctx, v)).collect()) } else { ctx.none() }; diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index c4391d116..5532e85f1 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -27,7 +27,7 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; fn compute_c_flag(mode: &str) -> u16 { - match mode.as_ref() { + match mode { "w" => 512, "x" => 512, "a" => 8, @@ -85,11 +85,8 @@ fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .map_err(|_| vm.new_value_error("IO Error".to_string()))?; //Copy bytes from the buffer vector into the results vector - match buffer.borrow_mut().payload { - PyObjectPayload::Bytes { ref mut value } => { - result.extend(value.iter().cloned()); - } - _ => {} + if let PyObjectPayload::Bytes { ref mut value } = buffer.borrow_mut().payload { + result.extend(value.iter().cloned()); }; let len = vm.get_method(buffer.clone(), &"__len__".to_string()); @@ -171,16 +168,14 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let handle = os::rust_file(raw_fd); let mut f = handle.take(length); - match obj.borrow_mut().payload { + if let PyObjectPayload::Bytes { ref mut value } = obj.borrow_mut().payload { //TODO: Implement for MemoryView - PyObjectPayload::Bytes { ref mut value } => { - value.clear(); - match f.read_to_end(&mut *value) { - Ok(_) => {} - Err(_) => return Err(vm.new_value_error("Error reading from Take".to_string())), - } + + value.clear(); + match f.read_to_end(&mut *value) { + Ok(_) => {} + Err(_) => return Err(vm.new_value_error("Error reading from Take".to_string())), } - _ => {} }; let updated = os::raw_file_number(f.into_inner()).to_bigint(); @@ -320,7 +315,7 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //the operation in the mode. //There are 3 possible classes here, each inheriting from the RawBaseIO // creating || writing || appending => BufferedWriter - let buffered = if rust_mode.contains("w") { + let buffered = if rust_mode.contains('w') { vm.invoke( buffered_writer_class, PyFuncArgs::new(vec![file_io.clone()], vec![]), @@ -334,7 +329,7 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { //TODO: updating => PyBufferedRandom }; - if rust_mode.contains("t") { + if rust_mode.contains('t') { //If the mode is text this buffer type is consumed on construction of //a TextIOWrapper which is subsequently returned. vm.invoke( diff --git a/vm/src/vm.rs b/vm/src/vm.rs index b1570e386..7a5a76d9a 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -91,8 +91,7 @@ impl VirtualMachine { }; // Call function: - let exception = self.invoke(exc_type, args).unwrap(); - exception + self.invoke(exc_type, args).unwrap() } pub fn new_type_error(&mut self, msg: String) -> PyObjectRef { From 51d5f9d3a49461025f06c25da98b84cecb69d514 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Wed, 6 Feb 2019 21:46:50 +1100 Subject: [PATCH 099/153] changed String to PathBuf --- src/main.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 18c0c679c..61352c0fb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -143,20 +143,19 @@ fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool } #[cfg(target_family = "windows")] -fn get_history_path() -> String { - // - String::from(".repl_history.txt") +fn get_history_path() -> PathBuf { + //Path buffer + PathBuf::from(".repl_history.txt") } #[cfg(target_family = "unix")] -fn get_history_path() -> String { +fn get_history_path() -> PathBuf { //work around for windows dependent builds. The xdg crate is unix specific //so access to the BaseDirectories struct breaks builds on python. extern crate xdg; let xdg_dirs = xdg::BaseDirectories::with_prefix("rustpython").unwrap(); - let repl_history_path = xdg_dirs.place_cache_file("repl_history.txt").unwrap(); - repl_history_path.to_str().unwrap() + xdg_dirs.place_cache_file("repl_history.txt").unwrap() } fn run_shell(vm: &mut VirtualMachine) -> PyResult { From f89dc0061b982767f596d478c920b219d62ae848 Mon Sep 17 00:00:00 2001 From: rmliddle Date: Wed, 6 Feb 2019 22:37:35 +1100 Subject: [PATCH 100/153] included all not unix in static pathbuf --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 61352c0fb..a5672e700 100644 --- a/src/main.rs +++ b/src/main.rs @@ -142,7 +142,7 @@ fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool true } -#[cfg(target_family = "windows")] +#[cfg(not(target_family = "unix"))] fn get_history_path() -> PathBuf { //Path buffer PathBuf::from(".repl_history.txt") From 2d7164df9202c625d9ac4055c41b023f0c523ffb Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Tue, 5 Feb 2019 14:49:41 +0300 Subject: [PATCH 101/153] float type: Added __doc__ --- vm/src/obj/objfloat.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 4cb996a51..1bb57e572 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -268,6 +268,9 @@ fn float_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let float_type = &context.float_type; + + let float_doc = "Convert a string or number to a floating point number, if possible."; + context.set_attr(&float_type, "__eq__", context.new_rustfunc(float_eq)); context.set_attr(&float_type, "__lt__", context.new_rustfunc(float_lt)); context.set_attr(&float_type, "__le__", context.new_rustfunc(float_le)); @@ -291,4 +294,9 @@ pub fn init(context: &PyContext) { context.set_attr(&float_type, "__pow__", context.new_rustfunc(float_pow)); context.set_attr(&float_type, "__sub__", context.new_rustfunc(float_sub)); context.set_attr(&float_type, "__repr__", context.new_rustfunc(float_repr)); + context.set_attr( + &float_type, + "__doc__", + context.new_str(float_doc.to_string()), + ); } From 1992e02b7653c008446963ca41228fb6f63bb752 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Wed, 6 Feb 2019 15:16:41 +0300 Subject: [PATCH 102/153] bytes type: Added __doc__ --- vm/src/obj/objbytes.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 6288264a2..c25edeeea 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -15,12 +15,29 @@ use std::ops::Deref; // Fill bytes class methods: pub fn init(context: &PyContext) { let bytes_type = &context.bytes_type; + + let bytes_doc = + "bytes(iterable_of_ints) -> bytes\n\ + bytes(string, encoding[, errors]) -> bytes\n\ + bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer\n\ + bytes(int) -> bytes object of size given by the parameter initialized with null bytes\n\ + bytes() -> empty bytes object\n\nConstruct an immutable array of bytes from:\n \ + - an iterable yielding integers in range(256)\n \ + - a text string encoded using the specified encoding\n \ + - any object implementing the buffer API.\n \ + - an integer"; + context.set_attr(bytes_type, "__eq__", context.new_rustfunc(bytes_eq)); context.set_attr(bytes_type, "__hash__", context.new_rustfunc(bytes_hash)); context.set_attr(bytes_type, "__new__", context.new_rustfunc(bytes_new)); context.set_attr(bytes_type, "__repr__", context.new_rustfunc(bytes_repr)); context.set_attr(bytes_type, "__len__", context.new_rustfunc(bytes_len)); - context.set_attr(bytes_type, "__iter__", context.new_rustfunc(bytes_iter)) + context.set_attr(bytes_type, "__iter__", context.new_rustfunc(bytes_iter)); + context.set_attr( + bytes_type, + "__doc__", + context.new_str(bytes_doc.to_string()), + ); } fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From ca63f899d6828f6cc6f43bf24716bb24434f3e4c Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Wed, 6 Feb 2019 14:54:27 +0300 Subject: [PATCH 103/153] list type: Added __doc__ --- vm/src/obj/objlist.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 1bda7f11d..4ec4a7e80 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -366,6 +366,11 @@ fn list_pop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let list_type = &context.list_type; + + let list_doc = "Built-in mutable sequence.\n\n\ + If no argument is given, the constructor creates a new empty list.\n\ + The argument must be an iterable if specified."; + context.set_attr(&list_type, "__add__", context.new_rustfunc(list_add)); context.set_attr( &list_type, @@ -392,6 +397,7 @@ pub fn init(context: &PyContext) { context.set_attr(&list_type, "__len__", context.new_rustfunc(list_len)); context.set_attr(&list_type, "__new__", context.new_rustfunc(list_new)); context.set_attr(&list_type, "__repr__", context.new_rustfunc(list_repr)); + context.set_attr(&list_type, "__doc__", context.new_str(list_doc.to_string())); context.set_attr(&list_type, "append", context.new_rustfunc(list_append)); context.set_attr(&list_type, "clear", context.new_rustfunc(list_clear)); context.set_attr(&list_type, "count", context.new_rustfunc(list_count)); From a83fd7c1afdb397abae65e00980eed47c6944faa Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 6 Feb 2019 16:01:07 +0000 Subject: [PATCH 104/153] Add __code__ to functions. --- vm/src/compile.rs | 7 ++----- vm/src/frame.rs | 4 +--- vm/src/obj/objfunction.rs | 13 +++++++++++++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 11def8b15..fb9215341 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -7,7 +7,7 @@ //! https://github.com/micropython/micropython/blob/master/py/compile.c use super::bytecode::{self, CallType, CodeObject, Instruction}; -use super::pyobject::{PyObject, PyObjectPayload, PyResult}; +use super::pyobject::PyResult; use super::vm::VirtualMachine; use num_complex::Complex64; use rustpython_parser::{ast, parser}; @@ -52,10 +52,7 @@ pub fn compile( let code = compiler.pop_code_object(); trace!("Compilation completed: {:?}", code); - Ok(PyObject::new( - PyObjectPayload::Code { code }, - vm.ctx.code_type(), - )) + Ok(vm.ctx.new_code_object(code)) } pub enum Mode { diff --git a/vm/src/frame.rs b/vm/src/frame.rs index d4ce64da8..3b763b0af 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -1042,9 +1042,7 @@ impl Frame { bytecode::Constant::String { ref value } => vm.new_str(value.clone()), bytecode::Constant::Bytes { ref value } => vm.ctx.new_bytes(value.clone()), bytecode::Constant::Boolean { ref value } => vm.new_bool(value.clone()), - bytecode::Constant::Code { ref code } => { - PyObject::new(PyObjectPayload::Code { code: code.clone() }, vm.get_type()) - } + bytecode::Constant::Code { ref code } => vm.ctx.new_code_object(code.clone()), bytecode::Constant::Tuple { ref elements } => vm.ctx.new_tuple( elements .iter() diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index b665e581a..f04897481 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -9,6 +9,12 @@ pub fn init(context: &PyContext) { let function_type = &context.function_type; context.set_attr(&function_type, "__get__", context.new_rustfunc(bind_method)); + context.set_attr( + &function_type, + "__code__", + context.new_member_descriptor(function_code), + ); + let member_descriptor_type = &context.member_descriptor_type; context.set_attr( &member_descriptor_type, @@ -55,6 +61,13 @@ fn bind_method(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } +fn function_code(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + match args.args[0].borrow().payload { + PyObjectPayload::Function { ref code, .. } => Ok(code.clone()), + _ => Err(vm.new_type_error("no code".to_string())), + } +} + fn member_get(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { match args.shift().get_attr("function") { Some(function) => vm.invoke(function, args), From aca02383798bc7d8e9f27d3a0fa46ffcfb266375 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 6 Feb 2019 17:36:22 +0000 Subject: [PATCH 105/153] Allow new_int to take anything with ToBigInt. --- vm/src/pyobject.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 072ebb6bc..8b3ced6b6 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -27,6 +27,7 @@ use super::obj::objtuple; use super::obj::objtype; use super::vm::VirtualMachine; use num_bigint::BigInt; +use num_bigint::ToBigInt; use num_complex::Complex64; use num_traits::{One, Zero}; use std::cell::RefCell; @@ -418,8 +419,13 @@ impl PyContext { ) } - pub fn new_int(&self, i: BigInt) -> PyObjectRef { - PyObject::new(PyObjectPayload::Integer { value: i }, self.int_type()) + pub fn new_int(&self, i: T) -> PyObjectRef { + PyObject::new( + PyObjectPayload::Integer { + value: i.to_bigint().unwrap(), + }, + self.int_type(), + ) } pub fn new_float(&self, i: f64) -> PyObjectRef { From fd344ec1e0996d6d9710102d096eaeb3176cb5a1 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 6 Feb 2019 17:38:01 +0000 Subject: [PATCH 106/153] Remove unnecessary to_bigint().unwrap() everywhere. --- vm/src/builtins.rs | 8 +++----- vm/src/frame.rs | 5 ++--- vm/src/obj/objbytearray.rs | 4 +--- vm/src/obj/objbytes.rs | 6 ++---- vm/src/obj/objdict.rs | 3 +-- vm/src/obj/objint.rs | 6 +++--- vm/src/obj/objiter.rs | 6 +++--- vm/src/obj/objlist.rs | 7 +++---- vm/src/obj/objrange.rs | 11 +++-------- vm/src/obj/objset.rs | 3 +-- vm/src/obj/objstr.rs | 15 +++++++-------- vm/src/obj/objtuple.rs | 7 +++---- vm/src/stdlib/ast.rs | 8 +++----- vm/src/stdlib/io.rs | 12 +++++------- vm/src/stdlib/json.rs | 11 ++++------- vm/src/stdlib/os.rs | 19 +++++++------------ vm/src/stdlib/pystruct.rs | 18 +++++++++--------- vm/src/sysmodule.rs | 11 +++-------- vm/src/vm.rs | 9 ++++----- 19 files changed, 67 insertions(+), 102 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 2e789383e..b1e5904eb 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -359,7 +359,7 @@ fn builtin_hex(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn builtin_id(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, None)]); - Ok(vm.context().new_int(obj.get_id().to_bigint().unwrap())) + Ok(vm.context().new_int(obj.get_id())) } // builtin_input @@ -553,9 +553,7 @@ fn builtin_ord(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ))); } match string.chars().next() { - Some(character) => Ok(vm - .context() - .new_int((character as i32).to_bigint().unwrap())), + Some(character) => Ok(vm.context().new_int(character as i32)), None => Err(vm.new_type_error( "ord() could not guess the integer representing this character".to_string(), )), @@ -635,7 +633,7 @@ fn builtin_sum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let items = vm.extract_elements(iterable)?; // Start with zero and add at will: - let mut sum = vm.ctx.new_int(Zero::zero()); + let mut sum = vm.ctx.new_int(0); for item in items { sum = vm._add(sum, item)?; } diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 3b763b0af..d19ceeeb7 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -20,7 +20,6 @@ use super::pyobject::{ PyResult, TypeProtocol, }; use super::vm::VirtualMachine; -use num_bigint::ToBigInt; use num_traits::ToPrimitive; #[derive(Clone, Debug)] @@ -121,7 +120,7 @@ impl Frame { trace!("Adding to traceback: {:?} {:?}", traceback, lineno); let pos = vm.ctx.new_tuple(vec![ vm.ctx.new_str(filename.clone()), - vm.ctx.new_int(lineno.get_row().to_bigint().unwrap()), + vm.ctx.new_int(lineno.get_row()), vm.ctx.new_str(run_obj_name.clone()), ]); objlist::list_append( @@ -1036,7 +1035,7 @@ impl Frame { fn unwrap_constant(&self, vm: &VirtualMachine, value: &bytecode::Constant) -> PyObjectRef { match *value { - bytecode::Constant::Integer { ref value } => vm.ctx.new_int(value.to_bigint().unwrap()), + bytecode::Constant::Integer { ref value } => vm.ctx.new_int(value.clone()), bytecode::Constant::Float { ref value } => vm.ctx.new_float(*value), bytecode::Constant::Complex { ref value } => vm.ctx.new_complex(*value), bytecode::Constant::String { ref value } => vm.new_str(value.clone()), diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index f62b689af..c6fc8c35c 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -9,7 +9,6 @@ use super::objint; use super::super::vm::VirtualMachine; use super::objbytes::get_value; use super::objtype; -use num_bigint::ToBigInt; use num_traits::ToPrimitive; // Binary data support @@ -110,8 +109,7 @@ fn bytesarray_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(a, Some(vm.ctx.bytearray_type()))]); let byte_vec = get_value(a).to_vec(); - let value = byte_vec.len().to_bigint(); - Ok(vm.ctx.new_int(value.unwrap())) + Ok(vm.ctx.new_int(byte_vec.len())) } fn bytearray_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 6288264a2..8a468aa73 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -4,7 +4,6 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objint; use super::objtype; -use num_bigint::ToBigInt; use num_traits::ToPrimitive; use std::cell::Ref; use std::hash::{Hash, Hasher}; @@ -70,8 +69,7 @@ fn bytes_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(a, Some(vm.ctx.bytes_type()))]); let byte_vec = get_value(a).to_vec(); - let value = byte_vec.len().to_bigint(); - Ok(vm.ctx.new_int(value.unwrap())) + Ok(vm.ctx.new_int(byte_vec.len())) } fn bytes_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -80,7 +78,7 @@ fn bytes_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut hasher = std::collections::hash_map::DefaultHasher::new(); data.hash(&mut hasher); let hash = hasher.finish(); - Ok(vm.ctx.new_int(hash.to_bigint().unwrap())) + Ok(vm.ctx.new_int(hash)) } pub fn get_value<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a { diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index b547eb906..0a3a0cb11 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -5,7 +5,6 @@ use super::super::vm::VirtualMachine; use super::objiter; use super::objstr; use super::objtype; -use num_bigint::ToBigInt; use std::cell::{Ref, RefMut}; use std::collections::HashMap; use std::ops::{Deref, DerefMut}; @@ -156,7 +155,7 @@ fn dict_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn dict_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(dict_obj, Some(vm.ctx.dict_type()))]); let elements = get_elements(dict_obj); - Ok(vm.ctx.new_int(elements.len().to_bigint().unwrap())) + Ok(vm.ctx.new_int(elements.len())) } fn dict_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 646dd13f9..6894a0492 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -250,7 +250,7 @@ fn int_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut hasher = std::collections::hash_map::DefaultHasher::new(); value.hash(&mut hasher); let hash = hasher.finish(); - Ok(vm.ctx.new_int(hash.to_bigint().unwrap())) + Ok(vm.ctx.new_int(hash)) } fn int_abs(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -414,7 +414,7 @@ fn int_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let v1 = get_value(i); if objtype::isinstance(i2, &vm.ctx.int_type()) { let v2 = get_value(i2).to_u32().unwrap(); - Ok(vm.ctx.new_int(v1.pow(v2).to_bigint().unwrap())) + Ok(vm.ctx.new_int(v1.pow(v2))) } else if objtype::isinstance(i2, &vm.ctx.float_type()) { let v2 = objfloat::get_value(i2); Ok(vm.ctx.new_float((v1.to_f64().unwrap()).powf(v2))) @@ -513,7 +513,7 @@ fn int_bit_length(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(i, Some(vm.ctx.int_type()))]); let v = get_value(i); let bits = v.bits(); - Ok(vm.ctx.new_int(bits.to_bigint().unwrap())) + Ok(vm.ctx.new_int(bits)) } fn int_conjugate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index b7368f7b2..7d4507212 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -9,7 +9,7 @@ use super::super::vm::VirtualMachine; use super::objbool; // use super::objstr; use super::objtype; // Required for arg_check! to use isinstance -use num_bigint::{BigInt, ToBigInt}; +use num_bigint::BigInt; /* * This helper function is called at multiple places. First, it is called @@ -128,7 +128,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { PyObjectPayload::Range { ref range } => { if let Some(int) = range.get(BigInt::from(*position)) { *position += 1; - Ok(vm.ctx.new_int(int.to_bigint().unwrap())) + Ok(vm.ctx.new_int(int)) } else { let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); let stop_iteration = @@ -139,7 +139,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { PyObjectPayload::Bytes { ref value } => { if *position < value.len() { - let obj_ref = vm.ctx.new_int(value[*position].to_bigint().unwrap()); + let obj_ref = vm.ctx.new_int(value[*position]); *position += 1; Ok(obj_ref) } else { diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 1bda7f11d..9b2582425 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -10,7 +10,6 @@ use super::objsequence::{ }; use super::objstr; use super::objtype; -use num_bigint::ToBigInt; use num_traits::ToPrimitive; // set_item: @@ -227,7 +226,7 @@ fn list_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { count = count + 1; } } - Ok(vm.context().new_int(count.to_bigint().unwrap())) + Ok(vm.context().new_int(count)) } pub fn list_extend(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -252,7 +251,7 @@ fn list_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { for (index, element) in get_elements(list).iter().enumerate() { let py_equal = vm.call_method(needle, "__eq__", vec![element.clone()])?; if objbool::get_value(&py_equal) { - return Ok(vm.context().new_int(index.to_bigint().unwrap())); + return Ok(vm.context().new_int(index)); } } let needle_str = objstr::get_value(&vm.to_str(needle).unwrap()); @@ -263,7 +262,7 @@ 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 elements = get_elements(list); - Ok(vm.context().new_int(elements.len().to_bigint().unwrap())) + Ok(vm.context().new_int(elements.len())) } fn list_reverse(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 647298ea2..605dabf84 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -4,7 +4,7 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objint; use super::objtype; -use num_bigint::{BigInt, ToBigInt}; +use num_bigint::BigInt; use num_integer::Integer; use num_traits::{One, Signed, ToPrimitive, Zero}; @@ -139,7 +139,7 @@ fn range_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { _ => unreachable!(), }; - Ok(vm.ctx.new_int(len.to_bigint().unwrap())) + Ok(vm.ctx.new_int(len)) } fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -157,12 +157,7 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match subscript.borrow().payload { PyObjectPayload::Integer { ref value } => { if let Some(int) = zrange.get(value.clone()) { - Ok(PyObject::new( - PyObjectPayload::Integer { - value: int.to_bigint().unwrap(), - }, - vm.ctx.int_type(), - )) + Ok(vm.ctx.new_int(int)) } else { Err(vm.new_index_error("range object index out of range".to_string())) } diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index b22213dd0..c0877a03f 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -11,7 +11,6 @@ use super::objbool; use super::objiter; use super::objstr; use super::objtype; -use num_bigint::ToBigInt; use std::collections::HashMap; pub fn get_elements(obj: &PyObjectRef) -> HashMap { @@ -91,7 +90,7 @@ fn set_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("set.len called with: {:?}", args); arg_check!(vm, args, required = [(s, Some(vm.ctx.set_type()))]); let elements = get_elements(s); - Ok(vm.context().new_int(elements.len().to_bigint().unwrap())) + Ok(vm.context().new_int(elements.len())) } fn set_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index c33f70326..73f1816b6 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -6,7 +6,6 @@ use super::super::vm::VirtualMachine; use super::objint; use super::objsequence::PySliceableSequence; use super::objtype; -use num_bigint::ToBigInt; use num_traits::ToPrimitive; use std::hash::{Hash, Hasher}; // rust's builtin to_lowercase isn't sufficient for casefold @@ -308,13 +307,13 @@ fn str_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut hasher = std::collections::hash_map::DefaultHasher::new(); value.hash(&mut hasher); let hash = hasher.finish(); - Ok(vm.ctx.new_int(hash.to_bigint().unwrap())) + Ok(vm.ctx.new_int(hash)) } fn str_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); let sv = get_value(s); - Ok(vm.ctx.new_int(sv.chars().count().to_bigint().unwrap())) + Ok(vm.ctx.new_int(sv.chars().count())) } fn str_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -554,7 +553,7 @@ fn str_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Err(e) => return Err(vm.new_index_error(e)), }; let num_occur: usize = value[start..end].matches(&sub).count(); - Ok(vm.ctx.new_int(num_occur.to_bigint().unwrap())) + Ok(vm.ctx.new_int(num_occur)) } fn str_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -579,7 +578,7 @@ fn str_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return Err(vm.new_value_error("substring not found".to_string())); } }; - Ok(vm.ctx.new_int(ind.to_bigint().unwrap())) + Ok(vm.ctx.new_int(ind)) } fn str_find(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -602,7 +601,7 @@ fn str_find(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Some(num) => num as i128, None => -1 as i128, }; - Ok(vm.ctx.new_int(ind.to_bigint().unwrap())) + Ok(vm.ctx.new_int(ind)) } // casefold is much more aggresive than lower @@ -894,7 +893,7 @@ fn str_rindex(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return Err(vm.new_value_error("substring not found".to_string())); } }; - Ok(vm.ctx.new_int(ind.to_bigint().unwrap())) + Ok(vm.ctx.new_int(ind)) } fn str_rfind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -917,7 +916,7 @@ fn str_rfind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Some(num) => num as i128, None => -1 as i128, }; - Ok(vm.ctx.new_int(ind.to_bigint().unwrap())) + Ok(vm.ctx.new_int(ind)) } fn str_isnumeric(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index b969142cc..54b06f278 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -9,7 +9,6 @@ use super::objsequence::{ }; use super::objstr; use super::objtype; -use num_bigint::ToBigInt; use std::hash::{Hash, Hasher}; fn tuple_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -135,7 +134,7 @@ fn tuple_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { count = count + 1; } } - Ok(vm.context().new_int(count.to_bigint().unwrap())) + Ok(vm.context().new_int(count)) } fn tuple_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -164,7 +163,7 @@ fn tuple_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { element_hash.hash(&mut hasher); } let hash = hasher.finish(); - Ok(vm.ctx.new_int(hash.to_bigint().unwrap())) + Ok(vm.ctx.new_int(hash)) } fn tuple_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -184,7 +183,7 @@ fn tuple_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn tuple_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.tuple_type()))]); let elements = get_elements(zelf); - Ok(vm.context().new_int(elements.len().to_bigint().unwrap())) + Ok(vm.context().new_int(elements.len())) } fn tuple_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index 6b9eb66c9..81f8aac27 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -9,9 +9,7 @@ use self::rustpython_parser::{ast, parser}; use super::super::obj::{objstr, objtype}; use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use super::super::VirtualMachine; -use num_bigint::ToBigInt; use num_complex::Complex64; -use num_traits::One; use std::ops::Deref; /* @@ -232,7 +230,7 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj }; // set lineno on node: - let lineno = ctx.new_int(statement.location.get_row().to_bigint().unwrap()); + let lineno = ctx.new_int(statement.location.get_row()); ctx.set_attr(&node, "lineno", lineno); node @@ -385,7 +383,7 @@ fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectR let node = create_node(ctx, "Num"); let py_n = match value { - ast::Number::Integer { value } => ctx.new_int(value.to_bigint().unwrap()), + ast::Number::Integer { value } => ctx.new_int(value.clone()), ast::Number::Float { value } => ctx.new_float(*value), ast::Number::Complex { real, imag } => { ctx.new_complex(Complex64::new(*real, *imag)) @@ -550,7 +548,7 @@ fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectR }; // TODO: retrieve correct lineno: - let lineno = ctx.new_int(One::one()); + let lineno = ctx.new_int(1); ctx.set_attr(&node, "lineno", lineno); node diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index c4391d116..2fe2c2255 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -183,9 +183,8 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { _ => {} }; - let updated = os::raw_file_number(f.into_inner()).to_bigint(); - vm.ctx - .set_attr(&file_io, "fileno", vm.ctx.new_int(updated.unwrap())); + let updated = os::raw_file_number(f.into_inner()); + vm.ctx.set_attr(&file_io, "fileno", vm.ctx.new_int(updated)); Ok(vm.get_none()) } @@ -209,12 +208,11 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match handle.write(&value[..]) { Ok(len) => { //reset raw fd on the FileIO object - let updated = os::raw_file_number(handle).to_bigint(); - vm.ctx - .set_attr(&file_io, "fileno", vm.ctx.new_int(updated.unwrap())); + let updated = os::raw_file_number(handle); + vm.ctx.set_attr(&file_io, "fileno", vm.ctx.new_int(updated)); //return number of bytes written - Ok(vm.ctx.new_int(len.to_bigint().unwrap())) + Ok(vm.ctx.new_int(len)) } Err(_) => Err(vm.new_value_error("Error Writing Bytes to Handle".to_string())), } diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 1101a8713..e284c2c31 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -11,7 +11,6 @@ use super::super::pyobject::{ TypeProtocol, }; use super::super::VirtualMachine; -use num_bigint::ToBigInt; use num_traits::cast::ToPrimitive; // We need to have a VM available to serialise a PyObject based on its subclass, so we implement @@ -119,7 +118,7 @@ impl<'de> serde::de::DeserializeSeed<'de> for PyObjectDeserializer<'de> { { // The JSON deserialiser always uses the i64/u64 deserialisers, so we only need to // implement those for now - Ok(self.ctx.new_int(value.to_bigint().unwrap())) + Ok(self.ctx.new_int(value)) } fn visit_u64(self, value: u64) -> Result @@ -128,7 +127,7 @@ impl<'de> serde::de::DeserializeSeed<'de> for PyObjectDeserializer<'de> { { // The JSON deserialiser always uses the i64/u64 deserialisers, so we only need to // implement those for now - Ok(self.ctx.new_int(value.to_bigint().unwrap())) + Ok(self.ctx.new_int(value)) } fn visit_f64(self, value: f64) -> Result @@ -223,10 +222,8 @@ fn loads(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .get_item("JSONDecodeError") .unwrap(); let exc = vm.new_exception(json_decode_error, format!("{}", err)); - vm.ctx - .set_item(&exc, "lineno", vm.ctx.new_int(err.line().into())); - vm.ctx - .set_item(&exc, "colno", vm.ctx.new_int(err.column().into())); + vm.ctx.set_item(&exc, "lineno", vm.ctx.new_int(err.line())); + vm.ctx.set_item(&exc, "colno", vm.ctx.new_int(err.column())); exc }) } diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 4c54f5847..a9ba6fe70 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -4,7 +4,6 @@ use std::fs::OpenOptions; use std::io::ErrorKind; //3rd party imports -use num_bigint::ToBigInt; use num_traits::cast::ToPrimitive; //custom imports @@ -91,11 +90,7 @@ pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { _ => vm.new_value_error("Unhandled file IO error".to_string()), })?; - Ok(vm.ctx.new_int( - raw_file_number(handle) - .to_bigint() - .expect("Invalid file descriptor"), - )) + Ok(vm.ctx.new_int(raw_file_number(handle))) } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { @@ -103,11 +98,11 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "open", ctx.new_rustfunc(os_open)); ctx.set_attr(&py_mod, "close", ctx.new_rustfunc(os_close)); - ctx.set_attr(&py_mod, "O_RDONLY", ctx.new_int(0.to_bigint().unwrap())); - ctx.set_attr(&py_mod, "O_WRONLY", ctx.new_int(1.to_bigint().unwrap())); - ctx.set_attr(&py_mod, "O_RDWR", ctx.new_int(2.to_bigint().unwrap())); - ctx.set_attr(&py_mod, "O_NONBLOCK", ctx.new_int(4.to_bigint().unwrap())); - ctx.set_attr(&py_mod, "O_APPEND", ctx.new_int(8.to_bigint().unwrap())); - ctx.set_attr(&py_mod, "O_CREAT", ctx.new_int(512.to_bigint().unwrap())); + ctx.set_attr(&py_mod, "O_RDONLY", ctx.new_int(0)); + ctx.set_attr(&py_mod, "O_WRONLY", ctx.new_int(1)); + ctx.set_attr(&py_mod, "O_RDWR", ctx.new_int(2)); + ctx.set_attr(&py_mod, "O_NONBLOCK", ctx.new_int(4)); + ctx.set_attr(&py_mod, "O_APPEND", ctx.new_int(8)); + ctx.set_attr(&py_mod, "O_CREAT", ctx.new_int(512)); py_mod } diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index 7babc6000..166007197 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -13,7 +13,7 @@ use self::byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use super::super::obj::{objbool, objbytes, objfloat, objint, objstr, objtype}; use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use super::super::VirtualMachine; -use num_bigint::{BigInt, ToBigInt}; +use num_bigint::BigInt; use num_traits::ToPrimitive; use std::io::{Cursor, Read, Write}; @@ -224,14 +224,14 @@ fn struct_pack(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn unpack_i8(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_i8() { Err(err) => panic!("Error in reading {:?}", err), - Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())), + Ok(v) => Ok(vm.ctx.new_int(v)), } } fn unpack_u8(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_u8() { Err(err) => panic!("Error in reading {:?}", err), - Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())), + Ok(v) => Ok(vm.ctx.new_int(v)), } } @@ -245,42 +245,42 @@ fn unpack_bool(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { fn unpack_i16(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_i16::() { Err(err) => panic!("Error in reading {:?}", err), - Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())), + Ok(v) => Ok(vm.ctx.new_int(v)), } } fn unpack_u16(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_u16::() { Err(err) => panic!("Error in reading {:?}", err), - Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())), + Ok(v) => Ok(vm.ctx.new_int(v)), } } fn unpack_i32(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_i32::() { Err(err) => panic!("Error in reading {:?}", err), - Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())), + Ok(v) => Ok(vm.ctx.new_int(v)), } } fn unpack_u32(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_u32::() { Err(err) => panic!("Error in reading {:?}", err), - Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())), + Ok(v) => Ok(vm.ctx.new_int(v)), } } fn unpack_i64(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_i64::() { Err(err) => panic!("Error in reading {:?}", err), - Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())), + Ok(v) => Ok(vm.ctx.new_int(v)), } } fn unpack_u64(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult { match rdr.read_u64::() { Err(err) => panic!("Error in reading {:?}", err), - Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())), + Ok(v) => Ok(vm.ctx.new_int(v)), } } diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index e747fda5a..43b01d0d9 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -1,4 +1,3 @@ -use num_bigint::ToBigInt; use obj::objtype; use pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use std::rc::Rc; @@ -26,14 +25,14 @@ fn getframe(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { fn sys_getrefcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(object, None)]); let size = Rc::strong_count(&object); - Ok(vm.ctx.new_int(size.to_bigint().unwrap())) + Ok(vm.ctx.new_int(size)) } fn sys_getsizeof(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(object, None)]); // TODO: implement default optional argument. let size = mem::size_of_val(&object.borrow()); - Ok(vm.ctx.new_int(size.to_bigint().unwrap())) + Ok(vm.ctx.new_int(size)) } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { @@ -62,11 +61,7 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_item(&sys_mod, "argv", argv(ctx)); ctx.set_item(&sys_mod, "getrefcount", ctx.new_rustfunc(sys_getrefcount)); ctx.set_item(&sys_mod, "getsizeof", ctx.new_rustfunc(sys_getsizeof)); - ctx.set_item( - &sys_mod, - "maxsize", - ctx.new_int(std::usize::MAX.to_bigint().unwrap()), - ); + ctx.set_item(&sys_mod, "maxsize", ctx.new_int(std::usize::MAX)); ctx.set_item(&sys_mod, "path", path); ctx.set_item(&sys_mod, "ps1", ctx.new_str(">>>>> ".to_string())); ctx.set_item(&sys_mod, "ps2", ctx.new_str("..... ".to_string())); diff --git a/vm/src/vm.rs b/vm/src/vm.rs index b1570e386..3fbec42cc 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -610,23 +610,22 @@ impl VirtualMachine { mod tests { use super::super::obj::{objint, objstr}; use super::VirtualMachine; - use num_bigint::ToBigInt; #[test] fn test_add_py_integers() { let mut vm = VirtualMachine::new(); - let a = vm.ctx.new_int(33_i32.to_bigint().unwrap()); - let b = vm.ctx.new_int(12_i32.to_bigint().unwrap()); + let a = vm.ctx.new_int(33_i32); + let b = vm.ctx.new_int(12_i32); let res = vm._add(a, b).unwrap(); let value = objint::get_value(&res); - assert_eq!(value, 45_i32.to_bigint().unwrap()); + assert_eq!(value, 45_i32); } #[test] fn test_multiply_str() { let mut vm = VirtualMachine::new(); let a = vm.ctx.new_str(String::from("Hello ")); - let b = vm.ctx.new_int(4_i32.to_bigint().unwrap()); + let b = vm.ctx.new_int(4_i32); let res = vm._mul(a, b).unwrap(); let value = objstr::get_value(&res); assert_eq!(value, String::from("Hello Hello Hello Hello ")) From 02d99758fed6031cb7e83845214b4caaf3871657 Mon Sep 17 00:00:00 2001 From: lausek Date: Wed, 6 Feb 2019 18:38:37 +0100 Subject: [PATCH 107/153] round() for int --- tests/snippets/numbers.py | 10 ++++++++++ vm/src/builtins.rs | 20 +++++++++++++++++++- vm/src/obj/objint.rs | 1 + 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/tests/snippets/numbers.py b/tests/snippets/numbers.py index c13470eda..da4ff5040 100644 --- a/tests/snippets/numbers.py +++ b/tests/snippets/numbers.py @@ -15,6 +15,16 @@ assert int(2).__ceil__() == 2 assert int(2).__floor__() == 2 assert int(2).__round__() == 2 assert int(2).__round__(3) == 2 +assert int(-2).__index__() == -2 +assert int(-2).__trunc__() == -2 +assert int(-2).__ceil__() == -2 +assert int(-2).__floor__() == -2 +assert int(-2).__round__() == -2 +assert int(-2).__round__(3) == -2 + +assert round(10) == 10 +assert round(10, 2) == 10 +assert round(10, -1) == 10 assert int(2).__bool__() == True assert int(0.5).__bool__() == False diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index a08e70fb7..31f377b28 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -664,7 +664,24 @@ fn builtin_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm.to_repr(obj) } // builtin_reversed -// builtin_round + +fn builtin_round(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(number, Some(vm.ctx.object()))], + optional = [(ndigits, None)] + ); + if let Some(ndigits) = ndigits { + let ndigits = vm.call_method(ndigits, "__int__", vec![])?; + let rounded = vm.call_method(number, "__round__", vec![ndigits])?; + Ok(rounded) + } else { + // without a parameter, the result type is coerced to int + let rounded = &vm.call_method(number, "__round__", vec![])?; + Ok(vm.ctx.new_int(objint::get_value(rounded))) + } +} fn builtin_setattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( @@ -777,6 +794,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "property", ctx.property_type()); ctx.set_attr(&py_mod, "range", ctx.range_type()); ctx.set_attr(&py_mod, "repr", ctx.new_rustfunc(builtin_repr)); + ctx.set_attr(&py_mod, "round", ctx.new_rustfunc(builtin_round)); ctx.set_attr(&py_mod, "set", ctx.set_type()); ctx.set_attr(&py_mod, "setattr", ctx.new_rustfunc(builtin_setattr)); ctx.set_attr(&py_mod, "staticmethod", ctx.staticmethod_type()); diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 8598e68e8..75e3b3dec 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -532,6 +532,7 @@ pub fn init(context: &PyContext) { context.set_attr(&int_type, "__floor__", context.new_rustfunc(int_pass_value)); context.set_attr(&int_type, "__index__", context.new_rustfunc(int_pass_value)); context.set_attr(&int_type, "__trunc__", context.new_rustfunc(int_pass_value)); + context.set_attr(&int_type, "__int__", context.new_rustfunc(int_pass_value)); context.set_attr( &int_type, "__floordiv__", From df89c439d98d95e1ee644ce5e0f7119d17f2d93c Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Wed, 6 Feb 2019 17:55:01 +0000 Subject: [PATCH 108/153] Fix accidentally broken test. --- vm/src/vm.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 3fbec42cc..d246e8c53 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -610,6 +610,7 @@ impl VirtualMachine { mod tests { use super::super::obj::{objint, objstr}; use super::VirtualMachine; + use num_bigint::ToBigInt; #[test] fn test_add_py_integers() { @@ -618,7 +619,7 @@ mod tests { let b = vm.ctx.new_int(12_i32); let res = vm._add(a, b).unwrap(); let value = objint::get_value(&res); - assert_eq!(value, 45_i32); + assert_eq!(value, 45_i32.to_bigint().unwrap()); } #[test] From 694cbda03da2e0cc6651040e337af6a8e18c0a46 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Thu, 7 Feb 2019 00:57:29 +0300 Subject: [PATCH 109/153] (frozen)set type: Added __doc__ --- vm/src/obj/objset.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index b22213dd0..3ef68c142 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -152,6 +152,11 @@ fn frozenset_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let set_type = &context.set_type; + + let set_doc = "set() -> new empty set object\n\ + set(iterable) -> new set object\n\n\ + Build an unordered collection of unique elements."; + context.set_attr( &set_type, "__contains__", @@ -160,15 +165,26 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__len__", context.new_rustfunc(set_len)); context.set_attr(&set_type, "__new__", context.new_rustfunc(set_new)); context.set_attr(&set_type, "__repr__", context.new_rustfunc(set_repr)); + context.set_attr(&set_type, "__doc__", context.new_str(set_doc.to_string())); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); let frozenset_type = &context.frozenset_type; + + let frozenset_doc = "frozenset() -> empty frozenset object\n\ + frozenset(iterable) -> frozenset object\n\n\ + Build an immutable unordered collection of unique elements."; + context.set_attr( &frozenset_type, "__contains__", context.new_rustfunc(set_contains), ); context.set_attr(&frozenset_type, "__len__", context.new_rustfunc(set_len)); + context.set_attr( + &frozenset_type, + "__doc__", + context.new_str(frozenset_doc.to_string()), + ); context.set_attr( &frozenset_type, "__repr__", From 1479d6bc77da3e26492214b6df8d00a04cfe0b29 Mon Sep 17 00:00:00 2001 From: veera venky Date: Thu, 7 Feb 2019 14:56:14 +0530 Subject: [PATCH 110/153] Added index to tuple --- tests/snippets/tuple.py | 4 ++++ vm/src/obj/objtuple.rs | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/tests/snippets/tuple.py b/tests/snippets/tuple.py index 56a61f827..eb5102fa3 100644 --- a/tests/snippets/tuple.py +++ b/tests/snippets/tuple.py @@ -15,3 +15,7 @@ assert x * -1 == () # integers less than zero treated as 0 assert y < x, "tuple __lt__ failed" assert x > y, "tuple __gt__ failed" + + +b = (1,2,3) +assert b.index(2) == 1 diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 54b06f278..fc95e819d 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -253,6 +253,21 @@ fn tuple_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { get_item(vm, tuple, &get_elements(&tuple), needle.clone()) } +pub fn tuple_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(tuple, Some(vm.ctx.tuple_type())), (needle, None)] + ); + for (index, element) in get_elements(tuple).iter().enumerate() { + let py_equal = vm.call_method(needle, "__eq__", vec![element.clone()])?; + if objbool::get_value(&py_equal) { + return Ok(vm.context().new_int(index)); + } + } + Err(vm.new_value_error("tuple.index(x): x not in tuple".to_string())) +} + pub fn tuple_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, @@ -298,4 +313,5 @@ pub fn init(context: &PyContext) { context.set_attr(&tuple_type, "__le__", context.new_rustfunc(tuple_le)); context.set_attr(&tuple_type, "__gt__", context.new_rustfunc(tuple_gt)); context.set_attr(&tuple_type, "__ge__", context.new_rustfunc(tuple_ge)); + context.set_attr(&tuple_type, "index", context.new_rustfunc(tuple_index)); } From 6c181263827802b8bb989bfdcfc8dede3ac5d7f5 Mon Sep 17 00:00:00 2001 From: Tom Forbes Date: Mon, 4 Feb 2019 10:32:17 +0000 Subject: [PATCH 111/153] Improve test coverage of os.open --- tests/snippets/builtin_open.py | 8 ++++++++ tests/snippets/os_open.py | 6 ++++++ vm/src/builtins.rs | 5 +++++ 3 files changed, 19 insertions(+) create mode 100644 tests/snippets/builtin_open.py diff --git a/tests/snippets/builtin_open.py b/tests/snippets/builtin_open.py new file mode 100644 index 000000000..805c975c8 --- /dev/null +++ b/tests/snippets/builtin_open.py @@ -0,0 +1,8 @@ +fd = open('README.md') +assert 'RustPython' in fd.read() + +try: + open('DoesNotExist') + assert False +except FileNotFoundError: + pass diff --git a/tests/snippets/os_open.py b/tests/snippets/os_open.py index ca5a61d5a..8138f820c 100644 --- a/tests/snippets/os_open.py +++ b/tests/snippets/os_open.py @@ -2,3 +2,9 @@ import os assert os.open('README.md', 0) > 0 + +try: + os.open('DOES_NOT_EXIST', 0) + assert False +except FileNotFoundError: + pass diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index b1e5904eb..068394afe 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -767,6 +767,11 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "ValueError", ctx.exceptions.value_error.clone()); ctx.set_attr(&py_mod, "IndexError", ctx.exceptions.index_error.clone()); ctx.set_attr(&py_mod, "ImportError", ctx.exceptions.import_error.clone()); + ctx.set_attr( + &py_mod, + "FileNotFoundError", + ctx.exceptions.file_not_found_error.clone(), + ); ctx.set_attr( &py_mod, "StopIteration", From b85cf91a2317de4469a5036ec4887003635c6d8a Mon Sep 17 00:00:00 2001 From: veera venky Date: Thu, 7 Feb 2019 23:47:47 +0530 Subject: [PATCH 112/153] Added __doc__ atrribute for tuple --- tests/snippets/tuple.py | 2 ++ vm/src/obj/objtuple.rs | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/tests/snippets/tuple.py b/tests/snippets/tuple.py index eb5102fa3..38617d036 100644 --- a/tests/snippets/tuple.py +++ b/tests/snippets/tuple.py @@ -19,3 +19,5 @@ assert x > y, "tuple __gt__ failed" b = (1,2,3) assert b.index(2) == 1 + +assert b.__doc__ == "tuple() -> empty tuple\ntuple(iterable) -> tuple initialized from iterable's items\n\nIf the argument is a tuple, the return value is the same object." diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index e5485fd4d..2d99db2d4 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -290,6 +290,10 @@ pub fn tuple_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let tuple_type = &context.tuple_type; + let tuple_doc = "tuple() -> empty tuple +tuple(iterable) -> tuple initialized from iterable's items + +If the argument is a tuple, the return value is the same object."; context.set_attr(&tuple_type, "__add__", context.new_rustfunc(tuple_add)); context.set_attr(&tuple_type, "__eq__", context.new_rustfunc(tuple_eq)); context.set_attr( @@ -313,5 +317,10 @@ pub fn init(context: &PyContext) { context.set_attr(&tuple_type, "__le__", context.new_rustfunc(tuple_le)); context.set_attr(&tuple_type, "__gt__", context.new_rustfunc(tuple_gt)); context.set_attr(&tuple_type, "__ge__", context.new_rustfunc(tuple_ge)); + context.set_attr( + &tuple_type, + "__doc__", + context.new_str(tuple_doc.to_string()), + ); context.set_attr(&tuple_type, "index", context.new_rustfunc(tuple_index)); } From 54bcb085181ef15e4f7591be5c2adbea37b0e474 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Wed, 6 Feb 2019 15:36:55 -0800 Subject: [PATCH 113/153] Add range.{__bool__, __contains__} - Also fix range.index for negative steps --- tests/snippets/builtin_range.py | 23 ++++++++++++ vm/src/obj/objrange.rs | 65 ++++++++++++++++++++++++++++----- 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/tests/snippets/builtin_range.py b/tests/snippets/builtin_range.py index 5e58ae069..c822ce7e7 100644 --- a/tests/snippets/builtin_range.py +++ b/tests/snippets/builtin_range.py @@ -22,6 +22,7 @@ assert range(2**63+1)[2**63] == 9223372036854775808 assert range(10).index(6) == 6 assert range(4, 10).index(6) == 2 assert range(4, 10, 2).index(6) == 1 +assert range(10, 4, -2).index(8) == 1 # index raises value error on out of bounds assert_raises(lambda _: range(10).index(-1), ValueError) @@ -29,3 +30,25 @@ assert_raises(lambda _: range(10).index(10), ValueError) # index raises value error if out of step assert_raises(lambda _: range(4, 10, 2).index(5), ValueError) + +# index raises value error if needle is not an int +assert_raises(lambda _: range(10).index('foo'), ValueError) + +# __bool__ +assert range(1).__bool__() +assert range(1, 2).__bool__() + +assert not range(0).__bool__() +assert not range(1, 1).__bool__() + +# __contains__ +assert range(10).__contains__(6) +assert range(4, 10).__contains__(6) +assert range(4, 10, 2).__contains__(6) +assert range(10, 4, -2).__contains__(10) +assert range(10, 4, -2).__contains__(8) + +assert not range(10).__contains__(-1) +assert not range(10, 4, -2).__contains__(9) +assert not range(10, 4, -2).__contains__(4) +assert not range(10).__contains__('foo') diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 605dabf84..cd897ff34 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -4,7 +4,7 @@ use super::super::pyobject::{ use super::super::vm::VirtualMachine; use super::objint; use super::objtype; -use num_bigint::BigInt; +use num_bigint::{BigInt, Sign}; use num_integer::Integer; use num_traits::{One, Signed, ToPrimitive, Zero}; @@ -27,16 +27,29 @@ impl RangeType { } #[inline] - pub fn index_of(&self, value: &BigInt) -> Option { - if value < &self.start || value >= &self.end { - return None; + fn offset(&self, value: &BigInt) -> Option { + match self.step.sign() { + Sign::Plus if value >= &self.start && value < &self.end => Some(value - &self.start), + Sign::Minus if value <= &self.start && value > &self.end => Some(&self.start - value), + _ => None, } + } - let offset = value - &self.start; - if offset.is_multiple_of(&self.step) { - Some(offset / &self.step) - } else { - None + #[inline] + pub fn contains(&self, value: &BigInt) -> bool { + match self.offset(value) { + Some(ref offset) => offset.is_multiple_of(&self.step), + None => false, + } + } + + #[inline] + pub fn index_of(&self, value: &BigInt) -> Option { + match self.offset(value) { + Some(ref offset) if offset.is_multiple_of(&self.step) => { + Some((offset / &self.step).abs()) + } + Some(_) | None => None, } } @@ -75,6 +88,12 @@ pub fn init(context: &PyContext) { "__getitem__", context.new_rustfunc(range_getitem), ); + context.set_attr(&range_type, "__bool__", context.new_rustfunc(range_bool)); + context.set_attr( + &range_type, + "__contains__", + context.new_rustfunc(range_contains), + ); context.set_attr(&range_type, "index", context.new_rustfunc(range_index)); } @@ -205,6 +224,34 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } +fn range_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); + + let len = match zelf.borrow().payload { + PyObjectPayload::Range { ref range } => range.len(), + _ => unreachable!(), + }; + + Ok(vm.ctx.new_bool(len > 0)) +} + +fn range_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.range_type())), (needle, None)] + ); + + if let PyObjectPayload::Range { ref range } = zelf.borrow().payload { + Ok(vm.ctx.new_bool(match needle.borrow().payload { + PyObjectPayload::Integer { ref value } => range.contains(value), + _ => false, + })) + } else { + unreachable!() + } +} + fn range_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, From 5896f049eff57f6812b60ca5b48a94ef1ba66500 Mon Sep 17 00:00:00 2001 From: ben Date: Fri, 8 Feb 2019 20:57:16 +1300 Subject: [PATCH 114/153] Add enumerate and zip types and made them lazy. --- tests/snippets/builtin_enumerate.py | 23 +++++++++ tests/snippets/builtin_zip.py | 24 +++++++++ tests/snippets/builtins.py | 4 -- vm/src/builtins.rs | 56 ++------------------- vm/src/obj/mod.rs | 2 + vm/src/obj/objenumerate.rs | 69 ++++++++++++++++++++++++++ vm/src/obj/objfilter.rs | 22 +-------- vm/src/obj/objiter.rs | 77 ++++++++++++++++------------- vm/src/obj/objmap.rs | 24 +-------- vm/src/obj/objzip.rs | 46 +++++++++++++++++ vm/src/pyobject.rs | 29 +++++++++++ 11 files changed, 242 insertions(+), 134 deletions(-) create mode 100644 tests/snippets/builtin_enumerate.py create mode 100644 tests/snippets/builtin_zip.py create mode 100644 vm/src/obj/objenumerate.rs create mode 100644 vm/src/obj/objzip.rs diff --git a/tests/snippets/builtin_enumerate.py b/tests/snippets/builtin_enumerate.py new file mode 100644 index 000000000..75f6f7412 --- /dev/null +++ b/tests/snippets/builtin_enumerate.py @@ -0,0 +1,23 @@ +assert list(enumerate(['a', 'b', 'c'])) == [(0, 'a'), (1, 'b'), (2, 'c')] + +assert type(enumerate([])) == enumerate + +assert list(enumerate(['a', 'b', 'c'], -100)) == [(-100, 'a'), (-99, 'b'), (-98, 'c')] +assert list(enumerate(['a', 'b', 'c'], 2**200)) == [(2**200, 'a'), (2**200 + 1, 'b'), (2**200 + 2, 'c')] + + +# test infinite iterator +class Counter(object): + counter = 0 + + def __next__(self): + self.counter += 1 + return self.counter + + def __iter__(self): + return self + + +it = enumerate(Counter()) +assert next(it) == (0, 1) +assert next(it) == (1, 2) diff --git a/tests/snippets/builtin_zip.py b/tests/snippets/builtin_zip.py new file mode 100644 index 000000000..3665c7702 --- /dev/null +++ b/tests/snippets/builtin_zip.py @@ -0,0 +1,24 @@ +assert list(zip(['a', 'b', 'c'], range(3), [9, 8, 7, 99])) == [('a', 0, 9), ('b', 1, 8), ('c', 2, 7)] + +assert list(zip(['a', 'b', 'c'])) == [('a',), ('b',), ('c',)] +assert list(zip()) == [] + +assert list(zip(*zip(['a', 'b', 'c'], range(1, 4)))) == [('a', 'b', 'c'), (1, 2, 3)] + + +# test infinite iterator +class Counter(object): + def __init__(self, counter=0): + self.counter = counter + + def __next__(self): + self.counter += 1 + return self.counter + + def __iter__(self): + return self + + +it = zip(Counter(), Counter(3)) +assert next(it) == (1, 4) +assert next(it) == (2, 5) diff --git a/tests/snippets/builtins.py b/tests/snippets/builtins.py index 539b49ef7..76b28a7b9 100644 --- a/tests/snippets/builtins.py +++ b/tests/snippets/builtins.py @@ -5,12 +5,8 @@ assert callable(type) # TODO: # assert callable(callable) -assert list(enumerate(['a', 'b', 'c'])) == [(0, 'a'), (1, 'b'), (2, 'c')] - assert type(frozenset) is type -assert list(zip(['a', 'b', 'c'], range(3), [9, 8, 7, 99])) == [('a', 0, 9), ('b', 1, 8), ('c', 2, 7)] - assert 3 == eval('1+2') code = compile('5+3', 'x.py', 'eval') diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 1db5c7e77..c1bc982d0 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -21,8 +21,7 @@ use super::pyobject::{ use super::stdlib::io::io_open; use super::vm::VirtualMachine; -use num_bigint::ToBigInt; -use num_traits::{Signed, ToPrimitive, Zero}; +use num_traits::{Signed, ToPrimitive}; fn get_locals(vm: &mut VirtualMachine) -> PyObjectRef { let d = vm.new_dict(); @@ -180,29 +179,6 @@ fn builtin_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn builtin_enumerate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(iterable, None)], - optional = [(start, None)] - ); - let items = vm.extract_elements(iterable)?; - let start = if let Some(start) = start { - objint::get_value(start) - } else { - Zero::zero() - }; - let mut new_items = vec![]; - for (i, item) in items.into_iter().enumerate() { - let element = vm - .ctx - .new_tuple(vec![vm.ctx.new_int(i.to_bigint().unwrap() + &start), item]); - new_items.push(element); - } - Ok(vm.ctx.new_list(new_items)) -} - /// Implements `eval`. /// See also: https://docs.python.org/3/library/functions.html#eval fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -641,32 +617,6 @@ fn builtin_sum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } // builtin_vars - -fn builtin_zip(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - no_kwargs!(vm, args); - - // TODO: process one element at a time from iterators. - let mut iterables = vec![]; - for iterable in args.args.iter() { - let iterable = vm.extract_elements(iterable)?; - iterables.push(iterable); - } - - let minsize: usize = iterables.iter().map(|i| i.len()).min().unwrap_or(0); - - let mut new_items = vec![]; - for i in 0..minsize { - let items = iterables - .iter() - .map(|iterable| iterable[i].clone()) - .collect(); - let element = vm.ctx.new_tuple(items); - new_items.push(element); - } - - Ok(vm.ctx.new_list(new_items)) -} - // builtin___import__ pub fn make_module(ctx: &PyContext) -> PyObjectRef { @@ -692,7 +642,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "dict", ctx.dict_type()); ctx.set_attr(&py_mod, "divmod", ctx.new_rustfunc(builtin_divmod)); ctx.set_attr(&py_mod, "dir", ctx.new_rustfunc(builtin_dir)); - ctx.set_attr(&py_mod, "enumerate", ctx.new_rustfunc(builtin_enumerate)); + ctx.set_attr(&py_mod, "enumerate", ctx.enumerate_type()); ctx.set_attr(&py_mod, "eval", ctx.new_rustfunc(builtin_eval)); ctx.set_attr(&py_mod, "exec", ctx.new_rustfunc(builtin_exec)); ctx.set_attr(&py_mod, "float", ctx.float_type()); @@ -733,7 +683,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "super", ctx.super_type()); ctx.set_attr(&py_mod, "tuple", ctx.tuple_type()); ctx.set_attr(&py_mod, "type", ctx.type_type()); - ctx.set_attr(&py_mod, "zip", ctx.new_rustfunc(builtin_zip)); + ctx.set_attr(&py_mod, "zip", ctx.zip_type()); // Exceptions: ctx.set_attr( diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index a60ce79ff..8b93ee174 100644 --- a/vm/src/obj/mod.rs +++ b/vm/src/obj/mod.rs @@ -6,6 +6,7 @@ pub mod objbytes; pub mod objcode; pub mod objcomplex; pub mod objdict; +pub mod objenumerate; pub mod objfilter; pub mod objfloat; pub mod objframe; @@ -25,3 +26,4 @@ pub mod objstr; pub mod objsuper; pub mod objtuple; pub mod objtype; +pub mod objzip; diff --git a/vm/src/obj/objenumerate.rs b/vm/src/obj/objenumerate.rs new file mode 100644 index 000000000..582f89852 --- /dev/null +++ b/vm/src/obj/objenumerate.rs @@ -0,0 +1,69 @@ +use super::super::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, +}; +use super::super::vm::VirtualMachine; +use super::objint; +use super::objiter; +use super::objtype; // Required for arg_check! to use isinstance +use num_bigint::BigInt; +use num_traits::Zero; +use std::ops::AddAssign; + +fn enumerate_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(cls, Some(vm.ctx.type_type())), (iterable, None)], + optional = [(start, Some(vm.ctx.int_type()))] + ); + let counter = if let Some(x) = start { + objint::get_value(x) + } else { + BigInt::zero() + }; + let iterator = objiter::get_iter(vm, iterable)?; + Ok(PyObject::new( + PyObjectPayload::EnumerateIterator { counter, iterator }, + cls.clone(), + )) +} + +fn enumerate_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(enumerate, Some(vm.ctx.enumerate_type()))] + ); + + if let PyObjectPayload::EnumerateIterator { + ref mut counter, + ref mut iterator, + } = enumerate.borrow_mut().payload + { + let next_obj = objiter::call_next(vm, iterator)?; + let result = vm + .ctx + .new_tuple(vec![vm.ctx.new_int(counter.clone()), next_obj]); + + AddAssign::add_assign(counter, 1); + + Ok(result) + } else { + panic!("enumerate doesn't have correct payload"); + } +} + +pub fn init(context: &PyContext) { + let enumerate_type = &context.enumerate_type; + objiter::iter_type_init(context, enumerate_type); + context.set_attr( + enumerate_type, + "__new__", + context.new_rustfunc(enumerate_new), + ); + context.set_attr( + enumerate_type, + "__next__", + context.new_rustfunc(enumerate_next), + ); +} diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index 009d2ad2e..4eda041ec 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -23,21 +23,6 @@ pub fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } -fn filter_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(filter, Some(vm.ctx.filter_type()))]); - // Return self: - Ok(filter.clone()) -} - -fn filter_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(filter, Some(vm.ctx.filter_type())), (needle, None)] - ); - objiter::contains(vm, filter, needle) -} - fn filter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(filter, Some(vm.ctx.filter_type()))]); @@ -72,12 +57,7 @@ fn filter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let filter_type = &context.filter_type; - context.set_attr( - &filter_type, - "__contains__", - context.new_rustfunc(filter_contains), - ); - context.set_attr(&filter_type, "__iter__", context.new_rustfunc(filter_iter)); + objiter::iter_type_init(context, filter_type); context.set_attr(&filter_type, "__new__", context.new_rustfunc(filter_new)); context.set_attr(&filter_type, "__next__", context.new_rustfunc(filter_next)); } diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 7d4507212..55b206511 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -65,7 +65,17 @@ pub fn get_all( Ok(elements) } -pub fn contains(vm: &mut VirtualMachine, iter: &PyObjectRef, needle: &PyObjectRef) -> PyResult { +pub fn new_stop_iteration(vm: &mut VirtualMachine) -> PyObjectRef { + let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); + vm.new_exception(stop_iteration_type, "End of iterator".to_string()) +} + +fn contains(vm: &mut VirtualMachine, args: PyFuncArgs, iter_type: PyObjectRef) -> PyResult { + arg_check!( + vm, + args, + required = [(iter, Some(iter_type)), (needle, None)] + ); loop { if let Some(element) = get_next_object(vm, iter)? { let equal = vm.call_method(needle, "__eq__", vec![element.clone()])?; @@ -80,6 +90,34 @@ pub fn contains(vm: &mut VirtualMachine, iter: &PyObjectRef, needle: &PyObjectRe } } +/// Common setup for iter types, adds __iter__ and __contains__ methods +pub fn iter_type_init(context: &PyContext, iter_type: &PyObjectRef) { + let contains_func = { + let cloned_iter_type = iter_type.clone(); + move |vm: &mut VirtualMachine, args: PyFuncArgs| { + contains(vm, args, cloned_iter_type.clone()) + } + }; + context.set_attr( + &iter_type, + "__contains__", + context.new_rustfunc(contains_func), + ); + let iter_func = { + let cloned_iter_type = iter_type.clone(); + move |vm: &mut VirtualMachine, args: PyFuncArgs| { + arg_check!( + vm, + args, + required = [(iter, Some(cloned_iter_type.clone()))] + ); + // Return self: + Ok(iter.clone()) + } + }; + context.set_attr(&iter_type, "__iter__", context.new_rustfunc(iter_func)); +} + // Sequence iterator: fn iter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iter_target, None)]); @@ -87,21 +125,6 @@ fn iter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { get_iter(vm, iter_target) } -fn iter_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(iter, Some(vm.ctx.iter_type()))]); - // Return self: - Ok(iter.clone()) -} - -fn iter_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(iter, Some(vm.ctx.iter_type())), (needle, None)] - ); - contains(vm, iter, needle) -} - fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(iter, Some(vm.ctx.iter_type()))]); @@ -118,10 +141,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { *position += 1; Ok(obj_ref) } else { - let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); - let stop_iteration = - vm.new_exception(stop_iteration_type, "End of iterator".to_string()); - Err(stop_iteration) + Err(new_stop_iteration(vm)) } } @@ -130,10 +150,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { *position += 1; Ok(vm.ctx.new_int(int)) } else { - let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); - let stop_iteration = - vm.new_exception(stop_iteration_type, "End of iterator".to_string()); - Err(stop_iteration) + Err(new_stop_iteration(vm)) } } @@ -143,10 +160,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { *position += 1; Ok(obj_ref) } else { - let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone(); - let stop_iteration = - vm.new_exception(stop_iteration_type, "End of iterator".to_string()); - Err(stop_iteration) + Err(new_stop_iteration(vm)) } } @@ -161,12 +175,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let iter_type = &context.iter_type; - context.set_attr( - &iter_type, - "__contains__", - context.new_rustfunc(iter_contains), - ); - context.set_attr(&iter_type, "__iter__", context.new_rustfunc(iter_iter)); + iter_type_init(context, iter_type); context.set_attr(&iter_type, "__new__", context.new_rustfunc(iter_new)); context.set_attr(&iter_type, "__next__", context.new_rustfunc(iter_next)); } diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index ed6130643..722eba01a 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -5,7 +5,7 @@ use super::super::vm::VirtualMachine; use super::objiter; use super::objtype; // Required for arg_check! to use isinstance -pub fn map_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn map_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { no_kwargs!(vm, args); let cls = &args.args[0]; if args.args.len() < 3 { @@ -27,21 +27,6 @@ pub fn map_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } -fn map_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(map, Some(vm.ctx.map_type()))]); - // Return self: - Ok(map.clone()) -} - -fn map_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(map, Some(vm.ctx.map_type())), (needle, None)] - ); - objiter::contains(vm, map, needle) -} - fn map_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(map, Some(vm.ctx.map_type()))]); @@ -70,12 +55,7 @@ fn map_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let map_type = &context.map_type; - context.set_attr( - &map_type, - "__contains__", - context.new_rustfunc(map_contains), - ); - context.set_attr(&map_type, "__iter__", context.new_rustfunc(map_iter)); + objiter::iter_type_init(context, map_type); context.set_attr(&map_type, "__new__", context.new_rustfunc(map_new)); context.set_attr(&map_type, "__next__", context.new_rustfunc(map_next)); } diff --git a/vm/src/obj/objzip.rs b/vm/src/obj/objzip.rs new file mode 100644 index 000000000..471df9c2b --- /dev/null +++ b/vm/src/obj/objzip.rs @@ -0,0 +1,46 @@ +use super::super::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, +}; +use super::super::vm::VirtualMachine; +use super::objiter; +use super::objtype; // Required for arg_check! to use isinstance + +fn zip_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + no_kwargs!(vm, args); + let cls = &args.args[0]; + let iterables = &args.args[1..]; + let iterators = iterables + .into_iter() + .map(|iterable| objiter::get_iter(vm, iterable)) + .collect::, _>>()?; + Ok(PyObject::new( + PyObjectPayload::ZipIterator { iterators }, + cls.clone(), + )) +} + +fn zip_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zip, Some(vm.ctx.zip_type()))]); + + if let PyObjectPayload::ZipIterator { ref mut iterators } = zip.borrow_mut().payload { + if iterators.is_empty() { + Err(objiter::new_stop_iteration(vm)) + } else { + let next_objs = iterators + .iter() + .map(|iterator| objiter::call_next(vm, iterator)) + .collect::, _>>()?; + + Ok(vm.ctx.new_tuple(next_objs)) + } + } else { + panic!("zip doesn't have correct payload"); + } +} + +pub fn init(context: &PyContext) { + let zip_type = &context.zip_type; + objiter::iter_type_init(context, zip_type); + context.set_attr(zip_type, "__new__", context.new_rustfunc(zip_new)); + context.set_attr(zip_type, "__next__", context.new_rustfunc(zip_next)); +} diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 02181e4fe..0cd19d339 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -7,6 +7,7 @@ use super::obj::objbytes; use super::obj::objcode; use super::obj::objcomplex; use super::obj::objdict; +use super::obj::objenumerate; use super::obj::objfilter; use super::obj::objfloat; use super::obj::objframe; @@ -25,6 +26,7 @@ use super::obj::objstr; use super::obj::objsuper; use super::obj::objtuple; use super::obj::objtype; +use super::obj::objzip; use super::vm::VirtualMachine; use num_bigint::BigInt; use num_bigint::ToBigInt; @@ -109,6 +111,7 @@ pub struct PyContext { pub classmethod_type: PyObjectRef, pub code_type: PyObjectRef, pub dict_type: PyObjectRef, + pub enumerate_type: PyObjectRef, pub filter_type: PyObjectRef, pub float_type: PyObjectRef, pub frame_type: PyObjectRef, @@ -130,6 +133,7 @@ pub struct PyContext { pub str_type: PyObjectRef, pub range_type: PyObjectRef, pub type_type: PyObjectRef, + pub zip_type: PyObjectRef, pub function_type: PyObjectRef, pub property_type: PyObjectRef, pub module_type: PyObjectRef, @@ -205,8 +209,10 @@ impl PyContext { let bytearray_type = create_type("bytearray", &type_type, &object_type, &dict_type); let tuple_type = create_type("tuple", &type_type, &object_type, &dict_type); let iter_type = create_type("iter", &type_type, &object_type, &dict_type); + let enumerate_type = create_type("enumerate", &type_type, &object_type, &dict_type); let filter_type = create_type("filter", &type_type, &object_type, &dict_type); let map_type = create_type("map", &type_type, &object_type, &dict_type); + let zip_type = create_type("zip", &type_type, &object_type, &dict_type); let bool_type = create_type("bool", &type_type, &int_type, &dict_type); let memoryview_type = create_type("memoryview", &type_type, &object_type, &dict_type); let code_type = create_type("code", &type_type, &int_type, &dict_type); @@ -247,8 +253,10 @@ impl PyContext { false_value, tuple_type, iter_type, + enumerate_type, filter_type, map_type, + zip_type, dict_type, none, str_type, @@ -284,8 +292,10 @@ impl PyContext { objsuper::init(&context); objtuple::init(&context); objiter::init(&context); + objenumerate::init(&context); objfilter::init(&context); objmap::init(&context); + objzip::init(&context); objbool::init(&context); objcode::init(&context); objframe::init(&context); @@ -357,6 +367,10 @@ impl PyContext { self.iter_type.clone() } + pub fn enumerate_type(&self) -> PyObjectRef { + self.enumerate_type.clone() + } + pub fn filter_type(&self) -> PyObjectRef { self.filter_type.clone() } @@ -365,6 +379,10 @@ impl PyContext { self.map_type.clone() } + pub fn zip_type(&self) -> PyObjectRef { + self.zip_type.clone() + } + pub fn str_type(&self) -> PyObjectRef { self.str_type.clone() } @@ -884,6 +902,10 @@ pub enum PyObjectPayload { position: usize, iterated_obj: PyObjectRef, }, + EnumerateIterator { + counter: BigInt, + iterator: PyObjectRef, + }, FilterIterator { predicate: PyObjectRef, iterator: PyObjectRef, @@ -892,6 +914,9 @@ pub enum PyObjectPayload { mapper: PyObjectRef, iterators: Vec, }, + ZipIterator { + iterators: Vec, + }, Slice { start: Option, stop: Option, @@ -960,8 +985,10 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), PyObjectPayload::Range { .. } => write!(f, "range"), PyObjectPayload::Iterator { .. } => write!(f, "iterator"), + PyObjectPayload::EnumerateIterator { .. } => write!(f, "enumerate"), PyObjectPayload::FilterIterator { .. } => write!(f, "filter"), PyObjectPayload::MapIterator { .. } => write!(f, "map"), + PyObjectPayload::ZipIterator { .. } => write!(f, "zip"), PyObjectPayload::Slice { .. } => write!(f, "slice"), PyObjectPayload::Code { ref code } => write!(f, "code: {:?}", code), PyObjectPayload::Function { .. } => write!(f, "function"), @@ -1057,8 +1084,10 @@ impl PyObject { position, iterated_obj.borrow_mut().str() ), + PyObjectPayload::EnumerateIterator { .. } => format!(""), PyObjectPayload::FilterIterator { .. } => format!(""), PyObjectPayload::MapIterator { .. } => format!(""), + PyObjectPayload::ZipIterator { .. } => format!(""), } } From e1284e34b05fcee5fd6b66d937da8cd052c7f63d Mon Sep 17 00:00:00 2001 From: ben Date: Fri, 8 Feb 2019 21:05:04 +1300 Subject: [PATCH 115/153] Made filter_new private --- vm/src/obj/objfilter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index 4eda041ec..b4bc4ff5e 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -7,7 +7,7 @@ use super::objbool; use super::objiter; use super::objtype; // Required for arg_check! to use isinstance -pub fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, args, From 1ebacafb00a67ddba44cefe71bcbf8c8ebf94f19 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Fri, 8 Feb 2019 00:19:14 -0800 Subject: [PATCH 116/153] Add reversed builtin and range.__reversed__ --- tests/snippets/builtin_range.py | 4 +++ tests/snippets/builtin_reversed.py | 1 + vm/src/builtins.rs | 14 +++++++++++ vm/src/obj/objrange.rs | 39 ++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 tests/snippets/builtin_reversed.py diff --git a/tests/snippets/builtin_range.py b/tests/snippets/builtin_range.py index c822ce7e7..cdba9b6d5 100644 --- a/tests/snippets/builtin_range.py +++ b/tests/snippets/builtin_range.py @@ -52,3 +52,7 @@ assert not range(10).__contains__(-1) assert not range(10, 4, -2).__contains__(9) assert not range(10, 4, -2).__contains__(4) assert not range(10).__contains__('foo') + +# __reversed__ +assert list(range(5).__reversed__()) == [4, 3, 2, 1, 0] +assert list(range(5, 0, -1).__reversed__()) == [1, 2, 3, 4, 5] diff --git a/tests/snippets/builtin_reversed.py b/tests/snippets/builtin_reversed.py new file mode 100644 index 000000000..2bbfcb98a --- /dev/null +++ b/tests/snippets/builtin_reversed.py @@ -0,0 +1 @@ +assert list(reversed(range(5))) == [4, 3, 2, 1, 0] diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 1db5c7e77..c785a250b 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -611,6 +611,19 @@ fn builtin_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, None)]); vm.to_repr(obj) } + +fn builtin_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(obj, None)]); + + match vm.get_method(obj.clone(), "__reversed__") { + Ok(value) => vm.invoke(value, PyFuncArgs::default()), + // TODO: fallback to using __len__ and __getitem__, if object supports sequence protocol + Err(..) => Err(vm.new_type_error(format!( + "'{}' object is not reversible", + objtype::get_type_name(&obj.typ()), + ))), + } +} // builtin_reversed // builtin_round @@ -725,6 +738,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "property", ctx.property_type()); ctx.set_attr(&py_mod, "range", ctx.range_type()); ctx.set_attr(&py_mod, "repr", ctx.new_rustfunc(builtin_repr)); + ctx.set_attr(&py_mod, "reversed", ctx.new_rustfunc(builtin_reversed)); ctx.set_attr(&py_mod, "set", ctx.set_type()); ctx.set_attr(&py_mod, "setattr", ctx.new_rustfunc(builtin_setattr)); ctx.set_attr(&py_mod, "staticmethod", ctx.staticmethod_type()); diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index cd897ff34..746822876 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -76,12 +76,34 @@ impl RangeType { None } } + + #[inline] + pub fn reversed(&self) -> Self { + match self.step.sign() { + Sign::Plus => RangeType { + start: &self.end - 1, + end: &self.start - 1, + step: -&self.step, + }, + Sign::Minus => RangeType { + start: &self.end + 1, + end: &self.start + 1, + step: -&self.step, + }, + Sign::NoSign => unreachable!(), + } + } } pub fn init(context: &PyContext) { let ref range_type = context.range_type; context.set_attr(&range_type, "__new__", context.new_rustfunc(range_new)); context.set_attr(&range_type, "__iter__", context.new_rustfunc(range_iter)); + context.set_attr( + &range_type, + "__reversed__", + context.new_rustfunc(range_reversed), + ); context.set_attr(&range_type, "__len__", context.new_rustfunc(range_len)); context.set_attr( &range_type, @@ -150,6 +172,23 @@ fn range_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } +fn range_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); + + let range = match zelf.borrow().payload { + PyObjectPayload::Range { ref range } => range.reversed(), + _ => unreachable!(), + }; + + Ok(PyObject::new( + PyObjectPayload::Iterator { + position: 0, + iterated_obj: PyObject::new(PyObjectPayload::Range { range }, vm.ctx.range_type()), + }, + vm.ctx.iter_type(), + )) +} + fn range_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); From 07fd61f55f43844c1e7b6e6ebc8d91f0869b0353 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Fri, 8 Feb 2019 15:18:14 +0100 Subject: [PATCH 117/153] Change instance and class to simplified dict type. (#379) * Change instance and class to simplified dict type. * Fix formatting. --- vm/src/obj/objdict.rs | 4 +-- vm/src/obj/objfunction.rs | 18 +++-------- vm/src/obj/objobject.rs | 40 ++++++++++++----------- vm/src/obj/objproperty.rs | 11 ++----- vm/src/obj/objtype.rs | 60 +++++++++++++++++----------------- vm/src/pyobject.rs | 68 ++++++++++++++++++++------------------- vm/src/stdlib/types.rs | 7 ++-- 7 files changed, 99 insertions(+), 109 deletions(-) diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index f2e5f2b84..8a3e77ff8 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -5,7 +5,7 @@ use super::super::vm::VirtualMachine; use super::objiter; use super::objstr; use super::objtype; -use std::cell::{Ref, RefMut}; +use std::cell::{Ref, RefCell, RefMut}; use std::collections::HashMap; use std::ops::{Deref, DerefMut}; @@ -273,7 +273,7 @@ fn dict_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) { (*dict_type.borrow_mut()).payload = PyObjectPayload::Class { name: String::from("dict"), - dict: new(dict_type.clone()), + dict: RefCell::new(HashMap::new()), mro: vec![object_type], }; (*dict_type.borrow_mut()).typ = Some(type_type.clone()); diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index f04897481..01c05aab1 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -1,6 +1,6 @@ use super::super::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, - PyResult, TypeProtocol, + AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, + TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objtype; @@ -110,12 +110,7 @@ fn classmethod_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("classmethod.__new__ {:?}", args.args); arg_check!(vm, args, required = [(cls, None), (callable, None)]); - let py_obj = PyObject::new( - PyObjectPayload::Instance { - dict: vm.ctx.new_dict(), - }, - cls.clone(), - ); + let py_obj = vm.ctx.new_instance(cls.clone(), None); vm.ctx.set_attr(&py_obj, "function", callable.clone()); Ok(py_obj) } @@ -148,12 +143,7 @@ fn staticmethod_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("staticmethod.__new__ {:?}", args.args); arg_check!(vm, args, required = [(cls, None), (callable, None)]); - let py_obj = PyObject::new( - PyObjectPayload::Instance { - dict: vm.ctx.new_dict(), - }, - cls.clone(), - ); + let py_obj = vm.ctx.new_instance(cls.clone(), None); vm.ctx.set_attr(&py_obj, "function", callable.clone()); Ok(py_obj) } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 190f3078b..cb5065b20 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -1,25 +1,25 @@ use super::super::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, - PyResult, TypeProtocol, + AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, + TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objbool; -use super::objdict; use super::objstr; use super::objtype; +use std::cell::RefCell; +use std::collections::HashMap; pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { // more or less __new__ operator let type_ref = args.shift(); - let dict = vm.new_dict(); - let obj = PyObject::new(PyObjectPayload::Instance { dict }, type_ref.clone()); + let obj = vm.ctx.new_instance(type_ref.clone(), None); Ok(obj) } -pub fn create_object(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) { +pub fn create_object(type_type: PyObjectRef, object_type: PyObjectRef, _dict_type: PyObjectRef) { (*object_type.borrow_mut()).payload = PyObjectPayload::Class { name: String::from("object"), - dict: objdict::new(dict_type), + dict: RefCell::new(HashMap::new()), mro: vec![], }; (*object_type.borrow_mut()).typ = Some(type_type.clone()); @@ -62,15 +62,14 @@ fn object_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ] ); - // Get dict: - let dict = match zelf.borrow().payload { - PyObjectPayload::Class { ref dict, .. } => dict.clone(), - PyObjectPayload::Instance { ref dict, .. } => dict.clone(), - _ => return Err(vm.new_type_error("TypeError: no dictionary.".to_string())), - }; - - // Delete attr from dict: - vm.call_method(&dict, "__delitem__", vec![attr.clone()]) + match zelf.borrow().payload { + PyObjectPayload::Class { ref dict, .. } | PyObjectPayload::Instance { ref dict, .. } => { + let attr_name = objstr::get_value(attr); + dict.borrow_mut().remove(&attr_name); + Ok(vm.get_none()) + } + _ => Err(vm.new_type_error("TypeError: no dictionary.".to_string())), + } } fn object_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -116,8 +115,13 @@ fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { fn object_dict(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match args.args[0].borrow().payload { - PyObjectPayload::Class { ref dict, .. } => Ok(dict.clone()), - PyObjectPayload::Instance { ref dict, .. } => Ok(dict.clone()), + PyObjectPayload::Class { ref dict, .. } | PyObjectPayload::Instance { ref dict, .. } => { + let new_dict = vm.new_dict(); + for (attr, value) in dict.borrow().iter() { + vm.ctx.set_item(&new_dict, &attr, value.clone()); + } + Ok(new_dict) + } _ => Err(vm.new_type_error("TypeError: no dictionary.".to_string())), } } diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index 1d146889b..7ec52a9fd 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -2,9 +2,7 @@ */ -use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, -}; +use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use super::super::vm::VirtualMachine; use super::objtype; @@ -55,12 +53,7 @@ fn property_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("property.__new__ {:?}", args.args); arg_check!(vm, args, required = [(cls, None), (fget, None)]); - let py_obj = PyObject::new( - PyObjectPayload::Instance { - dict: vm.ctx.new_dict(), - }, - cls.clone(), - ); + let py_obj = vm.ctx.new_instance(cls.clone(), None); vm.ctx.set_attr(&py_obj, "fget", fget.clone()); Ok(py_obj) } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index b72526eb2..ce98e0f4c 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -1,21 +1,22 @@ use super::super::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, - PyResult, TypeProtocol, + AttributeProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, PyObjectPayload, + PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objdict; use super::objstr; use super::objtype; // Required for arg_check! to use isinstance +use std::cell::RefCell; use std::collections::HashMap; /* * The magical type type */ -pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) { +pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, _dict_type: PyObjectRef) { (*type_type.borrow_mut()).payload = PyObjectPayload::Class { name: String::from("type"), - dict: objdict::new(dict_type), + dict: RefCell::new(PyAttributes::new()), mro: vec![object_type], }; (*type_type.borrow_mut()).typ = Some(type_type.clone()); @@ -119,12 +120,22 @@ pub fn type_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let mut bases = vm.extract_elements(bases)?; bases.push(vm.context().object()); let name = objstr::get_value(name); - new(typ.clone(), &name, bases, dict.clone()) + new(typ.clone(), &name, bases, py_dict_to_attributes(dict)) } else { Err(vm.new_type_error(format!(": type_new: {:?}", args))) } } +/// Take a python dictionary and convert it to attributes. +fn py_dict_to_attributes(dict: &PyObjectRef) -> PyAttributes { + let mut attrs = PyAttributes::new(); + for (key, value) in objdict::get_key_value_pairs(dict) { + let key = objstr::get_value(&key); + attrs.insert(key, value); + } + attrs +} + pub fn type_call(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { debug!("type_call: {:?}", args); let cls = args.shift(); @@ -204,18 +215,16 @@ pub fn type_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult } } -pub fn get_attributes(obj: &PyObjectRef) -> HashMap { +pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes { // Gather all members here: - let mut attributes: HashMap = HashMap::new(); + let mut attributes = PyAttributes::new(); // Get class attributes: let mut base_classes = objtype::base_classes(obj); base_classes.reverse(); for bc in base_classes { if let PyObjectPayload::Class { dict, .. } = &bc.borrow().payload { - let elements = objdict::get_key_value_pairs(dict); - for (name, value) in elements.iter() { - let name = objstr::get_value(name); + for (name, value) in dict.borrow().iter() { attributes.insert(name.to_string(), value.clone()); } } @@ -223,9 +232,7 @@ pub fn get_attributes(obj: &PyObjectRef) -> HashMap { // Get instance attributes: if let PyObjectPayload::Instance { dict } = &obj.borrow().payload { - let elements = objdict::get_key_value_pairs(dict); - for (name, value) in elements.iter() { - let name = objstr::get_value(name); + for (name, value) in dict.borrow().iter() { attributes.insert(name.to_string(), value.clone()); } } @@ -279,13 +286,18 @@ fn linearise_mro(mut bases: Vec>) -> Option> { Some(result) } -pub fn new(typ: PyObjectRef, name: &str, bases: Vec, dict: PyObjectRef) -> PyResult { +pub fn new( + typ: PyObjectRef, + name: &str, + bases: Vec, + dict: HashMap, +) -> PyResult { let mros = bases.into_iter().map(|x| _mro(x).unwrap()).collect(); let mro = linearise_mro(mros).unwrap(); Ok(PyObject::new( PyObjectPayload::Class { name: String::from(name), - dict, + dict: RefCell::new(dict), mro, }, typ, @@ -305,7 +317,7 @@ fn type_prepare(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { #[cfg(test)] mod tests { use super::{linearise_mro, new}; - use super::{IdProtocol, PyContext, PyObjectRef}; + use super::{HashMap, IdProtocol, PyContext, PyObjectRef}; fn map_ids(obj: Option>) -> Option> { match obj { @@ -320,20 +332,8 @@ mod tests { let object = context.object; let type_type = context.type_type; - let a = new( - type_type.clone(), - "A", - vec![object.clone()], - type_type.clone(), - ) - .unwrap(); - let b = new( - type_type.clone(), - "B", - vec![object.clone()], - type_type.clone(), - ) - .unwrap(); + let a = new(type_type.clone(), "A", vec![object.clone()], HashMap::new()).unwrap(); + let b = new(type_type.clone(), "B", vec![object.clone()], HashMap::new()).unwrap(); assert_eq!( map_ids(linearise_mro(vec![ diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 02181e4fe..424cf7646 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -70,6 +70,10 @@ pub type PyObjectWeakRef = Weak>; /// since exceptions are also python objects. pub type PyResult = Result; // A valid value, or an exception +/// For attributes we do not use a dict, but a hashmap. This is probably +/// faster, unordered, and only supports strings as keys. +pub type PyAttributes = HashMap; + impl fmt::Display for PyObject { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::TypeProtocol; @@ -161,14 +165,9 @@ pub fn create_type( name: &str, type_type: &PyObjectRef, base: &PyObjectRef, - dict_type: &PyObjectRef, + _dict_type: &PyObjectRef, ) -> PyObjectRef { - let dict = PyObject::new( - PyObjectPayload::Dict { - elements: HashMap::new(), - }, - dict_type.clone(), - ); + let dict = PyAttributes::new(); objtype::new(type_type.clone(), name, vec![base.clone()], dict).unwrap() } @@ -411,12 +410,7 @@ impl PyContext { } pub fn new_object(&self) -> PyObjectRef { - PyObject::new( - PyObjectPayload::Instance { - dict: self.new_dict(), - }, - self.object(), - ) + self.new_instance(self.object(), None) } pub fn new_int(&self, i: T) -> PyObjectRef { @@ -482,7 +476,7 @@ impl PyContext { } pub fn new_class(&self, name: &str, base: PyObjectRef) -> PyObjectRef { - objtype::new(self.type_type(), name, vec![base], self.new_dict()).unwrap() + objtype::new(self.type_type(), name, vec![base], PyAttributes::new()).unwrap() } pub fn new_scope(&self, parent: Option) -> PyObjectRef { @@ -536,12 +530,7 @@ impl PyContext { function: F, ) -> PyObjectRef { let fget = self.new_rustfunc(function); - let py_obj = PyObject::new( - PyObjectPayload::Instance { - dict: self.new_dict(), - }, - self.property_type(), - ); + let py_obj = self.new_instance(self.property_type(), None); self.set_attr(&py_obj, "fget", fget.clone()); py_obj } @@ -577,13 +566,23 @@ impl PyContext { &self, function: F, ) -> PyObjectRef { - let dict = self.new_dict(); - self.set_item(&dict, "function", self.new_rustfunc(function)); - self.new_instance(dict, self.member_descriptor_type()) + let mut dict = PyAttributes::new(); + dict.insert("function".to_string(), self.new_rustfunc(function)); + self.new_instance(self.member_descriptor_type(), Some(dict)) } - pub fn new_instance(&self, dict: PyObjectRef, class: PyObjectRef) -> PyObjectRef { - PyObject::new(PyObjectPayload::Instance { dict }, class) + pub fn new_instance(&self, class: PyObjectRef, dict: Option) -> PyObjectRef { + let dict = if let Some(dict) = dict { + dict + } else { + PyAttributes::new() + }; + PyObject::new( + PyObjectPayload::Instance { + dict: RefCell::new(dict), + }, + class, + ) } // Item set/get: @@ -611,8 +610,9 @@ impl PyContext { pub fn set_attr(&self, obj: &PyObjectRef, attr_name: &str, value: PyObjectRef) { match obj.borrow().payload { PyObjectPayload::Module { ref dict, .. } => self.set_item(dict, attr_name, value), - PyObjectPayload::Instance { ref dict } => self.set_item(dict, attr_name, value), - PyObjectPayload::Class { ref dict, .. } => self.set_item(dict, attr_name, value), + PyObjectPayload::Instance { ref dict } | PyObjectPayload::Class { ref dict, .. } => { + dict.borrow_mut().insert(attr_name.to_string(), value); + } ref payload => unimplemented!("set_attr unimplemented for: {:?}", payload), }; } @@ -697,7 +697,7 @@ pub trait AttributeProtocol { fn class_get_item(class: &PyObjectRef, attr_name: &str) -> Option { let class = class.borrow(); match class.payload { - PyObjectPayload::Class { ref dict, .. } => dict.get_item(attr_name), + PyObjectPayload::Class { ref dict, .. } => dict.borrow().get(attr_name).map(|v| v.clone()), _ => panic!("Only classes should be in MRO!"), } } @@ -705,7 +705,7 @@ fn class_get_item(class: &PyObjectRef, attr_name: &str) -> Option { fn class_has_item(class: &PyObjectRef, attr_name: &str) -> bool { let class = class.borrow(); match class.payload { - PyObjectPayload::Class { ref dict, .. } => dict.contains_key(attr_name), + PyObjectPayload::Class { ref dict, .. } => dict.borrow().contains_key(attr_name), _ => panic!("Only classes should be in MRO!"), } } @@ -726,7 +726,9 @@ impl AttributeProtocol for PyObjectRef { } None } - PyObjectPayload::Instance { ref dict } => dict.get_item(attr_name), + PyObjectPayload::Instance { ref dict } => { + dict.borrow().get(attr_name).map(|v| v.clone()) + } _ => None, } } @@ -738,7 +740,7 @@ impl AttributeProtocol for PyObjectRef { PyObjectPayload::Class { ref mro, .. } => { class_has_item(self, attr_name) || mro.iter().any(|d| class_has_item(d, attr_name)) } - PyObjectPayload::Instance { ref dict } => dict.contains_key(attr_name), + PyObjectPayload::Instance { ref dict } => dict.borrow().contains_key(attr_name), _ => false, } } @@ -931,14 +933,14 @@ pub enum PyObjectPayload { None, Class { name: String, - dict: PyObjectRef, + dict: RefCell, mro: Vec, }, WeakRef { referent: PyObjectWeakRef, }, Instance { - dict: PyObjectRef, + dict: RefCell, }, RustFunction { function: Box PyResult>, diff --git a/vm/src/stdlib/types.rs b/vm/src/stdlib/types.rs index 2060ad1f1..a744d89b7 100644 --- a/vm/src/stdlib/types.rs +++ b/vm/src/stdlib/types.rs @@ -3,7 +3,9 @@ */ use super::super::obj::{objsequence, objstr, objtype}; -use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; +use super::super::pyobject::{ + PyAttributes, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol, +}; use super::super::VirtualMachine; fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -15,7 +17,6 @@ fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let name = objstr::get_value(name); - let dict = vm.ctx.new_dict(); let bases = match bases { Some(b) => { @@ -28,7 +29,7 @@ fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { None => vec![vm.ctx.object()], }; - objtype::new(vm.ctx.type_type(), &name, bases, dict) + objtype::new(vm.ctx.type_type(), &name, bases, PyAttributes::new()) } pub fn mk_module(ctx: &PyContext) -> PyObjectRef { From 6274c3fe8bb6498a1ecf654360c0ea1495b45a82 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 8 Feb 2019 17:18:14 +0200 Subject: [PATCH 118/153] Add set.__ge__ --- tests/snippets/set.py | 2 ++ vm/src/obj/objset.rs | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tests/snippets/set.py diff --git a/tests/snippets/set.py b/tests/snippets/set.py new file mode 100644 index 000000000..eda3e7702 --- /dev/null +++ b/tests/snippets/set.py @@ -0,0 +1,2 @@ +assert set([1,2,3]) >= set([1,2]) +assert not set([1,3]) >= set([1,2]) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 5ac7507d6..85a01e294 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -126,6 +126,28 @@ pub fn set_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_bool(false)) } +fn set_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [ + (zelf, Some(vm.ctx.set_type())), + (other, Some(vm.ctx.set_type())) + ] + ); + for element in get_elements(other).iter() { + match vm.call_method(zelf, "__contains__", vec![element.1.clone()]) { + Ok(value) => { + if !objbool::get_value(&value) { + return Ok(vm.new_bool(false)); + } + } + Err(_) => return Err(vm.new_type_error("".to_string())), + } + } + Ok(vm.new_bool(true)) +} + fn frozenset_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.frozenset_type()))]); @@ -159,6 +181,7 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__len__", context.new_rustfunc(set_len)); context.set_attr(&set_type, "__new__", context.new_rustfunc(set_new)); context.set_attr(&set_type, "__repr__", context.new_rustfunc(set_repr)); + context.set_attr(&set_type, "__ge__", context.new_rustfunc(set_ge)); context.set_attr(&set_type, "__doc__", context.new_str(set_doc.to_string())); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); From 0c737ae8d2ec5d6b496fd29a4e607fd7f8b15772 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 8 Feb 2019 17:31:45 +0200 Subject: [PATCH 119/153] Add set.__eq__ --- tests/snippets/set.py | 3 +++ vm/src/obj/objset.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/tests/snippets/set.py b/tests/snippets/set.py index eda3e7702..22f1262b5 100644 --- a/tests/snippets/set.py +++ b/tests/snippets/set.py @@ -1,2 +1,5 @@ +assert set([1,2]) == set([1,2]) +assert not set([1,2,3]) == set([1,2]) + assert set([1,2,3]) >= set([1,2]) assert not set([1,3]) >= set([1,2]) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 85a01e294..8c7b66266 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -148,6 +148,33 @@ fn set_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_bool(true)) } +fn set_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [ + (zelf, Some(vm.ctx.set_type())), + (other, Some(vm.ctx.set_type())) + ] + ); + let zelf_elements = get_elements(zelf); + let other_elements = get_elements(other); + if zelf_elements.len() != other_elements.len() { + return Ok(vm.new_bool(false)); + } + for element in zelf_elements.iter() { + match vm.call_method(other, "__contains__", vec![element.1.clone()]) { + Ok(value) => { + if !objbool::get_value(&value) { + return Ok(vm.new_bool(false)); + } + } + Err(_) => return Err(vm.new_type_error("".to_string())), + } + } + Ok(vm.new_bool(true)) +} + fn frozenset_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.frozenset_type()))]); @@ -181,6 +208,7 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__len__", context.new_rustfunc(set_len)); context.set_attr(&set_type, "__new__", context.new_rustfunc(set_new)); context.set_attr(&set_type, "__repr__", context.new_rustfunc(set_repr)); + context.set_attr(&set_type, "__eq__", context.new_rustfunc(set_eq)); context.set_attr(&set_type, "__ge__", context.new_rustfunc(set_ge)); context.set_attr(&set_type, "__doc__", context.new_str(set_doc.to_string())); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); From 61cbb496475559dc1c4e3106792c98c0c14c476d Mon Sep 17 00:00:00 2001 From: veera venky Date: Fri, 8 Feb 2019 21:08:30 +0530 Subject: [PATCH 120/153] Added __doc__ for sys module --- .gitignore | 1 + tests/snippets/sysmod.py | 5 +++ tests/snippets/sysmod_argv.py | 4 -- vm/src/sysmodule.rs | 70 +++++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 tests/snippets/sysmod.py delete mode 100644 tests/snippets/sysmod_argv.py diff --git a/.gitignore b/.gitignore index ee7b4d3ee..c0bc411bd 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ __pycache__ .repl_history.txt .vscode wasm-pack.log +.idea/ diff --git a/tests/snippets/sysmod.py b/tests/snippets/sysmod.py new file mode 100644 index 000000000..7fb876b3e --- /dev/null +++ b/tests/snippets/sysmod.py @@ -0,0 +1,5 @@ +import sys + +print(sys.argv) +assert sys.argv[0].endswith('.py') +assert sys.__doc__ == "This module provides access to some objects used or maintained by the\ninterpreter and to functions that interact strongly with the interpreter.\n\nDynamic objects:\n\nargv -- command line arguments; argv[0] is the script pathname if known\npath -- module search path; path[0] is the script directory, else ''\nmodules -- dictionary of loaded modules\n\ndisplayhook -- called to show results in an interactive session\nexcepthook -- called to handle any uncaught exception other than SystemExit\n To customize printing in an interactive session or to install a custom\n top-level exception handler, assign other functions to replace these.\n\nstdin -- standard input file object; used by input()\nstdout -- standard output file object; used by print()\nstderr -- standard error object; used for error messages\n By assigning other file objects (or objects that behave like files)\n to these, it is possible to redirect all of the interpreter's I/O.\n\nlast_type -- type of last uncaught exception\nlast_value -- value of last uncaught exception\nlast_traceback -- traceback of last uncaught exception\n These three are only available in an interactive session after a\n traceback has been printed.\n\nStatic objects:\n\nbuiltin_module_names -- tuple of module names built into this interpreter\ncopyright -- copyright notice pertaining to this interpreter\nexec_prefix -- prefix used to find the machine-specific Python library\nexecutable -- absolute path of the executable binary of the Python interpreter\nfloat_info -- a struct sequence with information about the float implementation.\nfloat_repr_style -- string indicating the style of repr() output for floats\nhash_info -- a struct sequence with information about the hash algorithm.\nhexversion -- version information encoded as a single integer\nimplementation -- Python implementation information.\nint_info -- a struct sequence with information about the int implementation.\nmaxsize -- the largest supported length of containers.\nmaxunicode -- the value of the largest Unicode code point\nplatform -- platform identifier\nprefix -- prefix used to find the Python library\nthread_info -- a struct sequence with information about the thread implementation.\nversion -- the version of this interpreter as a string\nversion_info -- version information as a named tuple\n__stdin__ -- the original stdin; don't touch!\n__stdout__ -- the original stdout; don't touch!\n__stderr__ -- the original stderr; don't touch!\n__displayhook__ -- the original displayhook; don't touch!\n__excepthook__ -- the original excepthook; don't touch!\n\nFunctions:\n\ndisplayhook() -- print an object to the screen, and save it in builtins._\nexcepthook() -- print an exception and its traceback to sys.stderr\nexc_info() -- return thread-safe information about the current exception\nexit() -- exit the interpreter by raising SystemExit\ngetdlopenflags() -- returns flags to be used for dlopen() calls\ngetprofile() -- get the global profiling function\ngetrefcount() -- return the reference count for an object (plus one :-)\ngetrecursionlimit() -- return the max recursion depth for the interpreter\ngetsizeof() -- return the size of an object in bytes\ngettrace() -- get the global debug tracing function\nsetcheckinterval() -- control how often the interpreter checks for events\nsetdlopenflags() -- set the flags to be used for dlopen() calls\nsetprofile() -- set the global profiling function\nsetrecursionlimit() -- set the max recursion depth for the interpreter\nsettrace() -- set the global debug tracing function\n" diff --git a/tests/snippets/sysmod_argv.py b/tests/snippets/sysmod_argv.py deleted file mode 100644 index bfcf17201..000000000 --- a/tests/snippets/sysmod_argv.py +++ /dev/null @@ -1,4 +0,0 @@ -import sys - -print(sys.argv) -assert sys.argv[0].endswith('.py') diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index 43b01d0d9..913e4d4c3 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -53,6 +53,75 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { let modules = ctx.new_dict(); let sys_name = "sys"; + let sys_doc = "This module provides access to some objects used or maintained by the +interpreter and to functions that interact strongly with the interpreter. + +Dynamic objects: + +argv -- command line arguments; argv[0] is the script pathname if known +path -- module search path; path[0] is the script directory, else '' +modules -- dictionary of loaded modules + +displayhook -- called to show results in an interactive session +excepthook -- called to handle any uncaught exception other than SystemExit + To customize printing in an interactive session or to install a custom + top-level exception handler, assign other functions to replace these. + +stdin -- standard input file object; used by input() +stdout -- standard output file object; used by print() +stderr -- standard error object; used for error messages + By assigning other file objects (or objects that behave like files) + to these, it is possible to redirect all of the interpreter's I/O. + +last_type -- type of last uncaught exception +last_value -- value of last uncaught exception +last_traceback -- traceback of last uncaught exception + These three are only available in an interactive session after a + traceback has been printed. + +Static objects: + +builtin_module_names -- tuple of module names built into this interpreter +copyright -- copyright notice pertaining to this interpreter +exec_prefix -- prefix used to find the machine-specific Python library +executable -- absolute path of the executable binary of the Python interpreter +float_info -- a struct sequence with information about the float implementation. +float_repr_style -- string indicating the style of repr() output for floats +hash_info -- a struct sequence with information about the hash algorithm. +hexversion -- version information encoded as a single integer +implementation -- Python implementation information. +int_info -- a struct sequence with information about the int implementation. +maxsize -- the largest supported length of containers. +maxunicode -- the value of the largest Unicode code point +platform -- platform identifier +prefix -- prefix used to find the Python library +thread_info -- a struct sequence with information about the thread implementation. +version -- the version of this interpreter as a string +version_info -- version information as a named tuple +__stdin__ -- the original stdin; don't touch! +__stdout__ -- the original stdout; don't touch! +__stderr__ -- the original stderr; don't touch! +__displayhook__ -- the original displayhook; don't touch! +__excepthook__ -- the original excepthook; don't touch! + +Functions: + +displayhook() -- print an object to the screen, and save it in builtins._ +excepthook() -- print an exception and its traceback to sys.stderr +exc_info() -- return thread-safe information about the current exception +exit() -- exit the interpreter by raising SystemExit +getdlopenflags() -- returns flags to be used for dlopen() calls +getprofile() -- get the global profiling function +getrefcount() -- return the reference count for an object (plus one :-) +getrecursionlimit() -- return the max recursion depth for the interpreter +getsizeof() -- return the size of an object in bytes +gettrace() -- get the global debug tracing function +setcheckinterval() -- control how often the interpreter checks for events +setdlopenflags() -- set the flags to be used for dlopen() calls +setprofile() -- set the global profiling function +setrecursionlimit() -- set the max recursion depth for the interpreter +settrace() -- set the global debug tracing function +"; let sys_mod = ctx.new_module(&sys_name, ctx.new_scope(None)); ctx.set_item(&modules, sys_name, sys_mod.clone()); @@ -65,6 +134,7 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef { ctx.set_item(&sys_mod, "path", path); ctx.set_item(&sys_mod, "ps1", ctx.new_str(">>>>> ".to_string())); ctx.set_item(&sys_mod, "ps2", ctx.new_str("..... ".to_string())); + ctx.set_item(&sys_mod, "__doc__", ctx.new_str(sys_doc.to_string())); ctx.set_item(&sys_mod, "_getframe", ctx.new_rustfunc(getframe)); sys_mod From a180a4b6cbe424c1644125ecb5fe0ac4489110e4 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 18:43:27 +0300 Subject: [PATCH 121/153] bytearray type: Added __doc__ --- vm/src/obj/objbytearray.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index c6fc8c35c..a60cf1b65 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -16,6 +16,20 @@ use num_traits::ToPrimitive; /// Fill bytearray class methods dictionary. pub fn init(context: &PyContext) { let bytearray_type = &context.bytearray_type; + + let bytearray_doc = + "bytearray(iterable_of_ints) -> bytearray\n\ + bytearray(string, encoding[, errors]) -> bytearray\n\ + bytearray(bytes_or_buffer) -> mutable copy of bytes_or_buffer\n\ + bytearray(int) -> bytes array of size given by the parameter initialized with null bytes\n\ + bytearray() -> empty bytes array\n\n\ + Construct a mutable bytearray object from:\n \ + - an iterable yielding integers in range(256)\n \ + - a text string encoded using the specified encoding\n \ + - a bytes or a buffer object\n \ + - any object implementing the buffer API.\n \ + - an integer"; + context.set_attr( &bytearray_type, "__eq__", @@ -36,6 +50,11 @@ pub fn init(context: &PyContext) { "__len__", context.new_rustfunc(bytesarray_len), ); + context.set_attr( + &bytearray_type, + "__doc__", + context.new_str(bytearray_doc.to_string()), + ); context.set_attr( &bytearray_type, "isalnum", From 031f062e72a44ba15cbec71fbb6fc183e1b77cb6 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 18:47:23 +0300 Subject: [PATCH 122/153] complex type: Added __doc__ --- vm/src/obj/objcomplex.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index bc624d50f..353b89b7f 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -8,8 +8,18 @@ use num_complex::Complex64; pub fn init(context: &PyContext) { let complex_type = &context.complex_type; + + let complex_doc = + "Create a complex number from a real part and an optional imaginary part.\n\n\ + This is equivalent to (real + imag*1j) where imag defaults to 0."; + context.set_attr(&complex_type, "__add__", context.new_rustfunc(complex_add)); context.set_attr(&complex_type, "__new__", context.new_rustfunc(complex_new)); + context.set_attr( + &complex_type, + "__doc__", + context.new_str(complex_doc.to_string()), + ); context.set_attr( &complex_type, "__repr__", From 358aa6b2c03b3390cd3d53cc7a244ff4028abf96 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 8 Feb 2019 17:50:36 +0200 Subject: [PATCH 123/153] Add set.__gt__ --- tests/snippets/set.py | 5 +++++ vm/src/obj/objset.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/tests/snippets/set.py b/tests/snippets/set.py index 22f1262b5..27225a6e9 100644 --- a/tests/snippets/set.py +++ b/tests/snippets/set.py @@ -2,4 +2,9 @@ assert set([1,2]) == set([1,2]) assert not set([1,2,3]) == set([1,2]) assert set([1,2,3]) >= set([1,2]) +assert set([1,2]) >= set([1,2]) assert not set([1,3]) >= set([1,2]) + +assert set([1,2,3]) > set([1,2]) +assert not set([1,2]) > set([1,2]) +assert not set([1,3]) > set([1,2]) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 8c7b66266..40f131ab7 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -175,6 +175,34 @@ fn set_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_bool(true)) } +fn set_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [ + (zelf, Some(vm.ctx.set_type())), + (other, Some(vm.ctx.set_type())) + ] + ); + + let zelf_elements = get_elements(zelf); + let other_elements = get_elements(other); + if zelf_elements.len() <= other_elements.len() { + return Ok(vm.new_bool(false)); + } + for element in other_elements.iter() { + match vm.call_method(zelf, "__contains__", vec![element.1.clone()]) { + Ok(value) => { + if !objbool::get_value(&value) { + return Ok(vm.new_bool(false)); + } + } + Err(_) => return Err(vm.new_type_error("".to_string())), + } + } + Ok(vm.new_bool(true)) +} + fn frozenset_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.frozenset_type()))]); @@ -210,6 +238,7 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__repr__", context.new_rustfunc(set_repr)); context.set_attr(&set_type, "__eq__", context.new_rustfunc(set_eq)); context.set_attr(&set_type, "__ge__", context.new_rustfunc(set_ge)); + context.set_attr(&set_type, "__gt__", context.new_rustfunc(set_gt)); context.set_attr(&set_type, "__doc__", context.new_str(set_doc.to_string())); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); From c05f7dc83abefccf033c94778c4af3e6dff8f911 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 19:01:18 +0300 Subject: [PATCH 124/153] map type: Added __doc__ --- vm/src/obj/objmap.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index ed6130643..14cfe1d34 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -70,6 +70,11 @@ fn map_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let map_type = &context.map_type; + + let map_doc = "map(func, *iterables) --> map object\n\n\ + Make an iterator that computes the function using arguments from\n\ + each of the iterables. Stops when the shortest iterable is exhausted."; + context.set_attr( &map_type, "__contains__", @@ -78,4 +83,5 @@ pub fn init(context: &PyContext) { context.set_attr(&map_type, "__iter__", context.new_rustfunc(map_iter)); context.set_attr(&map_type, "__new__", context.new_rustfunc(map_new)); context.set_attr(&map_type, "__next__", context.new_rustfunc(map_next)); + context.set_attr(&map_type, "__doc__", context.new_str(map_doc.to_string())); } From 1ee583ef617b0157d99adc96ea060cf147e205b4 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 8 Feb 2019 18:06:22 +0200 Subject: [PATCH 125/153] Use set_compare_inner --- vm/src/obj/objset.rs | 51 ++++++-------------------------------------- 1 file changed, 7 insertions(+), 44 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 40f131ab7..216d90db4 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -127,55 +127,18 @@ pub fn set_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn set_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (zelf, Some(vm.ctx.set_type())), - (other, Some(vm.ctx.set_type())) - ] - ); - for element in get_elements(other).iter() { - match vm.call_method(zelf, "__contains__", vec![element.1.clone()]) { - Ok(value) => { - if !objbool::get_value(&value) { - return Ok(vm.new_bool(false)); - } - } - Err(_) => return Err(vm.new_type_error("".to_string())), - } - } - Ok(vm.new_bool(true)) + return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf < other}) } fn set_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (zelf, Some(vm.ctx.set_type())), - (other, Some(vm.ctx.set_type())) - ] - ); - let zelf_elements = get_elements(zelf); - let other_elements = get_elements(other); - if zelf_elements.len() != other_elements.len() { - return Ok(vm.new_bool(false)); - } - for element in zelf_elements.iter() { - match vm.call_method(other, "__contains__", vec![element.1.clone()]) { - Ok(value) => { - if !objbool::get_value(&value) { - return Ok(vm.new_bool(false)); - } - } - Err(_) => return Err(vm.new_type_error("".to_string())), - } - } - Ok(vm.new_bool(true)) + return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf != other}) } fn set_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf <= other}) +} + +fn set_compare_inner(vm: &mut VirtualMachine, args: PyFuncArgs, size_func: &Fn(usize, usize) -> bool) -> PyResult { arg_check!( vm, args, @@ -187,7 +150,7 @@ fn set_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let zelf_elements = get_elements(zelf); let other_elements = get_elements(other); - if zelf_elements.len() <= other_elements.len() { + if size_func(zelf_elements.len(), other_elements.len()) { return Ok(vm.new_bool(false)); } for element in other_elements.iter() { From a6beeac383b97a3d7e81aa910ed62ff8d824ce84 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 19:09:38 +0300 Subject: [PATCH 126/153] property type: Added __doc__ --- vm/src/obj/objproperty.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index 7ec52a9fd..ada639270 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -8,6 +8,35 @@ use super::objtype; pub fn init(context: &PyContext) { let property_type = &context.property_type; + + let property_doc = + "Property attribute.\n\n \ + fget\n \ + function to be used for getting an attribute value\n \ + fset\n \ + function to be used for setting an attribute value\n \ + fdel\n \ + function to be used for del\'ing an attribute\n \ + doc\n \ + docstring\n\n\ + Typical use is to define a managed attribute x:\n\n\ + class C(object):\n \ + def getx(self): return self._x\n \ + def setx(self, value): self._x = value\n \ + def delx(self): del self._x\n \ + x = property(getx, setx, delx, \"I\'m the \'x\' property.\")\n\n\ + Decorators make defining new properties or modifying existing ones easy:\n\n\ + class C(object):\n \ + @property\n \ + def x(self):\n \"I am the \'x\' property.\"\n \ + return self._x\n \ + @x.setter\n \ + def x(self, value):\n \ + self._x = value\n \ + @x.deleter\n \ + def x(self):\n \ + del self._x"; + context.set_attr( &property_type, "__get__", @@ -18,6 +47,11 @@ pub fn init(context: &PyContext) { "__new__", context.new_rustfunc(property_new), ); + context.set_attr( + &property_type, + "__doc__", + context.new_str(property_doc.to_string()), + ); // TODO: how to handle __set__ ? } From a6d6f7721bc4e129a57db174b81f96d9fbf2224c Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 19:17:08 +0300 Subject: [PATCH 127/153] range type: Added __doc__ --- vm/src/obj/objrange.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index cd897ff34..1d8930669 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -80,8 +80,22 @@ impl RangeType { pub fn init(context: &PyContext) { let ref range_type = context.range_type; + + let range_doc = "range(stop) -> range object\n\ + range(start, stop[, step]) -> range object\n\n\ + Return an object that produces a sequence of integers from start (inclusive)\n\ + to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1.\n\ + start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3.\n\ + These are exactly the valid indices for a list of 4 elements.\n\ + When step is given, it specifies the increment (or decrement)."; + context.set_attr(&range_type, "__new__", context.new_rustfunc(range_new)); context.set_attr(&range_type, "__iter__", context.new_rustfunc(range_iter)); + context.set_attr( + &range_type, + "__doc__", + context.new_str(range_doc.to_string()), + ); context.set_attr(&range_type, "__len__", context.new_rustfunc(range_len)); context.set_attr( &range_type, From adee66168b96d1cf6a427219acb676a6b3f0d62c Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 8 Feb 2019 18:25:33 +0200 Subject: [PATCH 128/153] Add set.__le__ --- tests/snippets/set.py | 4 ++++ vm/src/obj/objset.rs | 26 +++++++++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/tests/snippets/set.py b/tests/snippets/set.py index 27225a6e9..b1c938df7 100644 --- a/tests/snippets/set.py +++ b/tests/snippets/set.py @@ -8,3 +8,7 @@ assert not set([1,3]) >= set([1,2]) assert set([1,2,3]) > set([1,2]) assert not set([1,2]) > set([1,2]) assert not set([1,3]) > set([1,2]) + +assert set([1,2]) <= set([1,2,3]) +assert set([1,2]) <= set([1,2]) +assert not set([1,3]) <= set([1,2]) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 216d90db4..774ecb70a 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -126,19 +126,23 @@ pub fn set_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_bool(false)) } -fn set_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf < other}) +fn set_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf != other}, false) } -fn set_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf != other}) +fn set_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf < other}, false) } fn set_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf <= other}) + return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf <= other}, false) } -fn set_compare_inner(vm: &mut VirtualMachine, args: PyFuncArgs, size_func: &Fn(usize, usize) -> bool) -> PyResult { +fn set_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf < other}, true) +} + +fn set_compare_inner(vm: &mut VirtualMachine, args: PyFuncArgs, size_func: &Fn(usize, usize) -> bool, swap: bool) -> PyResult { arg_check!( vm, args, @@ -148,13 +152,16 @@ fn set_compare_inner(vm: &mut VirtualMachine, args: PyFuncArgs, size_func: &Fn(u ] ); - let zelf_elements = get_elements(zelf); - let other_elements = get_elements(other); + let get_zelf = |swap: bool| -> &PyObjectRef {if swap {other} else {zelf}}; + let get_other = |swap: bool| -> &PyObjectRef {if swap {zelf} else {other}}; + + let zelf_elements = get_elements(get_zelf(swap)); + let other_elements = get_elements(get_other(swap)); if size_func(zelf_elements.len(), other_elements.len()) { return Ok(vm.new_bool(false)); } for element in other_elements.iter() { - match vm.call_method(zelf, "__contains__", vec![element.1.clone()]) { + match vm.call_method(get_zelf(swap), "__contains__", vec![element.1.clone()]) { Ok(value) => { if !objbool::get_value(&value) { return Ok(vm.new_bool(false)); @@ -202,6 +209,7 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__eq__", context.new_rustfunc(set_eq)); context.set_attr(&set_type, "__ge__", context.new_rustfunc(set_ge)); context.set_attr(&set_type, "__gt__", context.new_rustfunc(set_gt)); + context.set_attr(&set_type, "__le__", context.new_rustfunc(set_le)); context.set_attr(&set_type, "__doc__", context.new_str(set_doc.to_string())); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); From 2bc946b748ec7395bd645b1eda82909a203e8cc5 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 8 Feb 2019 18:27:37 +0200 Subject: [PATCH 129/153] Add set.__lt__ --- tests/snippets/set.py | 4 ++++ vm/src/obj/objset.rs | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/tests/snippets/set.py b/tests/snippets/set.py index b1c938df7..c078908f7 100644 --- a/tests/snippets/set.py +++ b/tests/snippets/set.py @@ -12,3 +12,7 @@ assert not set([1,3]) > set([1,2]) assert set([1,2]) <= set([1,2,3]) assert set([1,2]) <= set([1,2]) assert not set([1,3]) <= set([1,2]) + +assert set([1,2]) < set([1,2,3]) +assert not set([1,2]) < set([1,2]) +assert not set([1,3]) < set([1,2]) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 774ecb70a..e2399776e 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -142,6 +142,10 @@ fn set_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf < other}, true) } +fn set_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf <= other}, true) +} + fn set_compare_inner(vm: &mut VirtualMachine, args: PyFuncArgs, size_func: &Fn(usize, usize) -> bool, swap: bool) -> PyResult { arg_check!( vm, @@ -210,6 +214,7 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__ge__", context.new_rustfunc(set_ge)); context.set_attr(&set_type, "__gt__", context.new_rustfunc(set_gt)); context.set_attr(&set_type, "__le__", context.new_rustfunc(set_le)); + context.set_attr(&set_type, "__lt__", context.new_rustfunc(set_lt)); context.set_attr(&set_type, "__doc__", context.new_str(set_doc.to_string())); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); From 2b0f87b69fea8894143604593c1b89e3191e5d46 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 19:28:39 +0300 Subject: [PATCH 130/153] super type: Added __doc__ --- vm/src/obj/objsuper.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 8a0830503..4fa239903 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -12,7 +12,27 @@ use super::objtype; pub fn init(context: &PyContext) { let super_type = &context.super_type; + + let super_doc = "super() -> same as super(__class__, )\n\ + super(type) -> unbound super object\n\ + super(type, obj) -> bound super object; requires isinstance(obj, type)\n\ + super(type, type2) -> bound super object; requires issubclass(type2, type)\n\ + Typical use to call a cooperative superclass method:\n\ + class C(B):\n \ + def meth(self, arg):\n \ + super().meth(arg)\n\ + This works for class methods too:\n\ + class C(B):\n \ + @classmethod\n \ + def cmeth(cls, arg):\n \ + super().cmeth(arg)\n"; + context.set_attr(&super_type, "__init__", context.new_rustfunc(super_init)); + context.set_attr( + &super_type, + "__doc__", + context.new_str(super_doc.to_string()), + ); } fn super_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From 9ec2eef57993226e206c15d00285aceacee7b197 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Fri, 8 Feb 2019 18:33:04 +0200 Subject: [PATCH 131/153] Add set.{issubset,issuperset} --- tests/snippets/set.py | 8 ++++++++ vm/src/obj/objset.rs | 2 ++ 2 files changed, 10 insertions(+) diff --git a/tests/snippets/set.py b/tests/snippets/set.py index c078908f7..8b31c7c23 100644 --- a/tests/snippets/set.py +++ b/tests/snippets/set.py @@ -5,6 +5,10 @@ assert set([1,2,3]) >= set([1,2]) assert set([1,2]) >= set([1,2]) assert not set([1,3]) >= set([1,2]) +assert set([1,2,3]).issuperset(set([1,2])) +assert set([1,2]).issuperset(set([1,2])) +assert not set([1,3]).issuperset(set([1,2])) + assert set([1,2,3]) > set([1,2]) assert not set([1,2]) > set([1,2]) assert not set([1,3]) > set([1,2]) @@ -13,6 +17,10 @@ assert set([1,2]) <= set([1,2,3]) assert set([1,2]) <= set([1,2]) assert not set([1,3]) <= set([1,2]) +assert set([1,2]).issubset(set([1,2,3])) +assert set([1,2]).issubset(set([1,2])) +assert not set([1,3]).issubset(set([1,2])) + assert set([1,2]) < set([1,2,3]) assert not set([1,2]) < set([1,2]) assert not set([1,3]) < set([1,2]) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index e2399776e..dfb46b4c2 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -215,6 +215,8 @@ pub fn init(context: &PyContext) { context.set_attr(&set_type, "__gt__", context.new_rustfunc(set_gt)); context.set_attr(&set_type, "__le__", context.new_rustfunc(set_le)); context.set_attr(&set_type, "__lt__", context.new_rustfunc(set_lt)); + context.set_attr(&set_type, "issubset", context.new_rustfunc(set_le)); + context.set_attr(&set_type, "issuperset", context.new_rustfunc(set_ge)); context.set_attr(&set_type, "__doc__", context.new_str(set_doc.to_string())); context.set_attr(&set_type, "add", context.new_rustfunc(set_add)); From ae88389e10b0ca57515b036ee4b990410405b1c6 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 19:36:10 +0300 Subject: [PATCH 132/153] type type: Added __doc__ --- vm/src/obj/objtype.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index ce98e0f4c..dd5fb3b2c 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -24,6 +24,11 @@ pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, _dict_type: pub fn init(context: &PyContext) { let type_type = &context.type_type; + + let type_doc = "type(object_or_name, bases, dict)\n\ + type(object) -> the object's type\n\ + type(name, bases, dict) -> a new type"; + context.set_attr(&type_type, "__call__", context.new_rustfunc(type_call)); context.set_attr(&type_type, "__new__", context.new_rustfunc(type_new)); context.set_attr( @@ -47,6 +52,7 @@ pub fn init(context: &PyContext) { "__getattribute__", context.new_rustfunc(type_getattribute), ); + context.set_attr(&type_type, "__doc__", context.new_str(type_doc.to_string())); } fn type_mro(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { From da68dfef78d3b376be3fb0fdc48d294dcbd43315 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 19:41:47 +0300 Subject: [PATCH 133/153] tests: Removed tests for the __doc__ --- tests/snippets/bools.py | 2 -- tests/snippets/numbers.py | 2 -- tests/snippets/tuple.py | 2 -- 3 files changed, 6 deletions(-) diff --git a/tests/snippets/bools.py b/tests/snippets/bools.py index 50bd39f7e..2aa817ca4 100644 --- a/tests/snippets/bools.py +++ b/tests/snippets/bools.py @@ -40,8 +40,6 @@ assert (True and True) assert not (False and fake) assert (True and 5) == 5 -assert bool.__doc__ == "bool(x) -> bool\n\nReturns True when the argument x is true, False otherwise.\nThe builtins True and False are the only two instances of the class bool.\nThe class bool is a subclass of the class int, and cannot be subclassed." - # Bools are also ints. assert isinstance(True, int) assert True + True == 2 diff --git a/tests/snippets/numbers.py b/tests/snippets/numbers.py index 12e892385..ecd2263af 100644 --- a/tests/snippets/numbers.py +++ b/tests/snippets/numbers.py @@ -2,8 +2,6 @@ x = 5 x.__init__(6) assert x == 5 -assert int.__doc__ == "int(x=0) -> integer\nint(x, base=10) -> integer\n\nConvert a number or string to an integer, or return 0 if no arguments\nare given. If x is a number, return x.__int__(). For floating point\nnumbers, this truncates towards zero.\n\nIf x is not a number or if base is given, then x must be a string,\nbytes, or bytearray instance representing an integer literal in the\ngiven base. The literal can be preceded by '+' or '-' and be surrounded\nby whitespace. The base defaults to 10. Valid bases are 0 and 2-36.\nBase 0 means to interpret the base from the string as an integer literal.\n>>> int('0b100', base=0)\n4" - class A(int): pass diff --git a/tests/snippets/tuple.py b/tests/snippets/tuple.py index 38617d036..eb5102fa3 100644 --- a/tests/snippets/tuple.py +++ b/tests/snippets/tuple.py @@ -19,5 +19,3 @@ assert x > y, "tuple __gt__ failed" b = (1,2,3) assert b.index(2) == 1 - -assert b.__doc__ == "tuple() -> empty tuple\ntuple(iterable) -> tuple initialized from iterable's items\n\nIf the argument is a tuple, the return value is the same object." From cf2d07502baa7ea4c00f27be79beb4347d358c70 Mon Sep 17 00:00:00 2001 From: veera venky Date: Fri, 8 Feb 2019 22:26:28 +0530 Subject: [PATCH 134/153] Removed strict testing for __doc__ string --- tests/snippets/sysmod.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/snippets/sysmod.py b/tests/snippets/sysmod.py index 7fb876b3e..bfcf17201 100644 --- a/tests/snippets/sysmod.py +++ b/tests/snippets/sysmod.py @@ -2,4 +2,3 @@ import sys print(sys.argv) assert sys.argv[0].endswith('.py') -assert sys.__doc__ == "This module provides access to some objects used or maintained by the\ninterpreter and to functions that interact strongly with the interpreter.\n\nDynamic objects:\n\nargv -- command line arguments; argv[0] is the script pathname if known\npath -- module search path; path[0] is the script directory, else ''\nmodules -- dictionary of loaded modules\n\ndisplayhook -- called to show results in an interactive session\nexcepthook -- called to handle any uncaught exception other than SystemExit\n To customize printing in an interactive session or to install a custom\n top-level exception handler, assign other functions to replace these.\n\nstdin -- standard input file object; used by input()\nstdout -- standard output file object; used by print()\nstderr -- standard error object; used for error messages\n By assigning other file objects (or objects that behave like files)\n to these, it is possible to redirect all of the interpreter's I/O.\n\nlast_type -- type of last uncaught exception\nlast_value -- value of last uncaught exception\nlast_traceback -- traceback of last uncaught exception\n These three are only available in an interactive session after a\n traceback has been printed.\n\nStatic objects:\n\nbuiltin_module_names -- tuple of module names built into this interpreter\ncopyright -- copyright notice pertaining to this interpreter\nexec_prefix -- prefix used to find the machine-specific Python library\nexecutable -- absolute path of the executable binary of the Python interpreter\nfloat_info -- a struct sequence with information about the float implementation.\nfloat_repr_style -- string indicating the style of repr() output for floats\nhash_info -- a struct sequence with information about the hash algorithm.\nhexversion -- version information encoded as a single integer\nimplementation -- Python implementation information.\nint_info -- a struct sequence with information about the int implementation.\nmaxsize -- the largest supported length of containers.\nmaxunicode -- the value of the largest Unicode code point\nplatform -- platform identifier\nprefix -- prefix used to find the Python library\nthread_info -- a struct sequence with information about the thread implementation.\nversion -- the version of this interpreter as a string\nversion_info -- version information as a named tuple\n__stdin__ -- the original stdin; don't touch!\n__stdout__ -- the original stdout; don't touch!\n__stderr__ -- the original stderr; don't touch!\n__displayhook__ -- the original displayhook; don't touch!\n__excepthook__ -- the original excepthook; don't touch!\n\nFunctions:\n\ndisplayhook() -- print an object to the screen, and save it in builtins._\nexcepthook() -- print an exception and its traceback to sys.stderr\nexc_info() -- return thread-safe information about the current exception\nexit() -- exit the interpreter by raising SystemExit\ngetdlopenflags() -- returns flags to be used for dlopen() calls\ngetprofile() -- get the global profiling function\ngetrefcount() -- return the reference count for an object (plus one :-)\ngetrecursionlimit() -- return the max recursion depth for the interpreter\ngetsizeof() -- return the size of an object in bytes\ngettrace() -- get the global debug tracing function\nsetcheckinterval() -- control how often the interpreter checks for events\nsetdlopenflags() -- set the flags to be used for dlopen() calls\nsetprofile() -- set the global profiling function\nsetrecursionlimit() -- set the max recursion depth for the interpreter\nsettrace() -- set the global debug tracing function\n" From 0e6fca01062d926693afc6fb1ede00bff5c96460 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Fri, 8 Feb 2019 20:04:30 +0300 Subject: [PATCH 135/153] Fix the 'if_same_then_else' clippy warnings Relevant clippy warning: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else --- vm/src/obj/objbytearray.rs | 7 ++++--- vm/src/obj/objrange.rs | 6 +++--- vm/src/stdlib/json.rs | 7 +++---- vm/src/vm.rs | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index c6fc8c35c..9de2eb0f0 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -203,11 +203,12 @@ fn bytearray_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } }; - if is_cased(current) && next.is_uppercase() && !prev_cased { - return Ok(vm.new_bool(false)); - } else if !is_cased(current) && next.is_lowercase() { + if (is_cased(current) && next.is_uppercase() && !prev_cased) + || (!is_cased(current) && next.is_lowercase()) + { return Ok(vm.new_bool(false)); } + prev_cased = is_cased(current); } diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index cd897ff34..d82a4abc3 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -68,9 +68,9 @@ impl RangeType { pub fn get(&self, index: BigInt) -> Option { let result = self.start.clone() + self.step.clone() * index; - if self.forward() && !self.is_empty() && result < self.end { - Some(result) - } else if !self.forward() && !self.is_empty() && result > self.end { + if (self.forward() && !self.is_empty() && result < self.end) + || (!self.forward() && !self.is_empty() && result > self.end) + { Some(result) } else { None diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index e284c2c31..e5d626168 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -53,10 +53,9 @@ impl<'s> serde::Serialize for PyObjectSerializer<'s> { serializer.serialize_i64(v.to_i64().unwrap()) // Allthough this may seem nice, it does not give the right result: // v.serialize(serializer) - } else if objtype::isinstance(self.pyobject, &self.ctx.list_type()) { - let elements = objsequence::get_elements(self.pyobject); - serialize_seq_elements(serializer, &elements) - } else if objtype::isinstance(self.pyobject, &self.ctx.tuple_type()) { + } else if objtype::isinstance(self.pyobject, &self.ctx.list_type()) + || objtype::isinstance(self.pyobject, &self.ctx.tuple_type()) + { let elements = objsequence::get_elements(self.pyobject); serialize_seq_elements(serializer, &elements) } else if objtype::isinstance(self.pyobject, &self.ctx.dict_type()) { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 9bbd94d64..8bcadd11c 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -439,9 +439,9 @@ impl VirtualMachine { value: &PyObjectRef, ) -> Result, PyObjectRef> { // Extract elements from item, if possible: - let elements = if objtype::isinstance(value, &self.ctx.tuple_type()) { - objsequence::get_elements(value).to_vec() - } else if objtype::isinstance(value, &self.ctx.list_type()) { + let elements = if objtype::isinstance(value, &self.ctx.tuple_type()) + || objtype::isinstance(value, &self.ctx.list_type()) + { objsequence::get_elements(value).to_vec() } else { let iter = objiter::get_iter(self, value)?; From d4b82007dfc8db396b847a746abc26517e067883 Mon Sep 17 00:00:00 2001 From: janczer Date: Fri, 8 Feb 2019 20:34:33 +0100 Subject: [PATCH 136/153] Add floats.{__truediv__, __mul__} --- tests/snippets/floats.py | 3 +++ vm/src/obj/objfloat.rs | 42 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/tests/snippets/floats.py b/tests/snippets/floats.py index cf6a9a0c0..b2b2cad8c 100644 --- a/tests/snippets/floats.py +++ b/tests/snippets/floats.py @@ -15,3 +15,6 @@ assert b >= a assert c >= a assert not a >= b +assert a + b == 2.5 +assert a - c == 0 +assert a / c == 1 diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 1bb57e572..4e4b09807 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -266,6 +266,46 @@ fn float_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } } +fn float_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(i, Some(vm.ctx.float_type())), (i2, None)] + ); + let v1 = get_value(i); + if objtype::isinstance(i2, &vm.ctx.float_type) { + Ok(vm + .ctx + .new_float(v1 / get_value(i2))) + } else if objtype::isinstance(i2, &vm.ctx.int_type) { + Ok(vm + .ctx + .new_float(v1 / objint::get_value(i2).to_f64().unwrap())) + } else { + Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow()))) + } +} + +fn float_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(i, Some(vm.ctx.float_type())), (i2, None)] + ); + let v1 = get_value(i); + if objtype::isinstance(i2, &vm.ctx.float_type) { + Ok(vm + .ctx + .new_float(v1 * get_value(i2))) + } else if objtype::isinstance(i2, &vm.ctx.int_type) { + Ok(vm + .ctx + .new_float(v1 * objint::get_value(i2).to_f64().unwrap())) + } else { + Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow()))) + } +} + pub fn init(context: &PyContext) { let float_type = &context.float_type; @@ -299,4 +339,6 @@ pub fn init(context: &PyContext) { "__doc__", context.new_str(float_doc.to_string()), ); + context.set_attr(&float_type, "__truediv__", context.new_rustfunc(float_truediv)); + context.set_attr(&float_type, "__mul__", context.new_rustfunc(float_mul)); } From 59706538f4071b2552f926d7bc7e0c8e303e0b52 Mon Sep 17 00:00:00 2001 From: janczer Date: Fri, 8 Feb 2019 21:56:07 +0100 Subject: [PATCH 137/153] Fix typo and fix fmt errors --- vm/src/obj/objfloat.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 4e4b09807..4067f395c 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -274,9 +274,7 @@ fn float_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); if objtype::isinstance(i2, &vm.ctx.float_type) { - Ok(vm - .ctx - .new_float(v1 / get_value(i2))) + Ok(vm.ctx.new_float(v1 / get_value(i2))) } else if objtype::isinstance(i2, &vm.ctx.int_type) { Ok(vm .ctx @@ -294,15 +292,13 @@ fn float_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let v1 = get_value(i); if objtype::isinstance(i2, &vm.ctx.float_type) { - Ok(vm - .ctx - .new_float(v1 * get_value(i2))) + Ok(vm.ctx.new_float(v1 * get_value(i2))) } else if objtype::isinstance(i2, &vm.ctx.int_type) { Ok(vm .ctx .new_float(v1 * objint::get_value(i2).to_f64().unwrap())) } else { - Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow()))) + Err(vm.new_type_error(format!("Cannot multiply {} and {}", i.borrow(), i2.borrow()))) } } @@ -339,6 +335,10 @@ pub fn init(context: &PyContext) { "__doc__", context.new_str(float_doc.to_string()), ); - context.set_attr(&float_type, "__truediv__", context.new_rustfunc(float_truediv)); + context.set_attr( + &float_type, + "__truediv__", + context.new_rustfunc(float_truediv), + ); context.set_attr(&float_type, "__mul__", context.new_rustfunc(float_mul)); } From ba19732fbb3b05eb5910e7ed5168bb3751e64053 Mon Sep 17 00:00:00 2001 From: janczer Date: Fri, 8 Feb 2019 22:09:42 +0100 Subject: [PATCH 138/153] Fix the fmt error --- vm/src/obj/objfloat.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 4067f395c..6d8c2ff73 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -298,7 +298,11 @@ fn float_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { .ctx .new_float(v1 * objint::get_value(i2).to_f64().unwrap())) } else { - Err(vm.new_type_error(format!("Cannot multiply {} and {}", i.borrow(), i2.borrow()))) + Err(vm.new_type_error(format!( + "Cannot multiply {} and {}", + i.borrow(), + i2.borrow() + ))) } } From 907dfb67702896ddd58e8437c28259aef02b2219 Mon Sep 17 00:00:00 2001 From: ben Date: Sat, 9 Feb 2019 12:07:04 +1300 Subject: [PATCH 139/153] Add slice type and use BigInts in slice payload. --- tests/snippets/builtin_slice.py | 45 ++++++++++++++++ vm/src/builtins.rs | 1 + vm/src/frame.rs | 14 ++--- vm/src/obj/mod.rs | 1 + vm/src/obj/objiter.rs | 3 +- vm/src/obj/objlist.rs | 9 ++-- vm/src/obj/objrange.rs | 22 +++++--- vm/src/obj/objsequence.rs | 72 ++++++++++++++++--------- vm/src/obj/objslice.rs | 95 +++++++++++++++++++++++++++++++++ vm/src/obj/objstr.rs | 10 ++-- vm/src/pyobject.rs | 15 ++++-- 11 files changed, 235 insertions(+), 52 deletions(-) create mode 100644 tests/snippets/builtin_slice.py create mode 100644 vm/src/obj/objslice.rs diff --git a/tests/snippets/builtin_slice.py b/tests/snippets/builtin_slice.py new file mode 100644 index 000000000..402a90133 --- /dev/null +++ b/tests/snippets/builtin_slice.py @@ -0,0 +1,45 @@ + +a = [] +assert a[:] == [] +assert a[:2**100] == [] +assert a[-2**100:] == [] +assert a[::2**100] == [] +assert a[10:20] == [] +assert a[-20:-10] == [] + +b = [1, 2] + +assert b[:] == [1, 2] +assert b[:2**100] == [1, 2] +assert b[-2**100:] == [1, 2] +assert b[2**100:] == [] +assert b[::2**100] == [1] +assert b[-10:1] == [1] + +slice_a = slice(5) +assert slice_a.start is None +assert slice_a.stop == 5 +assert slice_a.step is None + +slice_b = slice(1, 5) +assert slice_b.start == 1 +assert slice_b.stop == 5 +assert slice_b.step is None + +slice_c = slice(1, 5, 2) +assert slice_c.start == 1 +assert slice_c.stop == 5 +assert slice_c.step == 2 + + +class SubScript(object): + def __getitem__(self, item): + assert type(item) == slice + + def __setitem__(self, key, value): + assert type(key) == slice + + +ss = SubScript() +_ = ss[:] +ss[:1] = 1 diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index f8df09a62..cad71c42e 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -677,6 +677,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "repr", ctx.new_rustfunc(builtin_repr)); ctx.set_attr(&py_mod, "set", ctx.set_type()); ctx.set_attr(&py_mod, "setattr", ctx.new_rustfunc(builtin_setattr)); + ctx.set_attr(&py_mod, "slice", ctx.slice_type()); ctx.set_attr(&py_mod, "staticmethod", ctx.staticmethod_type()); ctx.set_attr(&py_mod, "str", ctx.str_type()); ctx.set_attr(&py_mod, "sum", ctx.new_rustfunc(builtin_sum)); diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 54fbf415b..b1b7952e2 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -20,7 +20,7 @@ use super::pyobject::{ PyResult, TypeProtocol, }; use super::vm::VirtualMachine; -use num_traits::ToPrimitive; +use num_bigint::BigInt; #[derive(Clone, Debug)] enum Block { @@ -262,22 +262,22 @@ impl Frame { assert!(*size == 2 || *size == 3); let elements = self.pop_multiple(*size); - let mut out: Vec> = elements + let mut out: Vec> = elements .into_iter() .map(|x| match x.borrow().payload { - PyObjectPayload::Integer { ref value } => Some(value.to_i32().unwrap()), + PyObjectPayload::Integer { ref value } => Some(value.clone()), PyObjectPayload::None => None, _ => panic!("Expect Int or None as BUILD_SLICE arguments, got {:?}", x), }) .collect(); - let start = out[0]; - let stop = out[1]; - let step = if out.len() == 3 { out[2] } else { None }; + let start = out[0].take(); + let stop = out[1].take(); + let step = if out.len() == 3 { out[2].take() } else { None }; let obj = PyObject::new( PyObjectPayload::Slice { start, stop, step }, - vm.ctx.type_type(), + vm.ctx.slice_type(), ); self.push_value(obj); Ok(None) diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index 8b93ee174..f08998fcb 100644 --- a/vm/src/obj/mod.rs +++ b/vm/src/obj/mod.rs @@ -22,6 +22,7 @@ pub mod objproperty; pub mod objrange; pub mod objsequence; pub mod objset; +pub mod objslice; pub mod objstr; pub mod objsuper; pub mod objtuple; diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 55b206511..40633e984 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -9,7 +9,6 @@ use super::super::vm::VirtualMachine; use super::objbool; // use super::objstr; use super::objtype; // Required for arg_check! to use isinstance -use num_bigint::BigInt; /* * This helper function is called at multiple places. First, it is called @@ -146,7 +145,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } PyObjectPayload::Range { ref range } => { - if let Some(int) = range.get(BigInt::from(*position)) { + if let Some(int) = range.get(*position) { *position += 1; Ok(vm.ctx.new_int(int)) } else { diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index ed8e552bc..369ed9fc5 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -21,9 +21,12 @@ fn set_item( ) -> PyResult { if objtype::isinstance(&idx, &vm.ctx.int_type()) { let value = objint::get_value(&idx).to_i32().unwrap(); - let pos_index = l.get_pos(value); - l[pos_index] = obj; - Ok(vm.get_none()) + if let Some(pos_index) = l.get_pos(value) { + l[pos_index] = obj; + Ok(vm.get_none()) + } else { + Err(vm.new_index_error("list index out of range".to_string())) + } } else { panic!( "TypeError: indexing type {:?} with index {:?} is not supported (yet?)", diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 8d320084c..91b875ae3 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -7,6 +7,7 @@ use super::objtype; use num_bigint::{BigInt, Sign}; use num_integer::Integer; use num_traits::{One, Signed, ToPrimitive, Zero}; +use std::ops::Mul; #[derive(Debug, Clone)] pub struct RangeType { @@ -77,8 +78,11 @@ impl RangeType { } #[inline] - pub fn get(&self, index: BigInt) -> Option { - let result = self.start.clone() + self.step.clone() * index; + pub fn get<'a, T>(&'a self, index: T) -> Option + where + &'a BigInt: Mul, + { + let result = &self.start + &self.step * index; if (self.forward() && !self.is_empty() && result < self.end) || (!self.forward() && !self.is_empty() && result > self.end) @@ -199,15 +203,19 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match subscript.borrow().payload { PyObjectPayload::Integer { ref value } => { - if let Some(int) = zrange.get(value.clone()) { + if let Some(int) = zrange.get(value) { Ok(vm.ctx.new_int(int)) } else { Err(vm.new_index_error("range object index out of range".to_string())) } } - PyObjectPayload::Slice { start, stop, step } => { + PyObjectPayload::Slice { + ref start, + ref stop, + ref step, + } => { let new_start = if let Some(int) = start { - if let Some(i) = zrange.get(int.into()) { + if let Some(i) = zrange.get(int) { i } else { zrange.start.clone() @@ -217,7 +225,7 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; let new_end = if let Some(int) = stop { - if let Some(i) = zrange.get(int.into()) { + if let Some(i) = zrange.get(int) { i } else { zrange.end @@ -227,7 +235,7 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { }; let new_step = if let Some(int) = step { - (int as i64) * zrange.step + int * zrange.step } else { zrange.step }; diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index d2fbb2a96..bc4d46008 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -2,7 +2,8 @@ use super::super::pyobject::{PyObject, PyObjectPayload, PyObjectRef, PyResult, T use super::super::vm::VirtualMachine; use super::objbool; use super::objint; -use num_traits::ToPrimitive; +use num_bigint::BigInt; +use num_traits::{Signed, ToPrimitive}; use std::cell::{Ref, RefMut}; use std::marker::Sized; use std::ops::{Deref, DerefMut}; @@ -11,22 +12,35 @@ pub trait PySliceableSequence { fn do_slice(&self, start: usize, stop: usize) -> Self; fn do_stepped_slice(&self, start: usize, stop: usize, step: usize) -> Self; fn len(&self) -> usize; - fn get_pos(&self, p: i32) -> usize { + fn get_pos(&self, p: i32) -> Option { if p < 0 { if -p as usize > self.len() { - // return something that is out of bounds so `get_item` raises an IndexError - self.len() + 1 + None } else { - self.len() - ((-p) as usize) + Some(self.len() - ((-p) as usize)) } - } else if p as usize > self.len() { - // This is for the slicing case where the end element is greater than the length of the - // sequence - self.len() + } else if p as usize >= self.len() { + None } else { - p as usize + Some(p as usize) } } + + fn get_slice_pos(&self, slice_pos: &BigInt) -> usize { + if let Some(pos) = slice_pos.to_i32() { + if let Some(index) = self.get_pos(pos) { + // within bounds + return index; + } + } + + if slice_pos.is_negative() { + 0 + } else { + self.len() + } + } + fn get_slice_items(&self, slice: &PyObjectRef) -> Self where Self: Sized, @@ -34,22 +48,31 @@ pub trait PySliceableSequence { // TODO: we could potentially avoid this copy and use slice match &(slice.borrow()).payload { PyObjectPayload::Slice { start, stop, step } => { - let start = match *start { - Some(start) => self.get_pos(start), - None => 0, + let start = if let Some(start) = start { + self.get_slice_pos(start) + } else { + 0 }; - let stop = match *stop { - Some(stop) => self.get_pos(stop), - None => self.len() as usize, + let stop = if let Some(stop) = stop { + self.get_slice_pos(stop) + } else { + self.len() }; - match *step { - None | Some(1) => self.do_slice(start, stop), - Some(num) => { - if num < 0 { - unimplemented!("negative step indexing not yet supported") - }; - self.do_stepped_slice(start, stop, num as usize) + if let Some(step) = step { + match step.to_i32() { + Some(1) => self.do_slice(start, stop), + Some(num) => self.do_stepped_slice(start, stop, num as usize), + None => self.do_slice( + start, + if start == self.len() { + start + } else { + start + 1 + }, + ), } + } else { + self.do_slice(start, stop) } } payload => panic!("get_slice_items called with non-slice: {:?}", payload), @@ -78,8 +101,7 @@ pub fn get_item( match &(subscript.borrow()).payload { PyObjectPayload::Integer { value } => match value.to_i32() { Some(value) => { - let pos_index = elements.to_vec().get_pos(value); - if pos_index < elements.len() { + if let Some(pos_index) = elements.to_vec().get_pos(value) { let obj = elements[pos_index].clone(); Ok(obj) } else { diff --git a/vm/src/obj/objslice.rs b/vm/src/obj/objslice.rs new file mode 100644 index 000000000..547e252ba --- /dev/null +++ b/vm/src/obj/objslice.rs @@ -0,0 +1,95 @@ +use super::super::pyobject::{ + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, +}; +use super::super::vm::VirtualMachine; +use super::objint; +use super::objtype; // Required for arg_check! to use isinstance +use num_bigint::BigInt; + +fn slice_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + no_kwargs!(vm, args); + let (cls, start, stop, step): ( + &PyObjectRef, + Option<&PyObjectRef>, + Option<&PyObjectRef>, + Option<&PyObjectRef>, + ) = match args.args.len() { + 0 | 1 => Err(vm.new_type_error("slice() must have at least one arguments.".to_owned())), + 2 => { + arg_check!( + vm, + args, + required = [ + (cls, Some(vm.ctx.type_type())), + (stop, Some(vm.ctx.int_type())) + ] + ); + Ok((cls, None, Some(stop), None)) + } + _ => { + arg_check!( + vm, + args, + required = [ + (cls, Some(vm.ctx.type_type())), + (start, Some(vm.ctx.int_type())), + (stop, Some(vm.ctx.int_type())) + ], + optional = [(step, Some(vm.ctx.int_type()))] + ); + Ok((cls, Some(start), Some(stop), step)) + } + }?; + Ok(PyObject::new( + PyObjectPayload::Slice { + start: start.map(|x| objint::get_value(x)), + stop: stop.map(|x| objint::get_value(x)), + step: step.map(|x| objint::get_value(x)), + }, + cls.clone(), + )) +} + +fn get_property_value(vm: &mut VirtualMachine, value: &Option) -> PyResult { + if let Some(value) = value { + Ok(vm.ctx.new_int(value.clone())) + } else { + Ok(vm.get_none()) + } +} + +fn slice_start(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(slice, Some(vm.ctx.slice_type()))]); + if let PyObjectPayload::Slice { start, .. } = &slice.borrow().payload { + get_property_value(vm, start) + } else { + panic!("Slice has incorrect payload."); + } +} + +fn slice_stop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(slice, Some(vm.ctx.slice_type()))]); + if let PyObjectPayload::Slice { stop, .. } = &slice.borrow().payload { + get_property_value(vm, stop) + } else { + panic!("Slice has incorrect payload."); + } +} + +fn slice_step(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(slice, Some(vm.ctx.slice_type()))]); + if let PyObjectPayload::Slice { step, .. } = &slice.borrow().payload { + get_property_value(vm, step) + } else { + panic!("Slice has incorrect payload."); + } +} + +pub fn init(context: &PyContext) { + let zip_type = &context.slice_type; + + context.set_attr(zip_type, "__new__", context.new_rustfunc(slice_new)); + context.set_attr(zip_type, "start", context.new_property(slice_start)); + context.set_attr(zip_type, "stop", context.new_property(slice_stop)); + context.set_attr(zip_type, "step", context.new_property(slice_step)); +} diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index fbdf58fc4..88561a899 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -1039,11 +1039,11 @@ pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResu match objint::get_value(&b).to_i32() { Some(pos) => { let graphemes = to_graphemes(value); - let idx = graphemes.get_pos(pos); - graphemes - .get(idx) - .map(|c| vm.new_str(c.to_string())) - .ok_or(vm.new_index_error("string index out of range".to_string())) + if let Some(idx) = graphemes.get_pos(pos) { + Ok(vm.new_str(graphemes[idx].to_string())) + } else { + Err(vm.new_index_error("string index out of range".to_string())) + } } None => { Err(vm.new_index_error("cannot fit 'int' into an index-sized integer".to_string())) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index d486bfaef..212757671 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -22,6 +22,7 @@ use super::obj::objobject; use super::obj::objproperty; use super::obj::objrange; use super::obj::objset; +use super::obj::objslice; use super::obj::objstr; use super::obj::objsuper; use super::obj::objtuple; @@ -136,6 +137,7 @@ pub struct PyContext { pub super_type: PyObjectRef, pub str_type: PyObjectRef, pub range_type: PyObjectRef, + pub slice_type: PyObjectRef, pub type_type: PyObjectRef, pub zip_type: PyObjectRef, pub function_type: PyObjectRef, @@ -216,6 +218,7 @@ impl PyContext { let memoryview_type = create_type("memoryview", &type_type, &object_type, &dict_type); let code_type = create_type("code", &type_type, &int_type, &dict_type); let range_type = create_type("range", &type_type, &object_type, &dict_type); + let slice_type = create_type("slice", &type_type, &object_type, &dict_type); let exceptions = exceptions::ExceptionZoo::new(&type_type, &object_type, &dict_type); let none = PyObject::new( @@ -260,6 +263,7 @@ impl PyContext { none, str_type, range_type, + slice_type, object: object_type, function_type, super_type, @@ -288,6 +292,7 @@ impl PyContext { objmemory::init(&context); objstr::init(&context); objrange::init(&context); + objslice::init(&context); objsuper::init(&context); objtuple::init(&context); objiter::init(&context); @@ -346,6 +351,10 @@ impl PyContext { self.range_type.clone() } + pub fn slice_type(&self) -> PyObjectRef { + self.slice_type.clone() + } + pub fn frozenset_type(&self) -> PyObjectRef { self.frozenset_type.clone() } @@ -920,9 +929,9 @@ pub enum PyObjectPayload { iterators: Vec, }, Slice { - start: Option, - stop: Option, - step: Option, + start: Option, + stop: Option, + step: Option, }, Range { range: objrange::RangeType, From 7abf02180a5c12f6ffd226bd2f56acf6877555d6 Mon Sep 17 00:00:00 2001 From: ben Date: Sat, 9 Feb 2019 15:48:07 +1300 Subject: [PATCH 140/153] implement slice negative step handling --- tests/snippets/builtin_slice.py | 32 ++++++++++ vm/src/obj/objsequence.rs | 103 ++++++++++++++++++++++---------- vm/src/obj/objstr.rs | 38 ++++++++++-- 3 files changed, 135 insertions(+), 38 deletions(-) diff --git a/tests/snippets/builtin_slice.py b/tests/snippets/builtin_slice.py index 402a90133..b7c3922c0 100644 --- a/tests/snippets/builtin_slice.py +++ b/tests/snippets/builtin_slice.py @@ -15,6 +15,38 @@ assert b[-2**100:] == [1, 2] assert b[2**100:] == [] assert b[::2**100] == [1] assert b[-10:1] == [1] +assert b[0:0] == [] +assert b[1:0] == [] + +try: + _ = b[::0] +except ValueError: + pass +else: + assert False, "Zero step slice should raise ValueError" + +assert b[::-1] == [2, 1] +assert b[1::-1] == [2, 1] +assert b[0::-1] == [1] +assert b[0:-5:-1] == [1] +assert b[:0:-1] == [2] +assert b[5:0:-1] == [2] + +c = list(range(10)) + +assert c[9:6:-3] == [9] +assert c[9::-3] == [9, 6, 3, 0] +assert c[9::-4] == [9, 5, 1] +assert c[8::-2**100] == [8] + +assert c[7:7:-2] == [] +assert c[7:8:-2] == [] + +d = "123456" + +assert d[3::-1] == "4321" +assert d[4::-3] == "52" + slice_a = slice(5) assert slice_a.start is None diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index bc4d46008..5d85b12a0 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -3,14 +3,17 @@ use super::super::vm::VirtualMachine; use super::objbool; use super::objint; use num_bigint::BigInt; -use num_traits::{Signed, ToPrimitive}; +use num_traits::{One, Signed, ToPrimitive, Zero}; use std::cell::{Ref, RefMut}; use std::marker::Sized; -use std::ops::{Deref, DerefMut}; +use std::ops::{Deref, DerefMut, Range}; pub trait PySliceableSequence { - fn do_slice(&self, start: usize, stop: usize) -> Self; - fn do_stepped_slice(&self, start: usize, stop: usize, step: usize) -> Self; + fn do_slice(&self, range: Range) -> Self; + fn do_slice_reverse(&self, range: Range) -> Self; + fn do_stepped_slice(&self, range: Range, step: usize) -> Self; + fn do_stepped_slice_reverse(&self, range: Range, step: usize) -> Self; + fn empty() -> Self; fn len(&self) -> usize; fn get_pos(&self, p: i32) -> Option { if p < 0 { @@ -41,38 +44,56 @@ pub trait PySliceableSequence { } } - fn get_slice_items(&self, slice: &PyObjectRef) -> Self + fn get_slice_range(&self, start: &Option, stop: &Option) -> Range { + let start = start.as_ref().map(|x| self.get_slice_pos(x)).unwrap_or(0); + let stop = stop + .as_ref() + .map(|x| self.get_slice_pos(x)) + .unwrap_or(self.len()); + + start..stop + } + + fn get_slice_items( + &self, + vm: &mut VirtualMachine, + slice: &PyObjectRef, + ) -> Result where Self: Sized, { // TODO: we could potentially avoid this copy and use slice match &(slice.borrow()).payload { PyObjectPayload::Slice { start, stop, step } => { - let start = if let Some(start) = start { - self.get_slice_pos(start) - } else { - 0 - }; - let stop = if let Some(stop) = stop { - self.get_slice_pos(stop) - } else { - self.len() - }; - if let Some(step) = step { - match step.to_i32() { - Some(1) => self.do_slice(start, stop), - Some(num) => self.do_stepped_slice(start, stop, num as usize), - None => self.do_slice( - start, - if start == self.len() { - start - } else { - start + 1 - }, - ), + let step = step.clone().unwrap_or(BigInt::one()); + if step.is_zero() { + Err(vm.new_value_error("slice step cannot be zero".to_string())) + } else if step.is_positive() { + let range = self.get_slice_range(start, stop); + if range.start < range.end { + match step.to_i32() { + Some(1) => Ok(self.do_slice(range)), + Some(num) => Ok(self.do_stepped_slice(range, num as usize)), + None => Ok(self.do_slice(range.start..range.start + 1)), + } + } else { + Ok(Self::empty()) } } else { - self.do_slice(start, stop) + // calculate the range for the reverse slice, first the bounds needs to be made + // exclusive around stop, the lower number + let start = start.as_ref().map(|x| x + 1); + let stop = stop.as_ref().map(|x| x + 1); + let range = self.get_slice_range(&stop, &start); + if range.start < range.end { + match (-step).to_i32() { + Some(1) => Ok(self.do_slice_reverse(range)), + Some(num) => Ok(self.do_stepped_slice_reverse(range, num as usize)), + None => Ok(self.do_slice(range.end - 1..range.end)), + } + } else { + Ok(Self::empty()) + } } } payload => panic!("get_slice_items called with non-slice: {:?}", payload), @@ -81,12 +102,28 @@ pub trait PySliceableSequence { } impl PySliceableSequence for Vec { - fn do_slice(&self, start: usize, stop: usize) -> Self { - self[start..stop].to_vec() + fn do_slice(&self, range: Range) -> Self { + self[range].to_vec() } - fn do_stepped_slice(&self, start: usize, stop: usize, step: usize) -> Self { - self[start..stop].iter().step_by(step).cloned().collect() + + fn do_slice_reverse(&self, range: Range) -> Self { + let mut slice = self[range].to_vec(); + slice.reverse(); + slice } + + fn do_stepped_slice(&self, range: Range, step: usize) -> Self { + self[range].iter().step_by(step).cloned().collect() + } + + fn do_stepped_slice_reverse(&self, range: Range, step: usize) -> Self { + self[range].iter().rev().step_by(step).cloned().collect() + } + + fn empty() -> Self { + Vec::new() + } + fn len(&self) -> usize { self.len() } @@ -116,7 +153,7 @@ pub fn get_item( PyObjectPayload::Slice { .. } => Ok(PyObject::new( match &(sequence.borrow()).payload { PyObjectPayload::Sequence { .. } => PyObjectPayload::Sequence { - elements: elements.to_vec().get_slice_items(&subscript), + elements: elements.to_vec().get_slice_items(vm, &subscript)?, }, ref payload => panic!("sequence get_item called for non-sequence: {:?}", payload), }, diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 88561a899..3ec520aed 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -8,6 +8,7 @@ use super::objsequence::PySliceableSequence; use super::objtype; use num_traits::ToPrimitive; use std::hash::{Hash, Hasher}; +use std::ops::Range; // rust's builtin to_lowercase isn't sufficient for casefold extern crate caseless; extern crate unicode_segmentation; @@ -1003,14 +1004,23 @@ fn str_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } impl PySliceableSequence for String { - fn do_slice(&self, start: usize, stop: usize) -> Self { + fn do_slice(&self, range: Range) -> Self { to_graphemes(self) - .get(start..stop) + .get(range) .map_or(String::default(), |c| c.join("")) } - fn do_stepped_slice(&self, start: usize, stop: usize, step: usize) -> Self { - if let Some(s) = to_graphemes(self).get(start..stop) { + fn do_slice_reverse(&self, range: Range) -> Self { + to_graphemes(self) + .get_mut(range) + .map_or(String::default(), |slice| { + slice.reverse(); + slice.join("") + }) + } + + fn do_stepped_slice(&self, range: Range, step: usize) -> Self { + if let Some(s) = to_graphemes(self).get(range) { return s .iter() .cloned() @@ -1021,6 +1031,23 @@ impl PySliceableSequence for String { String::default() } + fn do_stepped_slice_reverse(&self, range: Range, step: usize) -> Self { + if let Some(s) = to_graphemes(self).get(range) { + return s + .iter() + .rev() + .cloned() + .step_by(step) + .collect::>() + .join(""); + } + String::default() + } + + fn empty() -> Self { + String::default() + } + fn len(&self) -> usize { to_graphemes(self).len() } @@ -1052,7 +1079,8 @@ pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResu } else { match (*b.borrow()).payload { PyObjectPayload::Slice { .. } => { - Ok(vm.new_str(value.to_string().get_slice_items(&b).to_string())) + let string = value.to_string().get_slice_items(vm, &b)?; + Ok(vm.new_str(string)) } _ => panic!( "TypeError: indexing type {:?} with index {:?} is not supported (yet?)", From 587617d5e357bfa80e955ca99b3ed101df5379b6 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Sat, 9 Feb 2019 08:14:37 +0100 Subject: [PATCH 141/153] Add dict data type. (#380) * Add dict data type. * Fix formatting. * Implement review comments. --- vm/src/dictdatatype.rs | 230 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 vm/src/dictdatatype.rs diff --git a/vm/src/dictdatatype.rs b/vm/src/dictdatatype.rs new file mode 100644 index 000000000..3c4dee22a --- /dev/null +++ b/vm/src/dictdatatype.rs @@ -0,0 +1,230 @@ +use super::obj::objbool; +use super::obj::objint; +use super::pyobject::{IdProtocol, PyObjectRef, PyResult}; +use super::vm::VirtualMachine; +use num_traits::ToPrimitive; +/// Ordered dictionary implementation. +/// Inspired by: https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html +/// And: https://www.youtube.com/watch?v=p33CVV29OG8 +/// And: http://code.activestate.com/recipes/578375/ +use std::collections::HashMap; + +pub struct Dict { + size: usize, + indices: HashMap, + entries: Vec>, +} + +struct DictEntry { + hash: usize, + key: PyObjectRef, + value: PyObjectRef, +} + +impl Dict { + pub fn new() -> Self { + Dict { + size: 0, + indices: HashMap::new(), + entries: Vec::new(), + } + } + + /// Store a key + pub fn insert( + &mut self, + vm: &mut VirtualMachine, + key: &PyObjectRef, + value: PyObjectRef, + ) -> Result<(), PyObjectRef> { + match self.lookup(vm, key)? { + LookupResult::Existing(index) => { + // Update existing key + if let Some(ref mut entry) = self.entries[index] { + entry.value = value; + Ok(()) + } else { + panic!("Lookup returned invalid index into entries!"); + } + } + LookupResult::NewIndex { + hash_index, + hash_value, + } => { + // New key: + let entry = DictEntry { + hash: hash_value, + key: key.clone(), + value, + }; + let index = self.entries.len(); + self.entries.push(Some(entry)); + self.indices.insert(hash_index, index); + self.size += 1; + Ok(()) + } + } + } + + pub fn contains( + &self, + vm: &mut VirtualMachine, + key: &PyObjectRef, + ) -> Result { + if let LookupResult::Existing(_index) = self.lookup(vm, key)? { + Ok(true) + } else { + Ok(false) + } + } + + /// Retrieve a key + pub fn get(&self, vm: &mut VirtualMachine, key: &PyObjectRef) -> PyResult { + if let LookupResult::Existing(index) = self.lookup(vm, key)? { + if let Some(entry) = &self.entries[index] { + Ok(entry.value.clone()) + } else { + panic!("Lookup returned invalid index into entries!"); + } + } else { + let key_repr = vm.to_pystr(key)?; + Err(vm.new_value_error(format!("Key not found: {}", key_repr))) + } + } + + /// Delete a key + pub fn delete( + &mut self, + vm: &mut VirtualMachine, + key: &PyObjectRef, + ) -> Result<(), PyObjectRef> { + if let LookupResult::Existing(index) = self.lookup(vm, key)? { + self.entries[index] = None; + self.size -= 1; + Ok(()) + } else { + let key_repr = vm.to_pystr(key)?; + Err(vm.new_value_error(format!("Key not found: {}", key_repr))) + } + } + + pub fn len(&self) -> usize { + self.size + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn get_items(&self) -> Vec<(PyObjectRef, PyObjectRef)> { + self.entries + .iter() + .filter(|e| e.is_some()) + .map(|e| e.as_ref().unwrap()) + .map(|e| (e.key.clone(), e.value.clone())) + .collect() + } + + /// Lookup the index for the given key. + fn lookup( + &self, + vm: &mut VirtualMachine, + key: &PyObjectRef, + ) -> Result { + let hash_value = calc_hash(vm, key)?; + let perturb = hash_value; + let mut hash_index: usize = hash_value; + loop { + if self.indices.contains_key(&hash_index) { + // Now we have an index, lets check the key. + let index = self.indices[&hash_index]; + if let Some(entry) = &self.entries[index] { + // Okay, we have an entry at this place + if entry.key.is(key) { + // Literally the same object + break Ok(LookupResult::Existing(index)); + } else if entry.hash == hash_value { + if do_eq(vm, &entry.key, key)? { + break Ok(LookupResult::Existing(index)); + } else { + // entry mismatch. + } + } else { + // entry mismatch. + } + } else { + // Removed entry, continue search... + } + } else { + // Hash not in table, we are at free slot now. + break Ok(LookupResult::NewIndex { + hash_value, + hash_index, + }); + } + + // Update i to next probe location: + hash_index = hash_index + .wrapping_mul(5) + .wrapping_add(perturb) + .wrapping_add(1); + // warn!("Perturb value: {}", i); + } + } +} + +enum LookupResult { + NewIndex { + hash_value: usize, + hash_index: usize, + }, // return not found, index into indices + Existing(usize), // Existing record, index into entries +} + +fn calc_hash(vm: &mut VirtualMachine, key: &PyObjectRef) -> Result { + let hash = vm.call_method(key, "__hash__", vec![])?; + Ok(objint::get_value(&hash).to_usize().unwrap()) +} + +/// Invoke __eq__ on two keys +fn do_eq( + vm: &mut VirtualMachine, + key1: &PyObjectRef, + key2: &PyObjectRef, +) -> Result { + let result = vm._eq(key1, key2.clone())?; + Ok(objbool::get_value(&result)) +} + +#[cfg(test)] +mod tests { + use super::{Dict, VirtualMachine}; + + #[test] + fn test_insert() { + let mut vm = VirtualMachine::new(); + let mut dict = Dict::new(); + assert_eq!(0, dict.len()); + + let key1 = vm.new_bool(true); + let value1 = vm.new_str("abc".to_string()); + dict.insert(&mut vm, &key1, value1.clone()).unwrap(); + assert_eq!(1, dict.len()); + + let key2 = vm.new_str("x".to_string()); + let value2 = vm.new_str("def".to_string()); + dict.insert(&mut vm, &key2, value2.clone()).unwrap(); + assert_eq!(2, dict.len()); + + dict.insert(&mut vm, &key1, value2.clone()).unwrap(); + assert_eq!(2, dict.len()); + + dict.delete(&mut vm, &key1).unwrap(); + assert_eq!(1, dict.len()); + + dict.insert(&mut vm, &key1, value2).unwrap(); + assert_eq!(2, dict.len()); + + assert_eq!(true, dict.contains(&mut vm, &key1).unwrap()); + } +} From 2df9d799f5ca64a4fd5228a3df938266b3fbbe33 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 9 Feb 2019 10:18:05 +0200 Subject: [PATCH 142/153] Fix formatting errors --- vm/src/obj/objset.rs | 60 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index dfb46b4c2..2648fb4f3 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -127,26 +127,56 @@ pub fn set_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn set_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf != other}, false) + return set_compare_inner( + vm, + args, + &|zelf: usize, other: usize| -> bool { zelf != other }, + false, + ); } fn set_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf < other}, false) + return set_compare_inner( + vm, + args, + &|zelf: usize, other: usize| -> bool { zelf < other }, + false, + ); } fn set_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf <= other}, false) + return set_compare_inner( + vm, + args, + &|zelf: usize, other: usize| -> bool { zelf <= other }, + false, + ); } fn set_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf < other}, true) + return set_compare_inner( + vm, + args, + &|zelf: usize, other: usize| -> bool { zelf < other }, + true, + ); } fn set_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - return set_compare_inner(vm, args, &|zelf: usize, other: usize| -> bool {zelf <= other}, true) + return set_compare_inner( + vm, + args, + &|zelf: usize, other: usize| -> bool { zelf <= other }, + true, + ); } -fn set_compare_inner(vm: &mut VirtualMachine, args: PyFuncArgs, size_func: &Fn(usize, usize) -> bool, swap: bool) -> PyResult { +fn set_compare_inner( + vm: &mut VirtualMachine, + args: PyFuncArgs, + size_func: &Fn(usize, usize) -> bool, + swap: bool, +) -> PyResult { arg_check!( vm, args, @@ -156,13 +186,25 @@ fn set_compare_inner(vm: &mut VirtualMachine, args: PyFuncArgs, size_func: &Fn(u ] ); - let get_zelf = |swap: bool| -> &PyObjectRef {if swap {other} else {zelf}}; - let get_other = |swap: bool| -> &PyObjectRef {if swap {zelf} else {other}}; + let get_zelf = |swap: bool| -> &PyObjectRef { + if swap { + other + } else { + zelf + } + }; + let get_other = |swap: bool| -> &PyObjectRef { + if swap { + zelf + } else { + other + } + }; let zelf_elements = get_elements(get_zelf(swap)); let other_elements = get_elements(get_other(swap)); if size_func(zelf_elements.len(), other_elements.len()) { - return Ok(vm.new_bool(false)); + return Ok(vm.new_bool(false)); } for element in other_elements.iter() { match vm.call_method(get_zelf(swap), "__contains__", vec![element.1.clone()]) { From 79a7e5e42b22084319b912e0b49b76aa551ed840 Mon Sep 17 00:00:00 2001 From: klemens Date: Sat, 9 Feb 2019 12:00:46 +0100 Subject: [PATCH 143/153] spelling fixes --- parser/src/lexer.rs | 2 +- py_code_object/src/vm_old.rs | 4 ++-- src/main.rs | 2 +- vm/src/frame.rs | 2 +- vm/src/obj/objint.rs | 2 +- vm/src/obj/objstr.rs | 2 +- vm/src/stdlib/json.rs | 2 +- vm/src/stdlib/os.rs | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 0d721f7d1..58660a901 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -1,5 +1,5 @@ //! This module takes care of lexing python source text. This means source -//! code is translated into seperate tokens. +//! code is translated into separate tokens. pub use super::token::Tok; use num_bigint::BigInt; diff --git a/py_code_object/src/vm_old.rs b/py_code_object/src/vm_old.rs index aecefde46..a3747a946 100644 --- a/py_code_object/src/vm_old.rs +++ b/py_code_object/src/vm_old.rs @@ -56,7 +56,7 @@ impl VirtualMachine { } } - // Can we get rid of the code paramter? + // Can we get rid of the code parameter? fn make_frame(&self, code: PyCodeObject, callargs: HashMap>, globals: Option>>) -> Frame { //populate the globals and locals @@ -345,7 +345,7 @@ impl VirtualMachine { let exception = match argc { 1 => curr_frame.stack.pop().unwrap(), 0 | 2 | 3 => panic!("Not implemented!"), - _ => panic!("Invalid paramter for RAISE_VARARGS, must be between 0 to 3") + _ => panic!("Invalid parameter for RAISE_VARARGS, must be between 0 to 3") }; panic!("{:?}", exception); } diff --git a/src/main.rs b/src/main.rs index a5672e700..52be6656e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -177,7 +177,7 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { } loop { - // TODO: modules dont support getattr / setattr yet + // TODO: modules don't support getattr / setattr yet //let prompt = match vm.get_attribute(vm.sys_module.clone(), "ps1") { // Ok(value) => objstr::get_value(&value), // Err(_) => ">>>>> ".to_string(), diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 3b763b0af..ac118dcb7 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -504,7 +504,7 @@ impl Frame { let exception = match argc { 1 => self.pop_value(), 0 | 2 | 3 => panic!("Not implemented!"), - _ => panic!("Invalid paramter for RAISE_VARARGS, must be between 0 to 3"), + _ => panic!("Invalid parameter for RAISE_VARARGS, must be between 0 to 3"), }; if objtype::isinstance(&exception, &vm.ctx.exceptions.base_exception_type) { info!("Exception raised: {:?}", exception); diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 646dd13f9..06424e149 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -58,7 +58,7 @@ pub fn to_int( match i32::from_str_radix(&s, base) { Ok(v) => v.to_bigint().unwrap(), Err(err) => { - trace!("Error occured during int conversion {:?}", err); + trace!("Error occurred during int conversion {:?}", err); return Err(vm.new_value_error(format!( "invalid literal for int() with base {}: '{}'", base, s diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index c33f70326..d71cf3a69 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -494,7 +494,7 @@ fn str_islower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } -// doesn't implement keep new line delimeter just yet +// doesn't implement keep new line delimiter just yet fn str_splitlines(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]); let elements = get_value(&s) diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 1101a8713..9f9e705ec 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -52,7 +52,7 @@ impl<'s> serde::Serialize for PyObjectSerializer<'s> { } else if objtype::isinstance(self.pyobject, &self.ctx.int_type()) { let v = objint::get_value(self.pyobject); serializer.serialize_i64(v.to_i64().unwrap()) - // Allthough this may seem nice, it does not give the right result: + // Although this may seem nice, it does not give the right result: // v.serialize(serializer) } else if objtype::isinstance(self.pyobject, &self.ctx.list_type()) { let elements = objsequence::get_elements(self.pyobject); diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 4c54f5847..ac284bddc 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -54,7 +54,7 @@ pub fn os_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let raw_fileno = objint::get_value(&fileno); //The File type automatically closes when it goes out of scope. - //To enable us to close these file desciptors (and hence prevent leaks) + //To enable us to close these file descriptors (and hence prevent leaks) //we seek to create the relevant File and simply let it pass out of scope! rust_file(raw_fileno.to_i64().unwrap()); From 7b2508a7305ea3071a625fbe1c52a70485928ae5 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Thu, 7 Feb 2019 20:29:59 +0000 Subject: [PATCH 144/153] Start adding methods to code object. --- tests/snippets/code.py | 28 ++++++++++++++++++++++++++++ vm/src/obj/objcode.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 tests/snippets/code.py diff --git a/tests/snippets/code.py b/tests/snippets/code.py new file mode 100644 index 000000000..f0c2ee5fa --- /dev/null +++ b/tests/snippets/code.py @@ -0,0 +1,28 @@ +c1 = compile("1 + 1", "", 'eval') + +code_class = type(c1) + +def f(x, y, power=1): + z = x * y + return z ** power + +c2 = f.__code__ +assert type(c2) == code_class + +print(dir(c2)) + +assert c2.co_argcount == 3 +assert c2.co_cellvars == () +# assert isinstance(c2.co_code, bytes) +assert c2.co_consts == (None,) +assert "code.py" in c2.co_filename +assert c2.co_firstlineno == 5 +# assert isinstance(c2.co_flags, int) # 'OPTIMIZED, NEWLOCALS, NOFREE' +assert c2.co_freevars == () +assert c2.co_kwonlyargcount == 0 +# assert c2.co_lnotab == 0, c2.co_lnotab # b'\x00\x01' # Line number table +assert c2.co_name == 'f', c2.co_name +assert c2.co_names == (), c2.co_names # , c2.co_names +assert c2.co_nlocals == 4, c2.co_nlocals # +# assert c2.co_stacksize == ... 2 'co_stacksize', +assert c2.co_varnames == ('x', 'y', 'power', 'z'), c2.co_varnames diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 70d9f2d54..54f10649b 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -13,6 +13,11 @@ pub fn init(context: &PyContext) { let code_type = &context.code_type; context.set_attr(code_type, "__new__", context.new_rustfunc(code_new)); context.set_attr(code_type, "__repr__", context.new_rustfunc(code_repr)); + context.set_attr( + code_type, + "co_argcount", + context.new_member_descriptor(code_co_argcount), + ); } /// Extract rust bytecode object from a python code object. @@ -48,3 +53,24 @@ fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let repr = format!("", file, line); Ok(vm.new_str(repr)) } + +fn get_value(obj: &PyObjectRef) -> bytecode::CodeObject { + if let PyObjectPayload::Code { code } = &obj.borrow().payload { + code.clone() + } else { + panic!("Inner error getting code {:?}", obj) + } +} + +fn code_co_argcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [ + (zelf, Some(vm.ctx.code_type())), + (_cls, Some(vm.ctx.type_type())) + ] + ); + let code_obj = get_value(zelf); + Ok(vm.ctx.new_int(code_obj.arg_names.len())) +} From 7d08867419cb8ed5d7dad5a15ad288fee15c6e00 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 8 Feb 2019 17:59:27 +0000 Subject: [PATCH 145/153] Record first line number in code object. --- vm/src/bytecode.rs | 3 +++ vm/src/compile.rs | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index 77b12917a..55c339c95 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -23,6 +23,7 @@ pub struct CodeObject { pub kwonlyarg_names: Vec, pub varkeywords: Option>, // **kwargs or ** pub source_path: Option, + pub first_line_number: usize, pub obj_name: String, // Name of the object that created this code object pub is_generator: bool, } @@ -34,6 +35,7 @@ impl CodeObject { kwonlyarg_names: Vec, varkeywords: Option>, source_path: Option, + first_line_number: usize, obj_name: String, ) -> CodeObject { CodeObject { @@ -45,6 +47,7 @@ impl CodeObject { kwonlyarg_names, varkeywords, source_path, + first_line_number, obj_name, is_generator: false, } diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 9050e59ce..a8e3a4623 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -79,12 +79,14 @@ impl Compiler { } fn push_new_code_object(&mut self, source_path: Option, obj_name: String) { + let line_number = self.get_source_line_number(); self.code_object_stack.push(CodeObject::new( Vec::new(), None, Vec::new(), None, source_path.clone(), + line_number, obj_name, )); } @@ -453,12 +455,14 @@ impl Compiler { } => { self.prepare_decorators(decorator_list)?; self.emit(Instruction::LoadBuildClass); + let line_number = self.get_source_line_number(); self.code_object_stack.push(CodeObject::new( vec![String::from("__locals__")], None, vec![], None, self.source_path.clone(), + line_number, name.clone(), )); self.emit(Instruction::LoadName { @@ -653,12 +657,14 @@ impl Compiler { }); } + let line_number = self.get_source_line_number(); self.code_object_stack.push(CodeObject::new( args.args.clone(), args.vararg.clone(), args.kwonlyargs.clone(), args.kwarg.clone(), self.source_path.clone(), + line_number, name.to_string(), )); @@ -1162,6 +1168,7 @@ impl Compiler { } .to_string(); + let line_number = self.get_source_line_number(); // Create magnificent function : self.code_object_stack.push(CodeObject::new( vec![".0".to_string()], @@ -1169,6 +1176,7 @@ impl Compiler { vec![], None, self.source_path.clone(), + line_number, name.clone(), )); @@ -1338,6 +1346,10 @@ impl Compiler { self.current_source_location = location.clone(); } + fn get_source_line_number(&mut self) -> usize { + self.current_source_location.get_row() + } + fn mark_generator(&mut self) { self.current_code_object().is_generator = true; } From 8116dae65fecff0c853935a1d6e865afc73fa504 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 8 Feb 2019 17:59:56 +0000 Subject: [PATCH 146/153] Add some co_* methods to code objects. --- vm/src/frame.rs | 2 +- vm/src/obj/objcode.rs | 89 ++++++++++++++++++++++++++++++------------- vm/src/vm.rs | 4 +- 3 files changed, 66 insertions(+), 29 deletions(-) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index b1b7952e2..cd93e2ca9 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -70,7 +70,7 @@ impl Frame { // locals.extend(callargs); Frame { - code: objcode::copy_code(&code), + code: objcode::get_value(&code), stack: vec![], blocks: vec![], // save the callargs as locals diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 54f10649b..5e86ea55d 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -4,7 +4,7 @@ use super::super::bytecode; use super::super::pyobject::{ - PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, }; use super::super::vm::VirtualMachine; use super::objtype; @@ -18,15 +18,33 @@ pub fn init(context: &PyContext) { "co_argcount", context.new_member_descriptor(code_co_argcount), ); + context.set_attr( + code_type, + "co_cellvars", + context.new_member_descriptor(code_co_cellvars), + ); + context.set_attr( + code_type, + "co_consts", + context.new_member_descriptor(code_co_consts), + ); + context.set_attr( + code_type, + "co_filename", + context.new_member_descriptor(code_co_filename), + ); + context.set_attr( + code_type, + "co_firstlineno", + context.new_member_descriptor(code_co_firstlineno), + ); } -/// Extract rust bytecode object from a python code object. -pub fn copy_code(code_obj: &PyObjectRef) -> bytecode::CodeObject { - let code_obj = code_obj.borrow(); - if let PyObjectPayload::Code { ref code } = code_obj.payload { +pub fn get_value(obj: &PyObjectRef) -> bytecode::CodeObject { + if let PyObjectPayload::Code { code } = &obj.borrow().payload { code.clone() } else { - panic!("Must be code obj"); + panic!("Inner error getting code {:?}", obj) } } @@ -39,30 +57,24 @@ fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.code_type()))]); // Fetch actual code: - let code = copy_code(o); + let code = get_value(o); - let file = if let Some(source_path) = code.source_path { - format!(", file {}", source_path) - } else { - String::new() - }; + let file = code.source_path.unwrap_or_else(|| String::new()); - // TODO: fetch proper line info from code object - let line = ", line 1".to_string(); - - let repr = format!("", file, line); + let repr = format!( + "", + code.obj_name, + o.get_id(), + file, + code.first_line_number + ); Ok(vm.new_str(repr)) } -fn get_value(obj: &PyObjectRef) -> bytecode::CodeObject { - if let PyObjectPayload::Code { code } = &obj.borrow().payload { - code.clone() - } else { - panic!("Inner error getting code {:?}", obj) - } -} - -fn code_co_argcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { +fn member_code_obj( + vm: &mut VirtualMachine, + args: PyFuncArgs, +) -> Result { arg_check!( vm, args, @@ -71,6 +83,31 @@ fn code_co_argcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { (_cls, Some(vm.ctx.type_type())) ] ); - let code_obj = get_value(zelf); + Ok(get_value(zelf)) +} + +fn code_co_argcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let code_obj = member_code_obj(vm, args)?; Ok(vm.ctx.new_int(code_obj.arg_names.len())) } + +fn code_co_cellvars(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let _code_obj = member_code_obj(vm, args)?; + Ok(vm.ctx.new_tuple(vec![])) +} + +fn code_co_consts(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let _code_obj = member_code_obj(vm, args)?; + Ok(vm.ctx.new_tuple(vec![vm.get_none()])) +} + +fn code_co_filename(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let code_obj = member_code_obj(vm, args)?; + let source_path = code_obj.source_path.unwrap_or_else(|| String::new()); + Ok(vm.new_str(source_path)) +} + +fn code_co_firstlineno(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let code_obj = member_code_obj(vm, args)?; + Ok(vm.ctx.new_int(code_obj.first_line_number)) +} diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 435e7bcbc..cd7567c8e 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -11,7 +11,7 @@ use std::collections::hash_map::HashMap; use super::builtins; use super::bytecode; use super::frame::Frame; -use super::obj::objcode::copy_code; +use super::obj::objcode; use super::obj::objgenerator; use super::obj::objiter; use super::obj::objsequence; @@ -282,7 +282,7 @@ impl VirtualMachine { defaults: &PyObjectRef, args: PyFuncArgs, ) -> PyResult { - let code_object = copy_code(code); + let code_object = objcode::get_value(code); let scope = self.ctx.new_scope(Some(scope.clone())); self.fill_scope_from_args(&code_object, &scope, args, defaults)?; From 1869a9f1ed74d7120d94b965c3a8943be699979c Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 8 Feb 2019 18:00:58 +0000 Subject: [PATCH 147/153] Passing version of code.py tests. --- tests/snippets/code.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/tests/snippets/code.py b/tests/snippets/code.py index f0c2ee5fa..95d5910a2 100644 --- a/tests/snippets/code.py +++ b/tests/snippets/code.py @@ -7,22 +7,21 @@ def f(x, y, power=1): return z ** power c2 = f.__code__ +# print(c2) assert type(c2) == code_class - -print(dir(c2)) - +# print(dir(c2)) assert c2.co_argcount == 3 assert c2.co_cellvars == () # assert isinstance(c2.co_code, bytes) assert c2.co_consts == (None,) assert "code.py" in c2.co_filename -assert c2.co_firstlineno == 5 +assert c2.co_firstlineno == 5, str(c2.co_firstlineno) # assert isinstance(c2.co_flags, int) # 'OPTIMIZED, NEWLOCALS, NOFREE' -assert c2.co_freevars == () -assert c2.co_kwonlyargcount == 0 +# assert c2.co_freevars == () +# assert c2.co_kwonlyargcount == 0 # assert c2.co_lnotab == 0, c2.co_lnotab # b'\x00\x01' # Line number table -assert c2.co_name == 'f', c2.co_name -assert c2.co_names == (), c2.co_names # , c2.co_names -assert c2.co_nlocals == 4, c2.co_nlocals # +# assert c2.co_name == 'f', c2.co_name +# assert c2.co_names == (), c2.co_names # , c2.co_names +# assert c2.co_nlocals == 4, c2.co_nlocals # # assert c2.co_stacksize == ... 2 'co_stacksize', -assert c2.co_varnames == ('x', 'y', 'power', 'z'), c2.co_varnames +# assert c2.co_varnames == ('x', 'y', 'power', 'z'), c2.co_varnames From 769b889097420cde24560e8bdbcbaffa18c822c1 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 8 Feb 2019 18:24:37 +0000 Subject: [PATCH 148/153] Source path should always be set, make it non-optional for code object. --- src/main.rs | 8 ++++---- vm/src/builtins.rs | 6 +++--- vm/src/bytecode.rs | 4 ++-- vm/src/compile.rs | 14 +++++++------- vm/src/eval.rs | 11 ++++++++--- vm/src/frame.rs | 28 +++++++++------------------- vm/src/import.rs | 2 +- vm/src/obj/objcode.rs | 8 ++------ wasm/lib/src/lib.rs | 2 +- 9 files changed, 37 insertions(+), 46 deletions(-) diff --git a/src/main.rs b/src/main.rs index a5672e700..b5e7ef6e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -68,7 +68,7 @@ fn main() { handle_exception(&mut vm, result); } -fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: Option) -> PyResult { +fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: String) -> PyResult { let code_obj = compile::compile(vm, source, &compile::Mode::Exec, source_path)?; // trace!("Code object: {:?}", code_obj.borrow()); let builtins = vm.get_builtin_scope(); @@ -91,7 +91,7 @@ fn run_command(vm: &mut VirtualMachine, mut source: String) -> PyResult { // This works around https://github.com/RustPython/RustPython/issues/17 source.push_str("\n"); - _run_string(vm, &source, None) + _run_string(vm, &source, "".to_string()) } fn run_module(vm: &mut VirtualMachine, module: &str) -> PyResult { @@ -105,7 +105,7 @@ fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult { // Parse an ast from it: let filepath = Path::new(script_file); match parser::read_file(filepath) { - Ok(source) => _run_string(vm, &source, Some(filepath.to_str().unwrap().to_string())), + Ok(source) => _run_string(vm, &source, filepath.to_str().unwrap().to_string()), Err(msg) => { error!("Parsing went horribly wrong: {}", msg); std::process::exit(1); @@ -114,7 +114,7 @@ fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult { } fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool { - match compile::compile(vm, source, &compile::Mode::Single, None) { + match compile::compile(vm, source, &compile::Mode::Single, "".to_string()) { Ok(code) => { match vm.run_code_obj(code, scope) { Ok(_value) => { diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 355ae3a4b..75cd7afaf 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -150,7 +150,7 @@ fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let filename = objstr::get_value(filename); - compile::compile(vm, &source, &mode, Some(filename)) + compile::compile(vm, &source, &mode, filename) } fn builtin_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -200,7 +200,7 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let source = objstr::get_value(source); // TODO: fix this newline bug: let source = format!("{}\n", source); - compile::compile(vm, &source, &mode, None)? + compile::compile(vm, &source, &mode, "".to_string())? } else { return Err(vm.new_type_error("code argument must be str or code object".to_string())); }; @@ -246,7 +246,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let source = objstr::get_value(source); // TODO: fix this newline bug: let source = format!("{}\n", source); - compile::compile(vm, &source, &mode, None)? + compile::compile(vm, &source, &mode, "".to_string())? } else if objtype::isinstance(source, &vm.ctx.code_type()) { source.clone() } else { diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index 55c339c95..c0309f841 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -22,7 +22,7 @@ pub struct CodeObject { pub varargs: Option>, // *args or * pub kwonlyarg_names: Vec, pub varkeywords: Option>, // **kwargs or ** - pub source_path: Option, + pub source_path: String, pub first_line_number: usize, pub obj_name: String, // Name of the object that created this code object pub is_generator: bool, @@ -34,7 +34,7 @@ impl CodeObject { varargs: Option>, kwonlyarg_names: Vec, varkeywords: Option>, - source_path: Option, + source_path: String, first_line_number: usize, obj_name: String, ) -> CodeObject { diff --git a/vm/src/compile.rs b/vm/src/compile.rs index a8e3a4623..1aa8cce27 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -24,10 +24,10 @@ pub fn compile( vm: &mut VirtualMachine, source: &str, mode: &Mode, - source_path: Option, + source_path: String, ) -> PyResult { let mut compiler = Compiler::new(); - compiler.source_path = source_path.clone(); + compiler.source_path = Some(source_path.clone()); compiler.push_new_code_object(source_path, "".to_string()); let syntax_error = vm.context().exceptions.syntax_error.clone(); let result = match mode { @@ -78,7 +78,7 @@ impl Compiler { } } - fn push_new_code_object(&mut self, source_path: Option, obj_name: String) { + fn push_new_code_object(&mut self, source_path: String, obj_name: String) { let line_number = self.get_source_line_number(); self.code_object_stack.push(CodeObject::new( Vec::new(), @@ -461,7 +461,7 @@ impl Compiler { None, vec![], None, - self.source_path.clone(), + self.source_path.clone().unwrap(), line_number, name.clone(), )); @@ -663,7 +663,7 @@ impl Compiler { args.vararg.clone(), args.kwonlyargs.clone(), args.kwarg.clone(), - self.source_path.clone(), + self.source_path.clone().unwrap(), line_number, name.to_string(), )); @@ -1175,7 +1175,7 @@ impl Compiler { None, vec![], None, - self.source_path.clone(), + self.source_path.clone().unwrap(), line_number, name.clone(), )); @@ -1364,7 +1364,7 @@ mod tests { use rustpython_parser::parser; fn compile_exec(source: &str) -> CodeObject { let mut compiler = Compiler::new(); - compiler.push_new_code_object(Option::None, "".to_string()); + compiler.push_new_code_object("source_path".to_string(), "".to_string()); let ast = parser::parse_program(&source.to_string()).unwrap(); compiler.compile_program(&ast).unwrap(); compiler.pop_code_object() diff --git a/vm/src/eval.rs b/vm/src/eval.rs index f5f690e95..ef27e63af 100644 --- a/vm/src/eval.rs +++ b/vm/src/eval.rs @@ -4,8 +4,13 @@ use super::compile; use super::pyobject::{PyObjectRef, PyResult}; use super::vm::VirtualMachine; -pub fn eval(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> PyResult { - match compile::compile(vm, source, &compile::Mode::Eval, None) { +pub fn eval( + vm: &mut VirtualMachine, + source: &str, + scope: PyObjectRef, + source_path: &str, +) -> PyResult { + match compile::compile(vm, source, &compile::Mode::Eval, source_path.to_string()) { Ok(bytecode) => { debug!("Code object: {:?}", bytecode); vm.run_code_obj(bytecode, scope) @@ -24,7 +29,7 @@ mod tests { let source = String::from("print('Hello world')\n"); let mut vm = VirtualMachine::new(); let vars = vm.context().new_scope(None); - let _result = eval(&mut vm, &source, vars); + let _result = eval(&mut vm, &source, vars, ""); // TODO: check result? //assert_eq!( diff --git a/vm/src/frame.rs b/vm/src/frame.rs index cd93e2ca9..3971a6d2a 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -88,11 +88,7 @@ impl Frame { } pub fn run_frame(&mut self, vm: &mut VirtualMachine) -> Result { - let filename = if let Some(source_path) = &self.code.source_path { - source_path.to_string() - } else { - "".to_string() - }; + let filename = &self.code.source_path.to_string(); let prev_frame = mem::replace(&mut vm.current_frame, Some(vm.ctx.new_frame(self.clone()))); @@ -661,13 +657,10 @@ impl Frame { module: &str, symbol: &Option, ) -> FrameResult { - let current_path = match &self.code.source_path { - Some(source_path) => { - let mut source_pathbuf = PathBuf::from(source_path); - source_pathbuf.pop(); - source_pathbuf - } - None => PathBuf::from("."), + let current_path = { + let mut source_pathbuf = PathBuf::from(&self.code.source_path); + source_pathbuf.pop(); + source_pathbuf }; let obj = import(vm, current_path, module, symbol)?; @@ -678,13 +671,10 @@ impl Frame { } fn import_star(&mut self, vm: &mut VirtualMachine, module: &str) -> FrameResult { - let current_path = match &self.code.source_path { - Some(source_path) => { - let mut source_pathbuf = PathBuf::from(source_path); - source_pathbuf.pop(); - source_pathbuf - } - None => PathBuf::from("."), + let current_path = { + let mut source_pathbuf = PathBuf::from(&self.code.source_path); + source_pathbuf.pop(); + source_pathbuf }; // Grab all the names from the module and put them in the context diff --git a/vm/src/import.rs b/vm/src/import.rs index be8807881..d7393bd67 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -35,7 +35,7 @@ fn import_uncached_module( vm, &source, &compile::Mode::Exec, - Some(filepath.to_str().unwrap().to_string()), + filepath.to_str().unwrap().to_string(), )?; // trace!("Code object: {:?}", code_obj); diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 5e86ea55d..67016eb43 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -56,16 +56,12 @@ fn code_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(o, Some(vm.ctx.code_type()))]); - // Fetch actual code: let code = get_value(o); - - let file = code.source_path.unwrap_or_else(|| String::new()); - let repr = format!( "", code.obj_name, o.get_id(), - file, + code.source_path, code.first_line_number ); Ok(vm.new_str(repr)) @@ -103,7 +99,7 @@ fn code_co_consts(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn code_co_filename(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let code_obj = member_code_obj(vm, args)?; - let source_path = code_obj.source_path.unwrap_or_else(|| String::new()); + let source_path = code_obj.source_path; Ok(vm.new_str(source_path)) } diff --git a/wasm/lib/src/lib.rs b/wasm/lib/src/lib.rs index f7da7edd1..7ad3f1956 100644 --- a/wasm/lib/src/lib.rs +++ b/wasm/lib/src/lib.rs @@ -123,7 +123,7 @@ fn eval(vm: &mut VirtualMachine, source: &str, vars: PyObjectRef) -> PyResult { source.push('\n'); } - let code_obj = compile::compile(vm, &source, &compile::Mode::Exec, None)?; + let code_obj = compile::compile(vm, &source, &compile::Mode::Exec, "".to_string())?; vm.run_code_obj(code_obj, vars) } From 6539f078184f516732132ade6d10a498addaedd1 Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Fri, 8 Feb 2019 18:24:59 +0000 Subject: [PATCH 149/153] @generated comment added to Cargo.lock --- Cargo.lock | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 25a156c8c..aa31678ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "aho-corasick" version = "0.6.4" From 5c5d27203ab49cdeab957686b6108023d839544c Mon Sep 17 00:00:00 2001 From: Adam Kelly Date: Sat, 9 Feb 2019 12:14:53 +0000 Subject: [PATCH 150/153] Support for all co_* that can implemented currently. --- tests/snippets/code.py | 21 +++++++-------- vm/src/obj/objcode.rs | 58 +++++++++++++++++------------------------- 2 files changed, 34 insertions(+), 45 deletions(-) diff --git a/tests/snippets/code.py b/tests/snippets/code.py index 95d5910a2..ab3353e3b 100644 --- a/tests/snippets/code.py +++ b/tests/snippets/code.py @@ -2,26 +2,27 @@ c1 = compile("1 + 1", "", 'eval') code_class = type(c1) -def f(x, y, power=1): +def f(x, y, *args, power=1, **kwargs): + assert code_class == type(c1) z = x * y return z ** power c2 = f.__code__ # print(c2) assert type(c2) == code_class -# print(dir(c2)) -assert c2.co_argcount == 3 -assert c2.co_cellvars == () +print(dir(c2)) +assert c2.co_argcount == 2 +# assert c2.co_cellvars == () # assert isinstance(c2.co_code, bytes) -assert c2.co_consts == (None,) +# assert c2.co_consts == (None,) assert "code.py" in c2.co_filename assert c2.co_firstlineno == 5, str(c2.co_firstlineno) # assert isinstance(c2.co_flags, int) # 'OPTIMIZED, NEWLOCALS, NOFREE' -# assert c2.co_freevars == () -# assert c2.co_kwonlyargcount == 0 +# assert c2.co_freevars == (), str(c2.co_freevars) +assert c2.co_kwonlyargcount == 1, (c2.co_kwonlyargcount) # assert c2.co_lnotab == 0, c2.co_lnotab # b'\x00\x01' # Line number table -# assert c2.co_name == 'f', c2.co_name -# assert c2.co_names == (), c2.co_names # , c2.co_names +assert c2.co_name == 'f', c2.co_name +# assert c2.co_names == ('code_class', 'type', 'c1', 'AssertionError'), c2.co_names # , c2.co_names # assert c2.co_nlocals == 4, c2.co_nlocals # -# assert c2.co_stacksize == ... 2 'co_stacksize', +# assert c2.co_stacksize == 2, 'co_stacksize', # assert c2.co_varnames == ('x', 'y', 'power', 'z'), c2.co_varnames diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index 67016eb43..2649c1190 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -13,31 +13,19 @@ pub fn init(context: &PyContext) { let code_type = &context.code_type; context.set_attr(code_type, "__new__", context.new_rustfunc(code_new)); context.set_attr(code_type, "__repr__", context.new_rustfunc(code_repr)); - context.set_attr( - code_type, - "co_argcount", - context.new_member_descriptor(code_co_argcount), - ); - context.set_attr( - code_type, - "co_cellvars", - context.new_member_descriptor(code_co_cellvars), - ); - context.set_attr( - code_type, - "co_consts", - context.new_member_descriptor(code_co_consts), - ); - context.set_attr( - code_type, - "co_filename", - context.new_member_descriptor(code_co_filename), - ); - context.set_attr( - code_type, - "co_firstlineno", - context.new_member_descriptor(code_co_firstlineno), - ); + + for (name, f) in vec![ + ( + "co_argcount", + code_co_argcount as fn(&mut VirtualMachine, PyFuncArgs) -> PyResult, + ), + ("co_filename", code_co_filename), + ("co_firstlineno", code_co_firstlineno), + ("co_kwonlyargcount", code_co_kwonlyargcount), + ("co_name", code_co_name), + ] { + context.set_attr(code_type, name, context.new_member_descriptor(f)) + } } pub fn get_value(obj: &PyObjectRef) -> bytecode::CodeObject { @@ -87,16 +75,6 @@ fn code_co_argcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_int(code_obj.arg_names.len())) } -fn code_co_cellvars(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - let _code_obj = member_code_obj(vm, args)?; - Ok(vm.ctx.new_tuple(vec![])) -} - -fn code_co_consts(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - let _code_obj = member_code_obj(vm, args)?; - Ok(vm.ctx.new_tuple(vec![vm.get_none()])) -} - fn code_co_filename(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let code_obj = member_code_obj(vm, args)?; let source_path = code_obj.source_path; @@ -107,3 +85,13 @@ fn code_co_firstlineno(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let code_obj = member_code_obj(vm, args)?; Ok(vm.ctx.new_int(code_obj.first_line_number)) } + +fn code_co_kwonlyargcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let code_obj = member_code_obj(vm, args)?; + Ok(vm.ctx.new_int(code_obj.kwonlyarg_names.len())) +} + +fn code_co_name(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + let code_obj = member_code_obj(vm, args)?; + Ok(vm.new_str(code_obj.obj_name)) +} From 6c56c22f0fda2b982a7ceef3804a36c23317cd1f Mon Sep 17 00:00:00 2001 From: silmeth Date: Sat, 9 Feb 2019 15:15:54 +0100 Subject: [PATCH 151/153] fix floordiv and divmod by zero for ints and floats --- tests/snippets/builtin_divmod.py | 14 ++++++ tests/snippets/division_by_zero.py | 21 +++++++++ vm/src/obj/objfloat.rs | 71 ++++++++++++++++++++---------- vm/src/obj/objint.rs | 26 ++++++++--- 4 files changed, 103 insertions(+), 29 deletions(-) diff --git a/tests/snippets/builtin_divmod.py b/tests/snippets/builtin_divmod.py index 7bab71c99..43b77a452 100644 --- a/tests/snippets/builtin_divmod.py +++ b/tests/snippets/builtin_divmod.py @@ -1,3 +1,17 @@ assert divmod(11, 3) == (3, 2) assert divmod(8,11) == (0, 8) assert divmod(0.873, 0.252) == (3.0, 0.11699999999999999) + +try: + divmod(5, 0) +except ZeroDivisionError: + pass +else: + assert False, "Expected divmod by zero to throw ZeroDivisionError" + +try: + divmod(5.0, 0.0) +except ZeroDivisionError: + pass +else: + assert False, "Expected divmod by zero to throw ZeroDivisionError" diff --git a/tests/snippets/division_by_zero.py b/tests/snippets/division_by_zero.py index 7cb68cd76..d92419d79 100644 --- a/tests/snippets/division_by_zero.py +++ b/tests/snippets/division_by_zero.py @@ -26,6 +26,27 @@ except ZeroDivisionError: else: assert False, 'Expected ZeroDivisionError' +try: + 5 // 0 +except ZeroDivisionError: + pass +else: + assert False, 'Expected ZeroDivisionError' + +try: + 5.3 // (-0.0) +except ZeroDivisionError: + pass +else: + assert False, 'Expected ZeroDivisionError' + +try: + divmod(5, 0) +except ZeroDivisionError: + pass +else: + assert False, 'Expected ZeroDivisionError' + try: raise ZeroDivisionError('Is an ArithmeticError subclass?') except ArithmeticError: diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 6d8c2ff73..94f9f19b7 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -172,9 +172,9 @@ fn float_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { let args = PyFuncArgs::new(vec![i.clone(), i2.clone()], vec![]); if objtype::isinstance(i2, &vm.ctx.float_type()) || objtype::isinstance(i2, &vm.ctx.int_type()) { - let r1 = float_floordiv(vm, args.clone()); - let r2 = float_mod(vm, args.clone()); - Ok(vm.ctx.new_tuple(vec![r1.unwrap(), r2.unwrap()])) + let r1 = float_floordiv(vm, args.clone())?; + let r2 = float_mod(vm, args.clone())?; + Ok(vm.ctx.new_tuple(vec![r1, r2])) } else { Err(vm.new_type_error(format!( "Cannot divmod power {} and {}", @@ -190,18 +190,26 @@ fn float_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(i, Some(vm.ctx.float_type())), (i2, None)] ); - if objtype::isinstance(i2, &vm.ctx.float_type()) { - Ok(vm.ctx.new_float((get_value(i) / get_value(i2)).floor())) - } else if objtype::isinstance(i2, &vm.ctx.int_type()) { - Ok(vm - .ctx - .new_float((get_value(i) / objint::get_value(i2).to_f64().unwrap()).floor())) + + let v1 = get_value(i); + let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) { + get_value(i2) + } else if objtype::isinstance(i2, &vm.ctx.int_type) { + objint::get_value(i2) + .to_f64() + .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? } else { - Err(vm.new_type_error(format!( + return Err(vm.new_type_error(format!( "Cannot floordiv {} and {}", i.borrow(), i2.borrow() - ))) + ))); + }; + + if v2 != 0.0 { + Ok(vm.ctx.new_float((v1 / v2).floor())) + } else { + Err(vm.new_zero_division_error("float floordiv by zero".to_string())) } } @@ -229,14 +237,22 @@ fn float_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(i, Some(vm.ctx.float_type())), (i2, None)] ); - if objtype::isinstance(i2, &vm.ctx.float_type()) { - Ok(vm.ctx.new_float(get_value(i) % get_value(i2))) - } else if objtype::isinstance(i2, &vm.ctx.int_type()) { - Ok(vm - .ctx - .new_float(get_value(i) % objint::get_value(i2).to_f64().unwrap())) + + let v1 = get_value(i); + let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) { + get_value(i2) + } else if objtype::isinstance(i2, &vm.ctx.int_type) { + objint::get_value(i2) + .to_f64() + .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? } else { - Err(vm.new_type_error(format!("Cannot mod {} and {}", i.borrow(), i2.borrow()))) + return Err(vm.new_type_error(format!("Cannot mod {} and {}", i.borrow(), i2.borrow()))); + }; + + if v2 != 0.0 { + Ok(vm.ctx.new_float(v1 % v2)) + } else { + Err(vm.new_zero_division_error("float mod by zero".to_string())) } } @@ -272,15 +288,22 @@ fn float_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(i, Some(vm.ctx.float_type())), (i2, None)] ); + let v1 = get_value(i); - if objtype::isinstance(i2, &vm.ctx.float_type) { - Ok(vm.ctx.new_float(v1 / get_value(i2))) + let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) { + get_value(i2) } else if objtype::isinstance(i2, &vm.ctx.int_type) { - Ok(vm - .ctx - .new_float(v1 / objint::get_value(i2).to_f64().unwrap())) + objint::get_value(i2) + .to_f64() + .ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))? } else { - Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow()))) + return Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow()))); + }; + + if v2 != 0.0 { + Ok(vm.ctx.new_float(v1 / v2)) + } else { + Err(vm.new_zero_division_error("float division by zero".to_string())) } } diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 73267a83f..443fa552e 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -8,6 +8,7 @@ use super::objfloat; use super::objstr; use super::objtype; use num_bigint::{BigInt, ToBigInt}; +use num_integer::Integer; use num_traits::{Pow, Signed, ToPrimitive, Zero}; use std::hash::{Hash, Hasher}; @@ -289,7 +290,13 @@ fn int_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); if objtype::isinstance(i2, &vm.ctx.int_type()) { - Ok(vm.ctx.new_int(get_value(i) / get_value(i2))) + let (v1, v2) = (get_value(i), get_value(i2)); + + if v2 != BigInt::zero() { + Ok(vm.ctx.new_int(v1 / v2)) + } else { + Err(vm.new_zero_division_error("integer floordiv by zero".to_string())) + } } else { Err(vm.new_type_error(format!( "Cannot floordiv {} and {}", @@ -462,11 +469,20 @@ fn int_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(i, Some(vm.ctx.int_type())), (i2, None)] ); - let args = PyFuncArgs::new(vec![i.clone(), i2.clone()], vec![]); + if objtype::isinstance(i2, &vm.ctx.int_type()) { - let r1 = int_floordiv(vm, args.clone()); - let r2 = int_mod(vm, args.clone()); - Ok(vm.ctx.new_tuple(vec![r1.unwrap(), r2.unwrap()])) + let v1 = get_value(i); + let v2 = get_value(i2); + + if v2 != BigInt::zero() { + let (r1, r2) = v1.div_rem(&v2); + + Ok(vm + .ctx + .new_tuple(vec![vm.ctx.new_int(r1), vm.ctx.new_int(r2)])) + } else { + Err(vm.new_zero_division_error("integer divmod by zero".to_string())) + } } else { Err(vm.new_type_error(format!( "Cannot divmod power {} and {}", From 22a430cdc55912406d8b0c3e57a94b20338358ac Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sat, 9 Feb 2019 08:20:04 -0800 Subject: [PATCH 152/153] Don't use magic methods directly --- tests/snippets/builtin_range.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/snippets/builtin_range.py b/tests/snippets/builtin_range.py index 659eb4f4f..667e776a2 100644 --- a/tests/snippets/builtin_range.py +++ b/tests/snippets/builtin_range.py @@ -40,24 +40,24 @@ assert_raises(lambda _: range(4, 10, 2).index(5), ValueError) assert_raises(lambda _: range(10).index('foo'), ValueError) # __bool__ -assert range(1).__bool__() -assert range(1, 2).__bool__() +assert bool(range(1)) +assert bool(range(1, 2)) -assert not range(0).__bool__() -assert not range(1, 1).__bool__() +assert not bool(range(0)) +assert not bool(range(1, 1)) # __contains__ -assert range(10).__contains__(6) -assert range(4, 10).__contains__(6) -assert range(4, 10, 2).__contains__(6) -assert range(10, 4, -2).__contains__(10) -assert range(10, 4, -2).__contains__(8) +assert 6 in range(10) +assert 6 in range(4, 10) +assert 6 in range(4, 10, 2) +assert 10 in range(10, 4, -2) +assert 8 in range(10, 4, -2) -assert not range(10).__contains__(-1) -assert not range(10, 4, -2).__contains__(9) -assert not range(10, 4, -2).__contains__(4) -assert not range(10).__contains__('foo') +assert -1 not in range(10) +assert 9 not in range(10, 4, -2) +assert 4 not in range(10, 4, -2) +assert 'foo' not in range(10) # __reversed__ -assert list(range(5).__reversed__()) == [4, 3, 2, 1, 0] -assert list(range(5, 0, -1).__reversed__()) == [1, 2, 3, 4, 5] +assert list(reversed(range(5))) == [4, 3, 2, 1, 0] +assert list(reversed(range(5, 0, -1))) == [1, 2, 3, 4, 5] From 8ffd4c5e5639d9332e8a3b73a4aabc2062fac14d Mon Sep 17 00:00:00 2001 From: Tommaso Thea Cioni Date: Sat, 9 Feb 2019 19:10:40 +0100 Subject: [PATCH 153/153] Fixed #402. (#414) Raise syntax error rather than panicking on expected closing bracket. * Fixed #402. --- parser/src/lexer.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 58660a901..f48c48d49 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -54,6 +54,7 @@ pub struct Lexer> { #[derive(Debug)] pub enum LexicalError { StringError, + NestingError, } #[derive(Clone, Debug, Default, PartialEq)] @@ -428,9 +429,7 @@ where self.next_char(); loop { match self.chr0 { - Some('\n') => { - return; - } + Some('\n') => return, Some(_) => {} None => return, } @@ -904,6 +903,9 @@ where } Some(')') => { let result = self.eat_single_char(Tok::Rpar); + if self.nesting == 0 { + return Some(Err(LexicalError::NestingError)); + } self.nesting -= 1; return Some(result); } @@ -914,6 +916,9 @@ where } Some(']') => { let result = self.eat_single_char(Tok::Rsqb); + if self.nesting == 0 { + return Some(Err(LexicalError::NestingError)); + } self.nesting -= 1; return Some(result); } @@ -924,6 +929,9 @@ where } Some('}') => { let result = self.eat_single_char(Tok::Rbrace); + if self.nesting == 0 { + return Some(Err(LexicalError::NestingError)); + } self.nesting -= 1; return Some(result); }