

/*
 * This work was supported by the United States Government, and is
 * not subject to copyright.
 *
 * $Log: dict.c,v $
 * Revision 1.4  1997/01/21 19:19:51  dar
 * made C++ compatible
 *
 * Revision 1.3  1994/11/10  19:20:03  clark
 * Update to IS
 *
 * Revision 1.2  1993/10/15  18:49:55  libes
 * CADDETC certified
 *
 * Revision 1.6  1993/02/22  21:41:39  libes
 * enum fix
 *
 * Revision 1.5  1993/01/19  22:45:07  libes
 * *** empty log message ***
 *
 * Revision 1.4  1992/08/18  17:16:22  libes
 * rm'd extraneous error messages
 *
 * Revision 1.3  1992/06/08  18:08:05  libes
 * prettied up interface to print_objects_when_running
 *
 * Revision 1.2  1992/05/31  08:37:18  libes
 * multiple files
 *
 * Revision 1.1  1992/05/28  03:56:55  libes
 * Initial revision
 */

#define DICTIONARY_C
#include "express/dict.h"
#include "express/object.h"
#include "express/expbasic.h"

static Error    ERROR_duplicate_decl;
static Error    ERROR_duplicate_decl_diff_file;

void
DICTprint( Dictionary dict ) {
    Element e;
    DictionaryEntry de;

    HASHlistinit( dict, &de );

    while( 0 != ( e = ( HASHlist( &de ) ) ) ) {
        printf( "key <%s>  data <%s>  line <%d>  <\"%c\" %s>  <%s>\n",
                e->key, e->data, e->symbol->line, e->type,
                OBJget_type( e->type ), e->symbol->filename );
    }
}

/** Initialize the Dictionary module */
void DICTinitialize( void ) {
    ERROR_duplicate_decl = ERRORcreate(
                               "Redeclaration of %s.  Previous declaration was on line %d.", SEVERITY_ERROR );
    ERROR_duplicate_decl_diff_file = ERRORcreate(
                                         "Redeclaration of %s.  Previous declaration was on line %d in file %s.", SEVERITY_ERROR );
}

/*
** Procedure:   DICTcreate
** Parameters:  int  size   - estimated (initial) max # of entries
** Returns: Dictionary  - a new dictionary of the specified size
*/
/* now a macro */

/**
 * Define anything in a dictionary.  Generates an
 * error directly if there is a duplicate value.
 * \param dictionary dictionary to modify
 * \param entry entry to be added
 * \param experrc buffer for error code
 * \return 0 on success, 1 on failure
 */
int DICTdefine( Dictionary dict, char * name, Generic obj, Symbol * sym, char type ) {
    struct Element_ new, *old;

    new.key = name;
    new.data = obj;
    new.symbol = sym;
    new.type = type;

    if( 0 == ( old = HASHsearch( dict, &new, HASH_INSERT ) ) ) {
        return( 0 );
    }

    /* allow multiple definitions of an enumeration id in its
     * first scope of visibility.  *don't* allow enum id to be
     * shadowed by another type of symbol in the first scope
     * of visibility.  this changed (back) in the IS.
     *
     * Nov 2011 - Apparently, this changed again; I (MP) am
     * told that it is legal for an enum value and an entity
     * to have the same name. To fix this, I replaced the
     * || with && in the else-if below.
     */
    if( ( type == OBJ_ENUM ) && ( old->type == OBJ_ENUM ) ) {
        /* if we're adding an enum, but we've already seen one */
        /* (and only one enum), mark it ambiguous */
        DICTchange_type( old, OBJ_AMBIG_ENUM );
    } else if( ( type != OBJ_ENUM ) && ( !IS_ENUM( old->type ) ) ) {
        /* if we're adding a non-enum, and we've  *
         * already added a non-enum, complain     */
        if( sym->filename == old->symbol->filename ) {
            ERRORreport_with_symbol( ERROR_duplicate_decl, sym, name, old->symbol->line );
        } else {
            ERRORreport_with_symbol( ERROR_duplicate_decl_diff_file, sym, name, old->symbol->line, old->symbol->filename );
        }
        experrc = ERROR_subordinate_failed;
        return( 1 );
    }
    return 0;
}

/**
 * This version is used for defining things within an enumeration scope
 * I.e., the only error it would pick up would be an error such as
 * ENUMERATION OF ( A, A ) which has happened!
 * This is the way DICTdefine used to look before enumerations gained
 * their unusual behavior with respect to scoping and visibility rules
 */
int DICT_define( Dictionary dict, char * name, Generic obj, Symbol * sym, char type ) {
    struct Element_ e, *e2;

    e.key = name;
    e.data = obj;
    e.symbol = sym;
    e.type = type;

    if( 0 == ( e2 = HASHsearch( dict, &e, HASH_INSERT ) ) ) {
        return( 0 );
    }

    if( sym->filename == e2->symbol->filename ) {
        ERRORreport_with_symbol( ERROR_duplicate_decl, sym, name, e2->symbol->line );
    } else {
        ERRORreport_with_symbol( ERROR_duplicate_decl_diff_file, sym, name, e2->symbol->line, e2->symbol->filename );
    }
    experrc = ERROR_subordinate_failed;
    return( 1 );
}

/*
** Procedure:   DICTundefine
** Parameters:  Dictionary dictionary   - dictionary to modify
**      char *     name     - name to remove
** Returns: Generic         - the entry removed, NULL if not found
    Changed to return void, since the hash code frees the element, there
    is no way to return (without godawful casting) the generic itself.
*/

void
DICTundefine( Dictionary dict, char * name ) {
    struct Element_ e;

    e.key = name;
    HASHsearch( dict, &e, HASH_DELETE );
}

/*
** Procedure:   DICTlookup
** Parameters:  Dictionary dictionary   - dictionary to look in
**      char *     name     - name to look up
** Returns: Generic         - the value found, NULL if not found
*/

Generic
DICTlookup( Dictionary dictionary, char * name ) {
    struct Element_ e, *ep;

    if( !dictionary ) {
        return 0;
    }

    e.key = name;
    ep = HASHsearch( dictionary, &e, HASH_FIND );
    if( ep ) {
        DICT_type = ep->type;
        return( ep->data );
    }
    return( NULL );
}

/* like DICTlookup but returns symbol, too */
Generic
DICTlookup_symbol( Dictionary dictionary, char * name, Symbol ** sym ) {
    struct Element_ e, *ep;

    if( !dictionary ) {
        return 0;
    }

    e.key = name;
    ep = HASHsearch( dictionary, &e, HASH_FIND );
    if( ep ) {
        DICT_type = ep->type;
        *sym = ep->symbol;
        return( ep->data );
    }
    return( NULL );
}

Generic
DICTdo( DictionaryEntry * dict_entry ) {
    if( 0 == HASHlist( dict_entry ) ) {
        return 0;
    }

    DICT_type = dict_entry->e->type;    /* side-effect! */
    return dict_entry->e->data;
}
