/*
* f i c l . h
* Forth Inspired Command Language
* Author: John Sadler (john_sadler@alum.mit.edu)
* Created: 19 July 1997
* Dedicated to RHS, in loving memory
* $Id: ficl.h,v 1.25 2010/10/03 09:52:12 asau Exp $
*/
/*
* 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
*
* 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.
*/
#ifndef _FICL_H
#define _FICL_H
/*
* Ficl (Forth-inspired command language) is an ANS Forth
* interpreter written in C. Unlike traditional Forths, this
* interpreter is designed to be embedded into other systems
* as a command/macro/development prototype language.
*
* Where Forths usually view themselves as the center of the system
* and expect the rest of the system to be coded in Forth, Ficl
* acts as a component of the system. It is easy to export
* code written in C or ASM to Ficl in the style of TCL, or to invoke
* Ficl code from a compiled module. This allows you to do incremental
* development in a way that combines the best features of threaded
* reasonably fast) with the best features of C (everyone knows it,
* easier to support large blocks of code, efficient, type checking).
*
* 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. This
* is more like TCL than Forth, which usually expects to be at the center
* of the system, requesting input at its convenience. Each Ficl virtual
* machine can be bound to a different I/O channel, and is independent
* of all others in in the same address space except that all virtual
* machines share a common dictionary (a sort or open symbol table that
* defines all of the elements of the language).
*
* Code is written in ANSI C for portability.
*
* Summary of Ficl features and constraints:
* - Standard: Implements the ANSI Forth CORE word set and part
* of the CORE EXT word-set, SEARCH and SEARCH EXT, TOOLS and
* TOOLS EXT, LOCAL and LOCAL ext and various extras.
* - Extensible: you can export code written in Forth, C,
* or asm in a straightforward way. Ficl provides open
* facilities for extending the language in an application
* specific way. You can even add new control structures!
* - Ficl and C can interact in two ways: Ficl can encapsulate
* C code, or C code can invoke Ficl code.
* - Thread-safe, re-entrant: The shared system dictionary
* uses a locking mechanism that you can either supply
* or stub out to provide exclusive access. Each Ficl
* virtual machine has an otherwise complete state, and
* each can be bound to a separate I/O channel (or none at all).
* - Simple encapsulation into existing systems: a basic implementation
* requires three function calls (see the example program in testmain.c).
* - ROMable: Ficl is designed to work in RAM-based and ROM code / RAM data
* environments. It does require somewhat more memory than a pure
* ROM implementation because it builds its system dictionary in
* RAM at startup time.
* - Written an ANSI C to be as simple as I can make it to understand,
* support, debug, and port. Compiles without complaint at /Az /W4
* (require ANSI C, max warnings) under Microsoft VC++ 5.
* - Does full 32 bit math (but you need to implement
* two mixed precision math primitives (see sysdep.c))
* - Indirect threaded interpreter is not the fastest kind of
* Forth there is (see pForth 68K for a really fast subroutine
* threaded interpreter), but it's the cleanest match to a
* pure C implementation.
*
* P O R T I N G F i c l
*
* To install Ficl on your target system, you need an ANSI C compiler
* and its runtime library. Inspect the system dependent macros and
* system. For example, INT16 is a short on some compilers and an
* int on others. Check the default CELL alignment controlled by
* FICL_ALIGN. If necessary, add new definitions of ficlMalloc, ficlFree,
* ficlLockDictionary, and ficlCallbackDefaultTextOut to work with your
* operating system. Finally, use testmain.c as a guide to installing the
* Ficl system and one or more virtual machines into your code. You do not
* need to include testmain.c in your build.
*
* T o D o L i s t
*
* 1. Unimplemented system dependent CORE word: key
* 2. Ficl uses the PAD in some CORE words - this violates the standard,
* but it's cleaner for a multithreaded system. I'll have to make a
* second pad for reference by the word PAD to fix this.
*
* F o r M o r e I n f o r m a t i o n
*
* Web home of Ficl
* Check this website for Forth literature (including the ANSI standard)
* and here for software and more links
*/
#ifdef __cplusplus
extern "C" {
#endif
#ifdef STAND
#include <stand.h>
#else
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
extern void pager_open(void);
extern int pager_output(const char *);
extern void pager_close(void);
#endif
#include <setjmp.h>
#include <stdarg.h>
/*
* Put all your local defines in ficllocal.h,
* ficllocal.h will always ship as an inert file.
*/
#include "ficllocal.h"
#include "ficlplatform/unix.h"
/*
*
* B U I L D C O N T R O L S
*
* First, the FICL_WANT_* settings.
* These are all optional settings that you may or may not
* want Ficl to use.
*
*/
/*
* FICL_WANT_MINIMAL
* If set to nonzero, build the smallest possible Ficl interpreter.
*/
#if !defined(FICL_WANT_MINIMAL)
#define FICL_WANT_MINIMAL (0)
#endif
#define FICL_WANT_SOFTWORDS (0)
#define FICL_WANT_FILE (0)
#define FICL_WANT_FLOAT (0)
#define FICL_WANT_USER (0)
#define FICL_WANT_LOCALS (0)
#define FICL_WANT_DEBUGGER (0)
#define FICL_WANT_OOP (0)
#define FICL_WANT_PLATFORM (0)
#define FICL_WANT_MULTITHREADED (0)
#define FICL_WANT_EXTENDED_PREFIX (0)
#define FICL_ROBUST (0)
#endif /* FICL_WANT_MINIMAL */
/*
* FICL_WANT_PLATFORM
* Includes words defined in ficlCompilePlatform
* (see ficlplatform/win32.c and ficlplatform/unix.c for example)
*/
#if !defined(FICL_WANT_PLATFORM)
#endif /* FICL_WANT_PLATFORM */
/*
* FICL_WANT_LZ4_SOFTCORE
* If nonzero, the softcore words are stored compressed
* with patent-unencumbered LZ4 compression.
* This results in a smaller Ficl interpreter, and adds
* only a *tiny* runtime speed hit.
*
* Original LZ77 contributed by Larry Hastings.
* Updated to LZ4 which is even more space efficient.
*/
#if !defined(FICL_WANT_LZ4_SOFTCORE)
#endif /* FICL_WANT_LZ4_SOFTCORE */
/*
* FICL_WANT_FILE
* Includes the FILE and FILE-EXT wordset and associated code.
* Turn this off if you do not have a file system!
* Contributed by Larry Hastings
*/
#if !defined(FICL_WANT_FILE)
#define FICL_WANT_FILE (0)
#endif /* FICL_WANT_FILE */
/*
* FICL_WANT_FLOAT
* Includes a floating point stack for the VM, and words to do float operations.
* Contributed by Guy Carver
*/
#if !defined(FICL_WANT_FLOAT)
#endif /* FICL_WANT_FLOAT */
/*
* FICL_WANT_DEBUGGER
* Inludes a simple source level debugger
*/
#if !defined(FICL_WANT_DEBUGGER)
#endif /* FICL_WANT_DEBUGGER */
/*
* FICL_EXTENDED_PREFIX
* Enables a bunch of extra prefixes in prefix.c
* and prefix.fr (if included as part of softcore.c)
*/
#if !defined(FICL_WANT_EXTENDED_PREFIX)
#endif /* FICL_WANT_EXTENDED_PREFIX */
/*
* FICL_WANT_USER
* Enables user variables: per-instance variables bound to the VM.
* Kind of like thread-local storage. Could be implemented in a
* VM private dictionary, but I've chosen the lower overhead
* approach of an array of CELLs instead.
*/
#if !defined(FICL_WANT_USER)
#endif /* FICL_WANT_USER */
/*
* FICL_WANT_LOCALS
* Controls the creation of the LOCALS wordset
* and a private dictionary for local variable compilation.
*/
#if !defined FICL_WANT_LOCALS
#endif /* FICL_WANT_LOCALS */
/*
* FICL_WANT_OOP
* Inludes object oriented programming support (in softwords)
* OOP support requires locals and user variables!
*/
#if !defined(FICL_WANT_OOP)
#endif /* FICL_WANT_OOP */
/*
* FICL_WANT_SOFTWORDS
* Controls inclusion of all softwords in softcore.c.
*/
#if !defined(FICL_WANT_SOFTWORDS)
#endif /* FICL_WANT_SOFTWORDS */
/*
* FICL_WANT_MULTITHREADED
* Enables dictionary mutual exclusion wia the
* ficlLockDictionary() system dependent function.
*
* Note: this implementation is experimental and poorly
* tested. Further, it's unnecessary unless you really
* intend to have multiple SESSIONS (poor choice of name
* on my part) - that is, threads that modify the dictionary
* at the same time.
*/
#if !defined FICL_WANT_MULTITHREADED
#define FICL_WANT_MULTITHREADED (0)
#endif /* FICL_WANT_MULTITHREADED */
/*
* FICL_WANT_OPTIMIZE
* Do you want to optimize for size, or for speed?
* Note that this doesn't affect Ficl very much one way
* or the other at the moment.
* Contributed by Larry Hastings
*/
#if !defined(FICL_WANT_OPTIMIZE)
#endif /* FICL_WANT_OPTIMIZE */
/*
* FICL_WANT_VCALL
* Ficl OO support for calling vtable methods. Win32 only.
* Contributed by Guy Carver
*/
#if !defined(FICL_WANT_VCALL)
#define FICL_WANT_VCALL (0)
#endif /* FICL_WANT_VCALL */
/*
* P L A T F O R M S E T T I N G S
*
* The FICL_PLATFORM_* settings.
* These indicate attributes about the local platform.
*/
/*
* FICL_PLATFORM_OS
* String constant describing the current hardware architecture.
*/
#if !defined(FICL_PLATFORM_ARCHITECTURE)
#endif
/*
* FICL_PLATFORM_OS
* String constant describing the current operating system.
*/
#if !defined(FICL_PLATFORM_OS)
#endif
/*
* FICL_PLATFORM_HAS_2INTEGER
* Indicates whether or not the current architecture
* supports a native double-width integer type.
* If you set this to 1 in your ficlplatform/ *.h file,
* you *must* create typedefs for the following two types:
* ficl2Unsigned
* ficl2Integer
* If this is set to 0, Ficl will implement double-width
* integer math in C, which is both bigger *and* slower
* (the double whammy!). Make sure your compiler really
* genuinely doesn't support native double-width integers
* before setting this to 0.
*/
#if !defined(FICL_PLATFORM_HAS_2INTEGER)
#define FICL_PLATFORM_HAS_2INTEGER (0)
#endif
/*
* FICL_PLATFORM_HAS_FTRUNCATE
* Indicates whether or not the current platform provides
* the ftruncate() function (available on most UNIXes).
* This function is necessary to provide the complete
* File-Access wordset.
*
* If your platform does not have ftruncate() per se,
* but does have some method of truncating files, you
* should be able to implement ftruncate() yourself and
* set this constant to 1. For an example of this see
* "ficlplatform/win32.c".
*/
#if !defined(FICL_PLATFORM_HAS_FTRUNCATE)
#define FICL_PLATFORM_HAS_FTRUNCATE (0)
#endif
/*
* FICL_PLATFORM_INLINE
* Must be defined, should be a function prototype type-modifying
* keyword that makes a function "inline". Ficl does not assume
* that the local platform supports inline functions; it therefore
* only uses "inline" where "static" would also work, and uses "static"
* in the absence of another keyword.
*/
#if !defined FICL_PLATFORM_INLINE
#define FICL_PLATFORM_INLINE inline
#endif /* !defined FICL_PLATFORM_INLINE */
/*
* FICL_PLATFORM_EXTERN
* Must be defined, should be a keyword used to declare
* a function prototype as being a genuine prototype.
* You should only have to fiddle with this setting if
* you're not using an ANSI-compliant compiler, in which
* case, good luck!
*/
#if !defined FICL_PLATFORM_EXTERN
#define FICL_PLATFORM_EXTERN extern
#endif /* !defined FICL_PLATFORM_EXTERN */
/*
* FICL_PLATFORM_BASIC_TYPES
*
* If not defined yet,
*/
#if !defined(FICL_PLATFORM_BASIC_TYPES)
typedef char ficlInteger8;
typedef unsigned char ficlUnsigned8;
typedef short ficlInteger16;
typedef unsigned short ficlUnsigned16;
typedef long ficlInteger32;
typedef unsigned long ficlUnsigned32;
typedef float ficlFloat;
#endif /* !defined(FICL_PLATFORM_BASIC_TYPES) */
/*
* FICL_ROBUST enables bounds checking of stacks and the dictionary.
* This will detect stack over and underflows and dictionary overflows.
* Any exceptional condition will result in an assertion failure.
* (As generated by the ANSI assert macro)
* FICL_ROBUST == 1 --> stack checking in the outer interpreter
* FICL_ROBUST == 2 also enables checking in many primitives
*/
#if !defined FICL_ROBUST
#endif /* FICL_ROBUST */
/*
* FICL_DEFAULT_STACK_SIZE Specifies the default size (in CELLs) of
* a new virtual machine's stacks, unless overridden at
* create time.
*/
#if !defined FICL_DEFAULT_STACK_SIZE
#endif
/*
* FICL_DEFAULT_DICTIONARY_SIZE specifies the number of ficlCells to allocate
* for the system dictionary by default. The value
* can be overridden at startup time as well.
*/
#if !defined FICL_DEFAULT_DICTIONARY_SIZE
#endif
/*
* FICL_DEFAULT_ENVIRONMENT_SIZE specifies the number of cells
* to allot for the environment-query dictionary.
*/
#if !defined FICL_DEFAULT_ENVIRONMENT_SIZE
#endif
/*
* FICL_MAX_WORDLISTS specifies the maximum number of wordlists in
* the dictionary search order. See Forth DPANS sec 16.3.3
* (file://dpans16.htm#16.3.3)
*/
#if !defined FICL_MAX_WORDLISTS
#endif
/*
* FICL_MAX_PARSE_STEPS controls the size of an array in the FICL_SYSTEM
* structure that stores pointers to parser extension functions. I would
* never expect to have more than 8 of these, so that's the default limit.
* Too many of these functions will probably exact a nasty performance penalty.
*/
#if !defined FICL_MAX_PARSE_STEPS
#endif
/*
* Maximum number of local variables per definition.
* This only affects the size of the locals dictionary,
* and there's only one per entire ficlSystem, so it
* doesn't make sense to be a piker here.
*/
#if (!defined(FICL_MAX_LOCALS)) && FICL_WANT_LOCALS
#endif
/*
* The pad is a small scratch area for text manipulation. ANS Forth
* requires it to hold at least 84 characters.
*/
#if !defined FICL_PAD_SIZE
#endif
/*
* ANS Forth requires that a word's name contain {1..31} characters.
*/
#if !defined FICL_NAME_LENGTH
#endif
/*
* Default size of hash table. For most uniform
* performance, use a prime number!
*/
#if !defined FICL_HASH_SIZE
#endif
/*
* Default number of USER flags.
*/
#if (!defined(FICL_USER_CELLS)) && FICL_WANT_USER
#endif
/*
* Forward declarations... read on.
*/
struct ficlWord;
struct ficlVm;
struct ficlDictionary;
struct ficlSystem;
struct ficlSystemInformation;
struct ficlCallback;
struct ficlCountedString;
struct ficlString;
/*
* System dependent routines:
* Edit the implementations in your appropriate ficlplatform/ *.c to be
* compatible with your runtime environment.
*
* ficlCallbackDefaultTextOut sends a zero-terminated string to the
* default output device - used for system error messages.
*
* ficlMalloc(), ficlRealloc() and ficlFree() have the same semantics
* as the functions malloc(), realloc(), and free() from the standard C library.
*/
char *text);
FICL_PLATFORM_EXTERN void ficlFree(void *p);
/*
* the Good Stuff starts here...
*/
#if !defined(FICL_PROMPT)
#endif
/*
* ANS Forth requires false to be zero, and true to be the ones
* complement of false... that unifies logical and bitwise operations
* nicely.
*/
#define FICL_TRUE ((unsigned long)~(0L))
#define FICL_FALSE (0)
#if !defined FICL_IGNORE /* Macro to silence unused param warnings */
#define FICL_IGNORE(x) (void)x
#endif /* !defined FICL_IGNORE */
#if !defined NULL
#define NULL ((void *)0)
#endif
/*
* 2integer structures
*/
FICL_BITS_PER_CELL) - 1)))
#define ficl2IntegerMultiply(x, y) \
(((ficl2Integer)(x)) * ((ficl2Integer)(y)))
#define ficl2UnsignedSubtract(x, y) \
(((ficl2Unsigned)(x)) - ((ficl2Unsigned)(y)))
#define ficl2UnsignedMultiply(x, y) \
(((ficl2Unsigned)(x)) * ((ficl2Unsigned)(y)))
#define ficl2UnsignedOr(x, y) ((x) | (y))
#else /* FICL_PLATFORM_HAS_2INTEGER */
typedef struct
{
typedef struct
{
} ficl2Integer;
FICL_2UNSIGNED_SET(0, u, doubleu)
ficlInteger y);
ficl2Unsigned y);
ficl2Unsigned y);
ficlUnsigned y);
ficl2Unsigned y);
#endif /* FICL_PLATFORM_HAS_2INTEGER */
/*
* These structures represent the result of division.
*/
typedef struct
{
typedef struct
{
(*(ficl2UnsignedQR *)(&(doubleiqr)))
(*(ficl2IntegerQR *)(&(doubleuqr)))
/*
* 64 bit integer math support routines: multiply two UNS32s
* to get a 64 bit product, & divide the product by an UNS32
* to get an UNS32 quotient and remainder. Much easier in asm
* on a 32 bit CPU than in C, which usually doesn't support
* the double length result (but it should).
*/
/*
* A ficlCell is the main storage type. It must be large enough
* to contain a pointer or a scalar. In order to accommodate
* 32 bit and 64 bit processors, use abstract types for int,
* unsigned, and float.
*
* A ficlUnsigned, ficlInteger, and ficlFloat *MUST* be the same
* size as a "void *" on the target system. (Sorry, but that's
* a design constraint of FORTH.)
*/
typedef union ficlCell
{
ficlInteger i;
ficlUnsigned u;
#if (FICL_WANT_FLOAT)
ficlFloat f;
#endif
void *p;
void (*fn)(void);
/*
* FICL_PLATFORM_ALIGNMENT is the number of bytes to which
* the dictionary pointer address must be aligned. This value
* is usually either 2 or 4, depending on the memory architecture
* of the target system; 4 is safe on any 16 or 32 bit
* machine. 8 would be appropriate for a 64 bit machine.
*/
#if !defined FICL_PLATFORM_ALIGNMENT
#endif
/*
* PTRtoCELL is a cast through void * intended to satisfy the
* most outrageously pedantic compiler... (I won't mention
* its name)
*/
/*
* FORTH defines the "counted string" data type. This is
* a "Pascal-style" string, where the first byte is an unsigned
* count of characters, followed by the characters themselves.
* The Ficl structure for this is ficlCountedString.
* Ficl also often zero-terminates them so that they work with the
* usual C runtime library string functions... strlen(), strcmp(),
* and the like. (Belt & suspenders? You decide.)
*
* The problem is, this limits strings to 255 characters, which
* can be a bit constricting to us wordy types. So FORTH only
* uses counted strings for backwards compatibility, and all new
* words are "c-addr u" style, where the address and length are
* stored separately, and the length is a full unsigned "cell" size.
* (For more on this trend, see DPANS94 section A.3.1.3.4.)
* Ficl represents this with the ficlString structure. Note that
* these are frequently *not* zero-terminated! Don't depend on
* it--that way lies madness.
*/
struct ficlCountedString
{
};
struct ficlString
{
char *text;
};
/*
* Init a FICL_STRING from a pointer to a zero-terminated string
*/
/*
* Ficl uses this little structure to hold the address of
* the block of text it's working on and an index to the next
* unconsumed character in the string. Traditionally, this is
* done by a Text Input Buffer, so I've called this struct TIB.
*
* Since this structure also holds the size of the input buffer,
* and since evaluate requires that, let's put the size here.
* The size is stored as an end-pointer because that is what the
* null-terminated string aware functions find most easy to deal
* with.
* Notice, though, that nobody really uses this except evaluate,
* so it might just be moved to ficlVm instead. (sobral)
*/
typedef struct
{
char *end;
char *text;
} ficlTIB;
/*
* Stacks get heavy use in Ficl and Forth...
* Each virtual machine implements two of them:
* one holds parameters (data), and the other holds return
* addresses and control flow information for the virtual
* machine. (Note: C's automatic stack is implicitly used,
* but not modeled because it doesn't need to be...)
* Here's an abstract type for a stack
*/
typedef struct ficlStack
{
} ficlStack;
/*
* Stack methods... many map closely to required Forth words.
*/
#if FICL_WANT_LOCALS
#endif /* FICL_WANT_LOCALS */
FICL_PLATFORM_EXTERN void
#if (FICL_WANT_FLOAT)
#endif
FICL_PLATFORM_EXTERN void
FICL_PLATFORM_EXTERN void
#if FICL_ROBUST >= 1
FICL_PLATFORM_EXTERN void
#else /* FICL_ROBUST >= 1 */
#endif /* FICL_ROBUST >= 1 */
FICL_PLATFORM_EXTERN void
/*
* Each VM has a placeholder for an output function -
* this makes it possible to have each VM do I/O
* through a different device. If you specify no
* ficlOutputFunction, it defaults to ficlCallbackDefaultTextOut.
*
* You can also set a specific handler just for errors.
* If you don't specify one, it defaults to using textOut.
*/
struct ficlCallback
{
void *context;
};
FICL_PLATFORM_EXTERN void
FICL_PLATFORM_EXTERN void
/*
* For backwards compatibility.
*/
typedef void
FICL_PLATFORM_EXTERN void
/*
* Starting with Ficl 4.0, Ficl uses a "switch-threaded" inner loop,
* where each primitive word is represented with a numeric constant,
* and words are (more or less) arrays of these constants. In Ficl
* these constants are an enumerated type called ficlInstruction.
*/
enum ficlInstruction
{
#include "ficltokens.h"
};
/*
* The virtual machine (VM) contains the state for one interpreter.
* Defined operations include:
* Create & initialize
* Delete
* Execute a block of text
* Parse a word out of the input stream
* Call return, and branch
* Text output
* Throw an exception
*/
struct ficlVm
{
/* address of currently running word (often just *(ip-1) ) */
#if FICL_WANT_FLOAT
#endif
#if FICL_WANT_USER
#endif
};
/*
* Each VM operates in one of two non-error states: interpreting
* or compiling. When interpreting, words are simply executed.
* When compiling, most words in the input stream have their
* addresses inserted into the word under construction. Some words
* (known as IMMEDIATE) are executed in the compile state, too.
*/
/* values of STATE */
#define FICL_VM_STATE_INTERPRET (0)
/*
* Exit codes for vmThrow
*/
/* tell ficlVmExecuteXT to exit inner loop */
/* hungry - normal exit */
/* word needs more text to succeed -- re-run it */
/* user wants to quit */
/* interpreter found an error */
/* debugger breakpoint */
/* like FICL_VM_STATUS_ERROR_EXIT -- abort */
/* like FICL_VM_STATUS_ERROR_EXIT -- abort" */
/* like FICL_VM_STATUS_ERROR_EXIT, but leave dataStack & base alone */
FICL_PLATFORM_EXTERN char *
FICL_PLATFORM_EXTERN void
FICL_PLATFORM_EXTERN void
#if FICL_WANT_FLOAT
#endif /* FICL_WANT_FLOAT */
/*
* f i c l E v a l u a t e
* Evaluates a block of input text in the context of the
* specified interpreter. Also sets SOURCE-ID properly.
*
* PLEASE USE THIS FUNCTION when throwing a hard-coded
* string to the Ficl interpreter.
*/
/*
* f i c l V m 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. If the input string is NULL
* terminated, you can pass -1 as nChars rather than count it.
* Execution returns when the text block has been executed,
* or an error occurs.
* Returns one of the FICL_VM_STATUS_... codes defined in ficl.h:
* FICL_VM_STATUS_OUT_OF_TEXT is the normal exit condition
* FICL_VM_STATUS_ERROR_EXIT means that the interpreter encountered a syntax
* error and the vm has been reset to recover (some or all
* of the text block got ignored
* FICL_VM_STATUS_USER_EXIT 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.
* FICL_VM_STATUS_ABORT and FICL_VM_STATUS_ABORTQ are generated by 'abort'
* and 'abort"' commands.
* Preconditions: successful execution of ficlInitSystem,
* Successful creation and init of the VM by ficlNewVM (or equivalent)
*
* If you call ficlExec() or one of its brothers, you MUST
* ensure vm->sourceId was set to a sensible value.
* ficlExec() explicitly DOES NOT manage SOURCE-ID for you.
*/
FICL_PLATFORM_EXTERN void
FICL_PLATFORM_EXTERN void
FICL_PLATFORM_EXTERN void
/*
* TIB access routines...
* ANS forth seems to require the input buffer to be represented
* as a pointer to the start of the buffer, and an index to the
* next character to read.
* PushTib points the VM to a new input string and optionally
* returns a copy of the current state
* PopTib restores the TIB state given a saved TIB from PushTib
* GetInBuf returns a pointer to the next unused char of the TIB
*/
FICL_PLATFORM_EXTERN void
#if FICL_ROBUST >= 1
FICL_PLATFORM_EXTERN void
FICL_PLATFORM_EXTERN void
#else
#endif /* FICL_ROBUST >= 1 */
/*
* A FICL_CODE points to a function that gets called to help execute
* a word in the dictionary. It always gets passed a pointer to the
* running virtual machine, and from there it can get the address
* of the parameter area of the word it's supposed to operate on.
* For precompiled words, the code is all there is. For user defined
* words, the code assumes that the word's parameter area is a list
* of pointers to the code fields of other words to execute, and
* may also contain inline data. The first parameter is always
* a pointer to a code field.
*/
/*
* Ficl models memory as a contiguous space divided into
* words in a linked list called the dictionary.
* A ficlWord starts each entry in the list.
* Version 1.02: space for the name characters is allotted from
* the dictionary ahead of the word struct, rather than using
* a fixed size array for each name.
*/
struct ficlWord
{
/* Immediate, Smudge, Compile-only, IsOjbect, Instruction */
};
/*
* ficlWord.flag bitfield values:
*/
/*
* FICL_WORD_IMMEDIATE:
* This word is always executed immediately when
* encountered, even when compiling.
*/
/*
* FICL_WORD_COMPILE_ONLY:
* This word is only valid during compilation.
* Ficl will throw a runtime error if this word executed
* while not compiling.
*/
/*
* FICL_WORD_SMUDGED
* This word's definition is in progress.
* The word is hidden from dictionary lookups
* until it is "un-smudged".
*/
/*
* FICL_WORD_OBJECT
* This word is an object or object member variable.
* (Currently only used by "my=[".)
*/
/*
* FICL_WORD_INSTRUCTION
* This word represents a ficlInstruction, not a normal word.
* param[0] is the instruction.
* When compiled, Ficl will simply copy over the instruction,
* rather than executing the word as normal.
*
* (Do *not* use this flag for words that need their PFA pushed
* before executing!)
*/
/*
* FICL_WORD_COMPILE_ONLY_IMMEDIATE
* Most words that are "immediate" are also
* "compile-only".
*/
#define FICL_WORD_COMPILE_ONLY_IMMEDIATE \
#define FICL_WORD_DEFAULT (0)
/*
* Worst-case size of a word header: FICL_NAME_LENGTH chars in name
*/
#define FICL_CELLS_PER_WORD \
/ (sizeof (ficlCell)))
#if FICL_ROBUST >= 1
FICL_PLATFORM_EXTERN void
#else
#endif /* FICL_ROBUST >= 1 */
/*
* Generally useful string manipulators omitted by ANSI C...
* ltoa complements strtol
*/
FICL_PLATFORM_EXTERN char *
FICL_PLATFORM_EXTERN char *
FICL_PLATFORM_EXTERN char *ficlStringCaseFold(char *s);
/*
* Ficl hash table - variable size.
* assert(size > 0)
* If size is 1, the table degenerates into a linked list.
* A WORDLIST (see the search order word set in DPANS) is
* just a pointer to a FICL_HASH in this implementation.
*/
typedef struct ficlHash
{
} ficlHash;
/*
* A Dictionary is a linked list of FICL_WORDs. It is also Ficl's
* memory model. Description of fields:
*
* here -- points to the next free byte in the dictionary. This
* pointer is forced to be CELL-aligned before a definition is added.
* Do not assume any specific alignment otherwise - Use dictAlign().
*
* smudge -- pointer to word currently being defined (or last defined word)
* If the definition completes successfully, the word will be
* linked into the hash table. If unsuccessful, dictUnsmudge
* uses this pointer to restore the previous state of the dictionary.
* Smudge prevents unintentional recursion as a side-effect: the
* dictionary search algo examines only completed definitions, so a
* word cannot invoke itself by name. See the Ficl word "recurse".
* NOTE: smudge always points to the last word defined. IMMEDIATE
* makes use of this fact. Smudge is initially NULL.
*
* forthWordlist -- pointer to the default wordlist (FICL_HASH).
* This is the initial compilation list, and contains all
* Ficl's precompiled words.
*
* compilationWordlist -- compilation wordlist - initially equal to
* forthWordlist wordlists -- array of pointers to wordlists.
* Managed as a stack.
* Highest index is the first list in the search order.
* wordlistCount -- number of lists in wordlists. wordlistCount-1 is the
* highest filled slot in wordlists, and points to the first wordlist
* in the search order
* size -- number of cells in the dictionary (total)
* base -- start of data area. Must be at the end of the struct.
*/
struct ficlDictionary
{
int wordlistCount;
};
FICL_PLATFORM_EXTERN void
FICL_PLATFORM_EXTERN void
FICL_PLATFORM_EXTERN void
FICL_PLATFORM_EXTERN void
FICL_PLATFORM_EXTERN void
FICL_PLATFORM_EXTERN void
FICL_PLATFORM_EXTERN void *
FICL_PLATFORM_EXTERN char *
#if FICL_WANT_FLOAT
#endif /* FICL_WANT_FLOAT */
char *value);
#if FICL_WANT_FLOAT
#endif /* FICL_WANT_FLOAT */
FICL_PLATFORM_EXTERN void
FICL_PLATFORM_EXTERN void
FICL_PLATFORM_EXTERN void
FICL_PLATFORM_EXTERN void
FICL_PLATFORM_EXTERN void
FICL_PLATFORM_EXTERN void
FICL_PLATFORM_EXTERN void
/*
* Stub function for dictionary access control - does nothing
* by default, user can redefine to guarantee exclusive dictionary
* access to a single thread for updates. All dictionary update code
* must be bracketed as follows:
* ficlLockDictionary(dictionary, FICL_TRUE); // any non-zero value will do
* <code that updates dictionary>
* ficlLockDictionary(dictionary, FICL_FALSE);
*
* Returns zero if successful, nonzero if unable to acquire lock
* before timeout (optional - could also block forever)
*
* NOTE: this function must be implemented with lock counting
* semantics: nested calls must behave properly.
*/
#if FICL_MULTITHREAD
#else
#endif
/*
* P A R S E S T E P
* (New for 2.05)
* See words.c: interpWord
* By default, Ficl goes through two attempts to parse each token from its
* input stream: it first attempts to match it with a word in the dictionary,
* and if that fails, it attempts to convert it into a number. This mechanism
* is now extensible by additional steps. This allows extensions like floating
* point and double number support to be factored cleanly.
*
* Each parse step is a function that receives the next input token as a
* STRINGINFO. If the parse step matches the token, it must apply semantics
* to the token appropriate to the present value of VM.state (compiling or
* interpreting), and return FICL_TRUE.
* Otherwise it returns FICL_FALSE. See words.c: isNumber for an example
*
* Note: for the sake of efficiency, it's a good idea both to limit the number
* of parse steps and to code each parse step so that it rejects tokens that
* do not match as quickly as possible.
*/
/*
* FICL_BREAKPOINT record.
* oldXT - if NULL, this breakpoint is unused. Otherwise it stores the xt
* that the breakpoint overwrote. This is restored to the dictionary when the
* BP executes or gets cleared
* address - the location of the breakpoint (address of the instruction that
* has been replaced with the breakpoint trap
* oldXT - The original contents of the location with the breakpoint
* Note: address is NULL when this breakpoint is empty
*/
typedef struct ficlBreakpoint
{
void *address;
/*
* F I C L _ S Y S T E M
* The top level data structure of the system - ficl_system ties a list of
* virtual machines with their corresponding dictionaries. Ficl 3.0 added
* support for multiple Ficl systems, allowing multiple concurrent sessions
* to separate dictionaries with some constraints.
* Note: the context pointer is there to provide context for applications.
* It is copied to each VM's context field as that VM is created.
*/
struct ficlSystemInformation
{
/* Initializes VM's context pointer - for application use */
void *context;
};
#define ficlSystemInformationInitialize(x) \
{ memset((x), 0, sizeof (ficlSystemInformation)); \
(x)->size = sizeof (ficlSystemInformation); }
struct ficlSystem
{
#if FICL_WANT_LOCALS
#endif
};
/*
* External interface to Ficl...
*/
/*
* f i c l S y s t e m C r e a t e
* Binds a global dictionary to the interpreter system and initializes
* the dictionary to contain the ANSI CORE wordset.
* You can specify the address and size of the allocated area.
* You can also specify the text output function at creation time.
* 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.
*/
/*
* f i c l S y s t e m D e s t r o y
* Deletes the system dictionary and all virtual machines that
* were created with ficlNewVM (see below). Call this function to
* reclaim all memory used by the dictionary and VMs.
*/
/*
* Create a new VM from the heap, and link it into the system VM list.
* Initializes the VM and binds default sized stacks to it. Returns the
* address of the VM, or NULL if an error occurs.
* Precondition: successful execution of ficlInitSystem
*/
/*
* Force deletion of a VM. You do not need to do this
* unless you're creating and discarding a lot of VMs.
* For systems that use a constant pool of VMs for the life
* of the system, ficltermSystem takes care of VM cleanup
* automatically.
*/
/*
* Returns the address of the most recently defined word in the system
* dictionary with the given name, or NULL if no match.
* Precondition: successful execution of ficlInitSystem
*/
/*
* f i c l G e t D i c t
* Utility function - returns the address of the system dictionary.
* Precondition: successful execution of ficlInitSystem
*/
#if FICL_WANT_LOCALS
#endif
/*
* f i c l C o m p i l e C o r e
* Builds the ANS CORE wordset into the dictionary - called by
* ficlInitSystem - no need to waste dictionary space by doing it again.
*/
#if FICL_WANT_FLOAT
#endif /* FICL_WANT_FLOAT */
#endif /* FICL_WANT_PLATFORM */
#if FICL_WANT_LOCALS
#endif
/*
* from words.c...
*/
#if FICL_WANT_LOCALS
#endif /* FICL_WANT_LOCALS */
/*
* 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. Each parse step is a word in
* the dictionary. Precompiled parse steps can use (PARSE-STEP) as their
* CFA - see parenParseStep in words.c.
*/
/*
* From tools.c
*/
/*
* The following supports SEE and the debugger.
*/
typedef enum
{
#if FICL_WANT_FLOAT
#endif /* FICL_WANT_FLOAT */
#if FICL_WANT_USER
#endif
} ficlWordKind;
#if FICL_WANT_FILE
/*
* Used with File-Access wordset.
*/
typedef struct ficlFile
{
FILE *f;
} ficlFile;
#if defined(FICL_PLATFORM_HAS_FTRUNCATE)
#endif
#endif
#ifdef __cplusplus
}
#endif
#endif /* _FICL_H */