feat(v4.0.0): POST is the kernel's -- the boot feeds the cases, and nothing of the harness is on the node
The boot runs POST with the runner (v4/system/post.c) after the capsules:
it feeds the 538 cases to the node and judges them from outside. A
case's printing no longer reaches the console. What the cases define
stays in the dictionary, as in v3; the system is sealed after POST and
the boot prints PARITY:V4_SYSTEM word_count=N dict_hash=..., as v3 prints
its parity after POST.
Gone: capsules/v4/post79.4th and its blocks 7000 up; the harness words;
(CATCH) and (EMIT-HOOK), with what EMIT and the prompt loop did for them.
The generator runs v3 on the lines as the kernel sends them, without the
capsule's "T| ". One expected result follows from that: >IN.initial
prints 6, not 9.
make -C v4 test, sanitize and hosted-check pass; amd64, aarch64 and
riscv64 boot, POST 538 of 538, word_count=411, dict_hash
0x6fb1d09418b189ee on all six: logs/20261007-105118, -105335, -105651.
T{ is unknown at the prompt; RS1 prints 42 42 before and after COLD. A
scratch build with one expectation changed names the case and ends
PARITY:FAIL, POST: FAILED.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
2bf958ce3a
commit
4bdb210a99
+2
-2
@@ -139,7 +139,7 @@ CAPSULE_DIR_C := $(BINDIR)/capsule_generated.c
|
||||
|
||||
# the kernel's capsule code: find, check the hash, verify the signature,
|
||||
# split a payload into blocks
|
||||
SYSTEM_SRCS := $(HERE)/system/boot.c \
|
||||
SYSTEM_SRCS := $(HERE)/system/boot.c $(HERE)/system/post.c $(HERE)/system/post_cases.c \
|
||||
$(addprefix $(ROOT)/kernel/src/capsule/,capsule_find.c capsule_validate.c capsule_sig.c capsule_blocks.c) \
|
||||
$(ROOT)/kernel/src/hash/xxhash64.c $(ROOT)/kernel/src/crypto/x509_ed25519.c $(CRYPTO_SRCS)
|
||||
# The kernel's sources are held to the kernel's warnings, not this model's.
|
||||
@@ -169,7 +169,7 @@ $(foreach i,$(HOSTED_ISAS),$(eval $(call HOSTED_RULE,$(i))))
|
||||
|
||||
# `make post` boots the amd64 system alone, which is the quick check after a
|
||||
# change to the vocabulary: nucleus, forth79.4th, POST, prompt. `make
|
||||
# post79` writes the POST capsule, ../capsules/v4/post79.4th, again from
|
||||
# post79` writes POST's cases, system/post_cases.c, again from
|
||||
# v3's cases and tools/post79_rules.py (tools/mkpost.py); it needs the
|
||||
# hosted v3 binary.
|
||||
.PHONY: post post79
|
||||
|
||||
+14
-10
@@ -159,21 +159,25 @@ a time with POST after each, is the next step (`NUCLEUS.md` section 8).
|
||||
|
||||
## POST
|
||||
|
||||
`capsules/v4/post79.4th`: 538 cases in blocks 7000 up, covering 126 of the
|
||||
130 words of the FORTH-79 Required Word Set. 443 are v3's cases with what
|
||||
the hosted v3 binary did as the expected result. The other 107, and the 27
|
||||
v3 cases left out, are listed with reasons in `docs/v4.0.0/POST79.md`.
|
||||
POST is the kernel's, as in v3 (`MESH.md` step 6b, `NUCLEUS.md` 6.3). The
|
||||
kernel holds the cases, `v4/system/post_cases.c`: 538 of them, covering 126
|
||||
of the 130 words of the FORTH-79 Required Word Set. 431 are v3's cases with
|
||||
what the hosted v3 binary did as the expected result; every other case,
|
||||
and the v3 cases left out, are listed with reasons in
|
||||
`docs/v4.0.0/POST79.md`.
|
||||
|
||||
- `make -C v4 post` boots the amd64 system: the quick check after a change.
|
||||
- `make -C v4 hosted-check` boots all three hosted ISAs and compares them.
|
||||
- `make -C v4 post79` writes the capsule again (`tools/mkpost.py`,
|
||||
- `make -C v4 post79` writes the cases again (`tools/mkpost.py`,
|
||||
`tools/post79_rules.py`); it needs the hosted v3 binary.
|
||||
|
||||
POST is FORTH: a harness of FORTH-79 words and two nucleus hooks, `(CATCH)`
|
||||
and `(EMIT-HOOK)`. It forgets itself when it has finished. The boot passes
|
||||
only on seeing POST's tally line with `fail=0` (`v4/system/boot.c`). A colon
|
||||
definition broken on purpose (`U/MOD` without its last `SWAP`) fails five
|
||||
cases and the boot stops with `POST: FAILED`.
|
||||
The runner, `v4/system/post.c`, feeds each case to the node a line at a
|
||||
time, keeps what the node prints, reads its data stack from outside, and
|
||||
judges: an error exactly if one is expected, and otherwise the stack and
|
||||
every character printed. Nothing of POST is in the node's dictionary. What
|
||||
the cases define stays there, as in v3, and the system is sealed after
|
||||
POST. The boot then prints `PARITY:V4_SYSTEM word_count=N dict_hash=0x...`,
|
||||
and passes only if no case failed (`v4/system/boot.c`).
|
||||
|
||||
Not tested: `KEY`, `EXPECT`, `QUERY` (the keyboard) and `QUIT` (it returns
|
||||
without ` ok`).
|
||||
|
||||
+4
-10
@@ -251,18 +251,12 @@ header CMOVE
|
||||
(OUT) (OUT^) a! ! ;
|
||||
NONE: drop ;
|
||||
|
||||
\ EMIT puts the character with what has been printed, unless (EMIT-HOOK)
|
||||
\ holds the xt of a word: then that word is given it instead ( c -- ), and
|
||||
\ returns to EMIT's caller. Everything the node prints goes through EMIT,
|
||||
\ so a hook sees all of it. The hook is taken off when the text being
|
||||
\ interpreted ends (quit.v4), so that nothing can leave a node silent.
|
||||
\ POST uses it to compare what a case prints (docs/v4.0.0/NUCLEUS.md 6.3).
|
||||
\ A is kept: the words that print have always been free to use it across
|
||||
\ an EMIT. As v3, only the low 8 bits of the character are printed.
|
||||
\ EMIT puts the character with what has been printed. Everything the node
|
||||
\ prints goes through EMIT. A is kept: the words that print have always
|
||||
\ been free to use it across an EMIT. As v3, only the low 8 bits of the
|
||||
\ character are printed.
|
||||
header EMIT
|
||||
: EMIT ( c -- )
|
||||
(EMIT-HOOK) b! @b if NONE push ;
|
||||
NONE: drop
|
||||
255 and a push push \ A, and then the character, to the return stack
|
||||
(OUT^) b! @b a! pop !+ a !b \ the character goes where (OUT^) points, which moves on
|
||||
a (OUT)+256 xor if FULL drop pop a! ;
|
||||
|
||||
+3
-16
@@ -48,18 +48,10 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
||||
\ structures. A definition that was under way stays hidden.
|
||||
: (RESET) 0 STATE a! ! (CG-RESET) CFBASE (CFP) a! ! ;
|
||||
|
||||
\ CATCHING A LINE'S ERROR. While the variable (CATCH) is not zero, a line
|
||||
\ that ends in an error -- a fault, or a code in NODE-ERROR -- does not say
|
||||
\ ERROR: (CATCH) is set to -1 and the line is reported as completed. Whatever message the
|
||||
\ error printed has gone wherever EMIT was sending it. POST uses this to
|
||||
\ go on to its next case, and to check the cases that must fail
|
||||
\ (docs/v4.0.0/NUCLEUS.md 6.3). (EMIT-HOOK), core.v4, is set to 0 here at
|
||||
\ the end of every line, caught or not.
|
||||
\
|
||||
\ ( s -- ) the text is done with. What it printed is sent, then a message
|
||||
\ saying how it ended -- 0 QUIT, 1 completed, 2 an error -- and the node waits
|
||||
\ for the next. (DONE) is entered with the same number and decides whether
|
||||
\ an error was one to catch; it is always jumped to, never called, by
|
||||
\ for the next. (DONE) is entered with the same number and, for an error,
|
||||
\ stops compiling first; it is always jumped to, never called, by
|
||||
\ something that has emptied the return stack (or by a fault, which empties
|
||||
\ it). (LINE-STATUS) keeps how the last text ended.
|
||||
: (FINISH) ( s -- )
|
||||
@@ -70,11 +62,8 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
||||
jump (IDLE)
|
||||
|
||||
: (DONE)
|
||||
(EMIT-HOOK) b! 0 !b \ what is printed is printed again
|
||||
if GO -1 + if OK
|
||||
drop (RESET)
|
||||
(CATCH) b! @b if LOUD drop -1 !b 1 jump (DONE) \ caught: the text completed
|
||||
LOUD: drop 2 jump (FINISH)
|
||||
drop (RESET) 2 jump (FINISH)
|
||||
OK: drop 1 jump (FINISH)
|
||||
GO: drop 0 jump (FINISH)
|
||||
|
||||
@@ -247,13 +236,11 @@ header (SEAL)
|
||||
\ further, the message for the code is printed (-1: the word has printed
|
||||
\ its own) and the line ends with ERROR. The codes are listed in core.v4.
|
||||
header NODE-ERROR inline : NODE-ERROR' NODE-ERROR ;
|
||||
header (CATCH) inline : (CATCH)' (CATCH) ;
|
||||
header (ME) inline : (ME)' (ME) ;
|
||||
header (CONSOLE) inline : (CONSOLE)' (CONSOLE) ;
|
||||
header (LOST) inline : (LOST)' (LOST) ;
|
||||
header (WORD-DEFINED) inline : (WORD-DEFINED)' (WORD-DEFINED) ;
|
||||
header (WORD-FORGOTTEN) inline : (WORD-FORGOTTEN)' (WORD-FORGOTTEN) ;
|
||||
header (EMIT-HOOK) inline : (EMIT-HOOK)' (EMIT-HOOK) ;
|
||||
|
||||
header QUIT
|
||||
: QUIT R-CLEAR (RESET) CR 0 jump (DONE)
|
||||
|
||||
+51
-50
@@ -4,6 +4,7 @@
|
||||
* It is not part of the engine (v4/src): it needs the capsule directory,
|
||||
* which only a whole system has.
|
||||
*/
|
||||
#include "v4/post.h"
|
||||
#include "v4/blocks.h"
|
||||
#include "v4/boot.h"
|
||||
#include "v4/message.h"
|
||||
@@ -13,18 +14,18 @@
|
||||
#include "starkernel/capsule_blocks.h"
|
||||
|
||||
/* The capsules, in the order they are loaded. */
|
||||
/* POST is part of the boot: v4:post79.4th is loaded after the vocabulary it
|
||||
* tests and must end with a clean tally. -DV4_POST_AT_BOOT=0 leaves it
|
||||
* out, for work on a vocabulary that does not pass yet. */
|
||||
static const char *const boot_capsules[] = {
|
||||
"v4:forth79.4th",
|
||||
};
|
||||
|
||||
/* POST is part of the boot, and is the kernel's (v4/include/v4/post.h;
|
||||
* docs/v4.0.0/NUCLEUS.md 6.3): when the capsules are in, the kernel feeds
|
||||
* its cases to the node and judges them, and the boot passes only if none
|
||||
* failed. -DV4_POST_AT_BOOT=0 leaves it out, for work on a vocabulary
|
||||
* that does not pass yet. */
|
||||
#ifndef V4_POST_AT_BOOT
|
||||
#define V4_POST_AT_BOOT 1
|
||||
#endif
|
||||
static const char *const boot_capsules[] = {
|
||||
"v4:forth79.4th",
|
||||
#if V4_POST_AT_BOOT
|
||||
"v4:post79.4th",
|
||||
#endif
|
||||
};
|
||||
|
||||
#define LINE_MAX 1025u /* a line is at most 1024 characters, a block */
|
||||
#define STEP_LIMIT 4000000000ULL /* instruction words one line may take */
|
||||
@@ -77,37 +78,6 @@ static uint32_t word_count(const v4_node *n, const v4_image *im)
|
||||
|
||||
/* ---- handing the node a line ---------------------------------------------- */
|
||||
|
||||
/* POST's verdict. The POST capsule ends by printing one line,
|
||||
* PARITY:V4_POST tests=N pass=N fail=N
|
||||
* and the boot passes only if it has seen that line with fail=0. That no
|
||||
* line was refused is not enough: a POST that was broken half way would
|
||||
* refuse nothing. */
|
||||
static char post_line[96];
|
||||
static unsigned post_len;
|
||||
static int post_seen, post_clean;
|
||||
|
||||
static int text_at(const char *line, unsigned len, unsigned at, const char *want)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; want[i]; i++)
|
||||
if (at + i >= len || line[at + i] != want[i]) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void post_watch(char c)
|
||||
{
|
||||
static const char clean[] = " fail=0";
|
||||
if (c != '\n') {
|
||||
if (post_len < sizeof post_line) post_line[post_len++] = c;
|
||||
return;
|
||||
}
|
||||
if (text_at(post_line, post_len, 0, "PARITY:V4_POST ")) {
|
||||
post_seen = 1;
|
||||
post_clean = post_len >= sizeof clean - 1 && text_at(post_line, post_len, post_len - (unsigned)(sizeof clean - 1), clean);
|
||||
}
|
||||
post_len = 0;
|
||||
}
|
||||
|
||||
/* The boot is the node's neighbour on two of its ports. On port 0 it is
|
||||
* the kernel: the node writes the number of a request there (ENGINE.md
|
||||
* 3.3) -- for a block (blocks.h), or for one of the host's words. On port
|
||||
@@ -120,7 +90,10 @@ static void post_watch(char c)
|
||||
/* What the kernel keeps of the node's block window (blocks.h). */
|
||||
static v4_blocks boot_blocks;
|
||||
|
||||
int v4_boot_line(const v4_boot *b, const char *text, unsigned len)
|
||||
/* Hand the node a line and run it to its end. What it prints goes to the
|
||||
* console; or, if `keep` is given, into `keep`, at most `cap` characters,
|
||||
* and none of it is shown: *printed is then how many it printed in all. */
|
||||
static int run_line(const v4_boot *b, const char *text, unsigned len, char *keep, unsigned cap, unsigned *printed)
|
||||
{
|
||||
static v4_message out, in; /* one line at a time: the boot is not re-entered */
|
||||
v4_node *n = b->n;
|
||||
@@ -155,8 +128,8 @@ int v4_boot_line(const v4_boot *b, const char *text, unsigned len)
|
||||
if (v4_message_type(&in) == V4_MSG_OUTPUT) {
|
||||
for (i = 0; i < chars; i++) {
|
||||
char c = v4_message_char(&in, i);
|
||||
post_watch(c);
|
||||
b->out(&c, 1);
|
||||
if (!keep) b->out(&c, 1);
|
||||
else { if (*printed < cap) keep[*printed] = c; (*printed)++; }
|
||||
}
|
||||
} else if (v4_message_type(&in) == V4_MSG_DONE) {
|
||||
return (int)in.word[V4_MSG_HEADER];
|
||||
@@ -185,6 +158,27 @@ int v4_boot_line(const v4_boot *b, const char *text, unsigned len)
|
||||
}
|
||||
}
|
||||
|
||||
int v4_boot_line(const v4_boot *b, const char *text, unsigned len)
|
||||
{
|
||||
return run_line(b, text, len, 0, 0u, 0);
|
||||
}
|
||||
|
||||
#if V4_POST_AT_BOOT
|
||||
/* POST's host (post.h): a line is handed to the node as any line is, and
|
||||
* what the node prints is kept for the runner and not shown. */
|
||||
static int post_line(void *self, const char *text, unsigned text_len, char *out, unsigned cap, unsigned *len)
|
||||
{
|
||||
int how;
|
||||
*len = 0;
|
||||
how = run_line((const v4_boot *)self, text, text_len, out, cap, len);
|
||||
return (how == V4_TEXT_QUIT || how == V4_TEXT_COMPLETED || how == V4_TEXT_ERROR) ? how : -1;
|
||||
}
|
||||
static void post_say(void *self, const char *text, unsigned len)
|
||||
{
|
||||
((const v4_boot *)self)->out(text, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ---- one capsule --------------------------------------------------------- */
|
||||
|
||||
static void say_where(const v4_boot *b, const char *name, uint32_t block, uint32_t line)
|
||||
@@ -345,7 +339,6 @@ int v4_boot_run(const v4_boot *b)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
post_len = 0; post_seen = 0; post_clean = 0;
|
||||
/* a node is born with nothing in it, and is sent the nucleus */
|
||||
if (!v4_image_born(b->n, b->es, b->h, b->im)) {
|
||||
say(b, "V4: the nucleus is not for this build of the engine\nPARITY:FAIL\nPOST: FAILED\n");
|
||||
@@ -375,16 +368,24 @@ int v4_boot_run(const v4_boot *b)
|
||||
for (i = 0; i < sizeof boot_capsules / sizeof boot_capsules[0]; i++)
|
||||
if (!load_capsule(b, boot_capsules[i])) { say(b, "PARITY:FAIL\nPOST: FAILED\n"); return 0; }
|
||||
#if V4_POST_AT_BOOT
|
||||
if (!post_seen || !post_clean) {
|
||||
say(b, post_seen ? "V4: POST reported failures\n" : "V4: POST did not report\n");
|
||||
say(b, "PARITY:FAIL\nPOST: FAILED\n");
|
||||
return 0;
|
||||
{
|
||||
v4_post_host host;
|
||||
v4_post_tally tally;
|
||||
host.self = (void *)b; host.n = b->n; host.line = post_line; host.say = post_say;
|
||||
if (!v4_post_run(&host, v4_post_cases, v4_post_case_count, &tally)) {
|
||||
say(b, "V4: POST reported failures\nPARITY:FAIL\nPOST: FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The kernel's words and the capsules are the system: COLD comes back
|
||||
* to here, and FORGET will not go below it. */
|
||||
/* The kernel's words, the capsules and what POST's cases defined are
|
||||
* the system, as in v3: COLD comes back to here, and FORGET will not go
|
||||
* below it. */
|
||||
v4_image_seal(b->n, b->im);
|
||||
say(b, "PARITY:V4_SYSTEM word_count="); say_dec(b, word_count(b->n, b->im));
|
||||
say(b, " dict_hash="); say_hex(b, v4_boot_dict_hash(b->n, b->im));
|
||||
say(b, "\n");
|
||||
|
||||
#if V4_POST_AT_BOOT
|
||||
say(b, "PARITY:OK\nPOST: PASSED\n");
|
||||
|
||||
@@ -877,7 +877,7 @@ const v4_post_case v4_post_cases[] = {
|
||||
{ "-TRAILING.empty", l318, 1u, V4_POST_STACK, 0, 0u, "\012", 1u },
|
||||
{ "CMOVE.basic", l319, 2u, V4_POST_STACK, 0, 0u, "65 66 \012", 7u },
|
||||
{ "CMOVE.empty", l320, 1u, V4_POST_STACK, 0, 0u, "", 0u },
|
||||
{ ">IN.initial", l321, 1u, V4_POST_STACK, 0, 0u, "9 \012", 3u },
|
||||
{ ">IN.initial", l321, 1u, V4_POST_STACK, 0, 0u, "6 \012", 3u },
|
||||
{ ">IN.advances", l322, 1u, V4_POST_STACK, 0, 0u, "0 \012", 3u },
|
||||
{ "..positive", l323, 1u, V4_POST_STACK, 0, 0u, "42 \012", 4u },
|
||||
{ "..negative", l324, 1u, V4_POST_STACK, 0, 0u, "-42 \012", 5u },
|
||||
|
||||
@@ -65,8 +65,6 @@
|
||||
#define SRC (BVARS + 8)
|
||||
#define SRC_HOOK (BVARS + 9)
|
||||
#define ACL_HOOK (BUF0_W - 6) /* the xt of the access control recheck word, or 0 */
|
||||
#define EMIT_HOOK (BUF0_W - 7) /* the xt of a word EMIT gives its character to, or 0: the console */
|
||||
#define CATCH (BUF0_W - 8) /* non-zero: a line's error is recorded here, -1, and the line ends " ok" */
|
||||
#define LINE_STATUS (BUF0_W - 9) /* how the last line ended: 0 QUIT, 1 completed, 2 an error */
|
||||
#define PORT (BUF0_W - 32) /* the node's ports (node.h): V4_PORTS of them, then "any port" and "which port". Port 0 is where its requests go. */
|
||||
#define CONSOLE (BUF0_W - 16) /* the node what this one prints is sent to; 0: whoever sent the text being served */
|
||||
@@ -175,8 +173,6 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
|
||||
v4_text_constant(tx, "BLK", BLK);
|
||||
v4_text_constant(tx, "(SRC)", SRC);
|
||||
v4_text_constant(tx, "(SRC-HOOK)", SRC_HOOK);
|
||||
v4_text_constant(tx, "(EMIT-HOOK)", EMIT_HOOK);
|
||||
v4_text_constant(tx, "(CATCH)", CATCH);
|
||||
v4_text_constant(tx, "(LINE-STATUS)", LINE_STATUS);
|
||||
v4_text_constant(tx, "(PORT)", PORT);
|
||||
v4_text_constant(tx, "(ME)", ME);
|
||||
|
||||
@@ -1277,6 +1277,11 @@ int main(void)
|
||||
post_lost = 0;
|
||||
}
|
||||
|
||||
/* ---- nothing of POST is on the node (NUCLEUS.md 6.3) ---- */
|
||||
boot();
|
||||
CHECK(is(say("(CATCH)\n"), "UNKNOWN WORD: '(CATCH)'\n ERROR\nok> ") && is(say("(EMIT-HOOK)\n"), "UNKNOWN WORD: '(EMIT-HOOK)'\n ERROR\nok> ")
|
||||
&& is(say("T{\n"), "UNKNOWN WORD: 'T{'\n ERROR\nok> "), "the nucleus has neither of the variables the POST capsule needed, nor its words");
|
||||
|
||||
/* ---- storage that refuses a write (MESH.md 8.2) ---- */
|
||||
boot();
|
||||
CHECK(is(say(REFUSING_FIRST " BLOCK DROP UPDATE SAVE-BUFFERS 65 EMIT\n"), "Storage refused\n ERROR\nok> "), "a write storage will not take says so, and the prompt returns");
|
||||
@@ -1450,7 +1455,7 @@ int main(void)
|
||||
boot_bare();
|
||||
CHECK(load_source("tools.fth"), "tools.fth compiles, every line of it");
|
||||
CHECK(is(say(": SQ DUP * ; SEE SQ\n"), ": SQ\n dup call * \n ; \n ok\nok> "), "a definition: an in-line word, a call by name, the return");
|
||||
CHECK(is(say("SEE DUP\n"), ": DUP\n dup ; \n ok\nok> ") && is(say("SEE EMIT\n"), out) && strncmp(out, ": EMIT\n @p ", 12) == 0 && strstr(out, " b! @b \n if ") && strstr(out, " pop a! ; \n"),
|
||||
CHECK(is(say("SEE DUP\n"), ": DUP\n dup ; \n ok\nok> ") && is(say("SEE EMIT\n"), out) && strncmp(out, ": EMIT\n @p 255 and ", 20) == 0 && strstr(out, " pop a! ; \n"),
|
||||
"the capsule's own words; a literal's value follows its @p");
|
||||
CHECK(is(say("VARIABLE V 7 V ! SEE V\n"), ": V\n data: 7 \n ok\nok> ") && is(say("5 CONSTANT K SEE K\n"), ": K\n data: 5 \n ok\nok> "), "a variable and a constant show what they hold");
|
||||
CHECK(is(say(": IM 1 ; IMMEDIATE SEE IM\n"), ": IM\n @p 1 ; \nIMMEDIATE\n ok\nok> "), "an immediate word says so");
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "v4/manage.h"
|
||||
#include "v4/blocks.h"
|
||||
#include "v4/message.h"
|
||||
#include "v4/post.h"
|
||||
#include "v4/text.h"
|
||||
#include "block_subsystem.h"
|
||||
#include <stdio.h>
|
||||
@@ -154,7 +155,6 @@ static int host_open(void *self, const char *name, unsigned len)
|
||||
(void)self;
|
||||
if (len == 17 && memcmp(name, "v4:nucleus-64.f18", 17) == 0) { hera_reads.is_f18 = 1; hera_reads.cells_at = 0; return 1; }
|
||||
if (len == 14 && memcmp(name, "v4:forth79.4th", 14) == 0) return read_file(&hera_reads, "forth79.4th");
|
||||
if (len == 13 && memcmp(name, "v4:post79.4th", 13) == 0) return read_file(&hera_reads, "post79.4th");
|
||||
return 0;
|
||||
}
|
||||
static int host_cell(void *self, v4_cell *w)
|
||||
@@ -277,6 +277,30 @@ static const char *tell(v4_cell node, const char *text)
|
||||
}
|
||||
static int told(v4_cell node, const char *text) { return tell(node, text)[0] == 0 && ended == 1 && ended_how == V4_TEXT_COMPLETED; }
|
||||
|
||||
/* POST's host, for the node numbered 10 (v4/include/v4/post.h): a line is
|
||||
* told to it from the console, and what it prints is handed to the runner. */
|
||||
static int post_line(void *self, const char *text, unsigned text_len, char *out, unsigned cap, unsigned *len)
|
||||
{
|
||||
static char line[V4_MSG_MAX_CHARS + 1];
|
||||
const char *r;
|
||||
(void)self;
|
||||
*len = 0;
|
||||
if (text_len > V4_MSG_MAX_CHARS) return -1;
|
||||
memcpy(line, text, text_len); line[text_len] = 0;
|
||||
r = tell(10, line);
|
||||
if (ended != 1 || ended_from != 10) return -1;
|
||||
*len = printed_len;
|
||||
memcpy(out, r, printed_len < cap ? printed_len : cap);
|
||||
return (int)ended_how;
|
||||
}
|
||||
static char post_said[2048];
|
||||
static unsigned post_said_len;
|
||||
static void post_say(void *self, const char *text, unsigned len)
|
||||
{
|
||||
(void)self;
|
||||
if (post_said_len + len < sizeof post_said) { memcpy(post_said + post_said_len, text, len); post_said_len += len; post_said[post_said_len] = 0; }
|
||||
}
|
||||
|
||||
/* Every line of a capsule of FORTH blocks, told to a node from the console:
|
||||
* how many lines did not complete. */
|
||||
static unsigned load(v4_cell node, const char *file)
|
||||
@@ -376,7 +400,16 @@ int main(void)
|
||||
}
|
||||
CHECK(strcmp(tell(10, "NODE-ME ."), "0 ") == 0 && refused == 0, "it asks where it is, and is answered");
|
||||
CHECK(load(10, "forth79.4th") == 0, "it takes in FORTH-79");
|
||||
CHECK(load(10, "post79.4th") == 0 && post_passed[10] == 1 && post_other == 0, "and passes POST");
|
||||
{
|
||||
v4_post_host host;
|
||||
v4_post_tally tally;
|
||||
host.self = 0; host.n = node_numbered(10); host.line = post_line; host.say = post_say;
|
||||
CHECK(v4_post_run(&host, v4_post_cases, v4_post_case_count, &tally) && tally.tests == 538 && tally.pass == 538 && tally.fail == 0
|
||||
&& strstr(post_said, "PARITY:V4_POST tests=538 pass=538 fail=0\n") != NULL && strstr(post_said, "POST FAIL") == NULL,
|
||||
"and passes POST, which the kernel runs on her: %u of %u, %u failed; \"%s\"", tally.pass, tally.tests, tally.fail, post_said);
|
||||
}
|
||||
CHECK(strstr(tell(10, "T{"), "UNKNOWN WORD") != NULL, "nothing of POST's harness is on her: \"%s\"", printed);
|
||||
CHECK(strcmp(tell(10, "RS1"), "42 42 \n") == 0, "what POST's cases defined is: \"%s\"", printed);
|
||||
CHECK(load(10, "hera.4th") == 0, "and its own capsule, which makes it Hera");
|
||||
CHECK(told(10, "(SEAL)"), "that is the system it comes back to");
|
||||
|
||||
@@ -387,9 +420,10 @@ int main(void)
|
||||
CHECK(ended == 1 && ended_how == V4_TEXT_COMPLETED && ended_from == 10, "Hera births four nodes and joins them: \"%s\"", r);
|
||||
}
|
||||
CHECK(born_count == 4 && refused == 0, "four nodes were asked for: %u", born_count);
|
||||
CHECK(post_passed[10] == 1 && post_passed[11] == 0 && post_passed[12] == 0 && post_passed[13] == 0 && post_passed[14] == 0 && post_other == 0,
|
||||
"a node she births is not POSTed: POST is the kernel's, once, on Hera: %u; %u %u %u %u, %u failed",
|
||||
CHECK(post_passed[10] == 0 && post_passed[11] == 0 && post_passed[12] == 0 && post_passed[13] == 0 && post_passed[14] == 0 && post_other == 0,
|
||||
"a node she births is not POSTed, and no node prints a POST tally: POST is the kernel's, once, on Hera: %u %u %u %u %u, %u failed",
|
||||
post_passed[10], post_passed[11], post_passed[12], post_passed[13], post_passed[14], post_other);
|
||||
CHECK(strstr(tell(10, "COLD"), "Cold Start") != NULL && strcmp(tell(10, "RS1"), "42 42 \n") == 0, "COLD on Hera comes back to a system that has what POST's cases defined: \"%s\"", printed);
|
||||
CHECK(parity_count == 4 && parity_number[0] == 11 && parity_number[1] == 12 && parity_number[2] == 13 && parity_number[3] == 14, "the parity of each was recorded: %u", parity_count);
|
||||
CHECK(parity_hash[0] == parity_hash[1] && parity_hash[1] == parity_hash[2] && parity_hash[2] == parity_hash[3],
|
||||
"and their dictionary hashes are the same: %016llx %016llx %016llx %016llx",
|
||||
|
||||
+6
-2
@@ -240,10 +240,14 @@ ANSI = re.compile(r"\x1b\[[0-9;]*m")
|
||||
def run_v3(v3, cases):
|
||||
"""Runs the cases in one session. Returns a list, one entry per case:
|
||||
None if v3 did not get through it, else (error, stack, output)."""
|
||||
script = [": T| ; IMMEDIATE", ": TCLR BEGIN DEPTH WHILE DROP REPEAT ;"]
|
||||
# The lines are run as the kernel sends them to the node: without the
|
||||
# "T| " each began with when they were lines of a capsule. A case that
|
||||
# looks at where it is in its line (>IN) depends on it.
|
||||
script = [": TCLR BEGIN DEPTH WHILE DROP REPEAT ;"]
|
||||
for n, case in enumerate(cases):
|
||||
script.append('TCLR DECIMAL FORTH DEFINITIONS ." [[B%d]]"' % n)
|
||||
script.extend(case["lines"])
|
||||
assert all(line.startswith("T| ") for line in case["lines"]), case
|
||||
script.extend(line[3:] for line in case["lines"])
|
||||
script.append('." [[E%d]]" BASE @ DECIMAL .S BASE ! ." [[S%d]]"' % (n, n))
|
||||
script.append("BYE")
|
||||
done = subprocess.run([v3, "--log-none"], input=("\n".join(script) + "\n").encode("latin-1"),
|
||||
|
||||
@@ -112,7 +112,9 @@ STANDARD = {
|
||||
"TYPE.basic_string": dict(text="72 PAD C! 105 PAD 1+ C! PAD 2 TYPE CR", out="Hi\n", why=D1 + "; and " + RS),
|
||||
"TYPE.numbers": dict(text="49 PAD C! 50 PAD 1+ C! PAD 2 TYPE CR", out="12\n", why=D1 + "; and " + RS),
|
||||
|
||||
">IN.initial": dict(out="9 \n", why="FORTH-79: >IN is the offset into the input, 9 here; v3 prints an address"),
|
||||
# 6: the line is ">IN @ . CR" and >IN is read when "@" has been taken with the blank after it. It was 9
|
||||
# while the cases were a capsule, whose lines each began "T| " (until MESH.md step 6b).
|
||||
">IN.initial": dict(out="6 \n", why="FORTH-79: >IN is the offset into the input, 6 here; v3 prints an address"),
|
||||
|
||||
"WORD.empty_input": None, "WORD.space_delim": None, "WORD.newline_delim": None, "WORD.tab_delim": None,
|
||||
"WORD.comma_delim": None, "WORD.skip_leading": None, "WORD.single_char": None, "WORD.long_word": None,
|
||||
|
||||
Reference in New Issue
Block a user