/*	@(#)case.c	2.3	SCCS id keyword	*/
/* Copyright (c) 1979 Regents of the University of California */
#
/*
 * pi - Pascal interpreter code translator
 *
 * Charles Haley, Bill Joy UCB
 * Version 1.2 November 1978
 */

#include "whoami"
#include "0.h"
#include "tree.h"
#include "opcode.h"

/*
 * The structure used to
 * hold information about
 * each case label.
 */
struct ct {
	long	clong;
	int	cline;
};

/*
 * Caseop generates the
 * pascal case statement code
 */
caseop(r)
	int *r;
{
	register struct nl *p;
	register struct ct *ctab;
	register *cs;
	int *cl;
	double low, high;
	short *brtab;
	char *brtab0;
	char *csend;
	int w, i, j, m, n;
	int nr, goc;

	goc = gocnt;
	/*
	 * Obtain selector attributes:
	 *	p	type
	 *	w	width
	 *	low	lwb(p)
	 *	high	upb(p)
	 */
	p = rvalue((int *) r[2], NLNIL);
	if (p != NIL) {
		if (isnta(p, "bcsi")) {
			error("Case selectors cannot be %ss", nameof(p));
			p = NIL;
		} else {
			cl = p;
			if (p->class != RANGE)
				cl = p->type;
			if (cl == NIL)
				p = NIL;
			else {
				w = width(p);
#ifdef DEBUG
				if (hp21mx)
					w = 2;
#endif
				low = cl->range[0];
				high = cl->range[1];
			}
		}
	}
	/*
	 * Count # of cases
	 */
	n = 0;
	for (cl = r[3]; cl != NIL; cl = cl[2]) {
		cs = cl[1];
		if (cs == NIL)
			continue;
		for (cs = cs[2]; cs != NIL; cs = cs[2])
			n++;
	}
	/*
	 * Allocate case table space
	 */
	ctab = i = malloc(n * sizeof *ctab);
	if (i == 0) {
		error("Ran out of memory (case)");
		pexit(DIED);
	}
	/*
	 * Check the legality of the
	 * labels and count the number
	 * of good labels
	 */
	m = 0;
	for (cl = r[3]; cl != NIL; cl = cl[2]) {
		cs = cl[1];
		if (cs == NIL)
			continue;
		line = cs[1];
		for (cs = cs[2]; cs != NIL; cs = cs[2]) {
			gconst(cs[1]);
			if (p == NIL || con.ctype == NIL)
				continue;
			if (incompat(con.ctype, p, NIL)) {
				cerror("Case label type clashed with case selector expression type");
				continue;
			}
			if (con.crval < low || con.crval > high) {
				error("Case label out of range");
				continue;
			}
			ctab[m].clong = con.crval;
			ctab[m].cline = line;
			m++;
		}
	}

	/*
	 * Check for duplicate labels
	 */
	for (i = 0; i < m; i++)
		for (j = 0; j < m; j++)
			if (ctab[i].clong == ctab[j].clong) {
				if (i == j)
					continue;
				if (j < i)
					break;
				error("Multiply defined label in case, lines %d and %d", ctab[i].cline, ctab[j].cline);
			}
	/*
	 * Put out case operator and
	 * leave space for the
	 * branch table
	 */
	if (p != NIL) {
		put2(O_CASE1OP + (w >> 1), n);
		brtab = brtab0 = lc;
		putspace(n * 2);
		put1(O_CASEBEG);
		for (i=0; i<m; i++)
			put( 3 , O_CASE1 + (w >> 1), ctab[i].clong);
		put1(O_CASEEND);
	}
	csend = getlab();
	put2(O_TRA, csend);
	/*
	 * Free the case
	 * table space.
	 */
	free(ctab);
	/*
	 * Generate code for each
	 * statement. Patch branch
	 * table to beginning of each
	 * statement and follow each
	 * statement with a branch back
	 * to the TRA above.
	 */
	nr = 1;
	for (cl = r[3]; cl != NIL; cl = cl[2]) {
		cs = cl[1];
		if (cs == NIL)
			continue;
		if (p != NIL)
			for (cs = cs[2]; cs != NIL; cs = cs[2]) {
				patchfil(brtab - 1, lc - brtab0);
				brtab++;
			}
		cs = cl[1];
		putcnt();
		level++;
		statement(cs[3]);
		nr &= noreach;
		noreach = 0;
		put2(O_TRA, csend);
		level--;
		if (gotos[cbn])
			ungoto();
	}
	/*
	 * Patch the termination branch
	 */
	patch(csend);
	noreach = nr;
	if (goc != gocnt)
		putcnt();
}
