ficl.h revision 61149
1/******************************************************************* 2** f i c l . h 3** Forth Inspired Command Language 4** Author: John Sadler (john_sadler@alum.mit.edu) 5** Created: 19 July 1997 6** 7*******************************************************************/ 8/* 9** N O T I C E -- DISCLAIMER OF WARRANTY 10** 11** Ficl is freeware. Use it in any way that you like, with 12** the understanding that the code is supported on a "best effort" 13** basis only. 14** 15** Any third party may reproduce, distribute, or modify the ficl 16** software code or any derivative works thereof without any 17** compensation or license, provided that the author information 18** and this disclaimer text are retained in the source code files. 19** The ficl software code is provided on an "as is" basis without 20** warranty of any kind, including, without limitation, the implied 21** warranties of merchantability and fitness for a particular purpose 22** and their equivalents under the laws of any jurisdiction. 23** 24** I am interested in hearing from anyone who uses ficl. If you have 25** a problem, a success story, a defect, an enhancement request, or 26** if you would like to contribute to the ficl release (yay!), please 27** send me email at the address above. 28*/ 29 30/* $FreeBSD: head/sys/boot/ficl/ficl.h 61149 2000-06-01 18:10:44Z dcs $ */ 31 32#if !defined (__FICL_H__) 33#define __FICL_H__ 34/* 35** Ficl (Forth-inspired command language) is an ANS Forth 36** interpreter written in C. Unlike traditional Forths, this 37** interpreter is designed to be embedded into other systems 38** as a command/macro/development prototype language. 39** 40** Where Forths usually view themselves as the center of the system 41** and expect the rest of the system to be coded in Forth, Ficl 42** acts as a component of the system. It is easy to export 43** code written in C or ASM to Ficl in the style of TCL, or to invoke 44** Ficl code from a compiled module. This allows you to do incremental 45** development in a way that combines the best features of threaded 46** languages (rapid development, quick code/test/debug cycle, 47** reasonably fast) with the best features of C (everyone knows it, 48** easier to support large blocks of code, efficient, type checking). 49** 50** Ficl provides facilities for interoperating 51** with programs written in C: C functions can be exported to Ficl, 52** and Ficl commands can be executed via a C calling interface. The 53** interpreter is re-entrant, so it can be used in multiple instances 54** in a multitasking system. Unlike Forth, Ficl's outer interpreter 55** expects a text block as input, and returns to the caller after each 56** text block, so the "data pump" is somewhere in external code. This 57** is more like TCL than Forth, which usually expcets to be at the center 58** of the system, requesting input at its convenience. Each Ficl virtual 59** machine can be bound to a different I/O channel, and is independent 60** of all others in in the same address space except that all virtual 61** machines share a common dictionary (a sort or open symbol table that 62** defines all of the elements of the language). 63** 64** Code is written in ANSI C for portability. 65** 66** Summary of Ficl features and constraints: 67** - Standard: Implements the ANSI Forth CORE word set and part 68** of the CORE EXT word-set, SEARCH and SEARCH EXT, TOOLS and 69** TOOLS EXT, LOCAL and LOCAL ext and various extras. 70** - Extensible: you can export code written in Forth, C, 71** or asm in a straightforward way. Ficl provides open 72** facilities for extending the language in an application 73** specific way. You can even add new control structures! 74** - Ficl and C can interact in two ways: Ficl can encapsulate 75** C code, or C code can invoke Ficl code. 76** - Thread-safe, re-entrant: The shared system dictionary 77** uses a locking mechanism that you can either supply 78** or stub out to provide exclusive access. Each Ficl 79** virtual machine has an otherwise complete state, and 80** each can be bound to a separate I/O channel (or none at all). 81** - Simple encapsulation into existing systems: a basic implementation 82** requires three function calls (see the example program in testmain.c). 83** - ROMable: Ficl is designed to work in RAM-based and ROM code / RAM data 84** environments. It does require somewhat more memory than a pure 85** ROM implementation because it builds its system dictionary in 86** RAM at startup time. 87** - Written an ANSI C to be as simple as I can make it to understand, 88** support, debug, and port. Compiles without complaint at /Az /W4 89** (require ANSI C, max warnings) under Microsoft VC++ 5. 90** - Does full 32 bit math (but you need to implement 91** two mixed precision math primitives (see sysdep.c)) 92** - Indirect threaded interpreter is not the fastest kind of 93** Forth there is (see pForth 68K for a really fast subroutine 94** threaded interpreter), but it's the cleanest match to a 95** pure C implementation. 96** 97** P O R T I N G F i c l 98** 99** To install Ficl on your target system, you need an ANSI C compiler 100** and its runtime library. Inspect the system dependent macros and 101** functions in sysdep.h and sysdep.c and edit them to suit your 102** system. For example, INT16 is a short on some compilers and an 103** int on others. Check the default CELL alignment controlled by 104** FICL_ALIGN. If necessary, add new definitions of ficlMalloc, ficlFree, 105** ficlLockDictionary, and ficlTextOut to work with your operating system. 106** Finally, use testmain.c as a guide to installing the Ficl system and 107** one or more virtual machines into your code. You do not need to include 108** testmain.c in your build. 109** 110** T o D o L i s t 111** 112** 1. Unimplemented system dependent CORE word: key 113** 2. Kludged CORE word: ACCEPT 114** 3. Dictionary locking is full of holes - only one vm at a time 115** can alter the dict. 116** 4. Ficl uses the pad in CORE words - this violates the standard, 117** but it's cleaner for a multithreaded system. I'll have to make a 118** second pad for reference by the word PAD to fix this. 119** 120** F o r M o r e I n f o r m a t i o n 121** 122** Web home of ficl 123** http://www.taygeta.com/forth/compilers 124** Check this website for Forth literature (including the ANSI standard) 125** http://www.taygeta.com/forthlit.html 126** and here for software and more links 127** http://www.taygeta.com/forth.html 128** 129** Obvious Performance enhancement opportunities 130** Compile speed 131** - work on interpret speed 132** - turn off locals (FICL_WANT_LOCALS) 133** Interpret speed 134** - Change inner interpreter (and everything else) 135** so that a definition is a list of pointers to functions 136** and inline data rather than pointers to words. This gets 137** rid of vm->runningWord and a level of indirection in the 138** inner loop. I'll look at it for ficl 3.0 139** - Make the main hash table a bigger prime (HASHSIZE) 140** - FORGET about twiddling the hash function - my experience is 141** that that is a waste of time. 142** - eliminate the need to pass the pVM parameter on the stack 143** by dedicating a register to it. Most words need access to the 144** vm, but the parameter passing overhead can be reduced. One way 145** requires that the host OS have a task switch callout. Create 146** a global variable for the running VM and refer to it in words 147** that need VM access. Alternative: use thread local storage. 148** For single threaded implementations, you can just use a global. 149** The first two solutions create portability problems, so I 150** haven't considered doing them. Another possibility is to 151** declare the pVm parameter to be "register", and hope the compiler 152** pays attention. 153** 154*/ 155 156/* 157** Revision History: 158** 159** 15 Apr 1999 (sadler) Merged FreeBSD changes for exception wordset and 160** counted strings in ficlExec. 161** 12 Jan 1999 (sobral) Corrected EVALUATE behavior. Now TIB has an 162** "end" field, and all words respect this. ficlExec is passed a "size" 163** of TIB, as well as vmPushTib. This size is used to calculate the "end" 164** of the string, ie, base+size. If the size is not known, pass -1. 165** 166** 10 Jan 1999 (sobral) EXCEPTION word set has been added, and existing 167** words has been modified to conform to EXCEPTION EXT word set. 168** 169** 27 Aug 1998 (sadler) testing and corrections for LOCALS, LOCALS EXT, 170** SEARCH / SEARCH EXT, TOOLS / TOOLS EXT. 171** Added .X to display in hex, PARSE and PARSE-WORD to supplement WORD, 172** EMPTY to clear stack. 173** 174** 29 jun 1998 (sadler) added variable sized hash table support 175** and ANS Forth optional SEARCH & SEARCH EXT word set. 176** 26 May 1998 (sadler) 177** FICL_PROMPT macro 178** 14 April 1998 (sadler) V1.04 179** Ficlwin: Windows version, Skip Carter's Linux port 180** 5 March 1998 (sadler) V1.03 181** Bug fixes -- passes John Ryan's ANS test suite "core.fr" 182** 183** 24 February 1998 (sadler) V1.02 184** -Fixed bugs in <# # #> 185** -Changed FICL_WORD so that storage for the name characters 186** can be allocated from the dictionary as needed rather than 187** reserving 32 bytes in each word whether needed or not - 188** this saved 50% of the dictionary storage requirement. 189** -Added words in testmain for Win32 functions system,chdir,cwd, 190** also added a word that loads and evaluates a file. 191** 192** December 1997 (sadler) 193** -Added VM_RESTART exception handling in ficlExec -- this lets words 194** that require additional text to succeed (like :, create, variable...) 195** recover gracefully from an empty input buffer rather than emitting 196** an error message. Definitions can span multiple input blocks with 197** no restrictions. 198** -Changed #include order so that <assert.h> is included in sysdep.h, 199** and sysdep is included in all other files. This lets you define 200** NDEBUG in sysdep.h to disable assertions if you want to. 201** -Make PC specific system dependent code conditional on _M_IX86 202** defined so that ports can coexist in sysdep.h/sysdep.c 203*/ 204 205#ifdef __cplusplus 206extern "C" { 207#endif 208 209#include "sysdep.h" 210#include <limits.h> /* UCHAR_MAX */ 211 212/* 213** Forward declarations... read on. 214*/ 215struct ficl_word; 216struct vm; 217struct ficl_dict; 218 219/* 220** the Good Stuff starts here... 221*/ 222#define FICL_VER "2.03" 223#if !defined (FICL_PROMPT) 224#define FICL_PROMPT "ok> " 225#endif 226 227/* 228** ANS Forth requires false to be zero, and true to be the ones 229** complement of false... that unifies logical and bitwise operations 230** nicely. 231*/ 232#define FICL_TRUE (~(0L)) 233#define FICL_FALSE (0) 234#define FICL_BOOL(x) ((x) ? FICL_TRUE : FICL_FALSE) 235 236 237/* 238** A CELL is the main storage type. It must be large enough 239** to contain a pointer or a scalar. In order to accommodate 240** 32 bit and 64 bit processors, use abstract types for i and u. 241*/ 242typedef union _cell 243{ 244 FICL_INT i; 245 FICL_UNS u; 246 void *p; 247} CELL; 248 249/* 250** LVALUEtoCELL does a little pointer trickery to cast any 32 bit 251** lvalue (informal definition: an expression whose result has an 252** address) to CELL. Remember that constants and casts are NOT 253** themselves lvalues! 254*/ 255#define LVALUEtoCELL(v) (*(CELL *)&v) 256 257/* 258** PTRtoCELL is a cast through void * intended to satisfy the 259** most outrageously pedantic compiler... (I won't mention 260** its name) 261*/ 262#define PTRtoCELL (CELL *)(void *) 263#define PTRtoSTRING (FICL_STRING *)(void *) 264 265/* 266** Strings in FICL are stored in Pascal style - with a count 267** preceding the text. We'll also NULL-terminate them so that 268** they work with the usual C lib string functions. (Belt & 269** suspenders? You decide.) 270** STRINGINFO hides the implementation with a couple of 271** macros for use in internal routines. 272*/ 273 274typedef unsigned char FICL_COUNT; 275#define FICL_STRING_MAX UCHAR_MAX 276typedef struct _ficl_string 277{ 278 FICL_COUNT count; 279 char text[1]; 280} FICL_STRING; 281 282typedef struct 283{ 284 UNS32 count; 285 char *cp; 286} STRINGINFO; 287 288#define SI_COUNT(si) (si.count) 289#define SI_PTR(si) (si.cp) 290#define SI_SETLEN(si, len) (si.count = (UNS32)(len)) 291#define SI_SETPTR(si, ptr) (si.cp = (char *)(ptr)) 292/* 293** Init a STRINGINFO from a pointer to NULL-terminated string 294*/ 295#define SI_PSZ(si, psz) \ 296 {si.cp = psz; si.count = (FICL_COUNT)strlen(psz);} 297/* 298** Init a STRINGINFO from a pointer to FICL_STRING 299*/ 300#define SI_PFS(si, pfs) \ 301 {si.cp = pfs->text; si.count = pfs->count;} 302 303/* 304** Ficl uses a this little structure to hold the address of 305** the block of text it's working on and an index to the next 306** unconsumed character in the string. Traditionally, this is 307** done by a Text Input Buffer, so I've called this struct TIB. 308** 309** Since this structure also holds the size of the input buffer, 310** and since evaluate requires that, let's put the size here. 311** The size is stored as an end-pointer because that is what the 312** null-terminated string aware functions find most easy to deal 313** with. 314** Notice, though, that nobody really uses this except evaluate, 315** so it might just be moved to FICL_VM instead. (sobral) 316*/ 317typedef struct 318{ 319 FICL_INT index; 320 char *end; 321 char *cp; 322} TIB; 323 324 325/* 326** Stacks get heavy use in Ficl and Forth... 327** Each virtual machine implements two of them: 328** one holds parameters (data), and the other holds return 329** addresses and control flow information for the virtual 330** machine. (Note: C's automatic stack is implicitly used, 331** but not modeled because it doesn't need to be...) 332** Here's an abstract type for a stack 333*/ 334typedef struct _ficlStack 335{ 336 FICL_UNS nCells; /* size of the stack */ 337 CELL *pFrame; /* link reg for stack frame */ 338 CELL *sp; /* stack pointer */ 339 CELL base[1]; /* Bottom of the stack */ 340} FICL_STACK; 341 342/* 343** Stack methods... many map closely to required Forth words. 344*/ 345FICL_STACK *stackCreate(unsigned nCells); 346void stackDelete(FICL_STACK *pStack); 347int stackDepth (FICL_STACK *pStack); 348void stackDrop (FICL_STACK *pStack, int n); 349CELL stackFetch (FICL_STACK *pStack, int n); 350CELL stackGetTop(FICL_STACK *pStack); 351void stackLink (FICL_STACK *pStack, int nCells); 352void stackPick (FICL_STACK *pStack, int n); 353CELL stackPop (FICL_STACK *pStack); 354void *stackPopPtr (FICL_STACK *pStack); 355FICL_UNS stackPopUNS(FICL_STACK *pStack); 356FICL_INT stackPopINT(FICL_STACK *pStack); 357void stackPush (FICL_STACK *pStack, CELL c); 358void stackPushPtr (FICL_STACK *pStack, void *ptr); 359void stackPushUNS(FICL_STACK *pStack, FICL_UNS u); 360void stackPushINT(FICL_STACK *pStack, FICL_INT i); 361void stackReset (FICL_STACK *pStack); 362void stackRoll (FICL_STACK *pStack, int n); 363void stackSetTop(FICL_STACK *pStack, CELL c); 364void stackStore (FICL_STACK *pStack, int n, CELL c); 365void stackUnlink(FICL_STACK *pStack); 366 367/* 368** The virtual machine (VM) contains the state for one interpreter. 369** Defined operations include: 370** Create & initialize 371** Delete 372** Execute a block of text 373** Parse a word out of the input stream 374** Call return, and branch 375** Text output 376** Throw an exception 377*/ 378 379typedef struct ficl_word ** IPTYPE; /* the VM's instruction pointer */ 380 381/* 382** Each VM has a placeholder for an output function - 383** this makes it possible to have each VM do I/O 384** through a different device. If you specify no 385** OUTFUNC, it defaults to ficlTextOut. 386*/ 387typedef void (*OUTFUNC)(struct vm *pVM, char *text, int fNewline); 388 389/* 390** Each VM operates in one of two non-error states: interpreting 391** or compiling. When interpreting, words are simply executed. 392** When compiling, most words in the input stream have their 393** addresses inserted into the word under construction. Some words 394** (known as IMMEDIATE) are executed in the compile state, too. 395*/ 396/* values of STATE */ 397#define INTERPRET 0 398#define COMPILE 1 399 400/* 401** The pad is a small scratch area for text manipulation. ANS Forth 402** requires it to hold at least 84 characters. 403*/ 404#if !defined nPAD 405#define nPAD 256 406#endif 407 408/* 409** ANS Forth requires that a word's name contain {1..31} characters. 410*/ 411#if !defined nFICLNAME 412#define nFICLNAME 31 413#endif 414 415/* 416** OK - now we can really define the VM... 417*/ 418typedef struct vm 419{ 420 struct vm *link; /* Ficl keeps a VM list for simple teardown */ 421 jmp_buf *pState; /* crude exception mechanism... */ 422 OUTFUNC textOut; /* Output callback - see sysdep.c */ 423 void * pExtend; /* vm extension pointer */ 424 short fRestart; /* Set TRUE to restart runningWord */ 425 IPTYPE ip; /* instruction pointer */ 426 struct ficl_word 427 *runningWord;/* address of currently running word (often just *(ip-1) ) */ 428 UNS32 state; /* compiling or interpreting */ 429 UNS32 base; /* number conversion base */ 430 FICL_STACK *pStack; /* param stack */ 431 FICL_STACK *rStack; /* return stack */ 432 CELL sourceID; /* -1 if string, 0 if normal input */ 433 TIB tib; /* address of incoming text string */ 434#if FICL_WANT_USER 435 CELL user[FICL_USER_CELLS]; 436#endif 437 char pad[nPAD]; /* the scratch area (see above) */ 438} FICL_VM; 439 440/* 441** A FICL_CODE points to a function that gets called to help execute 442** a word in the dictionary. It always gets passed a pointer to the 443** running virtual machine, and from there it can get the address 444** of the parameter area of the word it's supposed to operate on. 445** For precompiled words, the code is all there is. For user defined 446** words, the code assumes that the word's parameter area is a list 447** of pointers to the code fields of other words to execute, and 448** may also contain inline data. The first parameter is always 449** a pointer to a code field. 450*/ 451typedef void (*FICL_CODE)(FICL_VM *pVm); 452 453#if 0 454#define VM_ASSERT(pVM) assert((*(pVM->ip - 1)) == pVM->runningWord) 455#else 456#define VM_ASSERT(pVM) 457#endif 458 459/* 460** Ficl models memory as a contiguous space divided into 461** words in a linked list called the dictionary. 462** A FICL_WORD starts each entry in the list. 463** Version 1.02: space for the name characters is allotted from 464** the dictionary ahead of the word struct - this saves about half 465** the storage on average with very little runtime cost. 466*/ 467typedef struct ficl_word 468{ 469 struct ficl_word *link; /* Previous word in the dictionary */ 470 UNS16 hash; 471 UNS8 flags; /* Immediate, Smudge, Compile-only */ 472 FICL_COUNT nName; /* Number of chars in word name */ 473 char *name; /* First nFICLNAME chars of word name */ 474 FICL_CODE code; /* Native code to execute the word */ 475 CELL param[1]; /* First data cell of the word */ 476} FICL_WORD; 477 478/* 479** Worst-case size of a word header: nFICLNAME chars in name 480*/ 481#define CELLS_PER_WORD \ 482 ( (sizeof (FICL_WORD) + nFICLNAME + sizeof (CELL)) \ 483 / (sizeof (CELL)) ) 484 485int wordIsImmediate(FICL_WORD *pFW); 486int wordIsCompileOnly(FICL_WORD *pFW); 487 488/* flag values for word header */ 489#define FW_IMMEDIATE 1 /* execute me even if compiling */ 490#define FW_COMPILE 2 /* error if executed when not compiling */ 491#define FW_SMUDGE 4 /* definition in progress - hide me */ 492#define FW_CLASS 8 /* Word defines a class */ 493 494#define FW_COMPIMMED (FW_IMMEDIATE | FW_COMPILE) 495#define FW_DEFAULT 0 496 497 498/* 499** Exit codes for vmThrow 500*/ 501#define VM_INNEREXIT -256 /* tell ficlExecXT to exit inner loop */ 502#define VM_OUTOFTEXT -257 /* hungry - normal exit */ 503#define VM_RESTART -258 /* word needs more text to succeed - re-run it */ 504#define VM_USEREXIT -259 /* user wants to quit */ 505#define VM_ERREXIT -260 /* interp found an error */ 506#define VM_ABORT -1 /* like errexit -- abort */ 507#define VM_ABORTQ -2 /* like errexit -- abort" */ 508#define VM_QUIT -56 /* like errexit, but leave pStack & base alone */ 509 510 511void vmBranchRelative(FICL_VM *pVM, int offset); 512FICL_VM * vmCreate (FICL_VM *pVM, unsigned nPStack, unsigned nRStack); 513void vmDelete (FICL_VM *pVM); 514void vmExecute(FICL_VM *pVM, FICL_WORD *pWord); 515char * vmGetString(FICL_VM *pVM, FICL_STRING *spDest, char delimiter); 516STRINGINFO vmGetWord(FICL_VM *pVM); 517STRINGINFO vmGetWord0(FICL_VM *pVM); 518int vmGetWordToPad(FICL_VM *pVM); 519STRINGINFO vmParseString(FICL_VM *pVM, char delimiter); 520STRINGINFO vmParseStringEx(FICL_VM *pVM, char delimiter, char fSkipLeading); 521CELL vmPop(FICL_VM *pVM); 522void vmPush(FICL_VM *pVM, CELL c); 523void vmPopIP (FICL_VM *pVM); 524void vmPushIP (FICL_VM *pVM, IPTYPE newIP); 525void vmQuit (FICL_VM *pVM); 526void vmReset (FICL_VM *pVM); 527void vmSetTextOut(FICL_VM *pVM, OUTFUNC textOut); 528#if FICL_WANT_DEBUGGER 529void vmStep(FICL_VM *pVM); 530#endif 531void vmTextOut(FICL_VM *pVM, char *text, int fNewline); 532void vmThrow (FICL_VM *pVM, int except); 533void vmThrowErr(FICL_VM *pVM, char *fmt, ...); 534 535#define vmGetRunningWord(pVM) ((pVM)->runningWord) 536 537 538/* 539** The inner interpreter - coded as a macro (see note for 540** INLINE_INNER_LOOP in sysdep.h for complaints about VC++ 5 541*/ 542#define M_VM_STEP(pVM) \ 543 FICL_WORD *tempFW = *(pVM)->ip++; \ 544 (pVM)->runningWord = tempFW; \ 545 tempFW->code(pVM); \ 546 547#define M_INNER_LOOP(pVM) \ 548 for (;;) { M_VM_STEP(pVM) } 549 550 551#if INLINE_INNER_LOOP != 0 552#define vmInnerLoop(pVM) M_INNER_LOOP(pVM) 553#else 554void vmInnerLoop(FICL_VM *pVM); 555#endif 556 557/* 558** vmCheckStack needs a vm pointer because it might have to say 559** something if it finds a problem. Parms popCells and pushCells 560** correspond to the number of parameters on the left and right of 561** a word's stack effect comment. 562*/ 563void vmCheckStack(FICL_VM *pVM, int popCells, int pushCells); 564 565/* 566** TIB access routines... 567** ANS forth seems to require the input buffer to be represented 568** as a pointer to the start of the buffer, and an index to the 569** next character to read. 570** PushTib points the VM to a new input string and optionally 571** returns a copy of the current state 572** PopTib restores the TIB state given a saved TIB from PushTib 573** GetInBuf returns a pointer to the next unused char of the TIB 574*/ 575void vmPushTib(FICL_VM *pVM, char *text, INT32 nChars, TIB *pSaveTib); 576void vmPopTib(FICL_VM *pVM, TIB *pTib); 577#define vmGetInBuf(pVM) ((pVM)->tib.cp + (pVM)->tib.index) 578#define vmGetInBufLen(pVM) ((pVM)->tib.end - (pVM)->tib.cp) 579#define vmGetInBufEnd(pVM) ((pVM)->tib.end) 580#define vmSetTibIndex(pVM, i) (pVM)->tib.index = i 581#define vmUpdateTib(pVM, str) (pVM)->tib.index = (str) - (pVM)->tib.cp 582 583/* 584** Generally useful string manipulators omitted by ANSI C... 585** ltoa complements strtol 586*/ 587#if defined(_WIN32) && !FICL_MAIN 588/* #SHEESH 589** Why do Microsoft Meatballs insist on contaminating 590** my namespace with their string functions??? 591*/ 592#pragma warning(disable: 4273) 593#endif 594 595int isPowerOfTwo(FICL_UNS u); 596 597char *ltoa( FICL_INT value, char *string, int radix ); 598char *ultoa(FICL_UNS value, char *string, int radix ); 599char digit_to_char(int value); 600char *strrev( char *string ); 601char *skipSpace(char *cp, char *end); 602char *caseFold(char *cp); 603int strincmp(char *cp1, char *cp2, FICL_COUNT count); 604 605#if defined(_WIN32) && !FICL_MAIN 606#pragma warning(default: 4273) 607#endif 608 609/* 610** Ficl hash table - variable size. 611** assert(size > 0) 612** If size is 1, the table degenerates into a linked list. 613** A WORDLIST (see the search order word set in DPANS) is 614** just a pointer to a FICL_HASH in this implementation. 615*/ 616#if !defined HASHSIZE /* Default size of hash table. For most uniform */ 617#define HASHSIZE 127 /* performance, use a prime number! */ 618#endif 619 620typedef struct ficl_hash 621{ 622 struct ficl_hash *link; /* eventual inheritance support */ 623 unsigned size; 624 FICL_WORD *table[1]; 625} FICL_HASH; 626 627void hashForget(FICL_HASH *pHash, void *where); 628UNS16 hashHashCode(STRINGINFO si); 629void hashInsertWord(FICL_HASH *pHash, FICL_WORD *pFW); 630FICL_WORD *hashLookup(struct ficl_hash *pHash, 631 STRINGINFO si, 632 UNS16 hashCode); 633void hashReset(FICL_HASH *pHash); 634 635/* 636** A Dictionary is a linked list of FICL_WORDs. It is also Ficl's 637** memory model. Description of fields: 638** 639** here -- points to the next free byte in the dictionary. This 640** pointer is forced to be CELL-aligned before a definition is added. 641** Do not assume any specific alignment otherwise - Use dictAlign(). 642** 643** smudge -- pointer to word currently being defined (or last defined word) 644** If the definition completes successfully, the word will be 645** linked into the hash table. If unsuccessful, dictUnsmudge 646** uses this pointer to restore the previous state of the dictionary. 647** Smudge prevents unintentional recursion as a side-effect: the 648** dictionary search algo examines only completed definitions, so a 649** word cannot invoke itself by name. See the ficl word "recurse". 650** NOTE: smudge always points to the last word defined. IMMEDIATE 651** makes use of this fact. Smudge is initially NULL. 652** 653** pForthWords -- pointer to the default wordlist (FICL_HASH). 654** This is the initial compilation list, and contains all 655** ficl's precompiled words. 656** 657** pCompile -- compilation wordlist - initially equal to pForthWords 658** pSearch -- array of pointers to wordlists. Managed as a stack. 659** Highest index is the first list in the search order. 660** nLists -- number of lists in pSearch. nLists-1 is the highest 661** filled slot in pSearch, and points to the first wordlist 662** in the search order 663** size -- number of cells in the dictionary (total) 664** dict -- start of data area. Must be at the end of the struct. 665*/ 666typedef struct ficl_dict 667{ 668 CELL *here; 669 FICL_WORD *smudge; 670 FICL_HASH *pForthWords; 671 FICL_HASH *pCompile; 672 FICL_HASH *pSearch[FICL_DEFAULT_VOCS]; 673 int nLists; 674 unsigned size; /* Number of cells in dict (total)*/ 675 CELL *dict; /* Base of dictionary memory */ 676} FICL_DICT; 677 678void *alignPtr(void *ptr); 679void dictAbortDefinition(FICL_DICT *pDict); 680void dictAlign(FICL_DICT *pDict); 681int dictAllot(FICL_DICT *pDict, int n); 682int dictAllotCells(FICL_DICT *pDict, int nCells); 683void dictAppendCell(FICL_DICT *pDict, CELL c); 684void dictAppendChar(FICL_DICT *pDict, char c); 685FICL_WORD *dictAppendWord(FICL_DICT *pDict, 686 char *name, 687 FICL_CODE pCode, 688 UNS8 flags); 689FICL_WORD *dictAppendWord2(FICL_DICT *pDict, 690 STRINGINFO si, 691 FICL_CODE pCode, 692 UNS8 flags); 693void dictAppendUNS(FICL_DICT *pDict, FICL_UNS u); 694int dictCellsAvail(FICL_DICT *pDict); 695int dictCellsUsed (FICL_DICT *pDict); 696void dictCheck(FICL_DICT *pDict, FICL_VM *pVM, int nCells); 697FICL_DICT *dictCreate(unsigned nCELLS); 698FICL_DICT *dictCreateHashed(unsigned nCells, unsigned nHash); 699void dictDelete(FICL_DICT *pDict); 700void dictEmpty(FICL_DICT *pDict, unsigned nHash); 701int dictIncludes(FICL_DICT *pDict, void *p); 702FICL_WORD *dictLookup(FICL_DICT *pDict, STRINGINFO si); 703#if FICL_WANT_LOCALS 704FICL_WORD *dictLookupLoc(FICL_DICT *pDict, STRINGINFO si); 705#endif 706void dictResetSearchOrder(FICL_DICT *pDict); 707void dictSetFlags(FICL_DICT *pDict, UNS8 set, UNS8 clr); 708void dictSetImmediate(FICL_DICT *pDict); 709void dictUnsmudge(FICL_DICT *pDict); 710CELL *dictWhere(FICL_DICT *pDict); 711 712 713/* 714** External interface to FICL... 715*/ 716/* 717** f i c l I n i t S y s t e m 718** Binds a global dictionary to the interpreter system and initializes 719** the dict to contain the ANSI CORE wordset. 720** You specify the address and size of the allocated area. 721** After that, ficl manages it. 722** First step is to set up the static pointers to the area. 723** Then write the "precompiled" portion of the dictionary in. 724** The dictionary needs to be at least large enough to hold the 725** precompiled part. Try 1K cells minimum. Use "words" to find 726** out how much of the dictionary is used at any time. 727*/ 728void ficlInitSystem(int nDictCells); 729 730/* 731** f i c l T e r m S y s t e m 732** Deletes the system dictionary and all virtual machines that 733** were created with ficlNewVM (see below). Call this function to 734** reclaim all memory used by the dictionary and VMs. 735*/ 736void ficlTermSystem(void); 737 738/* 739** f i c l E x e c 740** Evaluates a block of input text in the context of the 741** specified interpreter. Emits any requested output to the 742** interpreter's output function. If the input string is NULL 743** terminated, you can pass -1 as nChars rather than count it. 744** Execution returns when the text block has been executed, 745** or an error occurs. 746** Returns one of the VM_XXXX codes defined in ficl.h: 747** VM_OUTOFTEXT is the normal exit condition 748** VM_ERREXIT means that the interp encountered a syntax error 749** and the vm has been reset to recover (some or all 750** of the text block got ignored 751** VM_USEREXIT means that the user executed the "bye" command 752** to shut down the interpreter. This would be a good 753** time to delete the vm, etc -- or you can ignore this 754** signal. 755** VM_ABORT and VM_ABORTQ are generated by 'abort' and 'abort"' 756** commands. 757** Preconditions: successful execution of ficlInitSystem, 758** Successful creation and init of the VM by ficlNewVM (or equiv) 759*/ 760int ficlExec (FICL_VM *pVM, char *pText); 761int ficlExecC(FICL_VM *pVM, char *pText, INT32 nChars); 762int ficlExecXT(FICL_VM *pVM, FICL_WORD *pWord); 763 764/* 765** ficlExecFD(FICL_VM *pVM, int fd); 766 * Evaluates text from file passed in via fd. 767 * Execution returns when all of file has been executed or an 768 * error occurs. 769 */ 770int ficlExecFD(FICL_VM *pVM, int fd); 771 772/* 773** Create a new VM from the heap, and link it into the system VM list. 774** Initializes the VM and binds default sized stacks to it. Returns the 775** address of the VM, or NULL if an error occurs. 776** Precondition: successful execution of ficlInitSystem 777*/ 778FICL_VM *ficlNewVM(void); 779 780/* 781** Force deletion of a VM. You do not need to do this 782** unless you're creating and discarding a lot of VMs. 783** For systems that use a constant pool of VMs for the life 784** of the system, ficltermSystem takes care of VM cleanup 785** automatically. 786*/ 787void ficlFreeVM(FICL_VM *pVM); 788 789 790/* 791** Set the stack sizes (return and parameter) to be used for all 792** subsequently created VMs. Returns actual stack size to be used. 793*/ 794int ficlSetStackSize(int nStackCells); 795 796/* 797** Returns the address of the most recently defined word in the system 798** dictionary with the given name, or NULL if no match. 799** Precondition: successful execution of ficlInitSystem 800*/ 801FICL_WORD *ficlLookup(char *name); 802 803/* 804** f i c l G e t D i c t 805** Utility function - returns the address of the system dictionary. 806** Precondition: successful execution of ficlInitSystem 807*/ 808FICL_DICT *ficlGetDict(void); 809FICL_DICT *ficlGetEnv(void); 810void ficlSetEnv(char *name, UNS32 value); 811void ficlSetEnvD(char *name, UNS32 hi, UNS32 lo); 812#if FICL_WANT_LOCALS 813FICL_DICT *ficlGetLoc(void); 814#endif 815/* 816** f i c l B u i l d 817** Builds a word into the system default dictionary in a thread-safe way. 818** Preconditions: system must be initialized, and there must 819** be enough space for the new word's header! Operation is 820** controlled by ficlLockDictionary, so any initialization 821** required by your version of the function (if you "overrode" 822** it) must be complete at this point. 823** Parameters: 824** name -- the name of the word to be built 825** code -- code to execute when the word is invoked - must take a single param 826** pointer to a FICL_VM 827** flags -- 0 or more of FW_IMMEDIATE, FW_COMPILE, use bitwise OR! 828** Most words can use FW_DEFAULT. 829** nAllot - number of extra cells to allocate in the parameter area (usually zero) 830*/ 831int ficlBuild(char *name, FICL_CODE code, char flags); 832 833/* 834** f i c l C o m p i l e C o r e 835** Builds the ANS CORE wordset into the dictionary - called by 836** ficlInitSystem - no need to waste dict space by doing it again. 837*/ 838void ficlCompileCore(FICL_DICT *dp); 839void ficlCompileSoftCore(FICL_VM *pVM); 840 841/* 842** from words.c... 843*/ 844void constantParen(FICL_VM *pVM); 845void twoConstParen(FICL_VM *pVM); 846 847/* 848** Dictionary on-demand resizing 849*/ 850extern unsigned int dictThreshold; 851extern unsigned int dictIncrease; 852 853/* 854** So we can more easily debug... 855*/ 856#ifdef FICL_TRACE 857extern int ficl_trace; 858#endif 859 860#if defined(__i386__) && !defined(TESTMAIN) 861extern void ficlOutb(FICL_VM *pVM); 862extern void ficlInb(FICL_VM *pVM); 863#endif 864 865#ifdef __cplusplus 866} 867#endif 868 869#endif /* __FICL_H__ */ 870