/**	opprec.c - compute operator predecence function values
 *	R. A. Garmoe	89/05/09
 */

/*	This program accepts a directed graph (in matrix form) and calculates
 *	the operator precedence function values f(op) and g(op).  For more
 *	information see Compilers: Principles, Techniques and Tools, by Aho,
 *	Sethi and Ullman [Addison-Wesley], Section 4.6.  A value of 1 in the
 *	matrix indicates an edge; a value of 0 indicates no edge.  Note
 *	also that the entries fx -> fy and gx -> gy are only present as
 *	placeholders (to make the matrix easier to read in); these values
 *	should always be zero.
 *
 *	To use this program, first generate the directed graph file expr2.z and
 *	run it through the C preprocessor to remove comments:
 *
 *		cl -P expr2.z
 *
 *	This will produce the file expr2.i, which can then be run through
 *	opprec.exe:
 *
 *		graph {option} expr2.i > expr2.out
 *
 *	The output file expr2.out then contains the precedence function
 *	values in either assembler or C format.
 */



/*	Call
 *
 *	opprec vca file
 *
 *	where
 *		v	include operator values in output as comments
 *		c	generate C compilable output
 *		a	generate MASM assemblable output
 *		file	input file stripped of comments
 */





#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "opprec.h"

struct token {
	struct token *next;
	char   precstr[17];
	char   type[17];
	char   tclass[17];
	char   bind[17];
	char   eval[17];
};
struct token *tokhead = NULL;
struct token *toktail = NULL;
int	asmout = FALSE; 	//output assembler form if true
int	verbose = FALSE;	//output operator group data if true

void cdecl main(int argc, char **argv)
{
	int 	i;
	int 	j;
	int 	d;
	int    *pMat;
	int    *pPrec;
	char  **pStr;
	int 	cEnt;
	FILE   *fh;
	char   *p, *q;
	int 	len, f, g;
	struct	token *pt;
	int 	ntoken = 0;
	char	str[200];

	// check arguments

	if (argc != 3) {
		printf ("Usage: graph -vca file\n");
		exit (1);
	}

	for (i = 0; argv[1][i] != 0; i++) {
		switch (argv[1][i]) {
			case 'a':
				asmout = TRUE;
				break;

			case 'c':
				asmout = FALSE;
				break;

			case 'v':
				verbose = TRUE;
				break;

			default:
				printf ("Unknown argument %c\n", argv[1][i]);
				exit (1);
		}
	}
	if ((fh = fopen (argv[2], "r")) == NULL) {
		printf ("Unable to open '%s'\n", argv[1]);
		exit (1);
	}

	// read and print token class definitions

	for (;;) {
		if ((p = SkipBlank (fh, str, 200)) == NULL) {
			printf ("EOF reached\n");
			exit (1);
		}
		while (isspace (*p)) {
			p++;
		}
		q = strpbrk (p, " \t");
		if ( q )
			*q = 0;
		if (strcmp (p, "END") == 0) {
			break;
		}
		if (asmout) {
			printf ("OPCDAT %s\n", p);
		}
		else {
			printf ("OPCDAT (%s)\n", p);
		}
	}
	printf ("\n");

	// read token definitions

	for (;;) {
		if ((p = SkipBlank (fh, str, 200)) == NULL) {
			printf ("EOF reached\n");
			exit (1);
		}
		while (isspace (*p)) {
			p++;
		}
		if (strcmp (p, "END") == 0) {
			break;
		}
		if ((q = strpbrk (p, " \t")) == NULL) {
			printf ("Bad format (%s)\n", str);
			exit (1);
		}
		*q = 0;
		ntoken++;
		if ((pt = (struct token *)malloc (sizeof (struct token))) == NULL) {
			printf ("insufficient memory\n");
			exit (2);
		}
		pt->next = NULL;
		strcpy (pt->precstr, p);
		p = q + 1;
		while (isspace (*p)) {
			p++;
		}
		if ((q = strpbrk (p, " \t")) == NULL) {
			printf ("Bad format (%s)\n", str);
			exit (1);
		}
		*q = 0;
		strcpy (pt->type, p);
		p = q + 1;
		while (isspace (*p)) {
			p++;
		}
		if ((q = strpbrk (p, " \t")) != NULL) {
			*q = 0;
		}
		strcpy (pt->tclass, p);
		p = q + 1;
		while (isspace (*p)) {
			p++;
		}
		if ((q = strpbrk (p, " \t")) != NULL) {
			*q = 0;
		}
		strcpy (pt->bind, p);
		p = q + 1;
		while (isspace (*p)) {
			p++;
		}
		if ((q = strpbrk (p, " \t")) != NULL) {
			*q = 0;
		}
		strcpy (pt->eval, p);




		if (tokhead == NULL) {
			tokhead = pt;
			toktail = pt;
		}
		else {
			toktail->next = pt;
			toktail = pt;
		}
	}
	if (asmout) {
		printf ("OPCNT COPS_EXPR,\t%d\n\n", ntoken);
	}
	else {
		printf ("OPCNT (COPS_EXPR,\t%d\t)\n\n", ntoken);
	}

	// read dimension of matrix.	note that the upper left and lower right
	// quadrants of the matrix must be zero.

	if (SkipBlank (fh, str, 200) == NULL) {
		printf ("EOF reached\n");
		exit (1);
	}
	cEnt = atoi (str);

	// allocate space for the matrix and the description strings

	pMat = (int *)malloc (cEnt * cEnt * sizeof(int));
	pStr = malloc (cEnt * sizeof (char *));
	pPrec = (int *)malloc (cEnt * sizeof (int));
	if ((pMat == NULL) || (pStr == NULL) || (pPrec == NULL)) {
		printf ("insufficient memory\n");
		exit (2);
	}

	ReadMat (fh, pMat, pStr, cEnt);

	AddClosure (pMat, cEnt);

	// check for acyclic graph

	for (i = 0; i < cEnt; ++i) {
		if (pMat[i * cEnt + i] != 0) {
			printf ("Graph is cyclic for %s!!!\n", pStr[i]);
			exit(3);
		}
	}

	// print precedence function values

	for (i = 0; i < cEnt; ++i) {
		d = 0;
		for (j = 0; j < cEnt; ++j) {
			if (pMat[i * cEnt + j] > d) {
				d = pMat[i * cEnt + j];
			}
		}
		pPrec[i] = d;
		if (verbose) {
			if (asmout) {
				printf (";%-4s : %3d\n", pStr[i], d);
			}
			else {
				printf ("/*%-4s : %3d*/\n", pStr[i], d);
			}
		}
	}

	// print token definitions

	for (pt = tokhead; pt != NULL; pt = pt->next) {
		len = strlen (pt->precstr);

		// search for F string in list of precedence groupings

		for (i = 0; i < cEnt; i++) {
			if ((p = strstr(pStr[i], pt->precstr)) &&
			  ((*(p + len) == 0) || (*(p + len) == 'G'))) {
				break;
			}
		}
		if (i == cEnt) {
			printf ("F precedence string \"%s\" not found\n", pt->precstr);
			exit (4);
		}
		else {
			f = pPrec[i];
		}

		// search for G string in list of precedence groupings

		*pt->precstr = 'G';
		for (i = 0; i < cEnt; i++) {
			// search for string in list of precedence groupings
			if ((p = strstr(pStr[i], pt->precstr)) && (*(p + len) == 0)) {
				break;
			}
		}
		if (i == cEnt) {
			printf ("G precedence string \"%s\" not found\n", pt->precstr);
			exit (4);
		}
		else {
			g = pPrec[i];
		}
		if (asmout) {
			printf ("OPDAT %-16s,%4d,%4d,\t%-16s\n", pt->type, f, g,pt->tclass);
		}
		else {
			printf ("OPDAT (%-16s,%4d,%4d,\t%-16s,%-16s,%-16s\t)\n",
			  pt->type, f, g,pt->tclass, pt->bind, pt->eval);
		}
	}
	fclose (fh);
}




char *SkipBlank (FILE *fh, char *pStr, int cnt)
{
	int	len;

	for (;;) {
		if (fgets (pStr, cnt, fh) == NULL) {
			return (NULL);
		}
		len = strlen (pStr);
		if ((len == 1) || (*pStr == '#')) {
			continue;
		}
		*(pStr + len - 1) = 0;
		return (pStr);
	}
}