Skip to content

Commit ac37026

Browse files
Merge pull request #385 from OddCoincidence/reversed
Add reversed builtin and range.__reversed__
2 parents 56a36b7 + 22a430c commit ac37026

File tree

4 files changed

+71
-14
lines changed

4 files changed

+71
-14
lines changed

tests/snippets/builtin_range.py

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,24 @@ def assert_raises(expr, exc_type):
4040
assert_raises(lambda _: range(10).index('foo'), ValueError)
4141

4242
# __bool__
43-
assert range(1).__bool__()
44-
assert range(1, 2).__bool__()
43+
assert bool(range(1))
44+
assert bool(range(1, 2))
4545

46-
assert not range(0).__bool__()
47-
assert not range(1, 1).__bool__()
46+
assert not bool(range(0))
47+
assert not bool(range(1, 1))
4848

4949
# __contains__
50-
assert range(10).__contains__(6)
51-
assert range(4, 10).__contains__(6)
52-
assert range(4, 10, 2).__contains__(6)
53-
assert range(10, 4, -2).__contains__(10)
54-
assert range(10, 4, -2).__contains__(8)
55-
56-
assert not range(10).__contains__(-1)
57-
assert not range(10, 4, -2).__contains__(9)
58-
assert not range(10, 4, -2).__contains__(4)
59-
assert not range(10).__contains__('foo')
50+
assert 6 in range(10)
51+
assert 6 in range(4, 10)
52+
assert 6 in range(4, 10, 2)
53+
assert 10 in range(10, 4, -2)
54+
assert 8 in range(10, 4, -2)
55+
56+
assert -1 not in range(10)
57+
assert 9 not in range(10, 4, -2)
58+
assert 4 not in range(10, 4, -2)
59+
assert 'foo' not in range(10)
60+
61+
# __reversed__
62+
assert list(reversed(range(5))) == [4, 3, 2, 1, 0]
63+
assert list(reversed(range(5, 0, -1))) == [1, 2, 3, 4, 5]

tests/snippets/builtin_reversed.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
assert list(reversed(range(5))) == [4, 3, 2, 1, 0]

vm/src/builtins.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,19 @@ fn builtin_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
587587
arg_check!(vm, args, required = [(obj, None)]);
588588
vm.to_repr(obj)
589589
}
590+
591+
fn builtin_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
592+
arg_check!(vm, args, required = [(obj, None)]);
593+
594+
match vm.get_method(obj.clone(), "__reversed__") {
595+
Ok(value) => vm.invoke(value, PyFuncArgs::default()),
596+
// TODO: fallback to using __len__ and __getitem__, if object supports sequence protocol
597+
Err(..) => Err(vm.new_type_error(format!(
598+
"'{}' object is not reversible",
599+
objtype::get_type_name(&obj.typ()),
600+
))),
601+
}
602+
}
590603
// builtin_reversed
591604

592605
fn builtin_round(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
@@ -692,6 +705,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
692705
ctx.set_attr(&py_mod, "property", ctx.property_type());
693706
ctx.set_attr(&py_mod, "range", ctx.range_type());
694707
ctx.set_attr(&py_mod, "repr", ctx.new_rustfunc(builtin_repr));
708+
ctx.set_attr(&py_mod, "reversed", ctx.new_rustfunc(builtin_reversed));
695709
ctx.set_attr(&py_mod, "round", ctx.new_rustfunc(builtin_round));
696710
ctx.set_attr(&py_mod, "set", ctx.set_type());
697711
ctx.set_attr(&py_mod, "setattr", ctx.new_rustfunc(builtin_setattr));

vm/src/obj/objrange.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,22 @@ impl RangeType {
9494
}
9595

9696
#[inline]
97+
pub fn reversed(&self) -> Self {
98+
match self.step.sign() {
99+
Sign::Plus => RangeType {
100+
start: &self.end - 1,
101+
end: &self.start - 1,
102+
step: -&self.step,
103+
},
104+
Sign::Minus => RangeType {
105+
start: &self.end + 1,
106+
end: &self.start + 1,
107+
step: -&self.step,
108+
},
109+
Sign::NoSign => unreachable!(),
110+
}
111+
}
112+
97113
pub fn repr(&self) -> String {
98114
if self.step == BigInt::one() {
99115
format!("range({}, {})", self.start, self.end)
@@ -116,6 +132,11 @@ pub fn init(context: &PyContext) {
116132

117133
context.set_attr(&range_type, "__new__", context.new_rustfunc(range_new));
118134
context.set_attr(&range_type, "__iter__", context.new_rustfunc(range_iter));
135+
context.set_attr(
136+
&range_type,
137+
"__reversed__",
138+
context.new_rustfunc(range_reversed),
139+
);
119140
context.set_attr(
120141
&range_type,
121142
"__doc__",
@@ -190,6 +211,23 @@ fn range_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
190211
))
191212
}
192213

214+
fn range_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
215+
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]);
216+
217+
let range = match zelf.borrow().payload {
218+
PyObjectPayload::Range { ref range } => range.reversed(),
219+
_ => unreachable!(),
220+
};
221+
222+
Ok(PyObject::new(
223+
PyObjectPayload::Iterator {
224+
position: 0,
225+
iterated_obj: PyObject::new(PyObjectPayload::Range { range }, vm.ctx.range_type()),
226+
},
227+
vm.ctx.iter_type(),
228+
))
229+
}
230+
193231
fn range_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
194232
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]);
195233

0 commit comments

Comments
 (0)