all of these words are typically marked ?COMP, which just causes an error if they're executed in immediate mode.
these can be arranged into a neat table, and are partially based off of this conference paper and general zeitgeist:
| Forwards | Backwards | |
|---|---|---|
| Unconditional Branching | >BRANCH |
<BRANCH |
| Conditional (On-Zero) Branching | ?>BRANCH |
?<BRANCH |
| Marking | >MARK |
<MARK |
| Resolving/Backpatching | >RESOLVE |
<RESOLVE |
often, >BRANCH and <BRANCH are merged into one BRANCH and similarly ?>BRANCH and ?<BRANCH are merged into one ?BRANCH. additionally ?BRANCH may be called 0BRANCH or (?BRANCH) or JZ or (IF), BRANCH may be called (BRANCH) or JMP or (ELSE). >MARK >RESOLVE <MARK <RESOLVE can also be called (FORWARD FORWARD) (BACK BACK) a la albert van der hoorst's ciforth.
as a rule of thumb the MARK always comes before the RESOLVE of any type. so in an IF THEN, IF uses >MARK and THEN uses >RESOLVE. in a BEGIN UNTIL, BEGIN uses <MARK and UNTIL uses <RESOLVE.
these can be implemented thusly (higher level definitions)
\ WITH A >BRANCH/<BRANCH SPLIT
: >BRANCH R> DUP @ + >R ;
: <BRANCH R> DUP @ - >R ;
: ?>BRANCH R> DUP @ ROT 0= 0= AND + >R;
: ?<BRANCH R> DUP @ ROT 0= 0= AND - >R;
: >MARK HERE 0 , ;
: <MARK HERE ;
: >RESOLVE HERE OVER - SWAP ! ;
: <RESOLVE HERE SWAP - , ;
\ WITHOUT
: BRANCH R> DUP @ + >R ;
: ?BRANCH R> DUP @ ROT 0= 0= AND + >R;
: >MARK HERE 0 , ;
: <MARK HERE ;
: >RESOLVE HERE OVER - SWAP ! ;
: <RESOLVE HERE - , ;
(these implementations all use relative offsets, which is the most common implementation, but it's not uncommon to find implementations with absolute offsets instead, in which case : BRANCH R> @ >R ;, &c.)
in a move that can only be described as Completely and Utterly Baffling, or absolutely CUBbed out for short, there is an equivalent of ?BRANCH for on-nonzero branching named, you didn't guess it, ?BRANCH (with the on-zero branch named 0BRANCH). this is the system eulexForth uses (it also, even more specifically, uses BRANCH-TO to refer to <BRANCH <RESOLVE, and FORWARD-BRANCH to refer to >BRANCH with >MARK attached to it). it uses it in exactly two places: in the definition of ?DO, defined as 2DUP 2>R = FORWARD-?BRANCH HERE, which could easily be 2DUP 2>R - FORWARD-0BRANCH HERE (or even 2DUP 2>R <> FORWARD-BRANCH HERE, given that it already uses <> in another part of the same control flow structure!), and in +LOOP... directly after a NOT. this isn't a totally novel concept, Forth Dimensions has an article about TBRANCH as an on-nonzero equivalent to ?BRANCH (and NIF NWHILE NUNTIL for not-if, not-while, not-until), and there are probably dialects of Machine Forth that contain such a thing. i've personally used the convention of -BRANCH and -IF -WHILE -UNTIL (which has some historical precedent, citation Chuck Moore Used -IF but like only for a period of time and then he started using it for other things because for instance F31 Machine Forth specifically uses -IF for on-non-Negative for reasons scientists are still trying to understand to this day (it's me i'm scientists)).
a more generalized portable version of these are described in the open interpreter wordset
these are actually included in the Forth-2012 standard and ANS Forth standard and most standards really. they are ever significantly more portable than the quarks but slightly less flexible. they include:
| Forwards | Backwards | |
|---|---|---|
| ?BRANCH | IF | UNTIL |
| BRANCH | AHEAD | AGAIN |
| MARK/RESOLVE | THEN | BEGIN |
AHEAD and AGAIN (equivalent to 0 IF and 0 UNTIL) were largely introduced in order to facilitate more complex control flow, which is why they are core-ext words rather than core words. this also includes the unsavory CS-PICK and CS-ROLL: equivalents to PICK and ROLL for the "control-flow stack" (typically just the data stack, but sometimes the elements are double-width to accomodate a tag for checking. this is why ?PAIRS exists).
\ UNTAGGED
: IF COMPILE ?>BRANCH >MARK ; IMMEDIATE
: AHEAD COMPILE >BRANCH >MARK ; IMMEDIATE
: THEN >RESOLVE ; IMMEDIATE
: UNTIL COMPILE ?<BRANCH <RESOLVE ; IMMEDIATE
: AGAIN COMPILE <BRANCH <RESOLVE ; IMMEDIATE
: BEGIN <MARK ; IMMEDIATE
: CS-ROLL ROLL ;
: CS-PICK PICK ;
these atoms can be combined in ways uncountable. the really obvious ways are the ones that are so obvious they are practically reference implementations in the Forth-2012 standard:
: WHILE POSTPONE IF 1 CS-ROLL ; IMMEDIATE
: REPEAT POSTPONE AGAIN POSTPONE THEN ; IMMEDIATE
: ELSE POSTPONE AHEAD 1 CS-ROLL POSTPONE THEN ; IMMEDIATE
(POSTPONE here is equivalent to [COMPILE])
this may give you some ideas: if REPEAT is the same as AGAIN THEN, surely you could turn that AGAIN into an UNTIL and end up with some unholy abomination with two exit conditions where one exit condition executes additional code before finally exiting. and you would be completely correct! and if you also thought about using multiple WHILEs as long as you match them with more THENs at the end of the loop, that is also completely valid! there's a diagramming system called StackFlow with a downright beautifully intuitive representation for seemingly mismatched control flow structures.
there's also an alternate name for ?DUP IF: ?IF (called ?DUP-IF in gforth). alongside ?WHILE and ?UNTIL, which are really obscure but implemented in StrongForth. these are preferred because... ?DUP is a nasty word because it leaves a variable number of things on the stack and that is not a wise thing to do".
there is also a short-circuiting AND for IFs called ANDIF (also here too with ANDWHILE) or THENIF
there is also the ever-useful DO LOOP, which indicates a counted loop.
: DO COMPILE 2>R [COMPILE] BEGIN ; IMMEDIATE
: (LOOP) R> 2R> 1+ 2DUP <= ROT >R SWAP >R SWAP >R ;
: UNLOOP R> 2R> 2DROP >R ;
: LOOP COMPILE (LOOP) [COMPILE] UNTIL COMPILE UNLOOP ; IMMEDIATE
: LEAVE R> 2R> NIP DUP 2>R >R ;
: I 2R> OVER >R >R ; \ IDENTICAL TO R@
: I' 2R> R@ -ROT 2>R ;
: J 2R> 2R> OVER >R >R -ROT 2>R ;
as well as its sibling, DO +LOOP, which got catastrophically redefined in the '83 standard in a way that only makes sense from an assembly perspective on CPUs with an overflow flag (and made implementing LEAVE nontrivial to say the least and potentially compatible with an ordinary BEGIN WHILE REPEAT loop depending on implementation). i'll provide only the '79 standard definition here.
: (+LOOP) R> SWAP 2R> ROT DUP 0> IF + 2DUP < ELSE
+ 2DUP >= THEN ROT >R SWAP >R SWAP >R ;
: UNLOOP R> 2R> 2DROP >R ;
: +LOOP COMPILE (+LOOP) [COMPILE] UNTIL COMPILE UNLOOP ; IMMEDIATE
: LOOP 1 [COMPILE] LITERAL [COMPILE] +LOOP ; IMMEDIATE
this is a classic case of the code looking really funky even though the behaviour is dead simple (if the step is nonpositive, terminate on index<limit. if the step is positive, terminate on index>=limit).
there are then even more variations such as DO -LOOP, ?DO LOOP (if the index is already the limit then skip the whole loop and don't even do one iteration), DO /LOOP (use an unsigned comparison and also an inclusive range[?]), DO \LOOP (use an unsigned comparison but going downward and with an exclusive range), DO +/LOOP (??? (what. (has forth gone too far))), enhanced DO LOOPs (if you've used the else: on a loop in python you understand how LOOP--FALLTHRU: works)
there is also the FOR NEXT loop, which is similar to a DO LOOP except that the index is counting downward from the limit to zero, inclusive. this more closely mimicks hardware counted loops, such as the 8086's loop instruction.
: FOR COMPILE >R [COMPILE] BEGIN ; IMMEDIATE
: (NEXT) R> R> 1- DUP >R 0< SWAP >R ;
: NEXT COMPILE (NEXT) [COMPILE] UNTIL COMPILE RDROP ; IMMEDIATE
which can also be extended by specifying precisely Where you want NEXT to return to (which may not be the FOR if you want the first iteration to be different!)
\ REDEFINING USING MARK & RESOLVE
\ TO AVOID BEING BROKEN BY TAGS
: FOR COMPILE >R <MARK ; IMMEDIATE
: NEXT COMPILE (NEXT) ?<BRANCH <RESOLVE COMPILE RDROP ; IMMEDIATE
: AFT DROP >BRANCH >MARK <MARK SWAP ; IMMEDIATE
AFT behaves not unlike IF in that it can be followed by an ELSE and must be matched by THEN.
there's also Parnas's IT IFF BREAK CONTINUE ENDIT CAND COR SUBCASES ENDSUB set of words for state machines that i frankly do not understand enough to do justice here
wait.
parnas's it...ti stru-
oh god n-
FIG is sponsoring a contest for the best CASE statement for FORTH.
Prize......$100 ($50 from FIG and $40 from FORTH, Inc.)
SHUT TH- actually, wait, how much is that given all the like. inflation an-
Furthermore, entries will be considered as experimental proposals for possible inclusion in the future FORTH Standard.
it's $400. okay. wow holy macaro-
Judging Criteria...... A. Conformity to rules B. Generality of statement C. Simplicity of statement D. Self-identifying function E. "FORTH-like" style
okay STOP. stop.
okay?
okay. right. the Case Contest. this is what modern programmers would call a "switch statement". so there's... you know what, i'll just show you. i'm not going to implement all of these, some of them are in assembly and i cannot be bothered to learn enough 8080asm to decode these Runes. these mere Glyphs. Their incomprehensible runes. Our beautiful and elegant 8086. okay whatever look
\ NO CASE STATEMENT
: FRUIT DUP 2 = IF DROP ." TOMATO" EXIT THEN
DUP 3 = IF DROP ." CUCUMBER" EXIT THEN
DUP 5 = IF ." PUMPKIN" EXIT THEN
." ?" ;
\ NEPTUNE'S CASE STATEMENT
: FRUIT DO-CASE
2 CASE ." TOMATO" EXIT END-CASE
3 CASE ." CUCUMBER" EXIT END-CASE
5 CASE ." PUMPKIN" EXIT END-CASE
END-CASES ." ?" ;
\ EAKER'S CASE STATEMENT
\ 2012 STANDARD, HAS A FEW EXTENSIONS HERE
: FRUIT CASE
2 OF ." TOMATO" ENDOF
3 OF ." CUCUMBER" ENDOF
5 OF ." PUMPKIN" ENDOF
." ?"
ENDCASE ;
\ MUNSON'S CASE STATEMENT
: .TOMATO ." TOMATO" ; : .CUCUMBER ." CUCUMBER" ;
: .PUMPKIN ." PUMPKIN" ; : .? ." ?" ;
BYTECASE FRUIT
2 .TOMATO 3 .CUCUMBER 5 .PUMPKIN .? END-CASE
\ NOTE: VERY EFFICIENT. GENUINELY A JUMP TABLE. : FRUIT CASE ." ?" ENDCASE CASE ." ?" ENDCASE CASE ." TOMATO" ENDCASE CASE ." CUCUMBER" ENDCASE CASE ." ?" ENDCASE CASE ." PUMPKIN" ENDCASE ." ?" ENDCASES ; \ BRECHER'S CASE STATEMENT : FRUIT CASEOF 2 CASE ." TOMATO" ESAC 3 CASE ." CUCUMBER" ESAC 5 CASE ." PUMPKIN" ESAC OTHERWISE ." ?" ENDCASEOF ; \ SL5'S CASE STATEMENT : FRUIT CASE 2 =: ." TOMATO" ;; 3 =: ." CUCUMBER" ;; 5 =: ." PUMPKIN" ;; NOCASE =: ." ?" ;; CASEND ; \ ELVEY'S CASE STATEMENT : FRUIT DO-CASE 2 CASE ." TOMATO" END-CASE 3 CASE ." CUCUMBER" END-CASE 5 CASE ." PUMPKIN" END-CASE ." ?" END-CASES ; \ EMERY'S CASE STATEMENT : FRUIT <CASE 2 ." TOMATO" CASE 3 ." CUCUMBER" CASE 5 ." PUMKPIN" ELSE . ." ?" CASE> ; \ FITTERY'S CASE STATEMENT : FRUIT BEGIN-CASES 2 CASE ." TOMATO" END-CASE 3 CASE ." CUCUMBER" END-CASE 5 CASE ." PUMPKIN" END-CASE DROP ." ?" END-CASES ; \ GILES'S CASE STATEMENT : FRUIT DO-CASE 2 CASE ." TOMATO" END-CASE 3 CASE ." CUCUMBER" END-CASE 5 CASE ." PUMPKIN" END-CASE OTHERWISE ." ?" END-DO-CASE ; \ KATTENBERGS'S CASE STATEMENT : FRUIT 2 CASE ." TOMATO" ESAC 3 CASE ." CUCUMBER" ESAC 5 CASE ." PUMPKIN" ESAC OTHER ." ?" ENDCASE ; \ PERRY'S CASE STATEMENT : FRUIT BEGIN-CASE 2 CASE ." TOMATO" END-CASE 3 CASE ." CUCUMBER" END-CASE 5 CASE ." PUMPKIN" END-CASE ELSE-CASE ." ?" END-CASE END-CASES ; \ POWELL'S CASE STATEMENT : FRUIT 2 CASE ." TOMATO" THEN 3 CASE ." CUCUMBER" THEN 5 CASE ." PUMPKIN" THEN ELSE ." ?" THEN DROP ; \ SELZER'S CASE STATEMENT : FRUIT 2 CASE ." TOMATO" ELSE 3 CASE ." CUCUMBER" ELSE 5 CASE ." PUMPKIN" ELSE ." ?" THEN THEN THEN ; \ WILSON'S CASE STATEMENT : .TOMATO ." TOMATO" ; : .CUCUMBER ." CUCUMBER" ; : .PUMPKIN ." PUMPKIN" ; : .? ." ?" ; 4 <CASE (FRUIT) 1 -> .? (FRUIT) =CASE 2 -> .TOMATO (FRUIT) =CASE 3 -> .CUCUMBER (FRUIT) =CASE 4 -> .? (FRUIT) =CASE 5 -> .PUMPKIN (FRUIT) =CASE : FRUIT DUP 1 6 WITHIN IF (FRUIT) ELSE .? THEN ; \ THE WITT-BUSLER CASE STATEMENT : .TOMATO ." TOMATO" ; : .CUCUMBER ." CUCUMBER" ; : .PUMPKIN ." PUMPKIN" ; : .? ." ?" ; : FRUIT CASE .? .? .TOMATO .CUCUMBER .? .PUMPKIN OTHERCASE .? DONE ; \ THE KITT PEAK GODO CONSTRUCT : .TOMATO ." TOMATO" ; : .CUCUMBER ." CUCUMBER" ; : .PUMPKIN ." PUMPKIN" ; : .? ." ?" ; : FRUIT GODO .? .? .TOMATO .CUCUMBER .? .PUMPKIN .? THEN ; \ HELMER'S CASE SEL AND COND STRUCTURES : .TOMATO ." TOMATO" ; : .CUCUMBER ." CUCUMBER" ; : .PUMPKIN ." PUMPKIN" ; : .? ." ?" ; : FRUIT SEL << 2 WHEN .TOMATO > > << 3 WHEN .CUCUMBER > > << 5 WHEN .PUMPKIN > > OTHERWISE .? ENDSEL ; \ LERNER'S CASE STATEMENT : .TOMATO ." TOMATO" ; : .CUCUMBER ." CUCUMBER" ; : .PUMPKIN ." PUMPKIN" ; : .? ." ?" ; CASE FRUIT 2 IS .TOMATO 3 IS .CUCUMBER 5 IS .PUMPKIN OTHERS .? \ FEY'S CASE STATEMENT : .TOMATO ." TOMATO" ; : .CUCUMBER ." CUCUMBER" ; : .PUMPKIN ." PUMPKIN" ; : .? ." ?" ; MCASE: FRUIT KEYED [ 2 , ] .TOMATO [ 3 , ] .CUCUMBER [ 5 , ] .PUMPKIN DEFAULT: .? ; \ PARNA'S IT ... TI STRUCTURE : FRUIT IT DUP 2 = IFF ." TOMATO" BREAK DUP 3 = IFF ." CUCUMBER" BREAK DUP 5 = IFF ." PUMPKIN" BREAK DROP TRUE IFF ." ?" BREAK ENDIT ;
extremely autistic-eyed readers will notice that i have omitted lyons's case statement. this is because it hurts my brain and i have no clue how it would be used.
in my personal opinion, it's not that deep . you can just . not use CASE . it's kinda easy