feat(v4.0.0): division by zero is guarded (D-15); division words in the capsule

Ruled 2026-10-04: guarded, an error shown, back to the prompt.

- capsule/forth.v4: / MOD /MOD */ */MOD M/MOD, M* and S>D join the
  vocabulary.  Each dividing word tests its divisor first; zero prints
  v3's message ("/: Division by zero"), ends an open definition, prints
  ERROR and returns to the prompt from however deep.
- SM/REM here has UM/MOD written into it and keeps its signs in memory,
  and */MOD has M* written in, so that division can be used inside words
  that other words call: / from four words deep, */MOD from three.
- forth.v4 now loads after quit.v4, which the guard leaves through.
- tests: v3 transcripts for each message; /MOD, /, MOD and */MOD against
  C on every pair of edge values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-04 12:23:30 -04:00
co-authored by Claude Opus 5.5
parent b3d2d56d11
commit 361dcd1148
5 changed files with 156 additions and 7 deletions
+44 -2
View File
@@ -151,6 +151,22 @@ static const programme prog[] = {
{ ": DEEP 1 IF 2 IF 3 IF 4 ELSE 5 THEN ELSE 6 THEN ELSE 7 THEN ;", "DEEP", 1, { 4 }, 1 },
{ ": TM 2* 2* 1- 2/ ;", "5 TM", 1, { 9 }, 1 },
{ ": NEG NEGATE ;", "7 NEG -7 NEG", 2, { -7, 7 }, 1 },
{ "", "7 2 / -7 2 / 7 -2 / -7 -2 /", 4, { 3, -3, -3, 3 }, 1 },
{ "", "7 2 MOD -7 2 MOD 7 -2 MOD -7 -2 MOD", 4, { 1, -1, 1, -1 }, 1 },
{ "", "7 2 /MOD -7 2 /MOD", 4, { 1, 3, -1, -3 }, 1 },
{ "", "7 -2 /MOD -7 -2 /MOD", 4, { 1, -3, -1, 3 }, 1 },
{ "", "100 7 3 */ -100 7 3 */", 2, { 233, -233 }, 1 },
{ "", "100 7 3 */MOD -100 7 3 */MOD", 4, { 1, 233, -1, -233 }, 1 },
{ "", "3 4 M* -3 4 M*", 4, { 12, 0, -12, -1 }, 1 },
{ "", "-5 S>D 5 S>D", 4, { -5, -1, 5, 0 }, 1 },
{ "", "0 5 / 0 5 MOD 1 1 /", 3, { 0, 0, 1 }, 1 },
{ ": D1 / ; : D2 D1 ; : D3 D2 ; : D4 D3 ;", "20 4 D4", 1, { 5 }, 1 },
{ ": E1 */MOD ; : E2 E1 ; : E3 E2 ;", "-100 7 3 E3", 2, { -1, -233 }, 1 },
{ ": E4 */ ; : E5 E4 ;", "-100 7 3 E5", 1, { -233 }, 1 },
{ ": E6 M* ; : E7 E6 ; : E8 E7 ;", "-3 4 E8", 2, { -12, -1 }, 1 },
{ ": AV 0 SWAP 0 DO I + LOOP 10 / ;", "10 AV", 1, { 4 }, 1 },
{ "", "1000000 1000000 M* 1000 M/MOD", 2, { 0, 1000000000 }, 0 },
{ "", "-7 S>D 2 M/MOD", 2, { -1, -3 }, 0 },
};
#define NPROG (sizeof prog / sizeof prog[0])
@@ -179,8 +195,8 @@ int main(void)
printf("v4 host compiler tests: V4_CELL_BITS=%d, V4_NODE_WORDS=%u\n", V4_CELL_BITS, (unsigned)V4_NODE_WORDS);
{
static const char *const files[] = { "core.v4", "input.v4", "dict.v4", "codegen.v4", "compile.v4", "forth.v4" };
CHECK(host_load(&tx, &n, files, 6), "the capsule assembles");
static const char *const files[] = { "core.v4", "input.v4", "dict.v4", "codegen.v4", "compile.v4", "quit.v4", "forth.v4" };
CHECK(host_load(&tx, &n, files, 7), "the capsule assembles");
}
/* two in-line words of the test's own, to exercise the in-liner: one with
* two literals in one instruction word, one with a nop in it */
@@ -351,6 +367,32 @@ int main(void)
}
}
/* ---- /MOD, / and MOD against C, which also truncates toward zero ---- */
{
static const v4_cell v[] = { 0, 1, -1, 2, -2, 3, -7, 10, 12345, -12345, 65536, -65537, (v4_cell)V4_MSB, (v4_cell)(V4_MSB - 1u),
(v4_cell)(V4_MSB + 1u), (v4_cell)((v4_ucell)~(v4_ucell)0 / 3u) };
unsigned j, nv = (unsigned)(sizeof v / sizeof v[0]);
char line[128];
new_session();
for (i = 0; i < nv; i++)
for (j = 0; j < nv; j++) {
v4_cell q, r;
if (v[j] == 0) continue;
if (v[i] == (v4_cell)V4_MSB && v[j] == -1) continue; /* the quotient does not fit a cell */
q = v[i] / v[j]; r = v[i] % v[j];
snprintf(line, sizeof line, "%lld %lld /MOD", (long long)v[i], (long long)v[j]);
CHECK(interpret(line) && !err() && pop() == q && pop() == r && clean(), "%s", line);
snprintf(line, sizeof line, "%lld %lld / %lld %lld MOD", (long long)v[i], (long long)v[j], (long long)v[i], (long long)v[j]);
CHECK(interpret(line) && !err() && pop() == r && pop() == q && clean(), "%s", line);
/* quot * n3 + rem = n1 * n2, wherever the product fits a cell so that C can check it */
if (v[i] > -40000 && v[i] < 40000) {
v4_cell k = 177, prod = (v4_cell)((v4_ucell)v[i] * (v4_ucell)k);
snprintf(line, sizeof line, "%lld %lld %lld */MOD", (long long)v[i], (long long)k, (long long)v[j]);
CHECK(interpret(line) && !err() && pop() == prod / v[j] && pop() == prod % v[j] && clean(), "%s", line);
}
}
}
/* ---- how many values a line may have on the stack ---- */
{
char line[96];