Document optional line numbers
Chapter 2 gets the rule and the refusal, chapter 4 gets the payoff for LABEL, chapter 9 says DSAVE writes the numbers it handed out and to RENUMBER first if you want gaps, chapter 10 shows a host loading numberless source, chapter 13 gets the QuickBASIC-shaped divergence, and chapter 14's source[] passage gets its second half. Chapter 14 said "two prescans" and listed two; there were three before this and there are four now, so it lists all four and says which of them reports against the right line. TODO.md section 6 records four things found on the way and deliberately not fixed: set_label() filing into the active scope rather than the root, three prescans reporting the wrong line number, duplicate written line numbers still replacing silently, and renumber.c's file-scope scratch arrays. examples/embed.c runs the same program twice, numbered and not, so the example compiles the feature rather than describing it. Its header pointed at ./build/examples/embed, which is not where the binary lands. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -108,6 +108,19 @@ array slot, so `GOTO 500` is one assignment — `environment->nextline = 500`
|
||||
search. Blank slots are skipped by the step loop. That is why line numbers are capped at
|
||||
9998 and why a nine-line program still costs a 9999-entry array.
|
||||
|
||||
It is also why **a loaded line does not have to arrive with a number**. Execution needs a
|
||||
slot, not a number somebody typed, so a line that comes without one is simply given the
|
||||
slot after the last line filed. `akbasic_runtime_file_line()` is the one place that rule
|
||||
lives, shared by `akbasic_runtime_load()`, `RUNSTREAM` and `DLOAD`; the step loop, the
|
||||
branch verbs, the four prescans and `RENUMBER` never learn that anything changed, because
|
||||
all of them already worked in slots. `akbasic_SourceLine::numbered` records which kind a
|
||||
line was, and exactly one thing reads it — `akbasic_runtime_check_targets()`, which
|
||||
refuses `GOTO 100` when line 100 is a number nobody wrote.
|
||||
|
||||
The prompt is deliberately outside all of this: `process_line_repl()` files a line only
|
||||
when it *had* a number, because there the number is what separates program text from a
|
||||
statement to run now.
|
||||
|
||||
## One step
|
||||
|
||||
`akbasic_runtime_step()` is the whole loop, unrolled to a single iteration. The
|
||||
@@ -202,7 +215,7 @@ anything else sets it to `QUIT`, so `basic program.bas` exits. It is one field,
|
||||
the whole difference between an interactive session and a script runner.
|
||||
|
||||
**`akbasic_runtime_set_mode()` is not just an assignment.** Entering `REPL` prints
|
||||
`READY`. Entering `RUN` does two prescans of the whole program first:
|
||||
`READY`. Entering `RUN` does four prescans of the whole program first:
|
||||
|
||||
- **Labels.** Every `LABEL` in the source is filed before anything executes, textually
|
||||
rather than by parsing. Without it a label would exist only from the moment its `LABEL`
|
||||
@@ -211,11 +224,23 @@ the whole difference between an interactive session and a script runner.
|
||||
by label at all.
|
||||
- **`DATA` items.** `READ` walks a cursor along a list built before the program runs, so a
|
||||
`DATA` line above its `READ` is found.
|
||||
- **`TYPE` declarations.** A declaration has to be in effect wherever control goes, so
|
||||
`DIM P@ AS RECT` cannot run before `RECT` exists even if a branch skipped the lines that
|
||||
declared it.
|
||||
- **Branch targets.** The only one that reads nothing in and merely refuses: a numeric
|
||||
`GOTO`, `GOSUB`, `RUN`, `RESTORE`, `TRAP` or `COLLISION` target naming a line the program
|
||||
did not number. It shares `RENUMBER`'s walk in `src/renumber.c` rather than repeating it.
|
||||
|
||||
Every path into a run — `akbasic_runtime_start()`, the `RUN` verb, `CONT`, and the end of
|
||||
a `RUNSTREAM` load — goes through that one function, which is why the prescans live there
|
||||
and not in any of the four callers.
|
||||
|
||||
All four run inside one `ATTEMPT`, because **a prescan failure is the program's mistake
|
||||
and not the host's** and has to leave as a BASIC error line rather than a raised context.
|
||||
Three of them report against whichever line the loader stopped on and put the real one in
|
||||
the message text; the branch-target scan points `environment->lineno` at the line it is
|
||||
walking so the `? N :` prefix is right. `TODO.md` §5 item 64 records the difference.
|
||||
|
||||
The REPL's split between *file it* and *run it now* is one boolean: the scanner sets
|
||||
`hadlinenumber` when the line it just read began with a number. A line typed with a
|
||||
number is program text; a line typed without one is direct mode and runs immediately.
|
||||
|
||||
Reference in New Issue
Block a user