Code logic for sample code.
Generalities
The general idea is a maze of C routines that call each other but never return.
Each call represents a character moving in its stream from one process to the
next.
This causes the stack to grow and not shrink.
Occasionally a long jump resets the stack,
in which nothing of value has been kept, to an earlier state.
It would be strategic to get the compiler to do tail recursion
but I don't know how.
The pattern is much like goto with arguments, a bit like Actors.
The code is in C but C++ would be better in a few ways.
The node would be replaced by a class instance.
Two types of node are defined: unode and dnode.
dnodes define the next step down stream; a pointer to a dnode
is a place to deliver a character of a stream to its next processing stage.
If d locates a dnode then (d->nsn)(c, d, p);
is a non returning invocation that dispatches the character c
into the stream denoted by d.
p locates a unode (see below) to invoke when subsequent
characters of the stream are needed.
If p locates a unode then (p->code)(p); causes
the next character for that unode to be produced.
unodes implement the pulls referred to in the
introduction.
Reentrence & Thread Safety
As long as there are not two threads in the same stream, all is well.
If two nodes point to the same dnode there is a race condition
and also an opportunity for two processes to get into the same stream.
The pattern is safe, however, in the interleave test case in main.c, for there is just one thread between the two inputs.
Sample stream processes
The comon structure dnode is found in each of the down stream
node structures.
It locates the code for the transformation and also locates the next
processing node for the stream.
For each particular processing function a
a structure is defined whose first field is a dnode
and whose other fields carry any necessary node state as described in the
introduction.
To Upper Case
x2uc merely changes lower case letters
to upper case and does not otherwise change the stream.
cx2uc takes a dnode pointer and returns another pointer
with this transformation prepended.
Stream Splitter
Tee produces two identical streams in place of one.
A new structure dupS is defined which has an embedded dnode
called n, but also includes a variable character cell, s,
to store the character to be duplicated, a variable cell, sav,
in which to remember the continuation of the input stream,
and the constant address of a unode mate,
allocated to this stream process, which serves as a point to request the
second copy of the character for the second stream.
The dnode
located by the mate field is constant and points to dup2
and the dnode for this stream process.
ctee takes two downstream locators (character sincks) and returns a new sink. Characters sent to the new sink are serially supplied to both of the old sinks.
Repeater
This code suppplies a steady stream of the
same character. crptr takes a dnode (to which to deliver its stream,
and returns a unode which may be invoked to turn the stream on.
Interleaver
This takes characters alternately from two inputs and forwards them.
It applies flow control so as not to let one input stream get more
than one character ahead of the other.