Skip to content

Commit 9f06835

Browse files
committed
Add built-ins next and iter
1 parent 416e48b commit 9f06835

File tree

8 files changed

+193
-35
lines changed

8 files changed

+193
-35
lines changed

tests/snippets/ast_snippet.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,19 @@
55
source = """
66
def foo():
77
print('bar')
8+
pass
89
"""
910
n = ast.parse(source)
1011
print(n)
1112
print(n.body)
1213
print(n.body[0].name)
1314
assert n.body[0].name == 'foo'
14-
print(n.body[0].body)
15-
print(n.body[0].body[0])
16-
print(n.body[0].body[0].value.func.id)
17-
assert n.body[0].body[0].value.func.id == 'print'
15+
foo = n.body[0]
16+
assert foo.lineno == 2
17+
print(foo.body)
18+
assert len(foo.body) == 2
19+
print(foo.body[0])
20+
print(foo.body[0].value.func.id)
21+
assert foo.body[0].value.func.id == 'print'
22+
assert foo.body[0].lineno == 3
23+
assert foo.body[1].lineno == 4

tests/snippets/iterations.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
3+
ls = [1, 2, 3]
4+
5+
i = iter(ls)
6+
assert i.__next__() == 1
7+
assert i.__next__() == 2
8+
assert next(i) == 3
9+
10+
assert next(i, 'w00t') == 'w00t'
11+

vm/src/builtins.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::io::{self, Write};
66
use super::compile;
77
use super::obj::objstr;
88
use super::obj::objtype;
9+
use super::obj::objiter;
910
use super::objbool;
1011
use super::pyobject::{
1112
AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind,
@@ -221,7 +222,14 @@ fn builtin_issubclass(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
221222
Ok(vm.context().new_bool(objtype::issubclass(cls1, cls2)))
222223
}
223224

224-
// builtin_iter
225+
fn builtin_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
226+
arg_check!(
227+
vm,
228+
args,
229+
required = [(iter_target, None)]
230+
);
231+
objiter::get_iter(vm, iter_target)
232+
}
225233

226234
fn builtin_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
227235
arg_check!(vm, args, required = [(obj, None)]);
@@ -254,7 +262,30 @@ fn builtin_locals(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
254262
// builtin_max
255263
// builtin_memoryview
256264
// builtin_min
257-
// builtin_next
265+
266+
fn builtin_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
267+
arg_check!(
268+
vm,
269+
args,
270+
required = [(iterator, None)],
271+
optional = [(default_value, None)]
272+
);
273+
274+
match vm.call_method(iterator.clone(), "__next__", vec![]) {
275+
Ok(value) => Ok(value),
276+
Err(value) => {
277+
if objtype::isinstance(&value, vm.ctx.exceptions.stop_iteration.clone()) {
278+
match default_value {
279+
None => Err(value),
280+
Some(value) => Ok(value.clone()),
281+
}
282+
} else {
283+
Err(value)
284+
}
285+
}
286+
}
287+
}
288+
258289
// builtin_object
259290
// builtin_oct
260291
// builtin_open
@@ -347,9 +378,14 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
347378
String::from("issubclass"),
348379
ctx.new_rustfunc(builtin_issubclass),
349380
);
381+
dict.insert(
382+
String::from("iter"),
383+
ctx.new_rustfunc(builtin_iter),
384+
);
350385
dict.insert(String::from("len"), ctx.new_rustfunc(builtin_len));
351386
dict.insert(String::from("list"), ctx.list_type());
352387
dict.insert(String::from("locals"), ctx.new_rustfunc(builtin_locals));
388+
dict.insert(String::from("next"), ctx.new_rustfunc(builtin_next));
353389
dict.insert(String::from("print"), ctx.new_rustfunc(builtin_print));
354390
dict.insert(String::from("range"), ctx.new_rustfunc(builtin_range));
355391
dict.insert(String::from("repr"), ctx.new_rustfunc(builtin_repr));

vm/src/exceptions.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ pub struct ExceptionZoo {
8989
pub name_error: PyObjectRef,
9090
pub runtime_error: PyObjectRef,
9191
pub not_implemented_error: PyObjectRef,
92+
pub stop_iteration: PyObjectRef,
9293
pub type_error: PyObjectRef,
9394
pub value_error: PyObjectRef,
9495
}
@@ -138,6 +139,12 @@ impl ExceptionZoo {
138139
&runtime_error,
139140
&dict_type,
140141
);
142+
let stop_iteration = create_type(
143+
&String::from("StopIteration"),
144+
&type_type,
145+
&exception_type,
146+
&dict_type,
147+
);
141148
let type_error = create_type(
142149
&String::from("TypeError"),
143150
&type_type,
@@ -159,6 +166,7 @@ impl ExceptionZoo {
159166
name_error: name_error,
160167
runtime_error: runtime_error,
161168
not_implemented_error: not_implemented_error,
169+
stop_iteration: stop_iteration,
162170
type_error: type_error,
163171
value_error: value_error,
164172
}

vm/src/obj/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pub mod objdict;
33
pub mod objfloat;
44
pub mod objfunction;
55
pub mod objint;
6+
pub mod objiter;
67
pub mod objlist;
78
pub mod objobject;
89
pub mod objsequence;

vm/src/obj/objiter.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Various types to support iteration.
3+
*/
4+
5+
use super::super::pyobject::{
6+
AttributeProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef,
7+
PyResult, TypeProtocol,
8+
};
9+
use super::super::vm::VirtualMachine;
10+
use super::objstr;
11+
use super::objtype; // Required for arg_check! to use isinstance
12+
13+
pub fn get_iter(vm: &mut VirtualMachine, iter_target: &PyObjectRef) -> PyResult {
14+
// Check what we are going to iterate over:
15+
let iterated_obj = if objtype::isinstance(iter_target, vm.ctx.iter_type()) {
16+
// If object is already an iterator, return that one.
17+
return Ok(iter_target.clone())
18+
} else if objtype::isinstance(iter_target, vm.ctx.list_type()) {
19+
iter_target.clone()
20+
} else {
21+
let type_str = objstr::get_value(&vm.to_str(iter_target.typ()).unwrap());
22+
let type_error = vm.new_type_error(format!("Cannot iterate over {}", type_str));
23+
return Err(type_error);
24+
};
25+
26+
let iter_obj = PyObject::new(
27+
PyObjectKind::Iterator {
28+
position: 0,
29+
iterated_obj: iterated_obj,
30+
},
31+
vm.ctx.iter_type(),
32+
);
33+
34+
// We are all good here:
35+
Ok(iter_obj)
36+
}
37+
38+
// Sequence iterator:
39+
fn iter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
40+
arg_check!(
41+
vm,
42+
args,
43+
required = [(iter_target, None)]
44+
);
45+
46+
get_iter(vm, iter_target)
47+
}
48+
49+
fn iter_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
50+
arg_check!(
51+
vm,
52+
args,
53+
required = [(iter, Some(vm.ctx.iter_type()))]
54+
);
55+
// Return self:
56+
Ok(iter.clone())
57+
}
58+
59+
fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
60+
arg_check!(
61+
vm,
62+
args,
63+
required = [(iter, Some(vm.ctx.iter_type()))]
64+
);
65+
66+
let next_obj: Option<PyObjectRef> = {
67+
// We require a mutable pyobject here to update the iterator:
68+
let mut iterator: &mut PyObject = &mut iter.borrow_mut();
69+
iterator.nxt()
70+
};
71+
72+
// Return next item, or StopIteration
73+
match next_obj {
74+
Some(value) => Ok(value),
75+
None => {
76+
let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone();
77+
let stop_iteration = vm.new_exception(stop_iteration_type, "End of iterator".to_string());
78+
Err(stop_iteration)
79+
}
80+
}
81+
}
82+
83+
pub fn init(context: &PyContext) {
84+
let ref iter_type = context.iter_type;
85+
iter_type.set_attr("__new__", context.new_rustfunc(iter_new));
86+
iter_type.set_attr("__iter__", context.new_rustfunc(iter_iter));
87+
iter_type.set_attr("__next__", context.new_rustfunc(iter_next));
88+
}
89+

vm/src/pyobject.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use super::obj::objdict;
55
use super::obj::objfloat;
66
use super::obj::objfunction;
77
use super::obj::objint;
8+
use super::obj::objiter;
89
use super::obj::objlist;
910
use super::obj::objobject;
1011
use super::obj::objstr;
@@ -60,6 +61,7 @@ pub struct PyContext {
6061
pub false_value: PyObjectRef,
6162
pub list_type: PyObjectRef,
6263
pub tuple_type: PyObjectRef,
64+
pub iter_type: PyObjectRef,
6365
pub str_type: PyObjectRef,
6466
pub function_type: PyObjectRef,
6567
pub module_type: PyObjectRef,
@@ -123,6 +125,7 @@ impl PyContext {
123125
let float_type = create_type("float", &type_type, &object_type, &dict_type);
124126
let bytes_type = create_type("bytes", &type_type, &object_type, &dict_type);
125127
let tuple_type = create_type("tuple", &type_type, &object_type, &dict_type);
128+
let iter_type = create_type("iter", &type_type, &object_type, &dict_type);
126129
let bool_type = create_type("bool", &type_type, &int_type, &dict_type);
127130
let exceptions = exceptions::ExceptionZoo::new(&type_type, &object_type, &dict_type);
128131

@@ -142,6 +145,7 @@ impl PyContext {
142145
true_value: true_value,
143146
false_value: false_value,
144147
tuple_type: tuple_type,
148+
iter_type: iter_type,
145149
dict_type: dict_type,
146150
none: none,
147151
str_type: str_type,
@@ -164,6 +168,7 @@ impl PyContext {
164168
objbytes::init(&context);
165169
objstr::init(&context);
166170
objtuple::init(&context);
171+
objiter::init(&context);
167172
objbool::init(&context);
168173
exceptions::init(&context);
169174
context
@@ -190,6 +195,9 @@ impl PyContext {
190195
pub fn tuple_type(&self) -> PyObjectRef {
191196
self.tuple_type.clone()
192197
}
198+
pub fn iter_type(&self) -> PyObjectRef {
199+
self.iter_type.clone()
200+
}
193201
pub fn dict_type(&self) -> PyObjectRef {
194202
self.dict_type.clone()
195203
}

vm/src/vm.rs

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@
77
extern crate rustpython_parser;
88

99
use self::rustpython_parser::ast;
10-
use std::cell::RefMut;
1110
use std::collections::hash_map::HashMap;
12-
use std::ops::Deref;
1311

1412
use super::builtins;
1513
use super::bytecode;
1614
use super::frame::{copy_code, Block, Frame};
1715
use super::import::import;
16+
use super::obj::objiter;
1817
use super::obj::objlist;
1918
use super::obj::objobject;
2019
use super::obj::objstr;
@@ -829,46 +828,46 @@ impl VirtualMachine {
829828
}
830829
bytecode::Instruction::GetIter => {
831830
let iterated_obj = self.pop_value();
832-
let iter_obj = PyObject::new(
833-
PyObjectKind::Iterator {
834-
position: 0,
835-
iterated_obj: iterated_obj,
831+
match objiter::get_iter(self, &iterated_obj) {
832+
Ok(iter_obj) => {
833+
self.push_value(iter_obj);
834+
None
836835
},
837-
self.ctx.type_type(),
838-
);
839-
self.push_value(iter_obj);
840-
None
836+
Err(err) => Some(Err(err)),
837+
}
841838
}
842839
bytecode::Instruction::ForIter => {
843840
// The top of stack contains the iterator, lets push it forward:
844-
let next_obj: Option<PyObjectRef> = {
841+
let next_obj: PyResult = {
845842
let top_of_stack = self.last_value();
846-
let mut ref_mut: RefMut<PyObject> = top_of_stack.deref().borrow_mut();
847-
// We require a mutable pyobject here to update the iterator:
848-
let mut iterator = ref_mut; // &mut PyObject = ref_mut.;
849-
// let () = iterator;
850-
iterator.nxt()
843+
self.call_method(top_of_stack, "__next__", vec![])
851844
};
852845

853846
// Check the next object:
854847
match next_obj {
855-
Some(value) => {
848+
Ok(value) => {
856849
self.push_value(value);
850+
None
857851
}
858-
None => {
859-
// Pop iterator from stack:
860-
self.pop_value();
861-
862-
// End of for loop
863-
let end_label = if let Block::Loop { start: _, end } = self.last_block() {
864-
*end
852+
Err(next_error) => {
853+
// Check if we have stopiteration, or something else:
854+
if objtype::isinstance(&next_error, self.ctx.exceptions.stop_iteration.clone()) {
855+
// Pop iterator from stack:
856+
self.pop_value();
857+
858+
// End of for loop
859+
let end_label = if let Block::Loop { start: _, end } = self.last_block() {
860+
*end
861+
} else {
862+
panic!("Wrong block type")
863+
};
864+
self.jump(&end_label);
865+
None
865866
} else {
866-
panic!("Wrong block type")
867-
};
868-
self.jump(&end_label);
867+
Some(Err(next_error))
868+
}
869869
}
870-
};
871-
None
870+
}
872871
}
873872
bytecode::Instruction::MakeFunction { flags } => {
874873
let _qualified_name = self.pop_value();

0 commit comments

Comments
 (0)