-
Notifications
You must be signed in to change notification settings - Fork 95
/
Copy pathgenerator.go
138 lines (122 loc) · 4.68 KB
/
generator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// Copyright 2018 The go-python Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Generator objects
package py
// A python Generator object
type Generator struct {
// Note: gi_frame can be NULL if the generator is "finished"
Frame *Frame
// True if generator is being executed.
Running bool
// The code object backing the generator
Code *Code
// List of weak reference.
Weakreflist Object
}
var GeneratorType = NewType("generator", "generator object")
func init() {
// FIXME would like to do this with introspection
GeneratorType.Dict["send"] = MustNewMethod("send", func(self Object, value Object) (Object, error) {
return self.(*Generator).Send(value)
}, 0, "send(arg) -> send 'arg' into generator,\nreturn next yielded value or raise StopIteration.")
GeneratorType.Dict["throw"] = MustNewMethod("throw", func(self Object, args Tuple, kwargs StringDict) (Object, error) {
return self.(*Generator).Throw(args, kwargs)
}, 0, "throw(typ[,val[,tb]]) -> raise exception in generator,\nreturn next yielded value or raise StopIteration.")
GeneratorType.Dict["close"] = MustNewMethod("close", func(self Object) (Object, error) {
return self.(*Generator).Close()
}, 0, "close() -> raise GeneratorExit inside generator.")
}
// Type of this object
func (o *Generator) Type() *Type {
return GeneratorType
}
// Define a new generator
func NewGenerator(frame *Frame) *Generator {
g := &Generator{
Frame: frame,
Running: false,
Code: frame.Code,
}
return g
}
func (it *Generator) M__iter__() (Object, error) {
return it, nil
}
// generator.__next__()
//
// Starts the execution of a generator function or resumes it at the
// last executed yield expression. When a generator function is
// resumed with a __next__() method, the current yield expression
// always evaluates to None. The execution then continues to the next
// yield expression, where the generator is suspended again, and the
// value of the expression_list is returned to next()‘s caller. If the
// generator exits without yielding another value, a StopIteration
// exception is raised.
//
// This method is normally called implicitly, e.g. by a for loop, or by the built-in next() function.
func (it *Generator) M__next__() (Object, error) {
return it.Send(None)
}
// generator.send(value)
//
// Resumes the execution and “sends” a value into the generator
// function. The value argument becomes the result of the current
// yield expression. The send() method returns the next value yielded
// by the generator, or raises StopIteration if the generator exits
// without yielding another value. When send() is called to start the
// generator, it must be called with None as the argument, because
// there is no yield expression that could receive the value.
func (it *Generator) Send(arg Object) (Object, error) {
if it.Running {
return nil, ExceptionNewf(ValueError, "generator already executing")
}
if it.Frame.Lasti == 0 {
if arg != None {
return nil, ExceptionNewf(TypeError, "can't send non-None value to a just-started generator")
}
} else {
// If already returned a non yield value then stop
if !it.Frame.Yielded {
return nil, StopIteration
}
// Push arg onto the frame's value stack
it.Frame.Stack = append(it.Frame.Stack, arg)
}
it.Running = true
res, err := VmRunFrame(it.Frame)
it.Running = false
if err != nil {
return nil, err
}
if it.Frame.Yielded {
return res, nil
}
return nil, StopIteration
}
// generator.throw(type[, value[, traceback]])
//
// Raises an exception of type type at the point where generator was
// paused, and returns the next value yielded by the generator
// function. If the generator exits without yielding another value, a
// StopIteration exception is raised. If the generator function does
// not catch the passed-in exception, or raises a different exception,
// then that exception propagates to the caller.
func (it *Generator) Throw(args Tuple, kwargs StringDict) (Object, error) {
return nil, NotImplementedError
}
// generator.close()
//
// Raises a GeneratorExit at the point where the generator function
// was paused. If the generator function then raises StopIteration (by
// exiting normally, or due to already being closed) or GeneratorExit
// (by not catching the exception), close returns to its caller. If
// the generator yields a value, a RuntimeError is raised. If the
// generator raises any other exception, it is propagated to the
// caller. close() does nothing if the generator has already exited
// due to an exception or normal exit.
func (it *Generator) Close() (Object, error) {
return nil, NotImplementedError
}
// Check interface is satisfied
var _ I_generator = (*Generator)(nil)