Skip to content

Commit a3a427c

Browse files
committed
Add 'yield from' syntax
1 parent 29ce0cf commit a3a427c

File tree

8 files changed

+113
-34
lines changed

8 files changed

+113
-34
lines changed

parser/src/lexer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,14 +268,14 @@ where
268268
// Skip everything until end of line
269269
self.next_char();
270270
loop {
271-
self.next_char();
272271
match self.chr0 {
273272
Some('\n') => {
274273
return;
275274
}
276275
Some(_) => {}
277276
None => return,
278277
}
278+
self.next_char();
279279
}
280280
}
281281

tests/snippets/comments.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
# Snippet to demo comment handling...
3+
4+
5+
def foo():
6+
a = []
7+
8+
# This empty comment below manifests a bug:
9+
#
10+
if len(a) > 2:
11+
a.append(2)
12+
return a

tests/snippets/generators.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,13 @@ def g2(x):
2525
assert 15 == i.send(10)
2626
assert 17 == i.send(10)
2727

28+
29+
def g3():
30+
yield 23
31+
yield from make_numbers()
32+
yield 44
33+
34+
r = list(g3())
35+
# print(r)
36+
assert r == [23, 1, 2, 3, 44]
37+

vm/src/bytecode.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ pub enum Instruction {
130130
ForIter,
131131
ReturnValue,
132132
YieldValue,
133+
YieldFrom,
133134
SetupLoop {
134135
start: Label,
135136
end: Label,

vm/src/compile.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ impl Compiler {
293293
}
294294
}
295295
None => {
296-
unimplemented!();
296+
self.emit(Instruction::Raise { argc: 0 });
297297
}
298298
},
299299
ast::Statement::Try {
@@ -792,6 +792,7 @@ impl Compiler {
792792
value: name.to_string(),
793793
});
794794
} else {
795+
// This means **kwargs!
795796
panic!("name must be set");
796797
}
797798
self.compile_expression(&keyword.value)?;
@@ -916,8 +917,11 @@ impl Compiler {
916917
ast::Expression::YieldFrom { value } => {
917918
self.mark_generator();
918919
self.compile_expression(value)?;
919-
self.emit(Instruction::YieldValue);
920-
unimplemented!("yield from todo");
920+
self.emit(Instruction::GetIter);
921+
self.emit(Instruction::LoadConst {
922+
value: bytecode::Constant::None,
923+
});
924+
self.emit(Instruction::YieldFrom);
921925
}
922926
ast::Expression::True => {
923927
self.emit(Instruction::LoadConst {

vm/src/frame.rs

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,31 @@ impl Frame {
337337
let value = self.pop_value();
338338
Some(Ok(ExecutionResult::Yield(value)))
339339
}
340+
bytecode::Instruction::YieldFrom => {
341+
// Value send into iterator:
342+
self.pop_value();
343+
344+
let top_of_stack = self.last_value();
345+
let next_obj = objiter::get_next_object(vm, &top_of_stack);
346+
347+
match next_obj {
348+
Ok(Some(value)) => {
349+
// Set back program counter:
350+
self.lasti -= 1;
351+
Some(Ok(ExecutionResult::Yield(value)))
352+
}
353+
Ok(None) => {
354+
// Pop iterator from stack:
355+
self.pop_value();
356+
None
357+
}
358+
Err(next_error) => {
359+
// Pop iterator from stack:
360+
self.pop_value();
361+
Some(Err(next_error))
362+
}
363+
}
364+
}
340365
bytecode::Instruction::SetupLoop { start, end } => {
341366
self.push_block(Block::Loop {
342367
start: *start,
@@ -398,35 +423,31 @@ impl Frame {
398423
bytecode::Instruction::ForIter => {
399424
// The top of stack contains the iterator, lets push it forward:
400425
let top_of_stack = self.last_value();
401-
let next_obj: PyResult = vm.call_method(&top_of_stack, "__next__", vec![]);
426+
let next_obj = objiter::get_next_object(vm, &top_of_stack);
402427

403428
// Check the next object:
404429
match next_obj {
405-
Ok(value) => {
430+
Ok(Some(value)) => {
406431
self.push_value(value);
407432
None
408433
}
409-
Err(next_error) => {
410-
// Check if we have stopiteration, or something else:
411-
if objtype::isinstance(
412-
&next_error,
413-
vm.ctx.exceptions.stop_iteration.clone(),
414-
) {
415-
// Pop iterator from stack:
416-
self.pop_value();
417-
418-
// End of for loop
419-
let end_label = if let Block::Loop { start: _, end } = self.last_block()
420-
{
421-
*end
422-
} else {
423-
panic!("Wrong block type")
424-
};
425-
self.jump(end_label);
426-
None
434+
Ok(None) => {
435+
// Pop iterator from stack:
436+
self.pop_value();
437+
438+
// End of for loop
439+
let end_label = if let Block::Loop { start: _, end } = self.last_block() {
440+
*end
427441
} else {
428-
Some(Err(next_error))
429-
}
442+
panic!("Wrong block type")
443+
};
444+
self.jump(end_label);
445+
None
446+
}
447+
Err(next_error) => {
448+
// Pop iterator from stack:
449+
self.pop_value();
450+
Some(Err(next_error))
430451
}
431452
}
432453
}

vm/src/obj/objiter.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,44 @@ pub fn get_iter(vm: &mut VirtualMachine, iter_target: &PyObjectRef) -> PyResult
4343
Ok(iter_obj)
4444
}
4545

46+
/*
47+
* Helper function to retrieve the next object (or none) from an iterator.
48+
*/
49+
pub fn get_next_object(
50+
vm: &mut VirtualMachine,
51+
iter_obj: &PyObjectRef,
52+
) -> Result<Option<PyObjectRef>, PyObjectRef> {
53+
let next_obj: PyResult = vm.call_method(iter_obj, "__next__", vec![]);
54+
55+
match next_obj {
56+
Ok(value) => Ok(Some(value)),
57+
Err(next_error) => {
58+
// Check if we have stopiteration, or something else:
59+
if objtype::isinstance(&next_error, vm.ctx.exceptions.stop_iteration.clone()) {
60+
Ok(None)
61+
} else {
62+
Err(next_error)
63+
}
64+
}
65+
}
66+
}
67+
68+
/* Retrieve all elements from an iterator */
69+
pub fn get_all(
70+
vm: &mut VirtualMachine,
71+
iter_obj: &PyObjectRef,
72+
) -> Result<Vec<PyObjectRef>, PyObjectRef> {
73+
let mut elements = vec![];
74+
loop {
75+
let element = get_next_object(vm, iter_obj)?;
76+
match element {
77+
Some(v) => elements.push(v),
78+
None => break,
79+
}
80+
}
81+
Ok(elements)
82+
}
83+
4684
// Sequence iterator:
4785
fn iter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
4886
arg_check!(vm, args, required = [(iter_target, None)]);

vm/src/obj/objlist.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,8 @@ fn list_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
5151
let elements = match iterable {
5252
None => vec![],
5353
Some(iterable) => {
54-
let mut elements = vec![];
5554
let iterator = objiter::get_iter(vm, iterable)?;
56-
loop {
57-
match vm.call_method(&iterator, "__next__", vec![]) {
58-
Ok(v) => elements.push(v),
59-
_ => break,
60-
}
61-
}
62-
elements
55+
objiter::get_all(vm, &iterator)?
6356
}
6457
};
6558

0 commit comments

Comments
 (0)