177 lines
4.0 KiB
Markdown
177 lines
4.0 KiB
Markdown
|
|
# 4. Control flow
|
||
|
|
|
||
|
|
## IF ... THEN ... ELSE
|
||
|
|
|
||
|
|
```
|
||
|
|
10 A# = 5
|
||
|
|
20 IF A# = 5 THEN PRINT "FIVE" ELSE PRINT "NOT FIVE"
|
||
|
|
```
|
||
|
|
|
||
|
|
The condition is a whole expression, so `AND`, `OR` and `NOT` all work in one:
|
||
|
|
|
||
|
|
```
|
||
|
|
10 IF A# > 0 AND A# < 10 THEN PRINT "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:
|
||
|
|
|
||
|
|
```
|
||
|
|
10 IF A# = 5 THEN BEGIN
|
||
|
|
20 PRINT "IN THE BLOCK"
|
||
|
|
30 PRINT "STILL IN IT"
|
||
|
|
40 BEND
|
||
|
|
50 PRINT "AFTER"
|
||
|
|
```
|
||
|
|
|
||
|
|
When the condition is false every line up to the `BEND` is skipped.
|
||
|
|
|
||
|
|
## FOR ... NEXT
|
||
|
|
|
||
|
|
```
|
||
|
|
10 FOR I# = 1 TO 5
|
||
|
|
20 PRINT I#
|
||
|
|
30 NEXT I#
|
||
|
|
```
|
||
|
|
|
||
|
|
`STEP` sets the stride, and a negative one counts down:
|
||
|
|
|
||
|
|
```
|
||
|
|
10 FOR I# = 10 TO 0 STEP -2
|
||
|
|
20 PRINT I#
|
||
|
|
30 NEXT I#
|
||
|
|
```
|
||
|
|
|
||
|
|
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:
|
||
|
|
|
||
|
|
```
|
||
|
|
10 FOR I# = 1 TO 100
|
||
|
|
20 IF I# = 5 THEN EXIT
|
||
|
|
30 NEXT I#
|
||
|
|
```
|
||
|
|
|
||
|
|
## DO ... LOOP
|
||
|
|
|
||
|
|
The condition can go on either end, or neither:
|
||
|
|
|
||
|
|
```
|
||
|
|
10 I# = 0
|
||
|
|
20 DO WHILE I# < 3
|
||
|
|
30 PRINT I#
|
||
|
|
40 I# = I# + 1
|
||
|
|
50 LOOP
|
||
|
|
```
|
||
|
|
|
||
|
|
```
|
||
|
|
10 I# = 0
|
||
|
|
20 DO
|
||
|
|
30 PRINT I#
|
||
|
|
40 I# = I# + 1
|
||
|
|
50 LOOP UNTIL I# = 3
|
||
|
|
```
|
||
|
|
|
||
|
|
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
|
||
|
|
|
||
|
|
```
|
||
|
|
10 GOSUB 100
|
||
|
|
20 PRINT "BACK"
|
||
|
|
30 END
|
||
|
|
100 PRINT "IN THE SUBROUTINE"
|
||
|
|
110 RETURN
|
||
|
|
```
|
||
|
|
|
||
|
|
`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:
|
||
|
|
|
||
|
|
```
|
||
|
|
10 GOTO SETUP
|
||
|
|
20 PRINT "SKIPPED"
|
||
|
|
100 LABEL SETUP
|
||
|
|
110 PRINT "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:
|
||
|
|
|
||
|
|
```
|
||
|
|
10 ON CHOICE# GOTO 100, 200, 300
|
||
|
|
20 PRINT "CHOICE WAS OUT OF RANGE"
|
||
|
|
```
|
||
|
|
|
||
|
|
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:
|
||
|
|
|
||
|
|
```
|
||
|
|
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
|
||
|
|
```
|
||
|
|
|
||
|
|
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.
|
||
|
|
|
||
|
|
`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.
|