shithub: 9ficl

ref: 7c76dac1b268038f567939a70a18228e790a5cbc
dir: /dictionary.c/

View raw version
/*******************************************************************
** d i c t . c
** Forth Inspired Command Language - dictionary methods
** Author: John Sadler (john_sadler@alum.mit.edu)
** Created: 19 July 1997
** $Id: dictionary.c,v 1.6 2010/12/02 22:14:12 asau Exp $
*******************************************************************/
/*
** This file implements the dictionary -- Ficl's model of 
** memory management. All Ficl words are stored in the
** dictionary. A word is a named chunk of data with its
** associated code. Ficl treats all words the same, even
** precompiled ones, so your words become first-class
** extensions of the language. You can even define new 
** control structures.
**
** 29 jun 1998 (sadler) added variable sized hash table support
*/
/*
** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
** All rights reserved.
**
** Get the latest Ficl release at http://ficl.sourceforge.net
**
** I am interested in hearing from anyone who uses Ficl. If you have
** a problem, a success story, a defect, an enhancement request, or
** if you would like to contribute to the Ficl release, please
** contact me by email at the address above.
**
** L I C E N S E  and  D I S C L A I M E R
** 
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
**    notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
**    notice, this list of conditions and the following disclaimer in the
**    documentation and/or other materials provided with the distribution.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE.
*/

#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "ficl.h"

#define FICL_SAFE_CALLBACK_FROM_SYSTEM(system)  (((system) != NULL) ? &((system)->callback) : NULL)
#define FICL_SAFE_SYSTEM_FROM_DICTIONARY(dictionary)  (((dictionary) != NULL) ? (dictionary)->system : NULL)
#define FICL_DICTIONARY_ASSERT(dictionary, expression) FICL_SYSTEM_ASSERT(FICL_SAFE_SYSTEM_FROM_DICTIONARY(dictionary), expression)

/**************************************************************************
                        d i c t A b o r t D e f i n i t i o n
** Abort a definition in process: reclaim its memory and unlink it
** from the dictionary list. Assumes that there is a smudged 
** definition in process...otherwise does nothing.
** NOTE: this function is not smart enough to unlink a word that
** has been successfully defined (ie linked into a hash). It
** only works for defs in process. If the def has been unsmudged,
** nothing happens.
**************************************************************************/
void ficlDictionaryAbortDefinition(ficlDictionary *dictionary)
{
    ficlWord *word;
    ficlDictionaryLock(dictionary, FICL_TRUE);
    word = dictionary->smudge;

    if (word->flags & FICL_WORD_SMUDGED)
        dictionary->here = (ficlCell *)word->name;

    ficlDictionaryLock(dictionary, FICL_FALSE);
    return;
}


/**************************************************************************
                        d i c t A l i g n
** Align the dictionary's free space pointer
**************************************************************************/
void ficlDictionaryAlign(ficlDictionary *dictionary)
{
    dictionary->here = (ficlCell*)ficlAlignPointer(dictionary->here);
}


/**************************************************************************
                        d i c t A l l o t
** Allocate or remove n chars of dictionary space, with
** checks for underrun and overrun
**************************************************************************/
void ficlDictionaryAllot(ficlDictionary *dictionary, int n)
{
    char *here = (char *)dictionary->here;
    here += n;
    dictionary->here = FICL_POINTER_TO_CELL(here);
}


/**************************************************************************
                        d i c t A l l o t C e l l s
** Reserve space for the requested number of ficlCells in the
** dictionary. If nficlCells < 0 , removes space from the dictionary.
**************************************************************************/
void ficlDictionaryAllotCells(ficlDictionary *dictionary, int nficlCells)
{
    dictionary->here += nficlCells;
}


/**************************************************************************
                        d i c t A p p e n d C e l l
** Append the specified ficlCell to the dictionary
**************************************************************************/
void ficlDictionaryAppendCell(ficlDictionary *dictionary, ficlCell c)
{
    *dictionary->here++ = c;
    return;
}


/**************************************************************************
                        d i c t A p p e n d C h a r
** Append the specified char to the dictionary
**************************************************************************/
void ficlDictionaryAppendCharacter(ficlDictionary *dictionary, char c)
{
    char *here = (char *)dictionary->here;
    *here++ = c;
    dictionary->here = FICL_POINTER_TO_CELL(here);
    return;
}


/**************************************************************************
                        d i c t A p p e n d U N S
** Append the specified ficlUnsigned to the dictionary
**************************************************************************/
void ficlDictionaryAppendUnsigned(ficlDictionary *dictionary, ficlUnsigned u)
{
    *dictionary->here++ = FICL_LVALUE_TO_CELL(u);
    return;
}


void *ficlDictionaryAppendData(ficlDictionary *dictionary, void *data, ficlInteger length)
{
    char *here    = (char *)dictionary->here;
    char *oldHere = here;
	char *from    = (char *)data;
 
    if (length == 0)
    {
        ficlDictionaryAlign(dictionary);
        return (char *)dictionary->here;
    }

    while (length)
    {
        *here++ = *from++;
		length--;
    }

    *here++ = '\0';

    dictionary->here = FICL_POINTER_TO_CELL(here);
    ficlDictionaryAlign(dictionary);
    return oldHere;
}


/**************************************************************************
                        d i c t C o p y N a m e
** Copy up to FICL_NAME_LENGTH characters of the name specified by s into
** the dictionary starting at "here", then NULL-terminate the name,
** point "here" to the next available byte, and return the address of
** the beginning of the name. Used by dictAppendWord.
** N O T E S :
** 1. "here" is guaranteed to be aligned after this operation.
** 2. If the string has zero length, align and return "here"
**************************************************************************/
char *ficlDictionaryAppendString(ficlDictionary *dictionary, ficlString s)
{
	void *data = FICL_STRING_GET_POINTER(s);
	ficlInteger length = FICL_STRING_GET_LENGTH(s);

    if (length > FICL_NAME_LENGTH)
        length = FICL_NAME_LENGTH;
    
	return (char*)ficlDictionaryAppendData(dictionary, data, length);
}


ficlWord *ficlDictionaryAppendConstantInstruction(ficlDictionary *dictionary, ficlString name, ficlInstruction instruction, ficlInteger value)
{
	ficlWord *word = ficlDictionaryAppendWord(dictionary, name, (ficlPrimitive)instruction, FICL_WORD_DEFAULT);
	if (word != NULL)
		ficlDictionaryAppendUnsigned(dictionary, value);
	return word;
}


ficlWord *ficlDictionaryAppend2ConstantInstruction(ficlDictionary *dictionary, ficlString name, ficlInstruction instruction, ficl2Integer value)
{
	ficlWord *word = ficlDictionaryAppendWord(dictionary, name, (ficlPrimitive)instruction, FICL_WORD_DEFAULT);
	if (word != NULL)
	{
	    ficlDictionaryAppendUnsigned(dictionary, FICL_2UNSIGNED_GET_HIGH(value));
		ficlDictionaryAppendUnsigned(dictionary, FICL_2UNSIGNED_GET_LOW(value));
	}
	return word;
}



ficlWord *ficlDictionaryAppendConstant(ficlDictionary *dictionary, char *name, ficlInteger value)
{
    ficlString s;
	FICL_STRING_SET_FROM_CSTRING(s, name);
	return ficlDictionaryAppendConstantInstruction(dictionary, s, ficlInstructionConstantParen, value);
}



ficlWord *ficlDictionaryAppend2Constant(ficlDictionary *dictionary, char *name, ficl2Integer value)
{
    ficlString s;
	FICL_STRING_SET_FROM_CSTRING(s, name);
	return ficlDictionaryAppend2ConstantInstruction(dictionary, s, ficlInstruction2ConstantParen, value);
}



ficlWord *ficlDictionarySetConstantInstruction(ficlDictionary *dictionary, ficlString name, ficlInstruction instruction, ficlInteger value)
{
    ficlWord *word = ficlDictionaryLookup(dictionary, name);

    if (word == NULL)
    {
        word = ficlDictionaryAppendConstantInstruction(dictionary, name, instruction, value);
    }
    else
    {
		word->code = (ficlPrimitive)instruction;
        word->param[0] = FICL_LVALUE_TO_CELL(value);
    }
    return word;
}

ficlWord *ficlDictionarySetConstant(ficlDictionary *dictionary, char *name, ficlInteger value)
{
    ficlString s;
    FICL_STRING_SET_FROM_CSTRING(s, name);
    return ficlDictionarySetConstantInstruction(dictionary, s, ficlInstructionConstantParen, value);
}

ficlWord *ficlDictionarySet2ConstantInstruction(ficlDictionary *dictionary, ficlString s, ficlInstruction instruction, ficl2Integer value)
{
    ficlWord *word;
    word = ficlDictionaryLookup(dictionary, s);

	/* only reuse the existing word if we're sure it has space for a 2constant */
    if ((word != NULL) &&
		((((ficlInstruction)(uintptr_t)word->code) == ficlInstruction2ConstantParen)
#if FICL_WANT_FLOAT
		  ||
		(((ficlInstruction)(uintptr_t)word->code) == ficlInstructionF2ConstantParen)
#endif /* FICL_WANT_FLOAT */
		)
		)
    {
		word->code = (ficlPrimitive)instruction;
        word->param[0].u = FICL_2UNSIGNED_GET_HIGH(value);
        word->param[1].u = FICL_2UNSIGNED_GET_LOW(value);
    }
    else
    {
        word = ficlDictionaryAppend2ConstantInstruction(dictionary, s, instruction, value);
    }

    return word;
}


ficlWord *ficlDictionarySet2Constant(ficlDictionary *dictionary, char *name, ficl2Integer value)
{
    ficlString s;
    FICL_STRING_SET_FROM_CSTRING(s, name);
    return ficlDictionarySet2ConstantInstruction(dictionary, s, ficlInstruction2ConstantParen, value);
}


ficlWord *ficlDictionarySetConstantString(ficlDictionary *dictionary, char *name, char *value)
{
    ficlString s;
    ficl2Integer valueAs2Integer;
    FICL_2INTEGER_SET(strlen(value), (intptr_t)value, valueAs2Integer);
    FICL_STRING_SET_FROM_CSTRING(s, name);

	return ficlDictionarySet2ConstantInstruction(dictionary, s, ficlInstruction2ConstantParen, valueAs2Integer);
}



/**************************************************************************
                        d i c t A p p e n d W o r d
** Create a new word in the dictionary with the specified
** ficlString, code, and flags. Does not require a NULL-terminated
** name.
**************************************************************************/
ficlWord *ficlDictionaryAppendWord(ficlDictionary *dictionary, 
                           ficlString name, 
                           ficlPrimitive code,
                           ficlUnsigned8 flags)
{
    ficlUnsigned8 length  = (ficlUnsigned8)FICL_STRING_GET_LENGTH(name);
    char *nameCopy;
    ficlWord *word;

    ficlDictionaryLock(dictionary, FICL_TRUE);

    /*
    ** NOTE: ficlDictionaryAppendString advances "here" as a side-effect.
    ** It must execute before word is initialized.
    */
    nameCopy       = ficlDictionaryAppendString(dictionary, name);
    word           = (ficlWord *)dictionary->here;
    dictionary->smudge = word;
    word->hash     = ficlHashCode(name);
    word->code     = code;
	word->semiParen = ficlInstructionSemiParen;
    word->flags    = (ficlUnsigned8)(flags | FICL_WORD_SMUDGED);
    word->length    = length;
    word->name     = nameCopy;
    /*
    ** Point "here" to first ficlCell of new word's param area...
    */
    dictionary->here   = word->param;

    if (!(flags & FICL_WORD_SMUDGED))
        ficlDictionaryUnsmudge(dictionary);

    ficlDictionaryLock(dictionary, FICL_FALSE);
    return word;
}


/**************************************************************************
                        d i c t A p p e n d W o r d
** Create a new word in the dictionary with the specified
** name, code, and flags. Name must be NULL-terminated.
**************************************************************************/
ficlWord *ficlDictionaryAppendPrimitive(ficlDictionary *dictionary, 
                          char *name, 
                          ficlPrimitive code,
                          ficlUnsigned8 flags)
{
    ficlString s;
	FICL_STRING_SET_FROM_CSTRING(s, name);
    return ficlDictionaryAppendWord(dictionary, s, code, flags);
}


ficlWord *ficlDictionarySetPrimitive(ficlDictionary *dictionary, 
                          char *name, 
                          ficlPrimitive code,
                          ficlUnsigned8 flags)
{
    ficlString s;
    ficlWord *word;

	FICL_STRING_SET_FROM_CSTRING(s, name);
    word = ficlDictionaryLookup(dictionary, s);

    if (word == NULL)
    {
        word = ficlDictionaryAppendPrimitive(dictionary, name, code, flags);
    }
    else
    {
		word->code = (ficlPrimitive)code;
		word->flags = flags;
    }
	return word;
}


ficlWord *ficlDictionaryAppendInstruction(ficlDictionary *dictionary, 
                          char *name, 
                          ficlInstruction i,
						  ficlUnsigned8 flags)
{
    return ficlDictionaryAppendPrimitive(dictionary, name, (ficlPrimitive)i, (ficlUnsigned8)(FICL_WORD_INSTRUCTION | flags));
}

ficlWord *ficlDictionarySetInstruction(ficlDictionary *dictionary, 
                          char *name, 
                          ficlInstruction i,
                          ficlUnsigned8 flags)
{
    return ficlDictionarySetPrimitive(dictionary, name, (ficlPrimitive)i, (ficlUnsigned8)(FICL_WORD_INSTRUCTION | flags));
}


/**************************************************************************
                        d i c t C e l l s A v a i l
** Returns the number of empty ficlCells left in the dictionary
**************************************************************************/
int ficlDictionaryCellsAvailable(ficlDictionary *dictionary)
{
    return dictionary->size - ficlDictionaryCellsUsed(dictionary);
}


/**************************************************************************
                        d i c t C e l l s U s e d
** Returns the number of ficlCells consumed in the dicionary
**************************************************************************/
int ficlDictionaryCellsUsed(ficlDictionary *dictionary)
{
    return dictionary->here - dictionary->base;
}



/**************************************************************************
                        d i c t C r e a t e
** Create and initialize a dictionary with the specified number
** of ficlCells capacity, and no hashing (hash size == 1).
**************************************************************************/
ficlDictionary  *ficlDictionaryCreate(ficlSystem *system, unsigned size)
{
    return ficlDictionaryCreateHashed(system, size, 1);
}


ficlDictionary  *ficlDictionaryCreateHashed(ficlSystem *system, unsigned size, unsigned bucketCount)
{
    ficlDictionary *dictionary;
    size_t nAlloc;

    nAlloc =  sizeof(ficlDictionary) + (size * sizeof (ficlCell))
            + sizeof(ficlHash) + (bucketCount - 1) * sizeof (ficlWord *);

    dictionary = (ficlDictionary*)ficlMalloc(nAlloc);
    FICL_SYSTEM_ASSERT(system, dictionary != NULL);

    dictionary->size = size;
	dictionary->system = system;

    ficlDictionaryEmpty(dictionary, bucketCount);
    return dictionary;
}


/**************************************************************************
                        d i c t C r e a t e W o r d l i s t
** Create and initialize an anonymous wordlist
**************************************************************************/
ficlHash *ficlDictionaryCreateWordlist(ficlDictionary *dictionary, int bucketCount)
{
    ficlHash *hash;
    
    ficlDictionaryAlign(dictionary);
    hash    = (ficlHash *)dictionary->here;
    ficlDictionaryAllot(dictionary, sizeof (ficlHash) 
        + (bucketCount - 1) * sizeof (ficlWord *));

    hash->size = bucketCount;
    ficlHashReset(hash);
    return hash;
}


/**************************************************************************
                        d i c t D e l e t e 
** Free all memory allocated for the given dictionary 
**************************************************************************/
void ficlDictionaryDestroy(ficlDictionary *dictionary)
{
    FICL_DICTIONARY_ASSERT(dictionary, dictionary != NULL);
    ficlFree(dictionary);
    return;
}


/**************************************************************************
                        d i c t E m p t y
** Empty the dictionary, reset its hash table, and reset its search order.
** Clears and (re-)creates the hash table with the size specified by nHash.
**************************************************************************/
void ficlDictionaryEmpty(ficlDictionary *dictionary, unsigned bucketCount)
{
    ficlHash *hash;

    dictionary->here = dictionary->base;

    ficlDictionaryAlign(dictionary);
    hash = (ficlHash *)dictionary->here;
    ficlDictionaryAllot(dictionary, 
              sizeof (ficlHash) + (bucketCount - 1) * sizeof (ficlWord *));

    hash->size = bucketCount;
    ficlHashReset(hash);

    dictionary->forthWordlist = hash;
    dictionary->smudge = NULL;
    ficlDictionaryResetSearchOrder(dictionary);
    return;
}


/**************************************************************************
**                      i s A F i c l W o r d
** Vet a candidate pointer carefully to make sure
** it's not some chunk o' inline data...
** It has to have a name, and it has to look
** like it's in the dictionary address range.
** NOTE: this excludes :noname words!
**************************************************************************/
int ficlDictionaryIsAWord(ficlDictionary *dictionary, ficlWord *word)
{
	if ( (((ficlInstruction)(uintptr_t)word) > ficlInstructionInvalid)
		&& (((ficlInstruction)(uintptr_t)word) < ficlInstructionLast) )
		return 1;

    if (!ficlDictionaryIncludes(dictionary, word))
       return 0;

    if (!ficlDictionaryIncludes(dictionary, word->name))
        return 0;

	if ((word->link != NULL) && !ficlDictionaryIncludes(dictionary, word->link))
		return 0;

    if ((word->length <= 0) || (word->name[word->length] != '\0'))
		return 0;

	if (strlen(word->name) != word->length)
		return 0;

	return 1;
}


/**************************************************************************
                        f i n d E n c l o s i n g W o r d
** Given a pointer to something, check to make sure it's an address in the 
** dictionary. If so, search backwards until we find something that looks
** like a dictionary header. If successful, return the address of the 
** ficlWord found. Otherwise return NULL.
** nSEARCH_CELLS sets the maximum neighborhood this func will search before giving up
**************************************************************************/
#define nSEARCH_CELLS 100

ficlWord *ficlDictionaryFindEnclosingWord(ficlDictionary *dictionary, ficlCell *cell)
{
    ficlWord *word;
    int i;

    if (!ficlDictionaryIncludes(dictionary, (void *)cell))
        return NULL;

    for (i = nSEARCH_CELLS; i > 0; --i, --cell)
    {
        word = (ficlWord *)(cell + 1 - (sizeof(ficlWord) / sizeof(ficlCell)));
        if (ficlDictionaryIsAWord(dictionary, word))
            return word;
    }

    return NULL;
}


/**************************************************************************
                        d i c t I n c l u d e s
** Returns FICL_TRUE iff the given pointer is within the address range of 
** the dictionary.
**************************************************************************/
int ficlDictionaryIncludes(ficlDictionary *dictionary, void *p)
{
    return ((p >= (void *) &dictionary->base)
        &&  (p <  (void *)(&dictionary->base + dictionary->size)));
}


/**************************************************************************
                        d i c t L o o k u p
** Find the ficlWord that matches the given name and length.
** If found, returns the word's address. Otherwise returns NULL.
** Uses the search order list to search multiple wordlists.
**************************************************************************/
ficlWord *ficlDictionaryLookup(ficlDictionary *dictionary, ficlString name)
{
    ficlWord *word = NULL;
    ficlHash *hash;
    int i;
    ficlUnsigned16 hashCode   = ficlHashCode(name);

    FICL_DICTIONARY_ASSERT(dictionary, dictionary != NULL);

    ficlDictionaryLock(dictionary, FICL_TRUE);

    for (i = (int)dictionary->wordlistCount - 1; (i >= 0) && (!word); --i)
    {
        hash = dictionary->wordlists[i];
        word = ficlHashLookup(hash, name, hashCode);
    }

    ficlDictionaryLock(dictionary, FICL_TRUE);
    return word;
}


/**************************************************************************
                        s e e 
** TOOLS ( "<spaces>name" -- )
** Display a human-readable representation of the named word's definition.
** The source of the representation (object-code decompilation, source
** block, etc.) and the particular form of the display is implementation
** defined. 
**************************************************************************/
/*
** ficlSeeColon (for proctologists only)
** Walks a colon definition, decompiling
** on the fly. Knows about primitive control structures.
*/
char *ficlDictionaryInstructionNames[] =
{
#define FICL_TOKEN(token, description) description,
#define FICL_INSTRUCTION_TOKEN(token, description, flags) description,
#include "ficltokens.h"
#undef FICL_TOKEN
#undef FICL_INSTRUCTION_TOKEN
};

void ficlDictionarySee(ficlDictionary *dictionary, ficlWord *word, ficlCallback *callback)
{
    char *trace;
    ficlCell *cell = word->param;
    ficlCell *param0 = cell;
    char buffer[128];

    for (; cell->i != ficlInstructionSemiParen; cell++)
    {
        ficlWord *word = (ficlWord *)(cell->p);

        trace = buffer;
        if ((void *)cell == (void *)buffer)
            *trace++ = '>';
        else
            *trace++ = ' ';
        trace += sprintf(trace, "%3td   ", cell - param0);
        
        if (ficlDictionaryIsAWord(dictionary, word))
        {
            ficlWordKind kind = ficlWordClassify(word);
            ficlCell c, c2;

            switch (kind)
            {
            case FICL_WORDKIND_INSTRUCTION:
                sprintf(trace, "%s (instruction %ld)", ficlDictionaryInstructionNames[(long)word], (long)word);
                break;
            case FICL_WORDKIND_INSTRUCTION_WITH_ARGUMENT:
                c = *++cell;
                sprintf(trace, "%s (instruction %ld), with argument %ld (%#lx)", ficlDictionaryInstructionNames[(long)word], (long)word, c.i, c.u);
                break;
            case FICL_WORDKIND_INSTRUCTION_WORD:
                sprintf(trace, "%s :: executes %s (instruction word %ld)", word->name, ficlDictionaryInstructionNames[(long)word->code], (long)word->code);
                break;
            case FICL_WORDKIND_LITERAL:
                c = *++cell;
                if (ficlDictionaryIsAWord(dictionary, (ficlWord*)c.p) && (c.i >= ficlInstructionLast))
                {
                    ficlWord *word = (ficlWord *)c.p;
                    sprintf(trace, "%.*s ( %#jx literal )", 
                        word->length, word->name, (uintmax_t)c.u);
                }
                else
                    sprintf(trace, "literal %jd (%#jx)", (intmax_t)c.i, (uintmax_t)c.u);
                break;
            case FICL_WORDKIND_2LITERAL:
                c = *++cell;
                c2 = *++cell;
                sprintf(trace, "2literal %jd %jd (%#jx %#jx)", (intmax_t)c2.i, (intmax_t)c.i, (uintmax_t)c2.u, (uintmax_t)c.u);
                break;
#if FICL_WANT_FLOAT
            case FICL_WORDKIND_FLITERAL:
                c = *++cell;
                sprintf(trace, "fliteral %f (%#jx)", c.f, (uintmax_t)c.u);
                break;
#endif /* FICL_WANT_FLOAT */
            case FICL_WORDKIND_STRING_LITERAL:
                {
                    ficlCountedString *counted = (ficlCountedString *)(void *)++cell;
                    cell = (ficlCell *)ficlAlignPointer(counted->text + counted->length + 1) - 1;
                    sprintf(trace, "s\" %.*s\"", counted->length, counted->text);
                }
                break;
            case FICL_WORDKIND_CSTRING_LITERAL:
                {
                    ficlCountedString *counted = (ficlCountedString *)(void *)++cell;
                    cell = (ficlCell *)ficlAlignPointer(counted->text + counted->length + 1) - 1;
                    sprintf(trace, "c\" %.*s\"", counted->length, counted->text);
                }
                break;
            case FICL_WORDKIND_BRANCH0:
                c = *++cell;
                sprintf(trace, "branch0 %td", cell + c.i - param0);
                break;                                                           
            case FICL_WORDKIND_BRANCH:
                c = *++cell;
                sprintf(trace, "branch %td",     cell + c.i - param0);
                break;

            case FICL_WORDKIND_QDO:
                c = *++cell;
                sprintf(trace, "?do (leave %td)",  (ficlCell *)c.p - param0);
                break;
            case FICL_WORDKIND_DO:
                c = *++cell;
                sprintf(trace, "do (leave %td)", (ficlCell *)c.p - param0);
                break;
            case FICL_WORDKIND_LOOP:
                c = *++cell;
                sprintf(trace, "loop (branch %td)", cell + c.i - param0);
                break;
            case FICL_WORDKIND_OF:
                c = *++cell;
                sprintf(trace, "of (branch %td)",      cell + c.i - param0);
                break;
            case FICL_WORDKIND_PLOOP:
                c = *++cell;
                sprintf(trace, "+loop (branch %td)", cell + c.i - param0);
                break;
            default:
                sprintf(trace, "%.*s", word->length, word->name);
                break;
            }
 
        }
        else /* probably not a word - punt and print value */
        {
            sprintf(trace, "%ld ( %#lx )", cell->i, cell->u);
        }

        ficlCallbackTextOut(callback, buffer);
        ficlCallbackTextOut(callback, "\n");
    }

    ficlCallbackTextOut(callback, ";\n");
}

/**************************************************************************
                    d i c t R e s e t S e a r c h O r d e r
** Initialize the dictionary search order list to sane state
**************************************************************************/
void ficlDictionaryResetSearchOrder(ficlDictionary *dictionary)
{
    FICL_DICTIONARY_ASSERT(dictionary, dictionary);
    dictionary->compilationWordlist = dictionary->forthWordlist;
    dictionary->wordlistCount = 1;
    dictionary->wordlists[0] = dictionary->forthWordlist;
    return;
}


/**************************************************************************
                        d i c t S e t F l a g s
** Changes the flags field of the most recently defined word:
** Set all bits that are ones in the set parameter.
**************************************************************************/
void ficlDictionarySetFlags(ficlDictionary *dictionary, ficlUnsigned8 set)
{
    FICL_DICTIONARY_ASSERT(dictionary, dictionary->smudge);
    dictionary->smudge->flags |= set;
    return;
}


/**************************************************************************
                        d i c t C l e a r F l a g s
** Changes the flags field of the most recently defined word:
** Clear all bits that are ones in the clear parameter.
**************************************************************************/
void ficlDictionaryClearFlags(ficlDictionary *dictionary, ficlUnsigned8 clear)
{
    FICL_DICTIONARY_ASSERT(dictionary, dictionary->smudge);
    dictionary->smudge->flags &= ~clear;
    return;
}


/**************************************************************************
                        d i c t S e t I m m e d i a t e 
** Set the most recently defined word as IMMEDIATE
**************************************************************************/
void ficlDictionarySetImmediate(ficlDictionary *dictionary)
{
    FICL_DICTIONARY_ASSERT(dictionary, dictionary->smudge);
    dictionary->smudge->flags |= FICL_WORD_IMMEDIATE;
    return;
}


/**************************************************************************
                        d i c t U n s m u d g e 
** Completes the definition of a word by linking it
** into the main list
**************************************************************************/
void ficlDictionaryUnsmudge(ficlDictionary *dictionary)
{
    ficlWord *word = dictionary->smudge;
    ficlHash *hash = dictionary->compilationWordlist;

    FICL_DICTIONARY_ASSERT(dictionary, hash);
    FICL_DICTIONARY_ASSERT(dictionary, word);
    /*
    ** :noname words never get linked into the list...
    */
    if (word->length > 0)
        ficlHashInsertWord(hash, word);
    word->flags &= ~(FICL_WORD_SMUDGED);
    return;
}


/**************************************************************************
                        d i c t W h e r e
** Returns the value of the HERE pointer -- the address
** of the next free ficlCell in the dictionary
**************************************************************************/
ficlCell *ficlDictionaryWhere(ficlDictionary *dictionary)
{
    return dictionary->here;
}