fix(v4.0.0): shorter chains of calls in the compiler capsule

Measured, compiling a DO loop left one return entry spare under
INTERPRET and a defining word called from inside another word
overflowed the nine-entry return stack (D-2); the prompt loop will take
one more.

C@ and C! shift without a loop.  The code generator shifts each opcode
into the word being built, so nothing is placed by a counted shift, and
the address masks come from a table.  The dictionary search, the
in-liner, the end of a loop and the making of a data word are each one
word reached by a jump.

Every kind of line now leaves at least three return entries spare while
it compiles; a word run from the interpreter has six; and CREATE ...
DOES> works from the prompt, from a word, and from a word that calls
that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-04 11:17:58 -04:00
co-authored by Claude Opus 5.5
parent dac7ffac92
commit 806f876ee0
9 changed files with 165 additions and 123 deletions
+29 -13
View File
@@ -270,6 +270,15 @@ int main(void)
CHECK(ok("CREATE CA 5 , 6 , CREATE CB 7 ,") && interpret("CA @ CA 1+ @ CB @") && !err() && pop() == 7 && pop() == 6 && pop() == 5 && clean(),
"CREATE and , build a table");
/* ---- defining words used inside other words ---- */
new_session();
CHECK(ok(": D CREATE , DOES> @ ; : DD D ; : DDD DD ;"), "a defining word, and words that call it");
CHECK(ok("7 D SEVEN 8 DD EIGHT 9 DDD NINE") && interpret("SEVEN EIGHT NINE") && !err() && pop() == 9 && pop() == 8 && pop() == 7 && clean(),
"a defining word works from the prompt, from a word, and from a word that calls that");
CHECK(ok(": MKV VARIABLE ; : MKC CONSTANT ; MKV VV 5 MKC CC 3 VV !") && interpret("VV @ CC") && !err() && pop() == 5 && pop() == 3 && clean(),
"VARIABLE and CONSTANT inside words");
CHECK(ok(": MK: : ; MK: PLUS3 3 + ;") && one("4 PLUS3") == 7, ": inside a word");
/* ---- numbers in other bases ---- */
new_session();
CHECK(one("2 BASE ! 101") == 5, "a number in base 2");
@@ -314,6 +323,11 @@ int main(void)
&& abandoned(": X6 +LOOP ;") && abandoned(": X7 AGAIN ;") && abandoned(": X8 WHILE ;") && n.mem[CFP] == CFS_W,
"a closing word with nothing open abandons the definition, and the control-flow stack is not disturbed");
CHECK(abandoned(":") && abandoned("CREATE") && abandoned("VARIABLE") && interpret("5 CONSTANT") && err(), "a defining word with no name is an error");
{
v4_cell dp = n.mem[DP];
CHECK(interpret("5 CONSTANT") && err() && n.mem[DP] == dp, "CONSTANT with no name stores nothing");
CHECK(interpret("VARIABLE") && err() && n.mem[DP] == dp, "nor does VARIABLE");
}
CHECK(one("3 4 +") == 7, "after all that the interpreter still works");
n.mem[DP] = (DICT_END_W - 2) * 4;
CHECK(interpret(": FULL 1 2 3 4 5 6 7 8 9 ;") && err(), "a definition that does not fit sets NODE-ERROR");
@@ -358,22 +372,24 @@ int main(void)
CHECK(abandoned(": TOODEEP IF IF IF IF IF IF IF IF IF IF IF IF IF IF IF IF IF ;"), "more open structures than the control-flow stack holds abandons the line");
CHECK(one("3 4 +") == 7, "and the interpreter still works afterwards");
/* ---- how deep words may call each other from the interpreter ---- */
/* ---- how deep words may call each other from the interpreter ----
* Measured by how many marked return entries survive under INTERPRET
* while a word that calls nothing runs: each of them is a level of
* nesting some other word could have used. */
{
char line[64];
unsigned depth = 0;
unsigned r, k;
new_session();
CHECK(ok(": N0 1+ ;"), "N0");
for (i = 1; i <= 8; i++) {
snprintf(line, sizeof line, ": N%u N%u 1+ ;", i, i - 1);
CHECK(ok(line), "N%u compiles", i);
CHECK(ok(": N0 1+ ;") && ok(": N1 N0 1+ ;") && ok(": N2 N1 1+ ;") && ok(": N3 N2 1+ ;") && ok(": N4 N3 1+ ;"), "five words, each calling the one before");
CHECK(one("0 N4") == 5, "five levels of calls work from the interpreter");
for (r = 0; r < V4_RET_DEPTH; r++) {
unsigned good = 1;
if (!interpret_with("0 N0", 0, r + 1u) || err()) break;
if (pop() != 1 || pop() != CANARY) break;
for (k = r + 1u; k-- > 0; ) if (v4_rstack_pop(&n.rs) != (v4_cell)(0x6B000000 + k)) good = 0;
if (!good) break;
}
for (i = 0; i <= 8; i++) {
snprintf(line, sizeof line, "0 N%u", i);
if (one(line) == (v4_cell)(i + 1)) depth = i + 1; else break;
}
printf(" words called from the interpreter may nest %u deep\n", depth);
CHECK(depth >= 5, "a few levels of calls work from the interpreter");
printf(" a word run from the interpreter has %u return entries to itself\n", r + 1u);
CHECK(r + 1u >= 6, "the interpreter leaves most of the return stack to the user");
}
/* and how much of the data stack a line may use */
{