feat(v4.0.0): assemble definitions written as text
Test support, beside the slot packer: reads definitions in the notation DECOMPOSITION.md uses -- words, opcodes, literals, constants, labels, branches, FOR NEXT and FOR UNEXT, in-line macros, comments -- and lays them down through the slot packer, so they no longer have to be retyped opcode by opcode in C. Tested by assembling SWAP, UM/MOD, C@ and C! both ways on two nodes and comparing memory word for word, by running what is assembled, and on every error it reports, each with its line number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
861f800b7f
commit
7b710aa322
@@ -0,0 +1,131 @@
|
||||
/* text.h -- assemble definitions written as text onto a node.
|
||||
*
|
||||
* Until now every DECOMPOSITION.md definition was retyped by hand in the
|
||||
* tests as calls to the slot packer (asm.h), one opcode at a time. This
|
||||
* reads the definitions as they are written in the document instead:
|
||||
*
|
||||
* : EMIT ( c -- ) CONSOLE-TX b! !b ;
|
||||
* : SPACE 32 jump EMIT
|
||||
*
|
||||
* and lays them down through the same slot packer, so the words it produces
|
||||
* are the ones the hand-written calls would have produced. It is test
|
||||
* support, like asm.h: it is not the compiler capsule (JUSTIFICATION.md
|
||||
* section 9), which is itself v4 code and will be assembled by this.
|
||||
*
|
||||
* THE NOTATION. Tokens are separated by white space. Names are case
|
||||
* sensitive; the 32 opcode mnemonics are lower case.
|
||||
*
|
||||
* \ ... comment to the end of the line
|
||||
* ( ... ) comment to the closing parenthesis
|
||||
* : NAME start the word NAME at the next instruction word. A
|
||||
* word ends where the next one starts; `;` is the return
|
||||
* opcode and a word may hold several, or none (it then
|
||||
* runs on into the next word).
|
||||
* NAME call the word NAME. It may be defined later in the
|
||||
* text, or in a later call to v4_text_assemble.
|
||||
* 123 -5 $FF a literal: `@p` and its value. Decimal, or hex after $.
|
||||
* It must fit a cell, as a signed or an unsigned number.
|
||||
* CONST a constant given by v4_text_constant: a literal.
|
||||
* ; + dup a! ... an opcode. `@p` cannot be written; write the number.
|
||||
* jump X call X if X -if X next X
|
||||
* a branch to X: a label of this word if it has one of
|
||||
* that name, before or after the branch; otherwise a word.
|
||||
* LABEL: a label, at the next instruction word. Labels belong
|
||||
* to the word they are in and may be used before they
|
||||
* are defined.
|
||||
* FOR ... NEXT `push`, then the body, then `next` back to the body.
|
||||
* FOR ... UNEXT the same with `unext`: the body and the `unext` must
|
||||
* fit one instruction word and hold no literal.
|
||||
* macro NAME ... endmacro
|
||||
* NAME stands for the tokens between, wherever it is
|
||||
* written afterwards (the document's "in line"). A macro
|
||||
* may use other macros; it may not define a word, a label
|
||||
* or a macro.
|
||||
*
|
||||
* A name is looked up in this order: opcode, macro, number, constant, label
|
||||
* (for a branch target), word. So an opcode's name cannot be redefined, and
|
||||
* the FORTH words that share one (`@`, `!`, `+`, ...) are written as their
|
||||
* opcode sequences.
|
||||
*
|
||||
* BRANCH PLACEMENT. A branch's address field is what is left of its word, so
|
||||
* it reaches less the later its slot. The slot packer refuses a branch that
|
||||
* cannot reach its target; this never asks it to: a branch is put in a slot
|
||||
* only if that slot reaches the whole node (at 1024 words, every legal slot),
|
||||
* and otherwise starts a new word.
|
||||
*
|
||||
* ERRORS. The first error stops the assembly and is kept: v4_text_error
|
||||
* gives a message with the line number, and every later call does nothing.
|
||||
*/
|
||||
#ifndef V4_TEXT_H
|
||||
#define V4_TEXT_H
|
||||
|
||||
#include "v4/asm.h"
|
||||
|
||||
#define V4_TEXT_MAX_SYMBOLS 2048u /* words, constants and macros */
|
||||
#define V4_TEXT_MAX_LABELS 64u /* in one word */
|
||||
#define V4_TEXT_MAX_REFS 1024u /* branches whose target is not known yet */
|
||||
#define V4_TEXT_POOL 65536u /* characters of names and macro text */
|
||||
#define V4_TEXT_MAX_TOKEN 63u
|
||||
#define V4_TEXT_MAX_DEPTH 8u /* macros inside macros; FOR inside FOR */
|
||||
|
||||
typedef struct {
|
||||
unsigned name; /* offset into pool */
|
||||
unsigned char kind;
|
||||
v4_cell value; /* address, constant, or macro text offset */
|
||||
} v4_text_symbol;
|
||||
|
||||
typedef struct {
|
||||
unsigned name;
|
||||
v4_asm_ref ref;
|
||||
unsigned line;
|
||||
int local; /* may still turn out to be a label of this word */
|
||||
} v4_text_ref;
|
||||
|
||||
typedef struct {
|
||||
v4_asm as;
|
||||
|
||||
v4_text_symbol sym[V4_TEXT_MAX_SYMBOLS];
|
||||
unsigned nsym;
|
||||
|
||||
struct { unsigned name; v4_cell addr; } label[V4_TEXT_MAX_LABELS];
|
||||
unsigned nlabel;
|
||||
|
||||
v4_text_ref ref[V4_TEXT_MAX_REFS];
|
||||
unsigned nref;
|
||||
|
||||
v4_cell for_addr[V4_TEXT_MAX_DEPTH];
|
||||
unsigned nfor;
|
||||
|
||||
char pool[V4_TEXT_POOL];
|
||||
unsigned pool_used;
|
||||
|
||||
unsigned line;
|
||||
int failed;
|
||||
char message[160];
|
||||
} v4_text;
|
||||
|
||||
/* Start assembling at word address `origin` of node `n`. */
|
||||
void v4_text_begin(v4_text *t, v4_node *n, v4_cell origin);
|
||||
|
||||
/* Give NAME a value; written in the text it is a literal. */
|
||||
void v4_text_constant(v4_text *t, const char *name, v4_cell value);
|
||||
|
||||
/* Assemble `source`, a NUL-terminated string. May be called several times;
|
||||
* words, constants and macros carry over, labels do not. Returns 1 if there
|
||||
* has been no error. */
|
||||
int v4_text_assemble(v4_text *t, const char *source);
|
||||
|
||||
/* Finish: every branch must have found its target. Returns 1 if there has
|
||||
* been no error. Assembling may continue afterwards. */
|
||||
int v4_text_finish(v4_text *t);
|
||||
|
||||
/* The address of the word NAME, or -1 if there is none. */
|
||||
v4_cell v4_text_word(const v4_text *t, const char *name);
|
||||
|
||||
/* The next free word address. */
|
||||
v4_cell v4_text_here(v4_text *t);
|
||||
|
||||
/* "" if there has been no error, else what and where. */
|
||||
const char *v4_text_error(const v4_text *t);
|
||||
|
||||
#endif /* V4_TEXT_H */
|
||||
Reference in New Issue
Block a user