Honour a subscript in SSHAPE and GSHAPE

`shape_variable()` took the identifier off the leaf and looked the variable up
without ever evaluating the subscript, and both verbs then addressed element
zero with a literal. So `SSHAPE SH$(2), ...` wrote the handle into `SH$(0)` and
`GSHAPE SH$(2)` stamped whatever was in `SH$(0)`.

Ordinary assignment and `PRINT` honour the subscript, which is what made this
expensive: a program keeping several saved shapes in an array got every one of
them resolving to the same element, silently, and the only symptom was that
every stamp came out as the last shape captured. The Breakout in examples/ keeps
its six brick stamps in six separate scalars for exactly this reason.

`SPRSAV` was the counter-example and is the model -- it evaluates its argument
and handles an array element correctly. The subscript resolution itself is now
shared: `collect_subscripts()` comes out of src/environment.c as
`akbasic_environment_collect_subscripts()`, so a verb taking a variable by name
resolves a subscript the same way assignment does rather than each verb deciding
for itself.

tests/graphics_verbs.c covers TODO.md's reduction -- which used to print
"[SHAPE:0] []" and now prints "[] [SHAPE:0]" -- and the case a program actually
wants: two shapes captured into two elements, each stamped back through its own,
asserted against the device log so a fix that merely made the strings look right
would not pass.

Chapter 18's trap 4 becomes history, and Chapter 6 says an array works.

TODO.md section 9 item 6, struck.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
This commit is contained in:
2026-08-02 00:10:24 -04:00
parent f626d7985f
commit 748fb5f91c
7 changed files with 122 additions and 24 deletions

View File

@@ -329,12 +329,14 @@ akerr_ErrorContext *akbasic_environment_create(akbasic_Environment *obj, const c
* identifier yields the single subscript {0}, which is how a scalar is addressed
* -- every variable is really a one-element array.
*/
static akerr_ErrorContext *collect_subscripts(akbasic_Environment *obj, akbasic_ASTLeaf *lval, int64_t *subscripts, int *count)
akerr_ErrorContext *akbasic_environment_collect_subscripts(akbasic_Environment *obj, akbasic_ASTLeaf *lval, int64_t *subscripts, int *count)
{
PREPARE_ERROR(errctx);
akbasic_ASTLeaf *expr = NULL;
akbasic_Value *tval = NULL;
FAIL_ZERO_RETURN(errctx, (obj != NULL && lval != NULL && subscripts != NULL && count != NULL),
AKERR_NULLPOINTER, "NULL argument in collect_subscripts");
*count = 0;
if ( lval->expr != NULL &&
lval->expr->leaftype == AKBASIC_LEAF_ARGUMENTLIST &&
@@ -452,7 +454,7 @@ akerr_ErrorContext *akbasic_environment_assign(akbasic_Environment *obj, akbasic
PASS(errctx, akbasic_environment_get(obj, lval->identifier, &variable));
FAIL_ZERO_RETURN(errctx, (variable != NULL), AKBASIC_ERR_UNDEFINED,
"Identifier %s is undefined", lval->identifier);
PASS(errctx, collect_subscripts(obj, lval, subscripts, &subscriptcount));
PASS(errctx, akbasic_environment_collect_subscripts(obj, lval, subscripts, &subscriptcount));
/*
* Resolve the slot before the type switch. The reference notes that moving