Skip to content

Commit 9c4a071

Browse files
authored
Merge pull request RustPython#880 from youknowone/fix-range-negative
Fix range() to support negative index + Add range.__eq__
2 parents fd91d26 + a7091ca commit 9c4a071

File tree

2 files changed

+35
-10
lines changed

2 files changed

+35
-10
lines changed

tests/snippets/builtin_range.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,9 @@
5454
# range retains the original int refs
5555
i = 2**64
5656
assert range(i).stop is i
57+
58+
# negative index
59+
assert range(10)[-1] == 9
60+
assert_raises(IndexError, lambda: range(10)[-11], 'out of bound')
61+
assert range(10)[-2:4] == range(8, 4)
62+
assert range(10)[-6:-2] == range(4, 8)

vm/src/obj/objrange.rs

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::cell::Cell;
2-
use std::ops::Mul;
32

43
use num_bigint::{BigInt, Sign};
54
use num_integer::Integer;
@@ -14,7 +13,7 @@ use crate::vm::VirtualMachine;
1413
use super::objint::{PyInt, PyIntRef};
1514
use super::objiter;
1615
use super::objslice::{PySlice, PySliceRef};
17-
use super::objtype::PyClassRef;
16+
use super::objtype::{self, PyClassRef};
1817

1918
#[derive(Debug, Clone)]
2019
pub struct PyRange {
@@ -65,15 +64,22 @@ impl PyRange {
6564
}
6665

6766
#[inline]
68-
pub fn get<'a, T>(&'a self, index: T) -> Option<BigInt>
69-
where
70-
&'a BigInt: Mul<T, Output = BigInt>,
71-
{
67+
pub fn get(&self, index: &BigInt) -> Option<BigInt> {
7268
let start = self.start.as_bigint();
7369
let stop = self.stop.as_bigint();
7470
let step = self.step.as_bigint();
7571

76-
let result = start + step * index;
72+
let index = if index < &BigInt::zero() {
73+
let index = stop + index;
74+
if index < BigInt::zero() {
75+
return None;
76+
}
77+
index
78+
} else {
79+
index.clone()
80+
};
81+
82+
let result = start + step * &index;
7783

7884
if (self.forward() && !self.is_empty() && &result < stop)
7985
|| (!self.forward() && !self.is_empty() && &result > stop)
@@ -104,6 +110,7 @@ pub fn init(context: &PyContext) {
104110
"__bool__" => context.new_rustfunc(PyRange::bool),
105111
"__contains__" => context.new_rustfunc(PyRange::contains),
106112
"__doc__" => context.new_str(range_doc.to_string()),
113+
"__eq__" => context.new_rustfunc(PyRange::eq),
107114
"__getitem__" => context.new_rustfunc(PyRange::getitem),
108115
"__iter__" => context.new_rustfunc(PyRange::iter),
109116
"__len__" => context.new_rustfunc(PyRange::len),
@@ -240,6 +247,17 @@ impl PyRange {
240247
}
241248
}
242249

250+
fn eq(&self, rhs: PyObjectRef, vm: &VirtualMachine) -> bool {
251+
if objtype::isinstance(&rhs, &vm.ctx.range_type()) {
252+
let rhs = get_value(&rhs);
253+
self.start.as_bigint() == rhs.start.as_bigint()
254+
&& self.stop.as_bigint() == rhs.stop.as_bigint()
255+
&& self.step.as_bigint() == rhs.step.as_bigint()
256+
} else {
257+
false
258+
}
259+
}
260+
243261
fn index(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyInt> {
244262
if let Ok(int) = needle.downcast::<PyInt>() {
245263
match self.index_of(int.as_bigint()) {
@@ -274,7 +292,7 @@ impl PyRange {
274292
}
275293
RangeIndex::Slice(slice) => {
276294
let new_start = if let Some(int) = slice.start_index(vm)? {
277-
if let Some(i) = self.get(int) {
295+
if let Some(i) = self.get(&int) {
278296
PyInt::new(i).into_ref(vm)
279297
} else {
280298
self.start.clone()
@@ -284,7 +302,7 @@ impl PyRange {
284302
};
285303

286304
let new_end = if let Some(int) = slice.stop_index(vm)? {
287-
if let Some(i) = self.get(int) {
305+
if let Some(i) = self.get(&int) {
288306
PyInt::new(i).into_ref(vm)
289307
} else {
290308
self.stop.clone()
@@ -339,7 +357,8 @@ type PyRangeIteratorRef = PyRef<PyRangeIterator>;
339357

340358
impl PyRangeIteratorRef {
341359
fn next(self, vm: &VirtualMachine) -> PyResult<BigInt> {
342-
if let Some(int) = self.range.get(self.position.get()) {
360+
let position = BigInt::from(self.position.get());
361+
if let Some(int) = self.range.get(&position) {
343362
self.position.set(self.position.get() + 1);
344363
Ok(int)
345364
} else {

0 commit comments

Comments
 (0)