Here we show an example in the domain of text processing. On the Macintosh, one may set an option that implies that two blanks in a text is replaced by a dot ('.'
) and a blank. The coroutine doubleSpaceToDot
below scans through a text string and makes these replacements.
charFilter: obj
nextChar: var char
input: var String
i: var integer
next -> c: var char:
i := i + 1
c := input.get[i]
send(ch: var char):
nextChar := ch
doubleSpaceToDot: obj
ch: var char
doubleSpaceToDot.suspend
cycle
ch := next
if (ch = ' ') :then
ch := next
if (ch = ' ') :then
send('.')
doubleSpaceToDot.suspend
send(' ')
doubleSpaceToDot.suspend
:else
send(' ')
doubleSpaceToDot.Suspend
send(ch)
doubleSpaceToDot.Suspend
:else
send(ch)
doubleSpaceToDot.Suspend
input := "Hello world What a nice day!\n"
loop: do
cycle
doubleSpaceToDot.call
put(nextChar)
if (nextChar = ascii.newline) :then
leave(loop)
The central element of this program is the coroutine object doubleSpaceToDot
. When the object is generated, it starts by executing a suspend
.
The object charFilter
has a local String input
, which holds the text string to be filtered. For the purpose of the example, we assign it the String
"Hello world What a nice day!\n"
.
The charFilter
then starts executing a cycle and the first statement in the cycle
is a call of doubleSpaceToDot
. This implies that doubleSpaceToDot
is resumed and executes its cycle
-statement.
It assigns the next char in input to ch
by executing ch := next
.
If ch = ' '
, it reads the next char and checks if it is also a blank. If blank it sends a dot ('.'
) and suspends executions, and when resumed it sends a blank. If the next char is not a blank, it sends a blank one at a time and suspends in between.
The else
-part of the outermost if
-statement, is executed If ch is not a blank and here it just sends ch and suspends execution.
A simple version using a method
A coroutine object may invoke methods and a method may suspend the coroutine object. In the next example we use this to make a simpler version of doubleSpaceToDot
. Here the send
-method is made a local method of doubleSpaceToDot
and send
executes the suspend
, suspending the enclosing doubleSpaceToDot
coroutine:
doubleSpaceToDot: obj
ch: var char
send(ch: var char):
c := ch
doubleSpaceToDot.suspend
doubleSpaceToDot.suspend
cycle
ch := next
if (ch = ' ') :then
ch := next
if (ch = ' ') :then
send('.')
send(' ')
:else
send(' ')
send(ch)
:else
send(ch)
When send
executes a suspend
, we have a situation where doubleSpaceToDot
has invoked send
after having read the 'o'
in "Hello"
and thus i = 5
. In the figure below, the left-side (A) shows the situation before suspend and the middle part (B) shows the situation after suspend.
The figure shows the activation stack. (A) shows that charFilter
has called doubleSpaceToDot
, which has called send
. Send
is currently the active object executing statements. Note that the elements of the activation stack may be objects as well as method objects. Here the bottom element is an object and the other elements are method objects.
When send
executes suspend
, the situation is shown at (B). CharFilter.suspend
has the effect that control is returned to after charFilter
has called doubleSpaceToDot
. The arrow from doubleSpaceToDot
illustrates that it is suspended and that send
is at the top of its activation stack.
When charFilter
makes another call to doubleSpaceToDot
, the situation is as in the (C)-part, but with ch = ' '
and i = 6
. As can be seen, doubleSpaceToDot
now again points to the calling charFilter
-object.