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
+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)) {