/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include "dextern.h"

static void go2gen(int);
static void precftn(int, int, int);
static void wract(int);
static void wrstate(int);
static void wdef(wchar_t *, int);
static void wrmbchars(void);
	/* important local variables */
static int lastred; /* number of the last reduction of a state */
int *defact;
extern int *toklev;
extern int cwp;

/* print the output for the states */
void
output()
{
	int i, k, c;
	register WSET *u, *v;

	(void) fprintf(ftable, "static YYCONST yytabelem yyexca[] ={\n");

	SLOOP(i) { /* output the stuff for state i */
		nolook = !(tystate[i] == MUSTLOOKAHEAD);
		closure(i);
		/* output actions */
		nolook = 1;
		aryfil(temp1, ntoksz+nnontersz+1, 0);
		WSLOOP(wsets, u) {
			c = *(u->pitem);
			if (c > 1 && c < NTBASE && temp1[c] == 0) {
				WSLOOP(u, v) {
					if (c == *(v->pitem))
						putitem(v->pitem + 1,
						    (LOOKSETS *)0);
				}
				temp1[c] = state(c);
			} else if (c > NTBASE &&
			    temp1[(c -= NTBASE) + ntokens] == 0) {
				temp1[c + ntokens] = amem[indgo[i] + c];
			}
		}
		if (i == 1)
			temp1[1] = ACCEPTCODE;
		/* now, we have the shifts; look at the reductions */
		lastred = 0;
		WSLOOP(wsets, u) {
			c = *(u->pitem);
			if (c <= 0) { /* reduction */
				lastred = -c;
				TLOOP(k) {
					if (BIT(u->ws.lset, k)) {
						if (temp1[k] == 0)
							temp1[k] = c;
						else if (temp1[k] < 0) {
							/*
							 * reduce/reduce
							 * conflict
							 */
							/* BEGIN CSTYLED */
							if (foutput != NULL)
					(void) fprintf(foutput,
				WSFMT("\n%d: reduce/reduce conflict"
				" (red'ns %d and %d ) on %ws"),
							i, -temp1[k],
							lastred, symnam(k));
						    if (-temp1[k] > lastred)
							temp1[k] = -lastred;
						    ++zzrrconf;
							/* END CSTYLED */
						} else
							/*
							 * potentia
							 * shift/reduce
							 * conflict.
							 */
							precftn(lastred, k, i);
					}
				}
			}
		}
		wract(i);
	}

	(void) fprintf(ftable, "\t};\n");
	wdef(L"YYNPROD", nprod);
	if (nmbchars > 0) {
		wrmbchars();
	}
}

static int pkdebug = 0;
int
apack(p, n)
int *p;
int n;
{
	/* pack state i from temp1 into amem */
	int off;
	int *pp, *qq;
	int *q, *rr;
	int diff;

	/*
	 * we don't need to worry about checking because we
	 * we will only look up entries known to be there...
	 */

	/* eliminate leading and trailing 0's */

	q = p + n;
	for (pp = p, off = 0; *pp == 0 && pp <= q; ++pp, --off)
		/* NULL */;
	if (pp > q)
		return (0);  /* no actions */
	p = pp;

	/* now, find a place for the elements from p to q, inclusive */
	/* for( rr=amem; rr<=r; ++rr,++off ){ */  /* try rr */
	rr = amem;
	for (; ; ++rr, ++off) {
		while (rr >= &amem[new_actsize-1])
			exp_act(&rr);
		qq = rr;
		for (pp = p; pp <= q; ++pp, ++qq) {
			if (*pp) {
				diff = qq - rr;
				while (qq >= &amem[new_actsize-1]) {
					exp_act(&rr);
					qq = diff + rr;
				}
				if (*pp != *qq && *qq != 0)
					goto nextk;
			}
		}

		/* we have found an acceptable k */

		if (pkdebug && foutput != NULL)
			(void) fprintf(foutput,
				"off = %d, k = %" PRIdPTR "\n", off, rr-amem);

		qq = rr;
		for (pp = p; pp <= q; ++pp, ++qq) {
			if (*pp) {
				diff = qq - rr;
				while (qq >= &amem[new_actsize-1]) {
					exp_act(&rr);
					qq = diff + rr;
				}
				if (qq > memp)
					memp = qq;
				*qq = *pp;
			}
		}
		if (pkdebug && foutput != NULL) {
			for (pp = amem; pp <= memp; pp += 10) {
				(void) fprintf(foutput, "\t");
				for (qq = pp; qq <= pp + 9; ++qq)
					(void) fprintf(foutput, "%d ", *qq);
				(void) fprintf(foutput, "\n");
			}
		}
		return (off);
		nextk:;
	}
	/* error("no space in action table" ); */
	/* NOTREACHED */
}

void
go2out()
{
	/* output the gotos for the nontermninals */
	int i, j, k, best, count, cbest, times;

	(void) fprintf(ftemp, "$\n");  /* mark begining of gotos */

	for (i = 1; i <= nnonter; ++i) {
		go2gen(i);
		/* find the best one to make default */
		best = -1;
		times = 0;
		for (j = 0; j < nstate; ++j) { /* is j the most frequent */
			if (tystate[j] == 0)
				continue;
			if (tystate[j] == best)
				continue;
			/* is tystate[j] the most frequent */
			count = 0;
			cbest = tystate[j];
			for (k = j; k < nstate; ++k)
				if (tystate[k] == cbest)
					++count;
			if (count > times) {
				best = cbest;
				times = count;
			}
		}

		/* best is now the default entry */
		zzgobest += (times-1);
		for (j = 0; j < nstate; ++j) {
			if (tystate[j] != 0 && tystate[j] != best) {
				(void) fprintf(ftemp, "%d,%d,", j, tystate[j]);
				zzgoent += 1;
			}
		}

		/* now, the default */

		zzgoent += 1;
		(void) fprintf(ftemp, "%d\n", best);

	}
}

static int g2debug = 0;
static void go2gen(int c)
{
	/* output the gotos for nonterminal c */
	int i, work, cc;
	ITEM *p, *q;

	/* first, find nonterminals with gotos on c */
	aryfil(temp1, nnonter + 1, 0);
	temp1[c] = 1;

	work = 1;
	while (work) {
		work = 0;
		PLOOP(0, i) {
			if ((cc = prdptr[i][1] - NTBASE) >= 0) {
				/* cc is a nonterminal */
				if (temp1[cc] != 0) {
					/*
					 * cc has a goto on c
					 * thus, the left side of
					 * production i does too.
					 */
					cc = *prdptr[i] - NTBASE;
					if (temp1[cc] == 0) {
						work = 1;
						temp1[cc] = 1;
					}
				}
			}
		}
	}

	/* now, we have temp1[c] = 1 if a goto on c in closure of cc */

	if (g2debug && foutput != NULL) {
		(void) fprintf(foutput, WSFMT("%ws: gotos on "),
		    nontrst[c].name);
		NTLOOP(i) if (temp1[i])
			(void) fprintf(foutput, WSFMT("%ws "), nontrst[i].name);
		(void) fprintf(foutput, "\n");
	}

	/* now, go through and put gotos into tystate */
	aryfil(tystate, nstate, 0);
	SLOOP(i) {
		ITMLOOP(i, p, q) {
			if ((cc = *p->pitem) >= NTBASE) {
				if (temp1[cc -= NTBASE]) {
					/* goto on c is possible */
					tystate[i] = amem[indgo[i] + c];
					break;
				}
			}
		}
	}
}

/* decide a shift/reduce conflict by precedence.  */
static void
precftn(int r, int t, int s)
{

	/*
	 * r is a rule number, t a token number
	 * the conflict is in state s
	 * temp1[t] is changed to reflect the action
	 */

	int lp, lt, action;

	lp = levprd[r];
	lt = toklev[t];
	if (PLEVEL(lt) == 0 || PLEVEL(lp) == 0) {
		/* conflict */
		if (foutput != NULL)
			(void) fprintf(foutput,
			    WSFMT("\n%d: shift/reduce conflict"
			    " (shift %d, red'n %d) on %ws"),
			    s, temp1[t], r, symnam(t));
		++zzsrconf;
		return;
	}
	if (PLEVEL(lt) == PLEVEL(lp))
		action = ASSOC(lt) & ~04;
	else if (PLEVEL(lt) > PLEVEL(lp))
		action = RASC;  /* shift */
	else
		action = LASC;  /* reduce */

	switch (action) {
	case BASC:  /* error action */
		temp1[t] = ERRCODE;
		return;
	case LASC:  /* reduce */
		temp1[t] = -r;
		return;
	}
}

static void
wract(int i)
{
	/* output state i */
	/* temp1 has the actions, lastred the default */
	int p, p0, p1;
	int ntimes, tred, count, j;
	int flag;

	/* find the best choice for lastred */

	lastred = 0;
	ntimes = 0;
	TLOOP(j) {
		if (temp1[j] >= 0)
			continue;
		if (temp1[j] + lastred == 0)
			continue;
		/* count the number of appearances of temp1[j] */
		count = 0;
		tred = -temp1[j];
		levprd[tred] |= REDFLAG;
		TLOOP(p) {
			if (temp1[p] + tred == 0)
				++count;
		}
		if (count > ntimes) {
			lastred = tred;
			ntimes = count;
		}
	}

	/*
	 * for error recovery, arrange that, if there is a shift on the
	 * error recovery token, `error', that the default be the error action
	 */
	if (temp1[2] > 0)
		lastred = 0;

	/* clear out entries in temp1 which equal lastred */
	TLOOP(p) {
		if (temp1[p] + lastred == 0)
			temp1[p] = 0;
	}

	wrstate(i);
	defact[i] = lastred;

	flag = 0;
	TLOOP(p0) {
		if ((p1 = temp1[p0]) != 0) {
			if (p1 < 0) {
				p1 = -p1;
				goto exc;
			} else if (p1 == ACCEPTCODE) {
				p1 = -1;
				goto exc;
			} else if (p1 == ERRCODE) {
				p1 = 0;
				goto exc;
			exc:
				if (flag++ == 0)
					(void) fprintf(ftable, "-1, %d,\n", i);
				(void) fprintf(ftable,
				    "\t%d, %d,\n", tokset[p0].value, p1);
				++zzexcp;
			} else {
				(void) fprintf(ftemp,
				    "%d,%d,", tokset[p0].value, p1);
				++zzacent;
			}
		}
	}
	if (flag) {
		defact[i] = -2;
		(void) fprintf(ftable, "\t-2, %d,\n", lastred);
	}
	(void) fprintf(ftemp, "\n");
}

static void
wrstate(int i)
{
	/* writes state i */
	int j0, j1;
	register ITEM *pp, *qq;
	register WSET *u;

	if (foutput == NULL)
		return;
	(void) fprintf(foutput, "\nstate %d\n", i);
	ITMLOOP(i, pp, qq) {
		(void) fprintf(foutput, WSFMT("\t%ws\n"), writem(pp->pitem));
	}
	if (tystate[i] == MUSTLOOKAHEAD) {
		/* print out empty productions in closure */
		WSLOOP(wsets + (pstate[i + 1] - pstate[i]), u) {
			if (*(u->pitem) < 0)
				(void) fprintf(foutput,
				    WSFMT("\t%ws\n"), writem(u->pitem));
		}
	}

	/* check for state equal to another */
	TLOOP(j0) if ((j1 = temp1[j0]) != 0) {
		(void) fprintf(foutput, WSFMT("\n\t%ws  "), symnam(j0));
		if (j1 > 0) { /* shift, error, or accept */
			if (j1 == ACCEPTCODE)
				(void) fprintf(foutput,  "accept");
			else if (j1 == ERRCODE)
				(void) fprintf(foutput, "error");
			else
				(void) fprintf(foutput, "shift %d", j1);
		}
		else
			(void) fprintf(foutput, "reduce %d", -j1);
	}

	/* output the final production */
	if (lastred)
		(void) fprintf(foutput, "\n\t.  reduce %d\n\n", lastred);
	else
		(void) fprintf(foutput, "\n\t.  error\n\n");

	/* now, output nonterminal actions */
	j1 = ntokens;
	for (j0 = 1; j0 <= nnonter; ++j0) {
		if (temp1[++j1])
			(void) fprintf(foutput,
			    WSFMT("\t%ws  goto %d\n"),
			    symnam(j0 + NTBASE), temp1[j1]);
	}
}

static void
wdef(wchar_t *s, int n)
{
	/* output a definition of s to the value n */
	(void) fprintf(ftable, WSFMT("# define %ws %d\n"), s, n);
}

void
warray(s, v, n)
wchar_t *s;
int *v, n;
{
	int i;
	(void) fprintf(ftable, WSFMT("static YYCONST yytabelem %ws[]={\n"), s);
	for (i = 0; i < n; ) {
		if (i % 10 == 0)
			(void) fprintf(ftable, "\n");
		(void) fprintf(ftable, "%6d", v[i]);
		if (++i == n)
			(void) fprintf(ftable, " };\n");
		else
			(void) fprintf(ftable, ",");
	}
}

void
hideprod()
{
	/*
	 * in order to free up the mem and amem arrays for the optimizer,
	 * and still be able to output yyr1, etc., after the sizes of
	 * the action array is known, we hide the nonterminals
	 * derived by productions in levprd.
	 */

	int i, j;

	j = 0;
	levprd[0] = 0;
	PLOOP(1, i) {
		if (!(levprd[i] & REDFLAG)) {
			++j;
			if (foutput != NULL) {
				(void) fprintf(foutput,
				    WSFMT("Rule not reduced:   %ws\n"),
				    writem(prdptr[i]));
			}
		}
		levprd[i] = *prdptr[i] - NTBASE;
	}
	if (j)
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	Check how 'reduced' is translated in yacc man page/document.
 */
		(void) fprintf(stderr,
		    gettext("%d rules never reduced\n"),
		    j);
}


static int
cmpmbchars(p, q)
MBCLIT *p, *q;
{
	/* Compare two MBLITs. */
	return ((p->character) - (q->character));
}

static void
wrmbchars()
{
	int i;
	wdef(L"YYNMBCHARS", nmbchars);
	qsort(mbchars, nmbchars, sizeof (*mbchars),
	    (int (*)(const void *, const void *))cmpmbchars);
	(void) fprintf(ftable,
	    "static struct{\n\twchar_t character;"
	    "\n\tint tvalue;\n}yymbchars[YYNMBCHARS]={\n");
	for (i = 0; i < nmbchars; ++i) {
		(void) fprintf(ftable, "\t{%#x,%d}",
		    (int)mbchars[i].character, mbchars[i].tvalue);
		if (i < nmbchars - 1) {
			/* Not the last. */
			(void) fprintf(ftable, ",\n");
		}
	}
	(void) fprintf(ftable, "\n};\n");
}