From ad768892925d8f0bf217bcd1f2d06d63c951ca28 Mon Sep 17 00:00:00 2001 From: Andrew Kesterson Date: Sun, 2 Aug 2026 00:32:20 -0400 Subject: [PATCH] State where a scalar lives in the three chapters that describe the pool Chapter 13's limits table said 4096 array elements "in total", Chapter 16 said an instance's fields come out of the same pool and nothing is reclaimed, and Chapter 14's pool table listed `AKBASIC_MAX_ARRAY_VALUES` with no note about what does and does not draw from it. All three were written when a scalar drew from that pool, and all three now understate what a program may do. Each gains the same two facts in the register it is written in. Chapter 13: a scalar does not come out of the 4096, so creating one inside a `GOSUB` or a `FOR` -- the loop counter included -- costs nothing, while a `DIM` inside a scope does and is not given back. Chapter 16: scalars are the exception, and "nothing is reclaimed" is what lets a pointer into a record stay sound after its scope has gone -- which is the reason arrays and structures still spend. Chapter 14: a row for the variable's own storage, and a paragraph on why the value pool is the one budget that needs a second sentence. Co-Authored-By: Claude Opus 5 (1M context) --- docs/13-differences.md | 9 ++++++++- docs/14-architecture.md | 9 +++++++++ docs/16-structures.md | 5 ++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/13-differences.md b/docs/13-differences.md index eff113d..139a409 100644 --- a/docs/13-differences.md +++ b/docs/13-differences.md @@ -217,7 +217,7 @@ interpreter's error code, which bears no relation to a Commodore error number. P | Line length | 255 | | String length | 255 | | Variables | 128 | -| Array elements | 1024 per array, 4096 in total | +| Array elements | 1024 per array, 4096 across every array and structure | | Scopes | 32 | | Labels | 64 | | `DATA` items | 512 | @@ -226,3 +226,10 @@ interpreter's error code, which bears no relation to a Commodore error number. P Every one is a fixed pool. Nothing in the interpreter calls `malloc`, which is what makes it safe to embed in a game that cannot afford a surprise allocation. + +**A scalar does not come out of the 4096.** It lives in the variable itself, so creating +one inside a `GOSUB` or a `FOR` — including the loop counter — costs nothing and can be +done for as long as the program runs. An *array* declared inside a scope does come out of +it and is not given back: the pool never frees, which is what lets a pointer into a +record outlive the scope that declared it. In practice that means `DIM` at the top rather +than in a loop, which is where you would have put it anyway. diff --git a/docs/14-architecture.md b/docs/14-architecture.md index 2a18c7a..c64cf97 100644 --- a/docs/14-architecture.md +++ b/docs/14-architecture.md @@ -366,12 +366,21 @@ error naming the pool, not a crash and not a slow leak. | `AKBASIC_MAX_VARIABLES` | 128 | runtime | `Maximum runtime variables reached` | | `AKBASIC_MAX_FUNCTIONS` | 64 | runtime | `Maximum function definitions reached` | | `AKBASIC_MAX_ARRAY_VALUES` | 4096 | runtime (`valuepool`) | `Array of N elements does not fit in the M remaining value slots` | +| — a scalar is not in that pool | 1 | **the variable** (`inlinevalue`) | — | | `AKBASIC_MAX_TOKENS` | 32 | **environment** | `Line N has more than 32 tokens` | | `AKBASIC_MAX_LEAVES` | 32 | **environment** | `No more leaves available` | | `AKBASIC_MAX_VALUES` | 64 | **environment** | `Maximum values per line reached` | The numbers are in `include/akbasic/types.h`, transcribed from the reference's `main.go` plus three the Go version did not need because it called `make()`. + +**The value pool is the one that needs a second sentence.** It is a bump allocator with +no free, so anything drawn from it is spent for the life of the run — and scope exit +returns a variable's *slot* without returning its storage. A scalar therefore does not +draw from it at all: `akbasic_variable_init()` points a one-element non-`@` variable at +its own `inlinevalue`, which is what makes a local, a `FOR` counter and a `DEF` parameter +free. Arrays and structures still spend, deliberately, because a pointer into a record is +allowed to outlive the scope that DIMmed it. [Chapter 13](13-differences.md) states the same budget from a BASIC programmer's side. The per-environment three are reset at the top of every line, which is what makes diff --git a/docs/16-structures.md b/docs/16-structures.md index 5fbdcb2..0343040 100644 --- a/docs/16-structures.md +++ b/docs/16-structures.md @@ -365,4 +365,7 @@ Three things a host should know: An instance's fields come out of the same value pool arrays use, so the 4096-element budget in Chapter 13 covers both. **Nothing is reclaimed** — a structure lasts until -`CLR` or `NEW`, exactly as an array does. +`CLR` or `NEW`, exactly as an array does, and that is what lets a pointer into a record +stay sound after the scope that declared it has gone. Scalars are the exception and are +not in the pool at all; they live in the variable, so creating one inside a scope costs +nothing.