/* express.c - expression parsing routines for bcc */

/* Copyright (C) 1992 Bruce Evans */

#include "bcc.h"
#include "gencode.h"
#include "parse.h"
#include "reg.h"
#include "sc.h"
#include "scan.h"
#include "table.h"		/* just for charptr for string constant */
#include "type.h"

PRIVATE unsigned insizeof;	/* nest level for getsizeof */
				/* used to avoid aborting undefined idents */
				/* to 0 when they appear in a cpp expression */
				/* under sizeof */

/* names of expression functions are related to the 15 precedence levels */
/* on p49 of K & R */

FORWARD struct nodestruct *cast_exp P((void));
FORWARD struct nodestruct *expr2 P((void));
FORWARD struct nodestruct *exp3to12 P((fastin_pt lprecedence));
FORWARD struct nodestruct *listargs P((void));
FORWARD struct nodestruct *postfix_exp P((bool_pt seenlp));
FORWARD struct nodestruct *primary_exp P((void));
FORWARD struct nodestruct *unary_exp P((void));

PRIVATE struct nodestruct *cast_exp()
{
    struct nodestruct *nodeptr;
    scalar_t scalar;
    struct typestruct *vartype;

    if (sym != LPAREN)
	return unary_exp();
    nextsym();
    if ((vartype = typename()) == NULL)
	return postfix_exp(TRUE);
    rparen();
    scalar = (nodeptr = cast_exp())->nodetype->scalar;
    if (vartype->scalar & INT && scalar & (CHAR | SHORT | INT)
	&& !((vartype->scalar ^ scalar) & UNSIGNED))
    {
/* No ancient switch on low mem systems */
#ifndef VERY_SMALL_MEMORY
	/* In ancient UNIX C, casts remain lvalues */
	if (!ancient)
#endif
	    nodeptr->flags &= ~LVALUE;
	return nodeptr;		/* skip casts that are default promotions */
    }
    return castnode(vartype, nodeptr);
}

PUBLIC struct nodestruct *assignment_exp()
{
    struct nodestruct *lhs;
    op_pt op;

    lhs = expr2();
    if (sym >= ASSIGNOP && sym <= SUBABOP)	/* assign-op syms in order! */
    {
	op = sym;
	nextsym();
	lhs = node(op, lhs, assignment_exp());
    }
    return lhs;
}

PUBLIC struct nodestruct *expression()
{
    struct nodestruct *lhs;

    lhs = assignment_exp();
    while (sym == COMMA)
    {
	nextsym();
	lhs = node(COMMAOP, lhs, assignment_exp());
    }
    return lhs;
}

PRIVATE struct nodestruct *expr2()
{
    struct nodestruct *lhs;
    struct nodestruct *rhs;

    lhs = exp3to12(0);
    if (sym == CONDOP)
    {
	nextsym();
	rhs = expression();
	colon();
	lhs = node(CONDOP, lhs, node(COLONOP, rhs, expr2()));
    }
    return lhs;
}

PRIVATE struct nodestruct *exp3to12(lprecedence)
fastin_pt lprecedence;
{
    struct nodestruct *lhs;
    op_pt op;
    fastin_t rprecedence;

    lhs = cast_exp();
    while (TRUE)
    {
	rprecedence = 0;
	switch (sym)
	{
	case LOGOROP:
	    if ((fastin_t) lprecedence <= 1)
		rprecedence = 2;
	    break;
	case LOGANDOP:
	    if ((fastin_t) lprecedence <= 3)
		rprecedence = 4;
	    break;
	case OROP:
	    if ((fastin_t) lprecedence <= 5)
		rprecedence = 6;
	    break;
	case EOROP:
	    if ((fastin_t) lprecedence <= 7)
		rprecedence = 8;
	    break;
	case AMPERSAND:
	    if ((fastin_t) lprecedence <= 9)
	    {
		sym = ANDOP;
		rprecedence = 10;
	    }
	    break;
	case EQOP:
	case NEOP:
	    if ((fastin_t) lprecedence <= 11)
		rprecedence = 12;
	    break;
	case GEOP:
	case GTOP:
	case LEOP:
	case LTOP:
	    if ((fastin_t) lprecedence <= 13)
		rprecedence = 14;
	    break;
	case SLOP:
	case SROP:
	    if ((fastin_t) lprecedence <= 15)
		rprecedence = 16;
	    break;
	case HYPHEN:
	    if ((fastin_t) lprecedence <= 17)
	    {
		sym = SUBOP;
		rprecedence = 18;
	    }
	    break;
	case ADDOP:
	    if ((fastin_t) lprecedence <= 17)
		rprecedence = 18;
	    break;
	case STAR:
	    if ((fastin_t) lprecedence <= 19)
	    {
		sym = MULOP;
		rprecedence = 20;
	    }
	    break;
	case DIVOP:
	case MODOP:
	    if ((fastin_t) lprecedence <= 19)
		rprecedence = 20;
	    break;
	}
	if (rprecedence == 0)
	    break;
	op = sym;
	nextsym();
	lhs = node(op, lhs, exp3to12(rprecedence));
    }
    return lhs;
}

PRIVATE struct nodestruct *listargs()
{
    struct nodestruct *parent;
    struct nodestruct *nextright;

    if (sym == RPAREN)
    {
	nextsym();
	return NULLNODE;
    }
    parent = node(arg1op, assignment_exp(), NULLNODE);
    nextright = parent;
    while (sym == COMMA)
    {
	nextsym();
	nextright = nextright->right
	    = node(LISTOP, assignment_exp(), NULLNODE);
    }
    rparen();
    return parent;
}

PRIVATE struct nodestruct *postfix_exp(seenlp)
bool_pt seenlp;
{
    struct nodestruct *nodeptr;
    struct symstruct *symptr;

    if (seenlp)
    {
	nodeptr = expression();
	rparen();
    }
    else
	nodeptr = primary_exp();
    while (TRUE)
    {
	switch (sym)
	{
	case DECSYM:
	    nextsym();
	    nodeptr = node(POSTDECOP, nodeptr, NULLNODE);
	    continue;
	case INCSYM:
	    nextsym();
	    nodeptr = node(POSTINCOP, nodeptr, NULLNODE);
	    continue;
	case LBRACKET:
	    nextsym();
	    nodeptr = node(INDIRECTOP, node(ADDOP, nodeptr, expression()),
			   NULLNODE);
	    rbracket();
	    continue;
	case LPAREN:
	    nextsym();
	    nodeptr = node(FUNCOP, nodeptr, listargs());
	    {
		register struct nodestruct *np;

		for (np = nodeptr->right; np != NULL; np = np->right)
		{
		    if (np->nodetype->scalar & RSCALAR)
		    {
			np = nodeptr->left.nodeptr;
			if (np->tag != LEAF)
			    printf_fp = TRUE;
			else
			{
			    unsigned len;
			    register char *name;

			    name = np->left.symptr->name.namep;
			    if ((len = strlen(name)) >= 6
				&& strcmp(name + len - 6, "printf") == 0)
				printf_fp = TRUE;
			}
			break;
		    }
		}
		for (np = nodeptr->right; np != NULL; np = np->right)
		{
		    if (np->nodetype->constructor & POINTER
			&& np->nodetype->nexttype->scalar & RSCALAR)
		    {
			np = nodeptr->left.nodeptr;
			if (np->tag != LEAF)
			    scanf_fp = TRUE;
			else
			{
			    unsigned len;
			    register char *name;

			    name = np->left.symptr->name.namep;
			    if ((len = strlen(name)) >= 5
				&& strcmp(name + len - 5, "scanf") == 0)
				scanf_fp = TRUE;
			}
			break;
		    }
		}
	    }
	    continue;
	case STRUCPTROP:
	    nodeptr = node(INDIRECTOP, nodeptr, NULLNODE);
	case STRUCELTOP:
	    nextsym();
/* On memory constrained systems we save space that would be needed to keep */
/* track of anonymous structure members by not implementing lookup in them */
/* at all. */
#ifdef VERY_SMALL_MEMORY
            gs2name[0] = nodeptr->nodetype->structkey[0];
            gs2name[1] = nodeptr->nodetype->structkey[1];
            if ((gsymptr = findlorg(gs2name)) == NULL)
#else
	    if ((gsymptr = findstrm(nodeptr->nodetype, gs2name)) == NULL)
#endif
	    {
		error("undefined structure element");
		gsymptr = addglb(gs2name, itype);
	    }
	    symptr = exprsym(gsymptr);
	    nextsym();
	    nodeptr = node(STRUCELTOP, nodeptr, leafnode(symptr));
	    continue;
	default:
	    return nodeptr;
	}
    }
}

PRIVATE struct nodestruct *primary_exp()
{
    bool_t isdefined;
    struct nodestruct *nodeptr;
    uoffset_T stringlen;
    struct symstruct *symptr;
    struct symstruct *symptr1;
    bool_t waslparen;

    switch (sym)
    {
    case IDENT:
	if (incppexpr && !insizeof)
	{
cpp_ident:
	    nextsym();
	    return leafnode(constsym((value_t) 0));
	}
	if ((symptr = gsymptr) != NULL)
	    nextsym();
	else
	{
	    symptr = addglb(gsname, fitype);
	    nextsym();
	    if (sym != LPAREN)
	    {
		error2error(symptr->name.namea, " undeclared");
		symptr->indcount = 1;
		symptr->type = itype;
	    }
	}
	symptr1 = exprsym(symptr);
	if (symptr->flags & STATIC && symptr->level != GLBLEVEL)
	{
	    symptr1->flags |= LABELLED;
	    symptr1->offset.offi = 0;
	    symptr1->name.label = symptr->offset.offlabel;
	}
	nodeptr = leafnode(symptr1);
	if (!(nodeptr->nodetype->constructor & (ARRAY | FUNCTION | VOID)))
	    nodeptr->flags = LVALUE;
	return nodeptr;
    case TYPEDEFNAME:
	if (incppexpr && !insizeof)
	    goto cpp_ident;	/* else fall through */
    default:
	error("bad expression");
	constant.value.v = 0;
	constant.type = itype;
    case CHARCONST:
    case INTCONST:		/* this includes enumeration-constants */
	symptr = constsym(constant.value.v);
	symptr->type = constant.type;
	if (!(ltype->scalar & LONG))
	{
	    if (symptr->type == ltype)
		symptr->type = itype;
	    else if (symptr->type == ultype)
		symptr->type = uitype;
	}
	nextsym();
	return leafnode(symptr);
    case DEFINEDSYM:
	waslparen = isdefined = FALSE;
	if (!blanksident())
	{
	    nextsym();
	    if (sym != LPAREN)
		lparen();
	    else
		waslparen = TRUE;
	}
	if (waslparen && !blanksident())
	    needvarname();
	else
	{
	    if ((symptr = findlorg(gsname)) != NULL &&
		symptr->flags == DEFINITION)
		isdefined = TRUE;
	    nextsym();
	}
	if (waslparen)
	    rparen();
	return leafnode(constsym((value_t) isdefined));
    case FLOATCONST:
	symptr = constsym((value_t) 0);
	symptr->type = constant.type;
	symptr->offset.offd = qmalloc(sizeof *symptr->offset.offd);
	*symptr->offset.offd = constant.value.d;
	nextsym();
	return leafnode(symptr);
    case LPAREN:
	nextsym();
	nodeptr = expression();
	rparen();
	return nodeptr;
    case STRINGCONST:
	symptr = constsym((value_t) 0);
	symptr->storage = GLOBAL;
	symptr->flags = LABELLED | STRING;
	/* string length before defstr() or prefix() updates charptr */
	stringlen = charptr - constant.value.s + 1;
	symptr->name.label = defstr(constant.value.s, charptr, FALSE);
	symptr->type = prefix(ARRAY, stringlen, ctype);
	nextsym();
	return leafnode(symptr);
    }
}

PRIVATE struct nodestruct *unary_exp()
{
    value_t size;
    struct typestruct *vartype;

    switch (sym)
    {
    case ADDOP:
	nextsym();
	return cast_exp();
    case AMPERSAND:
	nextsym();
	return node(ADDRESSOP, cast_exp(), NULLNODE);	/* maybe unary_exp */
    case DECSYM:
	nextsym();
	return node(PREDECOP, unary_exp(), NULLNODE);
    case HYPHEN:
	nextsym();
	return node(NEGOP, cast_exp(), NULLNODE);
    case INCSYM:
	nextsym();
	return node(PREINCOP, unary_exp(), NULLNODE);
    case LOGNOTOP:
	nextsym();
	return node(LOGNOTOP, cast_exp(), NULLNODE);
    case NOTOP:
	nextsym();
	return node(NOTOP, cast_exp(), NULLNODE);
    case SIZEOFSYM:
	nextsym();
	++insizeof;
	if (sym != LPAREN)
	    size = unary_exp()->nodetype->typesize;
	else
	{
	    nextsym();
	    if ((vartype = typename()) != NULL)
	    {
		rparen();
		size = vartype->typesize;
	    }
	    else
		size = postfix_exp(TRUE)->nodetype->typesize;
	}
	--insizeof;
	return leafnode(constsym(size));
    case STAR:
	nextsym();
	return node(INDIRECTOP, cast_exp(), NULLNODE);	/* maybe unary_exp */
    }
    return postfix_exp(FALSE);
}
