%{

/* try /local/mouse/bin/localsym */

/* defines to cover the global yacc symbols */

#define yyact		_pdbYYact
#define yychar		_pdbYYchar
#define yychk		_pdbYYchk
#define yydebug		_pdbYYdebug
#define yydef		_pdbYYdef
#define yyerrflag	_pdbYYerrflag
#define yyexca		_pdbYYexca
#define yylex		_pdbYYlex
#define yylexinit	_pdbYYlexinit
#define yylval		_pdbYYlval
#define yynerrs		_pdbYYnerrs
#define yypact		_pdbYYpact
#define yyparse		_pdbYYparse
#define yypgo		_pdbYYpgo
#define yyr1		_pdbYYr1
#define yyr2		_pdbYYr2
#define yyval		_pdbYYval

#include <stdio.h>
#include <ctype.h>
#include <strings.h>

#include "../h/cdefs.h"
#include "pdb.h"

extern double strtod();

#define IBUFSIZE  1024

static FILE* inputFd;
static char inputBuf[IBUFSIZE];
static char* inputBufPtr;
static int lineno;
static char errMsgBuf[256];
static int errMsgSet = 0;
static PdbSet *rootSet;

#define YYSTYPE yystype
typedef struct {
	PdbSet *set;
	PdbPar *par;
	union {
	    double d;
	    int i;
	    char* s;
	 } u;
	int previousComma;
	int lineno;
} yystype;

void *hashFind();
void *hashInsert();

extern int yychar;

static char* tokenStr();
static printParName();
static char* copystr();
static yylexinit();
static yylex();
static matchNumber();

static int yyerror(str)
char* str;
{
	char *syntaxErrMsg = "syntax error";

	if (!errMsgSet)
	 { if (strncmp (str, syntaxErrMsg, strlen(syntaxErrMsg)) == 0)
	    { sprintf (errMsgBuf, "line %d: syntax error, near %s\n", 
		   		  lineno, tokenStr(yychar));
	    }
	   else
	    { sprintf (errMsgBuf, "%s\n", str);
	    }
	 }
	errMsgSet = 1;
}

%}
%token DOUBLE STRING INTEGER IDENTIFIER
%token LEFTPAREN RIGHTPAREN LEFTANGLE RIGHTANGLE 
%token EQUAL COMMA QUESTION_MARK COLON ASSIGN
%left PLUS MINUS 
%left TIMES DIVIDE
%token ERR
%start file
%%
file		: paramList
		   { rootSet = $1.set;
		   }
		;
paramList	:
		   { $$.set = (PdbSet*)calloc(1,sizeof(PdbSet));
		     $$.previousComma = 1;
		   }
		| paramList paramSpec
		   { $$.set = $1.set;
		     $$.par = $2.par;
		     if ($$.set->len++ == 0)
		      { $$.set->list = $2.par;
		      }
		     else
		      { $1.par->next = $2.par;
		      }
		     $$.set->code |= $2.par->type;
		     $$.previousComma = 0;
		   }
		| paramList QUESTION_MARK
		   { PdbPar *par;
		     par = (PdbPar*)calloc(1,sizeof(PdbPar));
	             $$.set = $1.set;
		     $$.par = $2.par;
		     if ($$.set->len++ == 0)
		      { $$.set->list = par;
		      }
		     else
		      { $1.par->next = par;
		      }
		     $$.par = par;
		     $$.par->lineno = $2.lineno;
		     $$.previousComma = 0;
		   }
		| paramList COMMA
		   { $$.set = $1.set;
		     if ($1.previousComma)
		      { PdbPar *par;
			par = (PdbPar*)calloc(1,sizeof(PdbPar));
			if ($$.set->len++ == 0)
			 { $$.set->list = par;
			 }
			else
			 { $1.par->next = par;
			 }			
			$$.par = par;
			$$.par->lineno = $2.lineno;
		      }
		     else
		      { $$.par = $1.par;
		      }
		     $$.previousComma = 1;
		   }
		| paramList IDENTIFIER ASSIGN param
		   { void *old;
		     hashInsert ($2.u.s, (void*)$4.par, /*clobber=*/1, &old);
		     if (old)
		      { free ((char*)old);
		      }
		     $$ = $1;
		   }	
		;
paramSpec	: IDENTIFIER EQUAL param
		   { $$.par = $3.par;
		     $$.par->name = $1.u.s;
		     $$.par->lineno = $1.lineno;
		     $$.par->flags |= PDB_DECLARED;
		   }
		| IDENTIFIER COLON param
		   { if ($3.par->type == PDB_SET)
		      { $$.par = $3.par;
			$$.par->name = $1.u.s;
			$$.par->lineno = $1.lineno;
			$$.par->flags |= PDB_CONSTRUCTED;
		      }
		     else
		      { sprintf(errMsgBuf,
"line %d: ':' operator must be followed by a set\n", $1.lineno);
			errMsgSet = 1;
			YYERROR;
		      }
		   }
		| param
		   { $$.par = $1.par;
		   }
		;
param		: DOUBLE
		   { $$.par = (PdbPar*)calloc(1,sizeof(PdbPar));
		     $$.par->type = PDB_DOUBLE;
		     $$.par->val.d = $1.u.d;
		     $$.par->lineno = $1.lineno;
		   }
		| INTEGER
		   { $$.par = (PdbPar*)calloc(1,sizeof(PdbPar));
		     $$.par->type = PDB_INTEGER;
		     $$.par->val.i = $1.u.i;
		     $$.par->lineno = $1.lineno;
		   }
		| STRING
		   { $$.par = (PdbPar*)calloc(1,sizeof(PdbPar));
		     $$.par->type = PDB_STRING;
		     $$.par->val.s = $1.u.s;
		     $$.par->lineno = $1.lineno;
		   }
		| paramSet
		   { $$.par = (PdbPar*)calloc(1,sizeof(PdbPar));
		     $$.par->type = PDB_SET;
		     $$.par->val.set = $1.set;
		     $$.par->lineno = $1.lineno;
		   }
		| IDENTIFIER
		   { PdbPar *p;	
		     if (p = (PdbPar*)hashFind($1.u.s))
		      { $$.par = (PdbPar*)calloc(1,sizeof(PdbPar));
			(*$$.par) = *p;
			$$.par->lineno = $1.lineno;
		      }
		     else
		      { sprintf(errMsgBuf,
"line %d: variable '%s' not defined\n", $1.lineno, $1.u.s);
			errMsgSet = 1;
			YYERROR;
		      }
		   }	
		| expr
		   { $$.par = $1.par;
		   }
expr		: param PLUS param
		   { if (binop ($1.par, $3.par, '+') < 0)
		      { YYERROR;
		      }
		     $$.par = $1.par;
		   }
		| param MINUS param
		   { if (binop ($1.par, $3.par, '-') < 0)
		      { YYERROR;
		      }
		     $$.par = $1.par;
		   }
		| param TIMES param
		   { if (binop ($1.par, $3.par, '*') < 0)
		      { YYERROR;
		      }
		     $$.par = $1.par;
		   }
		| param DIVIDE param
		   { if (binop ($1.par, $3.par, '/') < 0)
		      { YYERROR;
		      }
		     $$.par = $1.par;
		   }
		| MINUS param
		   { if (unnop ($2.par, '-') < 0)
		      { YYERROR;
		      }
		     $$.par = $2.par;
		   }
		| LEFTPAREN expr RIGHTPAREN
		   { $$.par = $2.par;
		   }	
paramSet	: LEFTANGLE { hashPushLevel(); } paramList RIGHTANGLE
		   { $$.set = $3.set;
		     hashPopLevel();
		   }
		;

%%

#include "pdbLex.c"

static char *tokenStr(num)
int num;
{
	static struct {
		int num;
		char *str;
	 } tokstrs[] = {
	DOUBLE,		"double",
	STRING,		"string",
	INTEGER,	"integer",
	IDENTIFIER,	"identifier",
	LEFTPAREN,	"(",
	RIGHTPAREN,	")",
	LEFTANGLE,	"<",
	RIGHTANGLE,	">",
	EQUAL,		"=",
	COMMA,		",", 
        QUESTION_MARK,  "?",
	COLON,		":",
	};
	int k;

	for (k=0; k<ARRAY_SIZE(tokstrs); k++)
	 { if (num == tokstrs[k].num)
	    { return (tokstrs[k].str);
	    }
	 }
	return ("???");
}

PdbSet *pdbParse (fp)
FILE *fp;
{
	hashTabCreate (1021, (int(*)())0);
	yylexinit(fp);
	errMsgSet = 0;
	if (yyparse())
	 { if (errMsgSet)
	    { printf ("%s", errMsgBuf);
	    }
	   else
	    { printf ("parse error\n");
	    }
	   hashTabFree ();
	   return (0);
	 }
	else
	 { hashTabFree ();
	   return (rootSet);
	 }

}

static printParName(p)
PdbPar *p;
{
	printf (" %s%s", (p->name?p->name:""),
			 (p->flags&PDB_DECLARED ? "=" : p->flags ? ":" : ""));
}

pdbPrintSet (set)
PdbSet *set;
{
	PdbPar *p;

	printf ("BEGIN SET code=0x%x len=%d\n", set->code, set->len);
	for (p=set->list; p; p=p->next)
	 { switch (p->type)
	    { case PDB_NONE:
	       { printf ("NONE\n");
		 break;
	       }
	      case PDB_INTEGER:
	       { printf ("INTEGER");
		 printParName(p);
		 printf ("%d\n", p->val.i);
		 break;
	       }
	      case PDB_DOUBLE:
	       { printf ("DOUBLE");
		 printParName(p);
		 printf ("%g\n", p->val.d);
		 break;
	       }
	      case PDB_STRING:
	       { printf ("STRING");
		 printParName(p);
		 printf ("%s\n", p->val.s);
		 break;
	       }
	      case PDB_SET:
	       { printf ("SET");
		 printParName(p);
		 printf ("\n");
		 pdbPrintSet (p->val.set);
		 break;
	       }
	    }
	 }
	printf ("END SET\n");
}

binop (p1, p2, op)
register PdbPar *p1, *p2;
char op;
{
	double val1, val2;

	if ((p1->type != PDB_INTEGER && p1->type != PDB_DOUBLE) ||
	    (p2->type != PDB_INTEGER && p2->type != PDB_DOUBLE))
	 { sprintf (errMsgBuf,
"line %d: only numeric expressions are permitted\n", p1->lineno);
	   errMsgSet = 1;
	   return (-1);
	 }
	if (p1->type == PDB_INTEGER && p2->type == PDB_INTEGER)
	 { switch (op)
	    { case '+':
	       { p1->val.i = p1->val.i + p2->val.i;
		 break;
	       }
	      case '-':
	       { p1->val.i = p1->val.i - p2->val.i;
		 break;
	       }
	      case '*':
	       { p1->val.i = p1->val.i * p2->val.i;
		 break;
	       }
	      case '/':
	       { if (p2->val.i == 0)
		  { sprintf (errMsgBuf,
"line %d: expression divide by zero\n", p2->lineno);
		    errMsgSet = 1;
		    return (-1);
		  }
		 p1->val.i = p1->val.i / p2->val.i;
		 break;
	       }
	    }
	 }
	else
	 { val1 = (p1->type == PDB_INTEGER ? p1->val.i : p1->val.d);
	   val2 = (p1->type == PDB_INTEGER ? p2->val.i : p2->val.d);
	   switch (op)
	    { case '+':
	       { p1->val.d = val1 + val2;
		 break;
	       }
	      case '-':
	       { p1->val.d = val1 - val2;
		 break;
	       }
	      case '*':
	       { p1->val.d = val1 * val2;
		 break;
	       }
	      case '/':
	       { if (val2 == 0.0)
		  { sprintf (errMsgBuf,
"line %d: expression divide by zero\n", p2->lineno);
		    errMsgSet = 1;
		    return (-1);
		  }
		 p1->val.d = val1 / val2;
		 break;
	       }
	    }
	   p1->type = PDB_DOUBLE;
	 }
	return (1);
}

unnop (p1, op)
register PdbPar *p1;
char op;
{
	if (p1->type != PDB_INTEGER && p1->type != PDB_DOUBLE)
	 { sprintf (errMsgBuf,
"line %d: only numeric expressions are permitted\n", p1->lineno);
	   errMsgSet = 1;
	   return (-1);
	 }
	if (p1->type == PDB_INTEGER)
	 { switch (op)
	    { case '-':
	       { p1->val.i = -p1->val.i;
		 break;
	       }
	    }
	 }
	else
	 { switch (op)
	    { case '-':
	       { p1->val.d = -p1->val.d;
		 break;
	       }
	    }
	 }
	return (1);
}

#if TEST

char **argvec;

main(argc, argv)
int argc;
char **argv;
{
	FILE *fp;
	char *fileName = NULL;
	int verbose = 0;

	argvec = argv;
	argc--; argv++;
	while (argc > 0)
	 { 
	   if (!fileName)
	    { fileName = *argv;
	    }
	   else if (ARGUMENT("-v"))
	    { verbose = 1;
	    }
	   else
	    { goto usage;
	    }
	   argc--; argv++;
	   continue;
usage:
	   fprintf (stderr, "Usage: %s [<file>] [-v]\n", argvec[0]);
	   exit (-1);
	 }

	if ((fp = fopen(fileName,"r")) == NULL)
	 { printf ("can't open file %s\n", fileName);
	 }

	if (pdbParse(fp))
	 { if (verbose)
	    { pdbPrintSet (rootSet);
	    }
	 }

	exit (0);
}

#endif
