ref: 67757267d0b9c6b15a0f9a87abab74ab152d9b09
dir: /system.c/
/******************************************************************* ** f i c l . c ** Forth Inspired Command Language - external interface ** Author: John Sadler (john_sadler@alum.mit.edu) ** Created: 19 July 1997 ** $Id: system.c,v 1.4 2010/12/02 13:56:43 asau Exp $ *******************************************************************/ /* ** This is an ANS Forth interpreter written in C. ** Ficl uses Forth syntax for its commands, but turns the Forth ** model on its head in other respects. ** Ficl provides facilities for interoperating ** with programs written in C: C functions can be exported to Ficl, ** and Ficl commands can be executed via a C calling interface. The ** interpreter is re-entrant, so it can be used in multiple instances ** in a multitasking system. Unlike Forth, Ficl's outer interpreter ** expects a text block as input, and returns to the caller after each ** text block, so the data pump is somewhere in external code in the ** style of TCL. ** ** Code is written in ANSI C for portability. */ /* ** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu) ** All rights reserved. ** ** Get the latest Ficl release at http://ficl.sourceforge.net ** ** I am interested in hearing from anyone who uses Ficl. If you have ** a problem, a success story, a defect, an enhancement request, or ** if you would like to contribute to the Ficl release, please ** contact me by email at the address above. ** ** L I C E N S E and D I S C L A I M E R ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. */ #include <stdlib.h> #include <string.h> #include "ficl.h" /* ** System statics ** Each ficlSystem builds a global dictionary during its start ** sequence. This is shared by all virtual machines of that system. ** Therefore only one VM can update the dictionary ** at a time. The system imports a locking function that ** you can override in order to control update access to ** the dictionary. The function is stubbed out by default, ** but you can insert one: #define FICL_WANT_MULTITHREADED 1 ** and supply your own version of ficlDictionaryLock. */ ficlSystem *ficlSystemGlobal = NULL; /************************************************************************** f i c l S e t V e r s i o n E n v ** Create a double ficlCell environment constant for the version ID **************************************************************************/ static void ficlSystemSetVersion(ficlSystem *system) { int major = 0; int minor = 0; ficl2Integer combined; ficlDictionary *environment = ficlSystemGetEnvironment(system); sscanf(FICL_VERSION, "%d.%d", &major, &minor); FICL_2INTEGER_SET(major, minor, combined); ficlDictionarySet2Constant(environment, "ficl-version", combined); ficlDictionarySetConstant(environment, "ficl-robust", FICL_ROBUST); return; } /************************************************************************** f i c l I n i t S y s t e m ** Binds a global dictionary to the interpreter system. ** You specify the address and size of the allocated area. ** After that, Ficl manages it. ** First step is to set up the static pointers to the area. ** Then write the "precompiled" portion of the dictionary in. ** The dictionary needs to be at least large enough to hold the ** precompiled part. Try 1K cells minimum. Use "words" to find ** out how much of the dictionary is used at any time. **************************************************************************/ ficlSystem *ficlSystemCreate(ficlSystemInformation *fsi) { ficlInteger dictionarySize; ficlInteger environmentSize; ficlInteger stackSize; ficlSystem *system; ficlCallback callback; ficlSystemInformation fauxInfo; ficlDictionary *environment; if (fsi == NULL) { fsi = &fauxInfo; ficlSystemInformationInitialize(fsi); } callback.context = fsi->context; callback.textOut = fsi->textOut; callback.errorOut = fsi->errorOut; callback.system = NULL; callback.vm = NULL; FICL_ASSERT(&callback, sizeof(ficlInteger) >= sizeof(void *)); FICL_ASSERT(&callback, sizeof(ficlUnsigned) >= sizeof(void *)); #if (FICL_WANT_FLOAT) FICL_ASSERT(&callback, sizeof(ficlFloat) <= sizeof(ficlInteger)); #endif system = (ficlSystem*)ficlMalloc(sizeof(ficlSystem)); FICL_ASSERT(&callback, system); memset(system, 0, sizeof(ficlSystem)); dictionarySize = fsi->dictionarySize; if (dictionarySize <= 0) dictionarySize = FICL_DEFAULT_DICTIONARY_SIZE; environmentSize = fsi->environmentSize; if (environmentSize <= 0) environmentSize = FICL_DEFAULT_DICTIONARY_SIZE; stackSize = fsi->stackSize; if (stackSize < FICL_DEFAULT_STACK_SIZE) stackSize = FICL_DEFAULT_STACK_SIZE; system->dictionary = ficlDictionaryCreateHashed(system, (unsigned)dictionarySize, FICL_HASH_SIZE); system->dictionary->forthWordlist->name = "forth-wordlist"; environment = ficlDictionaryCreate(system, (unsigned)environmentSize); system->environment = environment; system->environment->forthWordlist->name = "environment"; system->callback.textOut = fsi->textOut; system->callback.errorOut = fsi->errorOut; system->callback.context = fsi->context; system->callback.system = system; system->callback.vm = NULL; system->stackSize = stackSize; #if FICL_WANT_LOCALS /* ** The locals dictionary is only searched while compiling, ** but this is where speed is most important. On the other ** hand, the dictionary gets emptied after each use of locals ** The need to balance search speed with the cost of the 'empty' ** operation led me to select a single-threaded list... */ system->locals = ficlDictionaryCreate(system, (unsigned)FICL_MAX_LOCALS * FICL_CELLS_PER_WORD); #endif /* FICL_WANT_LOCALS */ /* ** Build the precompiled dictionary and load softwords. We need a temporary ** VM to do this - ficlNewVM links one to the head of the system VM list. ** ficlCompilePlatform (defined in win32.c, for example) adds platform specific words. */ ficlSystemCompileCore(system); ficlSystemCompilePrefix(system); #if FICL_WANT_FLOAT ficlSystemCompileFloat(system); #endif /* FICL_WANT_FLOAT */ #if FICL_WANT_PLATFORM ficlSystemCompilePlatform(system); #endif /* FICL_WANT_PLATFORM */ ficlSystemSetVersion(system); /* ** Establish the parse order. Note that prefixes precede numbers - ** this allows constructs like "0b101010" which might parse as a ** hex value otherwise. */ ficlSystemAddPrimitiveParseStep(system, "?word", ficlVmParseWord); ficlSystemAddPrimitiveParseStep(system, "?prefix", ficlVmParsePrefix); ficlSystemAddPrimitiveParseStep(system, "?number", ficlVmParseNumber); #if FICL_WANT_FLOAT ficlSystemAddPrimitiveParseStep(system, "?float", ficlVmParseFloatNumber); #endif /* ** Now create a temporary VM to compile the softwords. Since all VMs are ** linked into the vmList of ficlSystem, we don't have to pass the VM ** to ficlCompileSoftCore -- it just hijacks whatever it finds in the VM list. ** Ficl 2.05: vmCreate no longer depends on the presence of INTERPRET in the ** dictionary, so a VM can be created before the dictionary is built. It just ** can't do much... */ ficlSystemCreateVm(system); #define ADD_COMPILE_FLAG(name) ficlDictionarySetConstant(environment, #name, name) ADD_COMPILE_FLAG(FICL_WANT_LZ_SOFTCORE); ADD_COMPILE_FLAG(FICL_WANT_FILE); ADD_COMPILE_FLAG(FICL_WANT_FLOAT); ADD_COMPILE_FLAG(FICL_WANT_DEBUGGER); ADD_COMPILE_FLAG(FICL_WANT_EXTENDED_PREFIX); ADD_COMPILE_FLAG(FICL_WANT_USER); ADD_COMPILE_FLAG(FICL_WANT_LOCALS); ADD_COMPILE_FLAG(FICL_WANT_OOP); ADD_COMPILE_FLAG(FICL_WANT_SOFTWORDS); ADD_COMPILE_FLAG(FICL_WANT_MULTITHREADED); ADD_COMPILE_FLAG(FICL_WANT_OPTIMIZE); ADD_COMPILE_FLAG(FICL_WANT_VCALL); ADD_COMPILE_FLAG(FICL_PLATFORM_ALIGNMENT); ADD_COMPILE_FLAG(FICL_ROBUST); #define ADD_COMPILE_STRING(name) ficlDictionarySetConstantString(environment, #name, name) ADD_COMPILE_STRING(FICL_PLATFORM_ARCHITECTURE); ADD_COMPILE_STRING(FICL_PLATFORM_OS); ficlSystemCompileSoftCore(system); ficlSystemDestroyVm(system->vmList); if (ficlSystemGlobal == NULL) ficlSystemGlobal = system; return system; } /************************************************************************** f i c l T e r m S y s t e m ** Tear the system down by deleting the dictionaries and all VMs. ** This saves you from having to keep track of all that stuff. **************************************************************************/ void ficlSystemDestroy(ficlSystem *system) { if (system->dictionary) ficlDictionaryDestroy(system->dictionary); system->dictionary = NULL; if (system->environment) ficlDictionaryDestroy(system->environment); system->environment = NULL; #if FICL_WANT_LOCALS if (system->locals) ficlDictionaryDestroy(system->locals); system->locals = NULL; #endif while (system->vmList != NULL) { ficlVm *vm = system->vmList; system->vmList = system->vmList->link; ficlVmDestroy(vm); } ficlFree(system); system = NULL; if (ficlSystemGlobal == system) ficlSystemGlobal = NULL; return; } /************************************************************************** f i c l A d d P a r s e S t e p ** Appends a parse step function to the end of the parse list (see ** ficlParseStep notes in ficl.h for details). Returns 0 if successful, ** nonzero if there's no more room in the list. **************************************************************************/ int ficlSystemAddParseStep(ficlSystem *system, ficlWord *word) { int i; for (i = 0; i < FICL_MAX_PARSE_STEPS; i++) { if (system->parseList[i] == NULL) { system->parseList[i] = word; return 0; } } return 1; } /* ** Compile a word into the dictionary that invokes the specified ficlParseStep ** function. It is up to the user (as usual in Forth) to make sure the stack ** preconditions are valid (there needs to be a counted string on top of the stack) ** before using the resulting word. */ void ficlSystemAddPrimitiveParseStep(ficlSystem *system, char *name, ficlParseStep pStep) { ficlDictionary *dictionary = system->dictionary; ficlWord *word = ficlDictionaryAppendPrimitive(dictionary, name, ficlPrimitiveParseStepParen, FICL_WORD_DEFAULT); ficlDictionaryAppendCell(dictionary, FICL_LVALUE_TO_CELL(pStep)); ficlSystemAddParseStep(system, word); } /************************************************************************** f i c l N e w V M ** Create a new virtual machine and link it into the system list ** of VMs for later cleanup by ficlTermSystem. **************************************************************************/ ficlVm *ficlSystemCreateVm(ficlSystem *system) { ficlVm *vm = ficlVmCreate(NULL, system->stackSize, system->stackSize); vm->link = system->vmList; memcpy(&(vm->callback), &(system->callback), sizeof(system->callback)); vm->callback.vm = vm; vm->callback.system = system; system->vmList = vm; return vm; } /************************************************************************** f i c l F r e e V M ** Removes the VM in question from the system VM list and deletes the ** memory allocated to it. This is an optional call, since ficlTermSystem ** will do this cleanup for you. This function is handy if you're going to ** do a lot of dynamic creation of VMs. **************************************************************************/ void ficlSystemDestroyVm(ficlVm *vm) { ficlSystem *system = vm->callback.system; ficlVm *pList = system->vmList; FICL_VM_ASSERT(vm, vm != NULL); if (system->vmList == vm) { system->vmList = system->vmList->link; } else for (; pList != NULL; pList = pList->link) { if (pList->link == vm) { pList->link = vm->link; break; } } if (pList) ficlVmDestroy(vm); return; } /************************************************************************** f i c l L o o k u p ** Look in the system dictionary for a match to the given name. If ** found, return the address of the corresponding ficlWord. Otherwise ** return NULL. **************************************************************************/ ficlWord *ficlSystemLookup(ficlSystem *system, char *name) { ficlString s; FICL_STRING_SET_FROM_CSTRING(s, name); return ficlDictionaryLookup(system->dictionary, s); } /************************************************************************** f i c l G e t D i c t ** Returns the address of the system dictionary **************************************************************************/ ficlDictionary *ficlSystemGetDictionary(ficlSystem *system) { return system->dictionary; } /************************************************************************** f i c l G e t E n v ** Returns the address of the system environment space **************************************************************************/ ficlDictionary *ficlSystemGetEnvironment(ficlSystem *system) { return system->environment; } /************************************************************************** f i c l G e t L o c ** Returns the address of the system locals dictionary. This dictionary is ** only used during compilation, and is shared by all VMs. **************************************************************************/ #if FICL_WANT_LOCALS ficlDictionary *ficlSystemGetLocals(ficlSystem *system) { return system->locals; } #endif /************************************************************************** f i c l L o o k u p L o c ** Same as dictLookup, but looks in system locals dictionary first... ** Assumes locals dictionary has only one wordlist... **************************************************************************/ #if FICL_WANT_LOCALS ficlWord *ficlSystemLookupLocal(ficlSystem *system, ficlString name) { ficlWord *word = NULL; ficlDictionary *dictionary = system->dictionary; ficlHash *hash = ficlSystemGetLocals(system)->forthWordlist; int i; ficlUnsigned16 hashCode = ficlHashCode(name); FICL_SYSTEM_ASSERT(system, hash); FICL_SYSTEM_ASSERT(system, dictionary); ficlDictionaryLock(dictionary, FICL_TRUE); /* ** check the locals dictionary first... */ word = ficlHashLookup(hash, name, hashCode); /* ** If no joy, (!word) ------------------------------v ** iterate over the search list in the main dictionary */ for (i = (int)dictionary->wordlistCount - 1; (i >= 0) && (!word); --i) { hash = dictionary->wordlists[i]; word = ficlHashLookup(hash, name, hashCode); } ficlDictionaryLock(dictionary, FICL_FALSE); return word; } #endif