tools.c revision 76116
1/******************************************************************* 2** t o o l s . c 3** Forth Inspired Command Language - programming tools 4** Author: John Sadler (john_sadler@alum.mit.edu) 5** Created: 20 June 2000 6** $Id: tools.c,v 1.4 2001-04-26 21:41:24-07 jsadler Exp jsadler $ 7*******************************************************************/ 8/* 9** NOTES: 10** SEE needs information about the addresses of functions that 11** are the CFAs of colon definitions, constants, variables, DOES> 12** words, and so on. It gets this information from a table and supporting 13** functions in words.c. 14** colonParen doDoes createParen variableParen userParen constantParen 15** 16** Step and break debugger for Ficl 17** debug ( xt -- ) Start debugging an xt 18** Set a breakpoint 19** Specify breakpoint default action 20*/ 21/* 22** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu) 23** All rights reserved. 24** 25** Get the latest Ficl release at http://ficl.sourceforge.net 26** 27** L I C E N S E and D I S C L A I M E R 28** 29** Redistribution and use in source and binary forms, with or without 30** modification, are permitted provided that the following conditions 31** are met: 32** 1. Redistributions of source code must retain the above copyright 33** notice, this list of conditions and the following disclaimer. 34** 2. Redistributions in binary form must reproduce the above copyright 35** notice, this list of conditions and the following disclaimer in the 36** documentation and/or other materials provided with the distribution. 37** 38** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 39** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 40** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 41** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 42** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 43** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 44** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 45** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 46** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 47** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 48** SUCH DAMAGE. 49** 50** I am interested in hearing from anyone who uses ficl. If you have 51** a problem, a success story, a defect, an enhancement request, or 52** if you would like to contribute to the ficl release, please send 53** contact me by email at the address above. 54** 55** $Id: tools.c,v 1.4 2001-04-26 21:41:24-07 jsadler Exp jsadler $ 56*/ 57 58/* $FreeBSD: head/sys/boot/ficl/tools.c 76116 2001-04-29 02:36:36Z dcs $ */ 59 60#ifdef TESTMAIN 61#include <stdlib.h> 62#include <stdio.h> /* sprintf */ 63#include <ctype.h> 64#else 65#include <stand.h> 66#endif 67#include <string.h> 68#include "ficl.h" 69 70 71#if 0 72/* 73** nBREAKPOINTS sizes the breakpoint array. One breakpoint (bp 0) is reserved 74** for the STEP command. The rest are user programmable. 75*/ 76#define nBREAKPOINTS 32 77#endif 78 79/* 80** BREAKPOINT record. 81** origXT - if NULL, this breakpoint is unused. Otherwise it stores the xt 82** that the breakpoint overwrote. This is restored to the dictionary when the 83** BP executes or gets cleared 84** address - the location of the breakpoint (address of the instruction that 85** has been replaced with the breakpoint trap 86** origXT - The original contents of the location with the breakpoint 87** Note: address is NULL when this breakpoint is empty 88*/ 89typedef struct breakpoint 90{ 91 void *address; 92 FICL_WORD *origXT; 93} BREAKPOINT; 94 95static BREAKPOINT bpStep = {NULL, NULL}; 96 97/* 98** vmSetBreak - set a breakpoint at the current value of IP by 99** storing that address in a BREAKPOINT record 100*/ 101static void vmSetBreak(FICL_VM *pVM, BREAKPOINT *pBP) 102{ 103 FICL_WORD *pStep = ficlLookup("step-break"); 104 assert(pStep); 105 pBP->address = pVM->ip; 106 pBP->origXT = *pVM->ip; 107 *pVM->ip = pStep; 108} 109 110 111/* 112** isAFiclWord 113** Vet a candidate pointer carefully to make sure 114** it's not some chunk o' inline data... 115** It has to have a name, and it has to look 116** like it's in the dictionary address range. 117** NOTE: this excludes :noname words! 118*/ 119int isAFiclWord(FICL_WORD *pFW) 120{ 121 FICL_DICT *pd = ficlGetDict(); 122 123 if (!dictIncludes(pd, pFW)) 124 return 0; 125 126 if (!dictIncludes(pd, pFW->name)) 127 return 0; 128 129 return ((pFW->nName > 0) && (pFW->name[pFW->nName] == '\0')); 130} 131 132 133static int isPrimitive(FICL_WORD *pFW) 134{ 135 WORDKIND wk = ficlWordClassify(pFW); 136 return ((wk != COLON) && (wk != DOES)); 137} 138 139 140/************************************************************************** 141 s e e 142** TOOLS ( "<spaces>name" -- ) 143** Display a human-readable representation of the named word's definition. 144** The source of the representation (object-code decompilation, source 145** block, etc.) and the particular form of the display is implementation 146** defined. 147** NOTE: these funcs come late in the file because they reference all 148** of the word-builder funcs without declaring them again. Call me lazy. 149**************************************************************************/ 150/* 151** seeColon (for proctologists only) 152** Walks a colon definition, decompiling 153** on the fly. Knows about primitive control structures. 154*/ 155static void seeColon(FICL_VM *pVM, CELL *pc) 156{ 157 static FICL_WORD *pSemiParen = NULL; 158 159 if (!pSemiParen) 160 pSemiParen = ficlLookup("(;)"); 161 assert(pSemiParen); 162 163 for (; pc->p != pSemiParen; pc++) 164 { 165 FICL_WORD *pFW = (FICL_WORD *)(pc->p); 166 167 if (isAFiclWord(pFW)) 168 { 169 WORDKIND kind = ficlWordClassify(pFW); 170 CELL c; 171 172 switch (kind) 173 { 174 case LITERAL: 175 c = *++pc; 176 if (isAFiclWord(c.p)) 177 { 178 FICL_WORD *pLit = (FICL_WORD *)c.p; 179 sprintf(pVM->pad, " literal %.*s (%#lx)", 180 pLit->nName, pLit->name, c.u); 181 } 182 else 183 sprintf(pVM->pad, " literal %ld (%#lx)", c.i, c.u); 184 break; 185 case STRINGLIT: 186 { 187 FICL_STRING *sp = (FICL_STRING *)(void *)++pc; 188 pc = (CELL *)alignPtr(sp->text + sp->count + 1) - 1; 189 sprintf(pVM->pad, " s\" %.*s\"", sp->count, sp->text); 190 } 191 break; 192 case IF: 193 c = *++pc; 194 if (c.i > 0) 195 sprintf(pVM->pad, " if / while (branch rel %ld)", c.i); 196 else 197 sprintf(pVM->pad, " until (branch rel %ld)", c.i); 198 break; 199 case BRANCH: 200 c = *++pc; 201 if (c.i > 0) 202 sprintf(pVM->pad, " else (branch rel %ld)", c.i); 203 else 204 sprintf(pVM->pad, " repeat (branch rel %ld)", c.i); 205 break; 206 207 case QDO: 208 c = *++pc; 209 sprintf(pVM->pad, " ?do (leave abs %#lx)", c.u); 210 break; 211 case DO: 212 c = *++pc; 213 sprintf(pVM->pad, " do (leave abs %#lx)", c.u); 214 break; 215 case LOOP: 216 c = *++pc; 217 sprintf(pVM->pad, " loop (branch rel %#ld)", c.i); 218 break; 219 case PLOOP: 220 c = *++pc; 221 sprintf(pVM->pad, " +loop (branch rel %#ld)", c.i); 222 break; 223 default: 224 sprintf(pVM->pad, " %.*s", pFW->nName, pFW->name); 225 break; 226 } 227 228 vmTextOut(pVM, pVM->pad, 1); 229 } 230 else /* probably not a word - punt and print value */ 231 { 232 sprintf(pVM->pad, " %ld (%#lx)", pc->i, pc->u); 233 vmTextOut(pVM, pVM->pad, 1); 234 } 235 } 236 237 vmTextOut(pVM, ";", 1); 238} 239 240/* 241** Here's the outer part of the decompiler. It's 242** just a big nested conditional that checks the 243** CFA of the word to decompile for each kind of 244** known word-builder code, and tries to do 245** something appropriate. If the CFA is not recognized, 246** just indicate that it is a primitive. 247*/ 248static void seeXT(FICL_VM *pVM) 249{ 250 FICL_WORD *pFW; 251 WORDKIND kind; 252 253 pFW = (FICL_WORD *)stackPopPtr(pVM->pStack); 254 kind = ficlWordClassify(pFW); 255 256 switch (kind) 257 { 258 case COLON: 259 sprintf(pVM->pad, ": %.*s", pFW->nName, pFW->name); 260 vmTextOut(pVM, pVM->pad, 1); 261 seeColon(pVM, pFW->param); 262 break; 263 264 case DOES: 265 vmTextOut(pVM, "does>", 1); 266 seeColon(pVM, (CELL *)pFW->param->p); 267 break; 268 269 case CREATE: 270 vmTextOut(pVM, "create", 1); 271 break; 272 273 case VARIABLE: 274 sprintf(pVM->pad, "variable = %ld (%#lx)", pFW->param->i, pFW->param->u); 275 vmTextOut(pVM, pVM->pad, 1); 276 break; 277 278 case USER: 279 sprintf(pVM->pad, "user variable %ld (%#lx)", pFW->param->i, pFW->param->u); 280 vmTextOut(pVM, pVM->pad, 1); 281 break; 282 283 case CONSTANT: 284 sprintf(pVM->pad, "constant = %ld (%#lx)", pFW->param->i, pFW->param->u); 285 vmTextOut(pVM, pVM->pad, 1); 286 287 default: 288 vmTextOut(pVM, "primitive", 1); 289 break; 290 } 291 292 if (pFW->flags & FW_IMMEDIATE) 293 { 294 vmTextOut(pVM, "immediate", 1); 295 } 296 297 if (pFW->flags & FW_COMPILE) 298 { 299 vmTextOut(pVM, "compile-only", 1); 300 } 301 302 return; 303} 304 305 306static void see(FICL_VM *pVM) 307{ 308 ficlTick(pVM); 309 seeXT(pVM); 310 return; 311} 312 313 314/************************************************************************** 315 f i c l D e b u g X T 316** debug ( xt -- ) 317** Given an xt of a colon definition or a word defined by DOES>, set the 318** VM up to debug the word: push IP, set the xt as the next thing to execute, 319** set a breakpoint at its first instruction, and run to the breakpoint. 320** Note: the semantics of this word are equivalent to "step in" 321**************************************************************************/ 322void ficlDebugXT(FICL_VM *pVM) 323{ 324 FICL_WORD *xt = stackPopPtr(pVM->pStack); 325 WORDKIND wk = ficlWordClassify(xt); 326 FICL_WORD *pStep = ficlLookup("step-break"); 327 328 assert(pStep); 329 330 stackPushPtr(pVM->pStack, xt); 331 seeXT(pVM); 332 333 switch (wk) 334 { 335 case COLON: 336 case DOES: 337 /* 338 ** Run the colon code and set a breakpoint at the next instruction 339 */ 340 vmExecute(pVM, xt); 341 bpStep.address = pVM->ip; 342 bpStep.origXT = *pVM->ip; 343 *pVM->ip = pStep; 344 break; 345 346 default: 347 vmExecute(pVM, xt); 348 break; 349 } 350 351 return; 352} 353 354 355/************************************************************************** 356 s t e p I n 357** FICL 358** Execute the next instruction, stepping into it if it's a colon definition 359** or a does> word. This is the easy kind of step. 360**************************************************************************/ 361void stepIn(FICL_VM *pVM) 362{ 363 /* 364 ** Do one step of the inner loop 365 */ 366 { 367 M_VM_STEP(pVM) 368 } 369 370 /* 371 ** Now set a breakpoint at the next instruction 372 */ 373 vmSetBreak(pVM, &bpStep); 374 375 return; 376} 377 378 379/************************************************************************** 380 s t e p O v e r 381** FICL 382** Execute the next instruction atomically. This requires some insight into 383** the memory layout of compiled code. Set a breakpoint at the next instruction 384** in this word, and run until we hit it 385**************************************************************************/ 386void stepOver(FICL_VM *pVM) 387{ 388 FICL_WORD *pFW; 389 WORDKIND kind; 390 FICL_WORD *pStep = ficlLookup("step-break"); 391 assert(pStep); 392 393 pFW = *pVM->ip; 394 kind = ficlWordClassify(pFW); 395 396 switch (kind) 397 { 398 case COLON: 399 case DOES: 400 /* 401 ** assume that the next cell holds an instruction 402 ** set a breakpoint there and return to the inner interp 403 */ 404 bpStep.address = pVM->ip + 1; 405 bpStep.origXT = pVM->ip[1]; 406 pVM->ip[1] = pStep; 407 break; 408 409 default: 410 stepIn(pVM); 411 break; 412 } 413 414 return; 415} 416 417 418/************************************************************************** 419 s t e p - b r e a k 420** FICL 421** Handles breakpoints for stepped execution. 422** Upon entry, bpStep contains the address and replaced instruction 423** of the current breakpoint. 424** Clear the breakpoint 425** Get a command from the console. 426** i (step in) - execute the current instruction and set a new breakpoint 427** at the IP 428** o (step over) - execute the current instruction to completion and set 429** a new breakpoint at the IP 430** g (go) - execute the current instruction and exit 431** q (quit) - abort current word 432** b (toggle breakpoint) 433**************************************************************************/ 434void stepBreak(FICL_VM *pVM) 435{ 436 STRINGINFO si; 437 FICL_WORD *pFW; 438 FICL_WORD *pOnStep; 439 440 if (!pVM->fRestart) 441 { 442 443 assert(bpStep.address != NULL); 444 /* 445 ** Clear the breakpoint that caused me to run 446 ** Restore the original instruction at the breakpoint, 447 ** and restore the IP 448 */ 449 assert(bpStep.address); 450 assert(bpStep.origXT); 451 452 pVM->ip = (IPTYPE)bpStep.address; 453 *pVM->ip = bpStep.origXT; 454 455 /* 456 ** If there's an onStep, do it 457 */ 458 pOnStep = ficlLookup("on-step"); 459 if (pOnStep) 460 ficlExecXT(pVM, pOnStep); 461 462 /* 463 ** Print the name of the next instruction 464 */ 465 pFW = bpStep.origXT; 466 sprintf(pVM->pad, "next: %.*s", pFW->nName, pFW->name); 467 if (isPrimitive(pFW)) 468 { 469 strcat(pVM->pad, " primitive"); 470 } 471 472 vmTextOut(pVM, pVM->pad, 1); 473 } 474 else 475 { 476 pVM->fRestart = 0; 477 } 478 479 si = vmGetWord(pVM); 480 481 if (!strincmp(si.cp, "i", si.count)) 482 { 483 stepIn(pVM); 484 } 485 else if (!strincmp(si.cp, "g", si.count)) 486 { 487 return; 488 } 489 else if (!strincmp(si.cp, "o", si.count)) 490 { 491 stepOver(pVM); 492 } 493 else if (!strincmp(si.cp, "q", si.count)) 494 { 495 vmThrow(pVM, VM_ABORT); 496 } 497 else 498 { 499 vmTextOut(pVM, "i -- step In", 1); 500 vmTextOut(pVM, "o -- step Over", 1); 501 vmTextOut(pVM, "g -- Go (execute to completion)", 1); 502 vmTextOut(pVM, "q -- Quit (stop debugging and abort)", 1); 503 vmTextOut(pVM, "x -- eXecute a single word", 1); 504 vmThrow(pVM, VM_RESTART); 505 } 506 507 return; 508} 509 510 511/************************************************************************** 512 b y e 513** TOOLS 514** Signal the system to shut down - this causes ficlExec to return 515** VM_USEREXIT. The rest is up to you. 516**************************************************************************/ 517static void bye(FICL_VM *pVM) 518{ 519 vmThrow(pVM, VM_USEREXIT); 520 return; 521} 522 523 524/************************************************************************** 525 d i s p l a y S t a c k 526** TOOLS 527** Display the parameter stack (code for ".s") 528**************************************************************************/ 529static void displayStack(FICL_VM *pVM) 530{ 531 int d = stackDepth(pVM->pStack); 532 int i; 533 CELL *pCell; 534 535 vmCheckStack(pVM, 0, 0); 536 537 if (d == 0) 538 vmTextOut(pVM, "(Stack Empty) ", 0); 539 else 540 { 541 pCell = pVM->pStack->base; 542 for (i = 0; i < d; i++) 543 { 544 vmTextOut(pVM, ltoa((*pCell++).i, pVM->pad, pVM->base), 0); 545 vmTextOut(pVM, " ", 0); 546 } 547 } 548} 549 550 551static void displayRStack(FICL_VM *pVM) 552{ 553 int d = stackDepth(pVM->rStack); 554 int i; 555 CELL *pCell; 556 557 vmTextOut(pVM, "Return Stack: ", 0); 558 if (d == 0) 559 vmTextOut(pVM, "Empty ", 0); 560 else 561 { 562 pCell = pVM->rStack->base; 563 for (i = 0; i < d; i++) 564 { 565 vmTextOut(pVM, ultoa((*pCell++).i, pVM->pad, 16), 0); 566 vmTextOut(pVM, " ", 0); 567 } 568 } 569} 570 571 572/************************************************************************** 573 f o r g e t - w i d 574** 575**************************************************************************/ 576static void forgetWid(FICL_VM *pVM) 577{ 578 FICL_DICT *pDict = ficlGetDict(); 579 FICL_HASH *pHash; 580 581 pHash = (FICL_HASH *)stackPopPtr(pVM->pStack); 582 hashForget(pHash, pDict->here); 583 584 return; 585} 586 587 588/************************************************************************** 589 f o r g e t 590** TOOLS EXT ( "<spaces>name" -- ) 591** Skip leading space delimiters. Parse name delimited by a space. 592** Find name, then delete name from the dictionary along with all 593** words added to the dictionary after name. An ambiguous 594** condition exists if name cannot be found. 595** 596** If the Search-Order word set is present, FORGET searches the 597** compilation word list. An ambiguous condition exists if the 598** compilation word list is deleted. 599**************************************************************************/ 600static void forget(FICL_VM *pVM) 601{ 602 void *where; 603 FICL_DICT *pDict = ficlGetDict(); 604 FICL_HASH *pHash = pDict->pCompile; 605 606 ficlTick(pVM); 607 where = ((FICL_WORD *)stackPopPtr(pVM->pStack))->name; 608 hashForget(pHash, where); 609 pDict->here = PTRtoCELL where; 610 611 return; 612} 613 614 615/************************************************************************** 616 l i s t W o r d s 617** 618**************************************************************************/ 619#define nCOLWIDTH 8 620static void listWords(FICL_VM *pVM) 621{ 622 FICL_DICT *dp = ficlGetDict(); 623 FICL_HASH *pHash = dp->pSearch[dp->nLists - 1]; 624 FICL_WORD *wp; 625 int nChars = 0; 626 int len; 627 int y = 0; 628 unsigned i; 629 int nWords = 0; 630 char *cp; 631 char *pPad = pVM->pad; 632 633 for (i = 0; i < pHash->size; i++) 634 { 635 for (wp = pHash->table[i]; wp != NULL; wp = wp->link, nWords++) 636 { 637 if (wp->nName == 0) /* ignore :noname defs */ 638 continue; 639 640 cp = wp->name; 641 nChars += sprintf(pPad + nChars, "%s", cp); 642 643 if (nChars > 70) 644 { 645 pPad[nChars] = '\0'; 646 nChars = 0; 647 y++; 648 if(y>23) { 649 y=0; 650 vmTextOut(pVM, "--- Press Enter to continue ---",0); 651 getchar(); 652 vmTextOut(pVM,"\r",0); 653 } 654 vmTextOut(pVM, pPad, 1); 655 } 656 else 657 { 658 len = nCOLWIDTH - nChars % nCOLWIDTH; 659 while (len-- > 0) 660 pPad[nChars++] = ' '; 661 } 662 663 if (nChars > 70) 664 { 665 pPad[nChars] = '\0'; 666 nChars = 0; 667 y++; 668 if(y>23) { 669 y=0; 670 vmTextOut(pVM, "--- Press Enter to continue ---",0); 671 getchar(); 672 vmTextOut(pVM,"\r",0); 673 } 674 vmTextOut(pVM, pPad, 1); 675 } 676 } 677 } 678 679 if (nChars > 0) 680 { 681 pPad[nChars] = '\0'; 682 nChars = 0; 683 vmTextOut(pVM, pPad, 1); 684 } 685 686 sprintf(pVM->pad, "Dictionary: %d words, %ld cells used of %u total", 687 nWords, (long) (dp->here - dp->dict), dp->size); 688 vmTextOut(pVM, pVM->pad, 1); 689 return; 690} 691 692 693/************************************************************************** 694 l i s t E n v 695** Print symbols defined in the environment 696**************************************************************************/ 697static void listEnv(FICL_VM *pVM) 698{ 699 FICL_DICT *dp = ficlGetEnv(); 700 FICL_HASH *pHash = dp->pForthWords; 701 FICL_WORD *wp; 702 unsigned i; 703 int nWords = 0; 704 705 for (i = 0; i < pHash->size; i++) 706 { 707 for (wp = pHash->table[i]; wp != NULL; wp = wp->link, nWords++) 708 { 709 vmTextOut(pVM, wp->name, 1); 710 } 711 } 712 713 sprintf(pVM->pad, "Environment: %d words, %ld cells used of %u total", 714 nWords, (long) (dp->here - dp->dict), dp->size); 715 vmTextOut(pVM, pVM->pad, 1); 716 return; 717} 718 719 720/************************************************************************** 721 e n v C o n s t a n t 722** Ficl interface to ficlSetEnv and ficlSetEnvD - allow ficl code to set 723** environment constants... 724**************************************************************************/ 725static void envConstant(FICL_VM *pVM) 726{ 727 unsigned value; 728 729#if FICL_ROBUST > 1 730 vmCheckStack(pVM, 1, 0); 731#endif 732 733 vmGetWordToPad(pVM); 734 value = POPUNS(); 735 ficlSetEnv(pVM->pad, (FICL_UNS)value); 736 return; 737} 738 739static void env2Constant(FICL_VM *pVM) 740{ 741 unsigned v1, v2; 742 743#if FICL_ROBUST > 1 744 vmCheckStack(pVM, 2, 0); 745#endif 746 747 vmGetWordToPad(pVM); 748 v2 = POPUNS(); 749 v1 = POPUNS(); 750 ficlSetEnvD(pVM->pad, v1, v2); 751 return; 752} 753 754 755/************************************************************************** 756 f i c l C o m p i l e T o o l s 757** Builds wordset for debugger and TOOLS optional word set 758**************************************************************************/ 759 760void ficlCompileTools(FICL_SYSTEM *pSys) 761{ 762 FICL_DICT *dp = pSys->dp; 763 assert (dp); 764 765 /* 766 ** TOOLS and TOOLS EXT 767 */ 768 dictAppendWord(dp, ".r", displayRStack, FW_DEFAULT); /* guy carver */ 769 dictAppendWord(dp, ".s", displayStack, FW_DEFAULT); 770 dictAppendWord(dp, "bye", bye, FW_DEFAULT); 771 dictAppendWord(dp, "forget", forget, FW_DEFAULT); 772 dictAppendWord(dp, "see", see, FW_DEFAULT); 773 dictAppendWord(dp, "words", listWords, FW_DEFAULT); 774 775 /* 776 ** Set TOOLS environment query values 777 */ 778 ficlSetEnv("tools", FICL_TRUE); 779 ficlSetEnv("tools-ext", FICL_FALSE); 780 781 /* 782 ** Ficl extras 783 */ 784 dictAppendWord(dp, ".env", listEnv, FW_DEFAULT); 785 dictAppendWord(dp, "env-constant", 786 envConstant, FW_DEFAULT); 787 dictAppendWord(dp, "env-2constant", 788 env2Constant, FW_DEFAULT); 789 dictAppendWord(dp, "debug-xt", ficlDebugXT, FW_DEFAULT); 790 dictAppendWord(dp, "parse-order", 791 ficlListParseSteps, 792 FW_DEFAULT); 793 dictAppendWord(dp, "step-break",stepBreak, FW_DEFAULT); 794 dictAppendWord(dp, "forget-wid",forgetWid, FW_DEFAULT); 795 dictAppendWord(dp, "see-xt", seeXT, FW_DEFAULT); 796 dictAppendWord(dp, ".r", displayRStack, FW_DEFAULT); 797 798 return; 799} 800 801