aboutsummaryrefslogtreecommitdiff
path: root/system.c
diff options
context:
space:
mode:
Diffstat (limited to 'system.c')
-rw-r--r--system.c466
1 files changed, 466 insertions, 0 deletions
diff --git a/system.c b/system.c
new file mode 100644
index 000000000000..6caff931cf6f
--- /dev/null
+++ b/system.c
@@ -0,0 +1,466 @@
+/*******************************************************************
+** 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 <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 = 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
+
+