Control Flow in BBj
The loop and branch verbs behave the same in PRO/5-style line-numbered code and in BBj. The examples on this page use unnumbered code with labels, the style recommended for new code; every rule below applies equally to line-numbered programs.
The Shared Loop and Subroutine Stack
Rule
FOR, WHILE, REPEAT and GOSUB each
push an entry onto one shared FOR/GOSUB stack. Leave a construct only through its own end verb
(NEXT, WEND, UNTIL, RETURN), or through
BREAK, CONTINUE or EXITTO -- never through a bare
GOTO.
Why
The stack is how NEXT, WEND, UNTIL and
RETURN find and validate the construct they close; EXITTO explicitly
discards the top entry, while a bare GOTO out of the construct does not.
Edge
A GOTO out of a loop or subroutine leaves a stale entry on the stack, so a
later NEXT, WEND, UNTIL or RETURN finds the
wrong top entry and raises an error. RESET clears all incomplete FOR, GOSUB, REPEAT
and WHILE entries.
Evidence
EXITTO Verb, WHILE .. WEND Verbs, GOSUB Verb, RETURN Verb, RESET Verb (PRO/5, state the shared stack and the RESET rule).
BBj Modified/Enhanced Verbs (BBj generation: none of these five verbs is itself modified in BBj, so the PRO/5 pages remain the BBj documentation).
Example
rem 'leave a loop with BREAK, not GOTO, so the stack stays clean for i=1 to 10 if i=5 then break next i
FOR .. NEXT
Rule
FOR var=start TO end {STEP step} ... NEXT {var} -- in BBj, the
var on NEXT is optional. The loop body always runs at least once,
because the termination check happens at NEXT, not at FOR. Guard an
empty range before the loop if the body must not run when there is nothing to iterate.
Why
TO and STEP are evaluated once, when FOR runs,
and their results are saved; the test that ends the loop sits at NEXT, after the
body has already executed once.
Edge
FOR i=0 TO -1 still runs once, with i=0 (primer
gotchas/6, owner-signed). Changing the limit variable inside the loop body does not
change how many times the loop runs -- the limit was already evaluated and saved. STEP
cannot be zero. The loop's entry sits on the shared FOR/GOSUB stack (see "The Shared Loop and
Subroutine Stack" above).
Evidence
FOR .. NEXT Verbs (PRO/5) -- states the rule directly: "A FOR .. NEXT loop always executes at least once because the termination check is performed at the NEXT statement."
FOR .. NEXT Verbs - BBj (BBj) -- states the same rule for BBj.
Owner's observation, 2026-09-29 (.planning/OWNER-NOTES.md): the BBj page
omitted this fact even though the PRO/5 page states it.
Example
rem 'guard an empty vector before the loop -- FOR still runs once otherwise
items! = new BBjVector()
if items!.size() > 0 then
for i=0 to items!.size()-1
print items!.get(i)
next i
endif
WHILE .. WEND
Rule
WHILE expr ... WEND -- the loop body runs only while expr is
nonzero (true). A condition already false when WHILE is reached is never entered.
Why
The test sits at WHILE, before the body -- the opposite of
FOR .. NEXT, whose test sits at NEXT, after the body.
Edge
A condition false at the start skips the loop entirely: zero iterations, unlike
FOR, which always runs once. WHILE shares the FOR/GOSUB stack; leave
early with BREAK or EXITTO, never GOTO (see "The Shared
Loop and Subroutine Stack" above).
Evidence
WHILE .. WEND Verbs (PRO/5, states the skip-when-false behavior and the shared stack).
BBj Modified/Enhanced Verbs (BBj generation: WHILE .. WEND is not modified in BBj).
Example
rem 'a false condition at the start skips the loop entirely count=0 while count<0 count=count+1 wend print count
REPEAT .. UNTIL
Rule
REPEAT ... UNTIL expr -- the body always runs at least once, because the
test sits at UNTIL, after the body.
Why
Like FOR .. NEXT (and unlike WHILE .. WEND), the terminating
test is evaluated after the body executes, not before it.
Edge
The expr on UNTIL is numeric, and the loop repeats until it
evaluates true. Leave early with BREAK or EXITTO, never
GOTO.
Evidence
REPEAT .. UNTIL Verbs (PRO/5, describes the loop and the EXITTO-to-exit-early rule).
BBj Modified/Enhanced Verbs (BBj generation: REPEAT .. UNTIL is not modified in BBj).
Example
rem 'the body runs once even though the test starts true count=1 repeat print count until count>=1
BREAK and CONTINUE
Rule
BREAK ends the innermost loop or SWITCH. CONTINUE
jumps to the next UNTIL, WEND or NEXT.
Why
Both verbs find their target by scanning forward from their own location and counting nested constructs of the same kind.
Edge
Inside a SWITCH that is itself inside a loop, BREAK ends only
the SWITCH, not the loop -- use EXITTO to leave the loop from there.
CONTINUE in a SWITCH inside a loop ends the SWITCH and
continues with the loop's next iteration. CONTINUE with no active loop raises
!ERROR=27.
Evidence
BREAK Verb, CONTINUE Verb (PRO/5).
BBj Modified/Enhanced Verbs (BBj generation: neither verb is modified in BBj).
Example
rem 'BREAK inside a SWITCH inside a loop ends only the SWITCH
for i=1 to 3
switch i
case 2
break
case default
print i
swend
next i
EXITTO
Rule
EXITTO lineref -- branches like GOTO, but also discards the
top entry on the FOR/GOSUB/WHILE stack.
Why
EXITTO is the verb built to leave a loop or subroutine cleanly from inside
nested code, since it retires the stack entry a bare GOTO would leave stale.
Edge
In a compound statement, only ELSE, FI or REM may
follow EXITTO -- never BREAK.
Evidence
EXITTO Verb (PRO/5).
BBj Modified/Enhanced Verbs (BBj generation: EXITTO is not modified in BBj).
Example
rem 'EXITTO leaves the FOR loop cleanly, unlike a bare GOTO for i=1 to 10 if i=5 then exitto done next i done: print "left the loop"
SWITCH .. CASE .. SWEND
Rule
SWITCH expr ... CASE value ... CASE DEFAULT ... SWEND -- CASE bodies fall
through into the next CASE until BREAK, EXITTO or SWEND
ends them.
Why
SWITCH is a multi-way decision that scans CASE values in
order for a match, then keeps executing statements -- including any following CASE lines, which
are then skipped over like labels -- until something stops it.
Edge
Mark any intentional fallthrough with a comment. A CASE DEFAULT placed
before a matching CASE wins, so put CASE DEFAULT last.
SWITCH, CASE and SWEND must start a line; in a compound
statement, SWITCH may be followed only by REM; there is one
SWEND per SWITCH. PRO/5 accepts 32-bit integers only; BBj 17.00 and
later accepts any value, including strings and objects.
Evidence
SWITCH .. CASE .. SWEND Verbs (PRO/5, states fallthrough and the CASE DEFAULT order).
SWITCH .. CASE .. SWEND Verbs - BBj (BBj, states the any-value support added in BBj 17.00).
Example
rem 'BBj 17.00+ SWITCH accepts a string, not only an integer
kind$="b"
switch kind$
case "a"
print "first"
break
case "b"
print "second"
break
case default
print "other"
swend
IF .. THEN .. ELSE .. FI
Rule
IF expr THEN statements {ELSE statements} {FI | ENDIF} -- expr
is numeric (nonzero is true); on a single line, everything after THEN or
ELSE stays conditional until FI or ENDIF.
Why
PRO/5 must know where a conditional clause ends when several statements share one line;
FI/ENDIF marks that boundary explicitly.
Edge
ELSE pairs with the nearest unpaired THEN. No ;
before ELSE or FI -- a semicolon there is a compile error
(OWNER-NOTES #17). BBj's multi-line IF..THEN..ELSE must close with FI
or ENDIF; a single-line IF with no FI/ENDIF
closes implicitly at the end of its line, so a THEN clause cannot continue onto the
next line the way some BBx code assumed.
Evidence
IF Verb (PRO/5, ELSE/FI pairing and the no-semicolon rule).
IF..THEN..ELSE Statements (BBj, the multi-line form and the implicit-ENDIF note).
Example
rem 'multi-line IF..THEN..ELSE must close with ENDIF in BBj x=1 if x=1 then y=8 else y=9 endif print y
GOSUB .. RETURN, GOTO and ON .. GOTO/GOSUB
Rule
GOSUB lineref ... RETURN transfers to a subroutine and back;
GOTO lineref branches unconditionally; ON int GOTO lineref{,lineref...}
and ON int GOSUB lineref{,lineref...} branch to one of several targets by index.
Why
GOSUB saves the return point on the shared FOR/GOSUB stack so
RETURN knows where to resume; ON's index picks which of the listed
targets GOTO or GOSUB actually uses.
Edge
RETURN errors when the top stack entry is not a GOSUB -- for example, an
open loop left inside the subroutine. Only ELSE, FI or REM
may follow GOTO, RETURN or ON GOTO/GOSUB in a
compound statement; a BREAK after GOTO is silently ignored.
ON's index takes the first target for 0 or less, the second for 1, and the last for
anything past the end; a non-integer index raises
!ERROR=41.
Evidence
GOSUB Verb, RETURN Verb, GOTO Verb, ON GOTO/GOSUB Verbs (PRO/5).
BBj Modified/Enhanced Verbs (BBj generation: none of these four verbs is modified in BBj).
Example
rem 'ON GOSUB picks a target subroutine by index, then RETURN comes back choice=1 on choice gosub sub_one,sub_two goto finish sub_one: print "one" return sub_two: print "two" return finish: print "done"