A multi-line DEF called outside a running program does not run its body, and says nothing #8

Open
opened 2026-08-02 19:04:14 -04:00 by tachikoma · 0 comments
Collaborator

Source: TODO.md §6 item 41 (at 9151438)

DEF TRIPLE(N#)
T# = N# * 3
RETURN T#
PRINT TRIPLE(14)

At the REPL that prints (UNDEFINED STRING REPRESENTATION FOR 0). In a program run from a file
the same function answers 42. No error either way.

akbasic_runtime_user_function() (src/runtime.c) runs a multi-line body by handing control to
the call's environment and spinning a line loop until RETURN pops back out, guarded on
mode == AKBASIC_MODE_RUN
-- true only of a program running from a file. In REPL mode the loop
is skipped entirely and the result is whatever is in the caller's return slot, which is zero.

The single-expression form has no such loop and is unaffected, which is most of why this has gone
unnoticed; and tests/user_functions.c drives every case through run_program, so the whole
suite is in RUN mode.

The obvious fix is wrong, and was tried. Widening the guard to mode != AKBASIC_MODE_QUIT
makes the interpreter hang rather than answer wrongly: akbasic_runtime_process_line_run() does
not advance a REPL-mode runtime the way this loop assumes, so the environment never comes back.
Trading a silent wrong answer for a lock-up is worse. The fix wants the REPL's own line cycle
driving the body, which is a change to how a call is executed rather than to a condition.

A test belongs in tests/user_functions.c asserting the correct contract -- a multi-line function
answering 42 at the REPL -- registered in AKBASIC_KNOWN_FAILING_TESTS until it does.

Files: src/runtime.c (akbasic_runtime_user_function), tests/user_functions.c


Filed by Tachikoma (Claude Code, Opus 5, 1M context)

**Source:** TODO.md §6 item 41 (at 9151438) ```basic DEF TRIPLE(N#) T# = N# * 3 RETURN T# PRINT TRIPLE(14) ``` At the REPL that prints `(UNDEFINED STRING REPRESENTATION FOR 0)`. **In a program run from a file the same function answers 42. No error either way.** `akbasic_runtime_user_function()` (`src/runtime.c`) runs a multi-line body by handing control to the call's environment and spinning a line loop until `RETURN` pops back out, **guarded on `mode == AKBASIC_MODE_RUN`** -- true only of a program running from a file. In REPL mode the loop is skipped entirely and the result is whatever is in the caller's return slot, which is zero. The single-expression form has no such loop and is unaffected, which is most of why this has gone unnoticed; and `tests/user_functions.c` drives every case through `run_program`, **so the whole suite is in RUN mode.** **The obvious fix is wrong, and was tried.** Widening the guard to `mode != AKBASIC_MODE_QUIT` makes the interpreter *hang* rather than answer wrongly: `akbasic_runtime_process_line_run()` does not advance a REPL-mode runtime the way this loop assumes, so the environment never comes back. **Trading a silent wrong answer for a lock-up is worse.** The fix wants the REPL's own line cycle driving the body, which is a change to how a call is executed rather than to a condition. A test belongs in `tests/user_functions.c` asserting the correct contract -- a multi-line function answering 42 at the REPL -- registered in `AKBASIC_KNOWN_FAILING_TESTS` until it does. **Files:** `src/runtime.c` (`akbasic_runtime_user_function`), `tests/user_functions.c` --- Filed by Tachikoma (Claude Code, Opus 5, 1M context)
tachikoma added this to the 0.1.x milestone 2026-08-02 19:04:14 -04:00
tachikoma added the defectblast-radius:mediumstatus::grooming labels 2026-08-02 19:04:14 -04:00
Sign in to join this conversation.