Documentation

Ficl Documentation

Core reference, features, and API details.

What is ficl?

Ficl is a lightweight, embeddable scripting language designed to be incorporated into other programs, including memory constrained embedded systems. Ficl conforms to the 1994 ANSI Standard for Forth, and provides several useful extensions including OOP that can wrap compiled code and hardware interfaces. 

Unlike Lua or Python, Ficl acts as a component of your system: you feed it stuff to do, it does the stuff, and comes back to you for more. You can export compiled code to Ficl, execute Ficl code from your compiled code, or interact with a read-execute-print loop. Your choice. Ficl includes a simple but capable object model that can wrap existing data structures. 

Ficl vs. other Interpreters

Where language interpreters usually view themselves as the center of the system, Ficl acts as a component of the system. It is easy to export compiled code 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 languages (rapid development, quick code/test/debug cycle, reasonably fast) with the best features of C (everyone knows it, easier to support large blocks of code, efficient, type checking). In addition, Ficl provides a simple and powerful object model that can act as an object oriented adapter for code written in C/C++. 

Ficl Design goals

  • Scripting, prototyping, and extension language for systems written also in C/C++
  • Target 32 & 64 bit processors, including embedded systems with limited memory
  • Wrap functions and data structures written in C/C++
  • Conform to the Forth DPANS 94
  • Minimize porting effort - require an ANSI C runtime environment and minimal glue code
  • Provide Object Oriented extensions that can wrap system functions and structures

References: More information on Ficl

Forth and Threaded Interpretive Languages


LICENSE and DISCLAIMER

Copyright (c) 1997-2026 John Sadler, All rights reserved.

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 a ficl release, please contact me on Sourceforge. 

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.
3. Neither the name of the copyright holder nor the names of its contributors
   may be used to endorse or promote products derived from this software
   without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
 

Getting Started: Porting Ficl to your system

To install ficl on your target system, you need an ANSI C compiler (C11 or newer) and its runtime library. System dependent code is isolated to a few functions that you need to implement for your system. See sysdep.h for build controls. 

Edit the definitions (in sysdep.c) of ficlMalloc, ficlFree, ficlRealloc, and ficlTextOut for your application and target. Use testmain.c as a guide to installing the ficl system and one or more virtual machines into your code. 

Ficl includes portable double precision math routines that work for 32 and 64 bit machines. You may replace them with machine-dependent versions by #defining PORTABLE_LONGMULDIV to 0 in sysdep.h and implementing your versions of ficlLongMul and ficlLongDiv in sysdep.c.  

Build controls

The file sysdep.h contains default values for build controls. Most of these are written such that if you define them on the compiler command line, the defaults are overridden. I suggest you take the defaults on everything below the "build controls" section until you're confident of your port. Beware of declaring too small a dictionary. You can use unused and .dict to see how much space is left with a given configuration. 

Softwords

Many words from the supported wordsets are written in Forth and stored as a big string that Ficl compiles when it starts. The sources for all of these words are in directory ficl/softwords. There is a Python 3 script (softcore.py) that converts the .fr files into softcore.c. See the makefile in ficl/softwords. 

Application Programming Interface

The following is a partial listing of functions that interface your system or program to ficl. See the comments in ficl.c and ficl.h for additional information, and the example in file testmain.c.
FICL_SYSTEM *ficlInitSystemEx(FICL_SYSTEM_INFO *fsi)
Binds a global dictionary to the interpreter system and initializes the dict to contain the ANSI CORE wordset. You can specify the dictionary size, text output function, and an extension pointer via the FICL_SYSTEM_INFO structure. After initialization, ficl manages the dictionary. The dictionary needs to be at least large enough to hold the precompiled part. Try 5K cells to start. Use .dictto find out how much of the dictionary is used at any time.
FICL_SYSTEM *ficlInitSystem(int nDictCells)
Deprecated. Initializes Ficl's shared system data structures, and creates the dictionary allocating the specified number of CELLs from the heap (by a call to ficlMalloc). Use ficlInitSystemEx for new code.
void ficlTermSystem(FICL_SYSTEM *pSys)
Deletes the system dictionary and all virtual machines that were created with ficlNewVM. Call this function to reclaim all memory used by the dictionary and VMs. Any uses of the memory allocation words (allocate and resize) are your problem.
int ficlEvaluate(FICL_VM *pVM, const char *pText)
Evaluates a block of input text in the context of the specified interpreter. Also sets SOURCE-ID properly. Use this function when passing a hard-coded string to the Ficl interpreter.
int ficlExec(FICL_VM *pVM, const char *pText)
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 VM_XXXX codes defined in ficl.h: VM_OUTOFTEXT is the normal exit condition. VM_ERREXIT means the interpreter encountered a syntax error and the VM has been reset to recover. VM_USEREXIT means the user executed the "bye" command. VM_ABORT and VM_ABORTQ are generated by 'abort' and 'abort"'. ficlExec calls can be nested, and the function itself is re-entrant, but note that a VM is static, so you have to take reasonable precautions (for example, use one VM per thread in a multithreaded system if you want multiple threads to be able to execute commands). If you call ficlExec() or one of its brothers, you MUST ensure pVM->sourceID was set to a sensible value. ficlExec() explicitly DOES NOT manage SOURCE-ID for you.
int ficlExecC(FICL_VM *pVM, const char *pText, FICL_INT nChars)
Same as ficlExec, but takes a count indicating the length of the supplied string. Setting nChars to -1 is equivalent to ficlExec (expects '\0' termination).
int ficlExecXT(FICL_VM *pVM, FICL_WORD *pWord)
Same as ficlExec, but takes a pointer to a FICL_WORD instead of a string. Executes the word and returns after it has finished. If executing the word results in an exception, this function will re-throw the same code if it is nested under another ficlExec family function, or return the exception code directly if not. This function is useful if you need to execute the same word repeatedly - you save the dictionary search and outer interpreter overhead.
FICL_VM *ficlNewVM(FICL_SYSTEM *pSys)
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.
void ficlFreeVM(FICL_VM *pVM)
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.
void vmInterrupt(FICL_VM *pVM)
Interrupt a running VM from an external context such as a signal handler, hardware interrupt, watchdog timer ISR, or another thread. Causes the VM to longjmp back to the nearest exception recovery point with VM_INTERRUPT, which the caller of ficlExec (or ficlExecXT) will receive as a return code. This is how the console application in testmain.c handles Ctrl-C, for example. This is also the recommended way to break out of infinite loops or long-running computations (assuming they are coded in Ficl) in embedded systems: configure a watchdog orperiodic timer to call vmInterrupt on the active VM, and handle the VM_INTERRUPT return code in your outer loop. Safe to call from POSIX signal handlers on targets that use siglongjmp.
int ficlSetStackSize(int nStackCells)
Set the stack sizes (return and parameter) to be used for all subsequently created VMs. Returns actual stack size to be used.
FICL_WORD *ficlLookup(FICL_SYSTEM *pSys, const char *name)
Returns the address of the most recently defined word in the system dictionary with the given name, or NULL if no match. The address is also known as an XT and can be used in a call to ficlExecXT.
FICL_DICT *ficlGetDict(FICL_SYSTEM *pSys)
Returns a pointer to the main system dictionary, or NULL if the system is uninitialized.
FICL_HASH *ficlGetEnv(FICL_SYSTEM *pSys)
Returns a pointer to the environment wordlist (FICL_HASH). This wordlist is allocated within the main dictionary and stores implementation information as required by the Standard. The returned value is a wordlist identifier (wid) that can be added to the search order, enabling direct execution of environment constants.
void ficlSetEnv(FICL_SYSTEM *pSys, const char *name, FICL_UNS value)
Enters a new constant into the environment wordlist, with the specified name and value. The word is allocated in the main dictionary but inserted into the environment wordlist.
void ficlSetEnvD(FICL_SYSTEM *pSys, const char *name, FICL_UNS hi, FICL_UNS lo)
Enters a new double-cell constant into the environment wordlist with the specified name and value.
void ficlSetEnvF(FICL_SYSTEM *pSys, const char *name, FICL_FLOAT f)
Enters a new floating-point constant into the environment wordlist with the specified name and value. Only available when FICL_WANT_FLOAT is enabled.
FICL_DICT *ficlGetLoc(FICL_SYSTEM *pSys)
Returns a pointer to the locals dictionary. This function is defined only if FICL_WANT_LOCALS is #defined as non-zero (see sysdep.h). The locals dictionary is the symbol table for local variables.
int ficlBuild(FICL_SYSTEM *pSys, const char *name, FICL_CODE code, char flags)
Builds a word into the system default dictionary in a thread-safe way. Parameters: name -- the name of the word to be built; code -- code to execute when the word is invoked (must take a single pointer to a FICL_VM); flags -- 0 or more of FW_IMMEDIATE, FW_COMPILE, use bitwise OR (most words can use FW_DEFAULT).
void ficlCompileCore(FICL_SYSTEM *pSys)
Builds the ANS CORE wordset into the dictionary - called by ficlInitSystem. No need to call it again.
void ficlCompilePrefix(FICL_SYSTEM *pSys)
Builds the prefix parse step words into the dictionary.
void ficlCompileSearch(FICL_SYSTEM *pSys)
Builds the SEARCH-ORDER wordset into the dictionary.
void ficlCompileSoftCore(FICL_SYSTEM *pSys)
Defined in softcore.c, this function builds ANS required words and ficl extras by evaluating a text string. Python3 script softcore.py generates the string in softcore.c from .fr files in ficl/softcore.
void ficlCompileTools(FICL_SYSTEM *pSys)
Builds the TOOLS and TOOLS EXT wordsets into the dictionary.
void ficlCompileFile(FICL_SYSTEM *pSys)
Builds the File-Access wordset into the dictionary.
void ficlCompileFloat(FICL_SYSTEM *pSys)
Builds the optional FLOAT wordset into the dictionary. Only available when FICL_WANT_FLOAT is enabled.
void ficlCompilePlatform(FICL_SYSTEM *pSys)
Builds platform-specific extension words into the dictionary. Only available when FICL_PLATFORM_EXTEND is enabled.

Ficl Source Files

ficl.h Declares most public functions and all data structures. Includes sysdep.h and math.h
sysdep.h Declares system dependent functions and contains build control macros. Edit this file to suit your target.
dpmath.h Declares functions for double precision integer math. Sizes automatically to 128 bits on a 64 bit machine or 64 bits on a 32 bit machine
dict.c Dictionary
ficl.c System initialization, termination, and ficlExec
fileaccess.c Contains C implementations of the File-Access wordset. Conditioned on FICL_WANT_FILE.
float.c Adds precompiled definitions from the optional FLOAT word set. Ficl floats have a dedicated stack and are automatically sized to the target machine's register width, unless overridden by defining FICL_FLOAT_BITS to 32 or 64 explicitly. Most of the file is conditioned on FICL_WANT_FLOAT.
dpmath.c Double precision integer math support, including portable versions of ficlLongMul and ficlLongDiv
prefix.c The optional prefix parse step (conditioned on FICL_EXTENDED_PREFIX). This parse step handles numeric constructs like 0xa100, for example. See the release notes for more on parse steps.
search.c Contains C implementations of several of the SEARCH and SEARCH EXT words
softcore.c Contains all of the "soft" words - those written in Forth and compiled by Ficl at startup time. Sources for these words are in the softwords directory. The Python 3 script softwords/softcore.py generates softcore.c from the .fr sources.
stack.c Stack methods
sysdep.c Target dependent functions declared in sysdep.h. Edit this file to suit your target system.
testmain.c The main() function for console applications - use this as an example to integrate ficl into your system. Also runs unit tests and adds a few handy OS interface words.
tools.c Contains C implementations of TOOLS and TOOLS EXT words, the debugger, and debugger support words.
vm.c Virtual Machine methods
unix.c Platform extensions words loaded in ficl.c by ficlCompilePlatform() - conditioned on FICL_WANT_PLATFORM
wasm_main.c Entry points for the browser-based WebAssembly REPL. Provides ficlWasmInit, ficlWasmEval, and output-buffer functions for use from JavaScript.
words.c Exports ficlCompileCore(), the run-time dictionary builder, and contains most precompiled CORE and CORE-EXT words.
 
doc/ Project documentation including this reference, the interactive WebAssembly demo, and articles on Ficl internals, OOP, the prefix parser, local variables, and the debugger.
softwords/ Forth source files (.fr) for words compiled by Ficl at startup, including the object oriented extensions. A Python 3 script (softcore.py) converts the .fr files into softcore.c. See the makefile in this directory.
test/ Forth test scripts that exercise the ANS CORE, CORE EXT, and other wordsets, as well as Ficl-specific features like the OOP extensions and prefix parser.

Ficl Internals

Major Data Structures

A running memory image of Ficl consists of one or more FICL_SYSTEMs, each of which owns exactly one dictionary (FICL_DICT), and one or more virtual machines (FICL_VM). Each VM owns two stacks (FICL_STACK) - one for parameters (the parameter stack) and one for return addresses (the return stack). Ficl is a permissive, untyped language by nature, so its fundamental unit of storage is a CELL: a chunk of memory large enough to hold an address or a scalar type.

Ficl major data structures Diagram showing FICL_SYSTEM owning a dictionary and multiple virtual machines. Each VM owns parameter, return, and optional float stacks. The dictionary contains wordlists, FICL_WORD entries, and cell payloads. FICL_SYSTEM FICL_VM param stack return stack float stack (optional) FICL_DICT FICL_WORDLIST FICL_WORD owns owns owns contains

FICL_SYSTEM

The system structure associates one or more virtual machines with a dictionary. All FICL_SYSTEMS include a link pointer that is used to keep track of every allocated system so that memory can be freed by ficlTermSystem. Each system contains a list of virtual machines associated with it. Each system has at least one virtual machine. In a typical implementation, there is one virtual machine per native OS thread, and there may be several VMs sharing a single FICL_SYSTEM, or one FICL_SYSTEM per VM if the implementation needs to support multiple user sessions in a robust way. A FICL_SYSTEM also includes a special dictionary for local variable support (if enabled by FICL_WANT_LOCALS). Environment query constants are stored in a named wordlist within the main dictionary. This wordlist can be accessed via the environment-wordlist word and added to the search order for direct execution of environment constants.

FICL_DICT

A dictionary manages a fixed-size block of contiguous memory. It serves two roles: to keep track of allocated memory, and to collect symbol tables called wordlists. Each dictionary contains at least one wordlist. The dictionary organized memory (perhaps this is too kind) as an array of CELLs that grows from low memory to high memory within fixed limits determined by the FICL_DEFAULT_DICT parameter in sysdep.h. A wordlist is the controlling structure of a Ficl symbol table. Each wordlist is a hash table containing pointers to FICL_WORDs. Each FICL_WORD associates a pointer to code with one or more CELLs of the dictionay. Each word usually has a name as well, but this is not required. It is possible to create anonymous words using :NONAME. Each word's code pointer determines that word's runtime behavior, and by implication the purpose of its payload data. Some words interpret their payload as a list of Ficl words, and execute them. This is how new behaviors of the language are defined. Other words view their payload field as a location in which one or more CELLs can be stored (VARIABLEs, for example). At runtime, such words push the address of their payload area onto the parameter stack.

FICL_VM

The virtual machine collects state related to execution of Ficl words. Each VM includes registers used by the inner interpreter, some state variables (AKA user variables) such as the current numeric base, and a jmpbuf. A VM has a pointer to the FICL_SYSTEM of which it is a part. It also has a pointer to an incoming text string that it is interpreting. There are VM methods that excute a word given its address (xt), and ones that interpret a text string.

FICL_STACK

Each VM owns a parameter stack, a return stack, and if float support is enabled, a float parameter stack. Parameters, return addresses, and floats are all CELL sized, and values may be moved back and forth among stacks using various Ficl words for that purpose. 

Inner Interpreter: Example Execution

This example shows how the inner interpreter executes a simple colon definition with control flow. The word below computes a factorial using a counted loop and a running accumulator.

: fact ( n -- n! )
  1 swap 1+ 1 ?DO
    I *
  LOOP
;

Ficl stores colon definitions as a payload of execution tokens (XTs). In simplified form, the payload for fact looks like this:

XT (literal) 1
XT swap
XT (literal) 1
XT +
XT (?do) [exit address]
XT I
XT *
XT (loop) [branch address]
XT (;)

When C code calls ficlExecXT with the fact word, it sets up a nested exception frame, pushes the exit-inner sentinel on the IP stack, and then enters the inner loop. The inner loop walks the XT list until (;) triggers VM_INNEREXIT.

If an XT in the payload refers to a colon definition, the inner loop executes its code like any other word. The callee pushes the current instruction pointer, switches to its own payload, and returns to the caller when (;) pops the saved IP.

  • Setup Save the current pState and runningWord, install a new jmp_buf, and push pExitInner onto the IP stack.
  • Execute vmExecute positions the IP at the word's payload, then vmInnerLoop executes XTs in order.
  • Looping (?do) initializes the loop parameters, I fetches the loop index, and (loop) advances or exits the loop.
  • Exit (;) throws VM_INNEREXIT, the inner loop unwinds, and ficlExecXT restores the previous pState.

Ficl extras

Number syntax

You can precede a number with "0x", as in C, and it will be interpreted as a hex value regardless of the value of BASE. Likewise, numbers prefixed with "0d" will be interpreted as decimal values. Example:
ok> decimal 123 . cr
123
ok> 0x123 . cr
291
ok> 0x123 x. cr
123
Note: ficl2.05 and later - this behavior is controlled by the prefix parser defined in prefix.c. You can add other prefixes by defining handlers for them in ficl or C.

The SEARCH wordset and Ficl extensions

Ficl implements many of the search order words in terms of two primitives called >SEARCH and SEARCH>. As their names suggest (assuming you're familiar with Forth), they push and pop the search order stack.

The standard does not appear to specify any conditions under which the search order is reset to a sane state. Ficl resets the search order to its default state whenever ABORT happens. This includes stack underflows and overflows. QUIT does not affect the search order. The minimum search order (set by ONLY) is equivalent to

FORTH-WORDLIST 1 SET-ORDER

There is a default maximum of 16 wordlists in the search order. This can be changed by redefining FICL_DEFAULT_VOCS (declared in sysdep.h).

Note: Ficl resets the search order whenever it does ABORT. If you don't like this behavior, just comment out the dictResetSearchOrder() lines in ficlExec().

>search ( wid -- )
Push wid onto the search order. Many of the other search order words are written in terms of the SEARCH> and >SEARCH primitives. This word can be defined in ANS Forth as follows
: >search   >r get-order 1+ r> swap set-order ;
search>   ( -- wid )
Pop wid off the search order (can be coded in ANS Forth as : search>  get-order nip 1- set-order ; )
ficl-set-current   ( wid -- old-wid )
Set wid as compile wordlist, leaving the previous compile wordlist on the stack
ficl-vocabulary   ( nBins "name" -- )
Creates a ficl-wordlist with the specified number of hash table bins, binds it to the name, and associates the semantics of vocabulary with it (replaces the top wid in the search order list with its own wid when executed)
ficl-wordlist   ( nBins -- wid )
Creates a wordlist with the specified number of hash table bins, and leaves the address of the wordlist on the stack. A ficl-wordlist behaves exactly as a regular wordlist, but it may search faster depending on the number of bins chosen and the number of words it contains at search time. As implemented in ficl, a wordlist is single threaded by default. ficl-named-wordlist takes a name for the wordlist and creates a word that pushes the wid. This is by contrast to VOCABULARY, which also has a name, but replaces the top of the search order with its wid.
forget-wid   ( wid -- )
Iterates through the specified wordlist and unlinks all definitions whose xt addresses are greater than or equal to the value of HERE, the dictionary fill pointer. 
hide   ( -- current-wid-was )
Push the hidden wordlist onto the search order, and set it as the current compile wordlist (unsing ficl-set-current). Leaves the previous compile wordlist ID. I use this word to hide implementation factor words that have low reuse potential so that they don't clutter the default wordlist. To undo the effect of hide, execute  previous set-current
hidden   ( -- wid )
Wordlist for storing implementation factors of ficl provided words. To see what's in there, try:  hide words previous set-current
wid-get-name   ( wid -- c-addr u )
Ficl wordlists have a name property that can be assigned. This is used by ORDER to list the names of wordlists in the search order. 
wid-set-name   ( c-addr wid -- )
Ficl wordlists have a name property that can be assigned. This is used by ORDER to list the names of wordlists in the search order. The name is assumed to be a \0 terminated string (C style), which conveniently is how Ficl stores word names.  See softwords/softcore.fr definition of brand-wordlist 
wid-set-super   ( wid -- )
Ficl wordlists have a parent wordlist pointer that is not specified in standard Forth. Ficl initializes this pointer to NULL whenever it creates a wordlist, so it ordinarily has no effect. This word sets the parent pointer to the wordlist specified on the top of the stack. Ficl's implementation of SEARCH-WORDLIST will chain backward through the parent link of the wordlist when searching. This simplifies Ficl's object model in that the search order does not need to reflect an object's class hierarchy when searching for a method. It is possible to implement Ficl object syntax in strict ANS Forth, but method finders need to manipulate the search order explicitly.

User variables

user   ( -- ) name
Create a user variable with the given name. User variables are virtual machine local. Each VM allocates a fixed amount of storage for them. You can change the maximum number of user variables allowed by defining FICL_USER_CELLS on your compiiler's command line. Default is 16 user cells. User variables behave like VARIABLEs in all other respects (you use @ and ! on them, for example). Example:
user current-class
0 current-class !

Miscellaneous

-roll   ( xu xu-1 ... x0 u -- x0 xu-1 ... x1 ) 
Rotate u+1 items on top of the stack after removing u. Rotation is in the opposite sense to ROLL
-rot   ( a b c -- c a b )
Rotate the top three stack entries, moving the top of stack to third place. I like to think of this as 11/2swap because it's good for tucking a single cell value behind a cell-pair (like an object). 
.dict   ( -- )
Print dictionary usage statistics
.env   ( -- )
List all environment variables provided by the system
environment-wordlist   ( -- wid )
Returns the wordlist identifier for the environment query wordlist. This wordlist can be added to the search order (e.g. environment-wordlist >search) to allow direct execution of environment constants.
.hash   ( -- )
List hash table performance statistics of the wordlist that's first in the search order
.ver   ( -- )
Display ficl version ID
>buf   ( xt c-addr -- )
Redirect VM text output to the null-terminated buffer at c-addr, execute xt to completion, then restore the previous output handler and pExtend pointer. The buffer is cleared to an empty string before xt runs. The caller is responsible for ensuring the buffer is large enough to hold all text produced by xt. Example:
: greet ." Hello, World!" ;
create mybuf 256 allot
' greet mybuf >buf   ( capture greet's output into mybuf )
mybuf type cr         ( prints: Hello, World! )
>name   ( xt -- c-addr u )
Convert a word's execution token into the address and length of its name
body>   ( a-addr -- xt )
Reverses the effect of CORE word >body (converts a parameter field address to an execution token)
compile-only
Mark the most recently defined word as being executable only while in compile state. Many immediate words have this property.
empty   ( -- ) 
Empty the parameter stack
endif
Synonym for THEN
last-word   ( -- xt )
Pushes the xt address of the most recently defined word. This applies to colon definitions, constants, variables, and words that use create. You can print the name of the most recently defined word with 
last-word >name type 
parse-word   ( <spaces>name -- c-addr u )
Skip leading spaces and parse name delimited by a space. c-addr is the address within the input buffer and u is the length of the selected string. If the parse area is empty, the resulting string has a zero length. (From the Standard)
q@   ( addr -- x )
Fetch a 32 bit quantity from the specified address
q!   ( x addr -- )
Store a 32 bit quantity to the specified address 
w@   ( addr -- x )
Fetch a 16 bit quantity from the specified address
w!   ( x addr -- )
Store a 16 bit quantity to the specified address (the low 16 bits of the given value)
x.   ( x -- )
Pop and display the value in hex format, regardless of the current value of BASE

Extra words defined in testmain.c

break   ( -- )
Does nothing - just a handy place to set a debugger breakpoint
cd      ( "directory-name<newline>" -- )
Changes the working directory using the platform chdir() function (POSIX or Win32).
clock   ( -- now )
Returns the number of high-resolution timer ticks elapsed since process start (monotonic on POSIX/macOS, QPC on Windows).
clocks/sec   ( -- clocks_per_sec )
Pushes the number of ticks in a second for clock (1,000,000,000 on POSIX/macOS; QPC frequency on Windows).
load    ( "filename<newline>" -- )
Opens the Forth source file specified and loads it one line at a time, like INCLUDED (FILE)
pwd     ( -- )
Prints the current working directory as set by cd
system  ( "command<newline>" -- )
Issues a command to the platform shell using the standard C system() call.
spewhash   ( "filename<newline>" -- )
Dumps all threads of the current compilation wordlist to the specified text file. This was useful when I thought there might be some point in attempting to optimize the hash function. I no longer harbor those illusions.
test-error   ( -- )
Increments the internal test-failure counter. Called by ficltest.fr whenever a test assertion fails. Not intended for interactive use.
#errors   ( -- n )
Pushes the cumulative test-failure count (as incremented by test-error) onto the stack. Used by the test harness to report the total number of failures after a test run.

Local Variables

Named locally scoped variables came late to Forth. Purists feel that experienced Forth programmers ought to write supportable code using only anonymous stack variables and good factoring, but complain that novices use global variables too much. Local variables cost little in terms of code size and execution speed, and are very convenient for OO programming, where stack effects are more complex. I use them a lot (maybe I'm a weenie). 
Please refer to the Standard for more information on local variables.

Johns-Hopkins local variables

ANS Forth does not specify a complete and satisfying local variable facility. Instead it defines a foundation upon which to build one. Ficl comes with an adaptation of the Johns-Hopkins local variable syntax developed by John Hayes et al. This is my preferred form, and I've extended it to make OOP a bit simpler. Local variables can only be declared inside a definition, and are only visible in that definition. Here's the syntax of a JH local variable declaration:
{ <initialized-locals> | <cleared-locals> -- <ignored> }
The declaration is designed to look like a stack comment, but it uses curly braces instead of parens. The <initialized-locals> names get their initial values from the stack when the word executes. The <cleared-locals> names are (you guessed it) set to zero when the word executes, and any characters between -- and } are treated as a comment. The | and -- sections are optional, but they must appear in the order shown if they appear at all. 
Double cell locals (AKA 2locals): ordinarily, each local represents one cell. Local variable names prefixed with the characters "2:" in the declaration are double-cell locals. The prefix is not part of the local variable's name, only part of the declaration. They behave the same as single cell locals in all other respects. I use 2locals quite a bit in Ficl's OO classes, because objects in Ficl require two cells on the stack.
Float locals: when Ficl is compiled with FICL_WANT_FLOAT, local variable names prefixed with "f:" (or "F:") in the declaration are float locals. Like the 2: prefix, the f: prefix is not part of the variable's name. Float locals take their initial values from the float stack and push to the float stack when referenced. A float local occupies one or two cells in the locals frame depending on the size of FICL_FLOAT relative to CELL.
You can modify the value of any local with TO. TO determines whether it's operating on a LOCAL, a 2LOCAL, a float local, or a VALUE, and does the right thing accordingly.
Following are some examples to illustrate usage (they are not intended to be good code otherwise). Try these out in the demo to get a feeling for how they work. Also see softwords/string.fr for an example of use of locals in OO code.
: local-demo  { a b | c -- }
   ." a = " a . cr
   ." b = " b . cr
   ." c = " c . cr ;
1 2 local-demo  ( you should see 1 2 0 )

: my2dup  { 2:x }  x x ;  ( uses a 2local )
1 2 my2dup .s
.( you should see 1 2 1 2 on the stack ) cr empty

: my2swap  { 2:x 2:y -- y x }  y x ;  ( note use of 2locals )
1 2 3 4 my2swap .s
.( you should see 3 4 1 2 on the stack ) cr empty

: totally-lame-swap  { x y | temp -- y x }
   y to temp
   x to y
   temp to x
   x y ;

\ Using a float local (requires FICL_WANT_FLOAT)
: circle-area  { f:r -- f:area }
  r r f* 3.14159265e f* ;
The last two definitions introduce the use of TO applied to local variables.

Standard LOCALS and LOCALS EXT words

In addition to the Johns-Hopkins syntax, Ficl provides the standard LOCALS and LOCALS EXT wordsets. These produce the same compiled code as the JH syntax but only support single-cell locals. The JH syntax adds support for 2LOCALs and float locals.

Ficl implements both local variable syntaxes suggested in DPANS Appendix A.13. Examples:

\ Using LOCALS| from LOCALS EXT
: -rot  ( a b c -- c a b )
  locals| c b a |
  c a b
;
\ Using LOCAL END-LOCAL
: -rot  ( a b c -- c a b )
   local c
   local b
   local a
   end-locals
   c a b
;

Build Controls

Local variable support is optional because it adds a small amount of overhead to the outer interpreter. You can disable it by setting FICL_WANT_LOCALS to 0 in sysdep.h or in your compiler flags.

Notes:

  • Ficl's local variable syntax can make code quite a bit easier to read, so I encourage you to experiment with it.
  • Locals or not, keep your words simple. If the stack twiddles are too complex, consider factoring the definition into smaller and simpler words. 
  • Ficl's OOP code makes heavy use of local variables, so if you enable FICL_WANT_OOP, locals will also be enabled automatically.
  • The default maximum number of local variables per definition is 16. It's controlled by FICL_MAX_LOCALS in sysdep.h. 

Object Oriented Programming

Review of OO ideas

Click here for a short review of OO ideas, terms, and implementations in other languages.

Design goals of Ficl OO syntax

Ficl's object extensions provide the traditional OO benefits of associating data with the code that manipulates it, and reuse through single inheritance. Ficl also has some unusual capabilities that support interoperation with systems written in C. 
  • Ficl objects are normally late bound for safety (late binding guarantees that the appropriate method will always be invoked for a particular object). Early binding is also available, provided you know the object's class at compile-time.
  • Ficl OOP supports single inheritance, aggregation, and arrays of objects.
  • Classes have independent name spaces for their methods: methods are only visible in the context of a class or object. Methods can be overridden or added in subclasses; there is no fixed limit on the number of methods of a class or subclass.
  • Ficl OOP syntax is regular and unified over classes and objects. In ficl, all classes are objects. Class methods include the ability to subclass and instantiate.
  • Ficl can adapt legacy data structures with object wrappers. You can model a structure in a Ficl class, and create an instance that refers to an address in memory that holds an instance of the structure. The ref object can then manipulate the structure directly. This lets you wrap data structures written and instantiated in C.

Acknowledgements

Ficl's OO syntax builds on the work of John Hayes and Dick Pountain, among others. OO Ficl is different from other OO Forths in a few ways, though (some things never change). First, unlike several implementations, the syntax is documented (below) beyond the source code. In Ficl's spirit of working with C code, the OO syntax provides means to adapt existing data structures. I've tried to make Ficl's OO model simple and safe by unifying classes and objects, providing late binding by default, and separating namespaces so that methods and regular Forth words are not easily confused. 

 

Ficl Object Model

All classes in Ficl are derived from the common base class OBJECT, as shown in the figure below. All classes are instances of METACLASS. This means that classes are objects, too. METACLASS implements the methods for messages sent to classes. Class methods create instances and subclasses, and give information about the class. Each class is represented by a data stucture of three elements: 
  • The address (named .CLASS ) of a parent class, or zero if it's a base class (only OBJECT and METACLASS have this property)
  • The size (named .SIZE ) in address units of an instance of the class
  • A wordlist ID (named .WID ) for the methods of the class
In the figure below, METACLASS and OBJECT are real system-supplied classes. The others are contrived to illustrate the relationships among derived classes, instances, and the two system base classes. The dashed line with an arrow at the end indicates that the object/class at the arrow end is an instance of the class at the other end. The vertical line with a triangle denotes inheritance. 

Note for the curious: METACLASS behaves like a class - it responds to class messages and has the same properties as any other class. If you want to twist your brain in knots, you can think of METACLASS as an instance of itself. 
 

The Ficl Object Model diagram
 

Ficl OO Syntax Tutorial

Introduction

It's helpful to have some familiarity with Forth and the customary Forth stack notation to understand this tutorial. To get started, take a look at this web-based Forth tutorial. If you're comfortable with both OO and Forth, you can jump ahead.

A Ficl object associates a class with an instance (the storage for one set of instance variables). This is done explicitly on Ficl's stack, in that any Ficl object is represented by a cell pair: 

( instance-addr class-addr )
The instance-addr is the address of the object's storage, and the class-addr is the address of its class. Whenever a named Ficl object executes (eg. when you type its name and press enter at the Ficl prompt), it leaves this "signature". All methods by convention expect a class and instance on the stack when they execute, too. In many other OO languages, including C++, instances contain information about their classes (a vtable pointer, for example). By making this pairing explicit rather than implicit, Ficl can be OO about chunks of data that don't realize that they are objects, without sacrificing any robustness for native objects. That means that you can use Ficl to write object wrappers for data structures created in C or assembly language, as long as you can determine how they're laid out in memory. 
Whenever  you create an object in Ficl, you specify its class. After that, the object always pushes its class and the address of its payload (instance variable space) when invoked by name. 

Classes are special kinds of objects that store the methods of their instances, the size of an instance's payload, and a parent class pointer. Classes themselves are instances of a special base class called METACLASS, and all classes inherit from class OBJECT. This is confusing at first, but it means that Ficl has a very simple syntax for constructing and using objects. Class methods include subclassing (SUB), creating initialized and uninitialized instances (NEW and INSTANCE), and creating reference instances (REF), described later. Classes also have methods for disassembling their methods (SEE), identifying themselves (ID), and listing their pedigree (PEDIGREE). All objects inherit (from OBJECT) methods for initializing instances and arrays of instances, for performing array operations, and for getting information about themselves. 

Methods and messages

Methods are the functions that objects execute in response to messages. A message is a request to an object for a behavior that the object supports. When it receives a message, the target object looks up a method that performs the behavior for its class, and executes it. Any specific message may be bound to different methods in different objects, according to class. This separation of messages and methods allows objects to behave polymorphically. (In Ficl, methods are words defined in the context of a class, and messages are the names of those words.) Ficl classes associate messages with methods for their instances (a fancy way of saying that each class owns a wordlist). Ficl provides a late-binding operator --> that sends messages to objects at run-time, and an early-binding operator => that compiles a specific class's method. These operators are the only supported way to invoke methods. Regular Forth words are not visible to the method-binding operators,  so there's no chance of confusing a message with a regular word of the same name. 

Tutorial (finally!)

This is a tutorial. It works best if you follow along by pasting the examples into the web demo. If you're unfamiliar with Forth, please see one of these references. Ficl's OOP words are in vocabulary OOP. To put OOP in the search order and make it the compilation wordlist, type: 
ONLY   ( reset to default search order )
ALSO OOP DEFINITIONS
(Note for beginners: to see the effect of the commands above, type ORDER after each line. You can repeat the sequence above if you like.)

To start, we'll work with the two base classes OBJECT and METACLASS. Try this: 

metaclass --> methods
The line above contains three words. The first is the name of a class, so it pushes its signature on the stack. Since all classes are instances of METACLASS, METACLASS behaves as if it is an instance of itself (this is the only class with this property). It pushes the same address twice: once for the class and once for the payload, since they are the same. The next word finds a method in the context of a class and executes it. In this case, the name of the method is methods. Its job is to list all the methods that a class knows. What you get when you execute this line is a list of all the class methods Ficl provides. 
object --> sub c-led
c-byte obj: .state
: init   { 2:this -- }
    this --> super --> init
    ." initializing an instance of "
    this --> class --> id type cr ;
: on   { led# 2:this -- }
    this --> .state --> get
    1 led# lshift or dup !led
    this --> .state --> set  ;
: off   { led# 2:this -- }
    this --> .state --> get
    1 led# lshift invert and dup !led
    this --> .state --> set  ;
end-class
The first line causes base-class OBJECT to derive from itself a new class called c-led. If you type order and words you will see that class building words are now visible. Now we'll add some instance variables and methods to the new class... 

Note: I like to prefix the names of classes with "c-", and the names of member variables with a dot, but this is just a convention. If you don't like it, you can pick your own.

The second line adds an instance variable called .state to the class. This particular instance variable is an object - it will be an instance of c-byte, one of ficl's stock classes (the source for which can be found in the distribution in softwords/classes.fr). 
Next we've defined a method called init. This line also declares a local variable called this (the 2 in front tells Ficl that this is a double-cell local). All methods by convention expect the address of the class and instance on top of the stack when called.  The next three lines define init's behavior. It first calls its superclass's version of init (which in this case is object => init - this default implementation clears all instance variables). The rest displays some text and causes the instance to print its class name (this --> class --> id).
The init method is special for Ficl objects: whenever you create an initialized instance using new or new-array, Ficl calls the class's init method for you on that instance. The default init method supplied by object clears the instance, so we didn't really need to override it in this case (see the source code in ficl/softwords/oo.fr). 
The ON and OFF methods defined above hide the details of turning LEDs on and off. The interface to the demo's simulated hardware is handled by !led. The class keeps the LED state in a shadow variable (.STATE) so that ON and OFF can work in terms of LED number rather than a bitmask.

Now make an instance of the new class: 

c-led --> new led
And try a few things... 
led --> methods
led --> pedigree
1 led --> on
1 led --> off
Or you could type this with the same effect: 
led  2dup  --> methods  --> pedigree
Notice (from the output of methods) that we've overridden the init method supplied by object, and added two more methods for the member variables. If you type WORDS, you'll see that these methods are not visible outside the context of the class that contains them. The method finder --> uses the class to look up methods. You can use this word in a definition, as we did in init, and it performs late binding, meaning that the mapping from message (method name) to method (the code) is deferred until run-time. To see this, you can decompile the init method like this: 
c-led --> see init
or
led --> class --> see init

Early binding

Ficl also provides early binding if you ask for it. Early binding is not as safe as late binding, but it produces code that is more compact and efficient because it compiles method addresses rather then their names. In the preferred uses of early binding, the class is assumed to be the one you're defining. This kind of early binding can only be used inside a class definition. Early bound methods still expect to find a class and instance cell-pair on top of the stack when they run.
Here's an example that illustrates a potential problem:
object --> sub c1
: m1   { 2:this -- }  ." c1's m1" cr ;
: m2   { 2:this -- }  ." Running  " this  my=> m1 ; ( early )
: m3   { 2:this -- }  ." Running  " this --> m1     ( late )
end-class
c1     --> sub c2
: m1   { 2:this -- }  ." c2's m1" cr ;
end-class
c2 --> new i2
i2 --> m1   ( runs the m1 defined in c2 )
i2 --> m2   ( is this what you wanted? )
i2 --> m3   { runs the overridden m1)
Even though we overrode method m1 in class c2, the definition of m2 with early binding forced the use of m1 as defined in c1. If that's what you want, great, but more often you'll want the flexibility of overriding parent class behaviors appropriately. 
  1. my=> binds early to a method in the class being defined, as in the example above.
  2. my=[ ] binds a sequence of methods in the current class. Useful when the class has object members. Lines like this --> state --> set in the definition of c-led above can be replaced with this my=[ state set ] to get early binding.
  3. => (dangerous) pops a class off the stack and compiles the method in that class. Since you have to specify the class explicitly, there is a real danger that this will be out of sync with the class you really wanted. I recommend the my= operations.
Early binding using => is dangerous because it partially defeats the data-to-code matching mechanism object oriented languages were created to provide, but it does increase run-time speed by binding the method at compile time. In many cases, such as the init method, you can be reasonably certain of the class of thing you're working on. This is also true when invoking class methods, since all classes are instances of metaclass. Here's an example from the definition of metaclass in oo.fr:
: new   \ ( class metaclass "name" -- )
    metaclass => instance --> init ;
Try this...
metaclass --> see new
Decompiling the method with SEE shows the difference between the two strategies. The early bound method is compiled inline, while the late-binding operator compiles the method name and code to find and execute it in the context of whatever class is supplied on the stack at run-time.
Notice that the primitive early-binding operator => requires a class at compile time. For this reason, classes are IMMEDIATE, meaning that they push their signature at compile time or run time. I'd recommend that you avoid early binding until you're very comfortable with Forth, object-oriented programming, and Ficl's OOP syntax.

More About Instance Variables

Untyped instance variable methods (created by cell: cells: char: and chars:) just push the address of the corresponding instance variable when invoked on an instance of the class. It's up to you to remember the size of the instance variable and manipulate it with the usual Forth words for fetching and storing. 

As advertised earlier, Ficl provides ways to objectify existing data structures without changing them. Instead, you can create a Ficl class that models the structure, and instantiate a ref from this class, supplying the address of the structure. After that, the ref instance behaves as a Ficl object, but its instance variables take on the values in the existing structure. Example (from ficlclass.fr): 

object subclass c-wordlist
  c-wordlist  ref: .parent
  c-ptr       obj: .name
  c-cell      obj: .size
  c-word      ref: .hash

: ?
  2drop ." ficl wordlist" cr ;
: push  drop >search ;
: pop   2drop previous ;
: set-current drop set-current ;
: words  --> push  words previous ;
end-class
In this case, c-wordlist describes Ficl's wordlist structure; named-wid creates a wordlist and binds it to a ref instance of c-wordlist. The fancy footwork with POSTPONE and early binding is required because classes are immediate. An equivalent way to define named-wid with late binding is: 
: named-wid   ( "name" -- )
   wordlist  postpone c-wordlist --> ref ;
To do the same thing at run-time (and call it my-wordlist): 
wordlist  c-wordlist --> ref my-wordlist
Now you can deal with the wordlist through the ref instance: 
my-wordlist --> push
my-wordlist --> set-current
order
Ficl can also model linked lists and other structures that contain pointers to structures of the same or different types. The class constructor word ref: makes an aggregate reference to a particular class. See the instance variable glossary for an example

Ficl can make arrays of instances, and aggregate arrays into class descripions. The class methods array and new-array create uninitialized and initialized arrays, respectively, of a class. In order to initialize an array, the class must define (or inherit) a reasonable init method. New-array invokes it on each member of the array in sequence from lowest to highest. Array instances and array members use the object methods index, next, and prev to navigate. Aggregate a member array of objects using array:. The objects are not automatically initialized in this case - your class initializer has to call array-init explicitly if you want this behavior. 

For further examples of OOP in Ficl, please see the source file ficl/softwords/ficlclass.fr. This file wraps several Ficl internal data structures in objects and gives use examples. 

Ficl String classes

c-string (ficl 2.04 and later) is a reasonably useful dynamic string class. Source code for the class is located in ficl/softwords/string.fr. Features: dynamic creation and resizing; deletion, char cout, concatenation, output, comparison; creation from quoted string constant (s").

Examples of use:

c-string --> new homer
s" In this house, " homer --> set
s" we obey the laws of thermodynamics!" homer --> cat
homer --> type

OOP Glossary

Note: with the exception of the binding operators (the first two definitions here), all of the words in this section are internal factors that you don't need to worry about. These words provide method binding for all classes and instances. Also described are supporting words and execution factors. All are defined in softwords/oo.fr. 
--> ( instance class "method-name" -- xn )
Late binding: looks up and executes the given method in the context of the class on top of the stack. 
c-> ( instance class "method-name" -- xn exc )
Late binding with CATCH: looks up and CATCHes the given method in the context of the class on top of the stack, pushes zero or exception code upon return.
my=> comp: ( "method-name" -- ) exec: ( inst class -- xn )
Early binding: compiles code to execute the method of the class being defined. Only visible and valid in the scope of a --> sub .. end-class class definition.
my=[ comp: ( "obj1 obj2 .. method ]" -- ) exec:( inst class -- xn )
Early binding: compiles code to execute a chain of methods of the class being defined. Only visible and valid in the scope of a --> sub .. end-class class definition.
=> comp: ( class meta "method-name" -- ) exec: ( inst class -- xn )
Early binding: compiles code to execute the method of the class specified at compile time.
do-do-instance
When executed, causes the instance to push its ( instance class ) stack signature. Implementation factor of metaclass --> sub. Compiles .do-instance in the context of a class; .do-instance implements the does> part of a named instance. 
exec-method ( instance class c-addr u -- xn )
Given the address and length of a message (method name) on the stack, finds the method in the context of the specified class and invokes it. Upon entry to the method, the instance and class are on top of the stack, as usual. If unable to find the method, prints an error message and aborts.
find-method-xt ( class "method-name" -- class xt )
Attempts to map the message to a method in the specified class. If successful, leaves the class and the execution token of the method on the stack. Otherwise prints an error message and aborts.
lookup-method ( class c-addr u -- class xt )
Given the address and length of a message (method name) on the stack, finds the method in the context of the specified class. If unable to find the method, prints an error message and aborts.
parse-method comp: ( "method-name" -- ) exec: ( -- c-addr u )
Parse "name" from the input stream and compile code to push its length and address when the enclosing definition runs.

Instance Variable Glossary

Note: these words are only visible when creating a subclass! To create a subclass, use the sub method on object or any class derived from it (not metaclass). Source code for Ficl OOP is in ficl/softwords/oo.fr. 
Instance variable words do two things: they create methods that do an action appropriate for the type of instance variable they represent, and they reserve space in the class template for the instance variable. We'll use the term instance variable to refer both to the method that gives access to a particular field of an object, and to the field itself. Rather than give esentially the same example over and over, here's one example that shows several of the instance variable construction words in use:
object subclass c-example
  cell:         .cell0
  c-4byte  obj: .nCells
 4 c-4byte array: .quad
  char:         .length
79 chars:       .name
end-class
This class only defines instance variables, and it inherits some methods from object. Each untyped instance variable (.cell0, .length, .name) pushes its address when executed. Each object instance variable pushes the address and class of the aggregate object. Similar to C, an array instance variable leaves its base address (and its class) when executed. The word subclass is shorthand for "--> sub
cell: ( offset "name" -- offset' )
Execution: ( -- cell-addr )
Create an untyped instance variable one cell wide. The instance variable leaves its payload's address when executed. 
cells: ( offset nCells "name" -- offset' )
Execution: ( -- cell-addr )
Create an untyped instance variable n cells wide.
char: ( offset "name" -- offset' )
Execution: ( -- char-addr )
Create an untyped member variable one char wide
chars: ( offset nChars "name" -- offset' )
Execution: ( -- char-addr )
Create an untyped member variable n chars wide.
obj: ( offset class meta "name" -- offset' )
Execution: ( -- instance class )
Aggregate an uninitialized instance of class as a member variable of the class under construction.
array: ( offset n class meta "name" -- offset' )
Execution: ( -- instance class )
Aggregate an uninitialized array of instances of the class specified as a member variable of the class under construction.
ref: ( offset class meta "name" -- offset' )
Execution: ( -- ref-instance ref-class )
Aggregate a reference to a class instance. There is no way to set the value of an aggregated ref - it's meant as a way to manipulate existing data structures with a Ficl OO model. For example, if your system contains a linked list of 4 byte quantities, you can make a class that represents a list element like this: 
object subclass c-4list
c-4list ref: .link
c-4byte obj: .payload
end-class;
address-of-existing-list c-4list --> ref mylist
The last line binds the existing structure to an instance of the class we just created. The link method pushes the link value and the class c_4list, so that the link looks like an object to Ficl and like a struct to C (it doesn't carry any extra baggage for the object model - the Ficl methods alone take care of storing the class information). 
Note: Since a ref: aggregate can only support one class, it's good for modeling static structures, but not appropriate for polymorphism. If you want polymorphism, aggregate a c_ref (see classes.fr for source) into your class - it has methods to set and get an object.
By the way, it is also possible to construct a pair of classes that contain aggregate pointers to each other. Here's an example:
object subclass akbar
suspend-class    \ put akbar on hold while we define jeff
object subclass jeff
   akbar ref: .significant-other
   ( your additional methods here )
end-class        \ done with jeff
akbar --> resume-class  \ resume defining akbar
   jeff ref: .significant-other
   ( your additional methods here )
end-class        \ done with akbar

Class Methods Glossary

These words are methods of metaclass. They define the manipulations that can be performed on classes. Methods include various kinds of instantiation, programming tools, and access to member variables of classes. Source is in softwords/oo.fr. 
instance ( class metaclass "name" -- instance class ) 
Create an uninitialized instance of the class, giving it the name specified. The method leaves the instance 's signature on the stack (handy if you want to initialize). Example:
c_ref --> instance uninit-ref 2drop
new ( class metaclass "name" -- ) 
Create an initialized instance of class, giving it the name specified. This method calls init to perform initialization. 
array ( nObj class metaclass "name" -- nObjs instance class ) 
Create an array of nObj instances of the specified class. Instances are not initialized. Example:
10 c_4byte --> array 40-raw-bytes 2drop drop
new-array ( nObj class metaclass "name" -- ) 
Creates an initialized array of nObj instances of the class. Same syntax as array
alloc ( class metaclass -- instance class )
Creates an anonymous instance of class from the heap (using a call to ficlMalloc() to get the memory). Leaves the payload and class addresses on the stack. Usage example:
c-ref --> alloc 2constant instance-of-ref
Creates a double-cell constant that pushes the payload and class address of a heap instance of c-ref.
alloc-array ( nObj class metaclass -- instance class )
Same as new-array, but creates anonymous instances from the heap using a call to ficlMalloc(). Each instance is initialized using the class's init method
allot ( class metaclass -- instance class )
Creates an anonymous instance of class from the dictionary. Leaves the payload and class addresses on the stack. Usage example:
c-ref --> allot 2constant instance-of-ref
Creates a double-cell constant that pushes the payload and class address of a heap instance of c-ref.
allot-array ( nObj class metaclass -- instance class )
Same as new-array, but creates anonymous instances from the dictionary. Each instance is initialized using the class's init method
ref ( instance-addr class metaclass "name" -- ) 
Make a ref instance of the class that points to the supplied instance address. No new instance space is allotted. Instead, the instance refers to the address supplied on the stack forever afterward. For wrapping existing structures.
sub ( class metaclass -- old-wid addr[size] size )
Derive a subclass. You can add or override methods, and add instance variables. Alias: subclass. Examples:
c_4byte --> sub c_special4byte
( your new methods and instance variables here )
end-class
or
c_4byte subclass c_special4byte
( your new methods and instance variables here )
end-class
.size ( class metaclass -- instance-size ) 
Returns address of the class's instance size field, in address units. This is a metaclass member variable.
.super ( class metaclass -- superclass ) 
Returns address of the class's superclass field. This is a metaclass member variable.
.wid ( class metaclass -- wid ) 
Returns the address of the class's wordlist ID field. This is a metaclass member variable.
get-size
Returns the size of an instance of the class in address units. Imeplemented as
: get-size metaclass => .size @ ;
get-wid
Returns the wordlist ID of the class. Implemented as 
: get-wid metaclass => .wid @ ;
get-super
Returns the class's superclass. Implemented as
: get-super metaclass => .super @ ;
id ( class metaclass -- c-addr u ) 
Returns the address and length of a string that names the class.
methods ( class metaclass -- ) 
Lists methods of the class and all its superclasses
offset-of ( class metaclass "name" -- offset )
Pushes the offset from the instance base address of the named member variable. If the name is not that of an instance variable method, you get garbage. There is presently no way to detect this error. Example:
metaclass --> offset-of .wid
pedigree ( class metaclass -- ) 
Lists the pedigree of the class (inheritance trail)
see ( class metaclass "name" -- ) 
Decompiles the specified method - obect version of SEE, from the TOOLS wordset.

object base-class Methods Glossary

These are methods that are defined for all instances by the base class object. The methods include default initialization, array manipulations, aliases of class methods, upcasting, and programming tools. 
init ( instance class -- ) 
Default initializer called automatically for all instances created with new or new-array. Zero-fills the instance. You do not normally need to invoke init explicitly.
array-init ( nObj instance class -- ) 
Applies init to an array of objects created by new-array. Note that array: does not cause aggregate arrays to be initialized automatically. You do not normally need to invoke array-init explicitly.
free ( instance class -- )
Releases memory used by an instance previously created with alloc or alloc-array. Note - this method is not presently protected against accidentally deleting something from the dictionary. If you do this, Bad Things are likely to happen. Be careful for the moment to apply free only to instances created with alloc or alloc-array.
class ( instance class -- class metaclass ) 
Convert an object signature into that of its class. Useful for calling class methods that have no object aliases.
super ( instance class -- instance parent-class ) 
Upcast an object to its parent class. The parent class of object is zero. Useful for invoking an overridden parent class method.
pedigree ( instance class -- ) 
Display an object's pedigree - its chain of inheritance. This is an alias for the corresponding class method.
size ( instance class -- sizeof(instance) ) 
Returns the size, in address units, of one instance. Does not know about arrays! This is an alias for the class method get-size
methods ( instance class -- ) 
Class method alias. Displays the list of methods of the class and all superclasses of the instance.
index ( n instance class -- instance[n] class ) 
Convert array-of-objects base signature into signature for array element n. No check for bounds overflow. Index is zero-based, like C, so 
0 my-obj --> index 
is equivalent to 
my-obj
Check out the description of -ROT for help in dealing with indices on the stack.
next ( instance[n] class -- instance[n+1] class ) 
Convert an array-object signature  into the signature of the next object in the array. No check for bounds overflow.
prev ( instance[n] class -- instance[n-1] class ) 

Convert an object signature into the signature of the previous object in the array. No check for bounds underflow.

Supplied Classes (See classes.fr)

metaclass
Describes all classes of Ficl. Contains class methods. Should never be directly instantiated or subclassed. Defined in oo.fr. Methods described above.
object 
Mother of all Ficl objects. Defines default initialization and array indexing methods. Defined in oo.fr. Methods described above.
c-ref 
Holds the signature of another object. Aggregate one of these into a data structure or container class to get polymorphic behavior. Methods & members: 
get ( inst class -- ref-inst ref-class )
set ( ref-inst ref-class inst class -- )
.instance ( inst class -- a-addr ) cell member that holds the instance
.class ( inst class -- a-addr ) cell member that holds the class
c-byte
Primitive class derived from object, with a 1-byte payload. Set and get methods perform correct width fetch and store. Methods & members:
get ( inst class -- c )
set ( c inst class -- )
.payload ( inst class -- addr ) member holds instance's value
c-2byte 
Primitive class derived from object, with a 2-byte payload. Set and get methods perform correct width fetch and store. Methods & members:
get ( inst class -- 2byte )
set ( 2byte inst class -- )
.payload ( inst class -- addr ) member holds instance's value
c-4byte 
Primitive class derived from object, with a 4-byte payload. Set and get methods perform correct width fetch and store. Methods & members:
get ( inst class -- x )
set ( x inst class -- )
.payload ( inst class -- addr ) member holds instance's value
c-cell 
Primitive class derived from object, with a cell payload (equivalent to c-4byte in 32 bit implementations, 64 bits wide on Alpha). Set and get methods perform correct width fetch and store. Methods & members:
get ( inst class -- x )
set ( x inst class -- )
.payload ( inst class -- addr ) member holds instance's value
c-ptr
Base class derived from object for pointers to non-object types. This class is not complete by itself: several methods depend on a derived class definition of @size. Methods & members:
.addr ( inst class -- a-addr ) member variable - holds the pointer address
get-ptr ( inst class -- ptr )
set-ptr ( ptr inst class -- )
inc-ptr ( inst class -- ) Adds @size to pointer address
dec-ptr ( inst class -- ) Subtracts @size from pointer address
index-ptr ( i inst class -- ) Adds i*@size to pointer address
c-bytePtr
Pointer to byte derived from c-ptr. Methods & members:
@size ( inst class -- size ) Push size of the pointed-to thing
get ( inst class -- c ) Fetch the pointer's referent byte
set ( c inst class -- ) Store c at the pointer address
c-2bytePtr
Pointer to double byte derived from c-ptr. Methods & members:
@size ( inst class -- size ) Push size of the pointed-to thing
get ( inst class -- x ) Fetch the pointer's referent 2byte
set ( x inst class -- ) Store 2byte x at the pointer address
c-4bytePtr
Pointer to quad-byte derived from c-ptr. Methods & members:
@size ( inst class -- size ) Push size of the pointed-to thing
get ( inst class -- x ) Fetch the pointer's referent 2byte
set ( x inst class -- ) Store 2byte x at the pointer address
c-cellPtr
Pointer to cell derived from c-ptr. Methods & members:
@size ( inst class -- size ) Push size of the pointed-to thing
get ( inst class -- x ) Fetch the pointer's referent cell
set ( x inst class -- ) Storex at the pointer address
c-string  (see string.fr)
Dynamically allocated string similar to MFC CString (Partial list of methods follows)
set ( c-addr u 2:this -- ) Initialize buffer to the specified string
get ( 2:this -- c-addr u ) Return buffer contents as counted string
cat ( c-addr u 2:this -- ) Append given string to end of buffer
compare ( 2string 2:this -- n ) Return result of lexical compare
type ( 2:this -- ) Print buffer to the output stream
hashcode ( 2:this -- x ) Return hashcode of string (as in dictionary)
free ( 2:this -- ) Release internal buffer
c-hashstring  (see string.fr)
Derived from c-string. This class adds a hashcode member variable.

Parse Steps

Overview

Ficl 2.05 and later includes an extensible parser chain. Ficl feeds every incoming token (chunk of text with no internal whitespace) to each step in the parse chain in turn. The first parse step that successfully matches the token applies semantics to it and returns a TRUE flag, ending the sequence. If all parse steps fire without a match, ficl prints an error message and resets the virtual machine. Parse steps can be written in precompiled code, or in ficl itself, and can be appended to the chain at run-time if you like.

More detail:

  • If compiling and local variable support is enabled, attempt to find the token in the local variable dictionary. If found, execute the token's compilation semantics and return
  • Attempt to find the token in the system dictionary. If found, execute the token's semantics (may be different when compiling than when interpreting) and return
  • If prefix support is enabled (Compile-time constant FICL_WANT_PREFIX in sysdep.h is non-zero), attempt to match the beginning of the token to the list of known prefixes. If there's a match, execute the associated prefix method.
  • Attempt to convert the token to a number in the present BASE. If successful, push the value onto the stack if interpreting, compile it if compiling. Return
  • All previous parse steps failed to recognize the token. Print " not found" and abort
You can add steps to the parse chain, and you can add prefixes.

Adding Parse Steps

You can add a parse step in two ways. The first is to write a ficl word that has the correct stack signature for a parse step:
my-parse-step   ( c-addr u -- ??? flag )
Where c-addr u are the address and length of the incoming token, and flag is true if the parse step recognizes the token and false otherwise.
Install the parse step using add-parse-step. A trivial example:
: ?silly   ( c-addr u -- flag )
   ." Oh no! Not another  " type cr  true ;
' ?silly add-parse-step
parse-order

The other way to add a parse step is by writing it in C, and inserting it into the parse chain with:

void ficlAddPrecompiledParseStep(FICL_SYSTEM *pSys, char *name, FICL_PARSE_STEP pStep);
Where name is the display name of the parse step in the parse chain (as revealed by parse-order). Parameter pStep is a pointer to the code for the parse step itself, and must match the following declaration:
typedef int (*FICL_PARSE_STEP)(FICL_VM *pVM, STRINGINFO si);

Upon entry to the parse step, si points to the incoming token. The parse step must return FICL_TRUE if it succeeds in matching the token, and FICL_TRUE otherwise. If it succeeds in matching a token, the parse step applies semantics to it before returning. See ficlParseNumber() in words.c for an example.

Adding Prefixes

What's a prefix, anyway? A prefix (contributed by Larry Hastings) is a token that's recognized as the beginning of another token. Its presence modifies the semantics of the rest of the token. An example is 0x, which causes digits following it to be converted to hex regardless of the current value of BASE.

Caveat: Prefixes are matched in sequence, so the more of them there are, the slower the interpreter gets. On the other hand, because the prefix parse step occurs immediately after the dictionary lookup step, if you have a prefix for a particular purpose, using it may save time since it stops the parse process.

Each prefix is a ficl word stored in a special wordlist called <prefixes>. When the prefix parse step (?prefix AKA ficlParsePrefix()) fires, it searches each word in <prefixes> in turn, comparing it with the initial characters of the incoming token. If a prefix matches, the parse step returns the remainder of the token to the input stream and executes the code associated with the prefix. This code can be anything you like, but it would typically do something with the remainder of the token. If the prefix code does not consume the rest of the token, it will go through the parse process again (which may be what you want).

Prefixes are defined in prefix.c and in softwords/prefix.fr. The easiest way to add a new prefix is to insert it into prefix.fr and rebuild the system. You can also add prefixes interactively by bracketing prefix definitions as follows (see prefix,fr):

start-prefixes  ( defined in prefix.fr )
\ make dot-paren a prefix (create an alias for it in the prefixes list)
: .(  .( ;
: 0b  2 __tempbase ; immediate
end-prefixes

The precompiled word __tempbase is a helper for prefixes that specify a temporary value of BASE.

Constant FICL_EXTENDED_PREFIX controls the inclusion of a bunch of additional prefix definitions. This is turned off in the default build since several of these prefixes alter standard behavior, but you might like them.

Notes

Prefixes and parser extensions are non-standard, although with the exception of prefix support, ficl's default parse order follows the standard. Inserting parse steps in some other order will almost certainly break standard behavior.

The number of parse steps that can be added to the system is limited by the value of FICL_MAX_PARSE_STEPS (defined in sysdep.h unless you define it first), which defaults to 8. More parse steps means slower average interpret and compile performance, so be sparing. Same applies to the number of prefixes defined for the system, since each one has to be matched in turn before it can be proven that no prefix matches. On the other hand, if prefixes are defined, use them when possible: since they are matched early in the parse order, a prefix match short circuits the parse process, saving time relative to (for example) using a number builder parse step at the end of the parse chain.

Compile time constant FICL_EXTENDED_PREFIX enables several more prefix definitions in prefix.c and prefix.fr. Please note that this will slow average compile and interpret speed in most cases.

Parser Glossary

parse-order ( -- )
Prints the list of parse steps in the order in which they are evaluated. Each step is the name of a ficl word with the following signature:
parse-step   ( c-addr u -- ??? flag )
A parse step consumes a counted string (the incoming token) from the stack, and exits leaving a flag on top of the stack (it may also leave other parameters as side effects). The flag is true if the parse step succeeded at recognizing the token, false otherwise.
add-parse-step ( xt -- )
Appends a parse step to the parse chain. XT is the adress (execution token) of a ficl word to use as the parse step. The word must have the following signature:
parse-step   ( c-addr u -- ??? flag )
A parse step consumes a counted string (the incoming token) from the stack, and exits leaving a flag on top of the stack (it may also leave other parameters as side effects). The flag is true if the parse step succeeded at recognizing the token, false otherwise.
show-prefixes ( -- )
Defined in softwords/prefix.fr. Prints the list of all prefixes. Each prefix is a ficl word that is executed if its name is found at the beginning of a token. See softwords/prefix.fr and prefix.c for examples.
start-prefixes ( -- )
Defined in softwords/prefix.fr. Declares the beginning of one or more prefix definitions (it just switches the compile wordlist to <prefixes>
end-prefixes ( -- )
Defined in softwords/prefix.fr. Restores the compilation wordlist that was in effect before the last invocation of start-prefixes. Note: the prior wordlist ID is stored in a Ficl variable, so attempts to nest start-prefixes end-prefixes blocks wil result in mildly silly side effects.

Debugger

Ficl includes a simple step debugger for colon definitions and does> words. If you use it and can suggest improvements (or better yet if you write some), please let me know.

Using the debugger

To debug a word, set up the stack with any parameters the word requires, then type:
debug <your word here>

If the word is unnamed, or all you have is an xt, you can instead use:

debug-xt ( xt -- )

The debugger invokes see on the word, printing a crude source listing, then stops at the first instruction of the definition. There are four (case insensitive) commands you can use from here onwards:

I (step in)
If the next instruction is a colon defintion or does> word, steps into that word's code. If the word is a primitive, simply executes the word.
O (step over)
Executes the next instruction in its entirety
G (go)
Run the word to completion and exit the debugger
L (list)
Lists the source code of the word presently being stepped
Q (quit)
Abort the word and exit the debugger, clearing the stack
X (eXecute)
Interpret the remainder of the line as ficl words for their side effects. Any errors will abort the debug session and reset the VM. Usage example:
x drop 3 \ fix argument on stack
Anything else
Prints a list of available debugger commands

The on-step event

If there is a defined word named on-step when the debugger starts, that word will be executed before every step. As a guideline, this word should have no side effects. Its intended use is to display the stacks and any other VM state you're interested in, but you may have some better ideas. If so, please let me know. The default on-step is:

: on-step ." S: " .s cr ;

Other useful words for debugging and on-step

r.s ( -- )
Prints a represention of the state of the return stack non-destructively. You have to have a good understanding of the return stack side-effects of control words to make sense of it, but it does give an accurate representation of what's there. Example: DO .. LOOPs stack three parameters on the return stack: the loop count and limit, and the LEAVE target address.
.s ( -- )
Prints the parameter stack non-destructively
f.s ( -- )
Prints the float stack non-destructively (only available if FICL_WANT_FLOAT is enabled)

Debugger internals

The debugger words are mostly located in source file tools.c. There are supporting words (debug and on-step) in softcore.fr as well. There are two main words that make the debugger go: debug-xt and step-break. Debug-xt takes the xt of a word to debug (as returned by ', for example) checks to see if it is debuggable (not a primitive), sets a breakpoint at its first instruction, and runs see on it. To set a breakpoint, debug-xt replaces the instruction at the breakpoint with the xt of step-break, and stores the original instruction and its address in a static breakpoint record. To clear the breakpoint, step-break simply replaces the original instruction and adjusts the target virtual machine's instruction pointer to run it.

Step-break is responsible for processing debugger commands and setting breakpoints at subsequent instructions.

To Do

  • The debugger needs to exit automatically when it encounters the end of the word it was asked to debug. Perhaps this could be a special kind of breakpoint?
  • Add user-set breakpoints
  • Add "step out" command

ANS Required Information

  • ANS Forth System
  • Providing names from the Core Extensions word set 
  • Providing the Exception word set
  • Providing names from the Exception Extensions word set
  • Providing the Locals word set 
  • Providing the Locals Extensions word set 
  • Providing the Memory Allocation word set
  • Providing the Programming-Tools word set
  • Providing names from the Programming-Tools Extensions word set
  • Providing the Search-Order word set
  • Providing the Search-Order Extensions word set

Implementation-defined Options

The implementation-defined items in the following list represent characteristics and choices left to the discretion of the implementor, provided that the requirements of the Standard are met. A system shall document the values for, or behaviors of, each item. 
  • aligned address requirements (3.1.3.3 Addresses); 
    System dependent. You can change the default address alignment by defining FICL_ALIGN on your compiler's command line. The default value is set to 2 in sysdep.h. This causes dictionary entries and ALIGN and ALIGNED to align on 4 byte boundaries. To align on 2n byte boundaries, set FICL_ALIGN to n
  • behavior of 6.1.1320 EMIT for non-graphic characters
    Depends on target system, C runtime library, and your implementation of ficlTextOut(). 
  • character editing of 6.1.0695 ACCEPT and 6.2.1390 EXPECT
    None implemented in the versions supplied in words.c. Because ficlExec() is supplied a text buffer externally, it's up to your system to define how that buffer will be obtained.
  • character set (3.1.2 Character types, 6.1.1320 EMIT, 6.1.1750 KEY)
    Depends on target system and implementation of ficlTextOut().
  • character-aligned address requirements (3.1.3.3 Addresses)
    Ficl characters are one byte each. There are no alignment requirements.
  • character-set-extensions matching characteristics (3.4.2 Finding definition names)
    No special processing is performed on characters beyond case-folding. Therefore, extended characters will not match their unaccented counterparts.
  • conditions under which control characters match a space delimiter (3.4.1.1 Delimiters)
    Ficl uses the Standard C function isspace() to distinguish space characters. The rest is up to your library vendor. 
  • format of the control-flow stack (3.2.3.2 Control-flow stack)
    Uses the data stack
  • conversion of digits larger than thirty-five (3.2.1.2 Digit conversion)
    The maximum supported value of BASE is 36. Ficl will assertion fail in function ltoa of vm.c if the base is found to be larger than 36 or smaller than 2. There will be no effect if NDEBUG is defined, however, other than possibly unexpected behavior. 
  • display after input terminates in 6.1.0695 ACCEPT and 6.2.1390 EXPECT
    Target system dependent
  • exception abort sequence (as in 6.1.0680 ABORT")
    Does ABORT 
  • input line terminator (3.2.4.1 User input device); 
    Target system dependent (implementation of outer loop that calls ficlExec) 
  • maximum size of a counted string, in characters (3.1.3.4 Counted strings, 6.1.2450 WORD)
    255 
  • maximum size of a parsed string (3.4.1 Parsing)
    Limited by available memory and the maximum unsigned value that can fit in a CELL (232-1). 
  • maximum size of a definition name, in characters (3.3.1.2 Definition names)
    Ficl stores the first 31 characters of a definition name.
  • maximum string length for 6.1.1345 ENVIRONMENT?, in characters
    Same as maximum definition name length 
  • method of selecting 3.2.4.1 User input device
    None supported. This is up to the target system 
  • method of selecting 3.2.4.2 User output device
    None supported. This is up to the target system 
  • methods of dictionary compilation (3.3 The Forth dictionary)
  • number of bits in one address unit (3.1.3.3 Addresses)
    Target system dependent. Ficl usually supports processors that can address 8 bit quantities, but there is no dependency that I'm aware of. 
  • number representation and arithmetic (3.2.1.1 Internal number representation)
    System dependent. Ficl represents a CELL internally as a union that can hold INT32 (a signed 32 bit scalar value), UNS32 (32 bits unsigned), and an untyped pointer. No specific byte ordering is assumed. 
  • ranges for n, +n, u, d, +d, and ud (3.1.3 Single-cell types, 3.1.4 Cell-pair types)
    Assuming a 32 bit implementation, range for signed single-cell values is -231..231-1. Range for unsigned single cell values is 0..232-1. Range for signed double-cell values is -263..263-1. Range for unsigned single cell values is 0..264-1. 
  • read-only data-space regions (3.3.3 Data space);
    None 
  • size of buffer at 6.1.2450 WORD (3.3.3.6 Other transient regions)
    Default is 255. Depends on the setting of nPAD in ficl.h. 
  • size of one cell in address units (3.1.3 Single-cell types)
    System dependent, typically four for 32 bit targets, 8 for 64 bit targets.
  • size of one character in address units (3.1.2 Character types)
    System dependent, usually one.
  • size of the keyboard terminal input buffer (3.3.3.5 Input buffers)
    This buffer is supplied by the host program. Ficl imposes no practical limit. 
  • size of the pictured numeric output string buffer (3.3.3.6 Other transient regions)
    Default is 255 characters. Depends on the setting of nPAD in ficl.h. 
  • size of the scratch area whose address is returned by 6.2.2000 PAD (3.3.3.6 Other transient regions)
    Not presently supported 
  • system case-sensitivity characteristics (3.4.2 Finding definition names)
    Ficl is not case sensitive 
  • system prompt (3.4 The Forth text interpreter, 6.1.2050 QUIT)
    "ok>" 
  • type of division rounding (3.2.2.1 Integer division, 6.1.0100 */, 6.1.0110 */MOD, 6.1.0230 /, 6.1.0240 /MOD, 6.1.1890 MOD)
    Symmetric rounding (toward zero) 
  • values of 6.1.2250 STATE when true
    One (no others) 
  • values returned after arithmetic overflow (3.2.2.2 Other integer operations)
    System dependent. Ficl makes no special checks for overflow. 
  • whether the current definition can be found after 6.1.1250 DOES> (6.1.0450 :)
    No. Definitions are unsmudged after ; only, and only then if no control structure matching problems have been detected. 

Ambiguous Conditions

A system shall document the system action taken upon each of the general or specific ambiguous conditions identified in this Standard. See 3.4.4 Possible actions on an ambiguous condition. 

The following general ambiguous conditions could occur because of a combination of factors: 

  • a name is neither a valid definition name nor a valid number during text interpretation (3.4 The Forth text interpreter)
    Ficl does ABORT and prints the name followed by " not found". 
  • a definition name exceeded the maximum length allowed (3.3.1.2 Definition names)
    Ficl stores the first 31 characters of the definition name, and uses all characters of the name in computing its hash code. The actual length of the name, up to 255 characters, is stored in the definition's length field. 
  • addressing a region not listed in 3.3.3 Data Space
    No problem: all addresses in ficl are absolute. You can reach any address in Ficl's address space.
  • argument type incompatible with specified input parameter, e.g., passing a flag to a word expecting an n (3.1 Data types)
    Ficl makes no check for argument type compatibility. Effects of a mismatch vary widely depending on the specific problem and operands. 
  • attempting to obtain the execution token, (e.g., with 6.1.0070 ', 6.1.1550 FIND, etc.) of a definition with undefined interpretation semantics
    Ficl returns a valid token, but the result of executing that token while interpreting may be undesirable.
  • dividing by zero (6.1.0100 */, 6.1.0110 */MOD, 6.1.0230 /, 6.1.0240 /MOD, 6.1.1561 FM/MOD, 6.1.1890 MOD, 6.1.2214 SM/REM, 6.1.2370 UM/MOD, 8.6.1.1820 M*/);
    Results are target procesor dependent. Usually, Ficl makes no check for divide-by-zero. The target processor will throw an exception. 
  • insufficient data-stack space or return-stack space (stack overflow)
    With FICL_ROBUST (sysdep.h) set >= 2, most parameter stack operations are checked for underflow and overflow. Ficl does not check the return stack. 
  • insufficient space for loop-control parameters
    No check - Evil results. 
  • insufficient space in the dictionary
    Ficl generates an error message if the dictionary is too full to create a definition header. It checks ALLOT as well, but it is possible to make an unchecked allocation request that overflows the dictionary. 
  • interpreting a word with undefined interpretation semantics
    Ficl protects all ANS Forth words with undefined interpretation semantics from being executed while in interpret state. It is possible to defeat this protection using ' (tick) and EXECUTE, though. 
  • modifying the contents of the input buffer or a string literal (3.3.3.4 Text-literal regions, 3.3.3.5 Input buffers)
    Varies depending on the nature of the buffer. The input buffer is supplied by ficl's host function, and may reside in read-only memory. If so, writing the input buffer can ganerate an exception. String literals are stored in the dictionary, and are writable. 
  • overflow of a pictured numeric output string;
    In the unlikely event you are able to construct a pictured numeric string of more than 255 characters, the system will be corrupted unpredictably. The buffer area that holds pictured numeric output is at the end of the virtual machine. Whatever is mapped after the offending VM in memory will be trashed, along with the heap structures that contain it. 
  • parsed string overflow;
    Ficl does not copy parsed strings unless asked to. Ordinarily, a string parsed from the input buffer during normal interpretation is left in-place, so there is no possibility of overflow. If you ask to parse a string into the dictionary, as in SLITERAL, you need to have enough room for the string, otherwise bad things may happen. This is not usually a problem. 
  • producing a result out of range, e.g., multiplication (using *) results in a value too big to be represented by a single-cell integer (6.1.0090 *, 6.1.0100 */, 6.1.0110 */MOD, 6.1.0570 >NUMBER, 6.1.1561 FM/MOD, 6.1.2214 SM/REM, 6.1.2370 UM/MOD, 6.2.0970 CONVERT, 8.6.1.1820 M*/)
    Value will be truncated to fit in a single cell. 
  • reading from an empty data stack or return stack (stack underflow)
    Most stack underflows are detected and prevented if FICL_ROBUST (sysdep.h) is set to 2 or greater. Otherwise, the stack pointer and size are likely to be trashed. 
  • unexpected end of input buffer, resulting in an attempt to use a zero-length string as a name
    Ficl returns for a new input buffer until a non-empty one is supplied. 
The following specific ambiguous conditions are noted in the glossary entries of the relevant words: 
  • >IN greater than size of input buffer (3.4.1 Parsing)
    Bad Things occur - unpredictable bacause the input buffer is supplied by the host program's outer loop. 
  • 6.1.2120 RECURSE appears after 6.1.1250 DOES>
    It finds the address of the definition before DOES>
  • argument input source different than current input source for 6.2.2148 RESTORE-INPUT
    Not implemented 
  • data space containing definitions is de-allocated (3.3.3.2 Contiguous regions)
    This is OK until the cells are overwritten with something else. The dictionary maintains a hash table, and the table must be updated in order to de-allocate words without corruption. 
  • data space read/write with incorrect alignment (3.3.3.1 Address alignment)
    Target processor dependent. Consequences include: none (Intel), address error exception (68K). 
  • data-space pointer not properly aligned (6.1.0150 ,, 6.1.0860 C,)
    See above on data space read/write alignment 
  • less than u+2 stack items (6.2.2030 PICK, 6.2.2150 ROLL)
    Ficl detects a stack underflow and reports it, executing ABORT, as long as FICL_ROBUST is two or larger. 
  • loop-control parameters not available ( 6.1.0140 +LOOP, 6.1.1680 I, 6.1.1730 J, 6.1.1760 LEAVE, 6.1.1800 LOOP, 6.1.2380 UNLOOP)
    Loop initiation words are responsible for checking the stack and guaranteeing that the control parameters are pushed. Any underflows will be detected early if FICL_ROBUST is set to two or greater. Note however that Ficl only checks for return stack underflows at the end of each line of text. 
  • most recent definition does not have a name (6.1.1710 IMMEDIATE)
    No problem. 
  • name not defined by 6.2.2405 VALUE used by 6.2.2295 TO
    Ficl's version of TO works correctly with VALUEs, CONSTANTs and VARIABLEs. 
  • name not found (6.1.0070 ', 6.1.2033 POSTPONE, 6.1.2510 ['], 6.2.2530 [COMPILE])
    Ficl prints an error message and does ABORT
  • parameters are not of the same type (6.1.1240 DO, 6.2.0620 ?DO, 6.2.2440 WITHIN)
    No check. Results vary depending on the specific problem. 
  • 6.1.2033 POSTPONE or 6.2.2530 [COMPILE] applied to 6.2.2295 TO
    The word is postponed correctly. 
  • string longer than a counted string returned by 6.1.2450 WORD
    Ficl stores the first FICL_STRING_MAX-1 chars in the destination buffer. (The extra character is the trailing space required by the standard. Yuck.) 
  • u greater than or equal to the number of bits in a cell (6.1.1805 LSHIFT, 6.1.2162 RSHIFT)
    Depends on target process or and C runtime library implementations of the << and >> operators on unsigned values. For I386, the processor appears to shift modulo the number of bits in a cell. 
  • word not defined via 6.1.1000 CREATE (6.1.0550 >BODY, 6.1.1250 DOES>)
  • words improperly used outside 6.1.0490 <# and 6.1.0040 #> (6.1.0030 #, 6.1.0050 #S, 6.1.1670 HOLD, 6.1.2210 SIGN)
    Don't. CREATE reserves a field in words it builds for DOES>to fill in. If you use DOES> on a word not made by CREATE, it will overwrite the first cell of its parameter area. That's probably not what you want. Likewise, pictured numeric words assume that there is a string under construction in the VM's scratch buffer. If that's not the case, results may be unpleasant.

Locals Implementation-defined options

  • maximum number of locals in a definition (13.3.3 Processing locals, 13.6.2.1795 LOCALS|)
    Default is 16. Change by redefining FICL_MAX_LOCALS, defined in sysdep.h

Locals Ambiguous conditions

  • executing a named local while in interpretation state (13.6.1.0086 (LOCAL))
    Locals can be found in interpretation state while in the context of a definition under construction. Under these circumstances, locals behave correctly. Locals are not visible at all outside the scope of a definition. 
  • name not defined by VALUE or LOCAL (13.6.1.2295 TO)
    See the CORE ambiguous conditions, above (no change)

Programming Tools Implementation-defined options

  • source and format of display by 15.6.1.2194 SEE
    SEE de-compiles definitions from the dictionary. Because Ficl words are threaded by their header addresses, it is very straightforward to print the name and other characteristics of words in a definition. Primitives are so noted. Colon definitions are decompiled, but branch target labels are not reconstructed. Literals and string literals are so noted, and their contents displayed.

Search Order Implementation-defined options

  • maximum number of word lists in the search order (16.3.3 Finding definition names, 16.6.1.2197 SET-ORDER) 
    Defaults to 16. Can be changed by redefining FICL_DEFAULT_VOCS, declared in sysdep.h 
  • minimum search order (16.6.1.2197 SET-ORDER, 16.6.2.1965 ONLY) 
    Equivalent to FORTH-WORDLIST 1 SET-ORDER

Search Order Ambiguous conditions

  • changing the compilation word list (16.3.3 Finding definition names)
    Ficl stores a link to the current definition independently of the compile wordlist while it is being defined, and links it into the compile wordlist only after the definition completes successfully. Changing the compile wordlist mid-definition will cause the definition to link into the new compile wordlist. 
  • search order empty (16.6.2.2037 PREVIOUS)
    Ficl prints an error message if the search order underflows, and resets the order to its default state. 
  • too many word lists in search order (16.6.2.0715 ALSO)
    Ficl prints an error message if the search order overflows, and resets the order to its default state.