Skip to content

Commit 907dfb6

Browse files
committed
Add slice type and use BigInts in slice payload.
1 parent f454bf3 commit 907dfb6

11 files changed

Lines changed: 235 additions & 52 deletions

File tree

tests/snippets/builtin_slice.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
2+
a = []
3+
assert a[:] == []
4+
assert a[:2**100] == []
5+
assert a[-2**100:] == []
6+
assert a[::2**100] == []
7+
assert a[10:20] == []
8+
assert a[-20:-10] == []
9+
10+
b = [1, 2]
11+
12+
assert b[:] == [1, 2]
13+
assert b[:2**100] == [1, 2]
14+
assert b[-2**100:] == [1, 2]
15+
assert b[2**100:] == []
16+
assert b[::2**100] == [1]
17+
assert b[-10:1] == [1]
18+
19+
slice_a = slice(5)
20+
assert slice_a.start is None
21+
assert slice_a.stop == 5
22+
assert slice_a.step is None
23+
24+
slice_b = slice(1, 5)
25+
assert slice_b.start == 1
26+
assert slice_b.stop == 5
27+
assert slice_b.step is None
28+
29+
slice_c = slice(1, 5, 2)
30+
assert slice_c.start == 1
31+
assert slice_c.stop == 5
32+
assert slice_c.step == 2
33+
34+
35+
class SubScript(object):
36+
def __getitem__(self, item):
37+
assert type(item) == slice
38+
39+
def __setitem__(self, key, value):
40+
assert type(key) == slice
41+
42+
43+
ss = SubScript()
44+
_ = ss[:]
45+
ss[:1] = 1

vm/src/builtins.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
677677
ctx.set_attr(&py_mod, "repr", ctx.new_rustfunc(builtin_repr));
678678
ctx.set_attr(&py_mod, "set", ctx.set_type());
679679
ctx.set_attr(&py_mod, "setattr", ctx.new_rustfunc(builtin_setattr));
680+
ctx.set_attr(&py_mod, "slice", ctx.slice_type());
680681
ctx.set_attr(&py_mod, "staticmethod", ctx.staticmethod_type());
681682
ctx.set_attr(&py_mod, "str", ctx.str_type());
682683
ctx.set_attr(&py_mod, "sum", ctx.new_rustfunc(builtin_sum));

vm/src/frame.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use super::pyobject::{
2020
PyResult, TypeProtocol,
2121
};
2222
use super::vm::VirtualMachine;
23-
use num_traits::ToPrimitive;
23+
use num_bigint::BigInt;
2424

2525
#[derive(Clone, Debug)]
2626
enum Block {
@@ -262,22 +262,22 @@ impl Frame {
262262
assert!(*size == 2 || *size == 3);
263263
let elements = self.pop_multiple(*size);
264264

265-
let mut out: Vec<Option<i32>> = elements
265+
let mut out: Vec<Option<BigInt>> = elements
266266
.into_iter()
267267
.map(|x| match x.borrow().payload {
268-
PyObjectPayload::Integer { ref value } => Some(value.to_i32().unwrap()),
268+
PyObjectPayload::Integer { ref value } => Some(value.clone()),
269269
PyObjectPayload::None => None,
270270
_ => panic!("Expect Int or None as BUILD_SLICE arguments, got {:?}", x),
271271
})
272272
.collect();
273273

274-
let start = out[0];
275-
let stop = out[1];
276-
let step = if out.len() == 3 { out[2] } else { None };
274+
let start = out[0].take();
275+
let stop = out[1].take();
276+
let step = if out.len() == 3 { out[2].take() } else { None };
277277

278278
let obj = PyObject::new(
279279
PyObjectPayload::Slice { start, stop, step },
280-
vm.ctx.type_type(),
280+
vm.ctx.slice_type(),
281281
);
282282
self.push_value(obj);
283283
Ok(None)

vm/src/obj/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub mod objproperty;
2222
pub mod objrange;
2323
pub mod objsequence;
2424
pub mod objset;
25+
pub mod objslice;
2526
pub mod objstr;
2627
pub mod objsuper;
2728
pub mod objtuple;

vm/src/obj/objiter.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use super::super::vm::VirtualMachine;
99
use super::objbool;
1010
// use super::objstr;
1111
use super::objtype; // Required for arg_check! to use isinstance
12-
use num_bigint::BigInt;
1312

1413
/*
1514
* 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 {
146145
}
147146

148147
PyObjectPayload::Range { ref range } => {
149-
if let Some(int) = range.get(BigInt::from(*position)) {
148+
if let Some(int) = range.get(*position) {
150149
*position += 1;
151150
Ok(vm.ctx.new_int(int))
152151
} else {

vm/src/obj/objlist.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ fn set_item(
2121
) -> PyResult {
2222
if objtype::isinstance(&idx, &vm.ctx.int_type()) {
2323
let value = objint::get_value(&idx).to_i32().unwrap();
24-
let pos_index = l.get_pos(value);
25-
l[pos_index] = obj;
26-
Ok(vm.get_none())
24+
if let Some(pos_index) = l.get_pos(value) {
25+
l[pos_index] = obj;
26+
Ok(vm.get_none())
27+
} else {
28+
Err(vm.new_index_error("list index out of range".to_string()))
29+
}
2730
} else {
2831
panic!(
2932
"TypeError: indexing type {:?} with index {:?} is not supported (yet?)",

vm/src/obj/objrange.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use super::objtype;
77
use num_bigint::{BigInt, Sign};
88
use num_integer::Integer;
99
use num_traits::{One, Signed, ToPrimitive, Zero};
10+
use std::ops::Mul;
1011

1112
#[derive(Debug, Clone)]
1213
pub struct RangeType {
@@ -77,8 +78,11 @@ impl RangeType {
7778
}
7879

7980
#[inline]
80-
pub fn get(&self, index: BigInt) -> Option<BigInt> {
81-
let result = self.start.clone() + self.step.clone() * index;
81+
pub fn get<'a, T>(&'a self, index: T) -> Option<BigInt>
82+
where
83+
&'a BigInt: Mul<T, Output = BigInt>,
84+
{
85+
let result = &self.start + &self.step * index;
8286

8387
if (self.forward() && !self.is_empty() && result < self.end)
8488
|| (!self.forward() && !self.is_empty() && result > self.end)
@@ -199,15 +203,19 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
199203

200204
match subscript.borrow().payload {
201205
PyObjectPayload::Integer { ref value } => {
202-
if let Some(int) = zrange.get(value.clone()) {
206+
if let Some(int) = zrange.get(value) {
203207
Ok(vm.ctx.new_int(int))
204208
} else {
205209
Err(vm.new_index_error("range object index out of range".to_string()))
206210
}
207211
}
208-
PyObjectPayload::Slice { start, stop, step } => {
212+
PyObjectPayload::Slice {
213+
ref start,
214+
ref stop,
215+
ref step,
216+
} => {
209217
let new_start = if let Some(int) = start {
210-
if let Some(i) = zrange.get(int.into()) {
218+
if let Some(i) = zrange.get(int) {
211219
i
212220
} else {
213221
zrange.start.clone()
@@ -217,7 +225,7 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
217225
};
218226

219227
let new_end = if let Some(int) = stop {
220-
if let Some(i) = zrange.get(int.into()) {
228+
if let Some(i) = zrange.get(int) {
221229
i
222230
} else {
223231
zrange.end
@@ -227,7 +235,7 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
227235
};
228236

229237
let new_step = if let Some(int) = step {
230-
(int as i64) * zrange.step
238+
int * zrange.step
231239
} else {
232240
zrange.step
233241
};

vm/src/obj/objsequence.rs

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use super::super::pyobject::{PyObject, PyObjectPayload, PyObjectRef, PyResult, T
22
use super::super::vm::VirtualMachine;
33
use super::objbool;
44
use super::objint;
5-
use num_traits::ToPrimitive;
5+
use num_bigint::BigInt;
6+
use num_traits::{Signed, ToPrimitive};
67
use std::cell::{Ref, RefMut};
78
use std::marker::Sized;
89
use std::ops::{Deref, DerefMut};
@@ -11,45 +12,67 @@ pub trait PySliceableSequence {
1112
fn do_slice(&self, start: usize, stop: usize) -> Self;
1213
fn do_stepped_slice(&self, start: usize, stop: usize, step: usize) -> Self;
1314
fn len(&self) -> usize;
14-
fn get_pos(&self, p: i32) -> usize {
15+
fn get_pos(&self, p: i32) -> Option<usize> {
1516
if p < 0 {
1617
if -p as usize > self.len() {
17-
// return something that is out of bounds so `get_item` raises an IndexError
18-
self.len() + 1
18+
None
1919
} else {
20-
self.len() - ((-p) as usize)
20+
Some(self.len() - ((-p) as usize))
2121
}
22-
} else if p as usize > self.len() {
23-
// This is for the slicing case where the end element is greater than the length of the
24-
// sequence
25-
self.len()
22+
} else if p as usize >= self.len() {
23+
None
2624
} else {
27-
p as usize
25+
Some(p as usize)
2826
}
2927
}
28+
29+
fn get_slice_pos(&self, slice_pos: &BigInt) -> usize {
30+
if let Some(pos) = slice_pos.to_i32() {
31+
if let Some(index) = self.get_pos(pos) {
32+
// within bounds
33+
return index;
34+
}
35+
}
36+
37+
if slice_pos.is_negative() {
38+
0
39+
} else {
40+
self.len()
41+
}
42+
}
43+
3044
fn get_slice_items(&self, slice: &PyObjectRef) -> Self
3145
where
3246
Self: Sized,
3347
{
3448
// TODO: we could potentially avoid this copy and use slice
3549
match &(slice.borrow()).payload {
3650
PyObjectPayload::Slice { start, stop, step } => {
37-
let start = match *start {
38-
Some(start) => self.get_pos(start),
39-
None => 0,
51+
let start = if let Some(start) = start {
52+
self.get_slice_pos(start)
53+
} else {
54+
0
4055
};
41-
let stop = match *stop {
42-
Some(stop) => self.get_pos(stop),
43-
None => self.len() as usize,
56+
let stop = if let Some(stop) = stop {
57+
self.get_slice_pos(stop)
58+
} else {
59+
self.len()
4460
};
45-
match *step {
46-
None | Some(1) => self.do_slice(start, stop),
47-
Some(num) => {
48-
if num < 0 {
49-
unimplemented!("negative step indexing not yet supported")
50-
};
51-
self.do_stepped_slice(start, stop, num as usize)
61+
if let Some(step) = step {
62+
match step.to_i32() {
63+
Some(1) => self.do_slice(start, stop),
64+
Some(num) => self.do_stepped_slice(start, stop, num as usize),
65+
None => self.do_slice(
66+
start,
67+
if start == self.len() {
68+
start
69+
} else {
70+
start + 1
71+
},
72+
),
5273
}
74+
} else {
75+
self.do_slice(start, stop)
5376
}
5477
}
5578
payload => panic!("get_slice_items called with non-slice: {:?}", payload),
@@ -78,8 +101,7 @@ pub fn get_item(
78101
match &(subscript.borrow()).payload {
79102
PyObjectPayload::Integer { value } => match value.to_i32() {
80103
Some(value) => {
81-
let pos_index = elements.to_vec().get_pos(value);
82-
if pos_index < elements.len() {
104+
if let Some(pos_index) = elements.to_vec().get_pos(value) {
83105
let obj = elements[pos_index].clone();
84106
Ok(obj)
85107
} else {

0 commit comments

Comments
 (0)