summaryrefslogtreecommitdiff
path: root/ficl.c
diff options
context:
space:
mode:
Diffstat (limited to 'ficl.c')
-rw-r--r--ficl.c691
1 files changed, 0 insertions, 691 deletions
diff --git a/ficl.c b/ficl.c
deleted file mode 100644
index a9b4029f64384..0000000000000
--- a/ficl.c
+++ /dev/null
@@ -1,691 +0,0 @@
-/*******************************************************************
-** f i c l . c
-** Forth Inspired Command Language - external interface
-** Author: John Sadler (john_sadler@alum.mit.edu)
-** Created: 19 July 1997
-** $Id: ficl.c,v 1.17 2001-12-04 17:58:11-08 jsadler Exp jsadler $
-*******************************************************************/
-/*
-** 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 FICL_SYSTEM 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_MULTITHREAD 1
-** and supply your own version of ficlLockDictionary.
-*/
-static int defaultStack = FICL_DEFAULT_STACK;
-
-
-static void ficlSetVersionEnv(FICL_SYSTEM *pSys);
-
-
-/**************************************************************************
- 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.
-**************************************************************************/
-FICL_SYSTEM *ficlInitSystemEx(FICL_SYSTEM_INFO *fsi)
-{
- int nDictCells;
- int nEnvCells;
- FICL_SYSTEM *pSys = ficlMalloc(sizeof (FICL_SYSTEM));
-
- assert(pSys);
- assert(fsi->size == sizeof (FICL_SYSTEM_INFO));
-
- memset(pSys, 0, sizeof (FICL_SYSTEM));
-
- nDictCells = fsi->nDictCells;
- if (nDictCells <= 0)
- nDictCells = FICL_DEFAULT_DICT;
-
- nEnvCells = fsi->nEnvCells;
- if (nEnvCells <= 0)
- nEnvCells = FICL_DEFAULT_DICT;
-
- pSys->dp = dictCreateHashed((unsigned)nDictCells, HASHSIZE);
- pSys->dp->pForthWords->name = "forth-wordlist";
-
- pSys->envp = dictCreate((unsigned)nEnvCells);
- pSys->envp->pForthWords->name = "environment";
-
- pSys->textOut = fsi->textOut;
- pSys->pExtend = fsi->pExtend;
-
-#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...
- */
- pSys->localp = dictCreate((unsigned)FICL_MAX_LOCALS * CELLS_PER_WORD);
-#endif
-
- /*
- ** 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.
- */
- ficlCompileCore(pSys);
- ficlCompilePrefix(pSys);
-#if FICL_WANT_FLOAT
- ficlCompileFloat(pSys);
-#endif
-#if FICL_PLATFORM_EXTEND
- ficlCompilePlatform(pSys);
-#endif
- ficlSetVersionEnv(pSys);
-
- /*
- ** Establish the parse order. Note that prefixes precede numbers -
- ** this allows constructs like "0b101010" which might parse as a
- ** hex value otherwise.
- */
- ficlAddPrecompiledParseStep(pSys, "?prefix", ficlParsePrefix);
- ficlAddPrecompiledParseStep(pSys, "?number", ficlParseNumber);
-#if FICL_WANT_FLOAT
- ficlAddPrecompiledParseStep(pSys, ">float", ficlParseFloatNumber);
-#endif
-
- /*
- ** Now create a temporary VM to compile the softwords. Since all VMs are
- ** linked into the vmList of FICL_SYSTEM, 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...
- */
- ficlNewVM(pSys);
- ficlCompileSoftCore(pSys);
- ficlFreeVM(pSys->vmList);
-
-
- return pSys;
-}
-
-
-FICL_SYSTEM *ficlInitSystem(int nDictCells)
-{
- FICL_SYSTEM_INFO fsi;
- ficlInitInfo(&fsi);
- fsi.nDictCells = nDictCells;
- return ficlInitSystemEx(&fsi);
-}
-
-
-/**************************************************************************
- 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
-** FICL_PARSE_STEP notes in ficl.h for details). Returns 0 if successful,
-** nonzero if there's no more room in the list.
-**************************************************************************/
-int ficlAddParseStep(FICL_SYSTEM *pSys, FICL_WORD *pFW)
-{
- int i;
- for (i = 0; i < FICL_MAX_PARSE_STEPS; i++)
- {
- if (pSys->parseList[i] == NULL)
- {
- pSys->parseList[i] = pFW;
- return 0;
- }
- }
-
- return 1;
-}
-
-
-/*
-** Compile a word into the dictionary that invokes the specified FICL_PARSE_STEP
-** 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 ficlAddPrecompiledParseStep(FICL_SYSTEM *pSys, char *name, FICL_PARSE_STEP pStep)
-{
- FICL_DICT *dp = pSys->dp;
- FICL_WORD *pFW = dictAppendWord(dp, name, parseStepParen, FW_DEFAULT);
- dictAppendCell(dp, LVALUEtoCELL(pStep));
- ficlAddParseStep(pSys, pFW);
-}
-
-
-/*
-** This word lists the parse steps in order
-*/
-void ficlListParseSteps(FICL_VM *pVM)
-{
- int i;
- FICL_SYSTEM *pSys = pVM->pSys;
- assert(pSys);
-
- vmTextOut(pVM, "Parse steps:", 1);
- vmTextOut(pVM, "lookup", 1);
-
- for (i = 0; i < FICL_MAX_PARSE_STEPS; i++)
- {
- if (pSys->parseList[i] != NULL)
- {
- vmTextOut(pVM, pSys->parseList[i]->name, 1);
- }
- else break;
- }
- return;
-}
-
-
-/**************************************************************************
- 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.
-**************************************************************************/
-FICL_VM *ficlNewVM(FICL_SYSTEM *pSys)
-{
- FICL_VM *pVM = vmCreate(NULL, defaultStack, defaultStack);
- pVM->link = pSys->vmList;
- pVM->pSys = pSys;
- pVM->pExtend = pSys->pExtend;
- vmSetTextOut(pVM, pSys->textOut);
-
- pSys->vmList = pVM;
- return pVM;
-}
-
-
-/**************************************************************************
- 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 ficlFreeVM(FICL_VM *pVM)
-{
- FICL_SYSTEM *pSys = pVM->pSys;
- FICL_VM *pList = pSys->vmList;
-
- assert(pVM != 0);
-
- if (pSys->vmList == pVM)
- {
- pSys->vmList = pSys->vmList->link;
- }
- else for (; pList != NULL; pList = pList->link)
- {
- if (pList->link == pVM)
- {
- pList->link = pVM->link;
- break;
- }
- }
-
- if (pList)
- vmDelete(pVM);
- return;
-}
-
-
-/**************************************************************************
- f i c l B u i l d
-** Builds a word into the dictionary.
-** Preconditions: system must be initialized, and there must
-** be enough space for the new word's header! Operation is
-** controlled by ficlLockDictionary, so any initialization
-** required by your version of the function (if you overrode
-** it) must be complete at this point.
-** Parameters:
-** name -- duh, the name of the word
-** code -- code to execute when the word is invoked - must take a single param
-** pointer to a FICL_VM
-** flags -- 0 or more of F_IMMEDIATE, F_COMPILE, use bitwise OR!
-**
-**************************************************************************/
-int ficlBuild(FICL_SYSTEM *pSys, char *name, FICL_CODE code, char flags)
-{
-#if FICL_MULTITHREAD
- int err = ficlLockDictionary(TRUE);
- if (err) return err;
-#endif /* FICL_MULTITHREAD */
-
- assert(dictCellsAvail(pSys->dp) > sizeof (FICL_WORD) / sizeof (CELL));
- dictAppendWord(pSys->dp, name, code, flags);
-
- ficlLockDictionary(FALSE);
- return 0;
-}
-
-
-/**************************************************************************
- f i c l E v a l u a t e
-** Wrapper for ficlExec() which sets SOURCE-ID to -1.
-**************************************************************************/
-int ficlEvaluate(FICL_VM *pVM, char *pText)
-{
- int returnValue;
- CELL id = pVM->sourceID;
- pVM->sourceID.i = -1;
- returnValue = ficlExecC(pVM, pText, -1);
- pVM->sourceID = id;
- return returnValue;
-}
-
-
-/**************************************************************************
- f i c l E x e c
-** Evaluates a block of input text in the context of the
-** specified interpreter. Emits any requested output to the
-** interpreter's output function.
-**
-** Contains the "inner interpreter" code in a tight loop
-**
-** Returns one of the VM_XXXX codes defined in ficl.h:
-** VM_OUTOFTEXT is the normal exit condition
-** VM_ERREXIT means that the interp encountered a syntax error
-** and the vm has been reset to recover (some or all
-** of the text block got ignored
-** VM_USEREXIT means that the user executed the "bye" command
-** to shut down the interpreter. This would be a good
-** time to delete the vm, etc -- or you can ignore this
-** signal.
-**************************************************************************/
-int ficlExec(FICL_VM *pVM, char *pText)
-{
- return ficlExecC(pVM, pText, -1);
-}
-
-int ficlExecC(FICL_VM *pVM, char *pText, FICL_INT size)
-{
- FICL_SYSTEM *pSys = pVM->pSys;
- FICL_DICT *dp = pSys->dp;
-
- int except;
- jmp_buf vmState;
- jmp_buf *oldState;
- TIB saveTib;
-
- assert(pVM);
- assert(pSys->pInterp[0]);
-
- if (size < 0)
- size = strlen(pText);
-
- vmPushTib(pVM, pText, size, &saveTib);
-
- /*
- ** Save and restore VM's jmp_buf to enable nested calls to ficlExec
- */
- oldState = pVM->pState;
- pVM->pState = &vmState; /* This has to come before the setjmp! */
- except = setjmp(vmState);
-
- switch (except)
- {
- case 0:
- if (pVM->fRestart)
- {
- pVM->runningWord->code(pVM);
- pVM->fRestart = 0;
- }
- else
- { /* set VM up to interpret text */
- vmPushIP(pVM, &(pSys->pInterp[0]));
- }
-
- vmInnerLoop(pVM);
- break;
-
- case VM_RESTART:
- pVM->fRestart = 1;
- except = VM_OUTOFTEXT;
- break;
-
- case VM_OUTOFTEXT:
- vmPopIP(pVM);
- if ((pVM->state != COMPILE) && (pVM->sourceID.i == 0))
- ficlTextOut(pVM, FICL_PROMPT, 0);
- break;
-
- case VM_USEREXIT:
- case VM_INNEREXIT:
- case VM_BREAK:
- break;
-
- case VM_QUIT:
- if (pVM->state == COMPILE)
- {
- dictAbortDefinition(dp);
-#if FICL_WANT_LOCALS
- dictEmpty(pSys->localp, pSys->localp->pForthWords->size);
-#endif
- }
- vmQuit(pVM);
- break;
-
- case VM_ERREXIT:
- case VM_ABORT:
- case VM_ABORTQ:
- default: /* user defined exit code?? */
- if (pVM->state == COMPILE)
- {
- dictAbortDefinition(dp);
-#if FICL_WANT_LOCALS
- dictEmpty(pSys->localp, pSys->localp->pForthWords->size);
-#endif
- }
- dictResetSearchOrder(dp);
- vmReset(pVM);
- break;
- }
-
- pVM->pState = oldState;
- vmPopTib(pVM, &saveTib);
- return (except);
-}
-
-
-/**************************************************************************
- f i c l E x e c X T
-** Given a pointer to a FICL_WORD, push an inner interpreter and
-** execute the word to completion. This is in contrast with vmExecute,
-** which does not guarantee that the word will have completed when
-** the function returns (ie in the case of colon definitions, which
-** need an inner interpreter to finish)
-**
-** Returns one of the VM_XXXX exception codes listed in ficl.h. Normal
-** exit condition is VM_INNEREXIT, ficl's private signal to exit the
-** inner loop under normal circumstances. If another code is thrown to
-** exit the loop, this function will re-throw it if it's nested under
-** itself or ficlExec.
-**
-** NOTE: this function is intended so that C code can execute ficlWords
-** given their address in the dictionary (xt).
-**************************************************************************/
-int ficlExecXT(FICL_VM *pVM, FICL_WORD *pWord)
-{
- int except;
- jmp_buf vmState;
- jmp_buf *oldState;
- FICL_WORD *oldRunningWord;
-
- assert(pVM);
- assert(pVM->pSys->pExitInner);
-
- /*
- ** Save the runningword so that RESTART behaves correctly
- ** over nested calls.
- */
- oldRunningWord = pVM->runningWord;
- /*
- ** Save and restore VM's jmp_buf to enable nested calls
- */
- oldState = pVM->pState;
- pVM->pState = &vmState; /* This has to come before the setjmp! */
- except = setjmp(vmState);
-
- if (except)
- vmPopIP(pVM);
- else
- vmPushIP(pVM, &(pVM->pSys->pExitInner));
-
- switch (except)
- {
- case 0:
- vmExecute(pVM, pWord);
- vmInnerLoop(pVM);
- break;
-
- case VM_INNEREXIT:
- case VM_BREAK:
- break;
-
- case VM_RESTART:
- case VM_OUTOFTEXT:
- case VM_USEREXIT:
- case VM_QUIT:
- case VM_ERREXIT:
- case VM_ABORT:
- case VM_ABORTQ:
- default: /* user defined exit code?? */
- if (oldState)
- {
- pVM->pState = oldState;
- vmThrow(pVM, except);
- }
- break;
- }
-
- pVM->pState = oldState;
- pVM->runningWord = oldRunningWord;
- return (except);
-}
-
-
-/**************************************************************************
- 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 FICL_WORD. Otherwise
-** return NULL.
-**************************************************************************/
-FICL_WORD *ficlLookup(FICL_SYSTEM *pSys, char *name)
-{
- STRINGINFO si;
- SI_PSZ(si, name);
- return dictLookup(pSys->dp, si);
-}
-
-
-/**************************************************************************
- f i c l G e t D i c t
-** Returns the address of the system dictionary
-**************************************************************************/
-FICL_DICT *ficlGetDict(FICL_SYSTEM *pSys)
-{
- return pSys->dp;
-}
-
-
-/**************************************************************************
- f i c l G e t E n v
-** Returns the address of the system environment space
-**************************************************************************/
-FICL_DICT *ficlGetEnv(FICL_SYSTEM *pSys)
-{
- return pSys->envp;
-}
-
-
-/**************************************************************************
- f i c l S e t E n v
-** Create an environment variable with a one-CELL payload. ficlSetEnvD
-** makes one with a two-CELL payload.
-**************************************************************************/
-void ficlSetEnv(FICL_SYSTEM *pSys, char *name, FICL_UNS value)
-{
- STRINGINFO si;
- FICL_WORD *pFW;
- FICL_DICT *envp = pSys->envp;
-
- SI_PSZ(si, name);
- pFW = dictLookup(envp, si);
-
- if (pFW == NULL)
- {
- dictAppendWord(envp, name, constantParen, FW_DEFAULT);
- dictAppendCell(envp, LVALUEtoCELL(value));
- }
- else
- {
- pFW->param[0] = LVALUEtoCELL(value);
- }
-
- return;
-}
-
-void ficlSetEnvD(FICL_SYSTEM *pSys, char *name, FICL_UNS hi, FICL_UNS lo)
-{
- FICL_WORD *pFW;
- STRINGINFO si;
- FICL_DICT *envp = pSys->envp;
- SI_PSZ(si, name);
- pFW = dictLookup(envp, si);
-
- if (pFW == NULL)
- {
- dictAppendWord(envp, name, twoConstParen, FW_DEFAULT);
- dictAppendCell(envp, LVALUEtoCELL(lo));
- dictAppendCell(envp, LVALUEtoCELL(hi));
- }
- else
- {
- pFW->param[0] = LVALUEtoCELL(lo);
- pFW->param[1] = LVALUEtoCELL(hi);
- }
-
- return;
-}
-
-
-/**************************************************************************
- f i c l G e t L o c
-** Returns the address of the system locals dictionary. This dict is
-** only used during compilation, and is shared by all VMs.
-**************************************************************************/
-#if FICL_WANT_LOCALS
-FICL_DICT *ficlGetLoc(FICL_SYSTEM *pSys)
-{
- return pSys->localp;
-}
-#endif
-
-
-
-/**************************************************************************
- f i c l S e t S t a c k S i z e
-** Set the stack sizes (return and parameter) to be used for all
-** subsequently created VMs. Returns actual stack size to be used.
-**************************************************************************/
-int ficlSetStackSize(int nStackCells)
-{
- if (nStackCells >= FICL_DEFAULT_STACK)
- defaultStack = nStackCells;
- else
- defaultStack = FICL_DEFAULT_STACK;
-
- return defaultStack;
-}
-
-
-/**************************************************************************
- 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 ficlTermSystem(FICL_SYSTEM *pSys)
-{
- if (pSys->dp)
- dictDelete(pSys->dp);
- pSys->dp = NULL;
-
- if (pSys->envp)
- dictDelete(pSys->envp);
- pSys->envp = NULL;
-
-#if FICL_WANT_LOCALS
- if (pSys->localp)
- dictDelete(pSys->localp);
- pSys->localp = NULL;
-#endif
-
- while (pSys->vmList != NULL)
- {
- FICL_VM *pVM = pSys->vmList;
- pSys->vmList = pSys->vmList->link;
- vmDelete(pVM);
- }
-
- ficlFree(pSys);
- pSys = NULL;
- return;
-}
-
-
-/**************************************************************************
- f i c l S e t V e r s i o n E n v
-** Create a double cell environment constant for the version ID
-**************************************************************************/
-static void ficlSetVersionEnv(FICL_SYSTEM *pSys)
-{
- int major = 0;
- int minor = 0;
- sscanf(FICL_VER, "%d.%d", &major, &minor);
- ficlSetEnvD(pSys, "ficl-version", major, minor);
- ficlSetEnv (pSys, "ficl-robust", FICL_ROBUST);
- return;
-}
-