text: /*
  	HCDEF.C
  
  	contains CheckInitializer, CheckSymbol and Def... routines
  	for the Hugo Compiler
  
  	Copyright (c) 1995-2009 by Kent Tessman
  */
  
  
  #include "hcheader.h"
  
  
  char object_id[64];             /* name of current obj., routine, etc. */
  
  /* Object names, counter, and location in the property table */
  char **object; int *object_hash; int objectctr = 0;
  unsigned int *oprop;            /* needed for inheritance */
  
  /* Attribute names and counter */
  char *attribute[MAXATTRIBUTES]; int attribute_hash[MAXATTRIBUTES];
  int attrctr = 0;
  
  /* Property names, counter, set/not-set flags, and additive/complex flags
     (i.e., bitmasks).  Also property table size, property defaults,
     property data heap array.
  */
  char **property; int *property_hash; int propctr = 0; char *propset;
  char *propadd;
  unsigned int proptable = 0, *propdef;
  unsigned int *propdata[MAXPROPBLOCKS]; unsigned int propheap = 0;
  
  /* Label names, count, addresses */
  char **label; int *label_hash; int labelctr = 0; unsigned int *laddr;
  
  /* Routine names, count, addresses */
  char **routine; int *routine_hash; int routinectr = 0;
  unsigned int *raddr;
  
  /* Flag if routine is replaced (or count if replaced more than once) */
  char *rreplace;
  
  /* Total number of addresses and array for address storage */
  unsigned int addrctr = 0;
  struct addrstruct *addrdata[MAXADDRBLOCKS];
  
  /* Event data, including object the event is in, the address,
     and counter
  */
  unsigned int *eventin, *eventaddr; int eventctr = 0;
  
  /* Alias names, attribute/property alias is of, counter */
  char **alias; int *alias_hash; int *aliasof; int aliasctr = 0;
  
  /* Global variable names, local variable names, count for each */
  char *global[MAXGLOBALS], local[MAXLOCALS][33];
  int global_hash[MAXGLOBALS], local_hash[MAXLOCALS];
  int globalctr = 0, localctr = 0;
  char unused[MAXLOCALS];
  
  /* Global variable initial values */
  unsigned int globaldef[MAXGLOBALS];
  
  /* Constant names, values, count */
  char **constant; int *constant_hash;
  unsigned int *constantval; int constctr = 0;
  
  /* Array names, addresses in array table, length of each array, total
     size of array table, counter
  */
  char **array; int *array_hash; unsigned int *arrayaddr, *arraylen;
  unsigned int arraysize = MAXGLOBALS*2; int arrayctr = 0;
  
  
  /* CHECKINITIALIZER */
  
  static int initializer_type;
  
  int CheckInitializer(int wordnum)
  {
  	int t;
  
  	initializer_type = VALUE_T;
  	
  	t = IDWord(wordnum);
  
  	switch (t)
  	{
  		case MINUS_T:
  			KillWord(wordnum);
  			t = IDWord(wordnum);
  			return token_val*-1;
  
  		case TRUE_T:
  			return 1;
  		case FALSE_T:
  			return 0;
  
  		case AMPERSAND_T:
  		{
  			KillWord(wordnum);
  			t = IDWord(wordnum);
  			if (passnumber > 1)
  			{
  				switch (t)
  				{
  					case ROUTINE_T:
  					{
  						if ((routinectr) && token_val < routinectr)
  							return raddr[token_val];
  					}
  				}
  			}
  
  			/* Can init not-yet-known addresses for arrays in Pass1
  			   because they actually get written afterward */
  			else if (!strcmp(word[1], "array"))
  			{
  				switch (t)
  				{
  					case ROUTINE_T:
  					{
  						initializer_type = ROUTINE_T;
  						return token_val;
  					}
  				}
  			}
  
  			Error("?Address unavailable at compile-time");
  			return 0;
  		}
  
  		/* Valid initializers: */
  		case ARRAY_T:
  			token_val = arrayaddr[token_val];
  			break;
  		case ATTR_T:
  		case DICTENTRY_T:
  		case OBJECTNUM_T:
  		case PROP_T:
  		case VALUE_T:
  			break;
  
  #ifdef MATH_16BIT
  		case (-1):
  #else
  		case 0xffff:	/* see IDWord() */
  #endif
  			return 0;
  
  		default:
  		{
  			sprintf(line, "Illegal initializer:  %s", word[wordnum]);
  			Error(line);
  			return 0;
  		}
  	}
  	
  	return token_val;
  }
  
  
  /* CHECKSYMBOL */
  
  void CheckSymbol(char *a)
  {
  	int i, hash;
  	char *b = "";
  
  	if (strlen(a) > MAX_SYMBOL_LENGTH)
  		{sprintf(line, "Symbol name \"%s\" too long", a);
  		Error(line);
  		return;}
  
  	hash = FindHash(a);
  
  	/* Tokens */
  	for (i=1; i<=TOKENS; i++)
  		if (HashMatch(hash, token_hash[i], a, token[i]))
  			{b = "a token";
  			goto Checkb;}
  
  	/* Attributes */
  	for (i=0; i<attrctr; i++)
  		if (HashMatch(hash, attribute_hash[i], a, attribute[i]))
  			{b = "an attribute";
  			goto Checkb;}
  
  	/* Properties */
  	for (i=0; i<propctr; i++)
  		if (HashMatch(hash, property_hash[i], a, property[i]))
  			{b = "a property";
  			goto Checkb;}
  
  	/* Aliases */
  	for (i=0; i<aliasctr; i++)
  		if (HashMatch(hash, alias_hash[i], a, alias[i]))
  			{b = "an alias";
  			goto Checkb;}
  
  	/* Routines */
  	for (i=0; i<routinectr; i++)
  		if (HashMatch(hash, routine_hash[i], a, routine[i]))
  			{b = "a routine";
  			goto Checkb;}
  
  	/* Objects */
  	for (i=0; i<objectctr; i++)
  		if (HashMatch(hash, object_hash[i], a, object[i]))
  			{b = "an object";
  			goto Checkb;}
  
  	/* Globals */
  	for (i=0; i<globalctr; i++)
  		if (HashMatch(hash, global_hash[i], a, global[i]))
  			{b = "a global variable";
  			goto Checkb;}
  
  	/* Locals */
  	for (i=0; i<localctr; i++)
  		if (HashMatch(hash, local_hash[i], a, local[i]))
  			{b = "a local variable";
  			goto Checkb;}
  
  	/* Arrays */
  	for (i=0; i<arrayctr; i++)
  		if (HashMatch(hash, array_hash[i], a, array[i]))
  			{b = "an array";
  			goto Checkb;}
  
  	/* Constants */
  	for (i=0; i<constctr; i++)
  		if (HashMatch(hash, constant_hash[i], a, constant[i]))
  			{b = "a constant";
  			goto Checkb;}
  
  	/* Labels */
  	for (i=0; i<labelctr; i++)
  		if (HashMatch(hash, label_hash[i], a, label[i]))
  			{b = "a label";
  			goto Checkb;}
  
  	/* If b has been set */
  Checkb:
  	if (b[0]!='\0')
  	{
  		sprintf(line, "\"%s\" previously defined as %s", a, b);
  		Error(line);
  	}
  }
  
  
  /* DEFARRAY */
  
  void DefArray(void)
  {
  	unsigned int count = 0;
  	unsigned int array_location = arraysize;
  
  	/* Linking is illegal after definitions */
  	if (!linked) linked = FAILED;
  
  	Expect(3, "[ ]", "array size in brackets");
  
  	CheckSymbol(word[2]);
  
  	array[arrayctr] = MakeString(word[2]);
  	array_hash[arrayctr] = FindHash(array[arrayctr]);
  	arrayaddr[arrayctr] = arraysize;
  	if (IDWord(4)==0)
  		arraylen[arrayctr] = 0;
  	else
  		arraylen[arrayctr] = (unsigned int)token_val;
  
  	/* The size of an array entry includes two bytes for
  	   the length.
  	*/
  	arraysize = arraysize + (unsigned int)token_val*2 + 2;
  
  	arrayctr++;
  
  	if (arrayctr==MAXARRAYS)
  		{arrayctr = MAXARRAYS-1;
  		sprintf(line, "Maximum of %d arrays exceeded", MAXARRAYS);
  		Error(line);}
  
  	/* Process any default array values, remembering them for writing
  	   in Pass3()
  	*/
  	if (words > 5)
  	{
  		unsigned int val;
  
  		if (!Expect(6, "=", "end of line or array assignment(s)"))
  			goto SkipAssign;
  		KillWord(6);	/* '=' */
  		RemoveCommas();
  		while (words > 5)
  		{
  			if (count >= arraylen[arrayctr-1])
  			{
  				Error("?Too many array initializers");
  				return;
  			}
  
  			val = CheckInitializer(6);
  			if (initializer_type==ROUTINE_T)
  				RememberAddr(array_location+2+count*2, ARRAY_T+ROUTINE_T, val);
  			else
  				RememberAddr(array_location+2+count*2, ARRAY_T, val);
  			KillWord(5);
  			count++;
  		}
  	}
  SkipAssign:
  
  	if (words!=5) Error("Illegal array definition");
  }
  
  
  /* DEFATTRIBUTE */
  
  void DefAttribute(void)
  {
  	/* Linking is illegal after definitions */
  	if (!linked) linked = FAILED;
  
  	CheckSymbol(word[2]);
  
  	if (!strcmp(word[3], "alias"))
  	{
  		if (IDWord(4)!=ATTR_T)
  		{
  			sprintf(line, "Not an attribute:  %s", word[4]);
  			Error(line);
  		}
  		else
  		{
  			aliasof[aliasctr] = (unsigned)token_val;
  			alias[aliasctr] = MakeString(word[2]);
  			alias_hash[aliasctr] = FindHash(alias[aliasctr]);
  			aliasctr++;
  
  			if (aliasctr==MAXALIASES)
  				{attrctr = MAXALIASES-1;
  				sprintf(line, "Maximum of %d aliases exceeded", MAXALIASES);
  				Error(line);}
  		}
  		KillWord(4);
  		KillWord(3);
  	}
  	else
  	{
  		attribute[attrctr] = MakeString(word[2]);
  		attribute_hash[attrctr] = FindHash(attribute[attrctr]);
  		attrctr++;
  
  		if (attrctr==MAXATTRIBUTES)
  			{attrctr = MAXATTRIBUTES-1;
  			sprintf(line, "Maximum of %d attributes exceeded", MAXATTRIBUTES);
  			Error(line);}
  	}
  	if (words > 2) Error("Illegal attribute definition");
  }
  
  
  /* DEFCOMPOUND */
  
  void DefCompound(void)
  {
  	unsigned int c1, c2;
  	char c[257];
  
  	/* Linking is illegal after definitions */
  	if (!linked) linked = FAILED;
  
  	RemoveCommas();
  
  	if (words!=3) Error("Illegal compound definition");
  
  	StripQuotes(word[2]);
  	StripQuotes(word[3]);
  	c1 = AddDictionary(word[2]);
  	c2 = AddDictionary(word[3]);
  	strcpy(c, word[2]);
  	strcat(c, word[3]);
  	AddDictionary(c);
  
  	/* 2 = compound (0 = synonym, 1 = removal, 3 = punctuation) */
  	syndata[syncount].syntype = 2;
  	syndata[syncount].syn1 = c1;
  	syndata[syncount].syn2 = c2;
  	if (++syncount==MAXSPECIALWORDS)
  	{
  		syncount--;
  		sprintf(line, "Maximum of %d special words exceeded", MAXSPECIALWORDS);
  		Error(line);
  	}
  }
  
  
  /* DEFCONSTANT */
  
  void DefConstant(void)
  {
  	/* Linking is illegal after definitions */
  	if (!linked) linked = FAILED;
  
  	CheckSymbol(word[2]);
  
  	constant[constctr] = MakeString(word[2]);
  	constant_hash[constctr] = FindHash(constant[constctr]);
  
  	if (words>=3)
  		constantval[constctr] = (unsigned int)CheckInitializer(3);
  	else
  		constantval[constctr] = (unsigned int)constctr;
  
  	if (words<2 || words>3) Error("Illegal constant definition");
  
  	constctr++;
  
  	if (constctr == MAXCONSTANTS)
  		{constctr = MAXCONSTANTS-1;
  		sprintf(line, "Maximum of %d constants exceeded", MAXCONSTANTS);
  		Error(line);}
  }
  
  
  /* DEFENUM
  
  	enumerate [constants:globals] [, step [+:-:*:/] s] [, start = n]
  		{a, b, c,...}
  */
  
  void DefEnum(void)
  {
  	int i, j, s;
  	int tempwords, thisword;
  	char *tempword[256];
  
  	char firstval;
  	char enumtype = 0;      /* constants */
  	char step = '+';        /* default step type */
  	int stepby = 1;
  	int val = 0;
  	char valstring[8];
  
  	/* Linking is illegal after definitions */
  	if (!linked) linked = FAILED;
  
  	RemoveCommas();
  
  	thisword = 2;
  
  	while (words>=thisword)
  	{
  		if (!strcmp(word[thisword], "constants"))
  		{
  			enumtype = 0;
  			thisword++;
  		}
  
  		else if (!strcmp(word[thisword], "globals"))
  		{
  			enumtype = 1;
  			thisword++;
  		}
  
  		else if (!strcmp(word[thisword], "step"))
  		{
  			thisword++;
  			if (word[thisword][0]=='+' ||
  				word[thisword][0]=='-' ||
  				word[thisword][0]=='*' ||
  				word[thisword][0]=='/')
  			{
  				step = word[thisword++][0];
  			}
  			s = 1;
  			if (word[thisword][0]=='-')
  			{
  				thisword++;
  				s = -1;
  			}
  			IDWord(thisword++);
  			stepby = (int)(token_val*s);
  		}
  
  		else if (!strcmp(word[thisword], "start"))
  		{
  			Expect(++thisword, "=", "assignment following 'start'");
  			s = 1;
  			if (word[++thisword][0]=='-')
  			{
  				thisword++;
  				s = -1;
  			}
  			IDWord(thisword++);
  			val = (int)(token_val*s);
  		}
  
  		else
  		{
  			sprintf(line, "Syntax error in declaration:  %s", word[thisword]);
  			Error(line);
  			thisword++;
  		}
  	}
  
  	GetLine(0);
  	if (Expect(1, "{", "leading brace")) GetLine(0);
  
  	firstval = true;
  
  	while (word[1][0]!='}')
  	{
  		/* Get a line and copy it, because it's going to
  		   be changed.
  		*/
  		for (i=1; i<=words; i++)
  			tempword[i] = word[i];
  		tempwords = words;
  
  		word[1] = "enumerate";
  		word[2] = tempword[1];
  		words = 3;
  
  		if (enumtype==1)                /* globals */
  			word[words++] = "=";
  
  		/* Count the number of words to be deleted from
  		   the original line.
  		*/
  		j = 2;
  
  		s = 1;  /* default positive sign */
  
  		if ((tempwords>=2) && tempword[2][0]!=',')
  		{
  			if (tempword[2][0]!='=')
  			{
  				words-=2;
  				Error("Expecting ',' or '=' after token");
  				words+=2;
  			}
  
  			word[words] = tempword[3];
  			if (word[words][0]=='-')
  			{
  				s = -1;
  				word[++words] = tempword[4];
  			}
  			IDWord(words);
  			val = (int)(token_val);
  
  			j+=((s==1)?2:3);
  		}
  		else if (!firstval)
  		{
  			switch (step)
  			{
  				case '+':  val += stepby; break;
  				case '-':  val -= stepby; break;
  				case '*':  val *= stepby; break;
  				case '/':  val /= stepby; break;
  			}
  		}
  
  		/* Discrete values in a line of code must be positive
  		   integers
  		*/
  		if (val < 0)
  		{
  			s = -1;
  			word[words++] = "-";
  			val*=-1;
  		}
  
  		itoa(val, valstring, 10);
  		word[words] = valstring;
  
  		/* The current value must now be made negative if it was
  		   preceded by a '-'
  		*/
  		if (s==-1)
  		{
  			val*=-1;
  		}
  
  		switch (enumtype)
  		{
  			case 0:  DefConstant(); break;
  			case 1:  DefGlobal(); break;
  		}
  		firstval = false;
  
  		for (i=1; i<=tempwords; i++)
  			word[i] = tempword[i];
  		words = tempwords;
  
  		for (i=1; i<=j; i++)
  			KillWord(1);
  
  		if (words==0) GetLine(0);
  	}
  }
  
  
  /* DEFGLOBAL */
  
  void DefGlobal(void)
  {
  	/* Linking is illegal after definitions */
  	if (!linked) linked = FAILED;
  
  	CheckSymbol(word[2]);
  
  	global[globalctr] = MakeString(word[2]);
  	global_hash[globalctr] = FindHash(global[globalctr]);
  
  	/* If a default value is given */
  	if (word[3][0]=='=')
  	{
  		globaldef[globalctr] = (unsigned int)CheckInitializer(4);
  		KillWord(4);
  		KillWord(3);
  	}
  
  	if (words > 2)
  		Error("Illegal global variable definition");
  
  	globalctr++;
  	if (globalctr == MAXGLOBALS)
  		{globalctr = MAXGLOBALS-1;
  		sprintf(line, "Maximum of %d globals exceeded", MAXGLOBALS);
  		Error(line);}
  }
  
  
  /* DEFLOCALS
  
  	If <asargs> is true, then we're defining local variables as
  	routine arguments.
  */
  
  void DefLocals(int asargs)
  {
  	int i;
  	unsigned int j;
  
  	/* Linking is illegal after definitions */
  	if (!linked) linked = FAILED;
  
  	i = 2;
  
  	while (i<=words)
  	{
  		if (localctr==MAXLOCALS)
  		{
  			localctr = MAXLOCALS-1;
  			sprintf(line, "Maximum of %d locals exceeded", MAXLOCALS);
  			Error(line);
  			return;
  		}
  
  		if (word[i][0]==',') i++;
  
  		CheckSymbol(word[i]);
  
  		strcpy(local[localctr], word[i]);
  		local_hash[localctr] = FindHash(local[localctr]);
  		unused[localctr] = 1;
  
  		/* Write a label for the local variable name if we're
  		   building a debuggable file, so that the textual name
  		   of the local can be recovered by the debugger
  		*/
  		if (builddebug)
  		{
  			WriteCode(DEBUGDATA_T, 1);
  			WriteCode(VAR_T, 1);
  			WriteCode(strlen(local[localctr]), 1);
  			for (j=0; j<strlen(local[localctr]); j++)
  				WriteCode(local[localctr][j], 1);
  		}
  
  		/* local variable assignment */
  		if (word[i+1][0]=='=')
  		{
  			if (words < i+2) goto LocalAssignmentError;
  
  			if (asargs)
  				Error("?Argument preassignment overrides runtime initialization");
  
  			/* write "<local:n> = <val>" */
  			WriteCode(VAR_T, 1);
  			WriteCode(MAXGLOBALS+localctr, 1);
  			WriteCode(EQUALS_T, 1);
  			WriteCode(VALUE_T, 1);
  			WriteCode((unsigned int)CheckInitializer(i+2), 2);
  			WriteCode(EOL_T, 1);
  
  			i+=3;
  
  			if ((i<words) && word[i][0]!=',')
  			{
  LocalAssignmentError:
  				Error("Expecting single constant value as assignment");
  			}
  		}
  		else i++;
  
  		localctr++;
  	}
  }
  
  
  /* DEFOTHER
  
  	Use the same basic frame to define routines, events, and objects
  	(since they essentially look the same):
  
  		definition
  		{...}
  */
  
  void DefOther(void)
  {
  	char other[33], oname[33];
  	int i;
  	int blank = 0;
  	int starting_line = totallines;
  
  	/* Linking is illegal after definitions */
  	if (!linked) linked = FAILED;
  
  	if (!strcmp(word[1], "replace"))
  	{
  		if (token_val==ROUTINE_T) strcpy(other, "routine");
  		else strcpy(other, "object");
  	}
  	else strcpy(other, word[1]);
  
  	if (!strcmp(word[1], "event"))
  	{
  		if (words==1) strcpy(oname, "global event");
  		else if (words==2) strcpy(oname, word[2]);
  		else strcpy(oname, word[3]);
  	}
  
  	if (!strcmp(other, "routine"))
  	{
  		if (words==1 || (words > 2 && word[3][0]!='('))
  			Error("Illegal routine declaration");
  		else strcpy(oname, word[2]);
  	}
  	else if (!strcmp(other, "event"))
  	{
  		if (words > 3)
  			Error("Illegal event declaration");
  	}
  	else if (!strcmp(other, "resource"))
  	{
  		/* Handle everything in CreateResourceFile() from Pass2() */
  	}
  
  	/* If not a routine or an event, it must be an object */
  	else
  	{
  		if (words==1 || words > 3)
  		{
  			if ((words==1) || (strcmp(word[4], "in") && strcmp(word[4], "nearby")))
  				Error("Illegal object declaration");
  		}
  		strcpy(oname, word[2]);
  	}
  
  	endofgrammar = 1;
  	nest = 0;
  	do
  	{
  		GetLine(0);
  
  		if (word[1][0]=='#')
  		{
  			if (!strcmp(word[1], "#include") || !strcmp(word[2], "#link"))
  				goto NoClosingBrace;
  			CompilerDirective();
  		}
  		else
  		{
  			PrinttoAll();
  
  			if (word[1][0]=='~')
  			{
  				CheckSymbol(word[2]);
  				label[labelctr] = MakeString(word[2]);
  				label_hash[labelctr] = FindHash(label[labelctr]);
  				labelctr++;
  
  				if (labelctr == MAXLABELS)
  					{labelctr = MAXLABELS-1;
  					sprintf(line, "Maximum of %d labels exceeded", MAXLABELS);
  					Error(line);}
  
  				if (strcmp(word[3], ""))
  					Error("New line expected after label");
  			}
  
  			for (i=1; i<=words; i++)
  			{
  				if (word[i][0]=='{') nest++;
  				if (word[i][0]=='}') nest--;
  			}
  		}
  
  		/* Check for suspicious number of blank lines.  32 lines of
  		   white space is a bit much to intentionally leave in a
  		   program, don't you think?
  		*/
  		if (buffer[0]=='\0')
  		{
  			blank++;
  			if (blank > 32)
  			{
  NoClosingBrace:
  				sprintf(line, "Closing brace missing in %s:  %s", other, oname);
  
  				/* Force termination */
  				aborterror = true;
  				totallines = starting_line;
  
  				Error(line);
  			}
  		}
  	} while (nest > 0);
  }
  
  
  /* DEFPROPERTY */
  
  void DefProperty(void)
  {
  	char additive = 0;
  	char complex = 0;
  	int i;
  
  	/* Linking is illegal after definitions */
  	if (!linked) linked = FAILED;
  
  	for (i=1; i<=2; i++)
  	{
  		if (word[3][0]!='\0')
  		{
  			if (!strcmp(word[3], "$additive"))
  				{additive = true;
  				KillWord(3);}
  			if (!strcmp(word[3], "$complex"))
  				{complex = true;
  				KillWord(3);}
  		}
  	}
  
  	CheckSymbol(word[2]);
  
  	if (!strcmp(word[3], "alias"))
  	{
  		if (IDWord(4)!=PROP_T)
  		{
  			sprintf(line, "Not a property:  %s", word[4]);
  			Error(line);
  		}
  		else
  		{
  			aliasof[aliasctr] = (unsigned)token_val + MAXATTRIBUTES;
  			alias[aliasctr] = MakeString(word[2]);
  			alias_hash[aliasctr] = FindHash(alias[aliasctr]);
  			aliasctr++;
  
  			if (aliasctr==MAXALIASES)
  				{attrctr = MAXALIASES-1;
  				sprintf(line, "Maximum of %d aliases exceeded", MAXALIASES);
  				Error(line);}
  		}
  		KillWord(4);
  		KillWord(3);
  	}
  	else
  	{
  		property[propctr] = MakeString(word[2]);
  		property_hash[propctr] = FindHash(property[propctr]);
  		token_val = 0;
  		propdef[propctr] = 0;
  		propadd[propctr] = 0;
  
  		if (additive) propadd[propctr] = propadd[propctr] | (char)ADDITIVE_FLAG;
  		if (complex) propadd[propctr] = propadd[propctr] | (char)COMPLEX_FLAG;
  
  		/* If default value for the property is provided */
  		if (word[3][0]!='\0')
  		{
  			propdef[propctr] = CheckInitializer(3);
  		}
  		KillWord(3);
  		propctr++;
  
  		if (propctr==MAXPROPERTIES)
  			{attrctr = MAXPROPERTIES-1;
  			sprintf(line, "Maximum of %d properties exceeded", MAXPROPERTIES);
  			Error(line);}
  	}
  
  	if (words > 2) Error("Illegal property definition");
  }
  
  
  /* DEFREMOVALS */
  
  void DefRemovals(void)
  {
  	unsigned int s;
  
  	/* Linking is illegal after definitions */
  	if (!linked) linked = FAILED;
  
  	RemoveCommas();
  
  	while (words >= 2)
  	{
  
  		StripQuotes(word[2]);
  		s = AddDictionary(word[2]);
  
  		/* 1 = removal, 3 = punctuation (0 = synonym, 2 = compound) */
  		if (!strcmp(word[1], "removal"))
  			syndata[syncount].syntype = 1;
  		else
  			syndata[syncount].syntype = 3;
  		syndata[syncount].syn1 = s;
  		syndata[syncount].syn2 = 0;
  		if (++syncount==MAXSPECIALWORDS)
  		{
  			syncount--;
  			sprintf(line, "Maximum of %d special words exceeded", MAXSPECIALWORDS);
  			Error(line);
  		}
  
  		KillWord(2);
  	}
  }
  
  
  /* DEFSYNONYM */
  
  void DefSynonym(void)
  {
  	unsigned int s, sof;
  
  	/* Linking is illegal after definitions */
  	if (!linked) linked = FAILED;
  
  	Expect(3, "for", "keyword");
  
  	if (words!=4) Error("Illegal synonym definition");
  
  	StripQuotes(word[2]);
  	StripQuotes(word[4]);
  
  	s = AddDictionary(word[2]);
  	sof = AddDictionary(word[4]);
  
  	/* 0 = synonym, (1 = removal, 2 = compound, 3 = punctuation) */
  	syndata[syncount].syntype = 0;
  	syndata[syncount].syn1 = s;
  	syndata[syncount].syn2 = sof;
  	if (++syncount==MAXSPECIALWORDS)
  	{
  		syncount--;
  		sprintf(line, "Maximum of %d special words exceeded", MAXSPECIALWORDS);
  		Error(line);
  	}
  }
  
  
  /* FINDHASH
  
  	This is actually a bit of a misnomer, since it isn't really a
  	hash table per se that is being used.  What is done is to come
  	up with an _almost_ unique key for each token.  Only if the
  	hash key matches against another token does a (more expensive)
  	strcmp() need to be performed to verify the match.
  */
  
  int FindHash(char *a)
  {
  	int i, hash = 0;
  
  	for (i=0; a[i]!='\0'; i++)
  		hash += tolower(a[i])*HASH_KEY;
  
  	return hash;
  }
  