File the scanned line in RUNSTREAM, not the raw one

process_line_runstream() chose between the stripped line and the raw buffer
on obj->mode == AKBASIC_MODE_REPL, but nothing reaches that function except
the RUNSTREAM arm of step(). The stripped branch was dead, so every program
the driver ran from a file was stored with its own line number inside
source[]: LIST printed "10 10 PRINT", and DSAVE and HELP would have echoed
it the same way.

No .bas in either corpus calls LIST, and runtime_verbs.c's load() helper --
which claims to load "the way RUNSTREAM would" -- already stored the scanned
line, so it encoded the correct contract and could not catch the wrong one.
The new test drives the real sink path instead.

Keep the step-over-a-leading-number allowance in scan_line_labels() and
skip_lineno(): no path inside the library stores a raw line now, but
akbasic_runtime_store_line() is public and a host may hand it one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-01 16:25:05 -04:00
parent 812d846e47
commit d2ae0d75de
4 changed files with 66 additions and 16 deletions

View File

@@ -47,6 +47,32 @@ static void clear_output(void)
rewind(HARNESS_OUT);
}
/**
* @brief A program read through RUNSTREAM is stored without its line numbers.
*
* The load() helper above files what the scanner stripped, which is what every
* caller inside the library does -- but RUNSTREAM used to file the raw buffer,
* so a program run from a file held "10 PRINT" under slot 10 and LIST, DSAVE
* and HELP would each have printed the number twice. This drives the real sink
* path rather than the helper, because the helper is exactly what was not
* happening.
*/
static void test_runstream_strips_line_numbers(void)
{
TEST_REQUIRE_OK(harness_start("10 PRINT \"ONE\"\n20 PRINT \"TWO\"\n"));
TEST_REQUIRE_OK(akbasic_runtime_start(&HARNESS_RUNTIME, AKBASIC_MODE_RUNSTREAM));
TEST_REQUIRE_OK(akbasic_runtime_run(&HARNESS_RUNTIME, 0));
TEST_REQUIRE_STR(HARNESS_RUNTIME.source[10].code, "PRINT \"ONE\"");
TEST_REQUIRE_STR(HARNESS_RUNTIME.source[20].code, "PRINT \"TWO\"");
/* And LIST therefore prints each number once. */
clear_output();
TEST_REQUIRE_OK(run_line("LIST"));
TEST_REQUIRE_STR(HARNESS_OUTPUT, "10 PRINT \"ONE\"\n20 PRINT \"TWO\"\n");
harness_stop();
}
int main(void)
{
static const char *PROGRAM[] = {
@@ -241,5 +267,7 @@ int main(void)
TEST_REQUIRE_STATUS(run_line("Y# = PEEK(Z#)"), AKBASIC_ERR_VALUE);
harness_stop();
test_runstream_strips_line_numbers();
return akbasic_test_failures;
}