ficl.h revision 56719
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 3051786Sdcs/* $FreeBSD: head/sys/boot/ficl/ficl.h 56719 2000-01-28 03:59:12Z dcs $ */ 3151786Sdcs 3240843Smsmith#if !defined (__FICL_H__) 3340843Smsmith#define __FICL_H__ 3440843Smsmith/* 3540843Smsmith** Ficl (Forth-inspired command language) is an ANS Forth 3640843Smsmith** interpreter written in C. Unlike traditional Forths, this 3740843Smsmith** interpreter is designed to be embedded into other systems 3840843Smsmith** as a command/macro/development prototype language. 3940843Smsmith** 4040843Smsmith** Where Forths usually view themselves as the center of the system 4140843Smsmith** and expect the rest of the system to be coded in Forth, Ficl 4240843Smsmith** acts as a component of the system. It is easy to export 4340843Smsmith** code written in C or ASM to Ficl in the style of TCL, or to invoke 4440843Smsmith** Ficl code from a compiled module. This allows you to do incremental 4540843Smsmith** development in a way that combines the best features of threaded 4640843Smsmith** languages (rapid development, quick code/test/debug cycle, 4740843Smsmith** reasonably fast) with the best features of C (everyone knows it, 4840843Smsmith** easier to support large blocks of code, efficient, type checking). 4940843Smsmith** 5040843Smsmith** Ficl provides facilities for interoperating 5140843Smsmith** with programs written in C: C functions can be exported to Ficl, 5240843Smsmith** and Ficl commands can be executed via a C calling interface. The 5340843Smsmith** interpreter is re-entrant, so it can be used in multiple instances 5440843Smsmith** in a multitasking system. Unlike Forth, Ficl's outer interpreter 5540843Smsmith** expects a text block as input, and returns to the caller after each 5640843Smsmith** text block, so the "data pump" is somewhere in external code. This 5740843Smsmith** is more like TCL than Forth, which usually expcets to be at the center 5840843Smsmith** of the system, requesting input at its convenience. Each Ficl virtual 5940843Smsmith** machine can be bound to a different I/O channel, and is independent 6040843Smsmith** of all others in in the same address space except that all virtual 6140843Smsmith** machines share a common dictionary (a sort or open symbol table that 6240843Smsmith** defines all of the elements of the language). 6340843Smsmith** 6440843Smsmith** Code is written in ANSI C for portability. 6540843Smsmith** 6640843Smsmith** Summary of Ficl features and constraints: 6740843Smsmith** - Standard: Implements the ANSI Forth CORE word set and part 6840843Smsmith** of the CORE EXT word-set, SEARCH and SEARCH EXT, TOOLS and 6940843Smsmith** TOOLS EXT, LOCAL and LOCAL ext and various extras. 7040843Smsmith** - Extensible: you can export code written in Forth, C, 7140843Smsmith** or asm in a straightforward way. Ficl provides open 7240843Smsmith** facilities for extending the language in an application 7340843Smsmith** specific way. You can even add new control structures! 7440843Smsmith** - Ficl and C can interact in two ways: Ficl can encapsulate 7540843Smsmith** C code, or C code can invoke Ficl code. 7640843Smsmith** - Thread-safe, re-entrant: The shared system dictionary 7740843Smsmith** uses a locking mechanism that you can either supply 7840843Smsmith** or stub out to provide exclusive access. Each Ficl 7940843Smsmith** virtual machine has an otherwise complete state, and 8040843Smsmith** each can be bound to a separate I/O channel (or none at all). 8140843Smsmith** - Simple encapsulation into existing systems: a basic implementation 8240843Smsmith** requires three function calls (see the example program in testmain.c). 8340843Smsmith** - ROMable: Ficl is designed to work in RAM-based and ROM code / RAM data 8440843Smsmith** environments. It does require somewhat more memory than a pure 8540843Smsmith** ROM implementation because it builds its system dictionary in 8640843Smsmith** RAM at startup time. 8740843Smsmith** - Written an ANSI C to be as simple as I can make it to understand, 8840843Smsmith** support, debug, and port. Compiles without complaint at /Az /W4 8940843Smsmith** (require ANSI C, max warnings) under Microsoft VC++ 5. 9040843Smsmith** - Does full 32 bit math (but you need to implement 9140843Smsmith** two mixed precision math primitives (see sysdep.c)) 9240843Smsmith** - Indirect threaded interpreter is not the fastest kind of 9340843Smsmith** Forth there is (see pForth 68K for a really fast subroutine 9440843Smsmith** threaded interpreter), but it's the cleanest match to a 9540843Smsmith** pure C implementation. 9640843Smsmith** 9740843Smsmith** P O R T I N G F i c l 9840843Smsmith** 9940843Smsmith** To install Ficl on your target system, you need an ANSI C compiler 10040843Smsmith** and its runtime library. Inspect the system dependent macros and 10140843Smsmith** functions in sysdep.h and sysdep.c and edit them to suit your 10240843Smsmith** system. For example, INT16 is a short on some compilers and an 10340843Smsmith** int on others. Check the default CELL alignment controlled by 10440843Smsmith** FICL_ALIGN. If necessary, add new definitions of ficlMalloc, ficlFree, 10540843Smsmith** ficlLockDictionary, and ficlTextOut to work with your operating system. 10640843Smsmith** Finally, use testmain.c as a guide to installing the Ficl system and 10740843Smsmith** one or more virtual machines into your code. You do not need to include 10840843Smsmith** testmain.c in your build. 10940843Smsmith** 11040843Smsmith** T o D o L i s t 11140843Smsmith** 11240843Smsmith** 1. Unimplemented system dependent CORE word: key 11340843Smsmith** 2. Kludged CORE word: ACCEPT 11440843Smsmith** 3. Dictionary locking is full of holes - only one vm at a time 11540843Smsmith** can alter the dict. 11640843Smsmith** 4. Ficl uses the pad in CORE words - this violates the standard, 11740843Smsmith** but it's cleaner for a multithreaded system. I'll have to make a 11840843Smsmith** second pad for reference by the word PAD to fix this. 11940843Smsmith** 12040843Smsmith** F o r M o r e I n f o r m a t i o n 12140843Smsmith** 12240843Smsmith** Web home of ficl 12340843Smsmith** http://www.taygeta.com/forth/compilers 12440843Smsmith** Check this website for Forth literature (including the ANSI standard) 12540843Smsmith** http://www.taygeta.com/forthlit.html 12640843Smsmith** and here for software and more links 12740843Smsmith** http://www.taygeta.com/forth.html 12840843Smsmith** 12940843Smsmith** Obvious Performance enhancement opportunities 13040843Smsmith** Compile speed 13140843Smsmith** - work on interpret speed 13240843Smsmith** - turn off locals (FICL_WANT_LOCALS) 13340843Smsmith** Interpret speed 13440843Smsmith** - Change inner interpreter (and everything else) 13540843Smsmith** so that a definition is a list of pointers to functions 13640843Smsmith** and inline data rather than pointers to words. This gets 13740843Smsmith** rid of vm->runningWord and a level of indirection in the 13840843Smsmith** inner loop. I'll look at it for ficl 3.0 13940843Smsmith** - Make the main hash table a bigger prime (HASHSIZE) 14040843Smsmith** - FORGET about twiddling the hash function - my experience is 14140843Smsmith** that that is a waste of time. 14240843Smsmith** - eliminate the need to pass the pVM parameter on the stack 14340843Smsmith** by dedicating a register to it. Most words need access to the 14440843Smsmith** vm, but the parameter passing overhead can be reduced. One way 14540843Smsmith** requires that the host OS have a task switch callout. Create 14640843Smsmith** a global variable for the running VM and refer to it in words 14740843Smsmith** that need VM access. Alternative: use thread local storage. 14840843Smsmith** For single threaded implementations, you can just use a global. 14940843Smsmith** The first two solutions create portability problems, so I 15040843Smsmith** haven't considered doing them. Another possibility is to 15140843Smsmith** declare the pVm parameter to be "register", and hope the compiler 15240843Smsmith** pays attention. 15340843Smsmith** 15440843Smsmith*/ 15540843Smsmith 15640843Smsmith/* 15740843Smsmith** Revision History: 15851786Sdcs** 15951786Sdcs** 15 Apr 1999 (sadler) Merged FreeBSD changes for exception wordset and 16051786Sdcs** counted strings in ficlExec. 16143078Smsmith** 12 Jan 1999 (sobral) Corrected EVALUATE behavior. Now TIB has an 16243078Smsmith** "end" field, and all words respect this. ficlExec is passed a "size" 16343078Smsmith** of TIB, as well as vmPushTib. This size is used to calculate the "end" 16443078Smsmith** of the string, ie, base+size. If the size is not known, pass -1. 16543078Smsmith** 16643078Smsmith** 10 Jan 1999 (sobral) EXCEPTION word set has been added, and existing 16743078Smsmith** words has been modified to conform to EXCEPTION EXT word set. 16843078Smsmith** 16940843Smsmith** 27 Aug 1998 (sadler) testing and corrections for LOCALS, LOCALS EXT, 17040843Smsmith** SEARCH / SEARCH EXT, TOOLS / TOOLS EXT. 17140843Smsmith** Added .X to display in hex, PARSE and PARSE-WORD to supplement WORD, 17240843Smsmith** EMPTY to clear stack. 17340843Smsmith** 17440843Smsmith** 29 jun 1998 (sadler) added variable sized hash table support 17540843Smsmith** and ANS Forth optional SEARCH & SEARCH EXT word set. 17640843Smsmith** 26 May 1998 (sadler) 17740843Smsmith** FICL_PROMPT macro 17840843Smsmith** 14 April 1998 (sadler) V1.04 17940843Smsmith** Ficlwin: Windows version, Skip Carter's Linux port 18040843Smsmith** 5 March 1998 (sadler) V1.03 18140843Smsmith** Bug fixes -- passes John Ryan's ANS test suite "core.fr" 18240843Smsmith** 18340843Smsmith** 24 February 1998 (sadler) V1.02 18440843Smsmith** -Fixed bugs in <# # #> 18540843Smsmith** -Changed FICL_WORD so that storage for the name characters 18640843Smsmith** can be allocated from the dictionary as needed rather than 18740843Smsmith** reserving 32 bytes in each word whether needed or not - 18840843Smsmith** this saved 50% of the dictionary storage requirement. 18940843Smsmith** -Added words in testmain for Win32 functions system,chdir,cwd, 19040843Smsmith** also added a word that loads and evaluates a file. 19140843Smsmith** 19240843Smsmith** December 1997 (sadler) 19340843Smsmith** -Added VM_RESTART exception handling in ficlExec -- this lets words 19440843Smsmith** that require additional text to succeed (like :, create, variable...) 19540843Smsmith** recover gracefully from an empty input buffer rather than emitting 19640843Smsmith** an error message. Definitions can span multiple input blocks with 19740843Smsmith** no restrictions. 19840843Smsmith** -Changed #include order so that <assert.h> is included in sysdep.h, 19940843Smsmith** and sysdep is included in all other files. This lets you define 20040843Smsmith** NDEBUG in sysdep.h to disable assertions if you want to. 20140843Smsmith** -Make PC specific system dependent code conditional on _M_IX86 20240843Smsmith** defined so that ports can coexist in sysdep.h/sysdep.c 20340843Smsmith*/ 20440843Smsmith 20540843Smsmith#ifdef __cplusplus 20640843Smsmithextern "C" { 20740843Smsmith#endif 20840843Smsmith 20940843Smsmith#include "sysdep.h" 21040843Smsmith#include <limits.h> /* UCHAR_MAX */ 21140843Smsmith 21240843Smsmith/* 21340843Smsmith** Forward declarations... read on. 21440843Smsmith*/ 21540843Smsmithstruct ficl_word; 21640843Smsmithstruct vm; 21740843Smsmithstruct ficl_dict; 21840843Smsmith 21940843Smsmith/* 22040843Smsmith** the Good Stuff starts here... 22140843Smsmith*/ 22251786Sdcs#define FICL_VER "2.03" 22351786Sdcs#if !defined (FICL_PROMPT) 22451786Sdcs#define FICL_PROMPT "ok> " 22540949Smsmith#endif 22640843Smsmith 22740843Smsmith/* 22840843Smsmith** ANS Forth requires false to be zero, and true to be the ones 22940843Smsmith** complement of false... that unifies logical and bitwise operations 23040843Smsmith** nicely. 23140843Smsmith*/ 23256719Sdcs#define FICL_TRUE (~(0L)) 23340843Smsmith#define FICL_FALSE (0) 23440843Smsmith#define FICL_BOOL(x) ((x) ? FICL_TRUE : FICL_FALSE) 23540843Smsmith 23640843Smsmith 23740843Smsmith/* 23840843Smsmith** A CELL is the main storage type. It must be large enough 23951786Sdcs** to contain a pointer or a scalar. In order to accommodate 24051786Sdcs** 32 bit and 64 bit processors, use abstract types for i and u. 24140843Smsmith*/ 24240843Smsmithtypedef union _cell 24340843Smsmith{ 24451786Sdcs FICL_INT i; 24551786Sdcs FICL_UNS u; 24640843Smsmith void *p; 24740843Smsmith} CELL; 24840843Smsmith 24940843Smsmith/* 25040843Smsmith** LVALUEtoCELL does a little pointer trickery to cast any 32 bit 25140843Smsmith** lvalue (informal definition: an expression whose result has an 25240843Smsmith** address) to CELL. Remember that constants and casts are NOT 25340843Smsmith** themselves lvalues! 25440843Smsmith*/ 25540843Smsmith#define LVALUEtoCELL(v) (*(CELL *)&v) 25640843Smsmith 25740843Smsmith/* 25840843Smsmith** PTRtoCELL is a cast through void * intended to satisfy the 25940843Smsmith** most outrageously pedantic compiler... (I won't mention 26040843Smsmith** its name) 26140843Smsmith*/ 26240843Smsmith#define PTRtoCELL (CELL *)(void *) 26340843Smsmith#define PTRtoSTRING (FICL_STRING *)(void *) 26440843Smsmith 26540843Smsmith/* 26640843Smsmith** Strings in FICL are stored in Pascal style - with a count 26740843Smsmith** preceding the text. We'll also NULL-terminate them so that 26840843Smsmith** they work with the usual C lib string functions. (Belt & 26940843Smsmith** suspenders? You decide.) 27040843Smsmith** STRINGINFO hides the implementation with a couple of 27140843Smsmith** macros for use in internal routines. 27240843Smsmith*/ 27340843Smsmith 27440843Smsmithtypedef unsigned char FICL_COUNT; 27540843Smsmith#define FICL_STRING_MAX UCHAR_MAX 27640843Smsmithtypedef struct _ficl_string 27740843Smsmith{ 27840843Smsmith FICL_COUNT count; 27940843Smsmith char text[1]; 28040843Smsmith} FICL_STRING; 28140843Smsmith 28240843Smsmithtypedef struct 28340843Smsmith{ 28440843Smsmith UNS32 count; 28540843Smsmith char *cp; 28640843Smsmith} STRINGINFO; 28740843Smsmith 28840843Smsmith#define SI_COUNT(si) (si.count) 28940843Smsmith#define SI_PTR(si) (si.cp) 29040843Smsmith#define SI_SETLEN(si, len) (si.count = (UNS32)(len)) 29140843Smsmith#define SI_SETPTR(si, ptr) (si.cp = (char *)(ptr)) 29240843Smsmith/* 29340843Smsmith** Init a STRINGINFO from a pointer to NULL-terminated string 29440843Smsmith*/ 29540843Smsmith#define SI_PSZ(si, psz) \ 29640843Smsmith {si.cp = psz; si.count = (FICL_COUNT)strlen(psz);} 29740843Smsmith/* 29840843Smsmith** Init a STRINGINFO from a pointer to FICL_STRING 29940843Smsmith*/ 30040843Smsmith#define SI_PFS(si, pfs) \ 30140843Smsmith {si.cp = pfs->text; si.count = pfs->count;} 30240843Smsmith 30340843Smsmith/* 30440843Smsmith** Ficl uses a this little structure to hold the address of 30540843Smsmith** the block of text it's working on and an index to the next 30640843Smsmith** unconsumed character in the string. Traditionally, this is 30740843Smsmith** done by a Text Input Buffer, so I've called this struct TIB. 30843078Smsmith** 30943078Smsmith** Since this structure also holds the size of the input buffer, 31043078Smsmith** and since evaluate requires that, let's put the size here. 31143078Smsmith** The size is stored as an end-pointer because that is what the 31243078Smsmith** null-terminated string aware functions find most easy to deal 31343078Smsmith** with. 31443078Smsmith** Notice, though, that nobody really uses this except evaluate, 31543078Smsmith** so it might just be moved to FICL_VM instead. (sobral) 31640843Smsmith*/ 31740843Smsmithtypedef struct 31840843Smsmith{ 31940843Smsmith INT32 index; 32043078Smsmith char *end; 32140843Smsmith char *cp; 32240843Smsmith} TIB; 32340843Smsmith 32440843Smsmith 32540843Smsmith/* 32640843Smsmith** Stacks get heavy use in Ficl and Forth... 32740843Smsmith** Each virtual machine implements two of them: 32840843Smsmith** one holds parameters (data), and the other holds return 32940843Smsmith** addresses and control flow information for the virtual 33040843Smsmith** machine. (Note: C's automatic stack is implicitly used, 33140843Smsmith** but not modeled because it doesn't need to be...) 33240843Smsmith** Here's an abstract type for a stack 33340843Smsmith*/ 33440843Smsmithtypedef struct _ficlStack 33540843Smsmith{ 33651786Sdcs FICL_UNS nCells; /* size of the stack */ 33740843Smsmith CELL *pFrame; /* link reg for stack frame */ 33840843Smsmith CELL *sp; /* stack pointer */ 33940843Smsmith CELL base[1]; /* Bottom of the stack */ 34040843Smsmith} FICL_STACK; 34140843Smsmith 34240843Smsmith/* 34340843Smsmith** Stack methods... many map closely to required Forth words. 34440843Smsmith*/ 34540843SmsmithFICL_STACK *stackCreate(unsigned nCells); 34640843Smsmithvoid stackDelete(FICL_STACK *pStack); 34740843Smsmithint stackDepth (FICL_STACK *pStack); 34840843Smsmithvoid stackDrop (FICL_STACK *pStack, int n); 34940843SmsmithCELL stackFetch (FICL_STACK *pStack, int n); 35040843SmsmithCELL stackGetTop(FICL_STACK *pStack); 35140843Smsmithvoid stackLink (FICL_STACK *pStack, int nCells); 35240843Smsmithvoid stackPick (FICL_STACK *pStack, int n); 35340843SmsmithCELL stackPop (FICL_STACK *pStack); 35440843Smsmithvoid *stackPopPtr (FICL_STACK *pStack); 35551786SdcsFICL_UNS stackPopUNS(FICL_STACK *pStack); 35651786SdcsFICL_INT stackPopINT(FICL_STACK *pStack); 35740843Smsmithvoid stackPush (FICL_STACK *pStack, CELL c); 35840843Smsmithvoid stackPushPtr (FICL_STACK *pStack, void *ptr); 35951786Sdcsvoid stackPushUNS(FICL_STACK *pStack, FICL_UNS u); 36051786Sdcsvoid stackPushINT(FICL_STACK *pStack, FICL_INT i); 36140843Smsmithvoid stackReset (FICL_STACK *pStack); 36240843Smsmithvoid stackRoll (FICL_STACK *pStack, int n); 36340843Smsmithvoid stackSetTop(FICL_STACK *pStack, CELL c); 36440843Smsmithvoid stackStore (FICL_STACK *pStack, int n, CELL c); 36540843Smsmithvoid stackUnlink(FICL_STACK *pStack); 36640843Smsmith 36740843Smsmith/* 36840843Smsmith** The virtual machine (VM) contains the state for one interpreter. 36940843Smsmith** Defined operations include: 37040843Smsmith** Create & initialize 37140843Smsmith** Delete 37240843Smsmith** Execute a block of text 37340843Smsmith** Parse a word out of the input stream 37440843Smsmith** Call return, and branch 37540843Smsmith** Text output 37640843Smsmith** Throw an exception 37740843Smsmith*/ 37840843Smsmith 37940843Smsmithtypedef struct ficl_word ** IPTYPE; /* the VM's instruction pointer */ 38040843Smsmith 38140843Smsmith/* 38240843Smsmith** Each VM has a placeholder for an output function - 38340843Smsmith** this makes it possible to have each VM do I/O 38440843Smsmith** through a different device. If you specify no 38540843Smsmith** OUTFUNC, it defaults to ficlTextOut. 38640843Smsmith*/ 38740843Smsmithtypedef void (*OUTFUNC)(struct vm *pVM, char *text, int fNewline); 38840843Smsmith 38940843Smsmith/* 39040843Smsmith** Each VM operates in one of two non-error states: interpreting 39140843Smsmith** or compiling. When interpreting, words are simply executed. 39240843Smsmith** When compiling, most words in the input stream have their 39340843Smsmith** addresses inserted into the word under construction. Some words 39440843Smsmith** (known as IMMEDIATE) are executed in the compile state, too. 39540843Smsmith*/ 39640843Smsmith/* values of STATE */ 39740843Smsmith#define INTERPRET 0 39840843Smsmith#define COMPILE 1 39940843Smsmith 40040843Smsmith/* 40140843Smsmith** The pad is a small scratch area for text manipulation. ANS Forth 40240843Smsmith** requires it to hold at least 84 characters. 40340843Smsmith*/ 40440843Smsmith#if !defined nPAD 40540843Smsmith#define nPAD 256 40640843Smsmith#endif 40740843Smsmith 40840843Smsmith/* 40940843Smsmith** ANS Forth requires that a word's name contain {1..31} characters. 41040843Smsmith*/ 41140843Smsmith#if !defined nFICLNAME 41240843Smsmith#define nFICLNAME 31 41340843Smsmith#endif 41440843Smsmith 41540843Smsmith/* 41640843Smsmith** OK - now we can really define the VM... 41740843Smsmith*/ 41840843Smsmithtypedef struct vm 41940843Smsmith{ 42040843Smsmith struct vm *link; /* Ficl keeps a VM list for simple teardown */ 42140843Smsmith jmp_buf *pState; /* crude exception mechanism... */ 42240843Smsmith OUTFUNC textOut; /* Output callback - see sysdep.c */ 42340843Smsmith void * pExtend; /* vm extension pointer */ 42440843Smsmith short fRestart; /* Set TRUE to restart runningWord */ 42540843Smsmith IPTYPE ip; /* instruction pointer */ 42640843Smsmith struct ficl_word 42740843Smsmith *runningWord;/* address of currently running word (often just *(ip-1) ) */ 42840843Smsmith UNS32 state; /* compiling or interpreting */ 42940843Smsmith UNS32 base; /* number conversion base */ 43040843Smsmith FICL_STACK *pStack; /* param stack */ 43140843Smsmith FICL_STACK *rStack; /* return stack */ 43240843Smsmith CELL sourceID; /* -1 if string, 0 if normal input */ 43340843Smsmith TIB tib; /* address of incoming text string */ 43440843Smsmith#if FICL_WANT_USER 43540843Smsmith CELL user[FICL_USER_CELLS]; 43640843Smsmith#endif 43740843Smsmith char pad[nPAD]; /* the scratch area (see above) */ 43840843Smsmith} FICL_VM; 43940843Smsmith 44040843Smsmith/* 44140843Smsmith** A FICL_CODE points to a function that gets called to help execute 44240843Smsmith** a word in the dictionary. It always gets passed a pointer to the 44340843Smsmith** running virtual machine, and from there it can get the address 44440843Smsmith** of the parameter area of the word it's supposed to operate on. 44540843Smsmith** For precompiled words, the code is all there is. For user defined 44640843Smsmith** words, the code assumes that the word's parameter area is a list 44740843Smsmith** of pointers to the code fields of other words to execute, and 44840843Smsmith** may also contain inline data. The first parameter is always 44940843Smsmith** a pointer to a code field. 45040843Smsmith*/ 45140843Smsmithtypedef void (*FICL_CODE)(FICL_VM *pVm); 45240843Smsmith 45351786Sdcs#if 0 45451786Sdcs#define VM_ASSERT(pVM) assert((*(pVM->ip - 1)) == pVM->runningWord) 45551786Sdcs#else 45651786Sdcs#define VM_ASSERT(pVM) 45751786Sdcs#endif 45851786Sdcs 45940843Smsmith/* 46040843Smsmith** Ficl models memory as a contiguous space divided into 46140843Smsmith** words in a linked list called the dictionary. 46240843Smsmith** A FICL_WORD starts each entry in the list. 46340843Smsmith** Version 1.02: space for the name characters is allotted from 46440843Smsmith** the dictionary ahead of the word struct - this saves about half 46540843Smsmith** the storage on average with very little runtime cost. 46640843Smsmith*/ 46740843Smsmithtypedef struct ficl_word 46840843Smsmith{ 46940843Smsmith struct ficl_word *link; /* Previous word in the dictionary */ 47040843Smsmith UNS16 hash; 47140843Smsmith UNS8 flags; /* Immediate, Smudge, Compile-only */ 47240843Smsmith FICL_COUNT nName; /* Number of chars in word name */ 47340843Smsmith char *name; /* First nFICLNAME chars of word name */ 47440843Smsmith FICL_CODE code; /* Native code to execute the word */ 47540843Smsmith CELL param[1]; /* First data cell of the word */ 47640843Smsmith} FICL_WORD; 47740843Smsmith 47840843Smsmith/* 47940843Smsmith** Worst-case size of a word header: nFICLNAME chars in name 48040843Smsmith*/ 48140843Smsmith#define CELLS_PER_WORD \ 48240843Smsmith ( (sizeof (FICL_WORD) + nFICLNAME + sizeof (CELL)) \ 48340843Smsmith / (sizeof (CELL)) ) 48440843Smsmith 48540843Smsmithint wordIsImmediate(FICL_WORD *pFW); 48640843Smsmithint wordIsCompileOnly(FICL_WORD *pFW); 48740843Smsmith 48840843Smsmith/* flag values for word header */ 48940843Smsmith#define FW_IMMEDIATE 1 /* execute me even if compiling */ 49040843Smsmith#define FW_COMPILE 2 /* error if executed when not compiling */ 49140843Smsmith#define FW_SMUDGE 4 /* definition in progress - hide me */ 49240843Smsmith#define FW_CLASS 8 /* Word defines a class */ 49340843Smsmith 49440843Smsmith#define FW_COMPIMMED (FW_IMMEDIATE | FW_COMPILE) 49540843Smsmith#define FW_DEFAULT 0 49640843Smsmith 49740843Smsmith 49840843Smsmith/* 49940843Smsmith** Exit codes for vmThrow 50040843Smsmith*/ 50151786Sdcs#define VM_INNEREXIT -256 /* tell ficlExecXT to exit inner loop */ 50251786Sdcs#define VM_OUTOFTEXT -257 /* hungry - normal exit */ 50351786Sdcs#define VM_RESTART -258 /* word needs more text to succeed - re-run it */ 50451786Sdcs#define VM_USEREXIT -259 /* user wants to quit */ 50551786Sdcs#define VM_ERREXIT -260 /* interp found an error */ 50643078Smsmith#define VM_ABORT -1 /* like errexit -- abort */ 50743078Smsmith#define VM_ABORTQ -2 /* like errexit -- abort" */ 50843078Smsmith#define VM_QUIT -56 /* like errexit, but leave pStack & base alone */ 50940843Smsmith 51040843Smsmith 51140843Smsmithvoid vmBranchRelative(FICL_VM *pVM, int offset); 51240843SmsmithFICL_VM * vmCreate (FICL_VM *pVM, unsigned nPStack, unsigned nRStack); 51340843Smsmithvoid vmDelete (FICL_VM *pVM); 51440843Smsmithvoid vmExecute(FICL_VM *pVM, FICL_WORD *pWord); 51540843Smsmithchar * vmGetString(FICL_VM *pVM, FICL_STRING *spDest, char delimiter); 51640843SmsmithSTRINGINFO vmGetWord(FICL_VM *pVM); 51740843SmsmithSTRINGINFO vmGetWord0(FICL_VM *pVM); 51840843Smsmithint vmGetWordToPad(FICL_VM *pVM); 51940843SmsmithSTRINGINFO vmParseString(FICL_VM *pVM, char delimiter); 52040843Smsmithvoid vmPopIP (FICL_VM *pVM); 52140843Smsmithvoid vmPushIP (FICL_VM *pVM, IPTYPE newIP); 52240843Smsmithvoid vmQuit (FICL_VM *pVM); 52340843Smsmithvoid vmReset (FICL_VM *pVM); 52440843Smsmithvoid vmSetTextOut(FICL_VM *pVM, OUTFUNC textOut); 52540843Smsmithvoid vmTextOut(FICL_VM *pVM, char *text, int fNewline); 52640843Smsmithvoid vmThrow (FICL_VM *pVM, int except); 52740843Smsmithvoid vmThrowErr(FICL_VM *pVM, char *fmt, ...); 52840843Smsmith 52951786Sdcs#define vmGetRunningWord(pVM) ((pVM)->runningWord) 53051786Sdcs 53151786Sdcs 53240843Smsmith/* 53351786Sdcs** The inner interpreter - coded as a macro (see note for 53451786Sdcs** INLINE_INNER_LOOP in sysdep.h for complaints about VC++ 5 53551786Sdcs*/ 53651786Sdcs#define M_INNER_LOOP(pVM) \ 53751786Sdcs for (;;) \ 53851786Sdcs { \ 53951786Sdcs FICL_WORD *tempFW = *(pVM)->ip++; \ 54051786Sdcs (pVM)->runningWord = tempFW; \ 54151786Sdcs tempFW->code(pVM); \ 54251786Sdcs } 54351786Sdcs 54451786Sdcs 54551786Sdcs#if INLINE_INNER_LOOP != 0 54651786Sdcs#define vmInnerLoop(pVM) M_INNER_LOOP(pVM) 54751786Sdcs#else 54851786Sdcsvoid vmInnerLoop(FICL_VM *pVM); 54951786Sdcs#endif 55051786Sdcs 55151786Sdcs/* 55240843Smsmith** vmCheckStack needs a vm pointer because it might have to say 55340843Smsmith** something if it finds a problem. Parms popCells and pushCells 55440843Smsmith** correspond to the number of parameters on the left and right of 55540843Smsmith** a word's stack effect comment. 55640843Smsmith*/ 55740843Smsmithvoid vmCheckStack(FICL_VM *pVM, int popCells, int pushCells); 55840843Smsmith 55940843Smsmith/* 56040843Smsmith** TIB access routines... 56140843Smsmith** ANS forth seems to require the input buffer to be represented 56240843Smsmith** as a pointer to the start of the buffer, and an index to the 56340843Smsmith** next character to read. 56440843Smsmith** PushTib points the VM to a new input string and optionally 56540843Smsmith** returns a copy of the current state 56640843Smsmith** PopTib restores the TIB state given a saved TIB from PushTib 56740843Smsmith** GetInBuf returns a pointer to the next unused char of the TIB 56840843Smsmith*/ 56951786Sdcsvoid vmPushTib(FICL_VM *pVM, char *text, INT32 nChars, TIB *pSaveTib); 57040843Smsmithvoid vmPopTib(FICL_VM *pVM, TIB *pTib); 57140843Smsmith#define vmGetInBuf(pVM) ((pVM)->tib.cp + (pVM)->tib.index) 57251786Sdcs#define vmGetInBufLen(pVM) ((pVM)->tib.end - (pVM)->tib.cp) 57351786Sdcs#define vmGetInBufEnd(pVM) ((pVM)->tib.end) 57440843Smsmith#define vmSetTibIndex(pVM, i) (pVM)->tib.index = i 57540843Smsmith#define vmUpdateTib(pVM, str) (pVM)->tib.index = (str) - (pVM)->tib.cp 57640843Smsmith 57740843Smsmith/* 57840843Smsmith** Generally useful string manipulators omitted by ANSI C... 57940843Smsmith** ltoa complements strtol 58040843Smsmith*/ 58140843Smsmith#if defined(_WIN32) && !FICL_MAIN 58240843Smsmith/* #SHEESH 58340843Smsmith** Why do Microsoft Meatballs insist on contaminating 58440843Smsmith** my namespace with their string functions??? 58540843Smsmith*/ 58640843Smsmith#pragma warning(disable: 4273) 58740843Smsmith#endif 58840843Smsmith 58951786Sdcsint isPowerOfTwo(FICL_UNS u); 59051786Sdcs 59151786Sdcschar *ltoa( FICL_INT value, char *string, int radix ); 59251786Sdcschar *ultoa(FICL_UNS value, char *string, int radix ); 59340843Smsmithchar digit_to_char(int value); 59440843Smsmithchar *strrev( char *string ); 59551786Sdcschar *skipSpace(char *cp, char *end); 59640843Smsmithchar *caseFold(char *cp); 59740843Smsmithint strincmp(char *cp1, char *cp2, FICL_COUNT count); 59840843Smsmith 59940843Smsmith#if defined(_WIN32) && !FICL_MAIN 60040843Smsmith#pragma warning(default: 4273) 60140843Smsmith#endif 60240843Smsmith 60340843Smsmith/* 60440843Smsmith** Ficl hash table - variable size. 60540843Smsmith** assert(size > 0) 60640843Smsmith** If size is 1, the table degenerates into a linked list. 60740843Smsmith** A WORDLIST (see the search order word set in DPANS) is 60840843Smsmith** just a pointer to a FICL_HASH in this implementation. 60940843Smsmith*/ 61051786Sdcs#if !defined HASHSIZE /* Default size of hash table. For most uniform */ 61140843Smsmith#define HASHSIZE 127 /* performance, use a prime number! */ 61240843Smsmith#endif 61340843Smsmith 61440843Smsmithtypedef struct ficl_hash 61540843Smsmith{ 61640843Smsmith struct ficl_hash *link; /* eventual inheritance support */ 61740843Smsmith unsigned size; 61840843Smsmith FICL_WORD *table[1]; 61940843Smsmith} FICL_HASH; 62040843Smsmith 62140843Smsmithvoid hashForget(FICL_HASH *pHash, void *where); 62240843SmsmithUNS16 hashHashCode(STRINGINFO si); 62340843Smsmithvoid hashInsertWord(FICL_HASH *pHash, FICL_WORD *pFW); 62440843SmsmithFICL_WORD *hashLookup(struct ficl_hash *pHash, 62540843Smsmith STRINGINFO si, 62640843Smsmith UNS16 hashCode); 62740843Smsmithvoid hashReset(FICL_HASH *pHash); 62840843Smsmith 62940843Smsmith/* 63040843Smsmith** A Dictionary is a linked list of FICL_WORDs. It is also Ficl's 63140843Smsmith** memory model. Description of fields: 63240843Smsmith** 63340843Smsmith** here -- points to the next free byte in the dictionary. This 63440843Smsmith** pointer is forced to be CELL-aligned before a definition is added. 63540843Smsmith** Do not assume any specific alignment otherwise - Use dictAlign(). 63640843Smsmith** 63740843Smsmith** smudge -- pointer to word currently being defined (or last defined word) 63840843Smsmith** If the definition completes successfully, the word will be 63940843Smsmith** linked into the hash table. If unsuccessful, dictUnsmudge 64040843Smsmith** uses this pointer to restore the previous state of the dictionary. 64140843Smsmith** Smudge prevents unintentional recursion as a side-effect: the 64240843Smsmith** dictionary search algo examines only completed definitions, so a 64340843Smsmith** word cannot invoke itself by name. See the ficl word "recurse". 64440843Smsmith** NOTE: smudge always points to the last word defined. IMMEDIATE 64540843Smsmith** makes use of this fact. Smudge is initially NULL. 64640843Smsmith** 64740843Smsmith** pForthWords -- pointer to the default wordlist (FICL_HASH). 64840843Smsmith** This is the initial compilation list, and contains all 64940843Smsmith** ficl's precompiled words. 65040843Smsmith** 65140843Smsmith** pCompile -- compilation wordlist - initially equal to pForthWords 65240843Smsmith** pSearch -- array of pointers to wordlists. Managed as a stack. 65340843Smsmith** Highest index is the first list in the search order. 65440843Smsmith** nLists -- number of lists in pSearch. nLists-1 is the highest 65540843Smsmith** filled slot in pSearch, and points to the first wordlist 65640843Smsmith** in the search order 65740843Smsmith** size -- number of cells in the dictionary (total) 65840843Smsmith** dict -- start of data area. Must be at the end of the struct. 65940843Smsmith*/ 66040843Smsmithtypedef struct ficl_dict 66140843Smsmith{ 66240843Smsmith CELL *here; 66340843Smsmith FICL_WORD *smudge; 66440843Smsmith FICL_HASH *pForthWords; 66540843Smsmith FICL_HASH *pCompile; 66640843Smsmith FICL_HASH *pSearch[FICL_DEFAULT_VOCS]; 66740843Smsmith int nLists; 66840843Smsmith unsigned size; /* Number of cells in dict (total)*/ 66940843Smsmith CELL dict[1]; /* Base of dictionary memory */ 67040843Smsmith} FICL_DICT; 67140843Smsmith 67240843Smsmithvoid *alignPtr(void *ptr); 67340843Smsmithvoid dictAbortDefinition(FICL_DICT *pDict); 67440843Smsmithvoid dictAlign(FICL_DICT *pDict); 67540843Smsmithint dictAllot(FICL_DICT *pDict, int n); 67640843Smsmithint dictAllotCells(FICL_DICT *pDict, int nCells); 67740843Smsmithvoid dictAppendCell(FICL_DICT *pDict, CELL c); 67840843Smsmithvoid dictAppendChar(FICL_DICT *pDict, char c); 67940843SmsmithFICL_WORD *dictAppendWord(FICL_DICT *pDict, 68040843Smsmith char *name, 68140843Smsmith FICL_CODE pCode, 68240843Smsmith UNS8 flags); 68340843SmsmithFICL_WORD *dictAppendWord2(FICL_DICT *pDict, 68440843Smsmith STRINGINFO si, 68540843Smsmith FICL_CODE pCode, 68640843Smsmith UNS8 flags); 68751786Sdcsvoid dictAppendUNS(FICL_DICT *pDict, FICL_UNS u); 68840843Smsmithint dictCellsAvail(FICL_DICT *pDict); 68940843Smsmithint dictCellsUsed (FICL_DICT *pDict); 69040843Smsmithvoid dictCheck(FICL_DICT *pDict, FICL_VM *pVM, int nCells); 69140843SmsmithFICL_DICT *dictCreate(unsigned nCELLS); 69240843SmsmithFICL_DICT *dictCreateHashed(unsigned nCells, unsigned nHash); 69340843Smsmithvoid dictDelete(FICL_DICT *pDict); 69440843Smsmithvoid dictEmpty(FICL_DICT *pDict, unsigned nHash); 69540843Smsmithint dictIncludes(FICL_DICT *pDict, void *p); 69640843SmsmithFICL_WORD *dictLookup(FICL_DICT *pDict, STRINGINFO si); 69740843Smsmith#if FICL_WANT_LOCALS 69840843SmsmithFICL_WORD *dictLookupLoc(FICL_DICT *pDict, STRINGINFO si); 69940843Smsmith#endif 70040843Smsmithvoid dictResetSearchOrder(FICL_DICT *pDict); 70140843Smsmithvoid dictSetFlags(FICL_DICT *pDict, UNS8 set, UNS8 clr); 70240843Smsmithvoid dictSetImmediate(FICL_DICT *pDict); 70340843Smsmithvoid dictUnsmudge(FICL_DICT *pDict); 70440843SmsmithCELL *dictWhere(FICL_DICT *pDict); 70540843Smsmith 70640843Smsmith 70740843Smsmith/* 70840843Smsmith** External interface to FICL... 70940843Smsmith*/ 71040843Smsmith/* 71140843Smsmith** f i c l I n i t S y s t e m 71240843Smsmith** Binds a global dictionary to the interpreter system and initializes 71340843Smsmith** the dict to contain the ANSI CORE wordset. 71440843Smsmith** You specify the address and size of the allocated area. 71540843Smsmith** After that, ficl manages it. 71640843Smsmith** First step is to set up the static pointers to the area. 71740843Smsmith** Then write the "precompiled" portion of the dictionary in. 71840843Smsmith** The dictionary needs to be at least large enough to hold the 71940843Smsmith** precompiled part. Try 1K cells minimum. Use "words" to find 72040843Smsmith** out how much of the dictionary is used at any time. 72140843Smsmith*/ 72240843Smsmithvoid ficlInitSystem(int nDictCells); 72340843Smsmith 72440843Smsmith/* 72540843Smsmith** f i c l T e r m S y s t e m 72640843Smsmith** Deletes the system dictionary and all virtual machines that 72740843Smsmith** were created with ficlNewVM (see below). Call this function to 72840843Smsmith** reclaim all memory used by the dictionary and VMs. 72940843Smsmith*/ 73040843Smsmithvoid ficlTermSystem(void); 73140843Smsmith 73240843Smsmith/* 73340843Smsmith** f i c l E x e c 73440843Smsmith** Evaluates a block of input text in the context of the 73540843Smsmith** specified interpreter. Emits any requested output to the 73651786Sdcs** interpreter's output function. If the input string is NULL 73751786Sdcs** terminated, you can pass -1 as nChars rather than count it. 73840843Smsmith** Execution returns when the text block has been executed, 73940843Smsmith** or an error occurs. 74040843Smsmith** Returns one of the VM_XXXX codes defined in ficl.h: 74140843Smsmith** VM_OUTOFTEXT is the normal exit condition 74240843Smsmith** VM_ERREXIT means that the interp encountered a syntax error 74340843Smsmith** and the vm has been reset to recover (some or all 74440843Smsmith** of the text block got ignored 74540843Smsmith** VM_USEREXIT means that the user executed the "bye" command 74640843Smsmith** to shut down the interpreter. This would be a good 74740843Smsmith** time to delete the vm, etc -- or you can ignore this 74840843Smsmith** signal. 74943078Smsmith** VM_ABORT and VM_ABORTQ are generated by 'abort' and 'abort"' 75043078Smsmith** commands. 75140843Smsmith** Preconditions: successful execution of ficlInitSystem, 75240843Smsmith** Successful creation and init of the VM by ficlNewVM (or equiv) 75340843Smsmith*/ 75451786Sdcsint ficlExec (FICL_VM *pVM, char *pText); 75551786Sdcsint ficlExecC(FICL_VM *pVM, char *pText, INT32 nChars); 75651786Sdcsint ficlExecXT(FICL_VM *pVM, FICL_WORD *pWord); 75740843Smsmith 75840843Smsmith/* 75940989Sjkh** ficlExecFD(FICL_VM *pVM, int fd); 76040989Sjkh * Evaluates text from file passed in via fd. 76140989Sjkh * Execution returns when all of file has been executed or an 76240989Sjkh * error occurs. 76340989Sjkh */ 76440989Sjkhint ficlExecFD(FICL_VM *pVM, int fd); 76540989Sjkh 76640989Sjkh/* 76740843Smsmith** Create a new VM from the heap, and link it into the system VM list. 76840843Smsmith** Initializes the VM and binds default sized stacks to it. Returns the 76940843Smsmith** address of the VM, or NULL if an error occurs. 77040843Smsmith** Precondition: successful execution of ficlInitSystem 77140843Smsmith*/ 77240843SmsmithFICL_VM *ficlNewVM(void); 77340843Smsmith 77440843Smsmith/* 77551786Sdcs** Set the stack sizes (return and parameter) to be used for all 77651786Sdcs** subsequently created VMs. Returns actual stack size to be used. 77751786Sdcs*/ 77851786Sdcsint ficlSetStackSize(int nStackCells); 77951786Sdcs 78051786Sdcs/* 78140843Smsmith** Returns the address of the most recently defined word in the system 78240843Smsmith** dictionary with the given name, or NULL if no match. 78340843Smsmith** Precondition: successful execution of ficlInitSystem 78440843Smsmith*/ 78540843SmsmithFICL_WORD *ficlLookup(char *name); 78640843Smsmith 78740843Smsmith/* 78840843Smsmith** f i c l G e t D i c t 78940843Smsmith** Utility function - returns the address of the system dictionary. 79040843Smsmith** Precondition: successful execution of ficlInitSystem 79140843Smsmith*/ 79240843SmsmithFICL_DICT *ficlGetDict(void); 79340843SmsmithFICL_DICT *ficlGetEnv(void); 79440843Smsmithvoid ficlSetEnv(char *name, UNS32 value); 79540843Smsmithvoid ficlSetEnvD(char *name, UNS32 hi, UNS32 lo); 79640843Smsmith#if FICL_WANT_LOCALS 79740843SmsmithFICL_DICT *ficlGetLoc(void); 79840843Smsmith#endif 79940843Smsmith/* 80040843Smsmith** f i c l B u i l d 80140843Smsmith** Builds a word into the system default dictionary in a thread-safe way. 80240843Smsmith** Preconditions: system must be initialized, and there must 80340843Smsmith** be enough space for the new word's header! Operation is 80440843Smsmith** controlled by ficlLockDictionary, so any initialization 80540843Smsmith** required by your version of the function (if you "overrode" 80640843Smsmith** it) must be complete at this point. 80740843Smsmith** Parameters: 80840843Smsmith** name -- the name of the word to be built 80940843Smsmith** code -- code to execute when the word is invoked - must take a single param 81040843Smsmith** pointer to a FICL_VM 81140843Smsmith** flags -- 0 or more of FW_IMMEDIATE, FW_COMPILE, use bitwise OR! 81240843Smsmith** Most words can use FW_DEFAULT. 81340843Smsmith** nAllot - number of extra cells to allocate in the parameter area (usually zero) 81440843Smsmith*/ 81540843Smsmithint ficlBuild(char *name, FICL_CODE code, char flags); 81640843Smsmith 81740843Smsmith/* 81840843Smsmith** f i c l C o m p i l e C o r e 81940843Smsmith** Builds the ANS CORE wordset into the dictionary - called by 82040843Smsmith** ficlInitSystem - no need to waste dict space by doing it again. 82140843Smsmith*/ 82240843Smsmithvoid ficlCompileCore(FICL_DICT *dp); 82340843Smsmithvoid ficlCompileSoftCore(FICL_VM *pVM); 82440843Smsmith 82540843Smsmith/* 82640843Smsmith** from words.c... 82740843Smsmith*/ 82840843Smsmithvoid constantParen(FICL_VM *pVM); 82940843Smsmithvoid twoConstParen(FICL_VM *pVM); 83040843Smsmith 83143139Smsmith/* 83243139Smsmith** So we can more easily debug... 83343139Smsmith*/ 83443139Smsmith#ifdef FICL_TRACE 83543139Smsmithextern int ficl_trace; 83643139Smsmith#endif 83743139Smsmith 83842679Sabial#if defined(__i386__) && !defined(TESTMAIN) 83942679Sabialextern void ficlOutb(FICL_VM *pVM); 84042679Sabialextern void ficlInb(FICL_VM *pVM); 84142634Sabial#endif 84242634Sabial 84340843Smsmith#ifdef __cplusplus 84440843Smsmith} 84540843Smsmith#endif 84640843Smsmith 84740843Smsmith#endif /* __FICL_H__ */ 848