ficl.h revision 43139
140843Smsmith/******************************************************************* 240843Smsmith** f i c l . h 340843Smsmith** Forth Inspired Command Language 440843Smsmith** Author: John Sadler (john_sadler@alum.mit.edu) 540843Smsmith** Created: 19 July 1997 640843Smsmith** 740843Smsmith*******************************************************************/ 840843Smsmith/* 940843Smsmith** N O T I C E -- DISCLAIMER OF WARRANTY 1040843Smsmith** 1140843Smsmith** Ficl is freeware. Use it in any way that you like, with 1240843Smsmith** the understanding that the code is supported on a "best effort" 1340843Smsmith** basis only. 1440843Smsmith** 1540843Smsmith** Any third party may reproduce, distribute, or modify the ficl 1640843Smsmith** software code or any derivative works thereof without any 1740843Smsmith** compensation or license, provided that the author information 1840843Smsmith** and this disclaimer text are retained in the source code files. 1940843Smsmith** The ficl software code is provided on an "as is" basis without 2040843Smsmith** warranty of any kind, including, without limitation, the implied 2140843Smsmith** warranties of merchantability and fitness for a particular purpose 2240843Smsmith** and their equivalents under the laws of any jurisdiction. 2340843Smsmith** 2440843Smsmith** I am interested in hearing from anyone who uses ficl. If you have 2540843Smsmith** a problem, a success story, a defect, an enhancement request, or 2640843Smsmith** if you would like to contribute to the ficl release (yay!), please 2740843Smsmith** send me email at the address above. 2840843Smsmith*/ 2940843Smsmith 3040843Smsmith#if !defined (__FICL_H__) 3140843Smsmith#define __FICL_H__ 3240843Smsmith/* 3340843Smsmith** Ficl (Forth-inspired command language) is an ANS Forth 3440843Smsmith** interpreter written in C. Unlike traditional Forths, this 3540843Smsmith** interpreter is designed to be embedded into other systems 3640843Smsmith** as a command/macro/development prototype language. 3740843Smsmith** 3840843Smsmith** Where Forths usually view themselves as the center of the system 3940843Smsmith** and expect the rest of the system to be coded in Forth, Ficl 4040843Smsmith** acts as a component of the system. It is easy to export 4140843Smsmith** code written in C or ASM to Ficl in the style of TCL, or to invoke 4240843Smsmith** Ficl code from a compiled module. This allows you to do incremental 4340843Smsmith** development in a way that combines the best features of threaded 4440843Smsmith** languages (rapid development, quick code/test/debug cycle, 4540843Smsmith** reasonably fast) with the best features of C (everyone knows it, 4640843Smsmith** easier to support large blocks of code, efficient, type checking). 4740843Smsmith** 4840843Smsmith** Ficl provides facilities for interoperating 4940843Smsmith** with programs written in C: C functions can be exported to Ficl, 5040843Smsmith** and Ficl commands can be executed via a C calling interface. The 5140843Smsmith** interpreter is re-entrant, so it can be used in multiple instances 5240843Smsmith** in a multitasking system. Unlike Forth, Ficl's outer interpreter 5340843Smsmith** expects a text block as input, and returns to the caller after each 5440843Smsmith** text block, so the "data pump" is somewhere in external code. This 5540843Smsmith** is more like TCL than Forth, which usually expcets to be at the center 5640843Smsmith** of the system, requesting input at its convenience. Each Ficl virtual 5740843Smsmith** machine can be bound to a different I/O channel, and is independent 5840843Smsmith** of all others in in the same address space except that all virtual 5940843Smsmith** machines share a common dictionary (a sort or open symbol table that 6040843Smsmith** defines all of the elements of the language). 6140843Smsmith** 6240843Smsmith** Code is written in ANSI C for portability. 6340843Smsmith** 6440843Smsmith** Summary of Ficl features and constraints: 6540843Smsmith** - Standard: Implements the ANSI Forth CORE word set and part 6640843Smsmith** of the CORE EXT word-set, SEARCH and SEARCH EXT, TOOLS and 6740843Smsmith** TOOLS EXT, LOCAL and LOCAL ext and various extras. 6840843Smsmith** - Extensible: you can export code written in Forth, C, 6940843Smsmith** or asm in a straightforward way. Ficl provides open 7040843Smsmith** facilities for extending the language in an application 7140843Smsmith** specific way. You can even add new control structures! 7240843Smsmith** - Ficl and C can interact in two ways: Ficl can encapsulate 7340843Smsmith** C code, or C code can invoke Ficl code. 7440843Smsmith** - Thread-safe, re-entrant: The shared system dictionary 7540843Smsmith** uses a locking mechanism that you can either supply 7640843Smsmith** or stub out to provide exclusive access. Each Ficl 7740843Smsmith** virtual machine has an otherwise complete state, and 7840843Smsmith** each can be bound to a separate I/O channel (or none at all). 7940843Smsmith** - Simple encapsulation into existing systems: a basic implementation 8040843Smsmith** requires three function calls (see the example program in testmain.c). 8140843Smsmith** - ROMable: Ficl is designed to work in RAM-based and ROM code / RAM data 8240843Smsmith** environments. It does require somewhat more memory than a pure 8340843Smsmith** ROM implementation because it builds its system dictionary in 8440843Smsmith** RAM at startup time. 8540843Smsmith** - Written an ANSI C to be as simple as I can make it to understand, 8640843Smsmith** support, debug, and port. Compiles without complaint at /Az /W4 8740843Smsmith** (require ANSI C, max warnings) under Microsoft VC++ 5. 8840843Smsmith** - Does full 32 bit math (but you need to implement 8940843Smsmith** two mixed precision math primitives (see sysdep.c)) 9040843Smsmith** - Indirect threaded interpreter is not the fastest kind of 9140843Smsmith** Forth there is (see pForth 68K for a really fast subroutine 9240843Smsmith** threaded interpreter), but it's the cleanest match to a 9340843Smsmith** pure C implementation. 9440843Smsmith** 9540843Smsmith** P O R T I N G F i c l 9640843Smsmith** 9740843Smsmith** To install Ficl on your target system, you need an ANSI C compiler 9840843Smsmith** and its runtime library. Inspect the system dependent macros and 9940843Smsmith** functions in sysdep.h and sysdep.c and edit them to suit your 10040843Smsmith** system. For example, INT16 is a short on some compilers and an 10140843Smsmith** int on others. Check the default CELL alignment controlled by 10240843Smsmith** FICL_ALIGN. If necessary, add new definitions of ficlMalloc, ficlFree, 10340843Smsmith** ficlLockDictionary, and ficlTextOut to work with your operating system. 10440843Smsmith** Finally, use testmain.c as a guide to installing the Ficl system and 10540843Smsmith** one or more virtual machines into your code. You do not need to include 10640843Smsmith** testmain.c in your build. 10740843Smsmith** 10840843Smsmith** T o D o L i s t 10940843Smsmith** 11040843Smsmith** 1. Unimplemented system dependent CORE word: key 11140843Smsmith** 2. Kludged CORE word: ACCEPT 11240843Smsmith** 3. Dictionary locking is full of holes - only one vm at a time 11340843Smsmith** can alter the dict. 11440843Smsmith** 4. Ficl uses the pad in CORE words - this violates the standard, 11540843Smsmith** but it's cleaner for a multithreaded system. I'll have to make a 11640843Smsmith** second pad for reference by the word PAD to fix this. 11743078Smsmith** 5. The whole inner interpreter is screwed up. It ought to be detached 11843078Smsmith** from ficlExec. Also, it should fall in line with exception 11943078Smsmith** handling by saving state. (sobral) 12043078Smsmith** 6. EXCEPTION should be cleaned. Right now, it doubles ficlExec's 12143078Smsmith** inner interpreter. (sobral) 12243078Smsmith** 7. colonParen must get the inner interpreter working on it's "case" 12343078Smsmith** *before* returning, so that it becomes possible to execute them 12443078Smsmith** inside other definitions without recreating the inner interpreter 12543078Smsmith** or other such hacks. (sobral) 12643078Smsmith** 8. We now have EXCEPTION word set. Let's: 12743078Smsmith** 8.1. Use the appropriate exceptions throughout the code. 12843078Smsmith** 8.2. Print the error messages at ficlExec, so someone can catch 12943078Smsmith** them first. (sobral) 13040843Smsmith** 13140843Smsmith** F o r M o r e I n f o r m a t i o n 13240843Smsmith** 13340843Smsmith** Web home of ficl 13440843Smsmith** http://www.taygeta.com/forth/compilers 13540843Smsmith** Check this website for Forth literature (including the ANSI standard) 13640843Smsmith** http://www.taygeta.com/forthlit.html 13740843Smsmith** and here for software and more links 13840843Smsmith** http://www.taygeta.com/forth.html 13940843Smsmith** 14040843Smsmith** Obvious Performance enhancement opportunities 14140843Smsmith** Compile speed 14240843Smsmith** - work on interpret speed 14340843Smsmith** - turn off locals (FICL_WANT_LOCALS) 14440843Smsmith** Interpret speed 14540843Smsmith** - Change inner interpreter (and everything else) 14640843Smsmith** so that a definition is a list of pointers to functions 14740843Smsmith** and inline data rather than pointers to words. This gets 14840843Smsmith** rid of vm->runningWord and a level of indirection in the 14940843Smsmith** inner loop. I'll look at it for ficl 3.0 15040843Smsmith** - Make the main hash table a bigger prime (HASHSIZE) 15140843Smsmith** - FORGET about twiddling the hash function - my experience is 15240843Smsmith** that that is a waste of time. 15340843Smsmith** - eliminate the need to pass the pVM parameter on the stack 15440843Smsmith** by dedicating a register to it. Most words need access to the 15540843Smsmith** vm, but the parameter passing overhead can be reduced. One way 15640843Smsmith** requires that the host OS have a task switch callout. Create 15740843Smsmith** a global variable for the running VM and refer to it in words 15840843Smsmith** that need VM access. Alternative: use thread local storage. 15940843Smsmith** For single threaded implementations, you can just use a global. 16040843Smsmith** The first two solutions create portability problems, so I 16140843Smsmith** haven't considered doing them. Another possibility is to 16240843Smsmith** declare the pVm parameter to be "register", and hope the compiler 16340843Smsmith** pays attention. 16440843Smsmith** 16540843Smsmith*/ 16640843Smsmith 16740843Smsmith/* 16840843Smsmith** Revision History: 16943078Smsmith** 17043078Smsmith** 12 Jan 1999 (sobral) Corrected EVALUATE behavior. Now TIB has an 17143078Smsmith** "end" field, and all words respect this. ficlExec is passed a "size" 17243078Smsmith** of TIB, as well as vmPushTib. This size is used to calculate the "end" 17343078Smsmith** of the string, ie, base+size. If the size is not known, pass -1. 17443078Smsmith** 17543078Smsmith** 10 Jan 1999 (sobral) EXCEPTION word set has been added, and existing 17643078Smsmith** words has been modified to conform to EXCEPTION EXT word set. 17743078Smsmith** 17840843Smsmith** 27 Aug 1998 (sadler) testing and corrections for LOCALS, LOCALS EXT, 17940843Smsmith** SEARCH / SEARCH EXT, TOOLS / TOOLS EXT. 18040843Smsmith** Added .X to display in hex, PARSE and PARSE-WORD to supplement WORD, 18140843Smsmith** EMPTY to clear stack. 18240843Smsmith** 18340843Smsmith** 29 jun 1998 (sadler) added variable sized hash table support 18440843Smsmith** and ANS Forth optional SEARCH & SEARCH EXT word set. 18540843Smsmith** 26 May 1998 (sadler) 18640843Smsmith** FICL_PROMPT macro 18740843Smsmith** 14 April 1998 (sadler) V1.04 18840843Smsmith** Ficlwin: Windows version, Skip Carter's Linux port 18940843Smsmith** 5 March 1998 (sadler) V1.03 19040843Smsmith** Bug fixes -- passes John Ryan's ANS test suite "core.fr" 19140843Smsmith** 19240843Smsmith** 24 February 1998 (sadler) V1.02 19340843Smsmith** -Fixed bugs in <# # #> 19440843Smsmith** -Changed FICL_WORD so that storage for the name characters 19540843Smsmith** can be allocated from the dictionary as needed rather than 19640843Smsmith** reserving 32 bytes in each word whether needed or not - 19740843Smsmith** this saved 50% of the dictionary storage requirement. 19840843Smsmith** -Added words in testmain for Win32 functions system,chdir,cwd, 19940843Smsmith** also added a word that loads and evaluates a file. 20040843Smsmith** 20140843Smsmith** December 1997 (sadler) 20240843Smsmith** -Added VM_RESTART exception handling in ficlExec -- this lets words 20340843Smsmith** that require additional text to succeed (like :, create, variable...) 20440843Smsmith** recover gracefully from an empty input buffer rather than emitting 20540843Smsmith** an error message. Definitions can span multiple input blocks with 20640843Smsmith** no restrictions. 20740843Smsmith** -Changed #include order so that <assert.h> is included in sysdep.h, 20840843Smsmith** and sysdep is included in all other files. This lets you define 20940843Smsmith** NDEBUG in sysdep.h to disable assertions if you want to. 21040843Smsmith** -Make PC specific system dependent code conditional on _M_IX86 21140843Smsmith** defined so that ports can coexist in sysdep.h/sysdep.c 21240843Smsmith*/ 21340843Smsmith 21440843Smsmith#ifdef __cplusplus 21540843Smsmithextern "C" { 21640843Smsmith#endif 21740843Smsmith 21840843Smsmith#include "sysdep.h" 21940843Smsmith#include <limits.h> /* UCHAR_MAX */ 22040843Smsmith 22140843Smsmith/* 22240843Smsmith** Forward declarations... read on. 22340843Smsmith*/ 22440843Smsmithstruct ficl_word; 22540843Smsmithstruct vm; 22640843Smsmithstruct ficl_dict; 22740843Smsmith 22840843Smsmith/* 22940843Smsmith** the Good Stuff starts here... 23040843Smsmith*/ 23140949Smsmith#define FICL_VER "2.02" 23240977Sjkh#ifndef FICL_PROMPT 23340949Smsmith# define FICL_PROMPT "ok> " 23440949Smsmith#endif 23540843Smsmith 23640843Smsmith/* 23740843Smsmith** ANS Forth requires false to be zero, and true to be the ones 23840843Smsmith** complement of false... that unifies logical and bitwise operations 23940843Smsmith** nicely. 24040843Smsmith*/ 24140843Smsmith#define FICL_TRUE (0xffffffffL) 24240843Smsmith#define FICL_FALSE (0) 24340843Smsmith#define FICL_BOOL(x) ((x) ? FICL_TRUE : FICL_FALSE) 24440843Smsmith 24540843Smsmith 24640843Smsmith/* 24740843Smsmith** A CELL is the main storage type. It must be large enough 24840843Smsmith** to contain a pointer or a scalar. Let's be picky and make 24940843Smsmith** a 32 bit cell explicitly... 25040843Smsmith*/ 25140843Smsmithtypedef union _cell 25240843Smsmith{ 25340843Smsmith INT32 i; 25440843Smsmith UNS32 u; 25540843Smsmith void *p; 25640843Smsmith} CELL; 25740843Smsmith 25840843Smsmith/* 25940843Smsmith** LVALUEtoCELL does a little pointer trickery to cast any 32 bit 26040843Smsmith** lvalue (informal definition: an expression whose result has an 26140843Smsmith** address) to CELL. Remember that constants and casts are NOT 26240843Smsmith** themselves lvalues! 26340843Smsmith*/ 26440843Smsmith#define LVALUEtoCELL(v) (*(CELL *)&v) 26540843Smsmith 26640843Smsmith/* 26740843Smsmith** PTRtoCELL is a cast through void * intended to satisfy the 26840843Smsmith** most outrageously pedantic compiler... (I won't mention 26940843Smsmith** its name) 27040843Smsmith*/ 27140843Smsmith#define PTRtoCELL (CELL *)(void *) 27240843Smsmith#define PTRtoSTRING (FICL_STRING *)(void *) 27340843Smsmith 27440843Smsmith/* 27540843Smsmith** Strings in FICL are stored in Pascal style - with a count 27640843Smsmith** preceding the text. We'll also NULL-terminate them so that 27740843Smsmith** they work with the usual C lib string functions. (Belt & 27840843Smsmith** suspenders? You decide.) 27940843Smsmith** STRINGINFO hides the implementation with a couple of 28040843Smsmith** macros for use in internal routines. 28140843Smsmith*/ 28240843Smsmith 28340843Smsmithtypedef unsigned char FICL_COUNT; 28440843Smsmith#define FICL_STRING_MAX UCHAR_MAX 28540843Smsmithtypedef struct _ficl_string 28640843Smsmith{ 28740843Smsmith FICL_COUNT count; 28840843Smsmith char text[1]; 28940843Smsmith} FICL_STRING; 29040843Smsmith 29140843Smsmithtypedef struct 29240843Smsmith{ 29340843Smsmith UNS32 count; 29440843Smsmith char *cp; 29540843Smsmith} STRINGINFO; 29640843Smsmith 29740843Smsmith#define SI_COUNT(si) (si.count) 29840843Smsmith#define SI_PTR(si) (si.cp) 29940843Smsmith#define SI_SETLEN(si, len) (si.count = (UNS32)(len)) 30040843Smsmith#define SI_SETPTR(si, ptr) (si.cp = (char *)(ptr)) 30140843Smsmith/* 30240843Smsmith** Init a STRINGINFO from a pointer to NULL-terminated string 30340843Smsmith*/ 30440843Smsmith#define SI_PSZ(si, psz) \ 30540843Smsmith {si.cp = psz; si.count = (FICL_COUNT)strlen(psz);} 30640843Smsmith/* 30740843Smsmith** Init a STRINGINFO from a pointer to FICL_STRING 30840843Smsmith*/ 30940843Smsmith#define SI_PFS(si, pfs) \ 31040843Smsmith {si.cp = pfs->text; si.count = pfs->count;} 31140843Smsmith 31240843Smsmith/* 31340843Smsmith** Ficl uses a this little structure to hold the address of 31440843Smsmith** the block of text it's working on and an index to the next 31540843Smsmith** unconsumed character in the string. Traditionally, this is 31640843Smsmith** done by a Text Input Buffer, so I've called this struct TIB. 31743078Smsmith** 31843078Smsmith** Since this structure also holds the size of the input buffer, 31943078Smsmith** and since evaluate requires that, let's put the size here. 32043078Smsmith** The size is stored as an end-pointer because that is what the 32143078Smsmith** null-terminated string aware functions find most easy to deal 32243078Smsmith** with. 32343078Smsmith** Notice, though, that nobody really uses this except evaluate, 32443078Smsmith** so it might just be moved to FICL_VM instead. (sobral) 32540843Smsmith*/ 32640843Smsmithtypedef struct 32740843Smsmith{ 32840843Smsmith INT32 index; 32943078Smsmith char *end; 33040843Smsmith char *cp; 33140843Smsmith} TIB; 33240843Smsmith 33340843Smsmith 33440843Smsmith/* 33540843Smsmith** Stacks get heavy use in Ficl and Forth... 33640843Smsmith** Each virtual machine implements two of them: 33740843Smsmith** one holds parameters (data), and the other holds return 33840843Smsmith** addresses and control flow information for the virtual 33940843Smsmith** machine. (Note: C's automatic stack is implicitly used, 34040843Smsmith** but not modeled because it doesn't need to be...) 34140843Smsmith** Here's an abstract type for a stack 34240843Smsmith*/ 34340843Smsmithtypedef struct _ficlStack 34440843Smsmith{ 34540843Smsmith UNS32 nCells; /* size of the stack */ 34640843Smsmith CELL *pFrame; /* link reg for stack frame */ 34740843Smsmith CELL *sp; /* stack pointer */ 34840843Smsmith CELL base[1]; /* Bottom of the stack */ 34940843Smsmith} FICL_STACK; 35040843Smsmith 35140843Smsmith/* 35240843Smsmith** Stack methods... many map closely to required Forth words. 35340843Smsmith*/ 35440843SmsmithFICL_STACK *stackCreate(unsigned nCells); 35540843Smsmithvoid stackDelete(FICL_STACK *pStack); 35640843Smsmithint stackDepth (FICL_STACK *pStack); 35740843Smsmithvoid stackDrop (FICL_STACK *pStack, int n); 35840843SmsmithCELL stackFetch (FICL_STACK *pStack, int n); 35940843SmsmithCELL stackGetTop(FICL_STACK *pStack); 36040843Smsmithvoid stackLink (FICL_STACK *pStack, int nCells); 36140843Smsmithvoid stackPick (FICL_STACK *pStack, int n); 36240843SmsmithCELL stackPop (FICL_STACK *pStack); 36340843Smsmithvoid *stackPopPtr (FICL_STACK *pStack); 36440843SmsmithUNS32 stackPopUNS32 (FICL_STACK *pStack); 36540843SmsmithINT32 stackPopINT32 (FICL_STACK *pStack); 36640843Smsmithvoid stackPush (FICL_STACK *pStack, CELL c); 36740843Smsmithvoid stackPushPtr (FICL_STACK *pStack, void *ptr); 36840843Smsmithvoid stackPushUNS32(FICL_STACK *pStack, UNS32 u); 36940843Smsmithvoid stackPushINT32(FICL_STACK *pStack, INT32 i); 37040843Smsmithvoid stackReset (FICL_STACK *pStack); 37140843Smsmithvoid stackRoll (FICL_STACK *pStack, int n); 37240843Smsmithvoid stackSetTop(FICL_STACK *pStack, CELL c); 37340843Smsmithvoid stackStore (FICL_STACK *pStack, int n, CELL c); 37440843Smsmithvoid stackUnlink(FICL_STACK *pStack); 37540843Smsmith 37640843Smsmith/* 37740843Smsmith** The virtual machine (VM) contains the state for one interpreter. 37840843Smsmith** Defined operations include: 37940843Smsmith** Create & initialize 38040843Smsmith** Delete 38140843Smsmith** Execute a block of text 38240843Smsmith** Parse a word out of the input stream 38340843Smsmith** Call return, and branch 38440843Smsmith** Text output 38540843Smsmith** Throw an exception 38640843Smsmith*/ 38740843Smsmith 38840843Smsmithtypedef struct ficl_word ** IPTYPE; /* the VM's instruction pointer */ 38940843Smsmith 39040843Smsmith/* 39140843Smsmith** Each VM has a placeholder for an output function - 39240843Smsmith** this makes it possible to have each VM do I/O 39340843Smsmith** through a different device. If you specify no 39440843Smsmith** OUTFUNC, it defaults to ficlTextOut. 39540843Smsmith*/ 39640843Smsmithtypedef void (*OUTFUNC)(struct vm *pVM, char *text, int fNewline); 39740843Smsmith 39840843Smsmith/* 39940843Smsmith** Each VM operates in one of two non-error states: interpreting 40040843Smsmith** or compiling. When interpreting, words are simply executed. 40140843Smsmith** When compiling, most words in the input stream have their 40240843Smsmith** addresses inserted into the word under construction. Some words 40340843Smsmith** (known as IMMEDIATE) are executed in the compile state, too. 40440843Smsmith*/ 40540843Smsmith/* values of STATE */ 40640843Smsmith#define INTERPRET 0 40740843Smsmith#define COMPILE 1 40840843Smsmith 40940843Smsmith/* 41040843Smsmith** The pad is a small scratch area for text manipulation. ANS Forth 41140843Smsmith** requires it to hold at least 84 characters. 41240843Smsmith*/ 41340843Smsmith#if !defined nPAD 41440843Smsmith#define nPAD 256 41540843Smsmith#endif 41640843Smsmith 41740843Smsmith/* 41840843Smsmith** ANS Forth requires that a word's name contain {1..31} characters. 41940843Smsmith*/ 42040843Smsmith#if !defined nFICLNAME 42140843Smsmith#define nFICLNAME 31 42240843Smsmith#endif 42340843Smsmith 42440843Smsmith/* 42540843Smsmith** OK - now we can really define the VM... 42640843Smsmith*/ 42740843Smsmithtypedef struct vm 42840843Smsmith{ 42940843Smsmith struct vm *link; /* Ficl keeps a VM list for simple teardown */ 43040843Smsmith jmp_buf *pState; /* crude exception mechanism... */ 43140843Smsmith OUTFUNC textOut; /* Output callback - see sysdep.c */ 43240843Smsmith void * pExtend; /* vm extension pointer */ 43340843Smsmith short fRestart; /* Set TRUE to restart runningWord */ 43440843Smsmith IPTYPE ip; /* instruction pointer */ 43540843Smsmith struct ficl_word 43640843Smsmith *runningWord;/* address of currently running word (often just *(ip-1) ) */ 43740843Smsmith UNS32 state; /* compiling or interpreting */ 43840843Smsmith UNS32 base; /* number conversion base */ 43940843Smsmith FICL_STACK *pStack; /* param stack */ 44040843Smsmith FICL_STACK *rStack; /* return stack */ 44140843Smsmith CELL sourceID; /* -1 if string, 0 if normal input */ 44240843Smsmith TIB tib; /* address of incoming text string */ 44340843Smsmith#if FICL_WANT_USER 44440843Smsmith CELL user[FICL_USER_CELLS]; 44540843Smsmith#endif 44640843Smsmith char pad[nPAD]; /* the scratch area (see above) */ 44740843Smsmith} FICL_VM; 44840843Smsmith 44940843Smsmith/* 45040843Smsmith** A FICL_CODE points to a function that gets called to help execute 45140843Smsmith** a word in the dictionary. It always gets passed a pointer to the 45240843Smsmith** running virtual machine, and from there it can get the address 45340843Smsmith** of the parameter area of the word it's supposed to operate on. 45440843Smsmith** For precompiled words, the code is all there is. For user defined 45540843Smsmith** words, the code assumes that the word's parameter area is a list 45640843Smsmith** of pointers to the code fields of other words to execute, and 45740843Smsmith** may also contain inline data. The first parameter is always 45840843Smsmith** a pointer to a code field. 45940843Smsmith*/ 46040843Smsmithtypedef void (*FICL_CODE)(FICL_VM *pVm); 46140843Smsmith 46240843Smsmith/* 46340843Smsmith** Ficl models memory as a contiguous space divided into 46440843Smsmith** words in a linked list called the dictionary. 46540843Smsmith** A FICL_WORD starts each entry in the list. 46640843Smsmith** Version 1.02: space for the name characters is allotted from 46740843Smsmith** the dictionary ahead of the word struct - this saves about half 46840843Smsmith** the storage on average with very little runtime cost. 46940843Smsmith*/ 47040843Smsmithtypedef struct ficl_word 47140843Smsmith{ 47240843Smsmith struct ficl_word *link; /* Previous word in the dictionary */ 47340843Smsmith UNS16 hash; 47440843Smsmith UNS8 flags; /* Immediate, Smudge, Compile-only */ 47540843Smsmith FICL_COUNT nName; /* Number of chars in word name */ 47640843Smsmith char *name; /* First nFICLNAME chars of word name */ 47740843Smsmith FICL_CODE code; /* Native code to execute the word */ 47840843Smsmith CELL param[1]; /* First data cell of the word */ 47940843Smsmith} FICL_WORD; 48040843Smsmith 48140843Smsmith/* 48240843Smsmith** Worst-case size of a word header: nFICLNAME chars in name 48340843Smsmith*/ 48440843Smsmith#define CELLS_PER_WORD \ 48540843Smsmith ( (sizeof (FICL_WORD) + nFICLNAME + sizeof (CELL)) \ 48640843Smsmith / (sizeof (CELL)) ) 48740843Smsmith 48840843Smsmithint wordIsImmediate(FICL_WORD *pFW); 48940843Smsmithint wordIsCompileOnly(FICL_WORD *pFW); 49040843Smsmith 49140843Smsmith/* flag values for word header */ 49240843Smsmith#define FW_IMMEDIATE 1 /* execute me even if compiling */ 49340843Smsmith#define FW_COMPILE 2 /* error if executed when not compiling */ 49440843Smsmith#define FW_SMUDGE 4 /* definition in progress - hide me */ 49540843Smsmith#define FW_CLASS 8 /* Word defines a class */ 49640843Smsmith 49740843Smsmith#define FW_COMPIMMED (FW_IMMEDIATE | FW_COMPILE) 49840843Smsmith#define FW_DEFAULT 0 49940843Smsmith 50040843Smsmith 50140843Smsmith/* 50240843Smsmith** Exit codes for vmThrow 50340843Smsmith*/ 50443078Smsmith#define VM_OUTOFTEXT -256 /* hungry - normal exit */ 50543078Smsmith#define VM_RESTART -257 /* word needs more text to suxcceed - re-run it */ 50643078Smsmith#define VM_USEREXIT -258 /* user wants to quit */ 50743078Smsmith#define VM_ERREXIT -259 /* interp found an error */ 50843078Smsmith#define VM_ABORT -1 /* like errexit -- abort */ 50943078Smsmith#define VM_ABORTQ -2 /* like errexit -- abort" */ 51043078Smsmith#define VM_QUIT -56 /* like errexit, but leave pStack & base alone */ 51140843Smsmith 51240843Smsmith 51340843Smsmithvoid vmBranchRelative(FICL_VM *pVM, int offset); 51440843SmsmithFICL_VM * vmCreate (FICL_VM *pVM, unsigned nPStack, unsigned nRStack); 51540843Smsmithvoid vmDelete (FICL_VM *pVM); 51640843Smsmithvoid vmExecute(FICL_VM *pVM, FICL_WORD *pWord); 51740843Smsmithchar * vmGetString(FICL_VM *pVM, FICL_STRING *spDest, char delimiter); 51840843SmsmithSTRINGINFO vmGetWord(FICL_VM *pVM); 51940843SmsmithSTRINGINFO vmGetWord0(FICL_VM *pVM); 52040843Smsmithint vmGetWordToPad(FICL_VM *pVM); 52140843SmsmithSTRINGINFO vmParseString(FICL_VM *pVM, char delimiter); 52240843Smsmithvoid vmPopIP (FICL_VM *pVM); 52340843Smsmithvoid vmPushIP (FICL_VM *pVM, IPTYPE newIP); 52440843Smsmithvoid vmQuit (FICL_VM *pVM); 52540843Smsmithvoid vmReset (FICL_VM *pVM); 52640843Smsmithvoid vmSetTextOut(FICL_VM *pVM, OUTFUNC textOut); 52740843Smsmithvoid vmTextOut(FICL_VM *pVM, char *text, int fNewline); 52840843Smsmithvoid vmThrow (FICL_VM *pVM, int except); 52940843Smsmithvoid vmThrowErr(FICL_VM *pVM, char *fmt, ...); 53040843Smsmith 53140843Smsmith/* 53240843Smsmith** vmCheckStack needs a vm pointer because it might have to say 53340843Smsmith** something if it finds a problem. Parms popCells and pushCells 53440843Smsmith** correspond to the number of parameters on the left and right of 53540843Smsmith** a word's stack effect comment. 53640843Smsmith*/ 53740843Smsmithvoid vmCheckStack(FICL_VM *pVM, int popCells, int pushCells); 53840843Smsmith 53940843Smsmith/* 54040843Smsmith** TIB access routines... 54140843Smsmith** ANS forth seems to require the input buffer to be represented 54240843Smsmith** as a pointer to the start of the buffer, and an index to the 54340843Smsmith** next character to read. 54440843Smsmith** PushTib points the VM to a new input string and optionally 54540843Smsmith** returns a copy of the current state 54640843Smsmith** PopTib restores the TIB state given a saved TIB from PushTib 54740843Smsmith** GetInBuf returns a pointer to the next unused char of the TIB 54840843Smsmith*/ 54943078Smsmithvoid vmPushTib(FICL_VM *pVM, char *text, INT32 size, TIB *pSaveTib); 55040843Smsmithvoid vmPopTib(FICL_VM *pVM, TIB *pTib); 55140843Smsmith#define vmGetInBuf(pVM) ((pVM)->tib.cp + (pVM)->tib.index) 55240843Smsmith#define vmSetTibIndex(pVM, i) (pVM)->tib.index = i 55340843Smsmith#define vmUpdateTib(pVM, str) (pVM)->tib.index = (str) - (pVM)->tib.cp 55440843Smsmith 55540843Smsmith/* 55640843Smsmith** Generally useful string manipulators omitted by ANSI C... 55740843Smsmith** ltoa complements strtol 55840843Smsmith*/ 55940843Smsmith#if defined(_WIN32) && !FICL_MAIN 56040843Smsmith/* #SHEESH 56140843Smsmith** Why do Microsoft Meatballs insist on contaminating 56240843Smsmith** my namespace with their string functions??? 56340843Smsmith*/ 56440843Smsmith#pragma warning(disable: 4273) 56540843Smsmith#endif 56640843Smsmith 56740843Smsmithchar *ltoa( INT32 value, char *string, int radix ); 56840843Smsmithchar *ultoa(UNS32 value, char *string, int radix ); 56940843Smsmithchar digit_to_char(int value); 57040843Smsmithchar *strrev( char *string ); 57143078Smsmithchar *skipSpace(char *cp,char *end); 57240843Smsmithchar *caseFold(char *cp); 57340843Smsmithint strincmp(char *cp1, char *cp2, FICL_COUNT count); 57440843Smsmith 57540843Smsmith#if defined(_WIN32) && !FICL_MAIN 57640843Smsmith#pragma warning(default: 4273) 57740843Smsmith#endif 57840843Smsmith 57940843Smsmith/* 58040843Smsmith** Ficl hash table - variable size. 58140843Smsmith** assert(size > 0) 58240843Smsmith** If size is 1, the table degenerates into a linked list. 58340843Smsmith** A WORDLIST (see the search order word set in DPANS) is 58440843Smsmith** just a pointer to a FICL_HASH in this implementation. 58540843Smsmith*/ 58640843Smsmith#if !defined HASHSIZE /* Default size of hash table. For best */ 58740843Smsmith#define HASHSIZE 127 /* performance, use a prime number! */ 58840843Smsmith#endif 58940843Smsmith 59040843Smsmithtypedef struct ficl_hash 59140843Smsmith{ 59240843Smsmith struct ficl_hash *link; /* eventual inheritance support */ 59340843Smsmith unsigned size; 59440843Smsmith FICL_WORD *table[1]; 59540843Smsmith} FICL_HASH; 59640843Smsmith 59740843Smsmithvoid hashForget(FICL_HASH *pHash, void *where); 59840843SmsmithUNS16 hashHashCode(STRINGINFO si); 59940843Smsmithvoid hashInsertWord(FICL_HASH *pHash, FICL_WORD *pFW); 60040843SmsmithFICL_WORD *hashLookup(struct ficl_hash *pHash, 60140843Smsmith STRINGINFO si, 60240843Smsmith UNS16 hashCode); 60340843Smsmithvoid hashReset(FICL_HASH *pHash); 60440843Smsmith 60540843Smsmith/* 60640843Smsmith** A Dictionary is a linked list of FICL_WORDs. It is also Ficl's 60740843Smsmith** memory model. Description of fields: 60840843Smsmith** 60940843Smsmith** here -- points to the next free byte in the dictionary. This 61040843Smsmith** pointer is forced to be CELL-aligned before a definition is added. 61140843Smsmith** Do not assume any specific alignment otherwise - Use dictAlign(). 61240843Smsmith** 61340843Smsmith** smudge -- pointer to word currently being defined (or last defined word) 61440843Smsmith** If the definition completes successfully, the word will be 61540843Smsmith** linked into the hash table. If unsuccessful, dictUnsmudge 61640843Smsmith** uses this pointer to restore the previous state of the dictionary. 61740843Smsmith** Smudge prevents unintentional recursion as a side-effect: the 61840843Smsmith** dictionary search algo examines only completed definitions, so a 61940843Smsmith** word cannot invoke itself by name. See the ficl word "recurse". 62040843Smsmith** NOTE: smudge always points to the last word defined. IMMEDIATE 62140843Smsmith** makes use of this fact. Smudge is initially NULL. 62240843Smsmith** 62340843Smsmith** pForthWords -- pointer to the default wordlist (FICL_HASH). 62440843Smsmith** This is the initial compilation list, and contains all 62540843Smsmith** ficl's precompiled words. 62640843Smsmith** 62740843Smsmith** pCompile -- compilation wordlist - initially equal to pForthWords 62840843Smsmith** pSearch -- array of pointers to wordlists. Managed as a stack. 62940843Smsmith** Highest index is the first list in the search order. 63040843Smsmith** nLists -- number of lists in pSearch. nLists-1 is the highest 63140843Smsmith** filled slot in pSearch, and points to the first wordlist 63240843Smsmith** in the search order 63340843Smsmith** size -- number of cells in the dictionary (total) 63440843Smsmith** dict -- start of data area. Must be at the end of the struct. 63540843Smsmith*/ 63640843Smsmithtypedef struct ficl_dict 63740843Smsmith{ 63840843Smsmith CELL *here; 63940843Smsmith FICL_WORD *smudge; 64040843Smsmith FICL_HASH *pForthWords; 64140843Smsmith FICL_HASH *pCompile; 64240843Smsmith FICL_HASH *pSearch[FICL_DEFAULT_VOCS]; 64340843Smsmith int nLists; 64440843Smsmith unsigned size; /* Number of cells in dict (total)*/ 64540843Smsmith CELL dict[1]; /* Base of dictionary memory */ 64640843Smsmith} FICL_DICT; 64740843Smsmith 64840843Smsmithvoid *alignPtr(void *ptr); 64940843Smsmithvoid dictAbortDefinition(FICL_DICT *pDict); 65040843Smsmithvoid dictAlign(FICL_DICT *pDict); 65140843Smsmithint dictAllot(FICL_DICT *pDict, int n); 65240843Smsmithint dictAllotCells(FICL_DICT *pDict, int nCells); 65340843Smsmithvoid dictAppendCell(FICL_DICT *pDict, CELL c); 65440843Smsmithvoid dictAppendChar(FICL_DICT *pDict, char c); 65540843SmsmithFICL_WORD *dictAppendWord(FICL_DICT *pDict, 65640843Smsmith char *name, 65740843Smsmith FICL_CODE pCode, 65840843Smsmith UNS8 flags); 65940843SmsmithFICL_WORD *dictAppendWord2(FICL_DICT *pDict, 66040843Smsmith STRINGINFO si, 66140843Smsmith FICL_CODE pCode, 66240843Smsmith UNS8 flags); 66340843Smsmithvoid dictAppendUNS32(FICL_DICT *pDict, UNS32 u); 66440843Smsmithint dictCellsAvail(FICL_DICT *pDict); 66540843Smsmithint dictCellsUsed (FICL_DICT *pDict); 66640843Smsmithvoid dictCheck(FICL_DICT *pDict, FICL_VM *pVM, int nCells); 66740843SmsmithFICL_DICT *dictCreate(unsigned nCELLS); 66840843SmsmithFICL_DICT *dictCreateHashed(unsigned nCells, unsigned nHash); 66940843Smsmithvoid dictDelete(FICL_DICT *pDict); 67040843Smsmithvoid dictEmpty(FICL_DICT *pDict, unsigned nHash); 67140843Smsmithvoid dictHashSummary(FICL_VM *pVM); 67240843Smsmithint dictIncludes(FICL_DICT *pDict, void *p); 67340843SmsmithFICL_WORD *dictLookup(FICL_DICT *pDict, STRINGINFO si); 67440843Smsmith#if FICL_WANT_LOCALS 67540843SmsmithFICL_WORD *dictLookupLoc(FICL_DICT *pDict, STRINGINFO si); 67640843Smsmith#endif 67740843Smsmithvoid dictResetSearchOrder(FICL_DICT *pDict); 67840843Smsmithvoid dictSetFlags(FICL_DICT *pDict, UNS8 set, UNS8 clr); 67940843Smsmithvoid dictSetImmediate(FICL_DICT *pDict); 68040843Smsmithvoid dictUnsmudge(FICL_DICT *pDict); 68140843SmsmithCELL *dictWhere(FICL_DICT *pDict); 68240843Smsmith 68340843Smsmith 68440843Smsmith/* 68540843Smsmith** External interface to FICL... 68640843Smsmith*/ 68740843Smsmith/* 68840843Smsmith** f i c l I n i t S y s t e m 68940843Smsmith** Binds a global dictionary to the interpreter system and initializes 69040843Smsmith** the dict to contain the ANSI CORE wordset. 69140843Smsmith** You specify the address and size of the allocated area. 69240843Smsmith** After that, ficl manages it. 69340843Smsmith** First step is to set up the static pointers to the area. 69440843Smsmith** Then write the "precompiled" portion of the dictionary in. 69540843Smsmith** The dictionary needs to be at least large enough to hold the 69640843Smsmith** precompiled part. Try 1K cells minimum. Use "words" to find 69740843Smsmith** out how much of the dictionary is used at any time. 69840843Smsmith*/ 69940843Smsmithvoid ficlInitSystem(int nDictCells); 70040843Smsmith 70140843Smsmith/* 70240843Smsmith** f i c l T e r m S y s t e m 70340843Smsmith** Deletes the system dictionary and all virtual machines that 70440843Smsmith** were created with ficlNewVM (see below). Call this function to 70540843Smsmith** reclaim all memory used by the dictionary and VMs. 70640843Smsmith*/ 70740843Smsmithvoid ficlTermSystem(void); 70840843Smsmith 70940843Smsmith/* 71040843Smsmith** f i c l E x e c 71140843Smsmith** Evaluates a block of input text in the context of the 71240843Smsmith** specified interpreter. Emits any requested output to the 71343078Smsmith** interpreter's output function. If the size of the input 71443078Smsmith** is not known, pass -1. 71540843Smsmith** Execution returns when the text block has been executed, 71640843Smsmith** or an error occurs. 71740843Smsmith** Returns one of the VM_XXXX codes defined in ficl.h: 71840843Smsmith** VM_OUTOFTEXT is the normal exit condition 71940843Smsmith** VM_ERREXIT means that the interp encountered a syntax error 72040843Smsmith** and the vm has been reset to recover (some or all 72140843Smsmith** of the text block got ignored 72240843Smsmith** VM_USEREXIT means that the user executed the "bye" command 72340843Smsmith** to shut down the interpreter. This would be a good 72440843Smsmith** time to delete the vm, etc -- or you can ignore this 72540843Smsmith** signal. 72643078Smsmith** VM_ABORT and VM_ABORTQ are generated by 'abort' and 'abort"' 72743078Smsmith** commands. 72840843Smsmith** Preconditions: successful execution of ficlInitSystem, 72940843Smsmith** Successful creation and init of the VM by ficlNewVM (or equiv) 73040843Smsmith*/ 73143078Smsmithint ficlExec(FICL_VM *pVM, char *pText, INT32 size); 73240843Smsmith 73340843Smsmith/* 73440989Sjkh** ficlExecFD(FICL_VM *pVM, int fd); 73540989Sjkh * Evaluates text from file passed in via fd. 73640989Sjkh * Execution returns when all of file has been executed or an 73740989Sjkh * error occurs. 73840989Sjkh */ 73940989Sjkhint ficlExecFD(FICL_VM *pVM, int fd); 74040989Sjkh 74140989Sjkh/* 74240843Smsmith** Create a new VM from the heap, and link it into the system VM list. 74340843Smsmith** Initializes the VM and binds default sized stacks to it. Returns the 74440843Smsmith** address of the VM, or NULL if an error occurs. 74540843Smsmith** Precondition: successful execution of ficlInitSystem 74640843Smsmith*/ 74740843SmsmithFICL_VM *ficlNewVM(void); 74840843Smsmith 74940843Smsmith/* 75040843Smsmith** Returns the address of the most recently defined word in the system 75140843Smsmith** dictionary with the given name, or NULL if no match. 75240843Smsmith** Precondition: successful execution of ficlInitSystem 75340843Smsmith*/ 75440843SmsmithFICL_WORD *ficlLookup(char *name); 75540843Smsmith 75640843Smsmith/* 75740843Smsmith** f i c l G e t D i c t 75840843Smsmith** Utility function - returns the address of the system dictionary. 75940843Smsmith** Precondition: successful execution of ficlInitSystem 76040843Smsmith*/ 76140843SmsmithFICL_DICT *ficlGetDict(void); 76240843SmsmithFICL_DICT *ficlGetEnv(void); 76340843Smsmithvoid ficlSetEnv(char *name, UNS32 value); 76440843Smsmithvoid ficlSetEnvD(char *name, UNS32 hi, UNS32 lo); 76540843Smsmith#if FICL_WANT_LOCALS 76640843SmsmithFICL_DICT *ficlGetLoc(void); 76740843Smsmith#endif 76840843Smsmith/* 76940843Smsmith** f i c l B u i l d 77040843Smsmith** Builds a word into the system default dictionary in a thread-safe way. 77140843Smsmith** Preconditions: system must be initialized, and there must 77240843Smsmith** be enough space for the new word's header! Operation is 77340843Smsmith** controlled by ficlLockDictionary, so any initialization 77440843Smsmith** required by your version of the function (if you "overrode" 77540843Smsmith** it) must be complete at this point. 77640843Smsmith** Parameters: 77740843Smsmith** name -- the name of the word to be built 77840843Smsmith** code -- code to execute when the word is invoked - must take a single param 77940843Smsmith** pointer to a FICL_VM 78040843Smsmith** flags -- 0 or more of FW_IMMEDIATE, FW_COMPILE, use bitwise OR! 78140843Smsmith** Most words can use FW_DEFAULT. 78240843Smsmith** nAllot - number of extra cells to allocate in the parameter area (usually zero) 78340843Smsmith*/ 78440843Smsmithint ficlBuild(char *name, FICL_CODE code, char flags); 78540843Smsmith 78640843Smsmith/* 78740843Smsmith** f i c l C o m p i l e C o r e 78840843Smsmith** Builds the ANS CORE wordset into the dictionary - called by 78940843Smsmith** ficlInitSystem - no need to waste dict space by doing it again. 79040843Smsmith*/ 79140843Smsmithvoid ficlCompileCore(FICL_DICT *dp); 79240843Smsmithvoid ficlCompileSoftCore(FICL_VM *pVM); 79340843Smsmith 79440843Smsmith/* 79540843Smsmith** from words.c... 79640843Smsmith*/ 79740843Smsmithvoid constantParen(FICL_VM *pVM); 79840843Smsmithvoid twoConstParen(FICL_VM *pVM); 79940843Smsmith 80043139Smsmith/* 80143139Smsmith** So we can more easily debug... 80243139Smsmith*/ 80343139Smsmith#ifdef FICL_TRACE 80443139Smsmithextern int ficl_trace; 80543139Smsmith#endif 80643139Smsmith 80742679Sabial#if defined(__i386__) && !defined(TESTMAIN) 80842679Sabialextern void ficlOutb(FICL_VM *pVM); 80942679Sabialextern void ficlInb(FICL_VM *pVM); 81042634Sabial#endif 81142634Sabial 81240843Smsmith#ifdef __cplusplus 81340843Smsmith} 81440843Smsmith#endif 81540843Smsmith 81640843Smsmith#endif /* __FICL_H__ */ 817