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>
This commit is contained in:
@@ -359,6 +359,51 @@ static void test_shapes(void)
|
||||
harness_stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Both verbs honour a subscript on a string array.
|
||||
*
|
||||
* They used to ignore it. `shape_variable()` took the identifier and looked the
|
||||
* variable up without 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 it
|
||||
* 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 because of this. TODO.md
|
||||
* section 9 item 6.
|
||||
*/
|
||||
static void test_shape_subscripts(void)
|
||||
{
|
||||
TEST_REQUIRE_OK(run_program("10 DIM SH$(4)\n"
|
||||
"20 SSHAPE SH$(2), 0, 0, 8, 8\n"
|
||||
"30 PRINT \"[\" + SH$(0) + \"] [\" + SH$(2) + \"]\"\n"));
|
||||
/* The reduction from TODO.md, which used to print "[SHAPE:0] []". */
|
||||
TEST_REQUIRE_STR(HARNESS_OUTPUT, "[] [SHAPE:0]\n");
|
||||
harness_stop();
|
||||
|
||||
/*
|
||||
* Two shapes in two elements, which is the case a program actually wants and
|
||||
* the one that came out wrong: the second capture used to overwrite the
|
||||
* first, so both elements named the same handle.
|
||||
*/
|
||||
TEST_REQUIRE_OK(run_program("10 DIM SH$(4)\n"
|
||||
"20 SSHAPE SH$(1), 0, 0, 8, 8\n"
|
||||
"30 SSHAPE SH$(2), 0, 0, 4, 4\n"
|
||||
"40 PRINT \"[\" + SH$(1) + \"] [\" + SH$(2) + \"]\"\n"
|
||||
"50 GSHAPE SH$(2), 10, 10\n"
|
||||
"60 GSHAPE SH$(1), 20, 20\n"));
|
||||
TEST_REQUIRE_STR(HARNESS_OUTPUT, "[SHAPE:0] [SHAPE:1]\n");
|
||||
/* And each GSHAPE stamps the handle its own element names. */
|
||||
TEST_REQUIRE_STR(MOCK.log,
|
||||
"save 0,0-8,8 -> 0\n"
|
||||
"save 0,0-4,4 -> 1\n"
|
||||
"paste 1 at 10.0,10.0\n"
|
||||
"paste 0 at 20.0,20.0\n");
|
||||
harness_stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Every verb that needs a device says so by name when it has none.
|
||||
*
|
||||
@@ -398,6 +443,7 @@ int main(void)
|
||||
test_rgr();
|
||||
test_graphic();
|
||||
test_shapes();
|
||||
test_shape_subscripts();
|
||||
test_no_device();
|
||||
return akbasic_test_failures;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user