make len(range) throw OverflowError on too big ints + impl __repr__

This commit is contained in:
silmeth
2019-02-05 20:58:38 +01:00
parent b43c511542
commit 0a1eb4b91b
3 changed files with 58 additions and 11 deletions

View File

@@ -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,
}
}
}

View File

@@ -18,16 +18,21 @@ pub struct RangeType {
impl RangeType {
#[inline]
pub fn len(&self) -> usize {
pub fn try_len(&self) -> Option<usize> {
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))
}

View File

@@ -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>) -> PyObjectRef {