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