Okay, I got a bit wiser this time.
First, this thing was created to fulfill a Rosetta Code task. I like that site and from time to time, I contribute to it as well.
So it seems the library (found here:
https://theforth.net/package/co/0.0.1) was just a quick hack. Now, the circular buffer is nothing special (compare:
BUFFER.4TH). We start off with two pointers,
HEAD and
TAIL - which are identical at startup.
The moment an element is added, it's added at the HEAD. When an element is removed, it's removed from the TAIL - hence a "First in, first out" (FIFO) stack. Since the return stack is essentially a LIFO (Last in, first out) stack, I'd say that's a significant difference. ;-)
: ADJUST ( a--a') START - CQ# 1- AND START + ;
: PUT ( n-- ) TAIL @ TUCK ! CELL+ ADJUST TAIL ! ;
: TAKE ( --n ) HEAD @ DUP @ SWAP CELL+ ADJUST HEAD ! ;
: CQINIT ( -- ) START DUP HEAD ! TAIL ! ;
: NOEMPTY? ( --f ) HEAD @ TAIL @ <> ;
Now, COINIT simply initializes the pointers HEAD and TAIL. Since they are identical, NOEMPTY? renders false. PUT and TAKE are the equivalent of >R and R>. ADJUST wraps the "stack" so when a pointer reaches the "end", it returns to the beginning.
To illustrate that, let's consider a favorite word of mine (I use it often to annoy Python programmers. I know, I'm not a nice guy ;)
: am12pm 11 + 12 mod 1+ ;
Now lets do 24 hours:
: 24h cr 25 0 do ." ( " i . i am12pm . ." )" loop cr ; 24h
( 0 12 )( 1 1 )( 2 2 )( 3 3 )( 4 4 )( 5 5 )( 6 6 )( 7 7 )( 8 8 )( 9 9 )( 10 10 )( 11 11 )( 12 12 )( 13 1 )( 14 2 )( 15 3 )( 16 4 )( 17 5 )( 18 6 )( 19 7 )( 20 8 )( 21 9 )( 22 10 )( 23 11 )( 24 12 )
So that's how Americans name their hours. The wrapping here is done by MOD, the wrapping in ADJUST is done by a bit mask (that's why the buffer needs be "a power of two").
Now, those are all the words concerning the FIFO ring buffer.
Now I'm pretty sure that a co-routine is registered by CO:. I further assume that CO transfers control to the next one in line. I think ;CO is the equivalent of GRAB - but this is where it gets murky. I think GO is the thing that controls this "round robin" kind of cooperative multitasking.
What I did learn from the Rosetta Code page is that I've not used the example correctly. It should be invoked like this: cat synco.4th | pp4th -x synco.4th
And indeed, that works as advertised:
\ synco.fs Synchronous concurrency for RosettaCode
\ co.fs Coroutines by continuations.
include lib/anscore.4th
include lib/ansfile.4th
include lib/co.4th
\ * CHANNELS LEXEME
2 ARRAY CHAN
: CHAN? ( a--f ) DUP @ SWAP CELL+ @ XOR ;
\ * READER LEXEME
4096 CONSTANT L#
L# STRING Line
: READER
CO:
BEGIN \ Press CTRL-D or CTRL-Z to stop
Line L# STDIN read-line THROW
WHILE \ Use the length of the string read!
>ZERO Line CHAN 2!
CO
REPEAT DROP
Line DUP CHAN 2!
\ -- Wait for report back
BEGIN CO CHAN CHAN? UNTIL
\ -- Have it, show and go
CR S" -------" TYPE
CR S" LINES: " TYPE CHAN @ ?
;
\ * WRITER LEXEME
VARIABLE X
: WRITER
CO:
BEGIN
CHAN CHAN?
WHILE
CHAN @ COUNT TYPE CR
1 X +!
CO
REPEAT
\ -- Chance to stop other writers
CO
\ -- First of writers reports back
\ -- the shared global counter
CHAN CHAN? 0=
IF
0 X CHAN 2!
CO
THEN
;
\ * RUNNER
CQINIT 0 X ! READER WRITER ( WRITER WRITER :-) GO CR DEPTH .
-------
LINES: 65
$
So, that's what I got for you. Note this routine is also playing with the return stack - so I'm not sure it is actually "safer"..
Hans Bezemer