/* 
 * $RCSfile: parse.c,v $
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 *
 * $Log: parse.c,v $
 * Revision 1.2  1996/01/27 00:13:01  slm
 * Updated copyright and version.
 *
 * Revision 1.1  1995/07/29  03:08:13  slm
 * Initial revision
 *
 * Revision 1.18.2.4  1994/06/01  20:45:46  menze
 * Fixed parsing bug in file lists, introduced in
 * source-filename-extensions revision
 *
 * Revision 1.18.2.3  1994/05/31  23:58:20  menze
 * romopt entries are semi-colon terminated (consistent with other entries)
 *
 * Revision 1.18.2.2  1994/05/31  19:56:40  menze
 * Added support for general source-filename extensions
 *
 * Revision 1.18.2.1  1994/05/05  20:30:11  menze
 * more latitude in format of protocol instance names
 *
 * Revision 1.18  1994/04/07  23:32:54  menze
 *   [ 1994/03/08          menze ]
 *   added 'romopt' and 'romfile' entries, allowing rom files and options
 *   to be statically configured at compose/compile time
 */

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>

#include "global.h"

#define BUF_SIZE 80

#define strsame(A,B) (!(strcmp((A),(B))))

static int cur;
static int done = 0;
static int debug = 0;
int fileLine = 1;
int filePosition = 0;
static enum {
    DEV_STATE, PROT_STATE, TRACE_STATE
} state;

static char *	DIRECTORY(void);
static void	EDGE(PROTOCOL *);
static char *	FILEEXT( void );
static void	FILELIST(PROTOCOL *);
static char *	FILENAME(void);
static void	FILES(PROTOCOL *);
#if 0
static int	NUMBER(void);
#endif
static ProtName	PNAME(void);
static void	PROTOCOLLIST(PROTOCOL *);
static ProtName	PROTOCOLNAME(void);
static char *	PROTOCOLSTRING( void );
static void	PROTS(PROTOCOL *);
static void	ROM(void);
static void 	S(void);
static char *	STRING(void);
static void	TBL( void );
static char *	UPTO_CHAR( char );
static void	TRACE( PROTOCOL * );
static void	TRACEVAR( PROTOCOL *p );
static void	force_char(char);
static void	force_string(char *);
static void	get(void);
static void	skip_blanks(void);
static void	skip_blanks_force( void );

#define isWhiteSpace(_c) ( (_c) == ' ' || (_c) == '\t' || (_c) == '\n' )

static void
changeState()
{
    switch( state ) {

      case DEV_STATE:
	lastDriver();
	state = PROT_STATE;
	break;

      case PROT_STATE:
	state = TRACE_STATE;
	break;
	
      case TRACE_STATE:
	errorTooManyStates();
	break;
    }
}


void
parse()
{
  if (debug)
    printf("\n>parse\n");
  get();
  S();
}


/*
 * S -> ( EDGE | TBL | @ ) , S | EPSILON
 */
static void
S()
{
    PROTOCOL	p;
    
    if (debug)
      printf("\n>S\n");
    skip_blanks();
    while (!done) {
	if (cur == '@') {
	    force_char('@');
	    skip_blanks();
	    force_char(';');
	    changeState();
	} else if (cur == 'n') {
	    /* 
	     * Must be an EDGE
	     */
	    /* 
	     * Add to the trace variable list for both protocols and
	     * edges which represent only trace variables
	     */
	    bzero((char *)&p, sizeof(PROTOCOL));
	    EDGE(&p);
	    {
		char	varName[160];
		int	instantiate = 0;
		
		if ( p.traceVar ) {
		    if ( strsame(p.traceVar, "private") ) {
			sprintf(varName, "trace%s_%s",
				p.n.name, p.n.instance);
			instantiate = 1;
		    } else {
			sprintf(varName, "trace%s", p.traceVar);
		    }
		} else {
		    sprintf(varName, "trace%s", p.n.name);
		    if ( state == DEV_STATE || state == PROT_STATE ) {
			strcat(varName, "p");
		    }
		}
		p.tv = addTraceVar(varName, p.trace, instantiate);
	    }
	    if ( state == DEV_STATE || state == PROT_STATE ) {
		/* 
		 * These are the only states where the edge really
		 * represents a protocol.  In the TRACE_VAR state, the
		 * edge is just a trace variable
		 */
		addInstance(&p);
	    }
	} else if ( state == TRACE_STATE ) {
	    if (cur == 'p') {
		/* 
		 * Must be a table entry
		 */
		TBL();
	    } else if ( cur == 'r' ) {
		ROM();
	    }
	} else {
	    syntaxErrorString("'name' or 'prottbl' or '@'");
	}
	skip_blanks();
    }
}


static void
TBL()
{
    char *	fileName;

    force_string("prottbl");
    skip_blanks();
    force_string("=");
    skip_blanks();
    fileName = FILENAME();
    if (*fileName == 0) {
	syntaxErrorString("filename");
    }
    addProtTbl(fileName);
    skip_blanks();
    force_char(';');
    skip_blanks();
}    


static void
ROM()
{
    char *	fileName;
    char *	opt;

    force_string("rom");
    if ( cur == 'f' ) {
	force_string("file");
	skip_blanks();
	force_char('=');
	skip_blanks();
	fileName = FILENAME();
	if (*fileName == 0) {
	    syntaxErrorString("filename");
	}
	skip_blanks();
	force_char(';');
	skip_blanks();
	addRomFile(fileName);
    } else if ( cur == 'o' ) {
	force_string("opt");
	skip_blanks();
	opt = UPTO_CHAR(';');
	force_char(';');
	skip_blanks();
	addRomOption(opt);
    } else {
	syntaxErrorString("file or opt");
    }
}    




#if 0

/*
 * NUMBER 		-> [0..9]*
 */
static int
NUMBER()
{
  int i = 0;

  if (debug)
    printf("\n>NUMBER\n");

  while (isdigit(cur)) {
    i = i * 10 + cur - '0';
    get();
  }
  return i;
}

#endif

/*
 * EDGE ->
 *   PNAME " " (DIRECTORY " ") (FILES " ") (PROTS " ") ";"
 */
static void
EDGE(PROTOCOL *p)
{
    if (debug)
      printf("\n>EDGE\n");
    p->n = PNAME();
    while (1) {
	if ( cur != ';' ) {
	    skip_blanks_force();
	}
	switch ( cur ) {
	    
	  case ';':
	    force_char(';');
	    return;
	    
	  case 'd':
	    p->path = DIRECTORY();
	    break;
	    
	  case 'f':
	    FILES(p);
	    break;
	    
	  case 'p':
	    PROTS(p);
	    break;
	    
	  case 't':
	    TRACE(p);
	    break;
	    
	    default:
	    syntaxErrorString("'dir', 'files', 'protocols', 'trace', or ';'");
	}
    }
}


/* 
 * TRACE = "trace=" STRING | "traceVar =" STRING
 */
static void
TRACE( PROTOCOL *p )
{
    force_string("trace");
    if ( cur == 'v' ) {
	TRACEVAR( p );
    } else {
	skip_blanks();
	force_char('=');
	skip_blanks();
	p->trace = STRING();
    }
}


static void
TRACEVAR( PROTOCOL *p )
{
    force_string("var");
    skip_blanks();
    force_char('=');
    skip_blanks();
    p->traceVar = STRING();
}


/*
 * PNAME -> "name=" PROTOCOLNAME
 */
static ProtName
PNAME()
{
    if (debug)
      printf("\n>PNAME\n");
    force_string("name");
    skip_blanks();
    force_char('=');
    skip_blanks();
    return PROTOCOLNAME();
}


/*
 * DIRECTORY -> "dir=" STRING
 */
static char *
DIRECTORY()
{
  if (debug)
    printf("\n>DIRECTORY\n");
  force_string("dir");
  skip_blanks();
  force_char('=');
  skip_blanks();
  return STRING();
}
  

/*
 * FILES -> "files=" FILELIST
 */
static void
FILES(PROTOCOL *p)
{
  if (debug)
    printf("\n>FILE\n");
  force_string("files");
  skip_blanks();
  force_char('=');
  skip_blanks();
  FILELIST(p);
}


/*
 * FILELIST -> FILENAME[.FILEEXT] | FILENAME[.FILEEXT] "," FILELIST
 */
static void
FILELIST(PROTOCOL *p)
{
    char	next;
    char	*fn;
    int		i, j;

    if (debug)
      printf("\n>FILELIST\n");
    i = 0;
    do {
	skip_blanks();
	fn = FILENAME();
	for ( j = (strlen(fn) - 1); j; j-- ) {
	    if ( fn[j] == '.' ) {
		p->files[i].ext = &fn[j + 1];
		fn[j] = 0;
		break;
	    }
	}
	if ( ! p->files[i].ext ) {
	    p->files[i].ext = "c";
	}
	p->files[i].base = fn;
	i++;
	if ( (next = cur) == ',' ) {
	    get();
	}
    } while ( next == ',' );
    p->numfiles = i;
}


static ProtName
PROTOCOLNAME()
{
    ProtName	p;

    p.name = PROTOCOLSTRING();
    if ( cur == '/' ) {
	get();
	p.instance = PROTOCOLSTRING();
    } else {
	p.instance = "";
    }
    return p;
}


static char *
PROTOCOLSTRING()
{
    int len = 0;
    char buf[BUF_SIZE];
    
    if (debug)
      printf("\n>PROTOCOLNAME\n");
    while ( cur && ! isWhiteSpace(cur) && cur != '/'
	    	&& cur != ';' && cur != ',' ) {
	buf[len++] = cur;
	get();
    }
    buf[len] = 0;
    return (xerox(buf));
}


/*
 * STRING -> [^ \t\n;]*
 */
static char *
STRING()
{
  int len = 0;
  char buf[BUF_SIZE];

  if (debug)
    printf("\n>STRING\n");
  while (cur != ' ' && cur != '\t' && cur != '\n' && cur != ';') {
    buf[len++] = cur;
    get();
  }
  buf[len] = 0;
  return (xerox(buf));
}


/*
 * PROTS --> "protocols=" PROTOCOLLIST
 */
static void
PROTS(PROTOCOL *p)
{
  if (debug)
    printf("\n>PROTS\n");
  force_string("protocols");
  skip_blanks();
  force_char('=');
  skip_blanks();
  PROTOCOLLIST(p);
}


/*
 * PROTOCOLIST -> PROTOCOLNAME "," PROTOCOLLIST
 */
static void
PROTOCOLLIST( PROTOCOL *p )
{
    if (debug)
      printf("\n>PROTOCOLLIST\n");
    p->numdown = 0;
    p->down[0] = PROTOCOLNAME();
    while (cur == ',') {
	get();
	skip_blanks();
	p->numdown++;
	p->down[p->numdown] = PROTOCOLNAME();
    }
    p->numdown++;
}


/*
 * FILENAME -> [./-A-Za-z_0..9]*
 */
static char *
FILENAME()
{
  int len = 0;
  char buf[BUF_SIZE];

  if (debug)
    printf("\n>FILENAME\n");
  while (islower(cur) || isupper(cur) || isdigit(cur) || (cur == '_') ||
	 (cur == '/') || (cur == '.') || (cur == '-')) {
    buf[len++] = cur;
    get();
  }
  buf[len] = 0;
  return (xerox(buf));
}


static void
force_char(char ch)
{
  if (cur == ch) {
    get();
  } else {
    syntaxErrorChar(ch, cur);
  }
}


static void
force_string(char *str)
{
  char *temp;

  temp = str;
  while (cur == *temp) {
    get();
    temp++;
  }
  if (*temp != 0) {
    syntaxErrorString(str);
  }
}


static void
skip_blanks()
{
    while ( isWhiteSpace(cur) ) {
	get();
    }
}


static void
skip_blanks_force()
{
    if ( isWhiteSpace(cur) ) {
	skip_blanks();
    } else {
	syntaxErrorString("white space");
    }
}


static char *
UPTO_CHAR( char c )
{
    static char buf[200];
    int	i;

    for ( i=0; i < 200 && cur && cur != c; i++ ) {
	buf[i] = cur;
	get();
    }
    if ( i == 200 ) {
	errorRomLineTooLong("graph.comp");
    }
    buf[i] = 0;
    return buf;
}

static void
get()
{
  if (done)
    return;
  cur = getchar();
  if ( cur == '#' ) {
      while ( cur != '\n' && cur != EOF ) {
	  cur = getchar();
      }
  }
  filePosition++;
  if (debug)
    putchar(cur);
  if (cur == EOF) {
    done = 1;
    cur = 0;
  }
  if (cur == '\n') {
    filePosition=0;
    fileLine++;
    cur = ' ';
    if (debug)
      putchar(cur);
  }
}

