feat(v4): the ROM's symbol table

(FIND) ( -- rec | 0 ), (DEFINE) ( value kind -- ) and (OPCODE) ( -- op | -1 ),
entries 3-5.  Names are refused as text.c's define refuses them, compared
case by case with text.c; a set variable may be set again.  256 hash heads,
records growing up and names growing down in dictionary space, used only
while the ROM assembles.  text.c gains the data directives #, and ", (the
ROM's language; its mnemonic and reserved-word tables are data); the
nucleus image is byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-15 11:54:15 -04:00
co-authored by Claude Opus 5.5
parent 080811dbd2
commit 07911dcf91
9 changed files with 404 additions and 13 deletions
+3
View File
@@ -10,3 +10,6 @@
jump (KEY) \ 0 ( -- c )
jump (TOKEN) \ 1 ( -- len )
jump (NUMBER) \ 2 ( -- n flag )
jump (FIND) \ 3 ( -- rec | 0 )
jump (DEFINE) \ 4 ( value kind -- )
jump (OPCODE) \ 5 ( -- op | -1 )
+110
View File
@@ -0,0 +1,110 @@
\ sym.f18 -- the ROM's symbol table.
\
\ A name the ROM assembles is given a kind and a value: 1 a word (its
\ address), 2 a constant, 3 a macro, 4 a set variable. The rules are
\ v4/src/text.c's define: a name may not be an opcode, a reserved word, a
\ label (NAME:), a number or start with &; nothing is defined twice, except
\ that a set variable may be set again.
\
\ The table is used only while the ROM assembles, and lives in dictionary
\ space: 256 hash heads at (SYMH); records of four cells -- link, name,
\ kind, value -- growing up from SYMREC; names, as counted strings, growing
\ down from POOLTOP. When the two would meet, "too many names". (RECN) and
\ (POOLN) count the cells used, so an empty table is all zeroes. Names are
\ compared as the token is held, a counted string, a cell at a time.
\ (REC) (CNT) (PTR) (I) (WI) are working cells. Every routine uses A; some B.
\ ( addr -- f ) 1 if the counted string at addr is the token
: (SAME?)
(PTR) a! ! (TOK) (I) a! !
(TOKN) a! @ 4 + 2/ 2/
L: (PTR) a! @ b! @b (I) a! @ a! @ xor if EQ drop drop 0 ;
EQ: drop (PTR) a! @ 1 + ! (I) a! @ 1 + !
-1 + if DONE jump L
DONE: drop 1 ;
\ ( table -- i | -1 ) which counted string of a table the token is; the
\ table ends with a zero cell
: (WALK)
0 (WI) a! !
L: dup a! @ if END
drop dup (SAME?) if NO
drop drop (WI) a! @ ;
NO: drop dup a! @ 255 and 4 + 2/ 2/ +
(WI) a! @ 1 + ! jump L
END: drop drop -1 ;
\ the opcodes' names, in the order of their numbers
: (MNEMONICS)
", ; ", ex ", jump ", call ", unext ", next ", if ", -if
", @p ", @+ ", @b ", @ ", !p ", !+ ", !b ", !
", +* ", 2* ", 2/ ", inv ", + ", and ", xor ", drop
", dup ", pop ", over ", a ", nop ", push ", b! ", a!
#, 0
\ the ROM's reserved words: its notation and its directives
: (RESERVED)
", : ", macro ", endmacro ", FOR ", NEXT ", UNEXT
", constant ", set ", here ", #, ", ", ", store
", start
#, 0
\ ( -- op | -1 ) which opcode the token names
: (OPCODE) &(MNEMONICS) jump (WALK)
\ ( -- h ) the token's hash, 0 .. 255, from its first two cells
: (HASH)
(TOK) a! @+ @ xor
dup 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ +
dup 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ +
255 and ;
\ ( -- rec | 0 ) the newest record whose name is the token
: (FIND)
(HASH) (SYMH) + a! @
L: if NONE
dup (REC) a! ! 1 + a! @ (SAME?) if NO
drop (REC) a! @ ;
NO: drop (REC) a! @ a! @ jump L
NONE: ;
\ ( -- f ) 1 if the token cannot be a name
: (BADNAME?)
(OPCODE) -if BAD
drop &(RESERVED) (WALK) -if BAD
drop (NUMBER) push drop pop if NOTNUM drop 1 ;
NOTNUM: drop 0 (CHAR@) 38 xor if AMP
drop (TOKN) a! @ -1 + if ONE
(CHAR@) 58 xor if COLON drop 0 ;
COLON: drop 1 ;
ONE: drop 0 ;
AMP: drop 1 ;
BAD: drop 1 ;
\ ( value kind -- ) give the token a kind and a value
: (DEFINE)
(BADNAME?) if OKNAME drop drop drop ERR-BAD-NAME jump (FAIL)
OKNAME: drop
(FIND) if NEW
over 4 xor if K4 drop drop drop drop ERR-DEFINED jump (FAIL)
K4: drop 2 + a! @ 4 xor if BOTH drop drop drop ERR-DEFINED jump (FAIL)
BOTH: drop drop (REC) a! @ 3 + a! ! ;
NEW: drop
(TOKN) a! @ 4 + 2/ 2/ (CNT) a! !
POOLTOP (POOLN) a! @ inv 1 + + (CNT) a! @ inv 1 + +
dup SYMREC (RECN) a! @ + 4 + inv 1 + +
-if ROOM drop drop drop drop ERR-NAMES jump (FAIL)
ROOM: drop
dup (PTR) a! ! (TOK) (I) a! ! (CNT) a! @
C: (I) a! @ a! @ (PTR) a! @ b! !b
(PTR) a! @ 1 + ! (I) a! @ 1 + !
-1 + if CD jump C
CD: drop
(POOLN) a! @ (CNT) a! @ + (POOLN) a! !
SYMREC (RECN) a! @ + (REC) a! !
(REC) a! @ 1 + a! !
(REC) a! @ 2 + a! !
(REC) a! @ 3 + a! !
(HASH) (SYMH) + dup a! @ (REC) a! @ a! !
(REC) a! @ over a! ! drop
(RECN) a! @ 4 + ! ;
+7
View File
@@ -49,6 +49,13 @@
* constant NAME VALUE
* NAME is the number VALUE, as v4_text_constant gives it.
*
* #, VALUE one cell of data at the next word address (the word
* being built is ended first). VALUE is a number, a
* constant, CONST+N, or &NAME of a word already defined.
* ", TOKEN TOKEN, whatever its characters, as a counted string: its
* count byte, then its characters, four to a cell, low
* byte first, zero-padded to a whole cell.
*
* header NAME a dictionary entry for NAME (capsule/dict.v4), laid down
* in front of the word that the next `:` starts, and
* linked to the header before it. NAME is taken as it
+85 -12
View File
@@ -65,7 +65,8 @@ static int is_reserved(const char *s)
return strcmp(s, ":") == 0 || strcmp(s, "macro") == 0 || strcmp(s, "endmacro") == 0
|| strcmp(s, "\\") == 0 || strcmp(s, "(") == 0
|| strcmp(s, "FOR") == 0 || strcmp(s, "NEXT") == 0 || strcmp(s, "UNEXT") == 0
|| strcmp(s, "header") == 0 || strcmp(s, "constant") == 0 || flag_of(s) != 0;
|| strcmp(s, "header") == 0 || strcmp(s, "constant") == 0
|| strcmp(s, "#,") == 0 || strcmp(s, "\",") == 0 || flag_of(s) != 0;
}
static int is_label_def(const char *s)
@@ -171,6 +172,23 @@ static void put_cell(v4_text *t, v4_cell value)
as->here++;
}
/* NAME as a counted string at the next word address: its count byte, then
* its characters, four bytes to a cell, low byte first, zero-padded to a
* whole cell. The layout of a header's name, and of `",`. */
static void lay_name(v4_text *t, const char *name)
{
unsigned len = (unsigned)strlen(name), cells = (len + 4u) / 4u, i, b;
for (i = 0; i < cells; i++) {
v4_ucell w = 0;
for (b = 0; b < 4; b++) {
unsigned at = 4u * i + b; /* byte 0 is the count */
unsigned ch = at == 0 ? len : (at <= len ? (unsigned char)name[at - 1u] : 0u);
w |= (v4_ucell)ch << (8u * b);
}
put_cell(t, (v4_cell)w);
}
}
/* `header NAME`: a dictionary entry for NAME, as capsule/dict.v4 lays one
* out, in front of the word the next `:` starts:
* the name: its count byte, then its characters, four bytes to a cell,
@@ -180,7 +198,7 @@ static void put_cell(v4_text *t, v4_cell value)
static void header(v4_text *t, reader *r)
{
char name[V4_TEXT_MAX_TOKEN + 1u];
unsigned len, cells, i;
unsigned len;
v4_cell nfa;
if (r->depth != 1) { fail(t, "not allowed in a macro", "header"); return; }
@@ -190,16 +208,7 @@ static void header(v4_text *t, reader *r)
if (len > 31) { fail(t, "a header's name is at most 31 characters", name); return; }
end_word(t);
nfa = v4_asm_label(&t->as);
cells = (len + 4u) / 4u;
for (i = 0; i < cells; i++) {
v4_ucell w = 0;
for (unsigned b = 0; b < 4; b++) {
unsigned at = 4u * i + b; /* byte 0 is the count */
unsigned ch = at == 0 ? len : (at <= len ? (unsigned char)name[at - 1u] : 0u);
w |= (v4_ucell)ch << (8u * b);
}
put_cell(t, (v4_cell)w);
}
lay_name(t, name);
put_cell(t, 0);
put_cell(t, nfa);
put_cell(t, t->latest);
@@ -357,6 +366,68 @@ static void constant_directive(v4_text *t, reader *r)
define(t, name, KIND_CONST, v);
}
/* A VALUE in a directive: a number, a constant, CONST+N, or &NAME of a word
* already defined. 1 and its value, or 0 having failed. */
static int value_of(v4_text *t, const char *tok, v4_cell *out)
{
const v4_text_symbol *s;
int big;
if (number_of(tok, out, &big)) {
if (big) { fail(t, "number does not fit a cell", tok); return 0; }
return 1;
}
if (tok[0] == '&' && tok[1] != 0) {
s = find(t, tok + 1);
if (s && s->kind == KIND_WORD) { *out = s->value; return 1; }
fail(t, "& needs a word that is already defined", tok + 1);
return 0;
}
s = find(t, tok);
if (s && s->kind == KIND_CONST) { *out = s->value; return 1; }
if (!s) {
const char *plus = strrchr(tok, '+');
if (plus && plus != tok && plus[1] != 0) {
char left[V4_TEXT_MAX_TOKEN + 1u];
const v4_text_symbol *c;
v4_cell n;
size_t len = (size_t)(plus - tok);
memcpy(left, tok, len);
left[len] = 0;
c = find(t, left);
if (c && c->kind == KIND_CONST && number_of(plus + 1, &n, &big) && !big) {
*out = (v4_cell)((v4_ucell)c->value + (v4_ucell)n);
return 1;
}
}
}
fail(t, "not a value", tok);
return 0;
}
/* `#, VALUE`: one cell of data at the next word address. */
static void cell_directive(v4_text *t, reader *r)
{
char tok[V4_TEXT_MAX_TOKEN + 1u];
v4_cell v;
if (r->depth != 1) { fail(t, "not allowed in a macro", "#,"); return; }
if (!next_token(t, r, tok)) { if (!t->failed) fail(t, "#, without a value", NULL); return; }
if (!value_of(t, tok, &v)) return;
(void)v4_asm_label(&t->as);
put_cell(t, v);
check_asm(t, "#,");
}
/* `", TOKEN`: TOKEN, whatever its characters, as a counted string. */
static void string_directive(v4_text *t, reader *r)
{
char tok[V4_TEXT_MAX_TOKEN + 1u];
if (r->depth != 1) { fail(t, "not allowed in a macro", "\","); return; }
if (!raw_token(t, r, tok)) { fail(t, "\", without a string, or one too long", NULL); return; }
(void)v4_asm_label(&t->as);
lay_name(t, tok);
check_asm(t, tok);
}
static void one_token(v4_text *t, reader *r, const char *tok)
{
const v4_text_symbol *s;
@@ -381,6 +452,8 @@ static void one_token(v4_text *t, reader *r, const char *tok)
}
if (strcmp(tok, "macro") == 0) { define_macro(t, r); return; }
if (strcmp(tok, "constant") == 0) { constant_directive(t, r); return; }
if (strcmp(tok, "#,") == 0) { cell_directive(t, r); return; }
if (strcmp(tok, "\",") == 0) { string_directive(t, r); return; }
if (strcmp(tok, "endmacro") == 0) { fail(t, "endmacro without macro", NULL); return; }
if (is_label_def(tok)) {
+23
View File
@@ -133,9 +133,32 @@
#define ROM_NI (OUT_W - 72) /* the next character of the token */
#define ROM_NU (OUT_W - 71) /* the value so far, unsigned */
#define ROM_NBIG (OUT_W - 70) /* 1 once it has not fitted a cell */
#define ROM_RECN (OUT_W - 128) /* (DEFINE): cells of symbol records used */
#define ROM_POOLN (OUT_W - 127) /* cells of the name pool used */
#define ROM_REC (OUT_W - 126) /* (FIND): the record being looked at */
#define ROM_CNT (OUT_W - 125) /* (DEFINE): cells in the token's name */
#define ROM_PTR (OUT_W - 124) /* (SAME?), (DEFINE): where in a name */
#define ROM_I (OUT_W - 123) /* where in the token */
#define ROM_WI (OUT_W - 122) /* (WALK): which entry of a table */
/* THE ROM'S SYMBOL TABLE, used only while it assembles, in dictionary space (cleared at `start`): 256 hash heads,
* then records growing up -- link, name, kind, value -- and names, as counted strings, growing down from the top. */
#define ROM_SYMH DICT_W
#define ROM_SYMREC (DICT_W + 256)
#define ROM_POOLTOP DICT_END_W
#define ROM_REC_LINK 0
#define ROM_REC_NAME 1
#define ROM_REC_KIND 2
#define ROM_REC_VALUE 3
#define ROM_KIND_WORD 1
#define ROM_KIND_CONST 2
#define ROM_KIND_MACRO 3
#define ROM_KIND_SET 4
/* the ROM's error codes */
#define ROM_ERR_TOO_LONG 1 /* a token longer than 63 characters: "name too long" */
#define ROM_ERR_COMMENT 2 /* a ( with no ) before the end: "comment not closed" */
#define ROM_ERR_BAD_NAME 3 /* an opcode, a reserved word, a label, a number or &...: "cannot be used as a name" */
#define ROM_ERR_DEFINED 4 /* "already defined" */
#define ROM_ERR_NAMES 5 /* the symbol table is full: "too many names" */
#define WORD_DEFINED (BUF0_W - 11) /* the xt of the word that is told of each new entry, or 0 */
#define WORD_FORGOTTEN (BUF0_W - 12) /* the xt of the word that is told when entries go, or 0 */
#define LOG_LEVEL (BUF0_W - 5) /* the level at or below which a message is printed */
+1 -1
View File
@@ -29,7 +29,7 @@ extern const unsigned v4_rom_32_words;
/* The entry table, in its fixed order: entry i is at V4_ROM_BASE + i. */
static const char *const rom_entries[] = {
"(KEY)", "(TOKEN)", "(NUMBER)", "(FIND)", "(DEFINE)",
"(KEY)", "(TOKEN)", "(NUMBER)", "(FIND)", "(DEFINE)", "(OPCODE)",
"(OP)", "(LIT)", "(BRANCH)", "(BRANCH>)", "(RESOLVE)", "(LABEL)"
};
#define ROM_ENTRIES (sizeof rom_entries / sizeof rom_entries[0])
+140
View File
@@ -0,0 +1,140 @@
/* test_host_rom_sym.c -- the ROM's symbol table.
*
* Plan Task 4. (DEFINE) ( value kind -- ) gives the token (TOKEN) read a
* value and a kind; (FIND) ( -- rec | 0 ) finds it, newest first; (OPCODE)
* ( -- op | -1 ) says which of the 32 mnemonics it is. A name is refused as
* v4/src/text.c's define refuses one -- an opcode, a reserved word, a label
* (NAME:), a number, &... -- and every such case is also given to text.c,
* the oracle. A set variable (kind 4) may be set again; nothing else may be
* defined twice. The table is in dictionary space: records growing up,
* names growing down, and "too many names" when they meet.
*/
#include "rom_harness.h"
#include "v4/text.h"
static int failures = 0, checks = 0;
#define CHECK(c,...) do{checks++; if(!(c)){failures++; printf("FAIL %s:%d: ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\n");}}while(0)
static v4_node n, oracle_node;
static v4_text tx;
static void tokenize(const char *name)
{
char in[80];
snprintf(in, sizeof in, "%s ", name);
v4_node_console_feed(&n, in, (unsigned)strlen(in));
if (!rom_call(&n, "(TOKEN)", 100000)) printf(" (TOKEN) did not return for %s\n", name);
(void)v4_dstack_pop(&n.ds);
}
/* (DEFINE) the name; the error code it raised (0: none), cleared again */
static v4_cell define(const char *name, v4_cell value, v4_cell kind)
{
v4_cell err;
tokenize(name);
v4_dstack_push(&n.ds, value);
v4_dstack_push(&n.ds, kind);
if (!rom_call(&n, "(DEFINE)", 1000000)) return -99;
err = n.mem[ROM_FAILED];
n.mem[ROM_FAILED] = 0;
if (n.ds.depth != 0) return -98; /* (DEFINE) leaves nothing, refused or not */
return err;
}
static v4_cell find(const char *name)
{
tokenize(name);
if (!rom_call(&n, "(FIND)", 1000000)) return -99;
return v4_dstack_pop(&n.ds);
}
static v4_cell opcode(const char *name)
{
tokenize(name);
if (!rom_call(&n, "(OPCODE)", 1000000)) return -99;
return v4_dstack_pop(&n.ds);
}
/* text.c's verdict on NAME as a name: 0 accepted, ROM_ERR_BAD_NAME refused */
static v4_cell oracle(const char *name)
{
char src[120];
v4_node_reset(&oracle_node);
v4_text_begin(&tx, &oracle_node, 16);
snprintf(src, sizeof src, "constant %s 1", name);
if (v4_text_assemble(&tx, src)) return 0;
return strstr(v4_text_error(&tx), "cannot be used as a name") ? ROM_ERR_BAD_NAME : -1;
}
int main(void)
{
static const char *const mnemonic[32] = {
";", "ex", "jump", "call", "unext", "next", "if", "-if", "@p", "@+", "@b", "@", "!p", "!+", "!b", "!",
"+*", "2*", "2/", "inv", "+", "and", "xor", "drop", "dup", "pop", "over", "a", "nop", "push", "b!", "a!"
};
static const char *const names[] = {
"dup", "a!", ";", "-if", ":", "macro", "endmacro", "FOR", "NEXT", "UNEXT", "constant", "#,", "\",",
"x:", "ab:", "12", "$ff", "-1", "99999999999999999999999", "&y", "&",
"a1", "abc", "(x", "x:y", "DUP", "Dup", "+1", "-", "$", "LONGERNAME", ":x"
};
unsigned i, count;
v4_cell rec;
char nm[16];
rom_boot(&n);
/* the 32 mnemonics, and things that are not one */
for (i = 0; i < 32; i++) CHECK(opcode(mnemonic[i]) == (v4_cell)i, "%s is opcode %u", mnemonic[i], i);
CHECK(opcode("DUP") == -1 && opcode("dupe") == -1 && opcode("du") == -1 && opcode("@pp") == -1, "not mnemonics");
/* define and find */
CHECK(define("A", 5, ROM_KIND_CONST) == 0, "A defined");
CHECK(define("B", 100, ROM_KIND_WORD) == 0, "B defined");
CHECK(define("Cee", 7, ROM_KIND_MACRO) == 0, "Cee defined");
rec = find("A");
CHECK(rec > 0 && n.mem[rec + ROM_REC_KIND] == ROM_KIND_CONST && n.mem[rec + ROM_REC_VALUE] == 5, "A found: rec %ld", (long)rec);
rec = find("B");
CHECK(rec > 0 && n.mem[rec + ROM_REC_KIND] == ROM_KIND_WORD && n.mem[rec + ROM_REC_VALUE] == 100, "B found");
rec = find("Cee");
CHECK(rec > 0 && n.mem[rec + ROM_REC_KIND] == ROM_KIND_MACRO && n.mem[rec + ROM_REC_VALUE] == 7, "Cee found");
CHECK(find("C") == 0 && find("Ce") == 0 && find("Ceee") == 0 && find("a") == 0, "near misses are not found");
/* twice */
CHECK(define("A", 6, ROM_KIND_CONST) == ROM_ERR_DEFINED, "A twice: already defined");
CHECK(define("A", 6, ROM_KIND_SET) == ROM_ERR_DEFINED, "a set variable over a constant: already defined");
CHECK(define("V", 1, ROM_KIND_SET) == 0 && define("V", 2, ROM_KIND_SET) == 0, "a set variable may be set again");
rec = find("V");
CHECK(rec > 0 && n.mem[rec + ROM_REC_VALUE] == 2, "and has its new value");
CHECK(define("V", 3, ROM_KIND_CONST) == ROM_ERR_DEFINED, "but not defined as something else");
/* names refused as text.c refuses them */
for (i = 0; i < sizeof names / sizeof names[0]; i++) {
v4_cell o = oracle(names[i]), r = define(names[i], 1, ROM_KIND_CONST);
CHECK(o == r, "%s: text.c %ld, ROM %ld", names[i], (long)o, (long)r);
}
/* and the ROM's own directives, which text.c does not have */
CHECK(define("set", 1, ROM_KIND_CONST) == ROM_ERR_BAD_NAME && define("here", 1, ROM_KIND_CONST) == ROM_ERR_BAD_NAME
&& define("store", 1, ROM_KIND_CONST) == ROM_ERR_BAD_NAME && define("start", 1, ROM_KIND_CONST) == ROM_ERR_BAD_NAME,
"set here store start are reserved");
/* capacity: fill the table, then find every name with its value */
rom_boot(&n);
for (count = 0; count < 5000; count++) {
v4_cell err;
snprintf(nm, sizeof nm, "S%04u", count);
err = define(nm, (v4_cell)count * 3, ROM_KIND_WORD);
if (err == ROM_ERR_NAMES) break;
if (err != 0) { CHECK(0, "%s: error %ld", nm, (long)err); break; }
}
CHECK(count >= 800, "at least 800 five-character names fit: %u", count);
CHECK(count < 5000, "and the table does fill");
for (i = 0; i < count; i++) {
snprintf(nm, sizeof nm, "S%04u", i);
rec = find(nm);
if (!(rec > 0 && n.mem[rec + ROM_REC_VALUE] == (v4_cell)i * 3)) { CHECK(0, "%s not found with its value", nm); break; }
}
CHECK(i == count, "every one of them is found");
printf("test_host_rom_sym: %d checks, %d failures\n", checks, failures);
return failures != 0;
}
+22
View File
@@ -499,6 +499,28 @@ int main(void)
CHECK(refused("constant K x", "not a number", 1), "a constant's value is a number");
CHECK(refused(": constant ;", "cannot be used as a name", 1), "constant is reserved");
/* ---- `#, VALUE` and `", TOKEN`, data in the text (the ROM's language) ---- */
{
v4_ucell ab = 2u | ((v4_ucell)'a' << 8) | ((v4_ucell)'b' << 16), paren = 1u | ((v4_ucell)'(' << 8);
v4_ucell five_l = 5u | ((v4_ucell)'a' << 8) | ((v4_ucell)'b' << 16) | ((v4_ucell)'c' << 24);
v4_ucell five_h = (v4_ucell)'d' | ((v4_ucell)'e' << 8);
CHECK(ok("constant K 9 : T ; #, 5 #, -1 #, K #, K+2 #, &T \", ab \", ( \", abcde"),
"data assembles: %s", v4_text_error(&tx));
CHECK(nb.mem[ORIGIN + 1] == 5 && nb.mem[ORIGIN + 2] == -1 && nb.mem[ORIGIN + 3] == 9 && nb.mem[ORIGIN + 4] == 11
&& nb.mem[ORIGIN + 5] == ORIGIN, "#, lays a number, a constant, CONST+N and &NAME");
CHECK((v4_ucell)nb.mem[ORIGIN + 6] == ab && (v4_ucell)nb.mem[ORIGIN + 7] == paren,
"\", lays a counted string, padded to a whole cell; ( is a name here");
CHECK((v4_ucell)nb.mem[ORIGIN + 8] == five_l && (v4_ucell)nb.mem[ORIGIN + 9] == five_h && v4_text_here(&tx) == ORIGIN + 10,
"a longer one runs on into the next cell");
CHECK(ok(": T dup #, 7 ;") && nb.mem[ORIGIN + 1] == 7 && v4_text_word(&tx, "T") == ORIGIN,
"#, inside a word ends the instruction word first");
CHECK(refused("#, x", "not a value", 1), "#, needs a value");
CHECK(refused("#,", "#, without a value", 1), "#, at the end");
CHECK(refused("\",", "\", without a string", 1), "\", at the end");
CHECK(refused(": #, ;", "cannot be used as a name", 1) && refused(": \", ;", "cannot be used as a name", 1),
"#, and \", are reserved");
}
CHECK(v4_node_guards_intact(&na) && v4_node_guards_intact(&nb), "guards intact");
printf(" %d checks, %d failures\n", checks, failures);
+13
View File
@@ -36,6 +36,19 @@ int main(void)
constant("(NI)", (long)ROM_NI);
constant("(NU)", (long)ROM_NU);
constant("(NBIG)", (long)ROM_NBIG);
constant("(RECN)", (long)ROM_RECN);
constant("(POOLN)", (long)ROM_POOLN);
constant("(REC)", (long)ROM_REC);
constant("(CNT)", (long)ROM_CNT);
constant("(PTR)", (long)ROM_PTR);
constant("(I)", (long)ROM_I);
constant("(WI)", (long)ROM_WI);
constant("(SYMH)", (long)ROM_SYMH);
constant("SYMREC", (long)ROM_SYMREC);
constant("POOLTOP", (long)ROM_POOLTOP);
constant("ERR-BAD-NAME", (long)ROM_ERR_BAD_NAME);
constant("ERR-DEFINED", (long)ROM_ERR_DEFINED);
constant("ERR-NAMES", (long)ROM_ERR_NAMES);
/* the cell width's constants (NUMBER) needs, written as signed cells */
constant("MSB", (long)(v4_cell)V4_MSB);
constant("DEC-LIMIT", (long)(v4_cell)(~(v4_ucell)0 / 10u));