Refuse a branch to a line the program did not number
GOTO 100 in a script written without line numbers finds the hundredth line and branches there. Silent, plausible and wrong: the test for it loops forever printing the second line when the check is removed. akbasic_runtime_check_targets() is a fourth prescan beside the label, DATA and TYPE ones, run on every entry into MODE_RUN -- the earliest the check can be made and the only place all four ways a program arrives pass through. A target naming an empty line is still allowed, for the same reason RENUMBER leaves one alone, and a fully numbered program is unaffected, which is every program that existed before this. It shares RENUMBER's walk rather than repeating it. renumber.c grows an akbasic_TargetWalk -- a self pointer and a visit function -- and rewrite_line() takes one. RENUMBER's visitor substitutes the number a line moved to; the check's substitutes the number unchanged and raises. One walk, so the two cannot disagree about what a branch target is. The check points environment->lineno at the line being walked so the "? N :" prefix names the offending line. The other three prescans do not and report whichever line the loader stopped on; TODO.md section 5 item 64 records it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -380,7 +380,7 @@ akerr_ErrorContext *akbasic_runtime_set_mode(akbasic_Runtime *obj, int mode)
|
||||
*/
|
||||
if ( obj->mode == AKBASIC_MODE_RUN && obj->environment != NULL ) {
|
||||
/*
|
||||
* All three prescans, and all three inside one ATTEMPT.
|
||||
* All four prescans, and all four inside one ATTEMPT.
|
||||
*
|
||||
* **A prescan failure is the program's mistake, not the host's**, so it
|
||||
* has to leave here as a BASIC error line rather than as a raised
|
||||
@@ -417,6 +417,16 @@ akerr_ErrorContext *akbasic_runtime_set_mode(akbasic_Runtime *obj, int mode)
|
||||
* skipped the lines that declared it.
|
||||
*/
|
||||
CATCH(errctx, akbasic_structtype_scan(obj));
|
||||
/*
|
||||
* Then the branch targets, last because it is the only one that reads
|
||||
* nothing into the runtime -- it only refuses. A loaded line may be
|
||||
* *given* its number rather than carry one, so `GOTO 100` in a script
|
||||
* written without numbers would otherwise find the hundredth line and
|
||||
* branch there, which is plausible, silent and wrong. Said here rather
|
||||
* than at the branch, because before the program runs is earlier and
|
||||
* every path into a run comes through this function.
|
||||
*/
|
||||
CATCH(errctx, akbasic_runtime_check_targets(obj));
|
||||
} CLEANUP {
|
||||
} PROCESS(errctx) {
|
||||
} HANDLE_DEFAULT(errctx) {
|
||||
|
||||
Reference in New Issue
Block a user