/*  asnlext.c
* ===========================================================================
*
*                            PUBLIC DOMAIN NOTICE                          
*               National Center for Biotechnology Information
*                                                                          
*  This software/database is a "United States Government Work" under the   
*  terms of the United States Copyright Act.  It was written as part of    
*  the author's official duties as a United States Government employee and 
*  thus cannot be copyrighted.  This software/database is freely available 
*  to the public for use. The National Library of Medicine and the U.S.    
*  Government have not placed any restriction on its use or reproduction.  
*                                                                          
*  Although all reasonable efforts have been taken to ensure the accuracy  
*  and reliability of the software and data, the NLM and the U.S.          
*  Government do not and cannot warrant the performance or results that    
*  may be obtained by using this software or data. The NLM and the U.S.    
*  Government disclaim all warranties, express or implied, including       
*  warranties of performance, merchantability or fitness for any particular
*  purpose.                                                                
*                                                                          
*  Please cite the author in any work or product based on this material.   
*
* ===========================================================================
*
* File Name:  asnlext.c
*
* Author:  James Ostell
*
* Version Creation Date: 3/4/91
*
* $Revision: 6.10 $
*
* File Description:
*   Routines for parsing ASN.1 module definitions.  Extends asnlex.c
*
* Modifications:  
* --------------------------------------------------------------------------
* Date     Name        Description of modification
* -------  ----------  -----------------------------------------------------
* 2/11/91  Ostell      AsnModuleLink - corrected bug linking imported types
*                         to other imported types.
* 3/4/91   Kans        Stricter typecasting for GNU C and C++
* 04-20-93 Schuler     LIBCALL calling convention
*
* ==========================================================================
*/

/*****************************************************************************
*
*   asnlext.c
*   	routines of type AsnLexT..()
*   	Lexxer routines for Asn.1 specifications
*
*****************************************************************************/

#include "asnbuild.h"

#define NUMASNWORD 59         /* number of asnwords */

static CharPtr asnwords[NUMASNWORD] = {     /* primitives from asn.1 */
	"BOOLEAN",				 /*  1 */
	"INTEGER",				 /*  2 */
	"BIT",					 /*  3 */
	"OCTET",				 /*  4 */
	"NULL",					 /*  5 */
	"OBJECT",				 /*  6 */
	"ObjectDescriptor",		 /*  7 */
	"EXTERNAL",				 /*  8 */
	"REAL",					 /*  9 */
	"ENUMERATED",			 /* 10 */
	"SEQUENCE",				 /* 11 */
	"STRING",				 /* 12 */
	"SET",					 /* 13 */
	"OF",					 /* 14 */
	"CHOICE",                /* 15 */
	"ANY",           		/* 16 */
	"NumericString",    	/* 17 */
	"PrintableString",      /* 18 */
	"TeletexString", 		/* 19 */
	"UTF8String",           /* 20 */
	"IA5String",			/* 21 */
	"GraphicString",		/* 22 */
	"VisibleString",		/* 23 */
	"GeneralString",		/* 24 */
	"CharacterString",		/* 25 */
	"GeneralizedTime",		/* 26 */
	"UTCTime",				/* 27 */
	"DEFINITIONS",           /* 28 */
	"BEGIN",                 /* 29 */
	"END",                   /* 30 */
	"APPLICATION",           /* 31 */
	"PRIVATE",               /* 32 */
	"UNIVERSAL",             /* 33 */
	"COMPONENTS",			 /* 34 */
	"DEFAULT" ,              /* 35 */
	"FALSE",                 /* 36 */
	"TRUE",                  /* 37 */
	"IMPLICIT",              /* 38 */
	"OPTIONAL",              /* 39 */
	"EXPORTS",				/* 40 */
	"IMPORTS",				/* 41 */
	"ABSENT", 				/* 42 */
	"BY",					/* 43 */
	"COMPONENT",            /* 44 */
	"DEFINED",              /* 45 */
	"FROM",                 /* 46 */
	"INCLUDES",             /* 47 */
	"MIN",                  /* 48 */
	"MINUS-INFINITY",       /* 49 */
	"MAX",                  /* 50 */
	"PRESENT",              /* 51 */
	"PLUS-INFINITY",        /* 52 */
	"SIZE",                 /* 53 */
	"TAGS",                 /* 54 */
	"WITH",                 /* 55 */
	"IDENTIFIER",			 /* 56 */
	"StringStore",           /* 57 */      /* NCBI application type */
	"$Revision:",            /* 58 */       /* NCBI asn versions */
	"BigInt"                 /* 59 */       /* NCBI application type */
	};

static AsnPrimType asnprimtypes[] = {    
	{BOOLEAN_TYPE, "BOOLEAN", 0, 1 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{INTEGER_TYPE, "INTEGER", 0, 2 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{BITS_TYPE, "BIT STRING", 0, 3 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{OCTETS_TYPE, "OCTET STRING", 0, 4 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{NULL_TYPE, "NULL", 0, 5 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{OBID_TYPE, "OBJECT IDENTIFIER", 0, 6 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{OBDES_TYPE, "ObjectDescriptor", 0, 7 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{EXTERNAL_TYPE, "EXTERNAL", 0, 8 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{REAL_TYPE, "REAL", 0, 9 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{ENUM_TYPE, "ENUMERATED", 0, 10 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{SEQ_TYPE, "SEQUENCE", 0, 16 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{SEQOF_TYPE, "SEQUENCE OF", 0, 16 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{SET_TYPE, "SET", 0, 17 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{SETOF_TYPE, "SET OF", 0, 17 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{CHOICE_TYPE, "CHOICE", 0, -1 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{ANY_TYPE, "ANY", 0, -1 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{NUMERICSTRING_TYPE, "NumericString", 0, 18 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{PRINTABLESTRING_TYPE, "PrintableString", 0, 19 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{TELETEXSTRING_TYPE, "TeletexString", 0, 20 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{UTF8STRING_TYPE, "UTF8String", 0, 12 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{IA5STRING_TYPE, "IA5String", 0, 22 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{GRAPHICSTRING_TYPE, "GraphicString", 0, 25 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{VISIBLESTRING_TYPE, "VisibleString", 0, 26 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{GENERALSTRING_TYPE, "GeneralString", 0, 27 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{CHARACTERSTRING_TYPE, "CharacterString", 0, 28 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{GENTIME_TYPE, "GeneralizedTime", 0, 24 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{UTCTIME_TYPE, "UTCTime", 0, 23 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL}};

static AsnPrimType asnapptypes[] = {         /* application wide types */
	{STRSTORE_TYPE, "StringStore", 64, 1 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL},
	{BIGINT_TYPE, "BigInt", 64, 2 , 0, 0, 0, 0, 0, 0, NULL, NULL,NULL,0,NULL, NULL}
	};


/*****************************************************************************
*
*   AsnLoadModules(aip)
*   	reads and links several modules
*
*****************************************************************************/
NLM_EXTERN AsnModulePtr LIBCALL  AsnLoadModules (AsnIoPtr aip)
{
	AsnModulePtr amp, nextmod, currentmod = NULL;
	
	amp = NULL;
	while ((nextmod = AsnLexTReadModule(aip)) != NULL )
	{
		if (amp == NULL)
			amp = nextmod;
		else
			currentmod->next = nextmod;
		currentmod = nextmod;
	}

	AsnModuleLink(amp);      /* link up modules where possible */
	return amp;
}
					
/*****************************************************************************
*
*   AsnLexTReadModule(aip)
*
*****************************************************************************/
NLM_EXTERN AsnModulePtr AsnLexTReadModule (AsnIoPtr aip)
{
	AsnModulePtr amp;
	AsnOptionPtr aop;
	DataVal dv;

					/* get name and look for valid start */

	amp = AsnLexTStartModule(aip);
	if (amp == NULL)
		return amp;

	aop = AsnIoOptionGet(aip, OP_TYPEORDER, 0, NULL);
	if (aop == NULL)
	{
		MemSet(&dv, 0, sizeof(DataVal));
		aop = AsnIoOptionNew(aip, OP_TYPEORDER, 0, dv, NULL);
	}
	aop->data.intvalue = 1;  /* start the order counter */

			   		/* scan declarations */
	
	while (aip->token != END_TOKEN)     /* END */
	{
		if (aip->token == REF)        /* its a type assignment */
			aip->token = AsnLexTReadTypeAssign(aip, amp);
		/**************************** not implemented yet ********
		else if (token == IDENT) 
			aip->token = AsnReadDefinedValue(aip, amp);
		**********************************************************/
		else
		{
			AsnIoErrorMsg(aip, 60, aip->linenumber, aip->word);
			return NULL;
		}
	}

	amp = AsnCheckModule(amp, aip);

	return amp;
}

/*****************************************************************************
*
*   Int2 AsnLexTReadTypeAssign(aip, amp)
*   	reads a type assignment for one type
*       assumes has read name already
*   	returns token to following item
*       returns 0 on error
*
*****************************************************************************/
NLM_EXTERN Int2 AsnLexTReadTypeAssign (AsnIoPtr aip, AsnModulePtr amp)
{
	AsnTypePtr atp;
	Int2 token;
	AsnOptionPtr aop;

							/* store the typereference */

	atp = AsnTypeNew(aip, amp);
	aop = AsnIoOptionGet(aip, OP_TYPEORDER, 0, NULL);
	if (aop != NULL)   /* add ordered display */
	{
		AsnOptionNew(&(atp->hints), OP_TYPEORDER, 0, aop->data, NULL);
		aop->data.intvalue++;
	}

	if ((token = AsnLexTWord(aip)) != ISDEF)
	{
		AsnIoErrorMsg(aip, 28, aip->linenumber);
		return 0;
	}
	
	token = AsnLexTWord(aip);   /* get next element */

	token = AsnLexTReadType(aip, amp, atp);

	return token;
}

static void AsnLexTAddTypeComment(AsnIoPtr aip, AsnOptionPtr PNTR aopp)
{
	AsnOptionPtr aop, aop2;
	DataVal dv;

	aop = NULL;                 /* check for comments before this type */
	aop2 = NULL;
	while ((aop = AsnIoOptionGet(aip, OP_COMMENT, 0, aop)) != NULL)
	{
		dv.ptrvalue = aop->data.ptrvalue;
		aop->data.ptrvalue = NULL;
		aop2 = AsnOptionNew(aopp, OP_COMMENT, 0, dv, DefAsnOptionFree);
	}
	if (aop2 != NULL)   /* got some */
		AsnIoOptionFree(aip, OP_COMMENT, 0);   /* clear them out */
}
/*****************************************************************************
*
*   Int2 AsnLexTReadType(aip, amp, atp)
*   	reads a type assignment for one type
*       assumes has read name already
*       assumes has read next input item after name
*   	returns token to following item
*       returns 0 on error
*
*****************************************************************************/
NLM_EXTERN Int2 AsnLexTReadType (AsnIoPtr aip, AsnModulePtr amp, AsnTypePtr atp)
{
	Int2 isa;
	Boolean first;
	AsnValxNodePtr avnp, prevavnp;
	Int2 token;

	token = aip->token;

	if (token == COMMENT_TOKEN)
	{
		AsnLexTAddTypeComment(aip, &(atp->hints));
		token = AsnLexTWord(aip);
	}

	if (token == IMPLICIT_TOKEN) 
	{
		atp->implicit = TRUE;
		token = AsnLexTWord(aip);
	}

	if (token == START_TAG)                      /* TAG */
	{
		token = AsnLexTWord(aip);
		if (ISA_TAGCLASS(token))
		{
			switch (token)
			{
				case UNIV_TAG_TOKEN:
					atp->tagclass = TAG_UNIVERSAL;
					break;
				case PRIV_TAG_TOKEN:
					atp->tagclass = TAG_PRIVATE;
					break;
				case AP_TAG_TOKEN:
					atp->tagclass = TAG_APPLICATION;
					break;
			}
			token = AsnLexTWord(aip);
		}
		else
			atp->tagclass = TAG_CONTEXT;

		if (token == NUMBER)
		{
			atp->tagnumber = (Int2) AsnLexInteger(aip);
			token = AsnLexTWord(aip);
			if (token != END_TAG)
			{
				AsnIoErrorMsg(aip, 61, aip->linenumber);
				return 0;
			}
			else
				token = AsnLexTWord(aip);
		}
		else if (token == IDENT)
		{
			AsnIoErrorMsg(aip, 62, aip->linenumber);
			return 0;
		}
		else
		{
			AsnIoErrorMsg(aip, 63, aip->linenumber);
			return 0;
		}
	}

	atp->type = AsnGetType(aip, amp);
	isa = atp->type->isa;
	
	token = AsnLexTWord(aip);   /* read next token */

	AsnLexTAddTypeComment(aip, &(atp->hints));  /* check for stored comments */

	if (token == COMMENT_TOKEN)
		token = AsnLexTWord(aip);
            
 					/*********** SubType Processing *********************/
	if (token == OPEN_PAREN)
	{
		AsnIoErrorMsg(aip, 64, aip->linenumber);
		return 0;
	}
	                 /******************* process various types *********/
	
	if (isa < 400)       /* operate on builtin types */
		switch (isa)
	{
		case INTEGER_TYPE:           /* check for named integers */
			if (token != START_STRUCT)
				break;
		case ENUM_TYPE:
			if (token != START_STRUCT)
			{
				AsnIoErrorMsg(aip, 65, aip->linenumber);
				return 0;
			}

									 /* read named integers */
			first = TRUE;
			avnp = NULL;
			prevavnp = NULL;
			token = AsnLexTWord(aip);
			if (token == COMMENT_TOKEN)
			{
			    AsnLexTAddTypeComment(aip, &(atp->hints));  /* check for stored comments */
			    token = AsnLexTWord(aip);
		        }
			    
			while (token != END_STRUCT)
			{
				avnp = AsnValxNodeNew(avnp, VALUE_ISA_NAMED_INT);

				if (! first)
				{
					if (token != COMMA)
					{
						AsnIoErrorMsg(aip, 66, aip->linenumber);
						return 0;
					}
					else
					{
						token = AsnLexTWord(aip);
						if (token == COMMENT_TOKEN)
						{
							AsnLexTAddTypeComment(aip, &(prevavnp->aop));
							token = AsnLexTWord(aip);
						}
					}
							
				}
				else
				{
					first = FALSE;
					atp->branch = avnp;
				}

				if (token != IDENT)
				{
					AsnIoErrorMsg(aip, 86, aip->linenumber);
					return 0;
				}
				avnp->name = AsnLexSaveWord(aip);
				token = AsnLexTWord(aip);
				if (token != OPEN_PAREN)
				{
					AsnIoErrorMsg(aip, 87, aip->linenumber);
					return 0;
				}
				avnp->intvalue = AsnLexReadInteger(aip, atp);
				token = AsnLexTWord(aip);
				if (token != CLOSE_PAREN)
				{
					AsnIoErrorMsg(aip, 88, aip->linenumber);
					return 0;
				}
				token = AsnLexTWord(aip);
				if (token == COMMENT_TOKEN)
					token = AsnLexTWord(aip);

				AsnLexTAddTypeComment(aip, &(avnp->aop));  /* check for stored comments */
				prevavnp = avnp;
			}
			token = AsnLexTWord(aip);
			if (token == COMMENT_TOKEN)
			{
				AsnLexTAddTypeComment(aip, &(prevavnp->aop));
				token = AsnLexTWord(aip);
			}
			break;
		case SETOF_TYPE:			/* create branch for type */
		case SEQOF_TYPE:
			atp->branch = AsnElementTypeNew(NULL);   /* get unnamed element for type */
			token = AsnLexTReadType(aip, amp, (AsnTypePtr) atp->branch);
			break;
		case SEQ_TYPE:
		case SET_TYPE:
			atp->branch = AsnLexTReadElementTypeList(aip, amp, atp);
			token = aip->token;
			break;
		case CHOICE_TYPE:
			atp->branch = AsnLexTReadAlternativeTypeList(aip, amp, atp);
			token = aip->token;
			break;
	}
	atp->resolved = TRUE;
	return token;
}

/*****************************************************************************
*
*	AsnTypePtr AsnLexTReadElementTypeList(aip, amp)
*   	assumes has read first { already
*   	returns token to following element
*
*****************************************************************************/
NLM_EXTERN AsnTypePtr AsnLexTReadElementTypeList (AsnIoPtr aip, AsnModulePtr amp, AsnTypePtr parent)
{
	Int2 token;
	AsnTypePtr atp, atp1, atplast, atp2;
	AsnValxNodePtr avnp;

	token = aip->token;
	atp1 = NULL;
	atplast = NULL;

	if (token != START_STRUCT)
	{
		AsnIoErrorMsg(aip, 37, aip->linenumber);
		return NULL;
	}
	token = AsnLexTWord(aip);
	if (token == COMMENT_TOKEN)
	{
		if (parent != NULL)
			AsnLexTAddTypeComment(aip, &(parent->hints));
		token = AsnLexTWord(aip);
	}

	while (token != END_STRUCT)
	{
		if (atp1 != NULL)        /* not the first */
		{
			if (token != COMMA)
			{
				AsnIoErrorMsg(aip, 66, aip->linenumber);
				return NULL;
			}
			else
			{
				token = AsnLexTWord(aip);
				if (token == COMMENT_TOKEN)
				{
					if (atplast != NULL)
						AsnLexTAddTypeComment(aip, &(atplast->hints));
					token = AsnLexTWord(aip);
				}
			}
		}

		if (token == COMPS_TOKEN)        /* COMPONENTS OF */
		{
			AsnIoErrorMsg(aip, 93, aip->linenumber);
			return NULL;
		}
		else if (token != IDENT)	     /* not named */
		{
			atp = AsnElementTypeNew(NULL);
		}
		else					         /* named Type */
		{
			atp = AsnElementTypeNew(aip);
			token = AsnLexTWord(aip);
			if (token == COMMENT_TOKEN)
			{
				AsnLexTAddTypeComment(aip, &(atp->hints));
				token = AsnLexTWord(aip);
			}
		}
								/* add to chain */
		if (atp1 == NULL)
			atp1 = atp;
		else
			atplast->next = atp;

		token = AsnLexTReadType(aip, amp, atp);

		if (token == OPTIONAL_TOKEN)         /* OPTIONAL */
		{
			atp->optional = TRUE;
			token = AsnLexTWord(aip);
		}
		else if (token == DEFAULT_TOKEN)    /* DEFAULT */
		{
			atp->hasdefault = TRUE;
			avnp = AsnValxNodeNew(NULL, VALUE_ISA_BOOL);
			atp->defaultvalue = avnp;
						/* read the default value */
			atp2 = AsnFindBaseType(atp);

			if (atp2 == NULL)
			{
				AsnIoErrorMsg(aip, 95, AsnErrGetTypeName(atp->name), aip->linenumber);
				return NULL;
			}

			switch (atp2->type->isa)
			{
				case BOOLEAN_TYPE:
					avnp->intvalue = (Int4) AsnLexReadBoolean(aip, atp2);
					break;
				case INTEGER_TYPE:
				case ENUM_TYPE:
					avnp->valueisa = VALUE_ISA_INT;
					avnp->intvalue = AsnLexReadInteger(aip, atp2);
					break;
				case VISIBLESTRING_TYPE:
					avnp->valueisa = VALUE_ISA_PTR;
					avnp->name = (CharPtr) AsnLexReadString(aip, atp2);
					break;
				case REAL_TYPE:
					avnp->valueisa = VALUE_ISA_REAL;
					avnp->realvalue = AsnLexReadReal(aip, atp2);
					break;
				default:
					AsnIoErrorMsg(aip, 94, AsnErrGetTypeName(atp->name));
					return NULL;
			}
			token = AsnLexTWord(aip);
		}
		atplast = atp;

		AsnLexTAddTypeComment(aip, &(atplast->hints));
		if (token == COMMENT_TOKEN)
			token = AsnLexTWord(aip);

	}
	token = AsnLexTWord(aip);   /* read following item */
	if (token == COMMENT_TOKEN)
	{
		if (atplast != NULL)
			AsnLexTAddTypeComment(aip, &(atplast->hints));
		token = AsnLexTWord(aip);
	}
	return atp1;
}

/*****************************************************************************
*
*	AsnTypePtr AsnLexTReadAlternativeTypeList(aip, amp, parent)
*   	assumes has read first { already
*   	returns token to following element
*
*****************************************************************************/
NLM_EXTERN AsnTypePtr AsnLexTReadAlternativeTypeList (AsnIoPtr aip, AsnModulePtr amp, AsnTypePtr
						      parent)
{
	Int2 token;
	AsnTypePtr atp, atp1, atplast;

	token = aip->token;
	atp1 = NULL;
	atplast = NULL;

	if (token != START_STRUCT)
	{
		AsnIoErrorMsg(aip, 37, aip->linenumber);
		return NULL;
	}
	token = AsnLexTWord(aip);
	if (token == COMMENT_TOKEN)
	{
		if (parent != NULL)
			AsnLexTAddTypeComment(aip, &(parent->hints));
		token = AsnLexTWord(aip);
	}

	while (token != END_STRUCT)
	{
		if (atp1 != NULL)        /* not the first */
		{
			if (token != COMMA)
			{
				AsnIoErrorMsg(aip, 66, aip->linenumber);
				return NULL;
			}
			else
			{
				token = AsnLexTWord(aip);
				if (token == COMMENT_TOKEN)
				{
					if (atplast != NULL)
						AsnLexTAddTypeComment(aip, &(atplast->hints));
					token = AsnLexTWord(aip);
				}
			}
		}

		if (token != IDENT)	     /* not named */
		{
			atp = AsnElementTypeNew(NULL);
		}
		else					         /* named Type */
		{
			atp = AsnElementTypeNew(aip);
			token = AsnLexTWord(aip);
			if (token == COMMENT_TOKEN)
			{
				AsnLexTAddTypeComment(aip, &(atp->hints));
				token = AsnLexTWord(aip);
			}
		}
								/* add to chain */
		if (atp1 == NULL)
			atp1 = atp;
		else
			atplast->next = atp;

		token = AsnLexTReadType(aip, amp, atp);
		atplast = atp;

		AsnLexTAddTypeComment(aip, &(atplast->hints));
		if (token == COMMENT_TOKEN)
			token = AsnLexTWord(aip);
	}
	token = AsnLexTWord(aip);   /* read following item */
	if (token == COMMENT_TOKEN)
	{
		if (atplast != NULL)
			AsnLexTAddTypeComment(aip, &(atplast->hints));
		token = AsnLexTWord(aip);
	}
	return atp1;
}

/*****************************************************************************
*
*   AsnModulePtr AsnLexTStartModule(aip)
*   	if $Revision: in comment in first line of file, puts the string
*        containing the revision number in amp->filename
*       if --$Revision: X.YY
*     			stores "YY"
*       if --$Revision: X.Y  stores "XY"
*   	if --$Revision: X
*               stores "X"
*
*****************************************************************************/
NLM_EXTERN AsnModulePtr AsnLexTStartModule (AsnIoPtr aip)
{
	Int2 token;
	AsnModulePtr amp;
	Int2 asntype;
	AsnTypePtr atp;
	Boolean wasref;
	Char buf[10];
	CharPtr from, to;
	Int2 len;

	amp = (AsnModulePtr) MemNew(sizeof(AsnModule));

	token = AsnLexTWord(aip);    /* get the module name or $Revision: */

        while (token == COMMENT_TOKEN)
        {
                token = AsnLexTWord(aip);
	}


	if (token == REVISION_TOKEN)            /* revision of file */
	{
		to = buf;
		*to = '\0';
                len = (Int2)(aip->wordlen);
		if (len == 4)     /* x.yy */
		{
			from = aip->word + 2;
			len -= 2;
		}
		else
			from = aip->word;

		if (len)
		{
			if (IS_DIGIT(*from))
			{
				*to = *from; to++; from++;len--;
			}
			while ((len) && (! IS_DIGIT(*from)))
			{
				len--; from++;
			}
			if ((len) && (IS_DIGIT(*from)))
			{
				*to = *from; to++;
			}
			*to = '\0';
			amp->filename = StringSave(buf);
		}
			
		token = AsnLexTWord(aip);     /* get the module name */

		while (token == COMMENT_TOKEN)
		{
			token = AsnLexTWord(aip);
		}
	}

	if (token != REF)
	{
		MemFree(amp->filename);
		amp = (AsnModulePtr) MemFree(amp);
		return amp;
	}

	if ((asntype = AsnLexTMatchToken(aip)) != 0)     /* any predefined value is wrong */
	{
		AsnIoErrorMsg(aip, 67, asnwords[asntype -1], aip->linenumber);
		return NULL;
	}

						     /* create a new module */


	amp->modulename = AsnLexSaveWord(aip);
	amp->lasttype = 400;
	amp->lastvalue = 10000;

	token = AsnLexTWord(aip);

        while (token == COMMENT_TOKEN)
        {
                token = AsnLexTWord(aip);
	}

	if (token == START_STRUCT)       /* it has an object identifier */
	{
		AsnIoErrorMsg(aip, 68);
		    /* skip object identifier */
		AsnLexSkipStruct(aip);
		token = AsnLexTWord(aip);  /* next token */
	}

        while (token == COMMENT_TOKEN)
        {
                token = AsnLexTWord(aip);
	}

	if (token != DEF_TOKEN)        /* DEFINITIONS */
	{
		AsnIoErrorMsg(aip, 69, asnwords[DEF_TOKEN - 401], aip->linenumber);
		return NULL;
	}
	else
		token = AsnLexTWord(aip);

    while (token == COMMENT_TOKEN)
    {
        token = AsnLexTWord(aip);
    }
    while (token != ISDEF && token != BEGIN_TOKEN && token != ERROR_TOKEN)
    {
        token = AsnLexTWord(aip);
    }
	if (token != ISDEF)           /* ::= */
	{
		AsnIoErrorMsg(aip, 69, "::=", aip->linenumber);
		return NULL;
	}
	else
		token = AsnLexTWord(aip);

        while (token == COMMENT_TOKEN)
        {
                token = AsnLexTWord(aip);
	}

	if (token != BEGIN_TOKEN)    /* BEGIN */
	{
		AsnIoErrorMsg(aip, 69, asnwords[BEGIN_TOKEN - 401], aip->linenumber);
		return NULL;
	}
	else
		token = AsnLexTWord(aip);

        while (token == COMMENT_TOKEN)
        {
                token = AsnLexTWord(aip);
	}

	if (token == EXPORTS_TOKEN)     /* read any EXPORTS */
	{
		wasref = FALSE;
		while ((token = AsnLexTWord(aip)) != SEMI_COLON)
		{
			if (token == REF)     /* should be REF */
			{
				if (wasref)
				{
					AsnIoErrorMsg(aip, 66, aip->linenumber);
					return NULL;
				}
				atp = AsnTypeNew(aip, amp);
				atp->exported = TRUE;
				wasref = TRUE;
			}
			else if (token == COMMA)
			{
				if (! wasref)
				{
					AsnIoErrorMsg(aip, 70, aip->linenumber);
					return NULL;
				}
			   	wasref = FALSE;
			}
			else if (token != COMMENT_TOKEN)
			{
				AsnIoErrorMsg(aip, 59, ' ', aip->linenumber);
				return NULL;
			}
		}
						   /* get next after semi-colon */

		token = AsnLexTWord(aip);
	}

        while (token == COMMENT_TOKEN)
        {
                token = AsnLexTWord(aip);
	}

	if (token == IMPORTS_TOKEN)             /* IMPORTS */
	{
		wasref = FALSE;
		token = AsnLexTWord(aip);

		while (token != SEMI_COLON)
		{
			if (token == REF)     /* should be REF */
			{
				atp = AsnTypeNew(aip, amp);
				atp->imported = TRUE;
				wasref = TRUE;
			}
			else if (token == COMMA)
			{
				if (! wasref)
				{
					AsnIoErrorMsg(aip, 70, aip->linenumber);
					return NULL;
				}
			   	wasref = FALSE;
			}
			else if (token == FROM_TOKEN)
			{
				if (! wasref)
				{
					AsnIoErrorMsg(aip, 71, aip->linenumber);
					return NULL;
				}
				else								/* read source module */
					token = AsnLexTWord(aip);

				if (token != REF)
				{
					AsnIoErrorMsg(aip, 72, aip->linenumber);
					return NULL;
				}
				else
				{
					atp = amp->types;
					while (atp != NULL)
					{
						if ((atp->imported == TRUE) && (atp->branch == NULL))
							atp->branch = AsnLexSaveWord(aip);
						atp = atp->next;
					}
				}
				wasref = FALSE;
			}
			else if (token == START_STRUCT)
			{
				AsnIoErrorMsg(aip, 73, aip->linenumber);
				AsnLexSkipStruct(aip);
				token = AsnLexTWord(aip);
				wasref = FALSE;
			}
			else if (token != COMMENT_TOKEN)
				AsnIoErrorMsg(aip, 96, aip->linenumber);

			token = AsnLexTWord(aip);
		}
						   /* get next after semi-colon */
		token = AsnLexTWord(aip);
	}

        while (token == COMMENT_TOKEN)
        {
                token = AsnLexTWord(aip);
	}

	if (token == EXPORTS_TOKEN)     /* EXPORTS out of place */
		AsnIoErrorMsg(aip, 97, aip->linenumber);

	return amp;
}


/*****************************************************************************
*
*   Int2 AsnLexTMatchToken(aip)
*
*****************************************************************************/
NLM_EXTERN Int2 AsnLexTMatchToken (AsnIoPtr aip)
{
	CharPtr word;
	register int i;
	Int2 len;

	word = aip->word;
	len = aip->wordlen;

	for (i = 0; i < NUMASNWORD; i++)
	{
		if (StrMatch(asnwords[i], word, len))
			return i+1;
	}
	return 0;     /* not found */
}
static void AsnLexTAddComment(CharPtr cbeg, CharPtr cend, AsnIoPtr aip)
{
	Char tchar;
	CharPtr comment;
	DataVal dv;

	tchar = *cend;
	*cend = '\0';
	comment = StringSave(cbeg);
	*cend = tchar;
	dv.ptrvalue = comment;

	AsnIoOptionNew(aip, OP_COMMENT, 0, dv, NULL);
	return;
}
/*****************************************************************************
*
*   Int2 AsnLexTWord(aip)
*   	reads words, punctuation, and asn keywords with 2 parts
*   	returns tokens defined at top
*
*****************************************************************************/
NLM_EXTERN Int2 AsnLexTWord (AsnIoPtr aip)
{
	register CharPtr pos;
	register int len;
	Int1 state;
	Int2 token, asntype, linepos;
	int done;
	Boolean first = FALSE, hitnewline = FALSE;
	CharPtr commentptr;

	if (! aip->bytes)   /* no data loaded */
	{
		hitnewline = TRUE;
		first = TRUE;
		AsnIoGets(aip);
	}
		
	linepos = aip->linepos;
	pos = aip->linebuf + linepos;
	state = aip->state;
	len = 0;
	token = -1;

	while (*pos == '\n' || *pos == '\r')    /* skip empty lines */
	{
		hitnewline = TRUE;
		pos = AsnIoGets(aip);

		if (pos == NULL)
			return EOF_TOKEN;
	}
	
	if (state == IN_STRING_STATE)
	{
		aip->word = pos;
		if (* pos == '\"')    /* end of string */
		{
			token = END_STRING;
			pos++;
			state = 0;        /* reset state */
		}
		else
		{
			token = IN_STRING;
			while ((* pos != '\"') && (* pos != '\n') && (* pos != '\r'))
			{
				pos++; len++;
			}

			if ((*pos != '\n') && (*pos != '\r') && (* (pos + 1) == '\"')) /* internal quote */
			{
				len++;        /* include in previous string */
				pos += 2;     /* point to rest of string */
			}
		}
	}
	else if (state == IN_BITHEX_STATE)
	{
		aip->word = pos;
		if (*pos == '\'')  			  /* end of binhex */
		{
			state = 0;              /* set to normal */
			pos++;                       /* move past quote */
			while (IS_WHITESP(*pos))
				pos++;
			if (* pos == 'H')
				token = OCTETS;
			else if (* pos == 'B')
				token = ASNBITS;
			else
			{
				AsnIoErrorMsg(aip, 58, aip->linenumber);
				token = ERROR_TOKEN;
			}
		}
		else
		{
			token = IN_BITHEX;
			while ((* pos != '\'') && (* pos != '\n') && (* pos != '\r'))
			{
				pos++; len++;
			}
		}   
	}
	else              /* normal scanning */
	{
		done = 0;
		while (! done)
		{
			while (* pos <= ' ')     /* skip leading white space */
			{
				if (*pos == '\n' || *pos == '\r')
				{
					hitnewline = TRUE;
					pos = AsnIoGets(aip);

					if (pos == NULL)
						return EOF_TOKEN;
				}
				else
					pos++;
			}
			done = 1;
			
			while (done && (*pos == '-') && (*(pos+1) == '-'))   /* skip comments */
			{
				pos += 2;
				if (first)   /* could be revision */
				{
				 	first = FALSE;
					if (StrMatch(asnwords[57], pos, 10))  /* $Revision: */
					{
						token = REVISION_TOKEN;
						pos += 10;
						while (IS_WHITESP(*pos))
							pos++;
						aip->word = pos;
						while (IS_DIGIT(*pos))       /* eg. 1.2 */
						{
							len++;
							pos++;
						}
						if (*pos == '.')        /* take after . if present */
						{
							pos++;
                                                        len++;
							while (IS_DIGIT(*pos))
							{
								len++;
								pos++;
							}
						}
					}
				}
				commentptr = pos;
				
				done = 0;
				while (! done)   /* skip to end of comment */
				{
					if ((*pos == '-') && (*(pos +1) == '-'))
					{
						if (token != REVISION_TOKEN)
						{
							AsnLexTAddComment(commentptr, pos, aip);
							if ((! hitnewline) && (aip->token != COMMENT_TOKEN))
								token = COMMENT_TOKEN;
						}
						pos += 2;
						done = 1;
					}
					else if (*pos == '\n' || *pos == '\r')
					{
						if (token != REVISION_TOKEN)
						{
							AsnLexTAddComment(commentptr, pos, aip);
							if ((! hitnewline) && (aip->token != COMMENT_TOKEN))
								token = COMMENT_TOKEN;
						}

						done = 1;
					}
					else
						pos++;
				}

				if ((token == REVISION_TOKEN) || (token == COMMENT_TOKEN))
				{
					aip->linepos = pos - aip->linebuf;
					aip->state = state;
					aip->wordlen = len;
					aip->token = token;
					return token;
				}

				if (*pos <= ' ')
					done = 0;
				else
					done = 1;
			}
		}

		aip->word = pos;
		if (* pos == '\"')
		{
			token = START_STRING;
			state = IN_STRING_STATE;
		}
		else if (* pos == '\'')
		{
			token = START_BITHEX;
			state = IN_BITHEX_STATE;
		}
		else if (* pos == ',')
			token = COMMA;
		else if (* pos == '{')
			token = START_STRUCT;
		else if (* pos == '}')
			token = END_STRUCT;
		else if (* pos == '[')
			token = START_TAG;
		else if (* pos == ']')
			token = END_TAG;
		else if (* pos == '(')
			token = OPEN_PAREN;
		else if (* pos == ')')
			token = CLOSE_PAREN;
		else if (* pos == ';')
			token = SEMI_COLON;
		else if (* pos == ':')
		{
			if ((*(pos + 1) == ':') && (*(pos + 2) == '='))
			{
				token = ISDEF;
				pos += 2;
				len = 3;
			}
			else
			{
				AsnIoErrorMsg(aip, 59, *pos, aip->linenumber);
				token = ERROR_TOKEN;
			}
		}
		else if (IS_UPPER(*pos))  /* a reference or keyword */
		{
			token = REF;
			while ((IS_ALPHANUM(*pos)) || (*pos == '-'))
			{
				pos++; len++;
			}

			aip->wordlen = len;
			asntype = AsnLexTMatchToken(aip);    /* check types */
			if (asntype)          /* did it match ? */
			{
				if ((asntype > 27) && (asntype < 57))   /* not a primitive type */
				{
					token = asntype + 400;   /* make a keyword type */
					if (asntype == COMPS_TOKEN)  /* COMPONENTS OF */
					{
						if ((*(pos + 1) == 'O') &&
							(*(pos + 2) == 'F') &&
							(IS_WHITESP(*(pos+3))))
						{
							pos += 3;    /* move past OF */
							len += 3;
						}
						else
							AsnIoErrorMsg(aip, 89, aip->linenumber);
					}
				}
				else if (asntype == 57)    /* StringStore */
					token = STRSTORE_TYPE;
				else if (asntype == 59)    /* BitInt */
					token = BIGINT_TYPE;
				else
				{
					switch (asntype)
					{
						case 3:				/* BIT */
						case 4:				/* OCTET */
							if (! StrMatch(asnwords[11], (pos+1), 6))
								AsnIoErrorMsg(aip, 90, aip->linenumber);
							pos += 7;       /* move past STRING */
							len += 7;
							break;
						case 11:			/* SEQUENCE */
						case 13:			/* SET */
							if ((*(pos + 1) == 'O') &&
								(*(pos + 2) == 'F'))
							{
								asntype++;   /* SET or SEQ OF */
								pos += 3;
								len += 3;
								if (! IS_WHITESP(*pos))
									AsnIoErrorMsg(aip, 91, aip->linenumber);
							}
							break;
						case 6:				/* OBJECT */
							if ((! StrMatch(asnwords[55], (pos+1), 10)))  /* IDENTIFIER */
								AsnIoErrorMsg(aip, 92, aip->linenumber);
							pos += 11;
							len += 11;
							break;
						default:
							break;
					}
					token = asntype + 300;   /* change to point at type */
				}
			}
			pos--;    /* move back for increment at end */
			len--;
		}
		else if (IS_LOWER(*pos))  /* an identifier or valuereference */
		{
			token = IDENT;
			while ((IS_ALPHANUM(*pos)) || (*pos == '-'))
			{
				pos++; len++;
			}
			pos--;		  /* move back for increment at end */
			len--;
		}
		else if ((IS_DIGIT(*pos)) || ((*pos == '-') && (IS_DIGIT(*(pos+1)))))
		{
			token = NUMBER;
			if (*pos == '-')
			{
				pos++; len++;
			}

			while (IS_DIGIT(*pos))
			{
				pos++; len++;
			}
			pos--;    /* move back for increment at end */
			len--;
		}
		else
		{
			AsnIoErrorMsg(aip, 59, *pos, aip->linenumber);
			token = ERROR_TOKEN;
		}
		len++; pos++;     /* move over last symbol */
	}
	aip->linepos = pos - aip->linebuf;
	aip->state = state;
	aip->wordlen = len;
	aip->token = token;
	return token;
}

/*****************************************************************************
*
*   AsnTypePtr AsnGetType(aip, amp)
*   	return pointer to type of last element read
*
*****************************************************************************/
NLM_EXTERN AsnTypePtr AsnGetType (AsnIoPtr aip, AsnModulePtr amp)
{
	AsnTypePtr atp;
	AsnPrimTypePtr aptp;
	Int2 wordlen;
	Int2 token;

	wordlen = aip->wordlen;
	token = aip->token;

	if (ISA_ASNTYPE(aip->token))     /* a primitive */
	{
		aptp = asnprimtypes;
		while (aptp->isa != token)
			aptp++;
		return (AsnTypePtr)aptp;
	}
	else if (ISA_BASETYPE(aip->token))    /* an application type */
	{
		aptp = asnapptypes;
		while (aptp->isa != token)
			aptp++;
		return (AsnTypePtr)aptp;
	}

	if (aip->token != REF)        /* not a type */
	{
		AsnIoErrorMsg(aip, 98, aip->word, aip->linenumber);
		return NULL;
	}

		/**************** not a primitive - do we have it already? *******/

	atp = amp->types;
	while (atp != NULL)
	{
		if (StrMatch(atp->name, aip->word, wordlen))   /* it matches */
		{
			if (atp->imported == TRUE)   /* reference to imported type */
				atp->resolved = TRUE;
			return atp;
		}
		else
			atp = atp->next;
	}

		/*************** not defined - add unresolved node *************/

	atp = AsnTypeNew(aip, amp);

	return atp;
}

/*****************************************************************************
*
*   AsnTypePtr AsnTypeNew(aip, amp)
*
*****************************************************************************/
NLM_EXTERN AsnTypePtr AsnTypeNew (AsnIoPtr aip, AsnModulePtr amp)
{
	AsnTypePtr atp, next;
	Int2 token;

	next = amp->types;
	atp = amp->types;

	if ((token = AsnLexTMatchToken(aip)) != 0)    /* it's a reserved word */
		AsnIoErrorMsg(aip, 67, asnwords[token - 1], aip->linenumber);

	while (next != NULL)
	{
		atp = next;
		next = atp->next;
		if (StrMatch(atp->name, aip->word, aip->wordlen))     /* already used name */
		{
			if (! atp->resolved)     /* needs to be resolved yet, so OK */
				return atp;
			else                     /* already defined */
			{
				AsnIoErrorMsg(aip, 99, AsnErrGetTypeName(atp->name), aip->linenumber);
			}
		}
	}

	next = (AsnTypePtr) MemNew(sizeof(AsnType));
	next->tagclass = TAG_NONE;    /* default tag class */
	next->name = AsnLexSaveWord(aip);
	amp->lasttype++;       /* increment defined types isa */
	next->isa = amp->lasttype;

	if (atp == NULL)    /* first one */
		amp->types = next;
	else
		atp->next = next;

	return next;
}

/*****************************************************************************
*
*   AsnTypePtr AsnElementTypeNew(aip)
*
*****************************************************************************/
NLM_EXTERN AsnTypePtr AsnElementTypeNew (AsnIoPtr aip)
{
	AsnTypePtr atp;

	atp = (AsnTypePtr) MemNew(sizeof(AsnType));
	atp->tagclass = TAG_NONE;    /* default tag class */
	atp->tagnumber = -1;         /* not yet assigned */
	if (aip != NULL)
		atp->name = AsnLexSaveWord(aip);

	return atp;
}

/*****************************************************************************
*
*   AsnModulePtr AsnCheckModule(amp, aip)
*
*****************************************************************************/
NLM_EXTERN AsnModulePtr AsnCheckModule (AsnModulePtr amp, AsnIoPtr aip)
{
	AsnTypePtr atp;
	Boolean ok = TRUE;

	atp = amp->types;              /* check that everything got resolved */
	while (atp != NULL)
	{
		if (atp->resolved != TRUE)
		{
			if (atp->imported != TRUE)    /* only imported not resolved */
			{
				AsnIoErrorMsg(aip, 100,	AsnErrGetTypeName(atp->name), amp->modulename);
				ok = FALSE;
			}
			else
				AsnIoErrorMsg(aip, 101,	AsnErrGetTypeName(atp->name), amp->modulename);
		}

		if (atp->imported == TRUE)
		{
			if (atp->branch == NULL)
			{
				AsnIoErrorMsg(aip, 102,	AsnErrGetTypeName(atp->name), amp->modulename);
				ok = FALSE;
			}
		}

		atp = atp->next;
	}

	AsnSetTags(amp->types);      /* assign tags */

	if (ok)
		return amp;
	else
		return NULL;
}

/*****************************************************************************
*
*   void AsnSetTags(atp)
*   	automatically assign CONTEXT tags to named elements
*       if (tagnumer == -1) then not explicitly assigned in spec
*       if (>= 0)  then tag is assumed already assigned and not reassigned
*
*****************************************************************************/
NLM_EXTERN void AsnSetTags (AsnTypePtr atp)
{
	int ctr = 0;
	Int2 isa;
	AsnTypePtr orig;

	if (atp == NULL) return;
	                                           /* find highest set tag */

	for (orig = atp; atp != NULL; atp = atp->next)
	{
		if (atp->name != NULL)
		{
			if (IS_LOWER(*atp->name))      /* identifier, not Type */
			{
				if ((atp->tagclass == TAG_NONE) ||    /* context tag set? */
					(atp->tagclass == TAG_CONTEXT))
				{
					atp->tagclass = TAG_CONTEXT;
					if (atp->tagnumber > ctr)		  /* set default higher */
						ctr = (int)(atp->tagnumber + 1);
				}
			}
		}

	}

    atp = orig;

	while (atp != NULL)
	{
		if (atp->type != NULL)
		{
			isa = atp->type->isa;
			switch (isa)
			{
				case SEQOF_TYPE:
				case SETOF_TYPE:      /* check subtypes */
				case SEQ_TYPE:
				case SET_TYPE:
				case CHOICE_TYPE:
					AsnSetTags((AsnTypePtr) atp->branch);
					break;
				default:
					break;
			}
		}
		if (atp->name != NULL)
		{
			if (IS_LOWER(*atp->name))      /* identifier, not Type */
			{
				if (atp->tagclass == TAG_CONTEXT)    /* assign it */
				{
					if (atp->tagnumber < 0)  /* not assigned */
					{
						atp->tagnumber = ctr;
						ctr++;
					}
				}
			}
		}
		atp = atp->next;
	}
	return;
}

/*****************************************************************************
*
*   AsnValxNodePtr AsnValxNodeNew(anvp, type)
*
*****************************************************************************/
NLM_EXTERN AsnValxNodePtr AsnValxNodeNew (AsnValxNodePtr avnp, Int2 type)
{
	AsnValxNodePtr newnode;

	newnode = (AsnValxNodePtr) MemNew((sizeof(AsnValxNode)));
	newnode->valueisa = type;
	if (avnp != NULL)   /* add to chain */
	{
		while (avnp->next != NULL)
			avnp = avnp->next;
		avnp->next = newnode;
	}
	return newnode;
}




