/*******************************************************************1** t o o l s . c2** Forth Inspired Command Language - programming tools3** Author: John Sadler ([email protected])4** Created: 20 June 20005** $Id: tools.c,v 1.11 2001/12/05 07:21:34 jsadler Exp $6*******************************************************************/7/*8** Copyright (c) 1997-2001 John Sadler ([email protected])9** All rights reserved.10**11** Get the latest Ficl release at http://ficl.sourceforge.net12**13** I am interested in hearing from anyone who uses ficl. If you have14** a problem, a success story, a defect, an enhancement request, or15** if you would like to contribute to the ficl release, please16** contact me by email at the address above.17**18** L I C E N S E and D I S C L A I M E R19**20** Redistribution and use in source and binary forms, with or without21** modification, are permitted provided that the following conditions22** are met:23** 1. Redistributions of source code must retain the above copyright24** notice, this list of conditions and the following disclaimer.25** 2. Redistributions in binary form must reproduce the above copyright26** notice, this list of conditions and the following disclaimer in the27** documentation and/or other materials provided with the distribution.28**29** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND30** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE31** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE32** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE33** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL34** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS35** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)36** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT37** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY38** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF39** SUCH DAMAGE.40*/4142/*43** NOTES:44** SEE needs information about the addresses of functions that45** are the CFAs of colon definitions, constants, variables, DOES>46** words, and so on. It gets this information from a table and supporting47** functions in words.c.48** colonParen doDoes createParen variableParen userParen constantParen49**50** Step and break debugger for Ficl51** debug ( xt -- ) Start debugging an xt52** Set a breakpoint53** Specify breakpoint default action54*/555657#ifdef TESTMAIN58#include <stdlib.h>59#include <stdio.h> /* sprintf */60#include <ctype.h>61#else62#include <stand.h>63#endif64#include <string.h>65#include "ficl.h"666768#if 069/*70** nBREAKPOINTS sizes the breakpoint array. One breakpoint (bp 0) is reserved71** for the STEP command. The rest are user programmable.72*/73#define nBREAKPOINTS 327475#endif767778/**************************************************************************79v m S e t B r e a k80** Set a breakpoint at the current value of IP by81** storing that address in a BREAKPOINT record82**************************************************************************/83static void vmSetBreak(FICL_VM *pVM, FICL_BREAKPOINT *pBP)84{85FICL_WORD *pStep = ficlLookup(pVM->pSys, "step-break");86assert(pStep);8788pBP->address = pVM->ip;89pBP->origXT = *pVM->ip;90*pVM->ip = pStep;91}929394/**************************************************************************95** d e b u g P r o m p t96**************************************************************************/97static void debugPrompt(FICL_VM *pVM)98{99vmTextOut(pVM, "dbg> ", 0);100}101102103/**************************************************************************104** i s A F i c l W o r d105** Vet a candidate pointer carefully to make sure106** it's not some chunk o' inline data...107** It has to have a name, and it has to look108** like it's in the dictionary address range.109** NOTE: this excludes :noname words!110**************************************************************************/111int isAFiclWord(FICL_DICT *pd, FICL_WORD *pFW)112{113114if (!dictIncludes(pd, pFW))115return 0;116117if (!dictIncludes(pd, pFW->name))118return 0;119120if ((pFW->link != NULL) && !dictIncludes(pd, pFW->link))121return 0;122123if ((pFW->nName <= 0) || (pFW->name[pFW->nName] != '\0'))124return 0;125126if (strlen(pFW->name) != pFW->nName)127return 0;128129return 1;130}131132133#if 0134static int isPrimitive(FICL_WORD *pFW)135{136WORDKIND wk = ficlWordClassify(pFW);137return ((wk != COLON) && (wk != DOES));138}139#endif140141142/**************************************************************************143f i n d E n c l o s i n g W o r d144** Given a pointer to something, check to make sure it's an address in the145** dictionary. If so, search backwards until we find something that looks146** like a dictionary header. If successful, return the address of the147** FICL_WORD found. Otherwise return NULL.148** nSEARCH_CELLS sets the maximum neighborhood this func will search before giving up149**************************************************************************/150#define nSEARCH_CELLS 100151152static FICL_WORD *findEnclosingWord(FICL_VM *pVM, CELL *cp)153{154FICL_WORD *pFW;155FICL_DICT *pd = vmGetDict(pVM);156int i;157158if (!dictIncludes(pd, (void *)cp))159return NULL;160161for (i = nSEARCH_CELLS; i > 0; --i, --cp)162{163pFW = (FICL_WORD *)(cp + 1 - (sizeof (FICL_WORD) / sizeof (CELL)));164if (isAFiclWord(pd, pFW))165return pFW;166}167168return NULL;169}170171172/**************************************************************************173s e e174** TOOLS ( "<spaces>name" -- )175** Display a human-readable representation of the named word's definition.176** The source of the representation (object-code decompilation, source177** block, etc.) and the particular form of the display is implementation178** defined.179**************************************************************************/180/*181** seeColon (for proctologists only)182** Walks a colon definition, decompiling183** on the fly. Knows about primitive control structures.184*/185static void seeColon(FICL_VM *pVM, CELL *pc)186{187char *cp;188CELL *param0 = pc;189FICL_DICT *pd = vmGetDict(pVM);190FICL_WORD *pSemiParen = ficlLookup(pVM->pSys, "(;)");191assert(pSemiParen);192193for (; pc->p != pSemiParen; pc++)194{195FICL_WORD *pFW = (FICL_WORD *)(pc->p);196197cp = pVM->pad;198if ((void *)pc == (void *)pVM->ip)199*cp++ = '>';200else201*cp++ = ' ';202cp += sprintf(cp, "%3d ", (int)(pc-param0));203204if (isAFiclWord(pd, pFW))205{206WORDKIND kind = ficlWordClassify(pFW);207CELL c;208209switch (kind)210{211case LITERAL:212c = *++pc;213if (isAFiclWord(pd, c.p))214{215FICL_WORD *pLit = (FICL_WORD *)c.p;216sprintf(cp, "%.*s ( %#lx literal )",217pLit->nName, pLit->name, (unsigned long)c.u);218}219else220sprintf(cp, "literal %ld (%#lx)",221(long)c.i, (unsigned long)c.u);222break;223case STRINGLIT:224{225FICL_STRING *sp = (FICL_STRING *)(void *)++pc;226pc = (CELL *)alignPtr(sp->text + sp->count + 1) - 1;227sprintf(cp, "s\" %.*s\"", sp->count, sp->text);228}229break;230case CSTRINGLIT:231{232FICL_STRING *sp = (FICL_STRING *)(void *)++pc;233pc = (CELL *)alignPtr(sp->text + sp->count + 1) - 1;234sprintf(cp, "c\" %.*s\"", sp->count, sp->text);235}236break;237case IF:238c = *++pc;239if (c.i > 0)240sprintf(cp, "if / while (branch %d)", (int)(pc+c.i-param0));241else242sprintf(cp, "until (branch %d)", (int)(pc+c.i-param0));243break;244case BRANCH:245c = *++pc;246if (c.i == 0)247sprintf(cp, "repeat (branch %d)", (int)(pc+c.i-param0));248else if (c.i == 1)249sprintf(cp, "else (branch %d)", (int)(pc+c.i-param0));250else251sprintf(cp, "endof (branch %d)", (int)(pc+c.i-param0));252break;253254case OF:255c = *++pc;256sprintf(cp, "of (branch %d)", (int)(pc+c.i-param0));257break;258259case QDO:260c = *++pc;261sprintf(cp, "?do (leave %d)", (int)((CELL *)c.p-param0));262break;263case DO:264c = *++pc;265sprintf(cp, "do (leave %d)", (int)((CELL *)c.p-param0));266break;267case LOOP:268c = *++pc;269sprintf(cp, "loop (branch %d)", (int)(pc+c.i-param0));270break;271case PLOOP:272c = *++pc;273sprintf(cp, "+loop (branch %d)", (int)(pc+c.i-param0));274break;275default:276sprintf(cp, "%.*s", pFW->nName, pFW->name);277break;278}279280}281else /* probably not a word - punt and print value */282{283sprintf(cp, "%ld ( %#lx )", (long)pc->i, (unsigned long)pc->u);284}285286vmTextOut(pVM, pVM->pad, 1);287}288289vmTextOut(pVM, ";", 1);290}291292/*293** Here's the outer part of the decompiler. It's294** just a big nested conditional that checks the295** CFA of the word to decompile for each kind of296** known word-builder code, and tries to do297** something appropriate. If the CFA is not recognized,298** just indicate that it is a primitive.299*/300static void seeXT(FICL_VM *pVM)301{302FICL_WORD *pFW;303WORDKIND kind;304305pFW = (FICL_WORD *)stackPopPtr(pVM->pStack);306kind = ficlWordClassify(pFW);307308switch (kind)309{310case COLON:311sprintf(pVM->pad, ": %.*s", pFW->nName, pFW->name);312vmTextOut(pVM, pVM->pad, 1);313seeColon(pVM, pFW->param);314break;315316case DOES:317vmTextOut(pVM, "does>", 1);318seeColon(pVM, (CELL *)pFW->param->p);319break;320321case CREATE:322vmTextOut(pVM, "create", 1);323break;324325case VARIABLE:326sprintf(pVM->pad, "variable = %ld (%#lx)",327(long)pFW->param->i, (unsigned long)pFW->param->u);328vmTextOut(pVM, pVM->pad, 1);329break;330331#if FICL_WANT_USER332case USER:333sprintf(pVM->pad, "user variable %ld (%#lx)",334(long)pFW->param->i, (unsigned long)pFW->param->u);335vmTextOut(pVM, pVM->pad, 1);336break;337#endif338339case CONSTANT:340sprintf(pVM->pad, "constant = %ld (%#lx)",341(long)pFW->param->i, (unsigned long)pFW->param->u);342vmTextOut(pVM, pVM->pad, 1);343344default:345sprintf(pVM->pad, "%.*s is a primitive", pFW->nName, pFW->name);346vmTextOut(pVM, pVM->pad, 1);347break;348}349350if (pFW->flags & FW_IMMEDIATE)351{352vmTextOut(pVM, "immediate", 1);353}354355if (pFW->flags & FW_COMPILE)356{357vmTextOut(pVM, "compile-only", 1);358}359360return;361}362363364static void see(FICL_VM *pVM)365{366ficlTick(pVM);367seeXT(pVM);368return;369}370371372/**************************************************************************373f i c l D e b u g X T374** debug ( xt -- )375** Given an xt of a colon definition or a word defined by DOES>, set the376** VM up to debug the word: push IP, set the xt as the next thing to execute,377** set a breakpoint at its first instruction, and run to the breakpoint.378** Note: the semantics of this word are equivalent to "step in"379**************************************************************************/380void ficlDebugXT(FICL_VM *pVM)381{382FICL_WORD *xt = stackPopPtr(pVM->pStack);383WORDKIND wk = ficlWordClassify(xt);384385stackPushPtr(pVM->pStack, xt);386seeXT(pVM);387388switch (wk)389{390case COLON:391case DOES:392/*393** Run the colon code and set a breakpoint at the next instruction394*/395vmExecute(pVM, xt);396vmSetBreak(pVM, &(pVM->pSys->bpStep));397break;398399default:400vmExecute(pVM, xt);401break;402}403404return;405}406407408/**************************************************************************409s t e p I n410** FICL411** Execute the next instruction, stepping into it if it's a colon definition412** or a does> word. This is the easy kind of step.413**************************************************************************/414void stepIn(FICL_VM *pVM)415{416/*417** Do one step of the inner loop418*/419{420M_VM_STEP(pVM)421}422423/*424** Now set a breakpoint at the next instruction425*/426vmSetBreak(pVM, &(pVM->pSys->bpStep));427428return;429}430431432/**************************************************************************433s t e p O v e r434** FICL435** Execute the next instruction atomically. This requires some insight into436** the memory layout of compiled code. Set a breakpoint at the next instruction437** in this word, and run until we hit it438**************************************************************************/439void stepOver(FICL_VM *pVM)440{441FICL_WORD *pFW;442WORDKIND kind;443FICL_WORD *pStep = ficlLookup(pVM->pSys, "step-break");444assert(pStep);445446pFW = *pVM->ip;447kind = ficlWordClassify(pFW);448449switch (kind)450{451case COLON:452case DOES:453/*454** assume that the next cell holds an instruction455** set a breakpoint there and return to the inner interp456*/457pVM->pSys->bpStep.address = pVM->ip + 1;458pVM->pSys->bpStep.origXT = pVM->ip[1];459pVM->ip[1] = pStep;460break;461462default:463stepIn(pVM);464break;465}466467return;468}469470471/**************************************************************************472s t e p - b r e a k473** FICL474** Handles breakpoints for stepped execution.475** Upon entry, bpStep contains the address and replaced instruction476** of the current breakpoint.477** Clear the breakpoint478** Get a command from the console.479** i (step in) - execute the current instruction and set a new breakpoint480** at the IP481** o (step over) - execute the current instruction to completion and set482** a new breakpoint at the IP483** g (go) - execute the current instruction and exit484** q (quit) - abort current word485** b (toggle breakpoint)486**************************************************************************/487void stepBreak(FICL_VM *pVM)488{489STRINGINFO si;490FICL_WORD *pFW;491FICL_WORD *pOnStep;492493if (!pVM->fRestart)494{495assert(pVM->pSys->bpStep.address);496assert(pVM->pSys->bpStep.origXT);497/*498** Clear the breakpoint that caused me to run499** Restore the original instruction at the breakpoint,500** and restore the IP501*/502pVM->ip = (IPTYPE)(pVM->pSys->bpStep.address);503*pVM->ip = pVM->pSys->bpStep.origXT;504505/*506** If there's an onStep, do it507*/508pOnStep = ficlLookup(pVM->pSys, "on-step");509if (pOnStep)510ficlExecXT(pVM, pOnStep);511512/*513** Print the name of the next instruction514*/515pFW = pVM->pSys->bpStep.origXT;516sprintf(pVM->pad, "next: %.*s", pFW->nName, pFW->name);517#if 0518if (isPrimitive(pFW))519{520strcat(pVM->pad, " ( primitive )");521}522#endif523524vmTextOut(pVM, pVM->pad, 1);525debugPrompt(pVM);526}527else528{529pVM->fRestart = 0;530}531532si = vmGetWord(pVM);533534if (!strincmp(si.cp, "i", si.count))535{536stepIn(pVM);537}538else if (!strincmp(si.cp, "g", si.count))539{540return;541}542else if (!strincmp(si.cp, "l", si.count))543{544FICL_WORD *xt;545xt = findEnclosingWord(pVM, (CELL *)(pVM->ip));546if (xt)547{548stackPushPtr(pVM->pStack, xt);549seeXT(pVM);550}551else552{553vmTextOut(pVM, "sorry - can't do that", 1);554}555vmThrow(pVM, VM_RESTART);556}557else if (!strincmp(si.cp, "o", si.count))558{559stepOver(pVM);560}561else if (!strincmp(si.cp, "q", si.count))562{563ficlTextOut(pVM, FICL_PROMPT, 0);564vmThrow(pVM, VM_ABORT);565}566else if (!strincmp(si.cp, "x", si.count))567{568/*569** Take whatever's left in the TIB and feed it to a subordinate ficlExec570*/571int ret;572char *cp = pVM->tib.cp + pVM->tib.index;573int count = pVM->tib.end - cp;574FICL_WORD *oldRun = pVM->runningWord;575576ret = ficlExecC(pVM, cp, count);577578if (ret == VM_OUTOFTEXT)579{580ret = VM_RESTART;581pVM->runningWord = oldRun;582vmTextOut(pVM, "", 1);583}584585vmThrow(pVM, ret);586}587else588{589vmTextOut(pVM, "i -- step In", 1);590vmTextOut(pVM, "o -- step Over", 1);591vmTextOut(pVM, "g -- Go (execute to completion)", 1);592vmTextOut(pVM, "l -- List source code", 1);593vmTextOut(pVM, "q -- Quit (stop debugging and abort)", 1);594vmTextOut(pVM, "x -- eXecute the rest of the line as ficl words", 1);595debugPrompt(pVM);596vmThrow(pVM, VM_RESTART);597}598599return;600}601602603/**************************************************************************604b y e605** TOOLS606** Signal the system to shut down - this causes ficlExec to return607** VM_USEREXIT. The rest is up to you.608**************************************************************************/609static void bye(FICL_VM *pVM)610{611vmThrow(pVM, VM_USEREXIT);612return;613}614615616/**************************************************************************617d i s p l a y S t a c k618** TOOLS619** Display the parameter stack (code for ".s")620**************************************************************************/621static void displayPStack(FICL_VM *pVM)622{623FICL_STACK *pStk = pVM->pStack;624int d = stackDepth(pStk);625int i;626CELL *pCell;627628vmCheckStack(pVM, 0, 0);629630if (d == 0)631vmTextOut(pVM, "(Stack Empty) ", 0);632else633{634pCell = pStk->base;635for (i = 0; i < d; i++)636{637vmTextOut(pVM, ltoa((*pCell++).i, pVM->pad, pVM->base), 0);638vmTextOut(pVM, " ", 0);639}640}641return;642}643644645static void displayRStack(FICL_VM *pVM)646{647FICL_STACK *pStk = pVM->rStack;648int d = stackDepth(pStk);649int i;650CELL *pCell;651FICL_DICT *dp = vmGetDict(pVM);652653vmCheckStack(pVM, 0, 0);654655if (d == 0)656vmTextOut(pVM, "(Stack Empty) ", 0);657else658{659pCell = pStk->base;660for (i = 0; i < d; i++)661{662CELL c = *pCell++;663/*664** Attempt to find the word that contains the665** stacked address (as if it is part of a colon definition).666** If this works, print the name of the word. Otherwise print667** the value as a number.668*/669if (dictIncludes(dp, c.p))670{671FICL_WORD *pFW = findEnclosingWord(pVM, c.p);672if (pFW)673{674int offset = (CELL *)c.p - &pFW->param[0];675sprintf(pVM->pad, "%s+%d ", pFW->name, offset);676vmTextOut(pVM, pVM->pad, 0);677continue; /* no need to print the numeric value */678}679}680vmTextOut(pVM, ltoa(c.i, pVM->pad, pVM->base), 0);681vmTextOut(pVM, " ", 0);682}683}684685return;686}687688689/**************************************************************************690f o r g e t - w i d691**692**************************************************************************/693static void forgetWid(FICL_VM *pVM)694{695FICL_DICT *pDict = vmGetDict(pVM);696FICL_HASH *pHash;697698pHash = (FICL_HASH *)stackPopPtr(pVM->pStack);699hashForget(pHash, pDict->here);700701return;702}703704705/**************************************************************************706f o r g e t707** TOOLS EXT ( "<spaces>name" -- )708** Skip leading space delimiters. Parse name delimited by a space.709** Find name, then delete name from the dictionary along with all710** words added to the dictionary after name. An ambiguous711** condition exists if name cannot be found.712**713** If the Search-Order word set is present, FORGET searches the714** compilation word list. An ambiguous condition exists if the715** compilation word list is deleted.716**************************************************************************/717static void forget(FICL_VM *pVM)718{719void *where;720FICL_DICT *pDict = vmGetDict(pVM);721FICL_HASH *pHash = pDict->pCompile;722723ficlTick(pVM);724where = ((FICL_WORD *)stackPopPtr(pVM->pStack))->name;725hashForget(pHash, where);726pDict->here = PTRtoCELL where;727728return;729}730731732/**************************************************************************733l i s t W o r d s734**735**************************************************************************/736#define nCOLWIDTH 8737static void listWords(FICL_VM *pVM)738{739FICL_DICT *dp = vmGetDict(pVM);740FICL_HASH *pHash = dp->pSearch[dp->nLists - 1];741FICL_WORD *wp;742int nChars = 0;743int len;744int y = 0;745unsigned i;746int nWords = 0;747char *cp;748char *pPad = pVM->pad;749750for (i = 0; i < pHash->size; i++)751{752for (wp = pHash->table[i]; wp != NULL; wp = wp->link, nWords++)753{754if (wp->nName == 0) /* ignore :noname defs */755continue;756757cp = wp->name;758nChars += sprintf(pPad + nChars, "%s", cp);759760if (nChars > 70)761{762pPad[nChars] = '\0';763nChars = 0;764y++;765if(y>23) {766y=0;767vmTextOut(pVM, "--- Press Enter to continue ---",0);768getchar();769vmTextOut(pVM,"\r",0);770}771vmTextOut(pVM, pPad, 1);772}773else774{775len = nCOLWIDTH - nChars % nCOLWIDTH;776while (len-- > 0)777pPad[nChars++] = ' ';778}779780if (nChars > 70)781{782pPad[nChars] = '\0';783nChars = 0;784y++;785if(y>23) {786y=0;787vmTextOut(pVM, "--- Press Enter to continue ---",0);788getchar();789vmTextOut(pVM,"\r",0);790}791vmTextOut(pVM, pPad, 1);792}793}794}795796if (nChars > 0)797{798pPad[nChars] = '\0';799nChars = 0;800vmTextOut(pVM, pPad, 1);801}802803sprintf(pVM->pad, "Dictionary: %d words, %ld cells used of %u total",804nWords, (long) (dp->here - dp->dict), dp->size);805vmTextOut(pVM, pVM->pad, 1);806return;807}808809810/**************************************************************************811l i s t E n v812** Print symbols defined in the environment813**************************************************************************/814static void listEnv(FICL_VM *pVM)815{816FICL_DICT *dp = pVM->pSys->envp;817FICL_HASH *pHash = dp->pForthWords;818FICL_WORD *wp;819unsigned i;820int nWords = 0;821822for (i = 0; i < pHash->size; i++)823{824for (wp = pHash->table[i]; wp != NULL; wp = wp->link, nWords++)825{826vmTextOut(pVM, wp->name, 1);827}828}829830sprintf(pVM->pad, "Environment: %d words, %ld cells used of %u total",831nWords, (long) (dp->here - dp->dict), dp->size);832vmTextOut(pVM, pVM->pad, 1);833return;834}835836837/**************************************************************************838e n v C o n s t a n t839** Ficl interface to ficlSetEnv and ficlSetEnvD - allow ficl code to set840** environment constants...841**************************************************************************/842static void envConstant(FICL_VM *pVM)843{844unsigned value;845846#if FICL_ROBUST > 1847vmCheckStack(pVM, 1, 0);848#endif849850vmGetWordToPad(pVM);851value = POPUNS();852ficlSetEnv(pVM->pSys, pVM->pad, (FICL_UNS)value);853return;854}855856static void env2Constant(FICL_VM *pVM)857{858unsigned v1, v2;859860#if FICL_ROBUST > 1861vmCheckStack(pVM, 2, 0);862#endif863864vmGetWordToPad(pVM);865v2 = POPUNS();866v1 = POPUNS();867ficlSetEnvD(pVM->pSys, pVM->pad, v1, v2);868return;869}870871872/**************************************************************************873f i c l C o m p i l e T o o l s874** Builds wordset for debugger and TOOLS optional word set875**************************************************************************/876877void ficlCompileTools(FICL_SYSTEM *pSys)878{879FICL_DICT *dp = pSys->dp;880assert (dp);881882/*883** TOOLS and TOOLS EXT884*/885dictAppendWord(dp, ".s", displayPStack, FW_DEFAULT);886dictAppendWord(dp, "bye", bye, FW_DEFAULT);887dictAppendWord(dp, "forget", forget, FW_DEFAULT);888dictAppendWord(dp, "see", see, FW_DEFAULT);889dictAppendWord(dp, "words", listWords, FW_DEFAULT);890891/*892** Set TOOLS environment query values893*/894ficlSetEnv(pSys, "tools", FICL_TRUE);895ficlSetEnv(pSys, "tools-ext", FICL_FALSE);896897/*898** Ficl extras899*/900dictAppendWord(dp, "r.s", displayRStack, FW_DEFAULT); /* guy carver */901dictAppendWord(dp, ".env", listEnv, FW_DEFAULT);902dictAppendWord(dp, "env-constant",903envConstant, FW_DEFAULT);904dictAppendWord(dp, "env-2constant",905env2Constant, FW_DEFAULT);906dictAppendWord(dp, "debug-xt", ficlDebugXT, FW_DEFAULT);907dictAppendWord(dp, "parse-order",908ficlListParseSteps,909FW_DEFAULT);910dictAppendWord(dp, "step-break",stepBreak, FW_DEFAULT);911dictAppendWord(dp, "forget-wid",forgetWid, FW_DEFAULT);912dictAppendWord(dp, "see-xt", seeXT, FW_DEFAULT);913914return;915}916917918919