root/usr/src/common/ficl/search.c
/*
 * s e a r c h . c
 * Forth Inspired Command Language
 * ANS Forth SEARCH and SEARCH-EXT word-set written in C
 * Author: John Sadler (john_sadler@alum.mit.edu)
 * Created: 6 June 2000
 * $Id: search.c,v 1.10 2010/08/12 13:57:22 asau Exp $
 */
/*
 * Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
 * All rights reserved.
 *
 * Get the latest Ficl release at http://ficl.sourceforge.net
 *
 * I am interested in hearing from anyone who uses Ficl. If you have
 * a problem, a success story, a defect, an enhancement request, or
 * if you would like to contribute to the Ficl release, please
 * contact me by email at the address above.
 *
 * L I C E N S E  and  D I S C L A I M E R
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    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 <string.h>
#include "ficl.h"

/*
 * d e f i n i t i o n s
 * SEARCH ( -- )
 * Make the compilation word list the same as the first word list in the
 * search order. Specifies that the names of subsequent definitions will
 * be placed in the compilation word list. Subsequent changes in the search
 * order will not affect the compilation word list.
 */
static void
ficlPrimitiveDefinitions(ficlVm *vm)
{
        ficlDictionary *dictionary = ficlVmGetDictionary(vm);

        FICL_VM_ASSERT(vm, dictionary);
        if (dictionary->wordlistCount < 1) {
                ficlVmThrowError(vm, "DEFINITIONS error - empty search order");
        }

        dictionary->compilationWordlist =
            dictionary->wordlists[dictionary->wordlistCount-1];
}

/*
 * f o r t h - w o r d l i s t
 * SEARCH ( -- wid )
 * Return wid, the identifier of the word list that includes all standard
 * words provided by the implementation. This word list is initially the
 * compilation word list and is part of the initial search order.
 */
static void
ficlPrimitiveForthWordlist(ficlVm *vm)
{
        ficlHash *hash = ficlVmGetDictionary(vm)->forthWordlist;
        ficlStackPushPointer(vm->dataStack, hash);
}


/*
 * g e t - c u r r e n t
 * SEARCH ( -- wid )
 * Return wid, the identifier of the compilation word list.
 */
static void
ficlPrimitiveGetCurrent(ficlVm *vm)
{
        ficlDictionary *dictionary = ficlVmGetDictionary(vm);
        ficlDictionaryLock(dictionary, FICL_TRUE);
        ficlStackPushPointer(vm->dataStack, dictionary->compilationWordlist);
        ficlDictionaryLock(dictionary, FICL_FALSE);
}

/*
 * g e t - o r d e r
 * SEARCH ( -- widn ... wid1 n )
 * Returns the number of word lists n in the search order and the word list
 * identifiers widn ... wid1 identifying these word lists. wid1 identifies
 * the word list that is searched first, and widn the word list that is
 * searched last. The search order is unaffected.
 */
static void
ficlPrimitiveGetOrder(ficlVm *vm)
{
        ficlDictionary *dictionary = ficlVmGetDictionary(vm);
        int wordlistCount = dictionary->wordlistCount;
        int i;

        ficlDictionaryLock(dictionary, FICL_TRUE);
        for (i = 0; i < wordlistCount; i++) {
                ficlStackPushPointer(vm->dataStack, dictionary->wordlists[i]);
        }

        ficlStackPushUnsigned(vm->dataStack, wordlistCount);
        ficlDictionaryLock(dictionary, FICL_FALSE);
}

/*
 * s e a r c h - w o r d l i s t
 * SEARCH ( c-addr u wid -- 0 | xt 1 | xt -1 )
 * Find the definition identified by the string c-addr u in the word list
 * identified by wid. If the definition is not found, return zero. If the
 * definition is found, return its execution token xt and one (1) if the
 * definition is immediate, minus-one (-1) otherwise.
 */
static void
ficlPrimitiveSearchWordlist(ficlVm *vm)
{
        ficlString name;
        ficlUnsigned16 hashCode;
        ficlWord *word;
        ficlHash *hash = ficlStackPopPointer(vm->dataStack);

        name.length = (ficlUnsigned8)ficlStackPopUnsigned(vm->dataStack);
        name.text = ficlStackPopPointer(vm->dataStack);
        hashCode = ficlHashCode(name);

        ficlDictionaryLock(ficlVmGetDictionary(vm), FICL_TRUE);
        word = ficlHashLookup(hash, name, hashCode);
        ficlDictionaryLock(ficlVmGetDictionary(vm), FICL_FALSE);

        if (word) {
                ficlStackPushPointer(vm->dataStack, word);
                ficlStackPushInteger(vm->dataStack,
                    (ficlWordIsImmediate(word) ? 1 : -1));
        } else {
                ficlStackPushUnsigned(vm->dataStack, 0);
        }
}

/*
 * s e t - c u r r e n t
 * SEARCH ( wid -- )
 * Set the compilation word list to the word list identified by wid.
 */
static void
ficlPrimitiveSetCurrent(ficlVm *vm)
{
        ficlHash *hash = ficlStackPopPointer(vm->dataStack);
        ficlDictionary *dictionary = ficlVmGetDictionary(vm);
        ficlDictionaryLock(dictionary, FICL_TRUE);
        dictionary->compilationWordlist = hash;
        ficlDictionaryLock(dictionary, FICL_FALSE);
}

/*
 *                      s e t - o r d e r
 * SEARCH ( widn ... wid1 n -- )
 * Set the search order to the word lists identified by widn ... wid1.
 * Subsequently, word list wid1 will be searched first, and word list
 * widn searched last. If n is zero, empty the search order. If n is minus
 * one, set the search order to the implementation-defined minimum
 * search order. The minimum search order shall include the words
 * FORTH-WORDLIST and SET-ORDER. A system shall allow n to
 * be at least eight.
 */
static void
ficlPrimitiveSetOrder(ficlVm *vm)
{
        int i;
        int wordlistCount = ficlStackPopInteger(vm->dataStack);
        ficlDictionary *dictionary = ficlVmGetDictionary(vm);

        if (wordlistCount > FICL_MAX_WORDLISTS) {
                ficlVmThrowError(vm,
                    "set-order error: list would be too large");
        }

        ficlDictionaryLock(dictionary, FICL_TRUE);

        if (wordlistCount >= 0) {
                dictionary->wordlistCount = wordlistCount;
                for (i = wordlistCount-1; i >= 0; --i) {
                        dictionary->wordlists[i] =
                            ficlStackPopPointer(vm->dataStack);
                }
        } else {
                ficlDictionaryResetSearchOrder(dictionary);
        }

        ficlDictionaryLock(dictionary, FICL_FALSE);
}

/*
 * f i c l - w o r d l i s t
 * SEARCH ( -- wid )
 * Create a new empty word list, returning its word list identifier wid.
 * The new word list may be returned from a pool of preallocated word
 * lists or may be dynamically allocated in data space. A system shall
 * allow the creation of at least 8 new word lists in addition to any
 * provided as part of the system.
 * Notes:
 * 1. Ficl creates a new single-list hash in the dictionary and returns
 *    its address.
 * 2. ficl-wordlist takes an arg off the stack indicating the number of
 *    hash entries in the wordlist. Ficl 2.02 and later define WORDLIST as
 *    : wordlist 1 ficl-wordlist ;
 */
static void
ficlPrimitiveFiclWordlist(ficlVm *vm)
{
        ficlDictionary *dictionary = ficlVmGetDictionary(vm);
        ficlHash *hash;
        ficlUnsigned nBuckets;

        FICL_STACK_CHECK(vm->dataStack, 1, 1);

        nBuckets = ficlStackPopUnsigned(vm->dataStack);
        hash = ficlDictionaryCreateWordlist(dictionary, nBuckets);
        ficlStackPushPointer(vm->dataStack, hash);
}

/*
 * S E A R C H >
 * Ficl  ( -- wid )
 * Pop wid off the search order. Error if the search order is empty
 */
static void
ficlPrimitiveSearchPop(ficlVm *vm)
{
        ficlDictionary *dictionary = ficlVmGetDictionary(vm);
        int wordlistCount;

        ficlDictionaryLock(dictionary, FICL_TRUE);
        wordlistCount = dictionary->wordlistCount;
        if (wordlistCount == 0) {
                ficlVmThrowError(vm, "search> error: empty search order");
        }
        ficlStackPushPointer(vm->dataStack,
            dictionary->wordlists[--dictionary->wordlistCount]);
        ficlDictionaryLock(dictionary, FICL_FALSE);
}

/*
 * > S E A R C H
 * Ficl  ( wid -- )
 * Push wid onto the search order. Error if the search order is full.
 */
static void
ficlPrimitiveSearchPush(ficlVm *vm)
{
        ficlDictionary *dictionary = ficlVmGetDictionary(vm);

        ficlDictionaryLock(dictionary, FICL_TRUE);
        if (dictionary->wordlistCount > FICL_MAX_WORDLISTS) {
                ficlVmThrowError(vm, ">search error: search order overflow");
        }
        dictionary->wordlists[dictionary->wordlistCount++] =
            ficlStackPopPointer(vm->dataStack);
        ficlDictionaryLock(dictionary, FICL_FALSE);
}

/*
 * W I D - G E T - N A M E
 * Ficl  ( wid -- c-addr u )
 * Get wid's (optional) name and push onto stack as a counted string
 */
static void
ficlPrimitiveWidGetName(ficlVm *vm)
{
        ficlHash *hash;
        char *name;
        ficlInteger length;
        ficlCell c;

        hash = ficlVmPop(vm).p;
        name = hash->name;

        if (name != NULL)
                length = strlen(name);
        else
                length = 0;

        c.p = name;
        ficlVmPush(vm, c);

        c.i = length;
        ficlVmPush(vm, c);
}

/*
 * W I D - S E T - N A M E
 * Ficl  ( wid c-addr -- )
 * Set wid's name pointer to the \0 terminated string address supplied
 */
static void
ficlPrimitiveWidSetName(ficlVm *vm)
{
        char *name = (char *)ficlVmPop(vm).p;
        ficlHash *hash = ficlVmPop(vm).p;
        hash->name = name;
}

/*
 * setParentWid
 * Ficl
 * setparentwid   ( parent-wid wid -- )
 * Set WID's link field to the parent-wid. search-wordlist will
 * iterate through all the links when finding words in the child wid.
 */
static void
ficlPrimitiveSetParentWid(ficlVm *vm)
{
        ficlHash *parent, *child;

        FICL_STACK_CHECK(vm->dataStack, 2, 0);

        child  = (ficlHash *)ficlStackPopPointer(vm->dataStack);
        parent = (ficlHash *)ficlStackPopPointer(vm->dataStack);

        child->link = parent;
}

/*
 * f i c l C o m p i l e S e a r c h
 * Builds the primitive wordset and the environment-query namespace.
 */
void
ficlSystemCompileSearch(ficlSystem *system)
{
        ficlDictionary *dictionary = ficlSystemGetDictionary(system);
        ficlDictionary *environment = ficlSystemGetEnvironment(system);

        FICL_SYSTEM_ASSERT(system, dictionary);
        FICL_SYSTEM_ASSERT(system, environment);

        /*
         * optional SEARCH-ORDER word set
         */
        (void) ficlDictionarySetPrimitive(dictionary, ">search",
            ficlPrimitiveSearchPush, FICL_WORD_DEFAULT);
        (void) ficlDictionarySetPrimitive(dictionary, "search>",
            ficlPrimitiveSearchPop, FICL_WORD_DEFAULT);
        (void) ficlDictionarySetPrimitive(dictionary, "definitions",
            ficlPrimitiveDefinitions, FICL_WORD_DEFAULT);
        (void) ficlDictionarySetPrimitive(dictionary, "forth-wordlist",
            ficlPrimitiveForthWordlist, FICL_WORD_DEFAULT);
        (void) ficlDictionarySetPrimitive(dictionary, "get-current",
            ficlPrimitiveGetCurrent, FICL_WORD_DEFAULT);
        (void) ficlDictionarySetPrimitive(dictionary, "get-order",
            ficlPrimitiveGetOrder, FICL_WORD_DEFAULT);
        (void) ficlDictionarySetPrimitive(dictionary, "search-wordlist",
            ficlPrimitiveSearchWordlist, FICL_WORD_DEFAULT);
        (void) ficlDictionarySetPrimitive(dictionary, "set-current",
            ficlPrimitiveSetCurrent, FICL_WORD_DEFAULT);
        (void) ficlDictionarySetPrimitive(dictionary, "set-order",
            ficlPrimitiveSetOrder, FICL_WORD_DEFAULT);
        (void) ficlDictionarySetPrimitive(dictionary, "ficl-wordlist",
            ficlPrimitiveFiclWordlist, FICL_WORD_DEFAULT);

        /*
         * Set SEARCH environment query values
         */
        (void) ficlDictionarySetConstant(environment, "search-order",
            FICL_TRUE);
        (void) ficlDictionarySetConstant(environment, "search-order-ext",
            FICL_TRUE);
        (void) ficlDictionarySetConstant(environment, "wordlists",
            FICL_MAX_WORDLISTS);
        (void) ficlDictionarySetPrimitive(dictionary, "wid-get-name",
            ficlPrimitiveWidGetName, FICL_WORD_DEFAULT);
        (void) ficlDictionarySetPrimitive(dictionary, "wid-set-name",
            ficlPrimitiveWidSetName, FICL_WORD_DEFAULT);
        (void) ficlDictionarySetPrimitive(dictionary, "wid-set-super",
            ficlPrimitiveSetParentWid, FICL_WORD_DEFAULT);
}