/* * 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 ( "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 }