mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-09 22:49:57 +09:00
Add slice type and use BigInts in slice payload.
This commit is contained in:
45
tests/snippets/builtin_slice.py
Normal file
45
tests/snippets/builtin_slice.py
Normal file
@@ -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
|
||||
@@ -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));
|
||||
|
||||
@@ -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<Option<i32>> = elements
|
||||
let mut out: Vec<Option<BigInt>> = 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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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?)",
|
||||
|
||||
@@ -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<BigInt> {
|
||||
let result = self.start.clone() + self.step.clone() * index;
|
||||
pub fn get<'a, T>(&'a self, index: T) -> Option<BigInt>
|
||||
where
|
||||
&'a BigInt: Mul<T, Output = BigInt>,
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
@@ -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<usize> {
|
||||
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 {
|
||||
|
||||
95
vm/src/obj/objslice.rs
Normal file
95
vm/src/obj/objslice.rs
Normal file
@@ -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<BigInt>) -> 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));
|
||||
}
|
||||
@@ -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()))
|
||||
|
||||
@@ -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<PyObjectRef>,
|
||||
},
|
||||
Slice {
|
||||
start: Option<i32>,
|
||||
stop: Option<i32>,
|
||||
step: Option<i32>,
|
||||
start: Option<BigInt>,
|
||||
stop: Option<BigInt>,
|
||||
step: Option<BigInt>,
|
||||
},
|
||||
Range {
|
||||
range: objrange::RangeType,
|
||||
|
||||
Reference in New Issue
Block a user