(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>
141 lines
6.1 KiB
C
141 lines
6.1 KiB
C
/* 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;
|
|
}
|