/*******************************************************************1** f i c l . h2** Forth Inspired Command Language3** Author: John Sadler ([email protected])4** Created: 19 July 19975** Dedicated to RHS, in loving memory6** $Id: ficl.h,v 1.18 2001/12/05 07:21:34 jsadler Exp $7*******************************************************************/8/*9** Copyright (c) 1997-2001 John Sadler ([email protected])10** All rights reserved.11**12** Get the latest Ficl release at http://ficl.sourceforge.net13**14** I am interested in hearing from anyone who uses ficl. If you have15** a problem, a success story, a defect, an enhancement request, or16** if you would like to contribute to the ficl release, please17** contact me by email at the address above.18**19** L I C E N S E and D I S C L A I M E R20**21** Redistribution and use in source and binary forms, with or without22** modification, are permitted provided that the following conditions23** are met:24** 1. Redistributions of source code must retain the above copyright25** notice, this list of conditions and the following disclaimer.26** 2. Redistributions in binary form must reproduce the above copyright27** notice, this list of conditions and the following disclaimer in the28** documentation and/or other materials provided with the distribution.29**30** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND31** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE32** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE33** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE34** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL35** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS36** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)37** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT38** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY39** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF40** SUCH DAMAGE.41*/424344#if !defined (__FICL_H__)45#define __FICL_H__46/*47** Ficl (Forth-inspired command language) is an ANS Forth48** interpreter written in C. Unlike traditional Forths, this49** interpreter is designed to be embedded into other systems50** as a command/macro/development prototype language.51**52** Where Forths usually view themselves as the center of the system53** and expect the rest of the system to be coded in Forth, Ficl54** acts as a component of the system. It is easy to export55** code written in C or ASM to Ficl in the style of TCL, or to invoke56** Ficl code from a compiled module. This allows you to do incremental57** development in a way that combines the best features of threaded58** languages (rapid development, quick code/test/debug cycle,59** reasonably fast) with the best features of C (everyone knows it,60** easier to support large blocks of code, efficient, type checking).61**62** Ficl provides facilities for interoperating63** with programs written in C: C functions can be exported to Ficl,64** and Ficl commands can be executed via a C calling interface. The65** interpreter is re-entrant, so it can be used in multiple instances66** in a multitasking system. Unlike Forth, Ficl's outer interpreter67** expects a text block as input, and returns to the caller after each68** text block, so the "data pump" is somewhere in external code. This69** is more like TCL than Forth, which usually expcets to be at the center70** of the system, requesting input at its convenience. Each Ficl virtual71** machine can be bound to a different I/O channel, and is independent72** of all others in in the same address space except that all virtual73** machines share a common dictionary (a sort or open symbol table that74** defines all of the elements of the language).75**76** Code is written in ANSI C for portability.77**78** Summary of Ficl features and constraints:79** - Standard: Implements the ANSI Forth CORE word set and part80** of the CORE EXT word-set, SEARCH and SEARCH EXT, TOOLS and81** TOOLS EXT, LOCAL and LOCAL ext and various extras.82** - Extensible: you can export code written in Forth, C,83** or asm in a straightforward way. Ficl provides open84** facilities for extending the language in an application85** specific way. You can even add new control structures!86** - Ficl and C can interact in two ways: Ficl can encapsulate87** C code, or C code can invoke Ficl code.88** - Thread-safe, re-entrant: The shared system dictionary89** uses a locking mechanism that you can either supply90** or stub out to provide exclusive access. Each Ficl91** virtual machine has an otherwise complete state, and92** each can be bound to a separate I/O channel (or none at all).93** - Simple encapsulation into existing systems: a basic implementation94** requires three function calls (see the example program in testmain.c).95** - ROMable: Ficl is designed to work in RAM-based and ROM code / RAM data96** environments. It does require somewhat more memory than a pure97** ROM implementation because it builds its system dictionary in98** RAM at startup time.99** - Written an ANSI C to be as simple as I can make it to understand,100** support, debug, and port. Compiles without complaint at /Az /W4101** (require ANSI C, max warnings) under Microsoft VC++ 5.102** - Does full 32 bit math (but you need to implement103** two mixed precision math primitives (see sysdep.c))104** - Indirect threaded interpreter is not the fastest kind of105** Forth there is (see pForth 68K for a really fast subroutine106** threaded interpreter), but it's the cleanest match to a107** pure C implementation.108**109** P O R T I N G F i c l110**111** To install Ficl on your target system, you need an ANSI C compiler112** and its runtime library. Inspect the system dependent macros and113** functions in sysdep.h and sysdep.c and edit them to suit your114** system. For example, INT16 is a short on some compilers and an115** int on others. Check the default CELL alignment controlled by116** FICL_ALIGN. If necessary, add new definitions of ficlMalloc, ficlFree,117** ficlLockDictionary, and ficlTextOut to work with your operating system.118** Finally, use testmain.c as a guide to installing the Ficl system and119** one or more virtual machines into your code. You do not need to include120** testmain.c in your build.121**122** T o D o L i s t123**124** 1. Unimplemented system dependent CORE word: key125** 2. Ficl uses the PAD in some CORE words - this violates the standard,126** but it's cleaner for a multithreaded system. I'll have to make a127** second pad for reference by the word PAD to fix this.128**129** F o r M o r e I n f o r m a t i o n130**131** Web home of ficl132** http://ficl.sourceforge.net133** Check this website for Forth literature (including the ANSI standard)134** http://www.taygeta.com/forthlit.html135** and here for software and more links136** http://www.taygeta.com/forth.html137**138** Obvious Performance enhancement opportunities139** Compile speed140** - work on interpret speed141** - turn off locals (FICL_WANT_LOCALS)142** Interpret speed143** - Change inner interpreter (and everything else)144** so that a definition is a list of pointers to functions145** and inline data rather than pointers to words. This gets146** rid of vm->runningWord and a level of indirection in the147** inner loop. I'll look at it for ficl 3.0148** - Make the main hash table a bigger prime (HASHSIZE)149** - FORGET about twiddling the hash function - my experience is150** that that is a waste of time.151** - Eliminate the need to pass the pVM parameter on the stack152** by dedicating a register to it. Most words need access to the153** vm, but the parameter passing overhead can be reduced. One way154** requires that the host OS have a task switch callout. Create155** a global variable for the running VM and refer to it in words156** that need VM access. Alternative: use thread local storage.157** For single threaded implementations, you can just use a global.158** The first two solutions create portability problems, so I159** haven't considered doing them. Another possibility is to160** declare the pVm parameter to be "register", and hope the compiler161** pays attention.162**163*/164165/*166** Revision History:167**168** 15 Apr 1999 (sadler) Merged FreeBSD changes for exception wordset and169** counted strings in ficlExec.170** 12 Jan 1999 (sobral) Corrected EVALUATE behavior. Now TIB has an171** "end" field, and all words respect this. ficlExec is passed a "size"172** of TIB, as well as vmPushTib. This size is used to calculate the "end"173** of the string, ie, base+size. If the size is not known, pass -1.174**175** 10 Jan 1999 (sobral) EXCEPTION word set has been added, and existing176** words has been modified to conform to EXCEPTION EXT word set.177**178** 27 Aug 1998 (sadler) testing and corrections for LOCALS, LOCALS EXT,179** SEARCH / SEARCH EXT, TOOLS / TOOLS EXT.180** Added .X to display in hex, PARSE and PARSE-WORD to supplement WORD,181** EMPTY to clear stack.182**183** 29 jun 1998 (sadler) added variable sized hash table support184** and ANS Forth optional SEARCH & SEARCH EXT word set.185** 26 May 1998 (sadler)186** FICL_PROMPT macro187** 14 April 1998 (sadler) V1.04188** Ficlwin: Windows version, Skip Carter's Linux port189** 5 March 1998 (sadler) V1.03190** Bug fixes -- passes John Ryan's ANS test suite "core.fr"191**192** 24 February 1998 (sadler) V1.02193** -Fixed bugs in <# # #>194** -Changed FICL_WORD so that storage for the name characters195** can be allocated from the dictionary as needed rather than196** reserving 32 bytes in each word whether needed or not -197** this saved 50% of the dictionary storage requirement.198** -Added words in testmain for Win32 functions system,chdir,cwd,199** also added a word that loads and evaluates a file.200**201** December 1997 (sadler)202** -Added VM_RESTART exception handling in ficlExec -- this lets words203** that require additional text to succeed (like :, create, variable...)204** recover gracefully from an empty input buffer rather than emitting205** an error message. Definitions can span multiple input blocks with206** no restrictions.207** -Changed #include order so that <assert.h> is included in sysdep.h,208** and sysdep is included in all other files. This lets you define209** NDEBUG in sysdep.h to disable assertions if you want to.210** -Make PC specific system dependent code conditional on _M_IX86211** defined so that ports can coexist in sysdep.h/sysdep.c212*/213214#ifdef __cplusplus215extern "C" {216#endif217218#include "sysdep.h"219#include <limits.h> /* UCHAR_MAX */220221/*222** Forward declarations... read on.223*/224struct ficl_word;225typedef struct ficl_word FICL_WORD;226struct vm;227typedef struct vm FICL_VM;228struct ficl_dict;229typedef struct ficl_dict FICL_DICT;230struct ficl_system;231typedef struct ficl_system FICL_SYSTEM;232struct ficl_system_info;233typedef struct ficl_system_info FICL_SYSTEM_INFO;234235/*236** the Good Stuff starts here...237*/238#define FICL_VER "3.03"239#define FICL_VER_MAJOR 3240#define FICL_VER_MINOR 3241#if !defined (FICL_PROMPT)242#define FICL_PROMPT "ok> "243#endif244245/*246** ANS Forth requires false to be zero, and true to be the ones247** complement of false... that unifies logical and bitwise operations248** nicely.249*/250#define FICL_TRUE (~(FICL_UNS)0)251#define FICL_FALSE (0)252#define FICL_BOOL(x) ((x) ? FICL_TRUE : FICL_FALSE)253254255/*256** A CELL is the main storage type. It must be large enough257** to contain a pointer or a scalar. In order to accommodate258** 32 bit and 64 bit processors, use abstract types for int,259** unsigned, and float.260*/261typedef union _cell262{263FICL_INT i;264FICL_UNS u;265#if (FICL_WANT_FLOAT)266FICL_FLOAT f;267#endif268void *p;269void (*fn)(void);270} CELL;271272/*273** LVALUEtoCELL does a little pointer trickery to cast any CELL sized274** lvalue (informal definition: an expression whose result has an275** address) to CELL. Remember that constants and casts are NOT276** themselves lvalues!277*/278#define LVALUEtoCELL(v) (*(CELL *)&v)279280/*281** PTRtoCELL is a cast through void * intended to satisfy the282** most outrageously pedantic compiler... (I won't mention283** its name)284*/285#define PTRtoCELL (CELL *)(void *)286#define PTRtoSTRING (FICL_STRING *)(void *)287288/*289** Strings in FICL are stored in Pascal style - with a count290** preceding the text. We'll also NULL-terminate them so that291** they work with the usual C lib string functions. (Belt &292** suspenders? You decide.)293** STRINGINFO hides the implementation with a couple of294** macros for use in internal routines.295*/296297typedef unsigned char FICL_COUNT;298#define FICL_STRING_MAX UCHAR_MAX299typedef struct _ficl_string300{301FICL_COUNT count;302char text[1];303} FICL_STRING;304305typedef struct306{307FICL_UNS count;308char *cp;309} STRINGINFO;310311#define SI_COUNT(si) (si.count)312#define SI_PTR(si) (si.cp)313#define SI_SETLEN(si, len) (si.count = (FICL_UNS)(len))314#define SI_SETPTR(si, ptr) (si.cp = (char *)(ptr))315/*316** Init a STRINGINFO from a pointer to NULL-terminated string317*/318#define SI_PSZ(si, psz) \319{si.cp = psz; si.count = (FICL_COUNT)strlen(psz);}320/*321** Init a STRINGINFO from a pointer to FICL_STRING322*/323#define SI_PFS(si, pfs) \324{si.cp = pfs->text; si.count = pfs->count;}325326/*327** Ficl uses this little structure to hold the address of328** the block of text it's working on and an index to the next329** unconsumed character in the string. Traditionally, this is330** done by a Text Input Buffer, so I've called this struct TIB.331**332** Since this structure also holds the size of the input buffer,333** and since evaluate requires that, let's put the size here.334** The size is stored as an end-pointer because that is what the335** null-terminated string aware functions find most easy to deal336** with.337** Notice, though, that nobody really uses this except evaluate,338** so it might just be moved to FICL_VM instead. (sobral)339*/340typedef struct341{342FICL_INT index;343char *end;344char *cp;345} TIB;346347348/*349** Stacks get heavy use in Ficl and Forth...350** Each virtual machine implements two of them:351** one holds parameters (data), and the other holds return352** addresses and control flow information for the virtual353** machine. (Note: C's automatic stack is implicitly used,354** but not modeled because it doesn't need to be...)355** Here's an abstract type for a stack356*/357typedef struct _ficlStack358{359FICL_UNS nCells; /* size of the stack */360CELL *pFrame; /* link reg for stack frame */361CELL *sp; /* stack pointer */362CELL base[1]; /* Top of stack */363} FICL_STACK;364365/*366** Stack methods... many map closely to required Forth words.367*/368FICL_STACK *stackCreate (unsigned nCells);369void stackDelete (FICL_STACK *pStack);370int stackDepth (FICL_STACK *pStack);371void stackDrop (FICL_STACK *pStack, int n);372CELL stackFetch (FICL_STACK *pStack, int n);373CELL stackGetTop (FICL_STACK *pStack);374void stackLink (FICL_STACK *pStack, int nCells);375void stackPick (FICL_STACK *pStack, int n);376CELL stackPop (FICL_STACK *pStack);377void *stackPopPtr (FICL_STACK *pStack);378FICL_UNS stackPopUNS (FICL_STACK *pStack);379FICL_INT stackPopINT (FICL_STACK *pStack);380void stackPush (FICL_STACK *pStack, CELL c);381void stackPushPtr (FICL_STACK *pStack, void *ptr);382void stackPushUNS (FICL_STACK *pStack, FICL_UNS u);383void stackPushINT (FICL_STACK *pStack, FICL_INT i);384void stackReset (FICL_STACK *pStack);385void stackRoll (FICL_STACK *pStack, int n);386void stackSetTop (FICL_STACK *pStack, CELL c);387void stackStore (FICL_STACK *pStack, int n, CELL c);388void stackUnlink (FICL_STACK *pStack);389390#if (FICL_WANT_FLOAT)391float stackPopFloat (FICL_STACK *pStack);392void stackPushFloat(FICL_STACK *pStack, FICL_FLOAT f);393#endif394395/*396** Shortcuts (Guy Carver)397*/398#define PUSHPTR(p) stackPushPtr(pVM->pStack,p)399#define PUSHUNS(u) stackPushUNS(pVM->pStack,u)400#define PUSHINT(i) stackPushINT(pVM->pStack,i)401#define PUSHFLOAT(f) stackPushFloat(pVM->fStack,f)402#define PUSH(c) stackPush(pVM->pStack,c)403#define POPPTR() stackPopPtr(pVM->pStack)404#define POPUNS() stackPopUNS(pVM->pStack)405#define POPINT() stackPopINT(pVM->pStack)406#define POPFLOAT() stackPopFloat(pVM->fStack)407#define POP() stackPop(pVM->pStack)408#define GETTOP() stackGetTop(pVM->pStack)409#define SETTOP(c) stackSetTop(pVM->pStack,LVALUEtoCELL(c))410#define GETTOPF() stackGetTop(pVM->fStack)411#define SETTOPF(c) stackSetTop(pVM->fStack,LVALUEtoCELL(c))412#define STORE(n,c) stackStore(pVM->pStack,n,LVALUEtoCELL(c))413#define DEPTH() stackDepth(pVM->pStack)414#define DROP(n) stackDrop(pVM->pStack,n)415#define DROPF(n) stackDrop(pVM->fStack,n)416#define FETCH(n) stackFetch(pVM->pStack,n)417#define PICK(n) stackPick(pVM->pStack,n)418#define PICKF(n) stackPick(pVM->fStack,n)419#define ROLL(n) stackRoll(pVM->pStack,n)420#define ROLLF(n) stackRoll(pVM->fStack,n)421422/*423** The virtual machine (VM) contains the state for one interpreter.424** Defined operations include:425** Create & initialize426** Delete427** Execute a block of text428** Parse a word out of the input stream429** Call return, and branch430** Text output431** Throw an exception432*/433434typedef FICL_WORD ** IPTYPE; /* the VM's instruction pointer */435436/*437** Each VM has a placeholder for an output function -438** this makes it possible to have each VM do I/O439** through a different device. If you specify no440** OUTFUNC, it defaults to ficlTextOut.441*/442typedef void (*OUTFUNC)(FICL_VM *pVM, char *text, int fNewline);443444/*445** Each VM operates in one of two non-error states: interpreting446** or compiling. When interpreting, words are simply executed.447** When compiling, most words in the input stream have their448** addresses inserted into the word under construction. Some words449** (known as IMMEDIATE) are executed in the compile state, too.450*/451/* values of STATE */452#define INTERPRET 0453#define COMPILE 1454455/*456** The pad is a small scratch area for text manipulation. ANS Forth457** requires it to hold at least 84 characters.458*/459#if !defined nPAD460#define nPAD 256461#endif462463/*464** ANS Forth requires that a word's name contain {1..31} characters.465*/466#if !defined nFICLNAME467#define nFICLNAME 31468#endif469470/*471** OK - now we can really define the VM...472*/473struct vm474{475FICL_SYSTEM *pSys; /* Which system this VM belongs to */476FICL_VM *link; /* Ficl keeps a VM list for simple teardown */477jmp_buf *pState; /* crude exception mechanism... */478OUTFUNC textOut; /* Output callback - see sysdep.c */479void * pExtend; /* vm extension pointer for app use - initialized from FICL_SYSTEM */480short fRestart; /* Set TRUE to restart runningWord */481IPTYPE ip; /* instruction pointer */482FICL_WORD *runningWord;/* address of currently running word (often just *(ip-1) ) */483FICL_UNS state; /* compiling or interpreting */484FICL_UNS base; /* number conversion base */485FICL_STACK *pStack; /* param stack */486FICL_STACK *rStack; /* return stack */487#if FICL_WANT_FLOAT488FICL_STACK *fStack; /* float stack (optional) */489#endif490CELL sourceID; /* -1 if EVALUATE, 0 if normal input */491TIB tib; /* address of incoming text string */492#if FICL_WANT_USER493CELL user[FICL_USER_CELLS];494#endif495char pad[nPAD]; /* the scratch area (see above) */496};497498/*499** A FICL_CODE points to a function that gets called to help execute500** a word in the dictionary. It always gets passed a pointer to the501** running virtual machine, and from there it can get the address502** of the parameter area of the word it's supposed to operate on.503** For precompiled words, the code is all there is. For user defined504** words, the code assumes that the word's parameter area is a list505** of pointers to the code fields of other words to execute, and506** may also contain inline data. The first parameter is always507** a pointer to a code field.508*/509typedef void (*FICL_CODE)(FICL_VM *pVm);510511#if 0512#define VM_ASSERT(pVM) assert((*(pVM->ip - 1)) == pVM->runningWord)513#else514#define VM_ASSERT(pVM)515#endif516517/*518** Ficl models memory as a contiguous space divided into519** words in a linked list called the dictionary.520** A FICL_WORD starts each entry in the list.521** Version 1.02: space for the name characters is allotted from522** the dictionary ahead of the word struct, rather than using523** a fixed size array for each name.524*/525struct ficl_word526{527struct ficl_word *link; /* Previous word in the dictionary */528UNS16 hash;529UNS8 flags; /* Immediate, Smudge, Compile-only */530FICL_COUNT nName; /* Number of chars in word name */531char *name; /* First nFICLNAME chars of word name */532FICL_CODE code; /* Native code to execute the word */533CELL param[1]; /* First data cell of the word */534};535536/*537** Worst-case size of a word header: nFICLNAME chars in name538*/539#define CELLS_PER_WORD \540( (sizeof (FICL_WORD) + nFICLNAME + sizeof (CELL)) \541/ (sizeof (CELL)) )542543int wordIsImmediate(FICL_WORD *pFW);544int wordIsCompileOnly(FICL_WORD *pFW);545546/* flag values for word header */547#define FW_IMMEDIATE 1 /* execute me even if compiling */548#define FW_COMPILE 2 /* error if executed when not compiling */549#define FW_SMUDGE 4 /* definition in progress - hide me */550#define FW_ISOBJECT 8 /* word is an object or object member variable */551552#define FW_COMPIMMED (FW_IMMEDIATE | FW_COMPILE)553#define FW_DEFAULT 0554555556/*557** Exit codes for vmThrow558*/559#define VM_INNEREXIT -256 /* tell ficlExecXT to exit inner loop */560#define VM_OUTOFTEXT -257 /* hungry - normal exit */561#define VM_RESTART -258 /* word needs more text to succeed - re-run it */562#define VM_USEREXIT -259 /* user wants to quit */563#define VM_ERREXIT -260 /* interp found an error */564#define VM_BREAK -261 /* debugger breakpoint */565#define VM_ABORT -1 /* like errexit -- abort */566#define VM_ABORTQ -2 /* like errexit -- abort" */567#define VM_QUIT -56 /* like errexit, but leave pStack & base alone */568569570void vmBranchRelative(FICL_VM *pVM, int offset);571FICL_VM * vmCreate (FICL_VM *pVM, unsigned nPStack, unsigned nRStack);572void vmDelete (FICL_VM *pVM);573void vmExecute (FICL_VM *pVM, FICL_WORD *pWord);574FICL_DICT *vmGetDict (FICL_VM *pVM);575char * vmGetString (FICL_VM *pVM, FICL_STRING *spDest, char delimiter);576STRINGINFO vmGetWord (FICL_VM *pVM);577STRINGINFO vmGetWord0 (FICL_VM *pVM);578int vmGetWordToPad (FICL_VM *pVM);579STRINGINFO vmParseString (FICL_VM *pVM, char delimiter);580STRINGINFO vmParseStringEx(FICL_VM *pVM, char delimiter, char fSkipLeading);581CELL vmPop (FICL_VM *pVM);582void vmPush (FICL_VM *pVM, CELL c);583void vmPopIP (FICL_VM *pVM);584void vmPushIP (FICL_VM *pVM, IPTYPE newIP);585void vmQuit (FICL_VM *pVM);586void vmReset (FICL_VM *pVM);587void vmSetTextOut (FICL_VM *pVM, OUTFUNC textOut);588void vmTextOut (FICL_VM *pVM, char *text, int fNewline);589void vmTextOut (FICL_VM *pVM, char *text, int fNewline);590void vmThrow (FICL_VM *pVM, int except);591void vmThrowErr (FICL_VM *pVM, char *fmt, ...);592593#define vmGetRunningWord(pVM) ((pVM)->runningWord)594595596/*597** The inner interpreter - coded as a macro (see note for598** INLINE_INNER_LOOP in sysdep.h for complaints about VC++ 5599*/600#define M_VM_STEP(pVM) \601FICL_WORD *tempFW = *(pVM)->ip++; \602(pVM)->runningWord = tempFW; \603tempFW->code(pVM);604605#define M_INNER_LOOP(pVM) \606for (;;) { M_VM_STEP(pVM) }607608609#if INLINE_INNER_LOOP != 0610#define vmInnerLoop(pVM) M_INNER_LOOP(pVM)611#else612void vmInnerLoop(FICL_VM *pVM);613#endif614615/*616** vmCheckStack needs a vm pointer because it might have to say617** something if it finds a problem. Parms popCells and pushCells618** correspond to the number of parameters on the left and right of619** a word's stack effect comment.620*/621void vmCheckStack(FICL_VM *pVM, int popCells, int pushCells);622#if FICL_WANT_FLOAT623void vmCheckFStack(FICL_VM *pVM, int popCells, int pushCells);624#endif625626/*627** TIB access routines...628** ANS forth seems to require the input buffer to be represented629** as a pointer to the start of the buffer, and an index to the630** next character to read.631** PushTib points the VM to a new input string and optionally632** returns a copy of the current state633** PopTib restores the TIB state given a saved TIB from PushTib634** GetInBuf returns a pointer to the next unused char of the TIB635*/636void vmPushTib (FICL_VM *pVM, char *text, FICL_INT nChars, TIB *pSaveTib);637void vmPopTib (FICL_VM *pVM, TIB *pTib);638#define vmGetInBuf(pVM) ((pVM)->tib.cp + (pVM)->tib.index)639#define vmGetInBufLen(pVM) ((pVM)->tib.end - (pVM)->tib.cp)640#define vmGetInBufEnd(pVM) ((pVM)->tib.end)641#define vmGetTibIndex(pVM) (pVM)->tib.index642#define vmSetTibIndex(pVM, i) (pVM)->tib.index = i643#define vmUpdateTib(pVM, str) (pVM)->tib.index = (str) - (pVM)->tib.cp644645/*646** Generally useful string manipulators omitted by ANSI C...647** ltoa complements strtol648*/649#if defined(_WIN32) && !FICL_MAIN650/* #SHEESH651** Why do Microsoft Meatballs insist on contaminating652** my namespace with their string functions???653*/654#pragma warning(disable: 4273)655#endif656657int isPowerOfTwo(FICL_UNS u);658659char *ltoa( FICL_INT value, char *string, int radix );660char *ultoa(FICL_UNS value, char *string, int radix );661char digit_to_char(int value);662char *strrev( char *string );663char *skipSpace(char *cp, char *end);664char *caseFold(char *cp);665int strincmp(char *cp1, char *cp2, FICL_UNS count);666667#if defined(_WIN32) && !FICL_MAIN668#pragma warning(default: 4273)669#endif670671/*672** Ficl hash table - variable size.673** assert(size > 0)674** If size is 1, the table degenerates into a linked list.675** A WORDLIST (see the search order word set in DPANS) is676** just a pointer to a FICL_HASH in this implementation.677*/678#if !defined HASHSIZE /* Default size of hash table. For most uniform */679#define HASHSIZE 241 /* performance, use a prime number! */680#endif681682typedef struct ficl_hash683{684struct ficl_hash *link; /* link to parent class wordlist for OO */685char *name; /* optional pointer to \0 terminated wordlist name */686unsigned size; /* number of buckets in the hash */687FICL_WORD *table[1];688} FICL_HASH;689690void hashForget (FICL_HASH *pHash, void *where);691UNS16 hashHashCode (STRINGINFO si);692void hashInsertWord(FICL_HASH *pHash, FICL_WORD *pFW);693FICL_WORD *hashLookup (FICL_HASH *pHash, STRINGINFO si, UNS16 hashCode);694void hashReset (FICL_HASH *pHash);695696/*697** A Dictionary is a linked list of FICL_WORDs. It is also Ficl's698** memory model. Description of fields:699**700** here -- points to the next free byte in the dictionary. This701** pointer is forced to be CELL-aligned before a definition is added.702** Do not assume any specific alignment otherwise - Use dictAlign().703**704** smudge -- pointer to word currently being defined (or last defined word)705** If the definition completes successfully, the word will be706** linked into the hash table. If unsuccessful, dictUnsmudge707** uses this pointer to restore the previous state of the dictionary.708** Smudge prevents unintentional recursion as a side-effect: the709** dictionary search algo examines only completed definitions, so a710** word cannot invoke itself by name. See the ficl word "recurse".711** NOTE: smudge always points to the last word defined. IMMEDIATE712** makes use of this fact. Smudge is initially NULL.713**714** pForthWords -- pointer to the default wordlist (FICL_HASH).715** This is the initial compilation list, and contains all716** ficl's precompiled words.717**718** pCompile -- compilation wordlist - initially equal to pForthWords719** pSearch -- array of pointers to wordlists. Managed as a stack.720** Highest index is the first list in the search order.721** nLists -- number of lists in pSearch. nLists-1 is the highest722** filled slot in pSearch, and points to the first wordlist723** in the search order724** size -- number of cells in the dictionary (total)725** dict -- start of data area. Must be at the end of the struct.726*/727struct ficl_dict728{729CELL *here;730FICL_WORD *smudge;731FICL_HASH *pForthWords;732FICL_HASH *pCompile;733FICL_HASH *pSearch[FICL_DEFAULT_VOCS];734int nLists;735unsigned size; /* Number of cells in dict (total)*/736CELL *dict; /* Base of dictionary memory */737};738739void *alignPtr(void *ptr);740void dictAbortDefinition(FICL_DICT *pDict);741void dictAlign (FICL_DICT *pDict);742int dictAllot (FICL_DICT *pDict, int n);743int dictAllotCells (FICL_DICT *pDict, int nCells);744void dictAppendCell (FICL_DICT *pDict, CELL c);745void dictAppendChar (FICL_DICT *pDict, char c);746FICL_WORD *dictAppendWord (FICL_DICT *pDict,747char *name,748FICL_CODE pCode,749UNS8 flags);750FICL_WORD *dictAppendWord2(FICL_DICT *pDict,751STRINGINFO si,752FICL_CODE pCode,753UNS8 flags);754void dictAppendUNS (FICL_DICT *pDict, FICL_UNS u);755int dictCellsAvail (FICL_DICT *pDict);756int dictCellsUsed (FICL_DICT *pDict);757void dictCheck (FICL_DICT *pDict, FICL_VM *pVM, int n);758void dictCheckThreshold(FICL_DICT* dp);759FICL_DICT *dictCreate(unsigned nCELLS);760FICL_DICT *dictCreateHashed(unsigned nCells, unsigned nHash);761FICL_HASH *dictCreateWordlist(FICL_DICT *dp, int nBuckets);762void dictDelete (FICL_DICT *pDict);763void dictEmpty (FICL_DICT *pDict, unsigned nHash);764#if FICL_WANT_FLOAT765void dictHashSummary(FICL_VM *pVM);766#endif767int dictIncludes (FICL_DICT *pDict, void *p);768FICL_WORD *dictLookup (FICL_DICT *pDict, STRINGINFO si);769#if FICL_WANT_LOCALS770FICL_WORD *ficlLookupLoc (FICL_SYSTEM *pSys, STRINGINFO si);771#endif772void dictResetSearchOrder(FICL_DICT *pDict);773void dictSetFlags (FICL_DICT *pDict, UNS8 set, UNS8 clr);774void dictSetImmediate(FICL_DICT *pDict);775void dictUnsmudge (FICL_DICT *pDict);776CELL *dictWhere (FICL_DICT *pDict);777778779/*780** P A R S E S T E P781** (New for 2.05)782** See words.c: interpWord783** By default, ficl goes through two attempts to parse each token from its input784** stream: it first attempts to match it with a word in the dictionary, and785** if that fails, it attempts to convert it into a number. This mechanism is now786** extensible by additional steps. This allows extensions like floating point and787** double number support to be factored cleanly.788**789** Each parse step is a function that receives the next input token as a STRINGINFO.790** If the parse step matches the token, it must apply semantics to the token appropriate791** to the present value of VM.state (compiling or interpreting), and return FICL_TRUE.792** Otherwise it returns FICL_FALSE. See words.c: isNumber for an example793**794** Note: for the sake of efficiency, it's a good idea both to limit the number795** of parse steps and to code each parse step so that it rejects tokens that796** do not match as quickly as possible.797*/798799typedef int (*FICL_PARSE_STEP)(FICL_VM *pVM, STRINGINFO si);800801/*802** Appends a parse step function to the end of the parse list (see803** FICL_PARSE_STEP notes in ficl.h for details). Returns 0 if successful,804** nonzero if there's no more room in the list. Each parse step is a word in805** the dictionary. Precompiled parse steps can use (PARSE-STEP) as their806** CFA - see parenParseStep in words.c.807*/808int ficlAddParseStep(FICL_SYSTEM *pSys, FICL_WORD *pFW); /* ficl.c */809void ficlAddPrecompiledParseStep(FICL_SYSTEM *pSys, char *name, FICL_PARSE_STEP pStep);810void ficlListParseSteps(FICL_VM *pVM);811812/*813** FICL_BREAKPOINT record.814** origXT - if NULL, this breakpoint is unused. Otherwise it stores the xt815** that the breakpoint overwrote. This is restored to the dictionary when the816** BP executes or gets cleared817** address - the location of the breakpoint (address of the instruction that818** has been replaced with the breakpoint trap819** origXT - The original contents of the location with the breakpoint820** Note: address is NULL when this breakpoint is empty821*/822typedef struct FICL_BREAKPOINT823{824void *address;825FICL_WORD *origXT;826} FICL_BREAKPOINT;827828829/*830** F I C L _ S Y S T E M831** The top level data structure of the system - ficl_system ties a list of832** virtual machines with their corresponding dictionaries. Ficl 3.0 will833** support multiple Ficl systems, allowing multiple concurrent sessions834** to separate dictionaries with some constraints.835** The present model allows multiple sessions to one dictionary provided836** you implement ficlLockDictionary() as specified in sysdep.h837** Note: the pExtend pointer is there to provide context for applications. It is copied838** to each VM's pExtend field as that VM is created.839*/840struct ficl_system841{842FICL_SYSTEM *link;843void *pExtend; /* Initializes VM's pExtend pointer (for application use) */844FICL_VM *vmList;845FICL_DICT *dp;846FICL_DICT *envp;847#ifdef FICL_WANT_LOCALS848FICL_DICT *localp;849#endif850FICL_WORD *pInterp[3];851FICL_WORD *parseList[FICL_MAX_PARSE_STEPS];852OUTFUNC textOut;853854FICL_WORD *pBranchParen;855FICL_WORD *pDoParen;856FICL_WORD *pDoesParen;857FICL_WORD *pExitInner;858FICL_WORD *pExitParen;859FICL_WORD *pBranch0;860FICL_WORD *pInterpret;861FICL_WORD *pLitParen;862FICL_WORD *pTwoLitParen;863FICL_WORD *pLoopParen;864FICL_WORD *pPLoopParen;865FICL_WORD *pQDoParen;866FICL_WORD *pSemiParen;867FICL_WORD *pOfParen;868FICL_WORD *pStore;869FICL_WORD *pDrop;870FICL_WORD *pCStringLit;871FICL_WORD *pStringLit;872873#if FICL_WANT_LOCALS874FICL_WORD *pGetLocalParen;875FICL_WORD *pGet2LocalParen;876FICL_WORD *pGetLocal0;877FICL_WORD *pGetLocal1;878FICL_WORD *pToLocalParen;879FICL_WORD *pTo2LocalParen;880FICL_WORD *pToLocal0;881FICL_WORD *pToLocal1;882FICL_WORD *pLinkParen;883FICL_WORD *pUnLinkParen;884FICL_INT nLocals;885CELL *pMarkLocals;886#endif887888FICL_BREAKPOINT bpStep;889};890891struct ficl_system_info892{893int size; /* structure size tag for versioning */894int nDictCells; /* Size of system's Dictionary */895OUTFUNC textOut; /* default textOut function */896void *pExtend; /* Initializes VM's pExtend pointer - for application use */897int nEnvCells; /* Size of Environment dictionary */898};899900901#define ficlInitInfo(x) { memset((x), 0, sizeof(FICL_SYSTEM_INFO)); \902(x)->size = sizeof(FICL_SYSTEM_INFO); }903904/*905** External interface to FICL...906*/907/*908** f i c l I n i t S y s t e m909** Binds a global dictionary to the interpreter system and initializes910** the dict to contain the ANSI CORE wordset.911** You can specify the address and size of the allocated area.912** Using ficlInitSystemEx you can also specify the text output function.913** After that, ficl manages it.914** First step is to set up the static pointers to the area.915** Then write the "precompiled" portion of the dictionary in.916** The dictionary needs to be at least large enough to hold the917** precompiled part. Try 1K cells minimum. Use "words" to find918** out how much of the dictionary is used at any time.919*/920FICL_SYSTEM *ficlInitSystemEx(FICL_SYSTEM_INFO *fsi);921922/* Deprecated call */923FICL_SYSTEM *ficlInitSystem(int nDictCells);924925/*926** f i c l T e r m S y s t e m927** Deletes the system dictionary and all virtual machines that928** were created with ficlNewVM (see below). Call this function to929** reclaim all memory used by the dictionary and VMs.930*/931void ficlTermSystem(FICL_SYSTEM *pSys);932933/*934** f i c l E v a l u a t e935** Evaluates a block of input text in the context of the936** specified interpreter. Also sets SOURCE-ID properly.937**938** PLEASE USE THIS FUNCTION when throwing a hard-coded939** string to the FICL interpreter.940*/941int ficlEvaluate(FICL_VM *pVM, char *pText);942943/*944** f i c l E x e c945** Evaluates a block of input text in the context of the946** specified interpreter. Emits any requested output to the947** interpreter's output function. If the input string is NULL948** terminated, you can pass -1 as nChars rather than count it.949** Execution returns when the text block has been executed,950** or an error occurs.951** Returns one of the VM_XXXX codes defined in ficl.h:952** VM_OUTOFTEXT is the normal exit condition953** VM_ERREXIT means that the interp encountered a syntax error954** and the vm has been reset to recover (some or all955** of the text block got ignored956** VM_USEREXIT means that the user executed the "bye" command957** to shut down the interpreter. This would be a good958** time to delete the vm, etc -- or you can ignore this959** signal.960** VM_ABORT and VM_ABORTQ are generated by 'abort' and 'abort"'961** commands.962** Preconditions: successful execution of ficlInitSystem,963** Successful creation and init of the VM by ficlNewVM (or equiv)964**965** If you call ficlExec() or one of its brothers, you MUST966** ensure pVM->sourceID was set to a sensible value.967** ficlExec() explicitly DOES NOT manage SOURCE-ID for you.968*/969int ficlExec (FICL_VM *pVM, char *pText);970int ficlExecC(FICL_VM *pVM, char *pText, FICL_INT nChars);971int ficlExecXT(FICL_VM *pVM, FICL_WORD *pWord);972973/*974** ficlExecFD(FICL_VM *pVM, int fd);975* Evaluates text from file passed in via fd.976* Execution returns when all of file has been executed or an977* error occurs.978*/979int ficlExecFD(FICL_VM *pVM, int fd);980981/*982** Create a new VM from the heap, and link it into the system VM list.983** Initializes the VM and binds default sized stacks to it. Returns the984** address of the VM, or NULL if an error occurs.985** Precondition: successful execution of ficlInitSystem986*/987FICL_VM *ficlNewVM(FICL_SYSTEM *pSys);988989/*990** Force deletion of a VM. You do not need to do this991** unless you're creating and discarding a lot of VMs.992** For systems that use a constant pool of VMs for the life993** of the system, ficltermSystem takes care of VM cleanup994** automatically.995*/996void ficlFreeVM(FICL_VM *pVM);997998999/*1000** Set the stack sizes (return and parameter) to be used for all1001** subsequently created VMs. Returns actual stack size to be used.1002*/1003int ficlSetStackSize(int nStackCells);10041005/*1006** Returns the address of the most recently defined word in the system1007** dictionary with the given name, or NULL if no match.1008** Precondition: successful execution of ficlInitSystem1009*/1010FICL_WORD *ficlLookup(FICL_SYSTEM *pSys, char *name);10111012/*1013** f i c l G e t D i c t1014** Utility function - returns the address of the system dictionary.1015** Precondition: successful execution of ficlInitSystem1016*/1017FICL_DICT *ficlGetDict(FICL_SYSTEM *pSys);1018FICL_DICT *ficlGetEnv (FICL_SYSTEM *pSys);1019void ficlSetEnv (FICL_SYSTEM *pSys, char *name, FICL_UNS value);1020void ficlSetEnvD(FICL_SYSTEM *pSys, char *name, FICL_UNS hi, FICL_UNS lo);1021#if FICL_WANT_LOCALS1022FICL_DICT *ficlGetLoc (FICL_SYSTEM *pSys);1023#endif1024/*1025** f i c l B u i l d1026** Builds a word into the system default dictionary in a thread-safe way.1027** Preconditions: system must be initialized, and there must1028** be enough space for the new word's header! Operation is1029** controlled by ficlLockDictionary, so any initialization1030** required by your version of the function (if you "overrode"1031** it) must be complete at this point.1032** Parameters:1033** name -- the name of the word to be built1034** code -- code to execute when the word is invoked - must take a single param1035** pointer to a FICL_VM1036** flags -- 0 or more of FW_IMMEDIATE, FW_COMPILE, use bitwise OR!1037** Most words can use FW_DEFAULT.1038** nAllot - number of extra cells to allocate in the parameter area (usually zero)1039*/1040int ficlBuild(FICL_SYSTEM *pSys, char *name, FICL_CODE code, char flags);10411042/*1043** f i c l C o m p i l e C o r e1044** Builds the ANS CORE wordset into the dictionary - called by1045** ficlInitSystem - no need to waste dict space by doing it again.1046*/1047void ficlCompileCore(FICL_SYSTEM *pSys);1048void ficlCompilePrefix(FICL_SYSTEM *pSys);1049void ficlCompileSearch(FICL_SYSTEM *pSys);1050void ficlCompileSoftCore(FICL_SYSTEM *pSys);1051void ficlCompileTools(FICL_SYSTEM *pSys);1052void ficlCompileFile(FICL_SYSTEM *pSys);1053#if FICL_WANT_FLOAT1054void ficlCompileFloat(FICL_SYSTEM *pSys);1055int ficlParseFloatNumber( FICL_VM *pVM, STRINGINFO si ); /* float.c */1056#endif1057#if FICL_PLATFORM_EXTEND1058void ficlCompilePlatform(FICL_SYSTEM *pSys);1059#endif1060int ficlParsePrefix(FICL_VM *pVM, STRINGINFO si);10611062/*1063** from words.c...1064*/1065void constantParen(FICL_VM *pVM);1066void twoConstParen(FICL_VM *pVM);1067int ficlParseNumber(FICL_VM *pVM, STRINGINFO si);1068void ficlTick(FICL_VM *pVM);1069void parseStepParen(FICL_VM *pVM);10701071/*1072** From tools.c1073*/1074int isAFiclWord(FICL_DICT *pd, FICL_WORD *pFW);10751076/*1077** The following supports SEE and the debugger.1078*/1079typedef enum1080{1081BRANCH,1082COLON,1083CONSTANT,1084CREATE,1085DO,1086DOES,1087IF,1088LITERAL,1089LOOP,1090OF,1091PLOOP,1092PRIMITIVE,1093QDO,1094STRINGLIT,1095CSTRINGLIT,1096#if FICL_WANT_USER1097USER,1098#endif1099VARIABLE,1100} WORDKIND;11011102WORDKIND ficlWordClassify(FICL_WORD *pFW);11031104/*1105** Dictionary on-demand resizing1106*/1107extern CELL dictThreshold;1108extern CELL dictIncrease;11091110/*1111** Various FreeBSD goodies1112*/11131114#if defined(__i386__) && !defined(TESTMAIN)1115extern void ficlOutb(FICL_VM *pVM);1116extern void ficlInb(FICL_VM *pVM);1117#endif11181119extern void ficlSetenv(FICL_VM *pVM);1120extern void ficlSetenvq(FICL_VM *pVM);1121extern void ficlGetenv(FICL_VM *pVM);1122extern void ficlUnsetenv(FICL_VM *pVM);1123extern void ficlCopyin(FICL_VM *pVM);1124extern void ficlCopyout(FICL_VM *pVM);1125extern void ficlFindfile(FICL_VM *pVM);1126extern void ficlCcall(FICL_VM *pVM);1127#if !defined(TESTMAIN)1128extern void ficlPnpdevices(FICL_VM *pVM);1129extern void ficlPnphandlers(FICL_VM *pVM);1130#endif11311132/*1133** Used with File-Access wordset.1134*/1135#define FICL_FAM_READ 11136#define FICL_FAM_WRITE 21137#define FICL_FAM_APPEND 41138#define FICL_FAM_BINARY 811391140#define FICL_FAM_OPEN_MODE(fam) ((fam) & (FICL_FAM_READ | FICL_FAM_WRITE | FICL_FAM_APPEND))114111421143#if (FICL_WANT_FILE)1144typedef struct ficlFILE1145{1146FILE *f;1147char filename[256];1148} ficlFILE;1149#endif11501151#include <sys/linker_set.h>11521153typedef void ficlCompileFcn(FICL_SYSTEM *);1154#define FICL_COMPILE_SET(func) \1155DATA_SET(X4th_compile_set, func)1156SET_DECLARE(X4th_compile_set, ficlCompileFcn);11571158#ifdef LOADER_VERIEXEC1159#include <verify_file.h>1160#endif11611162#ifdef __cplusplus1163}1164#endif11651166#endif /* __FICL_H__ */116711681169