/* 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. * CONST+N the constant plus the number N: one literal. (Only when * the whole token is not itself a name.) * &NAME the address of the word NAME, which must already be * defined: 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. * * 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 * stands -- it may be ( or \ or + -- and is at most 31 * characters. Between it and the `:` may come the flags * immediate, data, inline and compile-only. The word after * `:` is the assembler's name for the code, as always, and * need not be the same: header + inline : PLUS + ; * * 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; v4_cell latest; /* xt of the newest header, 0 if none */ int header_pending; /* a header waits for its `:` */ unsigned line; int failed; char message[224]; } 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); /* The same for the text in the file at `path`. Its errors name the file. */ int v4_text_assemble_file(v4_text *t, const char *path); /* 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 xt of the newest `header`, 0 if there has been none: what the * dictionary's LATEST starts as. */ v4_cell v4_text_latest(const v4_text *t); /* 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 */