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>
170 lines
6.9 KiB
Markdown
170 lines
6.9 KiB
Markdown
# 15. Appendix: error codes
|
|
|
|
Two different things get called "the error" in this interpreter, and a program author
|
|
meets both. **The report** is the line printed when something goes wrong and nothing
|
|
catches it. **The code** is the number `ER#` holds when a `TRAP` handler catches it.
|
|
This appendix is the complete list of each.
|
|
|
|
Chapter 4 is where error trapping is explained; this is the reference you come back to
|
|
once you already know what a `TRAP` is.
|
|
|
|
## The report
|
|
|
|
An error nothing traps is printed as one line and stops the run:
|
|
|
|
```basic
|
|
10 PRINT LEFT(1, 2)
|
|
20 PRINT "NEVER REACHED"
|
|
```
|
|
|
|
```output
|
|
? 10 : RUNTIME ERROR LEFT expected a string
|
|
|
|
```
|
|
|
|
The shape is `? line : CLASS message`. There are exactly four classes, and which one
|
|
you get says *when* the interpreter gave up rather than what was wrong:
|
|
|
|
| Class | Raised when |
|
|
|---|---|
|
|
| `PARSE ERROR` | The line could not be turned into a statement. Nothing on it ran. |
|
|
| `SYNTAX ERROR` | A statement was well-formed but not a legal one here. |
|
|
| `RUNTIME ERROR` | A statement ran and failed. The commonest by a wide margin. |
|
|
| `IO ERROR` | Reading the program itself failed — not a file a program opened, which is a `RUNTIME ERROR` like any other. |
|
|
|
|
The line number is where the interpreter was, which for a `PARSE ERROR` is the line
|
|
that would not parse and for a `RUNTIME ERROR` is the line that failed.
|
|
|
|
## The code
|
|
|
|
`ER#` is the code, `EL#` is the line, and `ERR(n)` is the message text for a code.
|
|
They are ordinary global variables here rather than the bare `ER` and `EL` a C128
|
|
exposes; Chapter 13 says why.
|
|
|
|
Every one of them is set for a trapped error, and **a parse error is trappable too**:
|
|
|
|
```basic
|
|
10 TRAP 200
|
|
20 DIM Q#(2)
|
|
30 SCALE
|
|
40 PRINT LEFT(1, 2)
|
|
50 GOTO NOWHERE
|
|
60 PRINT Q#(9)
|
|
70 PRINT 1 / 0
|
|
80 RESUME
|
|
90 FILTER 1, 1
|
|
100 END
|
|
200 PRINT "LINE " + EL# + ": " + ER# + " " + ERR(ER#)
|
|
210 RESUME NEXT
|
|
```
|
|
|
|
```output
|
|
LINE 30: 512 Syntax Error
|
|
LINE 40: 513 Type Error
|
|
LINE 50: 514 Undefined Reference
|
|
LINE 60: 515 Out Of Bounds
|
|
LINE 70: 517 Value Error
|
|
LINE 80: 518 State Error
|
|
LINE 90: 519 Device Error
|
|
```
|
|
|
|
That program is run by the test suite, so the numbers in the table below are checked
|
|
rather than claimed. Line 90 is `FILTER` rather than a drawing verb because `FILTER`
|
|
refuses in *every* build — a `DRAW` would only give 519 in a build with no window, and
|
|
the point here is the code rather than the verb.
|
|
|
|
## The interpreter's own codes
|
|
|
|
These eight are akbasic's, they are absolute, and they do not move. The interpreter
|
|
owns the block 512 to 767 and uses the first eight of it.
|
|
|
|
| Code | `ERR()` text | What raises it |
|
|
|---|---|---|
|
|
| 512 | `Syntax Error` | A line that will not parse, a verb given the wrong shape of arguments, a keyword where a value belongs. The commonest code and the only one that can arrive before a statement runs. |
|
|
| 513 | `Type Error` | A string where a number belongs or the reverse — `LEFT(1, 2)`, `A$ = 1 + "X"` used as a number, a non-integer array subscript. |
|
|
| 514 | `Undefined Reference` | A branch to a label or line that does not exist, or a call to a function that was never defined. |
|
|
| 515 | `Out Of Bounds` | An array subscript past the end, a colour index outside 1 to 16, a sprite number outside 1 to 8, a `RGR` or `RSPRITE` field number that is not a field. |
|
|
| 516 | `Environment Error` | The scope pool is exhausted — 32 nested `GOSUB`, `FOR` or `DEF` bodies. **See below: this is the one code you cannot usefully trap.** |
|
|
| 517 | `Value Error` | A number that is the wrong number: division by zero, a negative `SCALE` maximum, a string longer than 255 characters, a `GSHAPE` handed a string that did not come from `SSHAPE`. |
|
|
| 518 | `State Error` | A verb run outside the structure it needs — `RESUME` with no handler running, `NEXT` with no `FOR`, `LOOP` with no `DO`. |
|
|
| 519 | `Device Error` | A verb needs a graphics, audio, input or sprite device the runtime has not been given, or one that cannot do what was asked. Everything in Chapters 6, 7 and 8 refuses with this in a build with no window. `FILTER` and `SYS` refuse with it always. |
|
|
|
|
**516 is not usefully trappable and that is inherent, not a defect.** Entering a
|
|
handler takes a scope, and the pool being empty is exactly what raised the error — so
|
|
a program that exhausts it gets the report rather than the handler. Runaway recursion
|
|
is the way to reach it, and a handler that could not run is the right answer to it.
|
|
|
|
## Codes from underneath
|
|
|
|
`ER#` carries whatever code the failure was raised with, and not every failure comes
|
|
from the interpreter. Two other kinds reach a program.
|
|
|
|
**Errors from the C library, as `errno`.** The disk verbs open, read and delete real
|
|
files, and a real file refuses for the reasons files refuse:
|
|
|
|
```basic
|
|
10 TRAP 100
|
|
20 DOPEN 1, "nosuchfile.bas"
|
|
30 END
|
|
100 PRINT "" + ER# + " " + ERR(ER#)
|
|
110 END
|
|
```
|
|
|
|
```output
|
|
2 No such file or directory
|
|
```
|
|
|
|
2 is `ENOENT`. Any `errno` your platform defines can arrive this way, with the text
|
|
your C library gives it — so this is the one part of the table that is your machine's
|
|
rather than this document's.
|
|
|
|
**Errors from the libraries akbasic is built on.** `libakerror` names its own codes
|
|
just above the `errno` range, and they surface when something fails below the language
|
|
rather than in it — `SCRATCH` on a file that will not delete gives its
|
|
`Input Output Error`, and `VAL` of text that is not a number gives its `Value Error`
|
|
rather than the interpreter's 517, because the conversion happens in `libakstdlib` and
|
|
the code comes back up with it. In the SDL build, `libakgl` owns 256 to 260 and an
|
|
`SDL Error` can reach `ER#` if the renderer itself refuses.
|
|
|
|
**Do not hardcode those numbers.** `libakerror`'s codes are defined as offsets from
|
|
the largest `errno` your platform has, so the same code is a different integer on a
|
|
different machine — on the machine this was written on the range begins at 134, and
|
|
that is not a promise. The interpreter's own 512 to 519 are absolute and are the only
|
|
numbers worth writing into a program.
|
|
|
|
## Two codes render as the same text
|
|
|
|
`ERR()` gives the *registered name* for a code, and two pairs of them collide:
|
|
|
|
| Text | Given by |
|
|
|---|---|
|
|
| `Type Error` | 513, and `libakerror`'s own type error |
|
|
| `Value Error` | 517, and `libakerror`'s own value error |
|
|
|
|
The `Value Error` pair is not hypothetical — it is the `VAL` case above:
|
|
|
|
```basic
|
|
10 TRAP 100
|
|
20 PRINT VAL("XYZ")
|
|
30 END
|
|
100 PRINT ERR(ER#)
|
|
110 IF ER# = 517 THEN PRINT "INTERPRETER"
|
|
120 IF ER# <> 517 THEN PRINT "FROM UNDERNEATH"
|
|
130 END
|
|
```
|
|
|
|
```output
|
|
Value Error
|
|
FROM UNDERNEATH
|
|
```
|
|
|
|
So `IF ERR(ER#) = "Value Error" THEN` is not a reliable test and `IF ER# = 517 THEN`
|
|
is. **Compare the code; print the text.**
|
|
|
|
## What is not in `ER#`
|
|
|
|
An error the interpreter reports *about the host* rather than about your program never
|
|
reaches `ER#`, because it is not the program's error to catch — a font that will not
|
|
open, a window that will not create. Those come out of the embedding API instead;
|
|
Chapter 14 draws that line, and Chapter 10 is what a host does with it.
|