/* 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; }