/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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 1990 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

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

/*	from OpenSolaris "y4.c	6.15	05/06/08 SMI"	*/

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)y4.c	1.5 (gritter) 11/26/05
 */

#include "dextern"
#include <wchar.h>
#include <unistd.h>
#define	NOMORE -1000

static void gin(int);
static void stin(int);
static void osummary(void);
static void aoutput(void);
static void arout(wchar_t *, int *, int);
static int nxti(void);
static int gtnm(void);

static int *ggreed;
static int *pgo;
static int *yypgo;

static int maxspr = 0;  /* maximum spread of any entry */
static int maxoff = 0;  /* maximum offset into an array */
int *optimmem;
static int *maxa;

static int nxdb = 0;
static int adb = 0;

void 
callopt(void)
{
	register int i, *p, j, k, *q;

	ggreed = malloc(sizeof (int) * size);
	pgo = malloc(sizeof (int) * size);
	yypgo = &nontrst[0].tvalue;

	/* read the arrays from tempfile and set parameters */

	if ((finput = fopen(TEMPNAME, "r")) == NULL)
		error("optimizer cannot open tempfile");

	optimmem = tracemem;
	pgo[0] = 0;
	temp1[0] = 0;
	nstate = 0;
	nnonter = 0;
	for (;;) {
		switch (gtnm()) {

		case L'\n':
			temp1[++nstate] = (--optimmem) - tracemem;
			/* FALLTHRU */

		case L',':
			continue;

		case L'$':
			break;

		default:
			error("bad tempfile");
		}
		break;
	}

	temp1[nstate] = yypgo[0] = (--optimmem) - tracemem;

	for (;;) {
		switch (gtnm()) {

		case L'\n':
			yypgo[++nnonter] = optimmem-tracemem;
			/* FALLTHRU */
		case L',':
			continue;

		case EOF:
			break;

		default:
			error("bad tempfile");
		}
		break;
	}

	yypgo[nnonter--] = (--optimmem) - tracemem;

	for (i = 0; i < nstate; ++i) {
		k = 32000000;
		j = 0;
		q = tracemem + temp1[i+1];
		for (p = tracemem + temp1[i]; p < q; p += 2) {
			if (*p > j)
				j = *p;
			if (*p < k)
				k = *p;
		}
		if (k <= j) {
			/*
			 * nontrivial situation
			 * temporarily, kill this for compatibility
			 */
			/* j -= k;  j is now the range */
			if (k > maxoff)
				maxoff = k;
		}
		tystate[i] = (temp1[i+1] - temp1[i]) + 2*j;
		if (j > maxspr)
			maxspr = j;
	}

	/* initialize ggreed table */
	for (i = 1; i <= nnonter; ++i) {
		ggreed[i] = 1;
		j = 0;
		/* minimum entry index is always 0 */
		q = tracemem + yypgo[i+1] -1;
		for (p = tracemem + yypgo[i]; p < q; p += 2) {
			ggreed[i] += 2;
			if (*p > j)
				j = *p;
		}
		ggreed[i] = ggreed[i] + 2*j;
		if (j > maxoff)
			maxoff = j;
	}

	/* now, prepare to put the shift actions into the amem array */
	for (i = 0; i < new_actsize; ++i)
		amem[i] = 0;
	maxa = amem;

	for (i = 0; i < nstate; ++i) {
		if (tystate[i] == 0 && adb > 1)
			fprintf(ftable, "State %d: null\n", i);
		indgo[i] = YYFLAG1;
	}

	while ((i = nxti()) != NOMORE) {
		if (i >= 0)
			stin(i);
		else
			gin(-i);
	}

	if (adb > 2) { /* print a array */
		for (p = amem; p <= maxa; p += 10) {
			fprintf(ftable, "%4d  ", p-amem);
			for (i = 0; i < 10; ++i)
				fprintf(ftable, "%4d  ", p[i]);
			fprintf(ftable, "\n");
		}
	}
	/* write out the output appropriate to the language */
	aoutput();
	osummary();
	ZAPFILE(TEMPNAME);
}

static void 
gin(int i)
{
	register int *r, *s, *q1, *q2;
	int *p;

	/* enter gotos on nonterminal i into array amem */
	ggreed[i] = 0;

	q2 = tracemem + yypgo[i+1] - 1;
	q1 = tracemem + yypgo[i];

	/* now, find a place for it */

	/* for( p=amem; p < &amem[new_actsize]; ++p ){ */
	p = amem;
	for (;;) {
		while (p >= &amem[new_actsize])
			exp_act(&p);
		if (*p)
			goto nextgp;
		for (r = q1; r < q2; r += 2) {
			s = p + *r + 1;
			/*
			 * Check if action table needs to
			 * be expanded or not. If so,
			 * expand it.
			 */
			while (s >= &amem[new_actsize]) {
				exp_act(&p);
				s = p + *r + 1;
			}
			if (*s)
				goto nextgp;
			if (s > maxa) {
				while ((maxa = s) >= &amem[new_actsize])
					/* error( "amem array overflow" ); */
					exp_act(&p);
			}
		}
		/* we have found a spot */
		*p = *q2;
		if (p > maxa) {
			while ((maxa = p) >= &amem[new_actsize])
				/* error("amem array overflow"); */
				exp_act(&p);
		}
		for (r = q1; r < q2; r += 2) {
			s = p + *r + 1;
			/*
			 * Check if action table needs to
			 * be expanded or not. If so,
			 * expand it.
			 */
			while (s >= &amem[new_actsize]) {
				exp_act(&p);
				s = p + *r + 1;
			}
			*s = r[1];
		}

		pgo[i] = p - amem;
		if (adb > 1)
			fprintf(ftable,
				"Nonterminal %d, entry at %d\n", i, pgo[i]);
		goto nextgi;

		nextgp:
			++p;
	}
	/* error( "cannot place goto %d\n", i ); */
	nextgi:;
}

static void 
stin(int i)
{
	register int *r, n, nn, flag, j, *q1, *q2;
	int *s;

	tystate[i] = 0;

	/* Enter state i into the amem array */

	q2 = tracemem + temp1[i + 1];
	q1 = tracemem + temp1[i];
	/* Find an acceptable place */

	nn = -maxoff;
	more:
	for (n = nn; n < new_actsize; ++n) {
		flag = 0;
		for (r = q1; r < q2; r += 2) {
			s = *r + n + amem;
			if (s < amem)
				goto nextn;
			/*
			 * Check if action table needs to
			 * be expanded or not. If so,
			 * expand it.
			 */
			while (s >= &amem[new_actsize]) {
				exp_act(NULL);
				s = *r + n + amem;
			}
			if (*s == 0)
				++flag;
			else if (*s != r[1])
				goto nextn;
		}

		/*
		 * check that the position equals another
		 * only if the states are identical
		 */
		for (j = 0; j < nstate; ++j) {
			if (indgo[j] == n) {
				if (flag)
					/*
					 * we have some disagreement.
					 */
					goto nextn;
				if (temp1[j+1] + temp1[i] ==
					temp1[j] + temp1[i+1]) {
					/* states are equal */
					indgo[i] = n;
					if (adb > 1)
						fprintf(ftable,
						"State %d: entry at"
						" %d equals state %d\n",
						i, n, j);
					return;
				}
				goto nextn;  /* we have some disagreement */
			}
		}

		for (r = q1; r < q2; r += 2) {
			while ((s = *r + n + amem) >= &amem[new_actsize]) {
				/*
				 * error( "out of space");
				 */
				exp_act(NULL);
			}
			if (s > maxa)
				maxa = s;
			if (*s != 0 && *s != r[1])
				error(
				"clobber of amem array, pos'n %d, by %d",
				s-amem, r[1]);
			*s = r[1];
		}
		indgo[i] = n;
		if (adb > 1)
			fprintf(ftable,
				"State %d: entry at %d\n", i, indgo[i]);
		return;
		nextn:;
	}

	/* error( "Error; failure to place state %d\n", i ); */
	exp_act(NULL);
	nn = new_actsize - ACTSIZE;
	goto more;
	/* NOTREACHED */
}

static int
nxti(void)
{
	/* finds the next i */
	register int i, max, maxi = 0;
	max = 0;

	for (i = 1; i <= nnonter; ++i)
		if (ggreed[i] >= max) {
		max = ggreed[i];
		maxi = -i;
		}

	for (i = 0; i < nstate; ++i)
		if (tystate[i] >= max) {
			max = tystate[i];
			maxi = i;
		}
	if (nxdb)
		fprintf(ftable, "nxti = %d, max = %d\n", maxi, max);
	if (max == 0)
		return (NOMORE);
	else
		return (maxi);
}

static void 
osummary(void)
{
	/* write summary */
	register int i, *p;

	if (foutput == NULL)
		return;
	i = 0;
	for (p = maxa; p >= amem; --p) {
		if (*p == 0)
			++i;
	}

	fprintf(foutput, "Optimizer space used: input %d/%d, output %d/%d\n",
		optimmem-tracemem + 1, new_memsize, maxa-amem + 1, new_actsize);
	fprintf(foutput, "%d table entries, %d zero\n", (maxa-amem) + 1, i);
	fprintf(foutput,
		"maximum spread: %d, maximum offset: %d\n", maxspr, maxoff);

}

static void 
aoutput(void)
{
	/* this version is for C */
	/* write out the optimized parser */

	fprintf(ftable, "# define YYLAST %d\n", maxa-amem + 1);
	arout(L"yyact", amem, (maxa - amem) + 1);
	arout(L"yypact", indgo, nstate);
	arout(L"yypgo", pgo, nnonter + 1);
}

static void
arout(wchar_t *s, int *v, int n)
{
	register int i;

	fprintf(ftable, "static YYCONST yytabelem %ls[]={\n", s);
	for (i = 0; i < n; ) {
		if (i % 10 == 0)
			fprintf(ftable, "\n");
		fprintf(ftable, "%6d", v[i]);
		if (++i == n)
			fprintf(ftable, " };\n");
		else
			fprintf(ftable, ",");
	}
}

static int
gtnm(void)
{
	register int s, val, c;

	/* read and convert an integer from the standard input */
	/* return the terminating character */
	/* blanks, tabs, and newlines are ignored */

	s = 1;
	val = 0;

	while ((c = getwc(finput)) != EOF) {
		if (iswdigit(c))
			val = val * 10 + c - L'0';
		else if (c == L'-')
			s = -1;
		else
			break;
	}
	*optimmem++ = s*val;
	if (optimmem >= &tracemem[new_memsize])
		exp_mem(0);
	return (c);
}

void 
exp_act(int **ptr)
{
	static int *actbase;
	int i;
	new_actsize += ACTSIZE;

	actbase = amem;
	amem = realloc(amem, sizeof (int) * new_actsize);
	if (amem == NULL)
		error("couldn't expand action table");

	for (i = new_actsize-ACTSIZE; i < new_actsize; ++i)
		amem[i] = 0;
	if (ptr != NULL)
		*ptr = *ptr - actbase + amem;
	if (memp >= amem)
		memp = memp - actbase + amem;
	if (maxa >= amem)
		maxa = maxa - actbase + amem;
}
