Skip to content

Commit 9e25827

Browse files
committed
Generators and YIELD_VALUE
1 parent aa100dc commit 9e25827

File tree

5 files changed

+178
-45
lines changed

5 files changed

+178
-45
lines changed

‎py/frame.go‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ type Frame struct{
2424
// Next free slot in f_valuestack. Frame creation sets to f_valuestack.
2525
// Frame evaluation usually NULLs it, but a frame that yields sets it
2626
// to the current stack top.
27-
Stacktop*Object
28-
TraceObject// Trace function
27+
// Stacktop *Object
28+
Yieldedbool// set if the function yielded, cleared otherwise
29+
TraceObject// Trace function
2930

3031
// In a generator, we need to be able to swap between the exception
3132
// state inside the generator and the exception state of the calling
@@ -62,6 +63,17 @@ func (o *Frame) Type() *Type{
6263
returnFrameType
6364
}
6465

66+
// Make a new frame for a code object
67+
funcNewFrame(globals, localsStringDict, code*Code) *Frame{
68+
return&Frame{
69+
Globals: globals,
70+
Locals: locals,
71+
Code: code,
72+
Builtins: Builtins.Globals,
73+
Stack: make([]Object, 0, code.Stacksize),
74+
}
75+
}
76+
6577
// Python names are looked up in three scopes
6678
//
6779
// First the local scope

‎py/generator.go‎

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// Generator objects
2+
3+
package py
4+
5+
// A python Generator object
6+
typeGeneratorstruct{
7+
// Note: gi_frame can be NULL if the generator is "finished"
8+
Frame*Frame
9+
10+
// True if generator is being executed.
11+
Runningbool
12+
13+
// The code object backing the generator
14+
Code*Code
15+
16+
// List of weak reference.
17+
WeakreflistObject
18+
}
19+
20+
varGeneratorType=NewType("generator", "generator object")
21+
22+
// Type of this object
23+
func (o*Generator) Type() *Type{
24+
returnGeneratorType
25+
}
26+
27+
// Define a new generator
28+
funcNewGenerator(frame*Frame) *Generator{
29+
g:=&Generator{
30+
Frame: frame,
31+
Running: false,
32+
Code: frame.Code,
33+
}
34+
returng
35+
}
36+
37+
func (it*Generator) M__iter__() Object{
38+
returnit
39+
}
40+
41+
// generator.__next__()
42+
//
43+
// Starts the execution of a generator function or resumes it at the
44+
// last executed yield expression. When a generator function is
45+
// resumed with a __next__() method, the current yield expression
46+
// always evaluates to None. The execution then continues to the next
47+
// yield expression, where the generator is suspended again, and the
48+
// value of the expression_list is returned to next()‘s caller. If the
49+
// generator exits without yielding another value, a StopIteration
50+
// exception is raised.
51+
//
52+
// This method is normally called implicitly, e.g. by a for loop, or by the built-in next() function.
53+
func (it*Generator) M__next__() Object{
54+
it.Running=true
55+
res, err:=RunFrame(it.Frame)
56+
it.Running=false
57+
// Push a None on the stack for next time
58+
// FIXME this value is the one sent by Send
59+
it.Frame.Stack=append(it.Frame.Stack, None)
60+
// FIXME not correct
61+
iferr!=nil{
62+
panic(err)
63+
}
64+
ifit.Frame.Yielded{
65+
returnres
66+
}
67+
panic(StopIteration)
68+
}
69+
70+
// generator.send(value)
71+
//
72+
// Resumes the execution and “sends” a value into the generator
73+
// function. The value argument becomes the result of the current
74+
// yield expression. The send() method returns the next value yielded
75+
// by the generator, or raises StopIteration if the generator exits
76+
// without yielding another value. When send() is called to start the
77+
// generator, it must be called with None as the argument, because
78+
// there is no yield expression that could receive the value.
79+
func (it*Generator) Send(valueObject){
80+
panic("generator send not implemented")
81+
}
82+
83+
// generator.throw(type[, value[, traceback]])
84+
//
85+
// Raises an exception of type type at the point where generator was
86+
// paused, and returns the next value yielded by the generator
87+
// function. If the generator exits without yielding another value, a
88+
// StopIteration exception is raised. If the generator function does
89+
// not catch the passed-in exception, or raises a different exception,
90+
// then that exception propagates to the caller.
91+
func (it*Generator) Throw(argsTuple, kwargsStringDict){
92+
panic("generator throw not implemented")
93+
}
94+
95+
// generator.close()
96+
//
97+
// Raises a GeneratorExit at the point where the generator function
98+
// was paused. If the generator function then raises StopIteration (by
99+
// exiting normally, or due to already being closed) or GeneratorExit
100+
// (by not catching the exception), close returns to its caller. If
101+
// the generator yields a value, a RuntimeError is raised. If the
102+
// generator raises any other exception, it is propagated to the
103+
// caller. close() does nothing if the generator has already exited
104+
// due to an exception or normal exit.
105+
func (it*Generator) Close(){
106+
panic("generator close not implemented")
107+
}
108+
109+
// Check interface is satisfied
110+
var_I_iterator= (*Generator)(nil)

‎py/py.go‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ type Object interface{
99
// Some well known objects
1010
var (
1111
EllipsisObject
12-
// See vm.Run - set to avoid circular import
13-
Runfunc(globals, localsStringDict, code*Code) (resObject, errerror)
12+
// See vm/eval.go - set to avoid circular import
13+
Runfunc(globals, localsStringDict, code*Code) (resObject, errerror)
14+
RunFramefunc(frame*Frame) (resObject, errerror)
1415
)
1516

1617
// Called to create a new instance of class cls. __new__() is a static method (special-cased so you need not declare it as such) that takes the class of which an instance was requested as its first argument. The remaining arguments are those passed to the object constructor expression (the call to the class). The return value of __new__() should be the new object instance (usually an instance of cls).

‎vm/eval.go‎

Lines changed: 32 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,8 @@ func do_RETURN_VALUE(vm *Vm, arg int32){
417417
fmt.Printf("vmstack = %#v\n", vm.frame.Stack)
418418
panic("vm stack should be empty at this point")
419419
}
420-
vm.PopFrame()
420+
vm.frame.Yielded=false
421+
vm.exit=exitReturn
421422
}
422423

423424
// Pops TOS and delegates to it as a subiterator from a generator.
@@ -427,7 +428,9 @@ func do_YIELD_FROM(vm *Vm, arg int32){
427428

428429
// Pops TOS and yields it from a generator.
429430
funcdo_YIELD_VALUE(vm*Vm, argint32){
430-
vm.NotImplemented("YIELD_VALUE", arg)
431+
vm.result=vm.POP()
432+
vm.frame.Yielded=true
433+
vm.exit=exitYield
431434
}
432435

433436
// Loads all symbols not starting with '_' directly from the module
@@ -958,43 +961,13 @@ func (vm *Vm) Call(fnObj py.Object, args []py.Object, kwargsTuple []py.Object){
958961
vm.PUSH(py.Call(fnObj, args, kwargs))
959962
}
960963

961-
// Make a new Frame with globals, locals and Code on the frames stack
962-
func (vm*Vm) PushFrame(globals, locals py.StringDict, code*py.Code){
963-
frame:= py.Frame{
964-
Globals: globals,
965-
Locals: locals,
966-
Code: code,
967-
Builtins: py.Builtins.Globals,
968-
Stack: make([]py.Object, 0, code.Stacksize),
969-
}
970-
vm.frames=append(vm.frames, frame)
971-
vm.frame=&vm.frames[len(vm.frames)-1]
972-
}
973-
974-
// Drop the current frame
975-
func (vm*Vm) PopFrame(){
976-
vm.frames=vm.frames[:len(vm.frames)-1]
977-
iflen(vm.frames) >0{
978-
vm.frame=&vm.frames[len(vm.frames)-1]
979-
} else{
980-
vm.frame=nil
981-
}
982-
}
983-
984-
// Write the py global to avoid circular import
985-
funcinit(){
986-
py.Run=Run
987-
}
988-
989-
// Run the virtual machine on the code object in the module
964+
// Run the virtual machine on a Frame object
990965
//
991966
// FIXME figure out how we are going to signal exceptions!
992967
//
993-
// Any parameters are expected to have been decoded into locals
994-
//
995968
// Returns an Object and an error
996-
funcRun(globals, locals py.StringDict, code*py.Code) (res py.Object, errerror){
997-
vm:=NewVm()
969+
funcRunFrame(frame*py.Frame) (res py.Object, errerror){
970+
vm:=NewVm(frame)
998971
deferfunc(){
999972
ifr:=recover(); r!=nil{
1000973
switchx:=r.(type){
@@ -1010,11 +983,10 @@ func Run(globals, locals py.StringDict, code *py.Code) (res py.Object, err error
1010983
debug.PrintStack()
1011984
}
1012985
}()
1013-
vm.PushFrame(globals, locals, code)
1014986

1015987
varopcodebyte
1016988
varargint32
1017-
forvm.frame!=nil{
989+
forvm.exit==exitNot{
1018990
frame:=vm.frame
1019991
fmt.Printf("* %4d:", frame.Lasti)
1020992
opcodes:=frame.Code.Code
@@ -1045,3 +1017,26 @@ func Run(globals, locals py.StringDict, code *py.Code) (res py.Object, err error
10451017
}
10461018
returnvm.result, nil
10471019
}
1020+
1021+
// Run the virtual machine on a Code object
1022+
//
1023+
// Any parameters are expected to have been decoded into locals
1024+
//
1025+
// Returns an Object and an error
1026+
funcRun(globals, locals py.StringDict, code*py.Code) (res py.Object, errerror){
1027+
frame:=py.NewFrame(globals, locals, code)
1028+
1029+
// If this is a generator then make a generator object from
1030+
// the frame and return that instead
1031+
ifcode.Flags&py.CO_GENERATOR!=0{
1032+
returnpy.NewGenerator(frame), nil
1033+
}
1034+
1035+
returnRunFrame(frame)
1036+
}
1037+
1038+
// Write the py global to avoid circular import
1039+
funcinit(){
1040+
py.Run=Run
1041+
py.RunFrame=RunFrame
1042+
}

‎vm/vm.go‎

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,23 @@ import (
55
"github.com/ncw/gpython/py"
66
)
77

8+
// VM exit type
9+
typevmExitbyte
10+
11+
// VM exit values
12+
const (
13+
exitNot=vmExit(iota) // No error
14+
exitException// Exception occurred
15+
exitReraise// Exception re-raised by 'finally'
16+
exitReturn// 'return' statement
17+
exitBreak// 'break' statement
18+
exitContinue// 'continue' statement
19+
exitYield// 'yield' operator
20+
exitSilenced// Exception silenced by 'with'
21+
)
22+
823
// Virtual machine state
924
typeVmstruct{
10-
// Frame stack
11-
frames []py.Frame
1225
// Current frame
1326
frame*py.Frame
1427
// Whether ext should be added to the next arg
@@ -17,11 +30,13 @@ type Vm struct{
1730
extint32
1831
// Return value
1932
result py.Object
33+
// Exit value
34+
exitvmExit
2035
}
2136

2237
// Make a new VM
23-
funcNewVm() *Vm{
38+
funcNewVm(frame*py.Frame) *Vm{
2439
return&Vm{
25-
frames: make([]py.Frame, 0, 16),
40+
frame: frame,
2641
}
2742
}

0 commit comments

Comments
(0)