-
Notifications
You must be signed in to change notification settings - Fork 638
Implement generator.throw() #369
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -590,9 +590,14 @@ def visit_function_inline(self, node): | |
| self.writer.write('var πE *πg.BaseException; _ = πE') | ||
| if func_block.is_generator: | ||
| self.writer.write( | ||
| 'return πg.NewGenerator(πF, func(πSent *πg.Object) ' | ||
| 'return πg.NewGenerator(πF, ' | ||
| 'func(πSent *πg.Object, πRaised *πg.BaseException) ' | ||
|
||
| '(*πg.Object, *πg.BaseException) {') | ||
| with self.writer.indent_block(): | ||
| self.writer.write(textwrap.dedent("""\ | ||
| if πRaised != nil { | ||
| \treturn nil, πRaised | ||
| }""")) | ||
| self.writer.write_block(func_block, visitor.writer.getvalue()) | ||
| self.writer.write('return nil, πE') | ||
| self.writer.write('}).ToObject(), nil') | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ | |
| package grumpy | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "reflect" | ||
| "sync" | ||
| ) | ||
|
|
@@ -39,11 +40,11 @@ type Generator struct { | |
| mutex sync.Mutex | ||
| state generatorState | ||
| frame *Frame | ||
| fn func(*Object) (*Object, *BaseException) | ||
| fn func(*Object, *BaseException) (*Object, *BaseException) | ||
| } | ||
|
|
||
| // NewGenerator returns a new Generator object that runs the given Block b. | ||
| func NewGenerator(f *Frame, fn func(*Object) (*Object, *BaseException)) *Generator { | ||
| func NewGenerator(f *Frame, fn func(*Object, *BaseException) (*Object, *BaseException)) *Generator { | ||
| f.taken = true // Claim the frame from being returned. | ||
|
|
||
| // The code generator basically gives us the Frame, so we can tare it | ||
|
|
@@ -57,33 +58,35 @@ func toGeneratorUnsafe(o *Object) *Generator { | |
| return (*Generator)(o.toPointer()) | ||
| } | ||
|
|
||
| func (g *Generator) resume(f *Frame, sendValue *Object) (*Object, *BaseException) { | ||
| var raised *BaseException | ||
| g.mutex.Lock() | ||
| oldState := g.state | ||
| switch oldState { | ||
| case generatorStateCreated: | ||
| if sendValue != None { | ||
| raised = f.RaiseType(TypeErrorType, "can't send non-None value to a just-started generator") | ||
| } else { | ||
| func (g *Generator) resume(f *Frame, sendValue *Object, raisedValue *BaseException) (*Object, *BaseException) { | ||
| if raisedValue == nil { | ||
|
||
| var raised *BaseException | ||
| g.mutex.Lock() | ||
| oldState := g.state | ||
| switch oldState { | ||
| case generatorStateCreated: | ||
| if sendValue != None { | ||
| raised = f.RaiseType(TypeErrorType, "can't send non-None value to a just-started generator") | ||
| } else { | ||
| g.state = generatorStateRunning | ||
| } | ||
| case generatorStateReady: | ||
| g.state = generatorStateRunning | ||
| case generatorStateRunning: | ||
| raised = f.RaiseType(ValueErrorType, "generator already executing") | ||
| case generatorStateDone: | ||
| raised = f.Raise(StopIterationType.ToObject(), nil, nil) | ||
| } | ||
| g.mutex.Unlock() | ||
| // Concurrent attempts to transition to running state will raise here | ||
| // so it's guaranteed that only one thread will proceed to execute the | ||
| // block below. | ||
| if raised != nil { | ||
| return nil, raised | ||
| } | ||
| case generatorStateReady: | ||
| g.state = generatorStateRunning | ||
| case generatorStateRunning: | ||
| raised = f.RaiseType(ValueErrorType, "generator already executing") | ||
| case generatorStateDone: | ||
| raised = f.Raise(StopIterationType.ToObject(), nil, nil) | ||
| } | ||
| g.mutex.Unlock() | ||
| // Concurrent attempts to transition to running state will raise here | ||
| // so it's guaranteed that only one thread will proceed to execute the | ||
| // block below. | ||
| if raised != nil { | ||
| return nil, raised | ||
| } | ||
| g.frame.pushFrame(f) | ||
| result, raised := g.fn(sendValue) | ||
| result, raised := g.fn(sendValue, raisedValue) | ||
| g.mutex.Lock() | ||
| if result == nil && raised == nil { | ||
| raised = f.Raise(StopIterationType.ToObject(), nil, nil) | ||
|
|
@@ -108,18 +111,45 @@ func generatorIter(f *Frame, o *Object) (*Object, *BaseException) { | |
| } | ||
|
|
||
| func generatorNext(f *Frame, o *Object) (*Object, *BaseException) { | ||
| return toGeneratorUnsafe(o).resume(f, None) | ||
| return toGeneratorUnsafe(o).resume(f, None, nil) | ||
| } | ||
|
|
||
| func generatorSend(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { | ||
| if raised := checkMethodArgs(f, "send", args, GeneratorType, ObjectType); raised != nil { | ||
| return nil, raised | ||
| } | ||
| return toGeneratorUnsafe(args[0]).resume(f, args[1]) | ||
| return toGeneratorUnsafe(args[0]).resume(f, args[1], nil) | ||
| } | ||
|
|
||
| func generatorThrow(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { | ||
| argc := len(args) | ||
| if argc == 1 { | ||
|
||
| return nil, f.RaiseType(TypeErrorType, "throw expected at least 1 arguments, got 0") | ||
| } | ||
| if argc > 4 { | ||
| return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("throw expected at most 3 arguments, got %v", argc-1)) | ||
| } | ||
| expectedTypes := []*Type{GeneratorType, ObjectType, ObjectType, ObjectType} | ||
| if argc > 1 && argc < 4 { | ||
| expectedTypes = expectedTypes[:argc] | ||
| } | ||
| if raised := checkMethodArgs(f, "throw", args, expectedTypes...); raised != nil { | ||
| return nil, raised | ||
| } | ||
| var v *Object | ||
| if argc > 2 { | ||
| v = args[2] | ||
| } | ||
| var tb *Object | ||
| if argc > 3 { | ||
| tb = args[3] | ||
| } | ||
| return toGeneratorUnsafe(args[0]).resume(f, nil, f.Raise(args[1], v, tb)) | ||
| } | ||
|
|
||
| func initGeneratorType(dict map[string]*Object) { | ||
| dict["send"] = newBuiltinFunction("send", generatorSend).ToObject() | ||
| dict["throw"] = newBuiltinFunction("throw", generatorThrow).ToObject() | ||
| GeneratorType.flags &= ^(typeFlagBasetype | typeFlagInstantiable) | ||
| GeneratorType.slots.Iter = &unaryOpSlot{generatorIter} | ||
| GeneratorType.slots.Next = &unaryOpSlot{generatorNext} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@trotterdylan not sure if
try/exceptblock is able to catch thisThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you want to do:
You could also merge the write() call below into this: