Skip to content

Commit 1ebacaf

Browse files
Add reversed builtin and range.__reversed__
1 parent a4b9925 commit 1ebacaf

File tree

4 files changed

+58
-0
lines changed

4 files changed

+58
-0
lines changed

tests/snippets/builtin_range.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,7 @@ def assert_raises(expr, exc_type):
5252
assert not range(10, 4, -2).__contains__(9)
5353
assert not range(10, 4, -2).__contains__(4)
5454
assert not range(10).__contains__('foo')
55+
56+
# __reversed__
57+
assert list(range(5).__reversed__()) == [4, 3, 2, 1, 0]
58+
assert list(range(5, 0, -1).__reversed__()) == [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
@@ -611,6 +611,19 @@ fn builtin_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
611611
arg_check!(vm, args, required = [(obj, None)]);
612612
vm.to_repr(obj)
613613
}
614+
615+
fn builtin_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
616+
arg_check!(vm, args, required = [(obj, None)]);
617+
618+
match vm.get_method(obj.clone(), "__reversed__") {
619+
Ok(value) => vm.invoke(value, PyFuncArgs::default()),
620+
// TODO: fallback to using __len__ and __getitem__, if object supports sequence protocol
621+
Err(..) => Err(vm.new_type_error(format!(
622+
"'{}' object is not reversible",
623+
objtype::get_type_name(&obj.typ()),
624+
))),
625+
}
626+
}
614627
// builtin_reversed
615628
// builtin_round
616629

@@ -725,6 +738,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
725738
ctx.set_attr(&py_mod, "property", ctx.property_type());
726739
ctx.set_attr(&py_mod, "range", ctx.range_type());
727740
ctx.set_attr(&py_mod, "repr", ctx.new_rustfunc(builtin_repr));
741+
ctx.set_attr(&py_mod, "reversed", ctx.new_rustfunc(builtin_reversed));
728742
ctx.set_attr(&py_mod, "set", ctx.set_type());
729743
ctx.set_attr(&py_mod, "setattr", ctx.new_rustfunc(builtin_setattr));
730744
ctx.set_attr(&py_mod, "staticmethod", ctx.staticmethod_type());

vm/src/obj/objrange.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,34 @@ impl RangeType {
7676
None
7777
}
7878
}
79+
80+
#[inline]
81+
pub fn reversed(&self) -> Self {
82+
match self.step.sign() {
83+
Sign::Plus => RangeType {
84+
start: &self.end - 1,
85+
end: &self.start - 1,
86+
step: -&self.step,
87+
},
88+
Sign::Minus => RangeType {
89+
start: &self.end + 1,
90+
end: &self.start + 1,
91+
step: -&self.step,
92+
},
93+
Sign::NoSign => unreachable!(),
94+
}
95+
}
7996
}
8097

8198
pub fn init(context: &PyContext) {
8299
let ref range_type = context.range_type;
83100
context.set_attr(&range_type, "__new__", context.new_rustfunc(range_new));
84101
context.set_attr(&range_type, "__iter__", context.new_rustfunc(range_iter));
102+
context.set_attr(
103+
&range_type,
104+
"__reversed__",
105+
context.new_rustfunc(range_reversed),
106+
);
85107
context.set_attr(&range_type, "__len__", context.new_rustfunc(range_len));
86108
context.set_attr(
87109
&range_type,
@@ -150,6 +172,23 @@ fn range_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
150172
))
151173
}
152174

175+
fn range_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
176+
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]);
177+
178+
let range = match zelf.borrow().payload {
179+
PyObjectPayload::Range { ref range } => range.reversed(),
180+
_ => unreachable!(),
181+
};
182+
183+
Ok(PyObject::new(
184+
PyObjectPayload::Iterator {
185+
position: 0,
186+
iterated_obj: PyObject::new(PyObjectPayload::Range { range }, vm.ctx.range_type()),
187+
},
188+
vm.ctx.iter_type(),
189+
))
190+
}
191+
153192
fn range_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
154193
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]);
155194

0 commit comments

Comments
 (0)