| /* |
| * 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.2 2010/09/10 10:35:54 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 "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 = FICL_VERSION_MAJOR; |
| int minor = FICL_VERSION_MINOR; |
| ficl2Integer combined; |
| ficlDictionary *environment = ficlSystemGetEnvironment(system); |
| FICL_2INTEGER_SET(major, minor, combined); |
| ficlDictionarySet2Constant(environment, "ficl-version", combined); |
| ficlDictionarySetConstant(environment, "ficl-robust", FICL_ROBUST); |
| } |
| |
| /* |
| * 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 = 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_ENVIRONMENT_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_LZ4_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); |
| } |
| |
| if (ficlSystemGlobal == system) |
| ficlSystemGlobal = NULL; |
| |
| ficlFree(system); |
| system = NULL; |
| } |
| |
| /* |
| * 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; |
| ficlCell c; |
| |
| word = ficlDictionaryAppendPrimitive(dictionary, name, |
| ficlPrimitiveParseStepParen, FICL_WORD_DEFAULT); |
| |
| c.fn = (void (*)(void))pStep; |
| ficlDictionaryAppendCell(dictionary, c); |
| 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); |
| } |
| |
| /* |
| * 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 |