Control Flow

all of these words are typically marked ?COMP, which just causes an error if they're executed in immediate mode.

The Eight Quarks

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

The Six Atoms

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 ;

The Infinite Molecules

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-

$$$$ CASE STATEMENT CONTEST $$$$

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
	

   \ THE BOCHERT-LION CASE STATEMENT
   \ 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