SystemOrganization addCategory: #Generator! Stream subclass: #Generator instanceVariableNames: 'block next continue' classVariableNames: '' poolDictionaries: '' category: 'Generator'! !Generator class methodsFor: 'instance-creation' stamp: 'lr 1/8/2009 15:54'! on: aBlock ^ self basicNew initializeOn: aBlock! ! !Generator methodsFor: 'testing' stamp: 'lr 1/8/2009 16:42'! atEnd ^ continue isNil! ! !Generator methodsFor: 'accessing' stamp: 'lr 2/10/2010 09:15'! close "Close the receiving generator and unwind its ensure-blocks." continue unwindTo: block. continue := block := next := nil! ! !Generator methodsFor: 'accessing' stamp: 'lr 4/26/2009 11:50'! contents "Answer the contents of this generator. Do not call this method on infinite generators." | stream | stream := (Array new: 10) writeStream. [ self atEnd ] whileFalse: [ stream nextPut: self next ]. ^ stream contents! ! !Generator methodsFor: 'private' stamp: 'lr 1/8/2009 16:41'! fork | result | block reentrant value: self. thisContext swapSender: continue. result := next. continue := next := nil. ^ result! ! !Generator methodsFor: 'initialization' stamp: 'lr 1/8/2009 16:18'! initializeOn: aBlock block := aBlock. self reset! ! !Generator methodsFor: 'accessing' stamp: 'lr 2/10/2010 09:15'! next "Generate and answer the next object in the receiver." ^ self atEnd ifFalse: [ continue := thisContext swapSender: continue ]! ! !Generator methodsFor: 'accessing' stamp: 'lr 2/10/2010 09:16'! nextPut: anObject "Add anObject into the generator. A synonym to #yield: and value:." | previous | previous := next. next := anObject. continue := thisContext swapSender: continue. ^ previous! ! !Generator methodsFor: 'accessing' stamp: 'lr 2/10/2010 09:16'! peek "Answer the upcoming object of the receiver." ^ next! ! !Generator methodsFor: 'printing' stamp: 'lr 1/8/2009 16:21'! printOn: aStream aStream nextPutAll: self class name; nextPutAll: ' on: '; print: block! ! !Generator methodsFor: 'public' stamp: 'lr 1/8/2009 16:39'! reset next := nil. continue := thisContext. [ self fork ] value! ! !Generator methodsFor: 'accessing' stamp: 'lr 2/10/2010 09:16'! size "A generator does not know its size." ^ self shouldNotImplement! ! !Generator methodsFor: 'public' stamp: 'lr 2/10/2010 08:47'! value: anObject ^ self nextPut: anObject! ! !Generator methodsFor: 'public' stamp: 'lr 4/26/2009 11:52'! yield: anObject ^ self nextPut: anObject! ! TestCase subclass: #GeneratorTest instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Generator'! !GeneratorTest class methodsFor: 'accessing' stamp: 'lr 2/10/2010 08:34'! packageNamesUnderTest ^ #('Generator')! ! !GeneratorTest methodsFor: 'generators' stamp: 'lr 1/8/2009 16:29'! fibonacciSequence "Yields an infinite sequence of fibonacci numbers." ^ Generator on: [ :generator | | a b | a := 0. b := 1. [ a := b + (b := a). generator yield: a ] repeat ]! ! !GeneratorTest methodsFor: 'generators' stamp: 'lr 1/8/2009 15:49'! numbersBetween: aStartInteger and: aStopInteger "Yields the nubmers between aStartInteger and aStopInteger." ^ Generator on: [ :generator | aStartInteger to: aStopInteger do: [ :value | generator yield: value ] ]! ! !GeneratorTest methodsFor: 'testing' stamp: 'lr 1/8/2009 16:33'! testAtEnd | generator | generator := self numbersBetween: 1 and: 3. self deny: generator atEnd. generator next. self deny: generator atEnd. generator next. self deny: generator atEnd. generator next. self assert: generator atEnd! ! !GeneratorTest methodsFor: 'testing' stamp: 'lr 2/10/2010 09:18'! testClose | generator doEnsure notEnsure | doEnsure := notEnsure := 0. [ generator := Generator on: [ :g | [ g yield: 1; yield: 2 ] ensure: [ doEnsure := doEnsure + 1 ] ]. self assert: doEnsure = 0; assert: notEnsure = 0. self assert: generator peek = 1. self assert: doEnsure = 0; assert: notEnsure = 0. generator close. self assert: doEnsure = 1; assert: notEnsure = 0 ] ensure: [ notEnsure := notEnsure + 1 ]. self assert: doEnsure = 1; assert: notEnsure = 1! ! !GeneratorTest methodsFor: 'testing' stamp: 'lr 4/26/2009 11:51'! testContents | generator | generator := self numbersBetween: 1 and: 3. self assert: generator contents = #(1 2 3)! ! !GeneratorTest methodsFor: 'testing' stamp: 'lr 1/8/2009 16:45'! testEmpty | generator | generator := Generator on: [ :g | ]. self assert: generator atEnd. self assert: generator peek isNil. self assert: generator next isNil! ! !GeneratorTest methodsFor: 'testing' stamp: 'lr 2/10/2010 09:02'! testEnsure | generator | generator := Generator on: [ :g | [ g yield: 1; yield: 2 ] ensure: [ g yield: 3 ] ]. self assert: generator upToEnd asArray = #( 1 2 3 )! ! !GeneratorTest methodsFor: 'testing' stamp: 'lr 1/8/2009 16:50'! testFibonacci | generator | generator := self fibonacciSequence. self assert: (generator next: 10) asArray = #( 1 1 2 3 5 8 13 21 34 55 )! ! !GeneratorTest methodsFor: 'testing' stamp: 'lr 1/8/2009 16:33'! testNext | generator | generator := self numbersBetween: 1 and: 3. self assert: generator next = 1. self assert: generator next = 2. self assert: generator next = 3. self assert: generator next isNil! ! !GeneratorTest methodsFor: 'testing' stamp: 'lr 1/8/2009 16:45'! testPeek | generator | generator := self numbersBetween: 1 and: 3. self assert: generator peek = 1. self assert: generator peek = 1. generator next. self assert: generator peek = 2! ! !GeneratorTest methodsFor: 'testing' stamp: 'lr 1/8/2009 16:35'! testReset | generator | generator := self numbersBetween: 1 and: 3. self assert: generator next = 1. self assert: generator next = 2. generator reset. self assert: generator next = 1. self assert: generator next = 2. self assert: generator next = 3. self assert: generator next = nil. generator reset. self assert: generator next = 1! ! !GeneratorTest methodsFor: 'testing' stamp: 'lr 1/8/2009 16:46'! testSimple | generator | generator := Generator on: [ :g | g yield: 1; yield: 2 ]. self assert: generator upToEnd asArray = #( 1 2 )! !