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.12 2010/08/12 13:57:22 asau Exp $ 7 */ 8 /* 9 * Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu) 10 * All rights reserved. 11 * 12 * Get the latest Ficl release at http://ficl.sourceforge.net 13 * 14 * I am interested in hearing from anyone who uses Ficl. If you have 15 * a problem, a success story, a defect, an enhancement request, or 16 * if you would like to contribute to the Ficl release, please 17 * contact me by email at the address above. 18 * 19 * L I C E N S E and D I S C L A I M E R 20 * 21 * Redistribution and use in source and binary forms, with or without 22 * modification, are permitted provided that the following conditions 23 * are met: 24 * 1. Redistributions of source code must retain the above copyright 25 * notice, this list of conditions and the following disclaimer. 26 * 2. Redistributions in binary form must reproduce the above copyright 27 * notice, this list of conditions and the following disclaimer in the 28 * documentation and/or other materials provided with the distribution. 29 * 30 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 * SUCH DAMAGE. 41 */ 42 43 /* 44 * NOTES: 45 * SEE needs information about the addresses of functions that 46 * are the CFAs of colon definitions, constants, variables, DOES> 47 * words, and so on. It gets this information from a table and supporting 48 * functions in words.c. 49 * fiColonParen fiDoDoes createParen fiVariableParen fiUserParen fiConstantParen 50 * 51 * Step and break debugger for Ficl 52 * debug ( xt -- ) Start debugging an xt 53 * Set a breakpoint 54 * Specify breakpoint default action 55 */ 56 57 #include <stdbool.h> 58 #include "ficl.h" 59 60 extern void exit(int); 61 62 static void ficlPrimitiveStepIn(ficlVm *vm); 63 static void ficlPrimitiveStepOver(ficlVm *vm); 64 static void ficlPrimitiveStepBreak(ficlVm *vm); 65 66 void 67 ficlCallbackAssert(ficlCallback *callback, int expression, 68 char *expressionString, char *filename, int line) 69 { 70 #if FICL_ROBUST >= 1 71 if (!expression) { 72 static char buffer[256]; 73 (void) sprintf(buffer, "ASSERTION FAILED at %s:%d: \"%s\"\n", 74 filename, line, expressionString); 75 ficlCallbackTextOut(callback, buffer); 76 exit(-1); 77 } 78 #else /* FICL_ROBUST >= 1 */ 79 FICL_IGNORE(callback); 80 FICL_IGNORE(expression); 81 FICL_IGNORE(expressionString); 82 FICL_IGNORE(filename); 83 FICL_IGNORE(line); 84 #endif /* FICL_ROBUST >= 1 */ 85 } 86 87 /* 88 * v m S e t B r e a k 89 * Set a breakpoint at the current value of IP by 90 * storing that address in a BREAKPOINT record 91 */ 92 static void 93 ficlVmSetBreak(ficlVm *vm, ficlBreakpoint *pBP) 94 { 95 ficlWord *pStep = ficlSystemLookup(vm->callback.system, "step-break"); 96 FICL_VM_ASSERT(vm, pStep); 97 98 pBP->address = vm->ip; 99 pBP->oldXT = *vm->ip; 100 *vm->ip = pStep; 101 } 102 103 /* 104 * d e b u g P r o m p t 105 */ 106 static void 107 ficlDebugPrompt(bool debug) 108 { 109 if (debug) 110 (void) setenv("prompt", "dbg> ", 1); 111 else 112 (void) setenv("prompt", "${interpret}", 1); 113 } 114 115 #if 0 116 static int 117 isPrimitive(ficlWord *word) 118 { 119 ficlWordKind wk = ficlWordClassify(word); 120 return ((wk != COLON) && (wk != DOES)); 121 } 122 #endif 123 124 /* 125 * d i c t H a s h S u m m a r y 126 * Calculate a figure of merit for the dictionary hash table based 127 * on the average search depth for all the words in the dictionary, 128 * assuming uniform distribution of target keys. The figure of merit 129 * is the ratio of the total search depth for all keys in the table 130 * versus a theoretical optimum that would be achieved if the keys 131 * were distributed into the table as evenly as possible. 132 * The figure would be worse if the hash table used an open 133 * addressing scheme (i.e. collisions resolved by searching the 134 * table for an empty slot) for a given size table. 135 */ 136 #if FICL_WANT_FLOAT 137 void 138 ficlPrimitiveHashSummary(ficlVm *vm) 139 { 140 ficlDictionary *dictionary = ficlVmGetDictionary(vm); 141 ficlHash *pFHash; 142 ficlWord **hash; 143 unsigned size; 144 ficlWord *word; 145 unsigned i; 146 int nMax = 0; 147 int nWords = 0; 148 int nFilled; 149 double avg = 0.0; 150 double best; 151 int nAvg, nRem, nDepth; 152 153 FICL_VM_DICTIONARY_CHECK(vm, dictionary, 0); 154 155 pFHash = dictionary->wordlists[dictionary->wordlistCount - 1]; 156 hash = pFHash->table; 157 size = pFHash->size; 158 nFilled = size; 159 160 for (i = 0; i < size; i++) { 161 int n = 0; 162 word = hash[i]; 163 164 while (word) { 165 ++n; 166 ++nWords; 167 word = word->link; 168 } 169 170 avg += (double)(n * (n+1)) / 2.0; 171 172 if (n > nMax) 173 nMax = n; 174 if (n == 0) 175 --nFilled; 176 } 177 178 /* Calc actual avg search depth for this hash */ 179 avg = avg / nWords; 180 181 /* Calc best possible performance with this size hash */ 182 nAvg = nWords / size; 183 nRem = nWords % size; 184 nDepth = size * (nAvg * (nAvg+1))/2 + (nAvg+1)*nRem; 185 best = (double)nDepth/nWords; 186 187 (void) sprintf(vm->pad, "%d bins, %2.0f%% filled, Depth: " 188 "Max=%d, Avg=%2.1f, Best=%2.1f, Score: %2.0f%%\n", 189 size, (double)nFilled * 100.0 / size, nMax, 190 avg, best, 100.0 * best / avg); 191 192 ficlVmTextOut(vm, vm->pad); 193 } 194 #endif 195 196 /* 197 * Here's the outer part of the decompiler. It's 198 * just a big nested conditional that checks the 199 * CFA of the word to decompile for each kind of 200 * known word-builder code, and tries to do 201 * something appropriate. If the CFA is not recognized, 202 * just indicate that it is a primitive. 203 */ 204 static void 205 ficlPrimitiveSeeXT(ficlVm *vm) 206 { 207 ficlWord *word; 208 ficlWordKind kind; 209 210 word = (ficlWord *)ficlStackPopPointer(vm->dataStack); 211 kind = ficlWordClassify(word); 212 213 switch (kind) { 214 case FICL_WORDKIND_COLON: 215 (void) sprintf(vm->pad, ": %.*s\n", word->length, word->name); 216 ficlVmTextOut(vm, vm->pad); 217 ficlDictionarySee(ficlVmGetDictionary(vm), word, 218 &(vm->callback)); 219 break; 220 case FICL_WORDKIND_DOES: 221 ficlVmTextOut(vm, "does>\n"); 222 ficlDictionarySee(ficlVmGetDictionary(vm), 223 (ficlWord *)word->param->p, &(vm->callback)); 224 break; 225 case FICL_WORDKIND_CREATE: 226 ficlVmTextOut(vm, "create\n"); 227 break; 228 case FICL_WORDKIND_VARIABLE: 229 (void) sprintf(vm->pad, "variable = %ld (%#lx)\n", 230 (long)word->param->i, (long unsigned)word->param->u); 231 ficlVmTextOut(vm, vm->pad); 232 break; 233 #if FICL_WANT_USER 234 case FICL_WORDKIND_USER: 235 (void) sprintf(vm->pad, "user variable %ld (%#lx)\n", 236 (long)word->param->i, (long unsigned)word->param->u); 237 ficlVmTextOut(vm, vm->pad); 238 break; 239 #endif 240 case FICL_WORDKIND_CONSTANT: 241 (void) sprintf(vm->pad, "constant = %ld (%#lx)\n", 242 (long)word->param->i, (long unsigned)word->param->u); 243 ficlVmTextOut(vm, vm->pad); 244 break; 245 case FICL_WORDKIND_2CONSTANT: 246 (void) sprintf(vm->pad, "constant = %ld %ld (%#lx %#lx)\n", 247 (long)word->param[1].i, (long)word->param->i, 248 (long unsigned)word->param[1].u, 249 (long unsigned)word->param->u); 250 ficlVmTextOut(vm, vm->pad); 251 break; 252 253 default: 254 (void) sprintf(vm->pad, "%.*s is a primitive\n", word->length, 255 word->name); 256 ficlVmTextOut(vm, vm->pad); 257 break; 258 } 259 260 if (word->flags & FICL_WORD_IMMEDIATE) { 261 ficlVmTextOut(vm, "immediate\n"); 262 } 263 264 if (word->flags & FICL_WORD_COMPILE_ONLY) { 265 ficlVmTextOut(vm, "compile-only\n"); 266 } 267 } 268 269 static void 270 ficlPrimitiveSee(ficlVm *vm) 271 { 272 ficlPrimitiveTick(vm); 273 ficlPrimitiveSeeXT(vm); 274 } 275 276 /* 277 * f i c l D e b u g X T 278 * debug ( xt -- ) 279 * Given an xt of a colon definition or a word defined by DOES>, set the 280 * VM up to debug the word: push IP, set the xt as the next thing to execute, 281 * set a breakpoint at its first instruction, and run to the breakpoint. 282 * Note: the semantics of this word are equivalent to "step in" 283 */ 284 static void 285 ficlPrimitiveDebugXT(ficlVm *vm) 286 { 287 ficlWord *xt = ficlStackPopPointer(vm->dataStack); 288 ficlWordKind wk = ficlWordClassify(xt); 289 290 ficlStackPushPointer(vm->dataStack, xt); 291 ficlPrimitiveSeeXT(vm); 292 293 switch (wk) { 294 case FICL_WORDKIND_COLON: 295 case FICL_WORDKIND_DOES: 296 /* 297 * Run the colon code and set a breakpoint at the next 298 * instruction 299 */ 300 ficlVmExecuteWord(vm, xt); 301 ficlVmSetBreak(vm, &(vm->callback.system->breakpoint)); 302 break; 303 default: 304 ficlVmExecuteWord(vm, xt); 305 break; 306 } 307 } 308 309 /* 310 * s t e p I n 311 * Ficl 312 * Execute the next instruction, stepping into it if it's a colon definition 313 * or a does> word. This is the easy kind of step. 314 */ 315 static void 316 ficlPrimitiveStepIn(ficlVm *vm) 317 { 318 /* 319 * Do one step of the inner loop 320 */ 321 ficlVmExecuteWord(vm, *vm->ip++); 322 323 /* 324 * Now set a breakpoint at the next instruction 325 */ 326 ficlVmSetBreak(vm, &(vm->callback.system->breakpoint)); 327 } 328 329 /* 330 * s t e p O v e r 331 * Ficl 332 * Execute the next instruction atomically. This requires some insight into 333 * the memory layout of compiled code. Set a breakpoint at the next instruction 334 * in this word, and run until we hit it 335 */ 336 static void 337 ficlPrimitiveStepOver(ficlVm *vm) 338 { 339 ficlWord *word; 340 ficlWordKind kind; 341 ficlWord *pStep = ficlSystemLookup(vm->callback.system, "step-break"); 342 FICL_VM_ASSERT(vm, pStep); 343 344 word = *vm->ip; 345 kind = ficlWordClassify(word); 346 347 switch (kind) { 348 case FICL_WORDKIND_COLON: 349 case FICL_WORDKIND_DOES: 350 /* 351 * assume that the next ficlCell holds an instruction 352 * set a breakpoint there and return to the inner interpreter 353 */ 354 vm->callback.system->breakpoint.address = vm->ip + 1; 355 vm->callback.system->breakpoint.oldXT = vm->ip[1]; 356 vm->ip[1] = pStep; 357 break; 358 default: 359 ficlPrimitiveStepIn(vm); 360 break; 361 } 362 } 363 364 /* 365 * s t e p - b r e a k 366 * Ficl 367 * Handles breakpoints for stepped execution. 368 * Upon entry, breakpoint contains the address and replaced instruction 369 * of the current breakpoint. 370 * Clear the breakpoint 371 * Get a command from the console. 372 * i (step in) - execute the current instruction and set a new breakpoint 373 * at the IP 374 * o (step over) - execute the current instruction to completion and set 375 * a new breakpoint at the IP 376 * g (go) - execute the current instruction and exit 377 * q (quit) - abort current word 378 * b (toggle breakpoint) 379 */ 380 381 extern char *ficlDictionaryInstructionNames[]; 382 383 static void 384 ficlPrimitiveStepBreak(ficlVm *vm) 385 { 386 ficlString command; 387 ficlWord *word; 388 ficlWord *pOnStep; 389 bool debug = true; 390 391 if (!vm->restart) { 392 FICL_VM_ASSERT(vm, vm->callback.system->breakpoint.address); 393 FICL_VM_ASSERT(vm, vm->callback.system->breakpoint.oldXT); 394 395 /* 396 * Clear the breakpoint that caused me to run 397 * Restore the original instruction at the breakpoint, 398 * and restore the IP 399 */ 400 vm->ip = (ficlIp)(vm->callback.system->breakpoint.address); 401 *vm->ip = vm->callback.system->breakpoint.oldXT; 402 403 /* 404 * If there's an onStep, do it 405 */ 406 pOnStep = ficlSystemLookup(vm->callback.system, "on-step"); 407 if (pOnStep) 408 (void) ficlVmExecuteXT(vm, pOnStep); 409 410 /* 411 * Print the name of the next instruction 412 */ 413 word = vm->callback.system->breakpoint.oldXT; 414 415 if ((((ficlInstruction)word) > ficlInstructionInvalid) && 416 (((ficlInstruction)word) < ficlInstructionLast)) 417 (void) sprintf(vm->pad, "next: %s (instruction %ld)\n", 418 ficlDictionaryInstructionNames[(long)word], 419 (long)word); 420 else { 421 (void) sprintf(vm->pad, "next: %s\n", word->name); 422 if (strcmp(word->name, "interpret") == 0) 423 debug = false; 424 } 425 426 ficlVmTextOut(vm, vm->pad); 427 ficlDebugPrompt(debug); 428 } else { 429 vm->restart = 0; 430 } 431 432 command = ficlVmGetWord(vm); 433 434 switch (command.text[0]) { 435 case 'i': 436 ficlPrimitiveStepIn(vm); 437 break; 438 439 case 'o': 440 ficlPrimitiveStepOver(vm); 441 break; 442 443 case 'g': 444 break; 445 446 case 'l': { 447 ficlWord *xt; 448 xt = ficlDictionaryFindEnclosingWord( 449 ficlVmGetDictionary(vm), (ficlCell *)(vm->ip)); 450 if (xt) { 451 ficlStackPushPointer(vm->dataStack, xt); 452 ficlPrimitiveSeeXT(vm); 453 } else { 454 ficlVmTextOut(vm, "sorry - can't do that\n"); 455 } 456 ficlVmThrow(vm, FICL_VM_STATUS_RESTART); 457 break; 458 } 459 460 case 'q': 461 ficlDebugPrompt(false); 462 ficlVmThrow(vm, FICL_VM_STATUS_ABORT); 463 break; 464 case 'x': { 465 /* 466 * Take whatever's left in the TIB and feed it to a 467 * subordinate ficlVmExecuteString 468 */ 469 int returnValue; 470 ficlString s; 471 ficlWord *oldRunningWord = vm->runningWord; 472 473 FICL_STRING_SET_POINTER(s, 474 vm->tib.text + vm->tib.index); 475 FICL_STRING_SET_LENGTH(s, 476 vm->tib.end - FICL_STRING_GET_POINTER(s)); 477 478 returnValue = ficlVmExecuteString(vm, s); 479 480 if (returnValue == FICL_VM_STATUS_OUT_OF_TEXT) { 481 returnValue = FICL_VM_STATUS_RESTART; 482 vm->runningWord = oldRunningWord; 483 ficlVmTextOut(vm, "\n"); 484 } 485 if (returnValue == FICL_VM_STATUS_ERROR_EXIT) 486 ficlDebugPrompt(false); 487 488 ficlVmThrow(vm, returnValue); 489 break; 490 } 491 492 default: 493 ficlVmTextOut(vm, 494 "i -- step In\n" 495 "o -- step Over\n" 496 "g -- Go (execute to completion)\n" 497 "l -- List source code\n" 498 "q -- Quit (stop debugging and abort)\n" 499 "x -- eXecute the rest of the line " 500 "as Ficl words\n"); 501 ficlDebugPrompt(true); 502 ficlVmThrow(vm, FICL_VM_STATUS_RESTART); 503 break; 504 } 505 506 ficlDebugPrompt(false); 507 } 508 509 /* 510 * b y e 511 * TOOLS 512 * Signal the system to shut down - this causes ficlExec to return 513 * VM_USEREXIT. The rest is up to you. 514 */ 515 static void 516 ficlPrimitiveBye(ficlVm *vm) 517 { 518 ficlVmThrow(vm, FICL_VM_STATUS_USER_EXIT); 519 } 520 521 /* 522 * d i s p l a y S t a c k 523 * TOOLS 524 * Display the parameter stack (code for ".s") 525 */ 526 527 struct stackContext 528 { 529 ficlVm *vm; 530 ficlDictionary *dictionary; 531 int count; 532 }; 533 534 static ficlInteger 535 ficlStackDisplayCallback(void *c, ficlCell *cell) 536 { 537 struct stackContext *context = (struct stackContext *)c; 538 char buffer[80]; 539 540 #ifdef _LP64 541 (void) snprintf(buffer, sizeof (buffer), 542 "[0x%016lx %3d]: %20ld (0x%016lx)\n", 543 (unsigned long)cell, context->count++, (long)cell->i, 544 (unsigned long)cell->u); 545 #else 546 (void) snprintf(buffer, sizeof (buffer), 547 "[0x%08x %3d]: %12d (0x%08x)\n", 548 (unsigned)cell, context->count++, cell->i, cell->u); 549 #endif 550 551 ficlVmTextOut(context->vm, buffer); 552 return (FICL_TRUE); 553 } 554 555 void 556 ficlStackDisplay(ficlStack *stack, ficlStackWalkFunction callback, 557 void *context) 558 { 559 ficlVm *vm = stack->vm; 560 char buffer[128]; 561 struct stackContext myContext; 562 563 FICL_STACK_CHECK(stack, 0, 0); 564 565 #ifdef _LP64 566 (void) sprintf(buffer, "[%s stack has %d entries, top at 0x%016lx]\n", 567 stack->name, ficlStackDepth(stack), (unsigned long)stack->top); 568 #else 569 (void) sprintf(buffer, "[%s stack has %d entries, top at 0x%08x]\n", 570 stack->name, ficlStackDepth(stack), (unsigned)stack->top); 571 #endif 572 ficlVmTextOut(vm, buffer); 573 574 if (callback == NULL) { 575 myContext.vm = vm; 576 myContext.count = 0; 577 context = &myContext; 578 callback = ficlStackDisplayCallback; 579 } 580 ficlStackWalk(stack, callback, context, FICL_FALSE); 581 582 #ifdef _LP64 583 (void) sprintf(buffer, "[%s stack base at 0x%016lx]\n", stack->name, 584 (unsigned long)stack->base); 585 #else 586 (void) sprintf(buffer, "[%s stack base at 0x%08x]\n", stack->name, 587 (unsigned)stack->base); 588 #endif 589 ficlVmTextOut(vm, buffer); 590 } 591 592 void 593 ficlVmDisplayDataStack(ficlVm *vm) 594 { 595 ficlStackDisplay(vm->dataStack, NULL, NULL); 596 } 597 598 static ficlInteger 599 ficlStackDisplaySimpleCallback(void *c, ficlCell *cell) 600 { 601 struct stackContext *context = (struct stackContext *)c; 602 char buffer[32]; 603 604 (void) sprintf(buffer, "%s%ld", context->count ? " " : "", 605 (long)cell->i); 606 context->count++; 607 ficlVmTextOut(context->vm, buffer); 608 return (FICL_TRUE); 609 } 610 611 void 612 ficlVmDisplayDataStackSimple(ficlVm *vm) 613 { 614 ficlStack *stack = vm->dataStack; 615 char buffer[32]; 616 struct stackContext context; 617 618 FICL_STACK_CHECK(stack, 0, 0); 619 620 (void) sprintf(buffer, "[%d] ", ficlStackDepth(stack)); 621 ficlVmTextOut(vm, buffer); 622 623 context.vm = vm; 624 context.count = 0; 625 ficlStackWalk(stack, ficlStackDisplaySimpleCallback, &context, 626 FICL_TRUE); 627 } 628 629 static ficlInteger 630 ficlReturnStackDisplayCallback(void *c, ficlCell *cell) 631 { 632 struct stackContext *context = (struct stackContext *)c; 633 char buffer[128]; 634 635 #ifdef _LP64 636 (void) sprintf(buffer, "[0x%016lx %3d] %20ld (0x%016lx)", 637 (unsigned long)cell, context->count++, cell->i, cell->u); 638 #else 639 (void) sprintf(buffer, "[0x%08x %3d] %12d (0x%08x)", (unsigned)cell, 640 context->count++, cell->i, cell->u); 641 #endif 642 643 /* 644 * Attempt to find the word that contains the return 645 * stack address (as if it is part of a colon definition). 646 * If this works, also print the name of the word. 647 */ 648 if (ficlDictionaryIncludes(context->dictionary, cell->p)) { 649 ficlWord *word; 650 word = ficlDictionaryFindEnclosingWord(context->dictionary, 651 cell->p); 652 if (word) { 653 int offset = (ficlCell *)cell->p - &word->param[0]; 654 (void) sprintf(buffer + strlen(buffer), ", %s + %d ", 655 word->name, offset); 656 } 657 } 658 (void) strcat(buffer, "\n"); 659 ficlVmTextOut(context->vm, buffer); 660 return (FICL_TRUE); 661 } 662 663 void 664 ficlVmDisplayReturnStack(ficlVm *vm) 665 { 666 struct stackContext context; 667 context.vm = vm; 668 context.count = 0; 669 context.dictionary = ficlVmGetDictionary(vm); 670 ficlStackDisplay(vm->returnStack, ficlReturnStackDisplayCallback, 671 &context); 672 } 673 674 /* 675 * f o r g e t - w i d 676 */ 677 static void 678 ficlPrimitiveForgetWid(ficlVm *vm) 679 { 680 ficlDictionary *dictionary = ficlVmGetDictionary(vm); 681 ficlHash *hash; 682 683 hash = (ficlHash *)ficlStackPopPointer(vm->dataStack); 684 ficlHashForget(hash, dictionary->here); 685 } 686 687 /* 688 * f o r g e t 689 * TOOLS EXT ( "<spaces>name" -- ) 690 * Skip leading space delimiters. Parse name delimited by a space. 691 * Find name, then delete name from the dictionary along with all 692 * words added to the dictionary after name. An ambiguous 693 * condition exists if name cannot be found. 694 * 695 * If the Search-Order word set is present, FORGET searches the 696 * compilation word list. An ambiguous condition exists if the 697 * compilation word list is deleted. 698 */ 699 static void 700 ficlPrimitiveForget(ficlVm *vm) 701 { 702 void *where; 703 ficlDictionary *dictionary = ficlVmGetDictionary(vm); 704 ficlHash *hash = dictionary->compilationWordlist; 705 706 ficlPrimitiveTick(vm); 707 where = ((ficlWord *)ficlStackPopPointer(vm->dataStack))->name; 708 ficlHashForget(hash, where); 709 dictionary->here = FICL_POINTER_TO_CELL(where); 710 } 711 712 /* 713 * w o r d s 714 */ 715 #define nCOLWIDTH 8 716 717 static void 718 ficlPrimitiveWordsBackend(ficlVm *vm, ficlDictionary *dictionary, 719 ficlHash *hash, char *ss) 720 { 721 ficlWord *wp; 722 int nChars = 0; 723 int len; 724 unsigned i; 725 int nWords = 0, dWords = 0; 726 char *cp; 727 char *pPad; 728 int columns; 729 730 cp = getenv("screen-#cols"); 731 /* 732 * using strtol for now. TODO: refactor number conversion from 733 * ficlPrimitiveToNumber() and use it instead. 734 */ 735 if (cp == NULL) 736 columns = 80; 737 else 738 columns = strtol(cp, NULL, 0); 739 740 /* 741 * the pad is fixed size area, it's better to allocate 742 * dedicated buffer space to deal with custom terminal sizes. 743 */ 744 pPad = malloc(columns + 1); 745 if (pPad == NULL) 746 ficlVmThrowError(vm, "Error: out of memory"); 747 748 pager_open(); 749 for (i = 0; i < hash->size; i++) { 750 for (wp = hash->table[i]; wp != NULL; wp = wp->link, nWords++) { 751 if (wp->length == 0) /* ignore :noname defs */ 752 continue; 753 754 if (ss != NULL && strstr(wp->name, ss) == NULL) 755 continue; 756 if (ss != NULL && dWords == 0) { 757 (void) sprintf(pPad, 758 " In vocabulary %s\n", 759 hash->name ? hash->name : "<unknown>"); 760 (void) pager_output(pPad); 761 } 762 dWords++; 763 764 /* prevent line wrap due to long words */ 765 if (nChars + wp->length >= columns) { 766 pPad[nChars++] = '\n'; 767 pPad[nChars] = '\0'; 768 nChars = 0; 769 if (pager_output(pPad)) 770 goto pager_done; 771 } 772 773 cp = wp->name; 774 nChars += sprintf(pPad + nChars, "%s", cp); 775 776 if (nChars > columns - 10) { 777 pPad[nChars++] = '\n'; 778 pPad[nChars] = '\0'; 779 nChars = 0; 780 if (pager_output(pPad)) 781 goto pager_done; 782 } else { 783 len = nCOLWIDTH - nChars % nCOLWIDTH; 784 while (len-- > 0) 785 pPad[nChars++] = ' '; 786 } 787 788 if (nChars > columns - 10) { 789 pPad[nChars++] = '\n'; 790 pPad[nChars] = '\0'; 791 nChars = 0; 792 if (pager_output(pPad)) 793 goto pager_done; 794 } 795 } 796 } 797 798 if (nChars > 0) { 799 pPad[nChars++] = '\n'; 800 pPad[nChars] = '\0'; 801 nChars = 0; 802 ficlVmTextOut(vm, pPad); 803 } 804 805 if (ss == NULL) { 806 (void) sprintf(pPad, 807 "Dictionary: %d words, %ld cells used of %u total\n", 808 nWords, (long)(dictionary->here - dictionary->base), 809 dictionary->size); 810 (void) pager_output(pPad); 811 } 812 813 pager_done: 814 free(pPad); 815 pager_close(); 816 } 817 818 static void 819 ficlPrimitiveWords(ficlVm *vm) 820 { 821 ficlDictionary *dictionary = ficlVmGetDictionary(vm); 822 ficlHash *hash = dictionary->wordlists[dictionary->wordlistCount - 1]; 823 ficlPrimitiveWordsBackend(vm, dictionary, hash, NULL); 824 } 825 826 void 827 ficlPrimitiveSiftingImpl(ficlVm *vm, char *ss) 828 { 829 ficlDictionary *dict = ficlVmGetDictionary(vm); 830 int i; 831 832 for (i = 0; i < dict->wordlistCount; i++) 833 ficlPrimitiveWordsBackend(vm, dict, dict->wordlists[i], ss); 834 } 835 836 /* 837 * l i s t E n v 838 * Print symbols defined in the environment 839 */ 840 static void 841 ficlPrimitiveListEnv(ficlVm *vm) 842 { 843 ficlDictionary *dictionary = vm->callback.system->environment; 844 ficlHash *hash = dictionary->forthWordlist; 845 ficlWord *word; 846 unsigned i; 847 int counter = 0; 848 849 pager_open(); 850 for (i = 0; i < hash->size; i++) { 851 for (word = hash->table[i]; word != NULL; 852 word = word->link, counter++) { 853 (void) sprintf(vm->pad, "%s\n", word->name); 854 if (pager_output(vm->pad)) 855 goto pager_done; 856 } 857 } 858 859 (void) sprintf(vm->pad, 860 "Environment: %d words, %ld cells used of %u total\n", 861 counter, (long)(dictionary->here - dictionary->base), 862 dictionary->size); 863 (void) pager_output(vm->pad); 864 865 pager_done: 866 pager_close(); 867 } 868 869 /* 870 * This word lists the parse steps in order 871 */ 872 void 873 ficlPrimitiveParseStepList(ficlVm *vm) 874 { 875 int i; 876 ficlSystem *system = vm->callback.system; 877 FICL_VM_ASSERT(vm, system); 878 879 ficlVmTextOut(vm, "Parse steps:\n"); 880 ficlVmTextOut(vm, "lookup\n"); 881 882 for (i = 0; i < FICL_MAX_PARSE_STEPS; i++) { 883 if (system->parseList[i] != NULL) { 884 ficlVmTextOut(vm, system->parseList[i]->name); 885 ficlVmTextOut(vm, "\n"); 886 } else 887 break; 888 } 889 } 890 891 /* 892 * e n v C o n s t a n t 893 * Ficl interface to ficlSystemSetEnvironment and ficlSetEnvD - allow Ficl 894 * code to set environment constants... 895 */ 896 static void 897 ficlPrimitiveEnvConstant(ficlVm *vm) 898 { 899 unsigned value; 900 FICL_STACK_CHECK(vm->dataStack, 1, 0); 901 902 (void) ficlVmGetWordToPad(vm); 903 value = ficlStackPopUnsigned(vm->dataStack); 904 (void) ficlDictionarySetConstant( 905 ficlSystemGetEnvironment(vm->callback.system), 906 vm->pad, (ficlUnsigned)value); 907 } 908 909 static void 910 ficlPrimitiveEnv2Constant(ficlVm *vm) 911 { 912 ficl2Integer value; 913 914 FICL_STACK_CHECK(vm->dataStack, 2, 0); 915 916 (void) ficlVmGetWordToPad(vm); 917 value = ficlStackPop2Integer(vm->dataStack); 918 (void) ficlDictionarySet2Constant( 919 ficlSystemGetEnvironment(vm->callback.system), vm->pad, value); 920 } 921 922 923 /* 924 * f i c l C o m p i l e T o o l s 925 * Builds wordset for debugger and TOOLS optional word set 926 */ 927 void 928 ficlSystemCompileTools(ficlSystem *system) 929 { 930 ficlDictionary *dictionary = ficlSystemGetDictionary(system); 931 ficlDictionary *environment = ficlSystemGetEnvironment(system); 932 933 FICL_SYSTEM_ASSERT(system, dictionary); 934 FICL_SYSTEM_ASSERT(system, environment); 935 936 937 /* 938 * TOOLS and TOOLS EXT 939 */ 940 (void) ficlDictionarySetPrimitive(dictionary, ".s", 941 ficlVmDisplayDataStack, FICL_WORD_DEFAULT); 942 (void) ficlDictionarySetPrimitive(dictionary, ".s-simple", 943 ficlVmDisplayDataStackSimple, FICL_WORD_DEFAULT); 944 (void) ficlDictionarySetPrimitive(dictionary, "bye", ficlPrimitiveBye, 945 FICL_WORD_DEFAULT); 946 (void) ficlDictionarySetPrimitive(dictionary, "forget", 947 ficlPrimitiveForget, FICL_WORD_DEFAULT); 948 (void) ficlDictionarySetPrimitive(dictionary, "see", ficlPrimitiveSee, 949 FICL_WORD_DEFAULT); 950 (void) ficlDictionarySetPrimitive(dictionary, "words", 951 ficlPrimitiveWords, FICL_WORD_DEFAULT); 952 953 /* 954 * Set TOOLS environment query values 955 */ 956 (void) ficlDictionarySetConstant(environment, "tools", FICL_TRUE); 957 (void) ficlDictionarySetConstant(environment, "tools-ext", FICL_FALSE); 958 959 /* 960 * Ficl extras 961 */ 962 (void) ficlDictionarySetPrimitive(dictionary, "r.s", 963 ficlVmDisplayReturnStack, FICL_WORD_DEFAULT); 964 (void) ficlDictionarySetPrimitive(dictionary, ".env", 965 ficlPrimitiveListEnv, FICL_WORD_DEFAULT); 966 (void) ficlDictionarySetPrimitive(dictionary, "env-constant", 967 ficlPrimitiveEnvConstant, FICL_WORD_DEFAULT); 968 (void) ficlDictionarySetPrimitive(dictionary, "env-2constant", 969 ficlPrimitiveEnv2Constant, FICL_WORD_DEFAULT); 970 (void) ficlDictionarySetPrimitive(dictionary, "debug-xt", 971 ficlPrimitiveDebugXT, FICL_WORD_DEFAULT); 972 (void) ficlDictionarySetPrimitive(dictionary, "parse-order", 973 ficlPrimitiveParseStepList, FICL_WORD_DEFAULT); 974 (void) ficlDictionarySetPrimitive(dictionary, "step-break", 975 ficlPrimitiveStepBreak, FICL_WORD_DEFAULT); 976 (void) ficlDictionarySetPrimitive(dictionary, "forget-wid", 977 ficlPrimitiveForgetWid, FICL_WORD_DEFAULT); 978 (void) ficlDictionarySetPrimitive(dictionary, "see-xt", 979 ficlPrimitiveSeeXT, FICL_WORD_DEFAULT); 980 981 #if FICL_WANT_FLOAT 982 (void) ficlDictionarySetPrimitive(dictionary, ".hash", 983 ficlPrimitiveHashSummary, FICL_WORD_DEFAULT); 984 #endif 985 } 986