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