/*
 * tsym.c -- functions for symbol table management.
 */

#include "..\h\config.h"
#include "general.h"
#include "tproto.h"
#include "globals.h"
#include "trans.h"
#include "token.h"
#include "tsym.h"

#ifndef VarTran
#include "lfile.h"
#endif					/* VarTran */

/*
 * Prototypes.
 */

hidden struct	tgentry *alcglob
   Params((struct tgentry *blink, char *name,int flag,int nargs));
hidden struct	tientry *alcid		Params((char *nam,int len));
hidden struct	tcentry *alclit	
   Params((struct tcentry *blink, char *name, int len,int flag));
hidden struct	tlentry *alcloc	
   Params((struct tlentry *blink, char *name,int flag));
hidden struct	tcentry *clookup	Params((char *id,int flag));
hidden struct	tgentry *glookup	Params((char *id));
hidden struct	tlentry *llookup	Params((char *id));
hidden novalue	putglob
   Params((char *id,int id_type, int n_args));
hidden int	streq			Params((int len,char *s1,char *s2));

#ifdef DeBugTrans
novalue	cdump	Params((noargs));
novalue	gdump	Params((noargs));
novalue	ldump	Params((noargs));
#endif					/* DeBugTrans */


/*
 * putid - install the identifier named by the string starting at strf
 *  and extending for len bytes.  The installation entails making an
 *  entry in the identifier hash table and then making an identifier
 *  table entry for it with alcid.  A side effect of installation
 *  is the incrementing of strf by the length of the string, thus
 *  "saving" it.
 *
 * Nothing is changed if the identifier has already been installed.
 */
char *putid(len)
int len;
   {
   register int hash;
   register char *s;
   register struct tientry *ip;
   int l;

   /*
    * Compute hash value by adding bytes and masking result with imask.
    *  (Recall that imask is ihsize-1.)
    */
   s = strf;
   hash = 0;
   l = len;
   while (l--)
      hash += *s++ & 0377;
   s = strf;
   l = len;
   hash &= imask;
   /*
    * If the identifier hasn't been installed, install it.
    */
   if ((ip = ihash[hash]) != NULL) {	/* collision */
      for (;;) {	/* work down i_blink chain until id is found or the
                            end of the chain is reached */
         if (l == ip->i_length && streq(l, s, ip->i_name))
            return (ip->i_name);	/* id is already installed */
         if (ip->i_blink == NULL) {	/* end of chain */
            ip->i_blink = alcid(s,l);
            strf += l;
            return s;
            }
         ip = ip->i_blink;
         }
      }
   /*
    * Hashed to an empty slot.
    */
   ihash[hash] = alcid(s,l);
   strf += l;
   return s;
   }

/*
 * streq - compare s1 with s2 for len bytes, and return 1 for equal,
 *  0 for not equal.
 */
static int streq(len, s1, s2)
register int len;
register char *s1, *s2;
   {
   while (len--)
      if (*s1++ != *s2++)
         return 0;
   return 1;
   }

/*
 * alcid - get the next free identifier table entry, and fill it in with
 *  the specified values.
 */
static struct tientry *alcid(nam, len)
char *nam;
int len;
   {
   register struct tientry *ip;

   if (ifree >= &itable[isize])
      tsyserr("out of identifier table space");
   ip = ifree++;
   ip->i_blink = NULL;
   ip->i_name = nam;
   ip->i_length = len;
   return ip;
   }

#ifndef VarTran

/*
 * loc_init - clear the local symbol table.
 */

novalue loc_init()
   {
   register struct tlentry **lp;
   register struct tcentry **cp;
   static int maxlfree = 0;
   static int maxcfree = 0;
					/* clear local table */
   maxlfree = (maxlfree > lfree-ltable) ? maxlfree : lfree-ltable;
   for (lp = lhash; lp < &lhash[lhsize]; lp++)
      *lp = NULL;
   lfree = ltable;
					/* clear constant table */
   maxcfree = (maxcfree > ctfree-ctable) ? maxcfree : ctfree-ctable;
   for (cp = chash; cp < &chash[chsize]; cp++)
      *cp = NULL;
   ctfree = ctable;
   }

/*
 * install - put an identifier into the global or local symbol table.
 *  The basic idea here is to look in the right table and install
 *  the identifier if it isn't already there.  Some semantic checks
 *  are performed.
 */
novalue install(name, flag, argcnt)
char *name;
int flag, argcnt;
   {
   union {
      struct tgentry *gp;
      struct tlentry *lp;
      } p;

   switch (flag) {
      case F_Global:	/* a variable in a global declaration */
         if ((p.gp = glookup(name)) == NULL)
            putglob(name, flag, argcnt);
         else
            p.gp->g_flag |= flag;
         break;

      case F_Proc|F_Global:	/* procedure declaration */
      case F_Record|F_Global:	/* record declaration */
      case F_Builtin|F_Global:	/* external declaration */
         if ((p.gp = glookup(name)) == NULL)
            putglob(name, flag, argcnt);
         else if ((p.gp->g_flag & (~F_Global)) == 0) { /* superfluous global
							   declaration for
							   record or proc */
            p.gp->g_flag |= flag;
            p.gp->g_nargs = argcnt;
            }
         else			/* the user can't make up his mind */
            tfatal("inconsistent redeclaration", name);
         break;

      case F_Static:	/* static declaration */
      case F_Dynamic:	/* local declaration (possibly implicit?) */
      case F_Argument:	/* formal parameter */
         if ((p.lp = llookup(name)) == NULL)
            putloc(name,flag);
         else if (p.lp->l_flag == flag) /* previously declared as same type */
            tfatal("redeclared identifier", name);
         else		/* previously declared as different type */
            tfatal("inconsistent redeclaration", name);
         break;

      default:
         tsyserr("install: unrecognized symbol table flag.");
      }
   }

/*
 * putloc - make a local symbol table entry and return the index
 *  of the entry in lhash.  alcloc does the work if there is a collision.
 */
int putloc(id,id_type)
char *id;
int id_type;
   {
   register struct tlentry *ptr;

   if ((ptr = llookup(id)) == NULL) {	/* add to head of hash chain */
      ptr = lhash[lhasher(id)];
      lhash[lhasher(id)] = alcloc(ptr, id, id_type);
      return (lhash[lhasher(id)] - ltable);
      }
   return (ptr - ltable);
   }

/*
 * putglob makes a global symbol table entry. alcglob does the work if there
 *  is a collision.
 */

static novalue putglob(id, id_type, n_args)
char *id;
int id_type, n_args;
   {
   register struct tgentry *ptr;

   if ((ptr = glookup(id)) == NULL) {	 /* add to head of hash chain */
      ptr = ghash[ghasher(id)];
      ghash[ghasher(id)] = alcglob(ptr, id, id_type, n_args);
      }
   }

/*
 * putlit makes a constant symbol table entry and returns the index
 *  of the entry in chash.  alclit does the work if there is a collision.
 */
int putlit(id, idtype, len)
char *id;
int len, idtype;
   {
   register struct tcentry *ptr;

   if ((ptr = clookup(id,idtype)) == NULL) {   /* add to head of hash chain */
      ptr = chash[chasher(id)];
      chash[chasher(id)] = alclit(ptr, id, len, idtype);
      return (chash[chasher(id)] - ctable);
      }
   return (ptr - ctable);
   }

/*
 * llookup looks up id in local symbol table and returns pointer to
 *  to it if found or NULL if not present.
 */

static struct tlentry *llookup(id)
char *id;
   {
   register struct tlentry *ptr;

   ptr = lhash[lhasher(id)];
   while (ptr != NULL && ptr->l_name != id)
      ptr = ptr->l_blink;
   return ptr;
   }

/*
 * glookup looks up id in global symbol table and returns pointer to
 *  to it if found or NULL if not present.
 */
static struct tgentry *glookup(id)
char *id;
   {
   register struct tgentry *ptr;

   ptr = ghash[ghasher(id)];
   while (ptr != NULL && ptr->g_name != id) {
      ptr = ptr->g_blink;
      }
   return ptr;
   }

/*
 * clookup looks up id in constant symbol table and returns pointer to
 *  to it if found or NULL if not present.
 */
static struct tcentry *clookup(id,flag)
char *id;
int flag;
   {
   register struct tcentry *ptr;

   ptr = chash[chasher(id)];
   while (ptr != NULL && (ptr->c_name != id || ptr->c_flag != flag))
      ptr = ptr->c_blink;

   return ptr;
   }

/*
 * klookup looks up keyword named by id in keyword table and returns
 *  its number (keyid).
 */
int klookup(id)
register char *id;
   {
   register struct keyent *kp;

   for (kp = keytab; kp->keyid >= 0; kp++)
      if (strcmp(kp->keyname,id) == 0)
         return (kp->keyid);

   return 0;
   }

#ifdef DeBugTrans
/*
 * ldump displays local symbol table to stdout.
 */

novalue ldump()
   {
   register int i;
   register struct tlentry *lptr;

   fprintf(stderr,"Dump of local symbol table (%d entries)\n",lfree-ltable);
   fprintf(stderr," loc   blink   id		  (name)      flags\n");
   for (i = 0; i < lhsize; i++)
      for (lptr = lhash[i]; lptr != NULL; lptr = lptr->l_blink)
         fprintf(stderr,"%5d  %5d  %5d	%20s  %7o\n", lptr-ltable,
		lptr->l_blink, lptr->l_name, lptr->l_name, lptr->l_flag);
   fflush(stderr);

   }

/*
 * gdump displays global symbol table to stdout.
 */

novalue gdump()
   {
   register int i;
   register struct tgentry *gptr;

   fprintf(stderr,"Dump of global symbol table (%d entries)\n",
      (int)(gfree-gtable));
   fprintf(stderr," loc   blink   id		  (name)      flags	  nargs\n");
   for (i = 0; i < ghsize; i++)
      for (gptr = ghash[i]; gptr != NULL; gptr = gptr->g_blink)
         fprintf(stderr,"%5d  %5d  %5d	%20s  %7o   %8d\n", gptr-gtable,
		gptr->g_blink, gptr->g_name, gptr->g_name,
		gptr->g_flag, gptr->g_nargs);
   fflush(stderr);
   }

/*
 * cdump displays constant symbol table to stdout.
 */

novalue cdump()
   {
   register int i;
   register struct tcentry *cptr;

   fprintf(stderr,"Dump of constant symbol table (%d entries)\n",ctfree-ctable);
   fprintf(stderr," loc   blink   id		  (name)      flags\n");
   for (i = 0; i < chsize; i++)
      for (cptr = chash[i]; cptr != NULL; cptr = cptr->c_blink)
         fprintf(stderr,"%5d  %5d  %5d	%20s  %7o\n", cptr-ctable,
		cptr->c_blink, cptr->c_name, cptr->c_name, cptr->c_flag);
   fflush(stderr);
   }
#endif					/* DeBugTrans */

/*
 * alcloc allocates a local symbol table entry, fills in fields with
 *  specified values and returns offset of new entry.  
 */
static struct tlentry *alcloc(blink, name, flag)
struct tlentry *blink;
char *name;
int flag;
   {
   register struct tlentry *lp;

   if (lfree >= &ltable[lsize])
      tsyserr("out of local symbol table space");
   lp = lfree++;
   lp->l_blink = blink;
   lp->l_name = name;
   lp->l_flag = flag;
   return lp;
   }

/*
 * alcglob allocates a global symbol table entry, fills in fields with
 *  specified values and returns offset of new entry.  
 */
static struct tgentry *alcglob(blink, name, flag, nargs)
struct tgentry *blink;
char *name;
int flag, nargs;
   {
   register struct tgentry *gp;

   if (gfree >= &gtable[gsize])
      tsyserr("out of global symbol table space");
   gp = gfree++;
   gp->g_blink = blink;
   gp->g_name = name;
   gp->g_flag = flag;
   gp->g_nargs = nargs;
   return gp;
   }

/*
 * alclit allocates a constant symbol table entry, fills in fields with
 *  specified values and returns offset of new entry.  
 */
static struct tcentry *alclit(blink, name, len, flag)
struct tcentry *blink;
char *name;
int len, flag;
   {
   register struct tcentry *cp;

   if (ctfree >= &ctable[csize])
      tsyserr("out of constant table space");
   cp = ctfree++;
   cp->c_blink = blink;
   cp->c_name = name;
   cp->c_length = len;
   cp->c_flag = flag;
   return cp;
   }

/*
 * lout dumps local symbol table to fd, which is a .u1 file.
 */
novalue lout(fd)
FILE *fd;
   {
   register int i;
   register struct tlentry *lp;

   i = 0;
   for (lp = ltable; lp < lfree; lp++)
      writecheck(fprintf(fd, "\tlocal\t%d,%06o,%s\n",
         i++, lp->l_flag, lp->l_name));
   }

/*
 * cout dumps constant symbol table to fd, which is a .u1 file.
 */
novalue cout(fd)
FILE *fd;
   {
   register int l;
   register char *c;
   register struct tcentry *cp;
   int i;

   i = 0;
   for (cp = ctable; cp < ctfree; cp++) {
      writecheck(fprintf(fd, "\tcon\t%d,%06o", i++, cp->c_flag));
      if (cp->c_flag & F_IntLit)
         writecheck(fprintf(fd, ",%d,%s\n", strlen(cp->c_name), cp->c_name));
      else if (cp->c_flag & F_RealLit)
         writecheck(fprintf(fd, ",%s\n", cp->c_name));
      else {
         c = cp->c_name;
         l = cp->c_length - 1;
         writecheck(fprintf(fd, ",%d", l));
         while (l--)
            writecheck(fprintf(fd, ",%03o", *c++ & 0377));
         writecheck(putc('\n', fd));
         }
      }
   }

/*
 * rout dumps a record declaration for name to file fd, which is a .u2 file.
 */
novalue rout(fd,name)
FILE *fd;
char *name;
   {
   register int i;
   register struct tlentry *lp;

   writecheck(fprintf(fd, "record\t%s,%d\n", name, (int)(lfree-ltable)));
   i = 0;
   for (lp = ltable; lp < lfree; lp++)
      writecheck(fprintf(fd, "\t%d,%s\n", i++, lp->l_name));
   }

/*
 * gout writes various items to fd, which is a .u2 file.  These items
 *  include: implicit status, tracing activation, link directives,
 *  and the global table.
 */
novalue gout(fd)
FILE *fd;
   {
   register int i;
   register char *name;
   register struct tgentry *gp;
   struct lfile *lfl;
   
   if (uwarn)
      name = "error";
   else
      name = "local";
   writecheck(fprintf(fd, "impl\t%s\n", name));
   if (trace)
      writecheck(fprintf(fd, "trace\n"));
   
   lfl = lfiles;
   while (lfl) {

#if MVS
      writecheck(fprintf(fd,"link\t%s\n",lfl->lf_name));
#else					/* MVS */
      writecheck(fprintf(fd,"link\t%s.u1\n",lfl->lf_name));
#endif					/* MVS */

      lfl = lfl->lf_link;
      }
   lfiles = 0;
   writecheck(fprintf(fd, "global\t%d\n", (int)(gfree-gtable)));
   i = 0;
   for (gp = gtable; gp < gfree; gp++)
      writecheck(fprintf(fd, "\t%d,%06o,%s,%d\n", i++, gp->g_flag,
         gp->g_name, gp->g_nargs));
   }
#endif					/* VarTran */
