/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char sccsid[] = "@(#)const.c	5.2 (Berkeley) 6/21/85";
#endif not lint

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

/*
 * Const enters the definitions
 * of the constant declaration
 * part into the namelist.
 */
#ifndef PI1
constbeg( lineofyconst )
    int	lineofyconst;
{
    static bool	const_order = FALSE;
    static bool	const_seen = FALSE;

/*
 * this allows for multiple declaration
 * parts, unless the "standard" option
 * has been specified.
 * If a routine segment is being compiled,
 * do level one processing.
 */

	if (!progseen)
		level1();
	line = lineofyconst;
	if (parts[ cbn ] & (TPRT|VPRT|RPRT)) {
	    if ( opt( 's' ) ) {
		standard();
		error("Constant declarations should precede type, var and routine declarations");
	    } else {
		if ( !const_order ) {
		    const_order = TRUE;
		    warning();
		    error("Constant declarations should precede type, var and routine declarations");
		}
	    }
	}
	if (parts[ cbn ] & CPRT) {
	    if ( opt( 's' ) ) {
		standard();
		error("All constants should be declared in one const part");
	    } else {
		if ( !const_seen ) {
		    const_seen = TRUE;
		    warning();
		    error("All constants should be declared in one const part");
		}
	    }
	}
	parts[ cbn ] |= CPRT;
}
#endif PI1

const(cline, cid, cdecl)
	int cline;
	register char *cid;
	register struct tnode *cdecl;
{
	register struct nl *np;

#ifdef PI0
	send(REVCNST, cline, cid, cdecl);
#endif
	line = cline;
	gconst(cdecl);
	np = enter(defnl(cid, CONST, con.ctype, con.cival));
#ifndef PI0
	np->nl_flags |= NMOD;
#endif

#ifdef PC
	if (cbn == 1) {
	    stabgconst( cid , line );
	}
#endif PC

#	ifdef PTREE
	    {
		pPointer	Const = ConstDecl( cid , cdecl );
		pPointer	*Consts;

		pSeize( PorFHeader[ nesting ] );
		Consts = &( pDEF( PorFHeader[ nesting ] ).PorFConsts );
		*Consts = ListAppend( *Consts , Const );
		pRelease( PorFHeader[ nesting ] );
	    }
#	endif
	if (con.ctype == NIL)
		return;
	if ( con.ctype == nl + TSTR )
		np->ptr[0] = (struct nl *) con.cpval;
	if (isa(con.ctype, "i"))
		np->range[0] = con.crval;
	else if (isa(con.ctype, "d"))
		np->real = con.crval;
#       ifdef PC
	    if (cbn == 1 && con.ctype != NIL) {
		    stabconst(np);
	    }
#       endif
}

#ifndef PI0
#ifndef PI1
constend()
{

}
#endif
#endif

/*
 * Gconst extracts
 * a constant declaration
 * from the tree for it.
 * only types of constants
 * are integer, reals, strings
 * and scalars, the first two
 * being possibly signed.
 */
gconst(c_node)
	struct tnode *c_node;
{
	register struct nl *np;
	register struct tnode *cn;
	char *cp;
	int negd, sgnd;
	long ci;

	con.ctype = NIL;
	cn = c_node;
	negd = sgnd = 0;
loop:
	if (cn == TR_NIL || cn->sign_const.number == TR_NIL)
		return;
	switch (cn->tag) {
		default:
			panic("gconst");
		case T_MINUSC:
			negd = 1 - negd;
		case T_PLUSC:
			sgnd++;
			cn = cn->sign_const.number;
			goto loop;
		case T_ID:
			np = lookup(cn->char_const.cptr);
			if (np == NLNIL)
				return;
			if (np->class != CONST) {
				derror("%s is a %s, not a constant as required", cn->char_const.cptr, classes[np->class]);
				return;
			}
			con.ctype = np->type;
			switch (classify(np->type)) {
				case TINT:
					con.crval = np->range[0];
					break;
				case TDOUBLE:
					con.crval = np->real;
					break;
				case TBOOL:
				case TCHAR:
				case TSCAL:
					con.cival = np->value[0];
					con.crval = con.cival;
					break;
				case TSTR:
					con.cpval = (char *) np->ptr[0];
					break;
				case NIL:
					con.ctype = NIL;
					return;
				default:
					panic("gconst2");
			}
			break;
		case T_CBINT:
			con.crval = a8tol(cn->char_const.cptr);
			goto restcon;
		case T_CINT:
			con.crval = atof(cn->char_const.cptr);
			if (con.crval > MAXINT || con.crval < MININT) {
				derror("Constant too large for this implementation");
				con.crval = 0;
			}
restcon:
			ci = con.crval;
#ifndef PI0
			if (bytes(ci, ci) <= 2)
				con.ctype = nl+T2INT;
			else	
#endif
				con.ctype = nl+T4INT;
			break;
		case T_CFINT:
			con.ctype = nl+TDOUBLE;
			con.crval = atof(cn->char_const.cptr);
			break;
		case T_CSTRNG:
			cp = cn->char_const.cptr;
			if (cp[1] == 0) {
				con.ctype = nl+T1CHAR;
				con.cival = cp[0];
				con.crval = con.cival;
				break;
			}
			con.ctype = nl+TSTR;
			con.cpval = savestr(cp);
			break;
	}
	if (sgnd) {
		if (isnta((struct nl *) con.ctype, "id"))
			derror("%s constants cannot be signed",
				nameof((struct nl *) con.ctype));
		else {
			if (negd)
				con.crval = -con.crval;
			ci = con.crval;
		}
	}
}

#ifndef PI0
isconst(cn)
	register struct tnode *cn;
{

	if (cn == TR_NIL)
		return (1);
	switch (cn->tag) {
		case T_MINUS:
			cn->tag = T_MINUSC;
			cn->sign_const.number = 
					 cn->un_expr.expr;
			return (isconst(cn->sign_const.number));
		case T_PLUS:
			cn->tag = T_PLUSC;
			cn->sign_const.number = 
					 cn->un_expr.expr;
			return (isconst(cn->sign_const.number));
		case T_VAR:
			if (cn->var_node.qual != TR_NIL)
				return (0);
			cn->tag = T_ID;
			cn->char_const.cptr = 
					cn->var_node.cptr;
			return (1);
		case T_BINT:
			cn->tag = T_CBINT;
			cn->char_const.cptr = 
				cn->const_node.cptr;
			return (1);
		case T_INT:
			cn->tag = T_CINT;
			cn->char_const.cptr = 
				cn->const_node.cptr;
			return (1);
		case T_FINT:
			cn->tag = T_CFINT;
			cn->char_const.cptr = 
				cn->const_node.cptr;
			return (1);
		case T_STRNG:
			cn->tag = T_CSTRNG;
			cn->char_const.cptr = 
				cn->const_node.cptr;
			return (1);
	}
	return (0);
}
#endif
