Files
akbasic/docs/04-control-flow.md
Andrew Kesterson 28ea99a638 Document optional line numbers
Chapter 2 gets the rule and the refusal, chapter 4 gets the payoff for
LABEL, chapter 9 says DSAVE writes the numbers it handed out and to
RENUMBER first if you want gaps, chapter 10 shows a host loading numberless
source, chapter 13 gets the QuickBASIC-shaped divergence, and chapter 14's
source[] passage gets its second half.

Chapter 14 said "two prescans" and listed two; there were three before this
and there are four now, so it lists all four and says which of them reports
against the right line.

TODO.md section 6 records four things found on the way and deliberately not
fixed: set_label() filing into the active scope rather than the root, three
prescans reporting the wrong line number, duplicate written line numbers
still replacing silently, and renumber.c's file-scope scratch arrays.

examples/embed.c runs the same program twice, numbered and not, so the
example compiles the feature rather than describing it. Its header pointed
at ./build/examples/embed, which is not where the binary lands.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 16:43:14 -04:00

284 lines
5.3 KiB
Markdown

# 4. Control flow
## IF ... THEN ... ELSE
```basic
10 A# = 5
20 IF A# = 5 THEN PRINT "FIVE" ELSE PRINT "NOT FIVE"
```
```output
FIVE
```
The condition is a whole expression, so `AND`, `OR` and `NOT` all work in one:
```basic
10 A# = 5
20 IF A# > 0 AND A# < 10 THEN PRINT "IN RANGE"
```
```output
IN RANGE
```
**Everything after `THEN` on the line belongs to the condition**, which matters as soon
as you put several statements on a line:
| Line | Condition | What runs |
|---|---|---|
| `IF C THEN A : B` | true | `A`, `B` |
| `IF C THEN A : B` | false | nothing |
| `IF C THEN A ELSE B : D` | true | `A` |
| `IF C THEN A ELSE B : D` | false | `B`, `D` |
The remainder always belongs to whichever arm was written *last*.
### Blocks
`BEGIN` and `BEND` make an `IF` span lines:
```basic
10 A# = 5
20 IF A# = 5 THEN BEGIN
30 PRINT "IN THE BLOCK"
40 PRINT "STILL IN IT"
50 BEND
60 PRINT "AFTER"
```
```output
IN THE BLOCK
STILL IN IT
AFTER
```
When the condition is false every line up to the `BEND` is skipped.
## FOR ... NEXT
```basic
10 FOR I# = 1 TO 5
20 PRINT I#
30 NEXT I#
```
```output
1
2
3
4
5
```
`STEP` sets the stride, and a negative one counts down:
```basic
10 FOR I# = 10 TO 0 STEP -2
20 PRINT I#
30 NEXT I#
```
```output
10
8
6
4
2
0
```
The counter is an ordinary variable and the body may assign to it. Two things to know:
- **The counter does not survive the loop.** It lives in the loop's own scope, so
reading it afterwards gives zero. On a C128 it keeps its final value.
- **A step that overshoots runs the body one extra time.** `FOR I = 1 TO 9 STEP 3` runs
with 1, 4, 7 *and 10*. Both are recorded as known defects; see Chapter 13.
`EXIT` leaves the loop early:
```basic
10 FOR I# = 1 TO 100
20 IF I# = 5 THEN EXIT
30 NEXT I#
40 PRINT "OUT"
```
```output
OUT
```
## DO ... LOOP
The condition can go on either end, or neither:
```basic
10 I# = 0
20 DO WHILE I# < 3
30 PRINT I#
40 I# = I# + 1
50 LOOP
```
```output
0
1
2
```
```basic
10 I# = 0
20 DO
30 PRINT I#
40 I# = I# + 1
50 LOOP UNTIL I# = 3
```
```output
0
1
2
```
A condition on the `DO` is tested before the body, so the body may run zero times. A
condition on the `LOOP` is tested after, so it runs at least once. `DO` with no
condition at all loops forever until an `EXIT` or a `GOTO` leaves it.
`EXIT` works here too.
## GOTO and GOSUB
```basic
10 GOSUB 100
20 PRINT "BACK"
30 END
100 PRINT "IN THE SUBROUTINE"
110 RETURN
```
```output
IN THE SUBROUTINE
BACK
```
`RETURN` goes back to the line after the `GOSUB`.
## Labels
A label is a name with no type suffix. It marks a line, and anything that takes a line
number takes a label instead:
```basic
10 GOTO SETUP
20 PRINT "SKIPPED"
100 LABEL SETUP
110 PRINT "ARRIVED"
```
```output
ARRIVED
```
**Labels are filed before the program runs**, so a forward `GOTO` works. This is worth
using for its own sake: a program written with labels is immune to `RENUMBER`, because
there is no number to rewrite.
Follow that one step further and the numbers stop earning their keep at all. A program
in a file can leave them out — see
[Chapter 2](02-getting-started.md#a-program-in-a-file-does-not-need-them) — and then
every branch it makes is by name:
```basic
GOSUB GREET
GOTO FINISH
PRINT "NOT REACHED"
LABEL GREET
PRINT "HELLO"
RETURN
LABEL FINISH
PRINT "DONE"
```
```output
HELLO
DONE
```
Nothing in that program can be broken by inserting a line into it, which is the whole
argument.
## ON
`ON` picks the *n*th target from a list, counting from one:
```basic
10 CHOICE# = 2
20 ON CHOICE# GOTO 100, 200, 300
30 PRINT "CHOICE WAS OUT OF RANGE"
40 END
100 PRINT "FIRST"
110 END
200 PRINT "SECOND"
210 END
300 PRINT "THIRD"
```
```output
SECOND
```
Out of range is not an error — it falls through to the next statement, which is what
lets you write the check as the line after. `ON ... GOSUB` works the same way and
returns.
## Trapping errors
`TRAP` sends an error to a handler instead of stopping the program:
```basic
10 TRAP HANDLER
20 DIM Q#(2)
30 PRINT Q#(9)
40 PRINT "CARRIED ON"
50 END
100 LABEL HANDLER
110 PRINT "CAUGHT " + ERR(ER#) + " ON LINE " + EL#
120 RESUME NEXT
```
```output
CAUGHT Out Of Bounds ON LINE 30
CARRIED ON
```
Two variables are set when the trap fires: **`ER#`** is the error code and **`EL#`** is
the line it happened on. `ERR(ER#)` gives the message text. On a C128 these are called
`ER` and `EL` with no suffix; this dialect has no bare variable names.
**[Chapter 15](15-error-codes.md) is the complete list** of what `ER#` can hold and
what each code means. The short version is that the interpreter's own codes are 512 to
519 and are the only numbers worth comparing against; two codes render as the same
`ERR()` text, so compare the number rather than the text.
`RESUME` comes in three forms:
| Form | Where it goes |
|---|---|
| `RESUME` | back to the line that failed, and tries again |
| `RESUME NEXT` | on to the line after the one that failed |
| `RESUME (line)` | to a line you name |
Bare `RESUME` only terminates if the handler fixed whatever was wrong — that is what
it is for.
`TRAP` with no argument turns trapping off. An error *inside* a handler is reported
normally rather than re-entering it, so a broken handler cannot loop forever.
## STOP, END and CONT
`STOP` stops the program and returns to the prompt; `CONT` resumes from there. `END`
stops it without arming `CONT`. `QUIT` ends the interpreter itself.