/***********************************************
 (C) Copyright 1992-1993; dit/upm
   Distributed under the conditions stated in the
   EPS General Public License (see file LICENSE)
 ***********************************************
 $Log: lexana.c,v $
 * Revision 2.6  1993/01/12  16:36:11  eps
 * portability issues
 *
 * Revision 2.5  1992/11/17  14:49:58  eps
 * fix portability details
 *
 * Revision 2.4  1992/11/17  11:48:59  eps
 * fix definition of *alloc for PC portability
 *
 * Revision 2.3  1992/01/14  15:44:26  eps
 * distribution issues
 *
 * Revision 2.2  92/01/07  11:35:11  eps
 * uniformization of lag & rag languages
 * addition of ATTRIBUTES to lag grammar
 *
 * Revision 2.1  90/10/30  08:40:56  eps
 * lag + rag -> lag
 * allowed productions are simplified (i.e. reduced)
 *
 **********************************************/

#ifndef lint
static char rcsid[]= "$Id: lexana.c,v 2.6 1993/01/12 16:36:11 eps Exp $";
#endif

# include "swbus.h"
# include <string.h>
# include <ctype.h>


/*----- scanner states -----*/
# define S_NORMAL	0
# define S_BLOCK 	1
# define S_ENDBLOCK     2
# define S_ATTRIB	4
# define S_CONDITION	8
# define S_VISIT	16
# define S_END_RULE	32
# define S_END_INC	64
/*--------------------------*/

# define ch	inbuf[ibp]
# define next	inbuf[ibp+1]
# define nnext	inbuf[ibp+2]

PRIVATE	int	ibp= 0;
PRIVATE	char	inbuf[BUFSIZ]= "\n";
PRIVATE int	tstart= 0;
PRIVATE int	tend= 0;
PRIVATE int	lineno= 0;
PRIVATE char    source[BUFSIZ]= "";
PRIVATE int	state= S_NORMAL;

PRIVATE	int	kwsize= 13;
PRIVATE struct {
	char*	kw;
	int	tk;
} kwtable [] = {
"ast",		TAST,
"attributes",	ATTRIBUTES,
"attribution",	ATTRIBUTION,
"condition",	CONDITION,
"end_include",	END_INCLUDE,
"end_language",	END_LANGUAGE,
"end_rule",	END_RULE,
"include_lag",  INCLUDE_LAG,
"include_rag",  INCLUDE_RAG,
"language",	LANGUAGE,
"rule",		RULE,
"syntax",	SYNTAX,
"visit",	VISIT
};

/*--------------------------------------------------------------------*/

PRIVATE void	get     ();
PRIVATE int	lookfkw ();
PRIVATE int	isspec	();
PRIVATE void	eatcomment ();
PRIVATE void	buildcom ();
PRIVATE void	blockstop ();

/*--------------------------------------------------------------------*/


PRIVATE	void
get	()
{
  if (ch == '\n'){
       ibp= 0;
       if (fgets(inbuf, BUFSIZ, sfp) == NULL){
	   inbuf[0]= EOF;
	   return;
       }
       lineno++;
       if (flagl)
	   (void)fprintf (lfp, "%5d%5d\t%s", lineno, nrules, inbuf);
       if (inbuf[0] == '#'){
	   if (strncmp (inbuf, "# line", 6) == 0){
	       (void)sscanf(inbuf, "# line %d \"%s\"\n",
				&lineno,
				filename);
	       filename[strlen(filename)-1]= '\0';
	       lineno--;
	       inbuf[0]= '\n';
	       get ();
	   }
	   else if (strncmp (inbuf, "#line", 5) == 0){
	       (void)sscanf(inbuf, "#line %d \"%s\"\n",
				&lineno,
				filename);
	       filename[strlen(filename)-1]= '\0';
	       lineno--;
	       inbuf[0]= '\n';
	       get ();
	   }
       }
  }
  else ibp++;
  tend= ibp;
}

PRIVATE int
lookfkw	(key)
	char*	key;
{
  /* KJT 22/01/23: added "int" type */
  register int loweri= 0;
  register int upperi= kwsize-1;
  register int idx;
  register int cmp;

  for (;;){
    idx= (loweri + upperi) / 2;
    cmp= strcmp(key, kwtable[idx].kw);
    if (cmp == 0)
	 return kwtable[idx].tk;	/* found */
    else if (cmp < 0){
	 if (idx == loweri) return 0;	/* not found */
	 upperi= --idx;
    }
    else{
	 if (idx == upperi) return 0;	/* not found */
	 loweri= ++idx;
    }
  }
}


PRIVATE	void
eatcomment ()
{
  do{
    get ();
  } while (ch != '\n');

}

PRIVATE	void
blockstop ()
{
	register int i;
	register unsigned bsz= 0;

  for (i= 0; i < blocktbl->size; i++)
       bsz += strlen(*(blocktbl->data + i));
  block= (char*) malloc(bsz);
  *block= '\0';
  for (i= 0; i < blocktbl->size; i++)
       (void) strcat(block, *(blocktbl->data + i));
  for (i= 0; i < blocktbl->size; i++)
       free(*(blocktbl->data + i));
  blocktbl->size= 0;
}

/******** Public Functions *********************************************/


PUBLIC  int
yyerror (mess)
	char*	mess;
{
	int	i;

  if (flagl) {
     (void) fprintf (lfp, "*****     \t");
     for (i=  0; i < tstart; i++) {
	 if (inbuf[i] == '\t')
	      (void) putc ('\t', lfp);
	 else (void) putc (' ', lfp);
     }
     for (i= tstart; i < tend; i++)
	  (void) putc ('^', lfp);
     (void) fprintf (lfp, " %s\n", mess);
  }
  else (void) fprintf (stderr, "near%6d:\t%s\n", lineno, mess);
  ++errorcount;
}


PUBLIC	int
pwar	(m)
	char*	m;
{
   if (flagl)
      (void) fprintf (lfp, "near%6d:\t(warning) %s\n", lineno, m);
   else {
      if (strcmp (source, filename) != 0) {
	 (void) fprintf (stderr, "%s:\n", filename);
	 (void) strcpy (source, filename);
      }
      (void) fprintf (stderr, "near%6d:\t(warning) %s\n", lineno, m);
   }
   return 0;
}


PUBLIC  token*
gtk     ()
{
  /* KJT 22/01/23: Changed
  token	tkvar;
  token*	tk= &tkvar;
  */
  token* tk = (token*) malloc(sizeof(token));
  /* KJT 22/01/23: added "aint" type */
  register int chend;
  register int typeid;
  register int inp;

  while (isspace(ch)){
    if (ch == '\n' && state == S_BLOCK)
	(void)STadd (&inbuf[0], blocktbl, FALSE);
    get ();
  }

  tstart= ibp;
  if (ch == '_'){
     tstart++;
     do{
       get ();
     } while (isalnum (ch) || (ch == '_'));
     chend= ch;
     ch= '\0';
     tk->yylval.name= STHadd (&inbuf[tstart], SymbolTable, hidtbl, FALSE);
     tk->yylval.line= newI2(lineno,nfname);
     ch= chend;
     tk->type= NODE;
     return tk;
  }

  typeid= 0;
  if (isalpha (ch)){
     do{
       if (isupper(ch)) typeid= typeid | 2;
       else if (islower(ch)) typeid= typeid | 4;
       get ();
     } while (isalnum (ch) || (ch == '_'));
     chend= ch;
     ch= '\0';
     tk->yylval.name= STHadd (&inbuf[tstart], SymbolTable, hidtbl, FALSE);
     tk->yylval.line= newI2(lineno,nfname);
     if ((typeid & 2) != 0 &&
	 state != S_BLOCK){	/* to put everything in lowercase keywords */
	for (inp= tstart; inp != ibp; inp++)
	    if (isupper(inbuf[inp]))
		inbuf[inp]= tolower(inbuf[inp]);
     }
	 /* keywords must be at the beginning of line */
     if (tstart == 0)
	  tk->type= lookfkw (&inbuf[tstart]);
     else tk->type= 0;
     ch= chend;
     if (tk->type == 0){
	 if ((typeid & 2) != 0 && (typeid & 4) == 0)
	      tk->type= UID;
	 else if ((typeid & 2) == 0 && (typeid & 4) != 0)
	      tk->type= LID;
	 else tk->type= ID;
     }
     else{
       switch (tk->type){
	 case INCLUDE_LAG :
	 case INCLUDE_RAG :
	      state= S_BLOCK;
	      get ();
	      break;
	 case END_INCLUDE :
	      state= S_NORMAL;
	      blockstop ();
	      break;
	 case TAST        :
	 case ATTRIBUTES  :
	 case ATTRIBUTION :
	 case CONDITION   :
	 case VISIT       :
	      if (state == S_BLOCK){
		state= S_NORMAL;
		blockstop ();
	      }
	      else{
		state= S_BLOCK;
		get ();
	      }
	      break;
	 case SYNTAX      :
	      if (state == S_BLOCK)
		blockstop ();
	      state= S_NORMAL;
	      break;
	 case END_RULE    :
	      if (state == S_BLOCK)
		blockstop ();
	      state= S_NORMAL;
	      break;
	 case RULE :
	      nrules++;
	      break;
	 default :
	      break;
       }
     }
     return tk;
  }

  switch (ch){
    case '[' :
    case ']' :
    case '+' :
    case '*' : tk->type= ch;
	       get ();
	       return tk;
    case ':' : if (next == ':' && nnext == '='){
		  get ();
		  get ();
		  get ();
		  tk->type= ASSIGN;
		  return tk;
	       }
	       break;
    case '\'': tstart= ibp;
	       do{
		  get ();
	       } while (ch != '\'');
	       get ();
	       chend= ch;
	       ch= '\0';
	       tk->yylval.name= STHadd (&inbuf[tstart], SymbolTable, hidtbl, FALSE);
	       tk->yylval.line= newI2(lineno,nfname);
	       ch= chend;
	       tk->type= STRING;
	       return tk;
    case '!' : if (ibp == 0){
		   eatcomment ();
		   return gtk ();
	       }
	       break;
    case EOF : tk->type= 0;
	       return tk;
    case '\n': if (state == S_BLOCK)
		  (void)STadd (&inbuf[0], blocktbl, FALSE);
	       get ();
	       return gtk ();
  }
  tk->type= CHAR;
  get ();
  return tk;
}
