The pin moves e4aa6a5 -> 149bee0, which is libakgl 0.8.0. Nothing in this repository changed to accommodate it: both suites pass unmodified, 110 without akgl and 111 with, and both breakout games run forty seconds headless with no error line. **0.8.0 is a whole collision subsystem** -- shapes, pooled proxies, a pluggable broad phase over a uniform grid, a narrowphase answering with a contact carrying a normal and a depth, world box queries, and static proxies for geometry that is not an actor. None of it is called from here yet. This commit is the pull and nothing else, so that if it has to come out it is one revert of one commit. **It adds two submodules of its own**, `deps/libccd` and `deps/tg`, so a tree that updates without `--recursive` configures and then fails compiling libccd. libakgl's own suite was built and run standalone at RelWithDebInfo before akbasic was pointed at it: 33/33. `libakstdlib` and `libakerror` did not move. MAINTENANCE.md requires checking, and the answer this time is that libakgl 0.8.0 pins exactly what this repository already pins -- 669b2b3 and 5eaa956 -- so the pairing rule is satisfied without a bump. Recorded because "we checked and it was already aligned" and "we forgot to check" look identical in a diff. **The version floor moves to 0.8.0** with its paragraph, following the convention in that header of saying what each minor release did and why the floor moved anyway. Worth knowing for whoever reads it next: `akgl_Actor` grew fields, so a translation unit compiled against a 0.7 `actor.h` and linked against 0.8 writes `renderfunc` and `actorData` at the wrong offsets -- and `src/sprite_akgl.c` writes exactly those two. That is the case the soname cannot catch and the guard exists for. **libakgl's reserved status band grew from five codes to six**, gaining `AKGL_ERR_COLLISION` at `AKGL_ERR_BASE + 5`, so it now owns 256-261 and the headroom below akbasic's band starts at 262. Four documents said otherwise: the coordinated range map in MAINTENANCE.md, the comment above the enum in `include/akbasic/error.h`, the file header of `include/akbasic/akgl.h`, and chapter 15. That map is the only coordination there is -- nothing enforces a band boundary at compile time, and the first anybody would know of an overlap is a status printing under the wrong owner's name in a stack trace. Also cleaned on the way past: `deps/libakgl/deps/libakstdlib` was showing dirty in `git status`. It had no local edits -- the checkout was simply one commit behind the gitlink libakgl records -- so `git submodule update` restored it and nothing was lost. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
170 lines
7.2 KiB
Markdown
170 lines
7.2 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. Also what the four prescans report as, since they run before any statement does. |
|
|
| `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, or a branch by number to a line the program did not number. 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 that does not exist, or a call to a function that was never defined. A branch to a *line* that does not exist raises nothing at all: `GOTO 500` with no line 500 walks to the end of the program and stops, which is the same answer `RENUMBER` gives such a branch. |
|
|
| 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 261 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.
|