fix(vm): a text run from inside a line no longer takes the line's place -- LOAD and THRU go on with the rest of it
FABRIC-3.md XXXIV.1, traced and fixed. vm_interpret() copied each text into the VM's one input buffer and reset the position. LOAD runs its block by calling it, so the block replaced the line LOAD was on and nothing after LOAD or THRU on that line was ever interpreted; the same for -->, a capsule's EXEC and a message kernel-Hermes delivers. vm_interpret() now keeps what the outer text had not yet read when it is called from inside another, and puts it back after, as FORTH-79's LOAD keeps the input stream it interrupts. In the kernel's VM and the hosted one alike. POST: LOAD.rest_of_line (1 LOAD 0 0 /) and THRU.rest_of_line (1 2 THRU 0 0 /) failed without the fix (logs/20261014-061222) and pass with it. Three-ISA acceptance: logs/20261014-061508 (amd64), -061624 (aarch64), -061837 (riscv64), 1052 tests, none failed, zuse prompt, hash 0x08873e0f44b7cb2a unchanged. The hosted binary: 100 LOAD 1 + . gives 8. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
9772ee8b68
commit
c15589b39e
+15
-2
@@ -5197,6 +5197,19 @@ sequence at all until/unless this is fixed at the source -- the interactive two-
|
||||
(typed live over the serial socket) sidesteps the bug, it does not fix it. Flagged here as a
|
||||
real, reproducible defect worth its own fix later, out of scope for this section.
|
||||
|
||||
**Fixed 2026-10-14.** The mechanism, traced: `vm_interpret()` (`kernel/src/vm/vm_core.c`, and
|
||||
the hosted `v3/src/vm.c`) copies each text it runs into the VM's one `input_buffer` and resets
|
||||
`input_pos`. `LOAD` runs its block by calling it, so the block took the place of the line `LOAD`
|
||||
was on, and when the block was done the outer loop found nothing left to read. The same held
|
||||
for every word that runs text from inside a line: `-->`, a capsule's `EXEC`, a message
|
||||
kernel-Hermes delivers. `vm_interpret()` now keeps what the outer text had not yet read when it
|
||||
is called from inside another, and puts it back afterwards, as FORTH-79's `LOAD` keeps the
|
||||
input stream it interrupts. Two POST cases, `LOAD.rest_of_line` (`1 LOAD 0 0 /`) and
|
||||
`THRU.rest_of_line` (`1 2 THRU 0 0 /`), fail without the fix (`logs/20261014-061222`) and pass
|
||||
with it; three-ISA acceptance `logs/20261014-061508` (amd64), `-061624` (aarch64), `-061837`
|
||||
(riscv64): 1052 tests, none failed, hash `0x08873e0f44b7cb2a` unchanged. So a `THRU`-then-call
|
||||
line now works as one `SK_CMD`; that use itself was not re-run.
|
||||
|
||||
### XXXIV.2 -- A cross-session QEMU collision killed the first real relaunch at 0 trials
|
||||
|
||||
Once running for real (via the two-line workaround), the campaign was silently killed a fourth
|
||||
@@ -5476,8 +5489,8 @@ device it was minted on.
|
||||
### XXXV.7 -- Genuinely open, not decided here
|
||||
|
||||
- **Exact v2 decimation factor**, if/when v2 is built at all (§XXXV.4).
|
||||
- Whether `THRU`'s own line-swallowing defect (§XXXIV.1, still un-root-caused at the C level) gets
|
||||
fixed before or after this work -- it blocks true single-command `SK_STARTUP_FORTH` autolaunch
|
||||
- ~~Whether `THRU`'s own line-swallowing defect (§XXXIV.1, still un-root-caused at the C level) gets
|
||||
fixed before or after this work~~ Fixed 2026-10-14, before it (§XXXIV.1) -- it blocks true single-command `SK_STARTUP_FORTH` autolaunch
|
||||
of the campaign regardless of persistence format; the two-line interactive workaround still
|
||||
applies either way.
|
||||
- Disposal of the two stale partial amd64 runs' data (§XXXIV.5's own still-open item) is unrelated
|
||||
|
||||
@@ -1184,6 +1184,23 @@ void vm_interpret(VM* vm, const char* input)
|
||||
|
||||
VM *prev_attrib_vm = g_log_attrib_vm;
|
||||
g_log_attrib_vm = vm;
|
||||
/* A TEXT RUN FROM INSIDE ANOTHER -- LOAD and THRU, -->, a capsule's EXEC,
|
||||
* a message kernel-Hermes delivers -- takes the VM's one input buffer.
|
||||
* What the outer text had not yet read is kept here and put back, so
|
||||
* that it goes on after: FORTH-79's LOAD keeps the input stream it
|
||||
* interrupts. Before 2026-10-14 it was overwritten, and everything
|
||||
* after `5044 5055 THRU` on a line was never interpreted
|
||||
* (FABRIC-3.md XXXIV.1). */
|
||||
int nested = g_vm_interpret_depth > 0;
|
||||
char outer_buffer[INPUT_BUFFER_SIZE];
|
||||
size_t outer_length = 0, outer_pos = 0;
|
||||
if (nested) {
|
||||
size_t k;
|
||||
outer_length = vm->input_length < INPUT_BUFFER_SIZE ? vm->input_length : INPUT_BUFFER_SIZE - 1;
|
||||
outer_pos = vm->input_pos;
|
||||
for (k = 0; k < outer_length; k++) outer_buffer[k] = vm->input_buffer[k];
|
||||
outer_buffer[outer_length] = '\0';
|
||||
}
|
||||
g_vm_interpret_depth++;
|
||||
|
||||
/* Load into input buffer (cap + NUL) */
|
||||
@@ -1218,6 +1235,12 @@ void vm_interpret(VM* vm, const char* input)
|
||||
}
|
||||
|
||||
g_vm_interpret_depth--;
|
||||
if (nested) {
|
||||
size_t k;
|
||||
for (k = 0; k <= outer_length; k++) vm->input_buffer[k] = outer_buffer[k];
|
||||
vm->input_length = outer_length;
|
||||
vm->input_pos = outer_pos;
|
||||
}
|
||||
g_log_attrib_vm = prev_attrib_vm;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -129,9 +129,12 @@ static WordTestSuite block_word_suites[] = {
|
||||
{"zero_block", "0 LOAD", "Should handle block 0", TEST_ERROR_CASE, 1, 1, {0}},
|
||||
{"nonexistent", "9999 LOAD", "Should handle missing block", TEST_ERROR_CASE, 1, 1, {0}},
|
||||
{"empty_stack", "LOAD", "Should cause stack underflow", TEST_ERROR_CASE, 1, 1, {0}},
|
||||
/* The line goes on after the block (FABRIC-3.md XXXIV.1): the division after LOAD runs, and fails.
|
||||
* Before 2026-10-14 the block took the line's place and nothing after LOAD was ever interpreted. */
|
||||
{"rest_of_line", "1 LOAD 0 0 /", "The rest of the line is interpreted after LOAD", TEST_ERROR_CASE, 1, 1, {0}},
|
||||
{NULL, NULL, NULL, TEST_NORMAL, 0, 0, {0}}
|
||||
},
|
||||
4, {0}
|
||||
5, {0}
|
||||
},
|
||||
|
||||
{
|
||||
@@ -173,6 +176,8 @@ static WordTestSuite block_word_suites[] = {
|
||||
|
||||
/* Stack underflow */
|
||||
{"empty_stack", "THRU", "Should cause stack underflow (no args)", TEST_ERROR_CASE, 1, 1, {0}},
|
||||
/* the line goes on after the blocks (FABRIC-3.md XXXIV.1) */
|
||||
{"rest_of_line", "1 2 THRU 0 0 /", "The rest of the line is interpreted after THRU", TEST_ERROR_CASE, 1, 1, {0}},
|
||||
{"one_arg", "1 THRU", "Should cause stack underflow (one arg)", TEST_ERROR_CASE, 1, 1, {0}},
|
||||
|
||||
/* Range tests */
|
||||
@@ -186,7 +191,7 @@ static WordTestSuite block_word_suites[] = {
|
||||
|
||||
{NULL, NULL, NULL, TEST_NORMAL, 0, 0, {0}}
|
||||
},
|
||||
17, {0}
|
||||
18, {0}
|
||||
},
|
||||
|
||||
/* End marker */
|
||||
|
||||
+30
@@ -793,10 +793,32 @@ void vm_interpret_word(VM* vm, const char* word_str, size_t len)
|
||||
* @param vm VM instance.
|
||||
* @param input NUL-terminated FORTH source string to interpret.
|
||||
*/
|
||||
/* How many vm_interpret() calls are running: see the nested-text note in it. */
|
||||
static int g_vm_interpret_depth;
|
||||
|
||||
void vm_interpret(VM* vm, const char* input)
|
||||
{
|
||||
if (!vm || !input) return;
|
||||
|
||||
/* A TEXT RUN FROM INSIDE ANOTHER -- LOAD and THRU, -->, a capsule's EXEC,
|
||||
* whatever else runs text -- takes the VM's one input buffer.
|
||||
* What the outer text had not yet read is kept here and put back, so
|
||||
* that it goes on after: FORTH-79's LOAD keeps the input stream it
|
||||
* interrupts. Before 2026-10-14 it was overwritten, and everything
|
||||
* after `5044 5055 THRU` on a line was never interpreted
|
||||
* (FABRIC-3.md XXXIV.1). */
|
||||
int nested = g_vm_interpret_depth > 0;
|
||||
char outer_buffer[INPUT_BUFFER_SIZE];
|
||||
size_t outer_length = 0, outer_pos = 0;
|
||||
if (nested) {
|
||||
size_t k;
|
||||
outer_length = vm->input_length < INPUT_BUFFER_SIZE ? vm->input_length : INPUT_BUFFER_SIZE - 1;
|
||||
outer_pos = vm->input_pos;
|
||||
for (k = 0; k < outer_length; k++) outer_buffer[k] = vm->input_buffer[k];
|
||||
outer_buffer[outer_length] = '\0';
|
||||
}
|
||||
g_vm_interpret_depth++;
|
||||
|
||||
/* Load into input buffer (cap + NUL) */
|
||||
size_t n = 0, cap = INPUT_BUFFER_SIZE ? INPUT_BUFFER_SIZE - 1 : 0;
|
||||
while (n < cap)
|
||||
@@ -821,6 +843,14 @@ void vm_interpret(VM* vm, const char* input)
|
||||
{
|
||||
vm_interpret_word(vm, word, wlen);
|
||||
}
|
||||
|
||||
g_vm_interpret_depth--;
|
||||
if (nested) {
|
||||
size_t k;
|
||||
for (k = 0; k <= outer_length; k++) vm->input_buffer[k] = outer_buffer[k];
|
||||
vm->input_length = outer_length;
|
||||
vm->input_pos = outer_pos;
|
||||
}
|
||||
}
|
||||
|
||||
/* ====================== VM memory helpers ======================= */
|
||||
|
||||
Reference in New Issue
Block a user