Earlier quoted context omitted.
Right. And once again, you’ll also notice that no one is actually coding anything useful in Forth.
As I like to say: "C is a language that solves a million problems. Forth is a million languages that solve almost nothing." :-P I've been reading about Forth for 30-40 years. The dual stack is easy to understand. My problem is that I cannot see how control flow works in Forth, e.g. a simple if-then-else. I think that something as fundamental as an if-then-else should be obvious in a useful language. Heck, it's obviou…
I will take a run at explaining IF ENDIF (endif is the Fig Forth term, used here to avoid confusion)
?BRANCH is an instruction in the virtual machine. It jumps if top of stack=0 . The offset (or address)that it jumps to is the memory word right after the ?BRANCH token. Like this:
Forth definition of IF
: IF COMPILE ?BRANCH HERE 0 , ; IMMEDIATE
At compile time IF "compiles" the token for ?BRANCH but then interprets "HERE 0 ,"
(IF is an IMMEDIATE word that executes even if the compiler is turned on)
HERE is like $ in Assembler, ie: the address where code is being laid down. It is simply left on the data stack. HERE is the address where the will be stored... later.
0 is a zero, that is pushed onto the data stack.
"Comma" (,) pops the zero and puts it in memory address HERE but! it advances the system memory pointer 1 integer width.
The zero is now a place holder in memory to be filled in by ENDIF.
: ENDIF( addr -- ) HERE OVER - SWAP ! ; IMMEDIATE
ENDIF needs that address left behind by IF shown in comment as addr.
ENDIF gets the new value of HERE which of course is different because we will have compiled some code after the IF keyword.
All we need to do is do NEWHERE-OLDHERE to get the offset for ?BRANCH.
That is covered by the forth code ( oldhere-on-stack) HERE OVER -
This will make the data stack be: ( OLDHERE offset )
If we do a SWAP we just need the store operator '!' to put the offset into memory.
For the morbidly curious here is the definition of ELSE. :-)
: ELSE COMPILE BRANCH HERE 0 , SWAP [COMPILE] ENDIF ; IMMEDIATE
So loops are just more of the same... (all loops jump back to BEGIN. BRANCH is an unconditional jump instruction)
: BEGIN HERE ; IMMEDIATE
: AGAIN COMPILE BRANCH HERE - , ; IMMEDIATE
: UNTIL COMPILE ?BRANCH HERE - , ; IMMEDIATE
: WHILE [COMPILE] IF SWAP ; IMMEDIATE
: REPEAT [COMPILE] AGAIN [COMPILE] ENDIF ; IMMEDIATE