/***********************************
  (C) Copyright 1992-1993; dit/upm
   Distributed under the conditions stated in the
   TOPO General Public License (see file LICENSE)
 ***********************************
 $Log: lexana.c,v $
 * Revision 2.18  1993/06/15  12:49:18  lotos
 * check for too long lines
 * save first useful line in an annotation
 * count number of lines in an annotation
 *
 * Revision 2.17  1993/01/18  18:04:54  lotos
 * distribution issues
 *
 * Revision 2.16  1993/01/12  14:10:50  lotos
 * use conf.h for portability
 * remove tbil.hh
 *
 * Revision 2.15  1992/11/19  18:27:27  lotos
 * bug fix
 *
 * Revision 2.14  1992/11/17  17:46:55  lotos
 * add source line information in 'i' and 'stop' nodes
 *
 * Revision 2.13  1992/09/02  14:47:27  lotos
 * keep annotations as independent (and sorted!) nodes in AST
 *
 * Revision 2.12  92/02/21  17:11:16  lotos
 * flags -t -w -n
 *
 * Revision 2.11  92/01/14  15:22:43  lotos
 * distribution issues
 *
 * Revision 2.10  92/01/13  16:24:11  lotos
 * make SymbolTable case insensitive
 * make keyword search case insensitive
 *
 * Revision 2.9  92/01/10  15:20:43  lotos
 * modified for ophuscation
 *
 * Revision 2.8  92/01/10  15:00:25  lotos
 * change to lower case *every* identifier
 *
 * Revision 2.7  91/08/28  15:52:19  lotos
 * added colours: internal, lexical, lexicalifpossible, using,
 *                constructor, nonconstructor, and Einfix
 * EOF may happen while scanning an annotation
 *
 * Revision 2.6  91/05/03  18:50:21  lotos
 * special identifiers may have _*, but not ending it
 *
 * Revision 2.5  91/04/12  11:32:08  lotos
 * make sprintf compatible for System V
 *
 * Revision 2.4  91/04/08  09:17:57  lotos
 * allow for /bin/cpp processed specs as input
 * fix bug in trailing '_' in identifiers
 *
 * Revision 2.3  91/02/28  17:17:48  lotos
 * rule 'premiss ::= _value_expression' removed
 * consequences: colours c_bool, c_rectrue, c_true removed
 * unique identifier per class generated
 *
 *
 * Revision 2.2  90/11/05  16:05:17  lotos
 * fig bug about newlines in annotations
 *
 * Revision 2.1  90/10/30  14:12:35  lotos
 * compressed AST
 * use of hardwired colours
 * compressed value expressions
 * annotations to colours
 *
 * Revision 1.4  90/06/04  15:23:51  lotos
 * if then else fi, removed
 * final match, removed
 * last ';' of eqns, made optional
 * executable comments are always lists
 * minor changes to shut lint up
 * adclr -> set_attr
 *
 * Revision 1.3  90/03/16  17:39:13  lotos
 * cosmetics
 *
 * Revision 1.2  90/01/29  13:06:40  lotos
 * fixing license details
 *
 * Revision 1.1  90/01/24  14:38:09  lotos
 * Initial revision
 *
 ***********************************/

#ifndef lint
static char rcsid[]= "$Id: lexana.c,v 2.18 1993/06/15 12:49:18 lotos Exp $";
#endif

# include "swbus.h"

# 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	int	fcomm= FALSE;
PUBLIC	char*	comm;

PRIVATE	int	kwsize= 38;
PRIVATE struct {
	char*	kw;
	int	tk;
} kwtable [] = {
"accept",       ACCEPT,
"actualizedby", ACTUALIZEDBY,
"any",          ANY,
"behavior",     BEHAVIOUR,
"behaviour",    BEHAVIOUR,
"choice",       CHOICE,
"endlib",       ENDLIB,
"endproc",      ENDPROC,
"endspec",      ENDSPEC,
"endtype",      ENDTYPE,
"eqns",         EQNS,
"exit",         EXIT,
"for",          FOR,
"forall",       FORALL,
"formaleqns",   FORMALEQNS,
"formalopns",   FORMALOPNS,
"formalsorts",  FORMALSORTS,
"hide",         HIDE,
"i",            I,
"in",           IN,
"is",           IS,
"let",          LET,
"library",      LIBRARY,
"noexit",       NOEXIT,
"of",           OF,
"ofsort",       OFSORT,
"opnnames",     OPNNAMES,
"opns",         OPNS,
"par",          PAR,
"process",      PROCESS,
"renamedby",    RENAMEDBY,
"sortnames",    SORTNAMES,
"sorts",        SORTS,
"specification",SPECIFICATION,
"stop",         STOP,
"type",         STYPE,
"using",        USING,
"where",        WHERE
};

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

PRIVATE void	get     ();
PRIVATE int	lookfkw ();
PRIVATE int	isspec	();
PRIVATE void	eatcomment ();
PRIVATE void	eatexc	();
PRIVATE void	buildcom ();
/*--------------------------------------------------------------------*/

PRIVATE	void
get ()
{
  if (ch == '\n') {
    ibp= 0;
    if (fgets (inbuf, BUFSIZ, sfp) == NULL) {
      inbuf[0]= EOF;
      return;
    }
    lineno++;
    if (strlen (inbuf) > (BUFSIZ-4)) {
      (void) fprintf (stderr, "%s:%d:", sfile, lineno);
      (void) fprintf (stderr, "line too long\n");
      exit (1);
    }

    if (flagl)
      (void)fprintf (lfp, "%5d\t%s", lineno, inbuf);
    if (inbuf[0] == '#') {
      if (strncmp (inbuf, "# line", 6) == 0) {
	(void)sscanf(inbuf, "# line %d \"%s\"\n",
		     &lineno,
		     sfile);
	lineno--;
	sfile[strlen(sfile)-1]= '\0';
	nfname= STHadd(sfile, SymbolTable, hidtbl, FALSE);
	inbuf[0]= '\n';
	get ();
      }
      else if (strncmp (inbuf, "#line", 5) == 0) {
	(void)sscanf(inbuf, "#line %d \"%s\"\n",
		     &lineno,
		     sfile);
	lineno--;
	sfile[strlen(sfile)-1]= '\0';
	nfname= STHadd(sfile, SymbolTable, hidtbl, FALSE);
	inbuf[0]= '\n';
	get ();
      }
      else if (sscanf (inbuf, "#%d \"%s\"\n", &lineno, sfile) == 2) {
	lineno--;
	sfile[strlen(sfile)-1]= '\0';
	nfname= STHadd(sfile, SymbolTable, hidtbl, FALSE);
	inbuf[0]= '\n';
	get ();
      }
    }
  }
  else ibp++;
  tend= ibp;
}

PRIVATE int
lookfkw	(key)
	char*	key;
{
  /* KJT 20/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;
    if (flagt)
      cmp= strcmp (key, kwtable[idx].kw);
    else
      cmp= cstrcmp (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	int
isspec	(c)
	char	c;
{
	switch (c) {
	  case '#' :
	  case '%' :
	  case '&' :
	  case '*' :
	  case '+' :
	  case '-' :
	  case '.' :
	  case '/' :
	  case '<' :
	  case '=' :
	  case '>' :
	  case '@' :
	  case '\\' :
	  case '^' :
	  case '~' :
	  case '{' :
	  case '}' :
		return TRUE ;
	}
	return FALSE ;
}


PRIVATE	void
buildcom (buf, cmflag, cmbuf)
	char*	buf;
	int	cmflag;
	char**	cmbuf;
{
	char*	pc;

    pc= (char*) strchr (buf, '\n');
    if (pc) *pc= '\0';
    if (cmflag) {
	  pc= *cmbuf;
	  *cmbuf= malloc ((unsigned)(strlen (*cmbuf) + strlen (buf) + 1));
	  (void) strcpy (*cmbuf, pc);
	  (void) strcat (*cmbuf, " ");
	  (void) strcat (*cmbuf, buf);
	  (void) free (pc);
    }
    else{
	  *cmbuf= str_alloc (buf);
	  cmflag= TRUE;
	}
}


PRIVATE	void
eatcomment ()
{
	int     comment;
	int	cstart, cend;
	int	abslin;
	int     exc, dumpit;
	long    position;       /* file position */
	int     nlin;           /* number of lines */
	char    textc[BUFSIZ];  /* to store comments */

  abslin= lineno;
  exc= (nnext == '|') && !flage;
  dumpit= flagc || exc;
  cstart= ibp;
  nlin= 0;
  get ();
  comment= 1;
  while (comment != 0) {
     get ();
     if ((ch == '(') && (next == '*')) {
	if (! flage) comment++;
	get ();
	get ();
     }
     else if ((ch == '*') && (next == ')')) {
	comment--;
	get ();
	get ();
	cend= ch;
     }
     if (ch == '\n') {
	if (dumpit) {
	   (void) fprintf (cfp, "%s", &inbuf[cstart]);
	   nlin++;
	}
	cstart= 0;
     }
     else if (ch == EOF) {
	if (flagl) {
	   (void) fprintf (lfp, "%5d:\t", lineno);
	   (void) fprintf (lfp, "EOF found while scanning comment ");
	   (void) fprintf (lfp, "that started in line %d\n", abslin);
	   (void) fclose (lfp);
	}
	(void) fprintf (stderr, "%s:%d:", sfile, abslin);
	(void) fprintf (stderr, "EOF found while scanning comment\n");
	exit (1);
     }
     else if (dumpit && comment == 0) {
	   ch= '\0';
	   (void) fprintf (cfp, "%s\n", &inbuf[cstart]);
	   nlin++;
	   ch= cend;
     }
  }
  if (dumpit){
     position= ftell (cfp);
     if (exc){
	(void) sprintf (textc, "%d %ld %d\n", ECOMM, position, nlin);
     }
     else{
	(void) sprintf (textc, "%d %ld %d\n", COMM, position, nlin);
	buildcom (textc, fcomm, &comm);
     }
  }
}


PRIVATE	void
eatexc	(tok)
	token*	tok;
{
	char*	key;
	char*	pval;
	char*	val;
	unsigned vsize;
	char c;
	int abslin;

  abslin= lineno;
  get ();
  get ();
  get ();
  while (isspace (ch)) get ();
  key= &ch;
  while (!isspace (ch)) get ();
  c= inbuf[ibp];
  inbuf[ibp]= '\0';
  tok->yylval.ckey= str_alloc(key);
  inbuf[ibp]= c;
  get ();
  while (isspace (ch))
    get ();
  tok->yylval.line= newI2 (lineno, nfname);
  tok->yylval.name= lineno;
  pval= &ch;
  vsize= BUFSIZ;
  val= malloc(vsize);
  *val= '\0';
  for (;;){
    switch (ch) {
     case '|':
       if (next == '*' && nnext == ')') {
	 inbuf[ibp]= 0;
	 while (strlen (val) + strlen (pval) >= vsize) {
	   vsize+= BUFSIZ;
	   val= realloc (val, vsize);
	 }
	 (void) strcat (val, pval);
	 tok->yylval.name= 1 + lineno - tok->yylval.name;
	 key= val+strlen(val)-1;
	 while (key != val && isspace (*key)) {
	   if (*key == '\n')
	     tok->yylval.name--;
	   key--;
	 }
	 *(key+1)= 0;
	 tok->yylval.cval= str_alloc(val);
	 free (val);
	 ibp+= 3;
	 return;
       }
       get ();
       break;
     case '\n':
	  while (strlen(val) + strlen(pval) >= vsize){
	      vsize += BUFSIZ;
	      val= realloc (val, vsize);
	  }
	  (void) strcat (val, pval);
	  get ();
	  pval= &ch;
	  break;
     case EOF:
	if (flagl) {
	   (void) fprintf (lfp, "%5d:\t", lineno);
	   (void) fprintf (lfp, "EOF found while scanning comment ");
	   (void) fprintf (lfp, "that started in line %d\n", abslin);
	   (void) fclose (lfp);
	}
	(void) fprintf (stderr, "%s:%d:", sfile, abslin);
	(void) fprintf (stderr, "EOF found while scanning annotation\n");
	exit (1);
     default:
	  get ();
	  break;
    }
  }
}

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


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

  if (flagl) {
    (void) fprintf (lfp, "%5d\t", lineno);
    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, "%s:%d: %s at %.*s\n",
		    sfile, lineno, mess,
		    tend-tstart, inbuf+tstart);
  ++errorcount;
}

PUBLIC  token*
gtk     ()
{
	/* KJT 14/02/12: Changed
	token	tkvar;
	token*	tk= &tkvar;
	*/
	token* tk = (token*) malloc(sizeof(token));
	int	chend;

  while (isspace(ch))
    get ();

  tstart= ibp;
  if (isalpha(ch)){
    do{
      if (flagt && isupper(ch))
	ch= tolower(ch);
      tend= ++ibp;
    } while (isalnum(ch) || ch == '_');
    /*
       Still, previous loop may read too many '_',
       that are allowed within an identifier,
       but the trailing character must be alphanumeric.
    */
    while (inbuf[ibp-1] == '_')
      tend= --ibp;
    chend= ch;
    ch= '\0';
    tk->type= lookfkw (&inbuf[tstart]);
    if (tk->type == 0)
    {
       tk->type= IDENTIFIER;
       tk->yylval.name= STHadd (&inbuf[tstart], SymbolTable, hidtbl, FALSE);
       if (flagw &&
	   strcmp (&inbuf[tstart], SymbolTable->data[tk->yylval.name]))
	 (void) fprintf (stderr,
			 "lfe: case anomaly, both <%s> and <%s>\n",
			 &inbuf[tstart],
			 SymbolTable->data[tk->yylval.name]);
    }
    tk->yylval.line= newI2(lineno,nfname);
    ch= chend;
    return tk;
  }
  if (isdigit(ch)){
    do{
      if (flagt && isupper(ch))
	ch= tolower(ch);
      tend= ++ibp;
    } while (isalnum(ch) || ch == '_' );
    /*
       Still, previous loop may read too many '_',
       that are allowed within an identifier,
       but the trailing character must be alphanumeric.
    */
    while (inbuf[ibp-1] == '_')
      tend= --ibp;
    chend= ch;
    ch= '\0';
    tk->type= SPECIAL;
    tk->yylval.name= STHadd (&inbuf[tstart], SymbolTable, hidtbl, FALSE);
     if (flagw &&
	 strcmp (&inbuf[tstart], SymbolTable->data[tk->yylval.name]))
       (void) fprintf (stderr,
		       "lfe: case anomaly, both <%s> and <%s>\n",
		       &inbuf[tstart],
		       SymbolTable->data[tk->yylval.name]);
    tk->yylval.line= newI2(lineno,nfname);
    ch= chend;
    return tk;
  }
  switch (ch){
    case '#' :
    case '%' :
    case '&' :
    case '*' :
    case '+' :
    case '-' :
    case '.' :
    case '/' :
    case '<' :
    case '=' :
    case '>' :
    case '@' :
    case '\\' :
    case '^' :
    case '~' :
    case '{' :
    case '}' :
     switch (ch){
       case '-' : if (next == '>' && (isspec(nnext) == FALSE)){
		     tk->type= ARROW;
		     get ();
		     get ();
		     return tk;
		  }
		  break;
       case '>' : if (next == '>' && (isspec(nnext) == FALSE)){
		     tk->type= ENABLE;
		     get ();
		     get ();
		     return tk;
		  }
		  break;
       case '=' : if (isspec(next) == FALSE){
		     tk->type= '=';
		     get ();
		     return tk;
		  }
		  else if (next == '>' && (isspec(nnext) == FALSE)){
		     tk->type= IMPLICATION;
		     get ();
		     get ();
		     return tk;
		  }
		  break;
     }
     do{
       get ();
     } while (isspec(ch));
     chend= ch;
     ch= '\0';
     tk->type= SPECIAL;
     tk->yylval.name= STHadd (&inbuf[tstart], SymbolTable, hidtbl, FALSE);
     if (flagw &&
	 strcmp (&inbuf[tstart], SymbolTable->data[tk->yylval.name]))
       (void) fprintf (stderr,
		       "lfe: case anomaly, both <%s> and <%s>\n",
		       &inbuf[tstart],
		       SymbolTable->data[tk->yylval.name]);
     tk->yylval.line= newI2(lineno,nfname);
     ch= chend;
     return tk;
    case '|' : if (next == '['){
		  get ();
		  get ();
		  tk->type= LEFT_PAR;
		  return tk;
	       }
	       if (next == '|'){
		  get ();
		  get ();
		  if (ch == '|'){
		      get ();
		      tk->type= INTERLEAVING;
		      return tk;
		  }
		  tk->type= SYNCHRONIZATION;
		  return tk;
	       }
	       break;
    case '[' : if (next == ']') {
		  get ();
		  get ();
		  tk->type= FAT_BAR;
		  return tk;
	       }
	       if (next == '>') {
		  get ();
		  get ();
		  tk->type= DISABLE;
		  return tk;
	       }
	       break;
    case ':' : if (next == '=') {
		  get ();
		  get ();
		  tk->type= DEFINITION;
		  return tk;
	       }
	       break;
    case ']' : if (next == '|') {
		  get ();
		  get ();
		  tk->type= RIGHT_PAR;
		  return tk;
	       }
	       break;
    case '(' : if (next == '*'){
		  if (nnext == '|' && !flage){
		       tk->type= EXCOMM;
		       eatexc (tk);
		       return tk;
		  }
		  else{
		       eatcomment ();
		       return gtk ();
		  }
	       }
	       break;
    case EOF : tk->type= 0;
	       return tk;
    case '\n': get ();
	       return gtk ();
  }
  tk->type= ch;
  get ();
  return tk;
}
