#include <stdbool.h>
#include "ficl.h"
extern void exit(int);
static void ficlPrimitiveStepIn(ficlVm *vm);
static void ficlPrimitiveStepOver(ficlVm *vm);
static void ficlPrimitiveStepBreak(ficlVm *vm);
void
ficlCallbackAssert(ficlCallback *callback, int expression,
char *expressionString, char *filename, int line)
{
#if FICL_ROBUST >= 1
if (!expression) {
static char buffer[256];
(void) sprintf(buffer, "ASSERTION FAILED at %s:%d: \"%s\"\n",
filename, line, expressionString);
ficlCallbackTextOut(callback, buffer);
exit(-1);
}
#else
FICL_IGNORE(callback);
FICL_IGNORE(expression);
FICL_IGNORE(expressionString);
FICL_IGNORE(filename);
FICL_IGNORE(line);
#endif
}
static void
ficlVmSetBreak(ficlVm *vm, ficlBreakpoint *pBP)
{
ficlWord *pStep = ficlSystemLookup(vm->callback.system, "step-break");
FICL_VM_ASSERT(vm, pStep);
pBP->address = vm->ip;
pBP->oldXT = *vm->ip;
*vm->ip = pStep;
}
static void
ficlDebugPrompt(bool debug)
{
if (debug)
(void) setenv("prompt", "dbg> ", 1);
else
(void) setenv("prompt", "${interpret}", 1);
}
#if 0
static int
isPrimitive(ficlWord *word)
{
ficlWordKind wk = ficlWordClassify(word);
return ((wk != COLON) && (wk != DOES));
}
#endif
#if FICL_WANT_FLOAT
void
ficlPrimitiveHashSummary(ficlVm *vm)
{
ficlDictionary *dictionary = ficlVmGetDictionary(vm);
ficlHash *pFHash;
ficlWord **hash;
unsigned size;
ficlWord *word;
unsigned i;
int nMax = 0;
int nWords = 0;
int nFilled;
double avg = 0.0;
double best;
int nAvg, nRem, nDepth;
FICL_VM_DICTIONARY_CHECK(vm, dictionary, 0);
pFHash = dictionary->wordlists[dictionary->wordlistCount - 1];
hash = pFHash->table;
size = pFHash->size;
nFilled = size;
for (i = 0; i < size; i++) {
int n = 0;
word = hash[i];
while (word) {
++n;
++nWords;
word = word->link;
}
avg += (double)(n * (n+1)) / 2.0;
if (n > nMax)
nMax = n;
if (n == 0)
--nFilled;
}
avg = avg / nWords;
nAvg = nWords / size;
nRem = nWords % size;
nDepth = size * (nAvg * (nAvg+1))/2 + (nAvg+1)*nRem;
best = (double)nDepth/nWords;
(void) sprintf(vm->pad, "%d bins, %2.0f%% filled, Depth: "
"Max=%d, Avg=%2.1f, Best=%2.1f, Score: %2.0f%%\n",
size, (double)nFilled * 100.0 / size, nMax,
avg, best, 100.0 * best / avg);
ficlVmTextOut(vm, vm->pad);
}
#endif
static void
ficlPrimitiveSeeXT(ficlVm *vm)
{
ficlWord *word;
ficlWordKind kind;
word = (ficlWord *)ficlStackPopPointer(vm->dataStack);
kind = ficlWordClassify(word);
switch (kind) {
case FICL_WORDKIND_COLON:
(void) sprintf(vm->pad, ": %.*s\n", word->length, word->name);
ficlVmTextOut(vm, vm->pad);
ficlDictionarySee(ficlVmGetDictionary(vm), word,
&(vm->callback));
break;
case FICL_WORDKIND_DOES:
ficlVmTextOut(vm, "does>\n");
ficlDictionarySee(ficlVmGetDictionary(vm),
(ficlWord *)word->param->p, &(vm->callback));
break;
case FICL_WORDKIND_CREATE:
ficlVmTextOut(vm, "create\n");
break;
case FICL_WORDKIND_VARIABLE:
(void) sprintf(vm->pad, "variable = %ld (%#lx)\n",
(long)word->param->i, (long unsigned)word->param->u);
ficlVmTextOut(vm, vm->pad);
break;
#if FICL_WANT_USER
case FICL_WORDKIND_USER:
(void) sprintf(vm->pad, "user variable %ld (%#lx)\n",
(long)word->param->i, (long unsigned)word->param->u);
ficlVmTextOut(vm, vm->pad);
break;
#endif
case FICL_WORDKIND_CONSTANT:
(void) sprintf(vm->pad, "constant = %ld (%#lx)\n",
(long)word->param->i, (long unsigned)word->param->u);
ficlVmTextOut(vm, vm->pad);
break;
case FICL_WORDKIND_2CONSTANT:
(void) sprintf(vm->pad, "constant = %ld %ld (%#lx %#lx)\n",
(long)word->param[1].i, (long)word->param->i,
(long unsigned)word->param[1].u,
(long unsigned)word->param->u);
ficlVmTextOut(vm, vm->pad);
break;
default:
(void) sprintf(vm->pad, "%.*s is a primitive\n", word->length,
word->name);
ficlVmTextOut(vm, vm->pad);
break;
}
if (word->flags & FICL_WORD_IMMEDIATE) {
ficlVmTextOut(vm, "immediate\n");
}
if (word->flags & FICL_WORD_COMPILE_ONLY) {
ficlVmTextOut(vm, "compile-only\n");
}
}
static void
ficlPrimitiveSee(ficlVm *vm)
{
ficlPrimitiveTick(vm);
ficlPrimitiveSeeXT(vm);
}
static void
ficlPrimitiveDebugXT(ficlVm *vm)
{
ficlWord *xt = ficlStackPopPointer(vm->dataStack);
ficlWordKind wk = ficlWordClassify(xt);
ficlStackPushPointer(vm->dataStack, xt);
ficlPrimitiveSeeXT(vm);
switch (wk) {
case FICL_WORDKIND_COLON:
case FICL_WORDKIND_DOES:
ficlVmExecuteWord(vm, xt);
ficlVmSetBreak(vm, &(vm->callback.system->breakpoint));
break;
default:
ficlVmExecuteWord(vm, xt);
break;
}
}
static void
ficlPrimitiveStepIn(ficlVm *vm)
{
ficlVmExecuteWord(vm, *vm->ip++);
ficlVmSetBreak(vm, &(vm->callback.system->breakpoint));
}
static void
ficlPrimitiveStepOver(ficlVm *vm)
{
ficlWord *word;
ficlWordKind kind;
ficlWord *pStep = ficlSystemLookup(vm->callback.system, "step-break");
FICL_VM_ASSERT(vm, pStep);
word = *vm->ip;
kind = ficlWordClassify(word);
switch (kind) {
case FICL_WORDKIND_COLON:
case FICL_WORDKIND_DOES:
vm->callback.system->breakpoint.address = vm->ip + 1;
vm->callback.system->breakpoint.oldXT = vm->ip[1];
vm->ip[1] = pStep;
break;
default:
ficlPrimitiveStepIn(vm);
break;
}
}
extern char *ficlDictionaryInstructionNames[];
static void
ficlPrimitiveStepBreak(ficlVm *vm)
{
ficlString command;
ficlWord *word;
ficlWord *pOnStep;
bool debug = true;
if (!vm->restart) {
FICL_VM_ASSERT(vm, vm->callback.system->breakpoint.address);
FICL_VM_ASSERT(vm, vm->callback.system->breakpoint.oldXT);
vm->ip = (ficlIp)(vm->callback.system->breakpoint.address);
*vm->ip = vm->callback.system->breakpoint.oldXT;
pOnStep = ficlSystemLookup(vm->callback.system, "on-step");
if (pOnStep)
(void) ficlVmExecuteXT(vm, pOnStep);
word = vm->callback.system->breakpoint.oldXT;
if ((((ficlInstruction)word) > ficlInstructionInvalid) &&
(((ficlInstruction)word) < ficlInstructionLast))
(void) sprintf(vm->pad, "next: %s (instruction %ld)\n",
ficlDictionaryInstructionNames[(long)word],
(long)word);
else {
(void) sprintf(vm->pad, "next: %s\n", word->name);
if (strcmp(word->name, "interpret") == 0)
debug = false;
}
ficlVmTextOut(vm, vm->pad);
ficlDebugPrompt(debug);
} else {
vm->restart = 0;
}
command = ficlVmGetWord(vm);
switch (command.text[0]) {
case 'i':
ficlPrimitiveStepIn(vm);
break;
case 'o':
ficlPrimitiveStepOver(vm);
break;
case 'g':
break;
case 'l': {
ficlWord *xt;
xt = ficlDictionaryFindEnclosingWord(
ficlVmGetDictionary(vm), (ficlCell *)(vm->ip));
if (xt) {
ficlStackPushPointer(vm->dataStack, xt);
ficlPrimitiveSeeXT(vm);
} else {
ficlVmTextOut(vm, "sorry - can't do that\n");
}
ficlVmThrow(vm, FICL_VM_STATUS_RESTART);
break;
}
case 'q':
ficlDebugPrompt(false);
ficlVmThrow(vm, FICL_VM_STATUS_ABORT);
break;
case 'x': {
int returnValue;
ficlString s;
ficlWord *oldRunningWord = vm->runningWord;
FICL_STRING_SET_POINTER(s,
vm->tib.text + vm->tib.index);
FICL_STRING_SET_LENGTH(s,
vm->tib.end - FICL_STRING_GET_POINTER(s));
returnValue = ficlVmExecuteString(vm, s);
if (returnValue == FICL_VM_STATUS_OUT_OF_TEXT) {
returnValue = FICL_VM_STATUS_RESTART;
vm->runningWord = oldRunningWord;
ficlVmTextOut(vm, "\n");
}
if (returnValue == FICL_VM_STATUS_ERROR_EXIT)
ficlDebugPrompt(false);
ficlVmThrow(vm, returnValue);
break;
}
default:
ficlVmTextOut(vm,
"i -- step In\n"
"o -- step Over\n"
"g -- Go (execute to completion)\n"
"l -- List source code\n"
"q -- Quit (stop debugging and abort)\n"
"x -- eXecute the rest of the line "
"as Ficl words\n");
ficlDebugPrompt(true);
ficlVmThrow(vm, FICL_VM_STATUS_RESTART);
break;
}
ficlDebugPrompt(false);
}
static void
ficlPrimitiveBye(ficlVm *vm)
{
ficlVmThrow(vm, FICL_VM_STATUS_USER_EXIT);
}
struct stackContext
{
ficlVm *vm;
ficlDictionary *dictionary;
int count;
};
static ficlInteger
ficlStackDisplayCallback(void *c, ficlCell *cell)
{
struct stackContext *context = (struct stackContext *)c;
char buffer[80];
#ifdef _LP64
(void) snprintf(buffer, sizeof (buffer),
"[0x%016lx %3d]: %20ld (0x%016lx)\n",
(unsigned long)cell, context->count++, (long)cell->i,
(unsigned long)cell->u);
#else
(void) snprintf(buffer, sizeof (buffer),
"[0x%08x %3d]: %12d (0x%08x)\n",
(unsigned)cell, context->count++, cell->i, cell->u);
#endif
ficlVmTextOut(context->vm, buffer);
return (FICL_TRUE);
}
void
ficlStackDisplay(ficlStack *stack, ficlStackWalkFunction callback,
void *context)
{
ficlVm *vm = stack->vm;
char buffer[128];
struct stackContext myContext;
FICL_STACK_CHECK(stack, 0, 0);
#ifdef _LP64
(void) sprintf(buffer, "[%s stack has %d entries, top at 0x%016lx]\n",
stack->name, ficlStackDepth(stack), (unsigned long)stack->top);
#else
(void) sprintf(buffer, "[%s stack has %d entries, top at 0x%08x]\n",
stack->name, ficlStackDepth(stack), (unsigned)stack->top);
#endif
ficlVmTextOut(vm, buffer);
if (callback == NULL) {
myContext.vm = vm;
myContext.count = 0;
context = &myContext;
callback = ficlStackDisplayCallback;
}
ficlStackWalk(stack, callback, context, FICL_FALSE);
#ifdef _LP64
(void) sprintf(buffer, "[%s stack base at 0x%016lx]\n", stack->name,
(unsigned long)stack->base);
#else
(void) sprintf(buffer, "[%s stack base at 0x%08x]\n", stack->name,
(unsigned)stack->base);
#endif
ficlVmTextOut(vm, buffer);
}
void
ficlVmDisplayDataStack(ficlVm *vm)
{
ficlStackDisplay(vm->dataStack, NULL, NULL);
}
static ficlInteger
ficlStackDisplaySimpleCallback(void *c, ficlCell *cell)
{
struct stackContext *context = (struct stackContext *)c;
char buffer[32];
(void) sprintf(buffer, "%s%ld", context->count ? " " : "",
(long)cell->i);
context->count++;
ficlVmTextOut(context->vm, buffer);
return (FICL_TRUE);
}
void
ficlVmDisplayDataStackSimple(ficlVm *vm)
{
ficlStack *stack = vm->dataStack;
char buffer[32];
struct stackContext context;
FICL_STACK_CHECK(stack, 0, 0);
(void) sprintf(buffer, "[%d] ", ficlStackDepth(stack));
ficlVmTextOut(vm, buffer);
context.vm = vm;
context.count = 0;
ficlStackWalk(stack, ficlStackDisplaySimpleCallback, &context,
FICL_TRUE);
}
static ficlInteger
ficlReturnStackDisplayCallback(void *c, ficlCell *cell)
{
struct stackContext *context = (struct stackContext *)c;
char buffer[128];
#ifdef _LP64
(void) sprintf(buffer, "[0x%016lx %3d] %20ld (0x%016lx)",
(unsigned long)cell, context->count++, cell->i, cell->u);
#else
(void) sprintf(buffer, "[0x%08x %3d] %12d (0x%08x)", (unsigned)cell,
context->count++, cell->i, cell->u);
#endif
if (ficlDictionaryIncludes(context->dictionary, cell->p)) {
ficlWord *word;
word = ficlDictionaryFindEnclosingWord(context->dictionary,
cell->p);
if (word) {
int offset = (ficlCell *)cell->p - &word->param[0];
(void) sprintf(buffer + strlen(buffer), ", %s + %d ",
word->name, offset);
}
}
(void) strcat(buffer, "\n");
ficlVmTextOut(context->vm, buffer);
return (FICL_TRUE);
}
void
ficlVmDisplayReturnStack(ficlVm *vm)
{
struct stackContext context;
context.vm = vm;
context.count = 0;
context.dictionary = ficlVmGetDictionary(vm);
ficlStackDisplay(vm->returnStack, ficlReturnStackDisplayCallback,
&context);
}
static void
ficlPrimitiveForgetWid(ficlVm *vm)
{
ficlDictionary *dictionary = ficlVmGetDictionary(vm);
ficlHash *hash;
hash = (ficlHash *)ficlStackPopPointer(vm->dataStack);
ficlHashForget(hash, dictionary->here);
}
static void
ficlPrimitiveForget(ficlVm *vm)
{
void *where;
ficlDictionary *dictionary = ficlVmGetDictionary(vm);
ficlHash *hash = dictionary->compilationWordlist;
ficlPrimitiveTick(vm);
where = ((ficlWord *)ficlStackPopPointer(vm->dataStack))->name;
ficlHashForget(hash, where);
dictionary->here = FICL_POINTER_TO_CELL(where);
}
#define nCOLWIDTH 8
static void
ficlPrimitiveWordsBackend(ficlVm *vm, ficlDictionary *dictionary,
ficlHash *hash, char *ss)
{
ficlWord *wp;
int nChars = 0;
int len;
unsigned i;
int nWords = 0, dWords = 0;
char *cp;
char *pPad;
int columns;
cp = getenv("screen-#cols");
if (cp == NULL)
columns = 80;
else
columns = strtol(cp, NULL, 0);
pPad = malloc(columns + 1);
if (pPad == NULL)
ficlVmThrowError(vm, "Error: out of memory");
pager_open();
for (i = 0; i < hash->size; i++) {
for (wp = hash->table[i]; wp != NULL; wp = wp->link, nWords++) {
if (wp->length == 0)
continue;
if (ss != NULL && strstr(wp->name, ss) == NULL)
continue;
if (ss != NULL && dWords == 0) {
(void) sprintf(pPad,
" In vocabulary %s\n",
hash->name ? hash->name : "<unknown>");
(void) pager_output(pPad);
}
dWords++;
if (nChars + wp->length >= columns) {
pPad[nChars++] = '\n';
pPad[nChars] = '\0';
nChars = 0;
if (pager_output(pPad))
goto pager_done;
}
cp = wp->name;
nChars += sprintf(pPad + nChars, "%s", cp);
if (nChars > columns - 10) {
pPad[nChars++] = '\n';
pPad[nChars] = '\0';
nChars = 0;
if (pager_output(pPad))
goto pager_done;
} else {
len = nCOLWIDTH - nChars % nCOLWIDTH;
while (len-- > 0)
pPad[nChars++] = ' ';
}
if (nChars > columns - 10) {
pPad[nChars++] = '\n';
pPad[nChars] = '\0';
nChars = 0;
if (pager_output(pPad))
goto pager_done;
}
}
}
if (nChars > 0) {
pPad[nChars++] = '\n';
pPad[nChars] = '\0';
nChars = 0;
ficlVmTextOut(vm, pPad);
}
if (ss == NULL) {
(void) sprintf(pPad,
"Dictionary: %d words, %ld cells used of %u total\n",
nWords, (long)(dictionary->here - dictionary->base),
dictionary->size);
(void) pager_output(pPad);
}
pager_done:
free(pPad);
pager_close();
}
static void
ficlPrimitiveWords(ficlVm *vm)
{
ficlDictionary *dictionary = ficlVmGetDictionary(vm);
ficlHash *hash = dictionary->wordlists[dictionary->wordlistCount - 1];
ficlPrimitiveWordsBackend(vm, dictionary, hash, NULL);
}
void
ficlPrimitiveSiftingImpl(ficlVm *vm, char *ss)
{
ficlDictionary *dict = ficlVmGetDictionary(vm);
int i;
for (i = 0; i < dict->wordlistCount; i++)
ficlPrimitiveWordsBackend(vm, dict, dict->wordlists[i], ss);
}
static void
ficlPrimitiveListEnv(ficlVm *vm)
{
ficlDictionary *dictionary = vm->callback.system->environment;
ficlHash *hash = dictionary->forthWordlist;
ficlWord *word;
unsigned i;
int counter = 0;
pager_open();
for (i = 0; i < hash->size; i++) {
for (word = hash->table[i]; word != NULL;
word = word->link, counter++) {
(void) sprintf(vm->pad, "%s\n", word->name);
if (pager_output(vm->pad))
goto pager_done;
}
}
(void) sprintf(vm->pad,
"Environment: %d words, %ld cells used of %u total\n",
counter, (long)(dictionary->here - dictionary->base),
dictionary->size);
(void) pager_output(vm->pad);
pager_done:
pager_close();
}
void
ficlPrimitiveParseStepList(ficlVm *vm)
{
int i;
ficlSystem *system = vm->callback.system;
FICL_VM_ASSERT(vm, system);
ficlVmTextOut(vm, "Parse steps:\n");
ficlVmTextOut(vm, "lookup\n");
for (i = 0; i < FICL_MAX_PARSE_STEPS; i++) {
if (system->parseList[i] != NULL) {
ficlVmTextOut(vm, system->parseList[i]->name);
ficlVmTextOut(vm, "\n");
} else
break;
}
}
static void
ficlPrimitiveEnvConstant(ficlVm *vm)
{
unsigned value;
FICL_STACK_CHECK(vm->dataStack, 1, 0);
(void) ficlVmGetWordToPad(vm);
value = ficlStackPopUnsigned(vm->dataStack);
(void) ficlDictionarySetConstant(
ficlSystemGetEnvironment(vm->callback.system),
vm->pad, (ficlUnsigned)value);
}
static void
ficlPrimitiveEnv2Constant(ficlVm *vm)
{
ficl2Integer value;
FICL_STACK_CHECK(vm->dataStack, 2, 0);
(void) ficlVmGetWordToPad(vm);
value = ficlStackPop2Integer(vm->dataStack);
(void) ficlDictionarySet2Constant(
ficlSystemGetEnvironment(vm->callback.system), vm->pad, value);
}
void
ficlSystemCompileTools(ficlSystem *system)
{
ficlDictionary *dictionary = ficlSystemGetDictionary(system);
ficlDictionary *environment = ficlSystemGetEnvironment(system);
FICL_SYSTEM_ASSERT(system, dictionary);
FICL_SYSTEM_ASSERT(system, environment);
(void) ficlDictionarySetPrimitive(dictionary, ".s",
ficlVmDisplayDataStack, FICL_WORD_DEFAULT);
(void) ficlDictionarySetPrimitive(dictionary, ".s-simple",
ficlVmDisplayDataStackSimple, FICL_WORD_DEFAULT);
(void) ficlDictionarySetPrimitive(dictionary, "bye", ficlPrimitiveBye,
FICL_WORD_DEFAULT);
(void) ficlDictionarySetPrimitive(dictionary, "forget",
ficlPrimitiveForget, FICL_WORD_DEFAULT);
(void) ficlDictionarySetPrimitive(dictionary, "see", ficlPrimitiveSee,
FICL_WORD_DEFAULT);
(void) ficlDictionarySetPrimitive(dictionary, "words",
ficlPrimitiveWords, FICL_WORD_DEFAULT);
(void) ficlDictionarySetConstant(environment, "tools", FICL_TRUE);
(void) ficlDictionarySetConstant(environment, "tools-ext", FICL_FALSE);
(void) ficlDictionarySetPrimitive(dictionary, "r.s",
ficlVmDisplayReturnStack, FICL_WORD_DEFAULT);
(void) ficlDictionarySetPrimitive(dictionary, ".env",
ficlPrimitiveListEnv, FICL_WORD_DEFAULT);
(void) ficlDictionarySetPrimitive(dictionary, "env-constant",
ficlPrimitiveEnvConstant, FICL_WORD_DEFAULT);
(void) ficlDictionarySetPrimitive(dictionary, "env-2constant",
ficlPrimitiveEnv2Constant, FICL_WORD_DEFAULT);
(void) ficlDictionarySetPrimitive(dictionary, "debug-xt",
ficlPrimitiveDebugXT, FICL_WORD_DEFAULT);
(void) ficlDictionarySetPrimitive(dictionary, "parse-order",
ficlPrimitiveParseStepList, FICL_WORD_DEFAULT);
(void) ficlDictionarySetPrimitive(dictionary, "step-break",
ficlPrimitiveStepBreak, FICL_WORD_DEFAULT);
(void) ficlDictionarySetPrimitive(dictionary, "forget-wid",
ficlPrimitiveForgetWid, FICL_WORD_DEFAULT);
(void) ficlDictionarySetPrimitive(dictionary, "see-xt",
ficlPrimitiveSeeXT, FICL_WORD_DEFAULT);
#if FICL_WANT_FLOAT
(void) ficlDictionarySetPrimitive(dictionary, ".hash",
ficlPrimitiveHashSummary, FICL_WORD_DEFAULT);
#endif
}