Files
akbasic/docs/04-control-flow.md
Andrew Kesterson 737fdc760f Document every error code a script can see, as chapter 15
ER# held numbers nothing explained. The appendix lists the four error classes
the interpreter prints, the eight codes it owns and what raises each, and the
codes that reach ER# from errno, libakerror and libakgl underneath it.

The table is not asserted. A program in the chapter trips seven of the eight and
prints what it got, and docs_examples byte-compares the result -- so the numbers
are checked rather than claimed. The eighth, 516, is not usefully trappable and
the chapter says why: entering a handler takes a scope, and the pool being empty
is what raised it.

Two things worth a reader's attention came out of writing it. Two codes register
the same ERR() text, so a program must compare the number and print the text.
And VAL reports libakerror's Value Error rather than the interpreter's 517,
which makes that number the platform's rather than ours -- filed as section 6
item 21, not fixed here, because deciding which libakstdlib failures to
translate is a boundary question and ENOENT out of DOPEN is the counter-case.

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

257 lines
4.9 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.
## 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.