ref: 67757267d0b9c6b15a0f9a87abab74ab152d9b09
dir: /tools.c/
/******************************************************************* ** t o o l s . c ** Forth Inspired Command Language - programming tools ** Author: John Sadler ([email protected]) ** Created: 20 June 2000 ** $Id: tools.c,v 1.16 2010/12/02 22:14:12 asau Exp $ *******************************************************************/ /* ** Copyright (c) 1997-2001 John Sadler ([email protected]) ** 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 <stdlib.h> #include <stdint.h> #include <stdio.h> /* sprintf */ #include <string.h> #include <ctype.h> #include "ficl.h" 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) { ficlVmTextOut(vm, "dbg> "); } #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); return; } #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", word->param->i, word->param->u); ficlVmTextOut(vm, vm->pad); break; #if FICL_WANT_USER case FICL_WORDKIND_USER: sprintf(vm->pad, "user variable %ld (%#lx)\n", word->param->i, word->param->u); ficlVmTextOut(vm, vm->pad); break; #endif case FICL_WORDKIND_CONSTANT: sprintf(vm->pad, "constant = %ld (%#lx)\n", word->param->i, word->param->u); ficlVmTextOut(vm, vm->pad); break; case FICL_WORDKIND_2CONSTANT: sprintf(vm->pad, "constant = %ld %ld (%#lx %#lx)\n", word->param[1].i, word->param->i, word->param[1].u, 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"); } return; } static void ficlPrimitiveSee(ficlVm *vm) { ficlPrimitiveTick(vm); ficlPrimitiveSeeXT(vm); return; } /************************************************************************** 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 = (ficlWord*)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; } return; } /************************************************************************** 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)); return; } /************************************************************************** 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; } return; } /************************************************************************** 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; ficlWordKind kind; 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; kind = ficlWordClassify(word); switch (kind) { case FICL_WORDKIND_INSTRUCTION: case FICL_WORDKIND_INSTRUCTION_WITH_ARGUMENT: sprintf(vm->pad, "next: %s (instruction %ld)\n", ficlDictionaryInstructionNames[(long)word], (long)word); break; default: sprintf(vm->pad, "next: %s\n", word->name); break; } ficlVmTextOut(vm, vm->pad); ficlDebugPrompt(vm); } 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': { ficlVmTextOut(vm, FICL_PROMPT); 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"); } 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); ficlVmThrow(vm, FICL_VM_STATUS_RESTART); break; } } return; } /************************************************************************** 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); return; } /************************************************************************** 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[64]; sprintf(buffer, "[0x%08jx %3d]: %12jd (0x%08jx)\n", (uintmax_t)cell, context->count++, (intmax_t)cell->i, (uintmax_t)cell->i); 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); sprintf(buffer, "[%s stack has %d entries, top at 0x%08jx]\n", stack->name, ficlStackDepth(stack), (uintmax_t)stack->top); ficlVmTextOut(vm, buffer); if (callback == NULL) { myContext.vm = vm; myContext.count = 0; context = &myContext; callback = ficlStackDisplayCallback; } ficlStackWalk(stack, callback, context, FICL_FALSE); sprintf(buffer, "[%s stack base at 0x%08jx]\n", stack->name, (uintmax_t)stack->base); ficlVmTextOut(vm, buffer); return; } void ficlVmDisplayDataStack(ficlVm *vm) { ficlStackDisplay(vm->dataStack, NULL, NULL); return; } static ficlInteger ficlStackDisplaySimpleCallback(void *c, ficlCell *cell) { struct stackContext *context = (struct stackContext *)c; char buffer[32]; sprintf(buffer, "%s%jd", context->count ? " " : "", (intmax_t)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); return; } static ficlInteger ficlReturnStackDisplayCallback(void *c, ficlCell *cell) { struct stackContext *context = (struct stackContext *)c; char buffer[128]; sprintf(buffer, "[0x%08jx %3d] %12jd (0x%08jx)", (uintmax_t)cell, context->count++, (intmax_t)cell->i, (uintmax_t)cell->i); /* ** 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 = ficlDictionaryFindEnclosingWord(context->dictionary, (ficlCell*)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); return; } /************************************************************************** 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); return; } /************************************************************************** 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); return; } /************************************************************************** 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 = vm->pad; 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; cp = wp->name; nChars += sprintf(pPad + nChars, "%s", cp); if (nChars > 70) { pPad[nChars++] = '\n'; pPad[nChars] = '\0'; nChars = 0; ficlVmTextOut(vm, pPad); } else { len = nCOLWIDTH - nChars % nCOLWIDTH; while (len-- > 0) pPad[nChars++] = ' '; } if (nChars > 70) { pPad[nChars++] = '\n'; pPad[nChars] = '\0'; nChars = 0; ficlVmTextOut(vm, pPad); } } } if (nChars > 0) { pPad[nChars++] = '\n'; pPad[nChars] = '\0'; nChars = 0; ficlVmTextOut(vm, pPad); } sprintf(vm->pad, "Dictionary: %d words, %ld cells used of %u total\n", nWords, (long) (dictionary->here - dictionary->base), dictionary->size); ficlVmTextOut(vm, vm->pad); return; } /************************************************************************** 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; for (i = 0; i < hash->size; i++) { for (word = hash->table[i]; word != NULL; word = word->link, counter++) { ficlVmTextOut(vm, word->name); ficlVmTextOut(vm, "\n"); } } sprintf(vm->pad, "Environment: %d words, %ld cells used of %u total\n", counter, (long) (dictionary->here - dictionary->base), dictionary->size); ficlVmTextOut(vm, vm->pad); return; } /* ** 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; } return; } /************************************************************************** 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); return; } 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); return; } /************************************************************************** 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 return; }