Release the scope a skipped BEGIN block's loop pushed
`akbasic_parse_for()` and `akbasic_parse_do()` create their environment while the line is *parsed*; whether to skip it is decided afterwards, when the line is evaluated. So a loop inside a block that was not taken pushed a scope, its body was skipped, and the `NEXT` or `LOOP` that would have popped it was skipped too. Nothing else ever would. At the top level that exhausted the pool after thirty-two skips. Inside a routine it was far more confusing: the orphan sat between the routine and its caller, so the `RETURN` after the block reported "RETURN outside the context of GOSUB" from a routine that plainly *was* entered by a `GOSUB` -- naming the one construct that was not at fault, which is why it cost an evening to find. The skip now releases what parsing pushed. **Narrower than it first looks.** Releasing on any skip breaks tests/reference/language/flowcontrol/nestedforloopwaitingforcommand.bas: a zero-iteration `FOR` skips its body by the same mechanism, and there the orphan is load-bearing -- it absorbs the inner `NEXT` so the outer `NEXT` still finds its own `FOR`. Releasing it turns that case into "NEXT outside the context of FOR". So the release is conditional on the skip being a *block* skip, which is decidable because nothing inside a skipped block ever runs to arm a `NEXT` wait. Both halves are asserted side by side in tests/structure_verbs.c, the second one citing the golden case that caught it. The forty-skip case names its own step budget: a skipped line is not free, and forty passes over a five-line block cost about 2700 steps against the shared runner's 2000. Chapter 18's trap 3 becomes history rather than a warning, and the note in Step 5 that called `GOTO`-guarded loops "not a style choice" now says why the shape is kept anyway. TODO.md section 9 item 2, struck. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -343,9 +343,10 @@ The game sets `DPLAY# = 1` or `DHUD# = 1` when something changes and never draws
|
||||
directly. One consequence is visible and deliberate: **the score lags the bricks by one
|
||||
frame**, because the field goes first. At thirty frames a second nobody can see it.
|
||||
|
||||
Those are `LABEL`s and `GOTO`s rather than `BEGIN` blocks, and again that is not a style
|
||||
choice — a `RETURN` inside a block leaves the interpreter with no `GOSUB` to return from.
|
||||
The last trap in this chapter is exactly that.
|
||||
Those are `LABEL`s and `GOTO`s rather than `BEGIN` blocks, and when this was written that
|
||||
was not a style choice: a loop inside a block that was skipped left the interpreter with
|
||||
no `GOSUB` to return from. That is fixed — see trap 3 below — and the shape is kept
|
||||
because it costs nothing and reads the same.
|
||||
|
||||
## Step 6: Draw the lettering, because text has no colour
|
||||
|
||||
@@ -672,7 +673,13 @@ NEXT I#
|
||||
RETURN
|
||||
```
|
||||
|
||||
### 3. A skipped `BEGIN` block containing a loop breaks the enclosing `RETURN`
|
||||
### 3. A skipped `BEGIN` block containing a loop — *fixed*
|
||||
|
||||
This one is here as history rather than as a warning. A loop inside a block that was not
|
||||
taken used to leave a scope behind, and inside a routine the orphan sat between it and
|
||||
its caller — so the `RETURN` after the block reported `RETURN outside the context of
|
||||
GOSUB` from a routine that plainly *was* entered by a `GOSUB`. The error named the one
|
||||
construct that was not at fault, which is why it cost an evening.
|
||||
|
||||
```basic
|
||||
T# = 0
|
||||
@@ -689,18 +696,13 @@ RETURN
|
||||
```
|
||||
|
||||
```output
|
||||
? 11 : RUNTIME ERROR RETURN outside the context of GOSUB
|
||||
|
||||
CAME BACK
|
||||
```
|
||||
|
||||
The routine plainly *was* called by a `GOSUB`. `FOR` creates its environment when the
|
||||
line is **parsed**, and the block skip is decided when it is **evaluated**, so a skipped
|
||||
loop pushes a scope that its skipped `NEXT` never pops — and the orphan sits between the
|
||||
routine and its caller. Outside a routine the same thing exhausts the pool of 32 after
|
||||
thirty-two skips instead.
|
||||
|
||||
Guard loops with `GOTO` rather than wrapping them in a block, which is what every draw
|
||||
routine in this game does.
|
||||
`FOR` creates its environment when the line is **parsed** and the skip is decided when it
|
||||
is **evaluated**; the skip now releases what parsing pushed. **The listing still guards
|
||||
its loops with `GOTO`**, which is where this chapter's own history shows: written that way
|
||||
because it had to be, and left that way because it works.
|
||||
|
||||
### 4. `SSHAPE` and `GSHAPE` ignore the subscript on a string array
|
||||
|
||||
|
||||
Reference in New Issue
Block a user