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:
co-authored by
Claude Opus 5.5
parent
080811dbd2
commit
07911dcf91
@@ -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 )
|
||||
|
||||
@@ -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 + ! ;
|
||||
@@ -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
@@ -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)) {
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user