/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: parameters.c 1.4 90/04/13 $"

#include <stdio.h>
#include "config.h"
#include "expression.h"			/* expression types and fns */
#include "symbols.h"			/* symbol table functions */
#include "allocate.h"			/* perm_alloc(), perm_str(), */
					/* new_exprlist() definitions */

#define	SYMBOL_HASHENTS	100		/* #of symbol table hash entries */

#define	ENV_PAR		0		/* environment parameter */
#define	INT_PAR		1		/* integer parameter */

extern int strcmp();
extern char *strchr();			/* search for character */
extern char *current_product_name();	/* return name of current product */

/*
 *  Configurable parameter symbol table entry:
 */
struct symbol {
	char sym_type;			/* parameter type */
	boolean sym_evaluated;		/* boolean "symbol evaluated" */
	boolean sym_evaluating;		/* boolean "symbol being evaluated" */
	boolean sym_undeclared;		/* boolean "symbol never defined" */
	short sym_visibility;		/* parameter visibility level */
	char *sym_name;			/* parameter name */
	char *sym_description;		/* parameter description directive */
	struct expr *sym_dflt;		/* parameter default */
	struct expr *sym_newdflt;	/* parameter new_default */
	struct exprlist *sym_adjust;	/* parameter adjustments */
	struct expr *sym_set;		/* parameter "set absolutely" expr */
	struct exprlist *sym_lbound;	/* parameter low bounds */
	struct exprlist *sym_hbound;	/* parameter high bounds */
	struct symbol *sym_dependency;	/* parameter depends on.... */
	struct symbol *sym_nextselect;	/* next parameter in selected set */
	struct {			/* parameter files */
		char *declare;		/* file for declaration directive */
		char *dflt;		/* file for default directive */
		char *newdflt;		/* file for new_default directive */
		char *set;		/* file for set directive */
	} sym_file;
	struct {			/* parameter products */
		char *declare;		/* product for declaration directive */
		char *dflt;		/* product for default directive */
		char *newdflt;		/* product for new_default directive */
		char *set;		/* product for set directive */
	} sym_product;
	struct {			/* parameter values */
		long dflt;		/* default parameter value */
		long newdflt;		/* new_default parameter value */
		long max;		/* max parameter value */
		long min;		/* min parameter value */
		long set;		/* set parameter value */
		long final;		/* final parameter value */
	} sym_val;
};

/*
 *  Initialize a symbol table entry:
 */
#define INIT_SYM(symp, typ, dcl, visibility, name, descr) \
	(symp)->sym_type = (typ); \
	(symp)->sym_evaluated = FALSE; \
	(symp)->sym_evaluating = FALSE; \
	(symp)->sym_undeclared = (dcl); \
	(symp)->sym_visibility = (visibility); \
	(symp)->sym_name = (name); \
	(symp)->sym_description = (descr); \
	(symp)->sym_dflt = (struct expr *)NULL; \
	(symp)->sym_newdflt = (struct expr *)NULL; \
	(symp)->sym_adjust = (struct exprlist *)NULL; \
	(symp)->sym_set = (struct expr *)NULL; \
	(symp)->sym_lbound = (struct exprlist *)NULL; \
	(symp)->sym_hbound = (struct exprlist *)NULL; \
	(symp)->sym_nextselect = (struct symbol *)NULL; \
	(symp)->sym_file.declare = Ffile; \
	(symp)->sym_product.declare = current_product_name();

/*
 *  Assertion structure:
 */
struct assert {
	struct assert *as_next;		/* next assertion */
	char as_relop;			/* relational operator */
	struct expr *as_lexpr;		/* left expression */
	struct expr *as_rexpr;		/* right expression */
	char *as_text;			/* assertion text */
	char *as_file;			/* file where assertion defined */
};

static opaque_ptr parameters = (opaque_ptr)NULL;
static struct symbol *environp = (struct symbol *)NULL;
static boolean selectbyfile = FALSE;
static struct symbol *firstselect = (struct symbol *)NULL;
static struct symbol *lastselect = (struct symbol *)NULL;
static struct assert *assertions = (struct assert *)NULL;
static struct assert *last_assertion = (struct assert *)NULL;


/*
 *  Indicate that the parameters are to be selected by file.
 */
void
selecting_params()
{
	selectbyfile = TRUE;
}


/*
 *  The "environment" directive has been parsed.
 *  Remember the string defining the environment.
 */
void
set_environment (env)
char *env;
{
	char error_message[1024];

	if (parameters == (opaque_ptr)NULL)
		parameters = create_symtab(SYMBOL_HASHENTS);
	if (environp != (struct symbol *)NULL) {
		(void)sprintf(error_message,
			       "redefinition of ENVIRONMENT as [%s] ignored.",
				env);
		yyerror(error_message);
	} else {
		environp = (struct symbol *) perm_alloc(sizeof(struct symbol));
		env = perm_str(env);
		if (insert_symtab(parameters, env,
				 (opaque_ptr)environp) != (opaque_ptr)NULL) {
			fatal("Environment already defined as parameter.\n");
			exit(1);
		}
		INIT_SYM(environp, ENV_PAR, FALSE, HIGHEST_VISIBILITY, env, "");
		environp->sym_val.final = 1;
	}
}


/*
 *  Declare a parameter.
 */
void
declare_par (param, visibility, select, typ, descr, explicit)
char *param;				/* parameter name */
int visibility;				/* parameter visibility */
boolean select;				/* parameter selected by -f option */
char *typ;				/* parameter type */
char *descr;				/* parameter description */
boolean explicit;			/* parameter explicitly declared */
{
	char error_message[1024];
	register struct symbol *sym;
	register char postref;		/* boolean "postref" declaration */

	if (parameters == (opaque_ptr)NULL)
		parameters = create_symtab(SYMBOL_HASHENTS);
	postref = 0;
	if (strcmp(typ, "int") != 0) {
		(void)sprintf(error_message, "%s: [%s]\n\t%s: [%s]",
			"Attempt to declare parameter", param,
			"as unsupported type", typ);
		yyerror(error_message);
		return;
	}
	if (strchr(descr, '@') != (char *)NULL) {
		(void)sprintf(error_message, "%s\n\t%s: [%s] %s\n\t%s: [%s]\n.",
			"IMPLEMENTATION RESTRICTION:",
			"Attempt to declare parameter", param,
			"with '@' character embedded",
			"in description", descr);
		yyerror(error_message);
		return;
	}
	if (explicit && param[0] == '_') {
		(void)sprintf(error_message, "%s: [%s]\n\t%s.\n",
			"Attempt to declare parameter with invalid name", param,
			"Explicit parameter names cannot begin with '_'");
		yyerror(error_message);
		return;
	}
	sym = (struct symbol *)search_symtab(parameters, param);
	if (sym != (struct symbol *)NULL) {
		/*
		 *  Symbol defined as environment, previously referenced,
		 *  or previously declared.
		 */
		switch (sym->sym_type) {
		case ENV_PAR:
			(void)sprintf(error_message, "%s: [%s]\n\t%s",
				"Parameter", param,
				"already declared as ENVIRONMENT.");
			yyerror(error_message);
			return;
		case INT_PAR:
			if (sym->sym_undeclared) {
				(void)sprintf(error_message,
					"%s: [%s]\n\t%s",
					"Parameter", param,
					"declared after reference.");
				yyerror(error_message);
				postref = 1;
			} else {
				(void)sprintf(error_message,
					"%s: [%s]\n\t%s",
					"Parameter", param,
					"already declared.");
				yyerror(error_message);
				return;
			}
			break;
		default:
			fatal("declare_par(): internal error\n");
			exit(1);
		}
	}
	if (explicit && descr[0] == '\0') {
		(void)sprintf(error_message, "%s: [%s] %s",
			"Declaration for parameter", param,
			"has null description.");
		yyerror(error_message);
	}
	descr = perm_str(descr);
	if (!postref) {
		param = perm_str(param);
		sym = (struct symbol *) perm_alloc(sizeof(struct symbol));
		if (insert_symtab(parameters, param,
				  (opaque_ptr)sym) != (opaque_ptr)NULL) {
			fatal("declare_par(): internal error\n");
			exit(1);
		}
	}
	INIT_SYM(sym, INT_PAR, FALSE, visibility, param, descr);
	if (select) {
		/*
		 *  This parameter is in a product selected by the
		 *  "-f" command-line option.
		 */
		if (lastselect == (struct symbol *)NULL)
			firstselect = sym;
		else
			lastselect->sym_nextselect = sym;
		lastselect = sym;
	}
	if (DEBUG_LEVEL(16))
		printf("Declare %s param: [%s] visibility: [%d] type: [%s]\n",
			explicit ? "explicit" : "implicit",
			param, visibility, typ);
}


/*
 *  default and new_default directives:
 */
void
default_par (param, new, expr, lowp, highp, explicit)
char *param;				/* parameter name */
boolean new;				/* TRUE if "new_default" else FALSE */
struct expr *expr;			/* expression */
struct expr *lowp;			/* ptr to lower bound (if defined) */
struct expr *highp;			/* ptr to high bound (if defined) */
boolean explicit;			/* parameter explicitly declared */
{
	static void toss_exprs();	/* toss (discard) expressions */
	char error_message[1024];
	register struct symbol *sym;

	sym = (struct symbol *)NULL;
	if (parameters != (opaque_ptr)NULL)
		sym = (struct symbol *)search_symtab(parameters, param);
	if (sym == (struct symbol *)NULL || sym->sym_undeclared) {
		/*
		 *  Symbol not declared.
		 */
		(void)sprintf(error_message, "%s: [%s] is not declared.",
			"Parameter", param);
		yyerror(error_message);
		toss_exprs(expr, lowp, highp);
		return;
	}
	/*
	 *  Symbol is declared.
	 */
	switch (sym->sym_type) {
	case ENV_PAR:
		(void)sprintf(error_message, "%s: [%s] %s\n\t%s",
			"Parameter", param, "declared as ENVIRONMENT.",
			"No value can be assigned.");
		yyerror(error_message);
		toss_exprs(expr, lowp, highp);
		return;
	case INT_PAR:
		break;
	default:
		fatal("default_par(): internal error\n");
		exit(1);
	}
	if (new) {
		if (sym->sym_newdflt != (struct expr *)NULL) {
			(void)sprintf(error_message,
				"%s: [%s] %s\n\t%s: [%s].",
				"Parameter", param,
				"already assigned a NEW_DEFAULT value",
				"in file", sym->sym_file.newdflt);
			yyerror(error_message);
			toss_exprs(expr, lowp, highp);
			return;
		}
		sym->sym_newdflt = expr;
		if (explicit) {
			sym->sym_file.newdflt = Ffile;
			sym->sym_product.newdflt = current_product_name();
		} else {
			sym->sym_file.newdflt = sym->sym_file.declare;
			sym->sym_product.newdflt = sym->sym_product.declare;
		}
	} else {
		if (sym->sym_dflt != (struct expr *)NULL) {
			(void)sprintf(error_message,
				"%s: [%s] %s\n\t%s: [%s].",
				"Parameter", param,
				"already assigned a DEFAULT value",
				"in file", sym->sym_file.dflt);
			yyerror(error_message);
			toss_exprs(expr, lowp, highp);
			return;
		}
		sym->sym_dflt = expr;
		if (explicit) {
			sym->sym_file.dflt = Ffile;
			sym->sym_product.dflt = current_product_name();
		} else {
			sym->sym_file.dflt = sym->sym_file.declare;
			sym->sym_product.dflt = sym->sym_product.declare;
		}
	}
	if (DEBUG_LEVEL(16)) {
		if (new)
			printf("New_default parameter: [%s]\n", param);
		else
			printf("Default parameter: [%s]\n", param);
		if (DEBUG_LEVEL(24)) {
			printf(" Default expression: ");
			print_expr(stdout, expr);
			putchar('\n');
		}
	}
	if (lowp != (struct expr *)NULL) {
		new_exprlist(&sym->sym_lbound, lowp);
		if (DEBUG_LEVEL(24)) {
			printf(" Low_bound expression: ");
			print_expr(stdout, lowp);
			putchar('\n');
		}
	}
	if (highp != (struct expr *)NULL) {
		new_exprlist(&sym->sym_hbound, highp);
		if (DEBUG_LEVEL(24)) {
			printf(" High_bound expression: ");
			print_expr(stdout, highp);
			putchar('\n');
		}
	}
}


/*
 *  adjust parameter directive:
 */
void
adjust_par (param, expr)
char *param;				/* parameter name */
struct expr *expr;			/* expr */
{
	char error_message[1024];
	register struct symbol *sym;

	sym = (struct symbol *)NULL;
	if (parameters != (opaque_ptr)NULL)
		sym = (struct symbol *)search_symtab(parameters, param);
	if (sym == (struct symbol *)NULL || sym->sym_undeclared) {
		/*
		 *  Symbol not declared.
		 */
		(void)sprintf(error_message, "%s: [%s] is not declared.",
			"Parameter", param);
		yyerror(error_message);
		free_expr(expr);
		return;
	}
	/*
	 *  Symbol is declared.
	 */
	switch (sym->sym_type) {
	case ENV_PAR:
		(void)sprintf(error_message, "%s: [%s] %s\n\t%s",
			"Parameter", param, "declared as ENVIRONMENT.",
			"No value can be assigned.");
		yyerror(error_message);
		free_expr(expr);
		return;
	case INT_PAR:
		break;
	default:
		fatal("adjust_par(): internal error\n");
		exit(1);
	}
	new_exprlist(&sym->sym_adjust, expr);
	if (DEBUG_LEVEL(16)) {
		printf("Adjust parameter: [%s]\n", param);
		if (DEBUG_LEVEL(24)) {
			printf(" Adjust expression: ");
			print_expr(stdout, expr);
			putchar('\n');
		}
	}
}


void
set_par (param, expr)
char *param;				/* parameter name */
struct expr *expr;			/* expr */
{
	char error_message[1024];
	register struct symbol *sym;

	sym = (struct symbol *)NULL;
	if (parameters != (opaque_ptr)NULL)
		sym = (struct symbol *)search_symtab(parameters, param);
	if (sym == (struct symbol *)NULL || sym->sym_undeclared) {
		/*
		 *  Symbol not declared.
		 */
		(void)sprintf(error_message, "%s: [%s] is not declared.",
			"Parameter", param);
		yyerror(error_message);
		free_expr(expr);
		return;
	}
	/*
	 *  Symbol is declared.
	 */
	switch (sym->sym_type) {
	case ENV_PAR:
		(void)sprintf(error_message, "%s: [%s] %s\n\t%s",
			"Parameter", param, "declared as ENVIRONMENT.",
			"No value can be assigned.");
		yyerror(error_message);
		free_expr(expr);
		return;
	case INT_PAR:
		break;
	default:
		fatal("set_par(): internal error\n");
		exit(1);
	}
	if (sym->sym_set != (struct expr *)NULL) {
		(void)sprintf(error_message,
			"%s: [%s] %s\n\t%s: [%s].",
			"Parameter", param,
			"already assigned a SET value",
			"in file", sym->sym_file.set);
		yyerror(error_message);
		free_expr(expr);
		return;
	}
	sym->sym_set = expr;
	sym->sym_file.set = Ffile;
	sym->sym_product.set = current_product_name();
	if (DEBUG_LEVEL(16)) {
		printf("Set parameter: [%s]\n", param);
		if (DEBUG_LEVEL(24)) {
			printf(" Set expression: ");
			print_expr(stdout, expr);
			putchar('\n');
		}
	}
}


void
bound_par (param, lowp, highp)
char *param;				/* parameter name */
struct expr *lowp;			/* ptr to lower bound (if defined) */
struct expr *highp;			/* ptr to high bound (if defined) */
{
	static void toss_exprs();	/* toss (discard) expressions */
	char error_message[1024];
	register struct symbol *sym;

	sym = (struct symbol *)NULL;
	if (parameters != (opaque_ptr)NULL)
		sym = (struct symbol *)search_symtab(parameters, param);
	if (sym == (struct symbol *)NULL || sym->sym_undeclared) {
		/*
		 *  Symbol not declared.
		 */
		(void)sprintf(error_message, "%s: [%s] is not declared.",
			"Parameter", param);
		yyerror(error_message);
		toss_exprs(lowp, highp, (struct expr *)NULL);
		return;
	}
	/*
	 *  Symbol is declared.
	 */
	switch (sym->sym_type) {
	case ENV_PAR:
		(void)sprintf(error_message, "%s: [%s] %s\n\t%s",
			"Parameter", param, "declared as ENVIRONMENT.",
			"No value can be assigned.");
		yyerror(error_message);
		toss_exprs(lowp, highp, (struct expr *)NULL);
		return;
	case INT_PAR:
		break;
	default:
		fatal("bound_par(): internal error\n");
		exit(1);
	}
	if (DEBUG_LEVEL(16))
		printf("Bound parameter: [%s]\n", param);
	if (lowp != (struct expr *)NULL) {
		new_exprlist(&sym->sym_lbound, lowp);
		if (DEBUG_LEVEL(24)) {
			printf(" Low_bound expression: ");
			print_expr(stdout, lowp);
			putchar('\n');
		}
	}
	if (highp != (struct expr *)NULL) {
		new_exprlist(&sym->sym_hbound, highp);
		if (DEBUG_LEVEL(24)) {
			printf(" High_bound expression: ");
			print_expr(stdout, highp);
			putchar('\n');
		}
	}
}


void
assert_par (expr1, relop, expr2, text)
struct expr *expr1;			/* expression #1 */
int relop;				/* relational operator */
struct expr *expr2;			/* expression #2 */
char *text;				/* failure text */
{
	register struct assert *assertp;	/* assertion */

	if (DEBUG_LEVEL(24)) {
		printf("Assertion:\n");
		printf(" Left expression: ");
		print_expr(stdout, expr1);
		printf("\n assertion relational operator is: ");
		switch (relop) {
		case RELOPless:
			printf("<");
			break;
		case RELOPlesseq:
			printf("<=");
			break;
		case RELOPeq:
			printf("=");
			break;
		case RELOPgtreq:
			printf(">=");
			break;
		case RELOPgtr:
			printf(">");
			break;
		default:
			fatal("assert_par(): internal error\n");
			exit(1);
		}
		printf("\n assertion: right expression: ");
		print_expr(stdout, expr2);
		printf("\n assertion text:%s\n", text);
	}
	assertp = (struct assert *)perm_alloc(sizeof(struct assert));
	assertp->as_next = (struct assert *)NULL;
	assertp->as_relop = relop;
	assertp->as_lexpr = expr1;
	assertp->as_rexpr = expr2;
	assertp->as_text = text;
	assertp->as_file = Ffile;
	if (assertions == (struct assert *)NULL)
		assertions = assertp;
	else
		last_assertion->as_next = assertp;
	last_assertion = assertp;
}


/*
 *  Return a pointer to the named parameter's symbol table entry
 *  if the named parameter (or environment) is defined.
 *
 *  If the named configuration parameter does not exist, then a pointer
 *  to a "dummy" symbol entry created for the named parameter is
 *  returned.  In this latter case, the dummy symbol entry is created
 *  to prevent multiple "symbol not defined" messages for the same
 *  undefined symbol.
 */
struct symbol *
reference_par(name)
char *name;				/* name of symbol */
{
	char error_message[1024];
	register struct symbol *sym;

	if (parameters == (opaque_ptr)NULL)
		parameters = create_symtab(SYMBOL_HASHENTS);
	sym = (struct symbol *)search_symtab(parameters, name);
	if (sym == (struct symbol *)NULL) {
		sym = (struct symbol *)perm_alloc(sizeof(struct symbol));
		(void)sprintf(error_message, "%s: [%s].",
			"Expression refers to undeclared parameter", name);
		yyerror(error_message);
		name = perm_str(name);
		if (insert_symtab(parameters, name,
				 (opaque_ptr)sym) != (opaque_ptr)NULL) {
			fatal("reference_par(): internal error\n");
			exit(1);
		}
		INIT_SYM(sym, INT_PAR, TRUE, LOWEST_VISIBILITY, name, "");
	} else if (sym->sym_type == ENV_PAR) {
		(void)sprintf(error_message, "%s: [%s].\n\t%s.",
			"Expression refers to system ENVIRONMENT",
			sym->sym_name,
			"ENVIRONMENT definition has no numerical value");
		yyerror(error_message);
	}
	return(sym);
}


/*
 *  Return a pointer to the name of the given configuration parameter.
 */
char *
parameter_name(sym)
register struct symbol *sym;		/* pointer to symbol */
{
	return (sym->sym_name);
}


/*
 *  Return the value of the given configuration parameter.
 */
expr_type
parameter_value(sym, forsym)
register struct symbol *sym;		/* pointer to symbol */
struct symbol *forsym;			/* 'sym' is evaluated for 'forsym' */
{
	register struct exprlist *exprlistp;
	register long longval;		/* long integer expression value */
	boolean minmax_defined;		/* minimum-maximum value defined flag */

	if (sym->sym_undeclared) {
		fatal("parameter_value(): internal error.\n");
		exit(1);
	}
	if (sym->sym_evaluated)
		return((expr_type)sym->sym_val.final);
	sym->sym_dependency = forsym;
	if (sym->sym_evaluating) {
		/*
		 *  Circular parameter dependency detected.
		 */
		forsym = sym;
		fflush(stdout);		/* In case stdout > stderr */
		fprintf(stderr,
			"ERROR: circular parameter dependency detected.\n");
		do {
			fprintf(stderr, "\t%s: [%s] required for %s [%s].\n",
				"Parameter", sym->sym_name,
				"parameter", sym->sym_dependency->sym_name);
			sym = sym->sym_dependency;
		} while (sym != forsym);
		sym->sym_evaluated = TRUE;
		sym->sym_val.final = 1;
		Trouble = TRUE;
		return((expr_type)1);
	}
	/*
	 *  The symbol has not been previously evaluated.
	 */
	sym->sym_evaluating = TRUE;	/* we're evaluating it now */
	/*
	 *  We always evaluate all expressions (to detect circular
	 *  references), even though "set" takes precedence over "default",
	 *  "new_default", and "adjust" directives, and "new_default" takes
	 *  precedence of "default."
	 */
	if (sym->sym_dflt != (struct expr *)NULL) {
		longval = (long)evaluate(sym->sym_dflt, sym);
		sym->sym_val.dflt = longval;
		sym->sym_evaluated = TRUE;
	}
	if (sym->sym_newdflt != (struct expr *)NULL) {
		longval = (long)evaluate(sym->sym_newdflt, sym);
		sym->sym_val.newdflt = longval;
		sym->sym_evaluated = TRUE;
	}
	if (sym->sym_set != (struct expr *)NULL) {
		longval = (long)evaluate(sym->sym_set, sym);
		sym->sym_val.set = longval;
		sym->sym_evaluated = TRUE;
	}
	if (!sym->sym_evaluated) {
		fflush(stdout);		/* In case stdout > stderr */
		fprintf(stderr, "No DEFAULT, NEW_DEFAULT, or SET directive\n");
		fprintf(stderr, "defined for parameter: [%s].\n",
				sym->sym_name);
		sym->sym_evaluated = TRUE;
		Trouble = TRUE;
		return((expr_type)1);
	}
	/*
	 *  Process ADJUST directives.
	 */
	exprlistp = sym->sym_adjust;
	while (exprlistp != (struct exprlist *)NULL) {
		exprlistp->exl_val = (long)evaluate(exprlistp->exl_expr, sym);
		if (sym->sym_set == (struct expr *)NULL)
			longval += exprlistp->exl_val;
		exprlistp = exprlistp->exl_next;
	}
	/*
	 *  Process BOUND directives.
	 */
	minmax_defined = FALSE;
	sym->sym_val.final = longval;
	exprlistp = sym->sym_lbound;
	while (exprlistp != (struct exprlist *)NULL) {
		longval = (long)evaluate(exprlistp->exl_expr, sym);
		exprlistp->exl_val = longval;
		if (!minmax_defined || longval > sym->sym_val.min) {
			minmax_defined = TRUE;
			sym->sym_val.min = longval;
			if (longval > sym->sym_val.final) {
				fflush(stdout);	/* In case stdout > stderr */
				fprintf(stderr,
					"WARNING: Parameter: [%s] raised ",
						sym->sym_name);
				fprintf(stderr, "to minimum bound defined\n");
				fprintf(stderr, "\tin file: [%s] ",
						exprlistp->exl_file);
				fprintf(stderr, "for product: [%s].\n",
						exprlistp->exl_product);
				Warning = TRUE;
				sym->sym_val.final = sym->sym_val.min = longval;
			}
		}
		exprlistp = exprlistp->exl_next;
	}
	minmax_defined = FALSE;
	exprlistp = sym->sym_hbound;
	while (exprlistp != (struct exprlist *)NULL) {
		longval = (long)evaluate(exprlistp->exl_expr, sym);
		exprlistp->exl_val = longval;
		if (!minmax_defined || longval < sym->sym_val.max) {
			minmax_defined = TRUE;
			sym->sym_val.max = longval;
			if (longval < sym->sym_val.final) {
				if (sym->sym_lbound != (struct exprlist *)NULL
				    && sym->sym_val.min > longval) {
					fflush(stdout);	/* If stdout > stderr */
					fprintf(stderr,
						"ERROR: Bounds intersection ");
					fprintf(stderr,"for parameter: [%s]\n",
							 sym->sym_name);
					fprintf(stderr,"\tis the empty set.\n");
					Trouble = TRUE;
				} else {
					fflush(stdout);	/* If stdout > stderr */
					fprintf(stderr,
						"WARNING: Parameter: [%s] ",
							 sym->sym_name);
					fprintf(stderr,
						"lowered to maximum bound ");
					fprintf(stderr,
						"defined\n\tin file: [%s] ",
							 exprlistp->exl_file);
					fprintf(stderr, "for product: [%s].\n",
						exprlistp->exl_product);
					Warning = TRUE;
					sym->sym_val.final = longval;
				}
			}
		}
		exprlistp = exprlistp->exl_next;
	}
	return((expr_type)sym->sym_val.final);
}


/* 
 *  Write parameter definitions.
 */
void
write_params(param_file_name, expr_file_name)
char *param_file_name;				/* parameters file */
char *expr_file_name;				/* expression file */
{
	static void write_param_dcl();		/* write param declaration */
	static void write_param_exprs();	/* write param expressions */
	register opaque_ptr opaque_symbolp;	/* current symbol */
	struct symbol *symbol_datap;		/* ptr to symbol data */
	register struct symbol *sym;		/* ptr to symbol data */
	char *sym_name;				/* symbol name */
	FILE *param_file;
	FILE *expr_file;

	if (parameters == (opaque_ptr)NULL)	/* No parameters */
		return;
	param_file = (FILE *)NULL;
	if (param_file_name != (char *)NULL) {
		param_file = fopen(param_file_name, "w+");
		if (param_file == (FILE *)NULL) {
			fatal(
			"Cannot create parameter declaration file: [%s].\n",
			      param_file_name);
			exit(1);
		}
	}
	expr_file = (FILE *)NULL;
	if (expr_file_name != (char *)NULL) {
		if (param_file_name != (char *)NULL &&
		    strcmp(param_file_name, expr_file_name) == 0) {
			expr_file = fopen(expr_file_name, "a+");
		} else {
			expr_file = fopen(expr_file_name, "w+");
		}
		if (expr_file == (FILE *)NULL) {
			fatal(
			"Cannot create parameter expression file: [%s].\n",
			      expr_file_name);
			exit(1);
		}
	}
	if (param_file != (FILE *)NULL) {
		if (selectbyfile) {
			/*
			 *  Print-out only those parameters in the
			 *  selected set.
			 */
			sym = firstselect;
			while (sym != (struct symbol *)NULL) {
				write_param_dcl(param_file, sym);
				sym = sym->sym_nextselect;
			}
		} else {
			/*
			 *  Print-out all parameter declarations.
			 */
			opaque_symbolp = first_symtab(parameters,
						&sym_name, &symbol_datap);
			if (symbol_datap->sym_type != ENV_PAR) {
				fatal("write_params(): internal error.\n");
				exit(1);
			}
			opaque_symbolp = next_symtab(opaque_symbolp,
						&sym_name, &symbol_datap);
			while (opaque_symbolp != (opaque_ptr)NULL) {
				write_param_dcl(param_file, symbol_datap);
				opaque_symbolp = next_symtab(opaque_symbolp,
						&sym_name, &symbol_datap);
			}
		}
	}
	if (expr_file != (FILE *)NULL) {
		if (selectbyfile) {
			/*
			 *  Print-out only those parameters in the
			 *  selected set.
			 */
			sym = firstselect;
			while (sym != (struct symbol *)NULL) {
				write_param_exprs(expr_file, sym);
				sym = sym->sym_nextselect;
			}
		} else {
			/*
			 *  Print-out all parameter declarations.
			 */
			opaque_symbolp = first_symtab(parameters,
						&sym_name, &symbol_datap);
			if (symbol_datap->sym_type != ENV_PAR) {
				fatal("write_params(): internal error.\n");
				exit(1);
			}
			opaque_symbolp = next_symtab(opaque_symbolp,
						&sym_name, &symbol_datap);
			while (opaque_symbolp != (opaque_ptr)NULL) {
				write_param_exprs(expr_file, symbol_datap);
				opaque_symbolp = next_symtab(opaque_symbolp,
						&sym_name, &symbol_datap);
			}
		}
	}
	if (param_file != (FILE *)NULL) {
		if (ferror(param_file) || fclose(param_file)) {
			fflush(stdout);
			fprintf(stderr,"Error writing parameters file: [%s].\n",
				param_file_name);
			Trouble = TRUE;
		}
	}
	if (expr_file != (FILE *)NULL) {
		if (ferror(expr_file) || fclose(expr_file)) {
			fflush(stdout);
			fprintf(stderr,"Error writing expression file: [%s].\n",
				expr_file_name);
			Trouble = TRUE;
		}
	}
}


/*
 *  Write out a parameter declaration, and its present value,
 *  minimum value, and maximum value.
 */
static void
write_param_dcl(param_file, sym)
register FILE *param_file;
register struct symbol *sym;
{
	if (Visibility && Visibility < sym->sym_visibility)
		return;
	/*NAME@product@file@short description@#value@#min@#max*/
	fprintf(param_file, "%s@%s@%s@%s@%d@%ld",
		sym->sym_name,
		sym->sym_product.declare,
		sym->sym_file.declare,
		sym->sym_description,
		(int)sym->sym_visibility,
		(long)sym->sym_val.final);
	if (sym->sym_lbound != (struct exprlist *)NULL)
		fprintf(param_file, "@%ld", (long)sym->sym_val.min);
	else
		putc('@', param_file);
	if (sym->sym_hbound != (struct exprlist *)NULL)
		fprintf(param_file, "@%ld", (long)sym->sym_val.max);
	else
		putc('@', param_file);
	putc('\n', param_file);
}


/*
 *  Write all directives referring to the same symbol
 *  to the designated file.
 */
static void
write_param_exprs(expr_file, sym)
register FILE *expr_file;			/* expression file stream */
register struct symbol *sym;			/* symbol to write */
{
	register struct exprlist *exprlistp;

	if (Visibility && Visibility < sym->sym_visibility)
		return;
	if (sym->sym_type != INT_PAR) {		/* must be integer parameter */
		fatal("write_param_exprs(): internal error.\n");
		exit(1);
	}
	if (sym->sym_undeclared)		/* referenced, but undeclared */
		return;
	if (sym->sym_dflt != (struct expr *)NULL) {
		/* NAME@product@file@directive@numerical value@expression */
		fprintf(expr_file, "%s@%s@%s@default@%ld@",
			sym->sym_name,
			sym->sym_product.dflt,
			sym->sym_file.dflt,
			(long) sym->sym_val.dflt);
		print_expr(expr_file, sym->sym_dflt);
		putc('\n', expr_file);
	}
	if (sym->sym_newdflt != (struct expr *)NULL) {
		/* NAME@product@file@directive@numerical value@expression */
		fprintf(expr_file, "%s@%s@%s@new_default@%ld@",
			sym->sym_name,
			sym->sym_product.newdflt,
			sym->sym_file.newdflt,
			(long) sym->sym_val.newdflt);
		print_expr(expr_file, sym->sym_newdflt);
		putc('\n', expr_file);
	}
	if (sym->sym_set != (struct expr *)NULL) {
		/* NAME@product@file@directive@numerical value@expression */
		fprintf(expr_file, "%s@%s@%s@set@%ld@",
			sym->sym_name,
			sym->sym_product.set,
			sym->sym_file.set,
			(long) sym->sym_val.set);
		print_expr(expr_file, sym->sym_set);
		putc('\n', expr_file);
	}
	exprlistp = sym->sym_adjust;
	while (exprlistp != (struct exprlist *)NULL) {
		/* NAME@product@file@directive@numerical value@expression */
		fprintf(expr_file, "%s@%s@%s@adjust@%ld@",
			sym->sym_name,
			exprlistp->exl_product,
			exprlistp->exl_file,
			(long) exprlistp->exl_val);
		print_expr(expr_file, exprlistp->exl_expr);
		putc('\n', expr_file);
		exprlistp = exprlistp->exl_next;
	}
	exprlistp = sym->sym_lbound;
	while (exprlistp != (struct exprlist *)NULL) {
		/* NAME@product@file@directive@numerical value@expression */
		fprintf(expr_file, "%s@%s@%s@low bound@%ld@",
			sym->sym_name,
			exprlistp->exl_product,
			exprlistp->exl_file,
			(long) exprlistp->exl_val);
		print_expr(expr_file, exprlistp->exl_expr);
		putc('\n', expr_file);
		exprlistp = exprlistp->exl_next;
	}
	exprlistp = sym->sym_hbound;
	while (exprlistp != (struct exprlist *)NULL) {
		/* NAME@product@file@directive@numerical value@expression */
		fprintf(expr_file, "%s@%s@%s@high bound@%ld@",
			sym->sym_name,
			exprlistp->exl_product,
			exprlistp->exl_file,
			(long) exprlistp->exl_val);
		print_expr(expr_file, exprlistp->exl_expr);
		putc('\n', expr_file);
		exprlistp = exprlistp->exl_next;
	}
}


/*
 *  Write the configurable parameters section of the config.h file.
 */
void
print_config_params(config_fp)
register struct FILE *config_fp;
{
	register opaque_ptr opaque_symbolp;	/* current symbol */
	struct symbol *symbol_datap;		/* ptr to symbol data */
	char *sym_name;				/* symbol name */

	if (parameters == (opaque_ptr)NULL)	/* no parameters */
		return;
	fprintf(config_fp, "\n/*\n");
	fprintf(config_fp, " *  Configurable parameters:\n");
	fprintf(config_fp, "*/\n");
	opaque_symbolp = first_symtab(parameters, &sym_name, &symbol_datap);
	while (opaque_symbolp != (opaque_ptr)NULL) {
		fprintf(config_fp, "#define %-20s\t%ld\n",
			symbol_datap->sym_name, symbol_datap->sym_val.final);
		opaque_symbolp = next_symtab(opaque_symbolp,
					     &sym_name, &symbol_datap);
	}
}


/* 
 *  Evaluate all parameters.
 */
void
eval_params()
{
	static void assertion_failed();		/* assert failed fn */
	register opaque_ptr opaque_symbolp;	/* current symbol */
	register struct assert *assertp;	/* ptr to assertion */
	expr_type leval;			/* left expression value */
	expr_type reval;			/* right expression value */
	struct symbol *symbol_datap;		/* ptr to symbol data */
	char *sym_name;				/* symbol name */

	if (parameters == (opaque_ptr)NULL)	/* no parameters */
		return;
	opaque_symbolp = first_symtab(parameters, &sym_name, &symbol_datap);
	while (opaque_symbolp != (opaque_ptr)NULL) {
		if (symbol_datap->sym_type != ENV_PAR) {
			(void)parameter_value(symbol_datap,
					      (struct symbol *)NULL);
		}
		opaque_symbolp = next_symtab(opaque_symbolp,
					     &sym_name, &symbol_datap);
	}
	if (!Trouble) {
		assertp = assertions;
		while (assertp != (struct assert *)NULL) {
			leval = evaluate(assertp->as_lexpr,
					 (struct symbol *)NULL);
			reval = evaluate(assertp->as_rexpr,
					 (struct symbol *)NULL);
			switch (assertp->as_relop) {
			case RELOPless:
				if (leval >= reval)
					assertion_failed(assertp);
				break;
			case RELOPlesseq:
				if (leval > reval)
					assertion_failed(assertp);
				break;
			case RELOPeq:
				if (leval != reval)
					assertion_failed(assertp);
				break;
			case RELOPgtreq:
				if (leval < reval)
					assertion_failed(assertp);
				break;
			case RELOPgtr:
				if (leval <= reval)
					assertion_failed(assertp);
				break;
			default:
				fatal("eval_params(): internal error\n");
				exit(1);
			}
			assertp = assertp->as_next;
		}
	}
}


/*
 *  Assertion failed.
 */
static void
assertion_failed (assertp)
register struct assert *assertp;
{
	fflush(stdout);		/* In case stdout > stderr */
	fprintf(stderr, "Assertion failed: \"");
	print_expr(stderr, assertp->as_lexpr);
	switch (assertp->as_relop) {
	case RELOPless:
		fprintf(stderr, " < ");
		break;
	case RELOPlesseq:
		fprintf(stderr, " <= ");
		break;
	case RELOPeq:
		fprintf(stderr, " = ");
		break;
	case RELOPgtreq:
		fprintf(stderr, " >= ");
		break;
	case RELOPgtr:
		fprintf(stderr, " > ");
		break;
	default:
		fatal("assert_failed(): internal error\n");
		exit(1);
	}
	print_expr(stderr, assertp->as_rexpr);
	fputs("\"\n", stderr);
	fprintf(stderr, "\t%s\n", assertp->as_text);
	fprintf(stderr, "\tAssertion was defined in file: [%s].\n",
			assertp->as_file);
	Trouble = TRUE;
}


/*
 *  Return TRUE if the named parameter or environment is defined.
 *  Otherwise FALSE is returned.
 */
boolean
ifdef(name)
char *name;				/* name of symbol */
{
	if (parameters == (opaque_ptr)NULL) {
		yyerror("ENVIRONMENT must be defined before IFDEF use.");
		return(0);
	}
	if (search_symtab(parameters, name) == (opaque_ptr)NULL)
		return(FALSE);
	return(TRUE);
}


/*
 *  Return TRUE if the named parameter or environment is NOT defined.
 *  Otherwise, FALSE is returned.
 */
boolean
ifndef(name)
char *name;
{
	if (ifdef(name))
		return(FALSE);
	return(TRUE);
}


/*
 *  "Toss" (discard) up to three expression treese.
 */
static void
toss_exprs (expr1, expr2, expr3)
struct expr *expr1;			/* Cannot be NULL */
struct expr *expr2;			/* Can be NULL */
struct expr *expr3;			/* Can be NULL */
{
	free_expr(expr1);
	if (expr2 != (struct expr *)NULL)
		free_expr(expr2);
	if (expr3 != (struct expr *)NULL)
		free_expr(expr3);
}
