/*
* t o o l s . c
* Forth Inspired Command Language - programming tools
* Author: John Sadler (john_sadler@alum.mit.edu)
* Created: 20 June 2000
* $Id: tools.c,v 1.12 2010/08/12 13:57:22 asau Exp $
*/
/*
* Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
* All rights reserved.
*
* Get the latest Ficl release at http://ficl.sourceforge.net
*
* I am interested in hearing from anyone who uses Ficl. If you have
* a problem, a success story, a defect, an enhancement request, or
* if you would like to contribute to the Ficl release, please
* contact me by email at the address above.
*
* L I C E N S E and D I S C L A I M E R
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* NOTES:
* SEE needs information about the addresses of functions that
* are the CFAs of colon definitions, constants, variables, DOES>
* words, and so on. It gets this information from a table and supporting
* functions in words.c.
* fiColonParen fiDoDoes createParen fiVariableParen fiUserParen fiConstantParen
*
* Step and break debugger for Ficl
* debug ( xt -- ) Start debugging an xt
* Set a breakpoint
* Specify breakpoint default action
*/
#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];
sprintf(buffer, "ASSERTION FAILED at %s:%d: \"%s\"\n",
filename, line, expressionString);
ficlCallbackTextOut(callback, buffer);
exit(-1);
}
#else /* FICL_ROBUST >= 1 */
FICL_IGNORE(callback);
FICL_IGNORE(expression);
FICL_IGNORE(expressionString);
FICL_IGNORE(filename);
FICL_IGNORE(line);
#endif /* FICL_ROBUST >= 1 */
}
/*
* v m S e t B r e a k
* Set a breakpoint at the current value of IP by
* storing that address in a BREAKPOINT record
*/
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;
}
/*
* d e b u g P r o m p t
*/
static void
ficlDebugPrompt(ficlVm *vm, int debug)
{
if (debug)
setenv("prompt", "dbg> ", 1);
else
setenv("prompt", "${interpret}", 1);
}
#if 0
static int
isPrimitive(ficlWord *word)
{
ficlWordKind wk = ficlWordClassify(word);
return ((wk != COLON) && (wk != DOES));
}
#endif
/*
* d i c t H a s h S u m m a r y
* Calculate a figure of merit for the dictionary hash table based
* on the average search depth for all the words in the dictionary,
* assuming uniform distribution of target keys. The figure of merit
* is the ratio of the total search depth for all keys in the table
* versus a theoretical optimum that would be achieved if the keys
* were distributed into the table as evenly as possible.
* The figure would be worse if the hash table used an open
* addressing scheme (i.e. collisions resolved by searching the
* table for an empty slot) for a given size table.
*/
#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;
}
/* Calc actual avg search depth for this hash */
avg = avg / nWords;
/* Calc best possible performance with this size hash */
nAvg = nWords / size;
nRem = nWords % size;
nDepth = size * (nAvg * (nAvg+1))/2 + (nAvg+1)*nRem;
best = (double)nDepth/nWords;
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
/*
* Here's the outer part of the decompiler. It's
* just a big nested conditional that checks the
* CFA of the word to decompile for each kind of
* known word-builder code, and tries to do
* something appropriate. If the CFA is not recognized,
* just indicate that it is a primitive.
*/
static void
ficlPrimitiveSeeXT(ficlVm *vm)
{
ficlWord *word;
ficlWordKind kind;
word = (ficlWord *)ficlStackPopPointer(vm->dataStack);
kind = ficlWordClassify(word);
switch (kind) {
case FICL_WORDKIND_COLON:
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:
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:
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:
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:
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:
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);
}
/*
* f i c l D e b u g X T
* debug ( xt -- )
* Given an xt of a colon definition or a word defined by DOES>, set the
* VM up to debug the word: push IP, set the xt as the next thing to execute,
* set a breakpoint at its first instruction, and run to the breakpoint.
* Note: the semantics of this word are equivalent to "step in"
*/
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:
/*
* Run the colon code and set a breakpoint at the next
* instruction
*/
ficlVmExecuteWord(vm, xt);
ficlVmSetBreak(vm, &(vm->callback.system->breakpoint));
break;
default:
ficlVmExecuteWord(vm, xt);
break;
}
}
/*
* s t e p I n
* Ficl
* Execute the next instruction, stepping into it if it's a colon definition
* or a does> word. This is the easy kind of step.
*/
static void
ficlPrimitiveStepIn(ficlVm *vm)
{
/*
* Do one step of the inner loop
*/
ficlVmExecuteWord(vm, *vm->ip++);
/*
* Now set a breakpoint at the next instruction
*/
ficlVmSetBreak(vm, &(vm->callback.system->breakpoint));
}
/*
* s t e p O v e r
* Ficl
* Execute the next instruction atomically. This requires some insight into
* the memory layout of compiled code. Set a breakpoint at the next instruction
* in this word, and run until we hit it
*/
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:
/*
* assume that the next ficlCell holds an instruction
* set a breakpoint there and return to the inner interpreter
*/
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;
}
}
/*
* s t e p - b r e a k
* Ficl
* Handles breakpoints for stepped execution.
* Upon entry, breakpoint contains the address and replaced instruction
* of the current breakpoint.
* Clear the breakpoint
* Get a command from the console.
* i (step in) - execute the current instruction and set a new breakpoint
* at the IP
* o (step over) - execute the current instruction to completion and set
* a new breakpoint at the IP
* g (go) - execute the current instruction and exit
* q (quit) - abort current word
* b (toggle breakpoint)
*/
extern char *ficlDictionaryInstructionNames[];
static void
ficlPrimitiveStepBreak(ficlVm *vm)
{
ficlString command;
ficlWord *word;
ficlWord *pOnStep;
int debug = 1;
if (!vm->restart) {
FICL_VM_ASSERT(vm, vm->callback.system->breakpoint.address);
FICL_VM_ASSERT(vm, vm->callback.system->breakpoint.oldXT);
/*
* Clear the breakpoint that caused me to run
* Restore the original instruction at the breakpoint,
* and restore the IP
*/
vm->ip = (ficlIp)(vm->callback.system->breakpoint.address);
*vm->ip = vm->callback.system->breakpoint.oldXT;
/*
* If there's an onStep, do it
*/
pOnStep = ficlSystemLookup(vm->callback.system, "on-step");
if (pOnStep)
ficlVmExecuteXT(vm, pOnStep);
/*
* Print the name of the next instruction
*/
word = vm->callback.system->breakpoint.oldXT;
if ((((ficlInstruction)word) > ficlInstructionInvalid) &&
(((ficlInstruction)word) < ficlInstructionLast))
sprintf(vm->pad, "next: %s (instruction %ld)\n",
ficlDictionaryInstructionNames[(long)word],
(long)word);
else {
sprintf(vm->pad, "next: %s\n", word->name);
if (strcmp(word->name, "interpret") == 0)
debug = 0;
}
ficlVmTextOut(vm, vm->pad);
ficlDebugPrompt(vm, 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(vm, 0);
ficlVmThrow(vm, FICL_VM_STATUS_ABORT);
break;
case 'x': {
/*
* Take whatever's left in the TIB and feed it to a
* subordinate ficlVmExecuteString
*/
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(vm, 0);
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(vm, 1);
ficlVmThrow(vm, FICL_VM_STATUS_RESTART);
break;
}
ficlDebugPrompt(vm, 0);
}
/*
* b y e
* TOOLS
* Signal the system to shut down - this causes ficlExec to return
* VM_USEREXIT. The rest is up to you.
*/
static void
ficlPrimitiveBye(ficlVm *vm)
{
ficlVmThrow(vm, FICL_VM_STATUS_USER_EXIT);
}
/*
* d i s p l a y S t a c k
* TOOLS
* Display the parameter stack (code for ".s")
*/
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
snprintf(buffer, sizeof (buffer), "[0x%016lx %3d]: %20ld (0x%016lx)\n",
(unsigned long)cell, context->count++, (long)cell->i,
(unsigned long)cell->u);
#else
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
sprintf(buffer, "[%s stack has %d entries, top at 0x%016lx]\n",
stack->name, ficlStackDepth(stack), (unsigned long)stack->top);
#else
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
sprintf(buffer, "[%s stack base at 0x%016lx]\n", stack->name,
(unsigned long)stack->base);
#else
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];
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);
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
sprintf(buffer, "[0x%016lx %3d] %20ld (0x%016lx)", (unsigned long)cell,
context->count++, cell->i, cell->u);
#else
sprintf(buffer, "[0x%08x %3d] %12d (0x%08x)", (unsigned)cell,
context->count++, cell->i, cell->u);
#endif
/*
* Attempt to find the word that contains the return
* stack address (as if it is part of a colon definition).
* If this works, also print the name of the word.
*/
if (ficlDictionaryIncludes(context->dictionary, cell->p)) {
ficlWord *word;
word = ficlDictionaryFindEnclosingWord(context->dictionary,
cell->p);
if (word) {
int offset = (ficlCell *)cell->p - &word->param[0];
sprintf(buffer + strlen(buffer), ", %s + %d ",
word->name, offset);
}
}
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);
}
/*
* f o r g e t - w i d
*/
static void
ficlPrimitiveForgetWid(ficlVm *vm)
{
ficlDictionary *dictionary = ficlVmGetDictionary(vm);
ficlHash *hash;
hash = (ficlHash *)ficlStackPopPointer(vm->dataStack);
ficlHashForget(hash, dictionary->here);
}
/*
* f o r g e t
* TOOLS EXT ( "<spaces>name" -- )
* Skip leading space delimiters. Parse name delimited by a space.
* Find name, then delete name from the dictionary along with all
* words added to the dictionary after name. An ambiguous
* condition exists if name cannot be found.
*
* If the Search-Order word set is present, FORGET searches the
* compilation word list. An ambiguous condition exists if the
* compilation word list is deleted.
*/
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);
}
/*
* w o r d s
*/
#define nCOLWIDTH 8
static void
ficlPrimitiveWords(ficlVm *vm)
{
ficlDictionary *dictionary = ficlVmGetDictionary(vm);
ficlHash *hash = dictionary->wordlists[dictionary->wordlistCount - 1];
ficlWord *wp;
int nChars = 0;
int len;
unsigned i;
int nWords = 0;
char *cp;
char *pPad;
int columns;
cp = getenv("COLUMNS");
/*
* using strtol for now. TODO: refactor number conversion from
* ficlPrimitiveToNumber() and use it instead.
*/
if (cp == NULL)
columns = 80;
else
columns = strtol(cp, NULL, 0);
/*
* the pad is fixed size area, it's better to allocate
* dedicated buffer space to deal with custom terminal sizes.
*/
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) /* ignore :noname defs */
continue;
/* prevent line wrap due to long words */
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);
}
sprintf(pPad, "Dictionary: %d words, %ld cells used of %u total\n",
nWords, (long)(dictionary->here - dictionary->base),
dictionary->size);
pager_output(pPad);
pager_done:
free(pPad);
pager_close();
}
/*
* l i s t E n v
* Print symbols defined in the environment
*/
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++) {
sprintf(vm->pad, "%s\n", word->name);
if (pager_output(vm->pad))
goto pager_done;
}
}
sprintf(vm->pad, "Environment: %d words, %ld cells used of %u total\n",
counter, (long)(dictionary->here - dictionary->base),
dictionary->size);
pager_output(vm->pad);
pager_done:
pager_close();
}
/*
* This word lists the parse steps in order
*/
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;
}
}
/*
* e n v C o n s t a n t
* Ficl interface to ficlSystemSetEnvironment and ficlSetEnvD - allow Ficl
* code to set environment constants...
*/
static void
ficlPrimitiveEnvConstant(ficlVm *vm)
{
unsigned value;
FICL_STACK_CHECK(vm->dataStack, 1, 0);
ficlVmGetWordToPad(vm);
value = ficlStackPopUnsigned(vm->dataStack);
ficlDictionarySetConstant(ficlSystemGetEnvironment(vm->callback.system),
vm->pad, (ficlUnsigned)value);
}
static void
ficlPrimitiveEnv2Constant(ficlVm *vm)
{
ficl2Integer value;
FICL_STACK_CHECK(vm->dataStack, 2, 0);
ficlVmGetWordToPad(vm);
value = ficlStackPop2Integer(vm->dataStack);
ficlDictionarySet2Constant(
ficlSystemGetEnvironment(vm->callback.system), vm->pad, value);
}
/*
* f i c l C o m p i l e T o o l s
* Builds wordset for debugger and TOOLS optional word set
*/
void
ficlSystemCompileTools(ficlSystem *system)
{
ficlDictionary *dictionary = ficlSystemGetDictionary(system);
ficlDictionary *environment = ficlSystemGetEnvironment(system);
FICL_SYSTEM_ASSERT(system, dictionary);
FICL_SYSTEM_ASSERT(system, environment);
/*
* TOOLS and TOOLS EXT
*/
ficlDictionarySetPrimitive(dictionary, ".s", ficlVmDisplayDataStack,
FICL_WORD_DEFAULT);
ficlDictionarySetPrimitive(dictionary, ".s-simple",
ficlVmDisplayDataStackSimple, FICL_WORD_DEFAULT);
ficlDictionarySetPrimitive(dictionary, "bye", ficlPrimitiveBye,
FICL_WORD_DEFAULT);
ficlDictionarySetPrimitive(dictionary, "forget", ficlPrimitiveForget,
FICL_WORD_DEFAULT);
ficlDictionarySetPrimitive(dictionary, "see", ficlPrimitiveSee,
FICL_WORD_DEFAULT);
ficlDictionarySetPrimitive(dictionary, "words", ficlPrimitiveWords,
FICL_WORD_DEFAULT);
/*
* Set TOOLS environment query values
*/
ficlDictionarySetConstant(environment, "tools", FICL_TRUE);
ficlDictionarySetConstant(environment, "tools-ext", FICL_FALSE);
/*
* Ficl extras
*/
ficlDictionarySetPrimitive(dictionary, "r.s", ficlVmDisplayReturnStack,
FICL_WORD_DEFAULT);
ficlDictionarySetPrimitive(dictionary, ".env", ficlPrimitiveListEnv,
FICL_WORD_DEFAULT);
ficlDictionarySetPrimitive(dictionary, "env-constant",
ficlPrimitiveEnvConstant, FICL_WORD_DEFAULT);
ficlDictionarySetPrimitive(dictionary, "env-2constant",
ficlPrimitiveEnv2Constant, FICL_WORD_DEFAULT);
ficlDictionarySetPrimitive(dictionary, "debug-xt", ficlPrimitiveDebugXT,
FICL_WORD_DEFAULT);
ficlDictionarySetPrimitive(dictionary, "parse-order",
ficlPrimitiveParseStepList, FICL_WORD_DEFAULT);
ficlDictionarySetPrimitive(dictionary, "step-break",
ficlPrimitiveStepBreak, FICL_WORD_DEFAULT);
ficlDictionarySetPrimitive(dictionary, "forget-wid",
ficlPrimitiveForgetWid, FICL_WORD_DEFAULT);
ficlDictionarySetPrimitive(dictionary, "see-xt", ficlPrimitiveSeeXT,
FICL_WORD_DEFAULT);
#if FICL_WANT_FLOAT
ficlDictionarySetPrimitive(dictionary, ".hash",
ficlPrimitiveHashSummary, FICL_WORD_DEFAULT);
#endif
}