/*******************************************************************************
*
* McStas, neutron ray-tracing package
*         Copyright 1997-2002, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Kernel: symtab.c
*
* %Identification
* Written by: K.N.
* Date: Jul  1, 1997
* Origin: Risoe
* Release: McStas 1.6
* Version: 1.4
*
* Symbol tables.
*
*	$Id: symtab.c,v 1.16 2003/10/06 15:00:12 farhi Exp $
*
*	$Log: symtab.c,v $
*	Revision 1.16  2003/10/06 15:00:12  farhi
*	Added symtab_previous function for PREVIOUS keyword
*	
*	Revision 1.15  2003/02/11 12:28:45  farhi
*	Variouxs bug fixes after tests in the lib directory
*	mcstas_r  : disable output with --no-out.. flag. Fix 1D McStas output
*	read_table:corrected MC_SYS_DIR -> MCSTAS define
*	monitor_nd-lib: fix Log(signal) log(coord)
*	HOPG.trm: reduce 4000 points -> 400 which is enough and faster to resample
*	Progress_bar: precent -> percent parameter
*	CS: ----------------------------------------------------------------------
*	
*	Revision 1.4  1999/04/16 07:41:31  kn
*	Make the value_free argument for the symtab_free function optional.
*
*	Revision 1.3  1998/10/02 08:39:25  kn
*	Fixed header comment.
*
*	Revision 1.2  1997/09/07 17:58:45  kn
*	Snapshot with (untested) code generation complete.
*
*	Revision 1.1  1997/08/13 09:16:14  kn
*	Initial revision
*
*******************************************************************************/

#include <string.h>

#include "mcstas.h"


/*******************************************************************************
* A symbol table is an abstract data type that maps any name to a
* corresponding symbol table entry (which can be anything).
*
* In this first version, we use a simple, but inefficient approach. Symbol
* tables are implemented as fixed-size arrays of string and void pointer
* pairs. Table lookups are performed with a linear search. But the interface
* is designed to make a transition to a more efficient implementation easily
* possible if and when it becomes necessary.
*******************************************************************************/

struct Symbol_table
  {
    int size;			/* Number of entries currently in table. */
    int maxsize;		/* Total size of table. */
    struct Symtab_entry *entries; /* Array of pairs of names and values. */
  };

#define MAXSIZE 1000		/* Max. table size. */

/* Position in a symbol table for doing traversals. */
struct Symtab_position
  {
    struct Symbol_table *symtab; /* The symbol table we are traversing. */
    int index;			 /* Next entry to return. */
  };


/*******************************************************************************
* Allocate and initialize a new symbol table.
*******************************************************************************/
Symtab
symtab_create(void)
{
  Symtab st;

  palloc(st);			/* Allocate new symbol table. */
  st->maxsize = MAXSIZE;
  nalloc(st->entries, st->maxsize); /* Allocate array for entries. */
  st->size = 0;			/* Empty table. */
  return st;
}


/*******************************************************************************
* Look up a name in a symbol table. Returns a void pointer to the table
* entry, or NULL if not found.
*******************************************************************************/
struct Symtab_entry *
symtab_lookup(Symtab st, char *name)
{
  int i;

  for(i = 0; i < st->size; i++)
  {
    if(!strcmp(name, st->entries[i].name)) /* Found? */
    {
      return &(st->entries[i]);
    }
  }

  /* Not found. */
  return NULL;
}


/*******************************************************************************
* Add a new name to an existing symbol table along with a table entry. The
* table entry is a void pointer which could eg. point to a structure
* containing information relevant to the name.
*
* The table entry must point to memory that remains valid for the duration of
* the symbol table. The name, however, may be re-used freely as a new copy is
* allocated in the symbol table.
*******************************************************************************/
struct Symtab_entry *
symtab_add(Symtab st, char *name, void *value)
{
  int i;
  
  /* First see if an entry for this name already exists (it shouldn't, but ...) */
  for(i = 0; i < st->size; i++)
  {
    if(!strcmp(name, st->entries[i].name))
    {
      /* Hmm ... adding an already present name. */
      debugn((DEBUG_MEDIUM, "add_to_symtab: name already exists: %s.\n", name));
      return &(st->entries[i]);
    }
  }

  /* Make sure the table is large enough. */
  if(st->size >= st->maxsize)
  {
    /* No room in table. This causes the program to abort. */
    fatal_error("symtab_add: symbol table too small.");
  }

  /* Add the name at the end of the table. */
  i = st->size;
  st->size++;
  st->entries[i].name = str_dup(name);
  st->entries[i].val = value;
  return value;
}


/*******************************************************************************
* Free up memory allocated to a symbol table. The caller can supply a
* function value_free that will free the memory for each table entry.
* Pass NULL for value_free if no freeing is necessary.
*******************************************************************************/
void
symtab_free(Symtab st, void (*value_free)(void *))
{
  int i;

  for(i = 0; i < st->size; i++)
  {
    str_free(st->entries[i].name);
    if(value_free)
      (*value_free)(st->entries[i].val);
  }
  memfree(st->entries);
  memfree(st);
}


/*******************************************************************************
* Prepare to start traversing a symbol table. Note that an improved
* implementation is free to change the order in which a traversal returns the
* elements (for example if using a hash table).
*******************************************************************************/
Symtab_handle
symtab_iterate(Symtab s)
{
  Symtab_handle sh;

  palloc(sh);
  sh->symtab = s;
  sh->index = 0;
  return sh;
}


/*******************************************************************************
* Get the next element during a traversal of a symbol table. Returns NULL
* when no more elements exist.
*******************************************************************************/
struct Symtab_entry *
symtab_next(Symtab_handle sh)
{
  int i = sh->index;

  /* Check if there are any more entries. */
  if(i >= sh->symtab->size)
  {
    return NULL;
  }
  else
  {
    sh->index++;
    return &(sh->symtab->entries[i]);
  }
}

/*******************************************************************************
* Get the index-th previous element stored in the symbol table. Returns NULL
* when error occurs.
*******************************************************************************/
struct Symtab_entry *
symtab_previous(Symtab st, int index)
{
  if (index <= 0 || index > st->size) {
    return NULL;
  } else { 
    return &(st->entries[st->size - index]);
  }
}


/*******************************************************************************
* End a symbol table traversal, freeing the memory allocated to the handle.
*******************************************************************************/
void
symtab_iterate_end(Symtab_handle sh)
{
  memfree(sh);
}


syntax highlighted by Code2HTML, v. 0.9.1