simpleForth/simpleForth.c

1029 lines
21 KiB
C

#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#define TRACE 0
#define TRACE2 0
#define CELLCOUNT 0x1000
#define CELLL (sizeof (uintptr_t))
#define FALSE 0
#define TRUE -1
#define IMM_MASK 0x80
#define INC(lval) do { lval += sizeof (lval); } while (0)
#define DEC(lval) do { lval -= sizeof (lval); } while (0)
#define AT(addr) (*((Cell *)addr))
#define C_AT(addr) (*((char *)addr))
#define HEADER_AT(addr) ((DictEntry *)( addr ))
#define RPUSH(rval) do { \
AT(FS.RP) = rval; \
DEC(FS.RP); \
} while (0)
#define RPOP(lval) do { \
INC(FS.RP); \
lval = AT(FS.RP); \
} while (0)
#define SPUSH(rval) do { \
AT(FS.SP) = rval; \
DEC(FS.SP); \
} while (0)
#define SPOP(lval) do { \
INC(FS.SP); \
lval = AT(FS.SP); \
} while (0)
#define NEXT return
typedef uintptr_t Cell;
typedef void (*Code)(void);
typedef Code * XT;
#define HDRNAMESZ 3
typedef struct forthDictEntry DictEntry;
struct forthDictEntry {
union {
struct {
unsigned char nameLength;
unsigned char nameBegin[HDRNAMESZ];
};
Cell padding;
};
DictEntry *link;
Code codeAddress;
};
typedef struct forthState {
/* Interpreter Registers */
Cell IP; /* Interpreter Pointer */
Cell WP; /* Word/Work Pointer */
Cell SP; /* Argument Stack Pointer */
Cell RP; /* Return Stack Pointer */
Cell DP; /* Dictionary Pointer */
int running;
} ForthState;
ForthState FS;
Cell FM[CELLCOUNT];
char TIB[128];
DictEntry **VOCAB = (DictEntry **)&FM[0];
Cell *BASE = &FM[1];
Cell *NUM_TIB = &FM[2];
Cell *TO_IN = &FM[3];
Cell *STATE = &FM[4];
void dumpStack(void) {
Cell *BOS = (Cell *)(FM + CELLCOUNT - 1);
Cell *TOS = (Cell *)(FS.SP);
Cell val;
int depth = (BOS-TOS);
int i;
for (i = 0; i < depth; i++) {
val = AT(BOS-i);
fprintf(stderr, "%d: %lu\n", i, val);
}
}
/*
* Terminal IO Primitives
*/
void QRX(void) {
int rv;
int oldflag, newflag;
char buf[1];
struct termios oldt, newt;
tcgetattr(STDIN_FILENO, &oldt);
fcntl(STDIN_FILENO, F_GETFL, &oldflag);
newt = oldt;
newt.c_lflag &= ~( ICANON | ECHO );
newflag = oldflag | O_NONBLOCK;
fcntl(STDIN_FILENO, F_SETFL, newflag);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
rv = read(STDIN_FILENO, buf, 1);
fcntl(STDIN_FILENO, F_SETFL, oldflag);
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
if (rv == -1 || rv == 0) {
SPUSH(FALSE);
} else {
SPUSH(buf[0]);
SPUSH(TRUE);
}
NEXT;
}
char rxchar(void) {
int rv;
char b;
struct termios oldt, newt;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~( ICANON | ECHO );
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
rv = read(STDIN_FILENO, &b, 1);
//fprintf(stderr, "%d\n\n", b);
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
return b;
}
void txchar(char b) {
int rv;
struct termios oldt, newt;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~( ICANON | ECHO );
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
rv = write(STDIN_FILENO, &b, 1);
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
}
// accept ( a l -- l2 ) Read a string of up to l characters, storing it at a.
// Return the length of the string actually read.
#define BACKSPACE_IN 127
#define BACKSPACE_OUT 8
void ACCEPT(void) {
Cell addr, len;
char *inbuf, in;
int incount = 0;
SPOP(len);
SPOP(addr);
inbuf = (char*)addr;
for (;;) {
in = rxchar();
switch (in) {
case BACKSPACE_IN:
if (incount > 0) {
txchar(BACKSPACE_OUT);
txchar(' ');
txchar(BACKSPACE_OUT);
incount--;
}
break;
case '\n':
if (incount > 0) {
goto finished;
}
break;
default:
if (incount < (int)len) {
inbuf[incount] = in;
incount++;
txchar(in);
}
break;
}
}
finished:
txchar('\n');
SPUSH(incount);
NEXT;
}
// word ( c -- cstring ) Consume the next word in TIB as delimited by c,
// returning the address of the word as a counted string.
void WORD(void) {
Cell cell;
char delim, *ch, *last, *lenp, *strp;
SPOP(cell);
delim = cell;
ch = TIB + *TO_IN;
last = TIB + *NUM_TIB;
lenp = (char*)FS.DP;
strp = lenp + 1;
*lenp = 0;
/* skip leading delimiters */
while (*ch == delim) {
ch++;
}
/* copy to pad area until a delimiter is reached */
for (;;) {
if (ch == last) {
break;
}
if (*ch == delim) {
break;
}
*strp = *ch;
strp++; ch++;
*lenp += 1;
}
*strp = ' ';
*TO_IN = ch - TIB;
cell = (Cell)lenp;
SPUSH(cell);
NEXT;
}
// emit ( c -- ) display c on the terminal
void EMIT(void) {
Cell cell;
char c;
SPOP(cell);
c = cell;
txchar(c);
NEXT;
}
void NL(void) {
txchar('\n');
NEXT;
}
void DOT(void) {
Cell a;
char buf[64];
Cell i;
SPOP(a);
sprintf(buf, "%ld", (long)a);
for (i = 0; i < strlen(buf); i++) {
txchar(buf[i]);
}
txchar('\n');
NEXT;
}
/*
* Memory Access Primitives
*/
// @ ( addr -- x ) \ read x from addr
void PEEK(void) {
Cell addr, val;
SPOP(addr);
val = AT(addr);
SPUSH(val);
NEXT;
}
// ! ( x addr -- ) \ store x at addr
void POKE(void) {
Cell addr, val;
SPOP(addr);
SPOP(val);
AT(addr) = val;
NEXT;
}
// c@ ( addr -- c ) \ read character from addr
void CPEEK(void) {
Cell addr;
char c;
SPOP(addr);
c = AT(addr);
SPUSH(c);
NEXT;
}
/*
* Stack Manipulation Primitives
*/
// drop ( x -- ) \ drop x from stack
void DROP(void) {
INC(FS.SP);
NEXT;
}
// dup ( x -- x x ) \ push a copy of x to stack
void DUP(void) {
Cell addr, val;
addr = FS.SP;
INC(addr);
val = AT(addr);
SPUSH(val);
NEXT;
}
// swap ( x y -- y x ) \ swap top two values on stack
void SWAP(void) {
Cell vx, vy;
SPOP(vy);
SPOP(vx);
SPUSH(vy);
SPUSH(vx);
NEXT;
}
// rot ( x y z -- y z x ) \ rotate-left top 3 values on stack
void ROT(void) {
Cell vx, vy, vz;
SPOP(vz);
SPOP(vy);
SPOP(vx);
SPUSH(vy);
SPUSH(vz);
SPUSH(vx);
NEXT;
}
/*
* Flow Control Primitives
*/
// next ( -- ) \ Decrement index on return stack and exit loop if < 0
void DONEXT(void) {
AT(FS.RP) -= 1;
if ((int)AT(FS.RP) < 0) {
RPOP(FS.RP);
INC(FS.IP);
} else {
FS.IP = AT(FS.IP);
}
NEXT;
}
// ?branch ( f -- ) \ Branch if flag is 0
void DOQBRANCH(void) {
Cell flag;
SPOP(flag);
if (flag == FALSE) {
FS.IP = AT(FS.IP);
} else {
INC(FS.IP);
}
NEXT;
}
// branch ( -- ) \ Branch to an inline address.
void DOBRANCH(void) {
FS.IP = AT(FS.IP);
NEXT;
}
/*
* Compiling Primitives
*/
// , ( x -- ) \ Compile x to the current definition
void COMMA(void) {
Cell x;
SPOP(x);
AT(FS.DP) = x;
FS.DP += sizeof x;
NEXT;
}
// c, ( c -- ) \ Compile character c to the current definition
void CCOMMA(void) {
char c;
SPOP(c);
C_AT(FS.DP) = c;
FS.DP += 1;
NEXT;
}
/* DOVAR is the machine code for variable definitions. When it is executed, WP
will be pointing at the codeAddress field of its dictionary entry, so the
next cell will be the variable cell. DOVAR pushes the address of the variable
cell to the data stack and */
void DOVAR(void) {
Cell LP;
LP = FS.WP;
INC(LP);
SPUSH(LP);
NEXT;
}
// create ( -- ) build a header for a new word in the dictionary, taking the
// name as the next word in the input buffer
void CREATE(void) {
DictEntry *newHead = HEADER_AT(FS.DP);
Cell cell;
char *name;
int len;
SPUSH(' ');
WORD();
SPOP(cell);
name = (char *)(cell + 1);
len = *(char *)(cell);
#if 0
fprintf(stderr, "create: nameLength %d, nameBegin '%s'\n",
newHead->nameLength, newHead->nameBegin);
#endif
newHead->link = HEADER_AT(*VOCAB);
newHead->codeAddress = DOVAR;
*VOCAB = newHead;
FS.DP += sizeof (DictEntry);
NEXT;
}
void DOCOL(void);
// : ( -- ) start defining a new word.
void COLON(void) {
*STATE = -1;
CREATE();
(*VOCAB)->codeAddress = DOCOL;
NEXT;
}
void compileCode(XT codeAddress);
void compileLiteral(Cell literal);
static XT DOEXIT_XT;
// ; ( -- ) finish defining a new word.
void SEMICOLON(void) {
compileCode(DOEXIT_XT);
*STATE = 0;
NEXT;
}
void DOCONST(void);
// constant ( x -- ) create a new constant of value x, with name from input buffer
void CONSTANT(void) {
Cell value;
SPOP(value);
CREATE();
(*VOCAB)->codeAddress = DOCONST;
AT(FS.DP) = value;
FS.DP += sizeof value;
NEXT;
}
/*
* Math Primitives
*/
// + ( x y -- z ) \ Push the result of x + y
void PLUS(void) {
Cell a, b;
SPOP(a);
SPOP(b);
a = a + b;
SPUSH(a);
NEXT;
}
// = ( x y -- f ) \ Push true if x = y, false otherwise
void EQUALS(void) {
Cell x, y, f;
SPOP(y);
SPOP(x);
f = (x == y) ? TRUE : FALSE;
SPUSH(f);
NEXT;
}
/*
* String Primitives
*/
// count ( a -- a2 len ) a is addr of counted string; a2 is addr of first char
void COUNT(void) {
char *a, *a2;
Cell len, addr;
SPOP(addr);
a = (char *)addr;
len = a[0];
a2 = a+1;
SPUSH((Cell)a2);
SPUSH(len);
}
/* >number ( n a l -- n2 a2 l2 ) Convert string to number; n is starting number,
a is addr of first char of string, l is number of chars in string. If
successful, n2 is the converted number, l2 is zero, and a2 points just
after the last char in the string. If unsuccessful, l2 is the non-zero
length of the remaining characters in the string, and a2 is the address
of the character that caused conversion to fail. */
void TONUMBER(void) {
Cell num, addr, len, num2;
char *str, *end;
char convbuf[128];
SPOP(len);
SPOP(addr);
SPOP(num);
memcpy(convbuf, (char*)addr, len);
convbuf[len] = '\0';
str = convbuf;
num2 = strtol(str, &end, *BASE);
if (*end == '\0') {
addr += len;
num += num2;
len = 0;
} else {
len = len - (end - str);
addr += len;
}
SPUSH(num);
SPUSH(addr);
SPUSH(len);
NEXT;
}
/*
* Dictionary Lookup Primitive
*/
DictEntry *findWord(int len, char *name, DictEntry *vocab) {
char length;
length = (len > HDRNAMESZ) ? HDRNAMESZ : len;
while (vocab != NULL) {
if ((vocab->nameLength & ~(IMM_MASK)) == len) {
if (strncmp(name, (char*)vocab->nameBegin, length) == 0) {
return vocab;
}
}
vocab = vocab->link;
}
return NULL;
}
// find ( cstring -- a2 flag ) look up a word in the dictionary.
// If found, a2 is XT of the word; flag is 1 for imm, -1 otherwise
// If not found, a2 is cstring and flag is 0
void FIND(void) {
Cell cstr;
int len;
char *name;
DictEntry *hdr;
SPOP(cstr);
len = ((char*)cstr)[0];
name = ((char*)cstr)+1;
hdr = findWord(len, name, *VOCAB);
if (hdr == NULL) {
SPUSH(cstr);
SPUSH(0);
} else if (hdr->nameLength & IMM_MASK) {
SPUSH((Cell)&hdr->codeAddress);
SPUSH(1);
} else {
SPUSH((Cell)&hdr->codeAddress);
SPUSH((Cell)-1);
}
NEXT;
}
/*
* Inner Interpreter Primitives
*/
/* DOCOL pushes IP onto the R stack, sets IP to the next cell after WP, and
jumps to NEXT */
void DOCOL(void) {
RPUSH(FS.IP);
FS.IP = FS.WP;
INC(FS.IP);
NEXT;
}
/* EXIT terminates a colon word, popping IP from R stack. */
void EXIT(void) {
RPOP(FS.IP);
NEXT;
}
/* DOLIT fetches the value pointed to by IP, pushes it to the data stack, and
increments IP; it is used for in-line literals in a secondary definition */
void DOLIT(void) {
Cell L;
L = AT(FS.IP);
SPUSH(L);
INC(FS.IP);
NEXT;
}
static Code DOLIT_codeAddress = DOLIT;
/* DOCONST is like DOVAR, but it pushes the value of the cell after the codeAddress rather than the address of the cell */
void DOCONST(void) {
Cell LP, L;
LP = FS.WP;
INC(LP);
L = AT(LP);
SPUSH(L);
NEXT;
}
/* BYE instructs the inner interpreter to stop */
void BYE(void) {
FS.running = 0;
NEXT;
}
/* NEXT extracts the word address of the next instruction pointed to by IP,
stores it in WP, and increments IP to the next cell. It then retrieves the
address stored at the location indicated by WP and jumps to that address. */
void inner_interpreter(void) {
Code XP;
#if TRACE2
DictEntry *hdr;
char buf[HDRNAMESZ+1];
int sz;
#endif
while (FS.running) {
/* The body of this loop constitutes NEXT */
FS.WP = AT(FS.IP);
#if TRACE2
if (FS.WP == (Cell)&DOLIT_codeAddress) {
memcpy(buf, "LIT", 4);
} else {
hdr = HEADER_AT(FS.WP-(2* sizeof (Cell)));
sz = hdr->nameLength; sz = (sz > HDRNAMESZ) ? HDRNAMESZ : sz;
memcpy(buf, hdr->nameBegin, sz); buf[sz] = '\0';
}
fprintf(stderr, "-----------------\n");
fprintf(stderr, "\nExecuting word %s\n", buf);
fprintf(stderr, "Stack before:\n");
dumpStack();
#endif
INC(FS.IP);
XP = (Code)AT(FS.WP);
XP();
#if TRACE2
fprintf(stderr, "Stack after:\n");
dumpStack();
fprintf(stderr, "-----------------\n");
#endif
}
}
/* EXECUTE pops a code address from the data stack into the IP, and jumps to
NEXT */
void EXECUTE(void) {
Code XP;
#if TRACE
DictEntry *hdr;
char buf[HDRNAMESZ+1];
int sz;
#endif
SPOP(FS.WP);
#if TRACE
if (FS.WP == (Cell)&DOLIT_codeAddress) {
memcpy(buf, "LIT", 4);
} else {
hdr = HEADER_AT(FS.WP-(2* sizeof (Cell)));
sz = hdr->nameLength; sz = (sz > HDRNAMESZ) ? HDRNAMESZ : sz;
memcpy(buf, hdr->nameBegin, sz); buf[sz] = '\0';
}
fprintf(stderr, "-----------------\n");
fprintf(stderr, "EXECUTE: Executing word %s\n", buf);
fprintf(stderr, "Stack before:\n");
dumpStack();
#endif
XP = (Code)AT(FS.WP);
XP();
#if TRACE
fprintf(stderr, "Stack after:\n");
dumpStack();
fprintf(stderr, "-----------------\n");
#endif
NEXT;
}
void compileHeader(const char *name, Code code) {
DictEntry *newHead = HEADER_AT(FS.DP);
int i, len;
len = strlen(name);
newHead->nameLength = len;
for (i = 0; i < len && i < HDRNAMESZ; i++) {
newHead->nameBegin[i] = name[i];
}
newHead->link = HEADER_AT(*VOCAB);
newHead->codeAddress = code;
*VOCAB = newHead;
FS.DP += sizeof (DictEntry);
}
void compileCode(XT codeAddress) {
AT(FS.DP) = (Cell)codeAddress;
FS.DP += sizeof codeAddress;
}
void compileLiteral(Cell literal) {
AT(FS.DP) = (Cell)&DOLIT_codeAddress;
FS.DP += sizeof literal;
AT(FS.DP) = literal;
FS.DP += sizeof literal;
}
XT lookupWord(const char *name, DictEntry *vocab) {
int len;
DictEntry *hdr;
len = strlen(name);
hdr = findWord(len, (char *)name, vocab);
if (hdr != NULL) {
return &hdr->codeAddress;
} else {
return NULL;
}
}
void addPrimitive(const char *name, Code code) {
compileHeader(name, code);
}
void addImmPrimitive(const char *name, Code code) {
compileHeader(name, code);
(*VOCAB)->nameLength |= IMM_MASK;
}
void addSecondary(const char *name, const char **words) {
XT code;
Cell lit;
compileHeader(name, DOCOL);
while (*words != NULL) {
code = lookupWord(*words, *VOCAB);
if (code != NULL) {
compileCode(code);
} else {
char *end;
lit = strtol(*words, &end, 0);
if (*end == '\0') {
compileLiteral(lit);
} else {
printf("Error adding secondary '%s': '%s' is not in the dictionary"
" and is not a number\n", name, *words);
exit(1);
}
}
words++;
}
compileCode(lookupWord("EXIT", *VOCAB));
}
void addVariable(const char *name, Cell initVal) {
compileHeader(name, DOVAR);
AT(FS.DP) = initVal;
FS.DP += sizeof initVal;
}
void addConstant(const char *name, Cell value) {
compileHeader(name, DOCONST);
AT(FS.DP) = value;
FS.DP += sizeof value;
}
void ABORT(void) {
*NUM_TIB = 0;
*TO_IN = 0;
*STATE = 0;
FS.SP = (Cell)(FM + CELLCOUNT - 1);
FS.RP = (Cell)(FM + CELLCOUNT - 65);
/* Load IP with the first parameter cell of a secondary. */
FS.IP = (Cell)((Cell *)lookupWord("COLD", *VOCAB) + 1);
NEXT;
}
static const char *TEST[] = {
"40",
"2",
"+",
".",
"NL"
};
static const char *TESTWORD[] = {
"TIB", "128", "ACCEPT",
"#TIB", "!",
"0", ">IN", "!",
"0",
"32", "WORD",
"COUNT",
">NUMBER", "DROP", "DROP", "NL", ".", "NL",
0
};
static const char *TESTCREATE[] = {
"TIB", "128", "ACCEPT",
"#TIB", "!",
"0", ">IN", "!",
"CREATE",
0
};
static const char *COLD[] = {
"INTERPRET",
0
};
Cell cW(const char *name) {
XT code;
Cell codeAddr = 0;
code = lookupWord(name, *VOCAB);
if (code != NULL) {
codeAddr = FS.DP;
compileCode(code);
} else {
fprintf(stderr, "Error: %s not in dictionary\n", name);
exit(1);
}
return codeAddr;
}
Cell *cB(const char *name) {
Cell labelAddr;
cW(name);
labelAddr = FS.DP;
// would code label in here if we knew it
FS.DP += sizeof (Cell);
return (Cell *)labelAddr;
}
Cell cL(Cell literal) {
Cell codeAddr = FS.DP;
compileLiteral(literal);
return codeAddr;
}
void buildOuterInterpreter(void) {
Cell interpt, intpar, intexc, intnf, intnc, intskip, intdone;
Cell *b_interpt, *b_intpar, *b_intexc, *b_intnf, *b_intnc,
*b_intskip, *b_intdone, *b_intdone2, *b_intdone3;
compileHeader("INTERPRET", DOCOL);
interpt = cW("#TIB");
cW("@");
cW(">IN");
cW("@");
cW("=");
b_intpar = cB("?BRANCH");
cW("TIB");
cL(50);
cW("ACCEPT");
cW("#TIB");
cW("!");
cL(0);
cW(">IN");
cW("!");
intpar = cL(32);
cW("WORD");
cW("FIND");
cW("DUP");
b_intnf = cB("?BRANCH");
cW("STATE");
cW("@");
cW("=");
b_intexc = cB("?BRANCH");
cW(",");
b_intdone = cB("BRANCH");
intexc = cW("EXECUTE");
b_intdone2 = cB("BRANCH");
intnf = cW("SWAP");
cW("COUNT");
cW(">NUMBER");
b_intskip = cB("?BRANCH");
cW("STATE");
cW("@");
b_intnc = cB("?BRANCH");
cW("VOCAB");
cW("@");
cW("DUP");
cL(sizeof (Cell));
cW("+");
cW("@");
cW("VOCAB");
cW("!");
cL((Cell)&(FS.DP));
cW("!");
intnc = cW("ABORT");
intskip = cW("DROP");
cW("STATE");
cW("@");
b_intdone3 = cB("?BRANCH");
cL((Cell)&DOLIT_codeAddress);
cW(",");
cW(","); intdone = FS.DP;
b_interpt = cB("BRANCH");
cW("EXIT");
/* Fix up branch targets */
*b_interpt = interpt;
*b_intpar = intpar;
*b_intexc = intexc;
*b_intnf = intnf;
*b_intnc = intnc;
*b_intskip = intskip;
*b_intdone = intdone;
*b_intdone2 = intdone;
*b_intdone3 = intdone;
}
void initialize(void) {
FS.SP = (Cell)(FM + CELLCOUNT - 1);
FS.RP = (Cell)(FM + CELLCOUNT - 65);
FS.DP = (Cell)(FM + 128);
*VOCAB = NULL;
/* Inner Interpreter Primitives */
addPrimitive("EXECUTE", EXECUTE);
addPrimitive("BYE", BYE);
addPrimitive("EXIT", EXIT);
DOEXIT_XT = &(*VOCAB)->codeAddress;
/* Core address constants */
addConstant("VOCAB", (Cell)VOCAB);
addConstant("TIB", (Cell)TIB);
addConstant("BASE", (Cell)BASE);
addConstant("TIB", (Cell)TIB);
addConstant("#TIB", (Cell)NUM_TIB);
addConstant(">IN", (Cell)TO_IN);
addConstant("STATE", (Cell)STATE);
/* Terminal I/O Primitives */
addPrimitive("ACCEPT", ACCEPT);
addPrimitive("WORD", WORD);
addPrimitive("EMIT", EMIT);
addPrimitive("NL", NL);
addPrimitive(".", DOT);
/* Memory Access Primitives */
addPrimitive("@", PEEK);
addPrimitive("!", POKE);
addPrimitive("c@", CPEEK);
/* Stack Manipulation Primitives */
addPrimitive("DROP", DROP);
addPrimitive("DUP", DUP);
addPrimitive("SWAP", SWAP);
addPrimitive("ROT", ROT);
/* Flow Control Primitives */
addPrimitive("DONEXT", DONEXT);
addPrimitive("?BRANCH", DOQBRANCH);
addPrimitive("BRANCH", DOBRANCH);
/* Compiling Primitives */
addPrimitive(",", COMMA);
addPrimitive("c,", CCOMMA);
addPrimitive("CREATE", CREATE);
addPrimitive(":", COLON);
addImmPrimitive(";", SEMICOLON);
addPrimitive("CONSTANT", CONSTANT);
/* Math Primitives */
addPrimitive("+", PLUS);
addPrimitive("=", EQUALS);
/* String Primitives */
addPrimitive("COUNT", COUNT);
addPrimitive(">NUMBER", TONUMBER);
/* Dictionary Search Primitives */
addPrimitive("FIND", FIND);
/* Outer Interpreter */
addPrimitive("ABORT", ABORT);
buildOuterInterpreter();
/* Extra stuff! */
addSecondary("TEST", TEST);
addSecondary("TESTCREATE", TESTCREATE);
addSecondary("TESTWORD", TESTWORD);
addSecondary("COLD", COLD);
/* Load IP with the first parameter cell of a secondary. */
FS.IP = (Cell)((Cell *)lookupWord("COLD", *VOCAB) + 1);
/* Set the running state to a true value */
FS.running = 1;
}
int main(void) {
initialize();
inner_interpreter();
return 0;
}