/*
** Small-C Compiler -- Part 3 -- Expression Analyzer.
** Copyright 1982, 1983, 1985, 1988 J. E. Hendrix
** Copyright 1998 H T Walheim
** All rights reserved.
*/

#include <stdio.h>
#include "cc.h"

#define ST 0   /* is[ST] - symbol table address, else 0 */
#define TI 1   /* is[TI] - type of indirect obj to fetch, else 0 */
#define TA 2   /* is[TA] - type of address, else 0 */
#define TC 3   /* is[TC] - type of constant (INT or UINT), else 0 */
#define CV 4   /* is[CV] - value of constant (+ auxiliary uses) */
#define OP 5   /* is[OP] - code of highest/last binary operator */
#define SA 6   /* is[SA] - stage address of "op 0" code, else 0 */

extern char
 *litq, *glbptr, *lptr,  ssname[NAMESIZE],  quote[2];
extern int
  ch,  csp,  litlab,  litptr,  nch,  op[16],  op2[16],
  opindex,  opsize, *snext;

/***************** lead-in functions *******************/

constexpr(val) int *val; {
  int constant;
  int *before, *start;
  setstage(&before, &start);
  expression(&constant, val);
  clearstage(before, 0);     /* scratch generated code */
  if(constant == 0) error("must be constant expression");
  return constant;
  }

null_is (is)
int is[7];
  {
    int i;

    for (i = 0; i < 7; ++i)
      {
	is[i] = 0;	/* <-- */
      }
  }
    
  
expression(con, val) int *con, *val;
  {
    int i;
    int is[7];

    null_is (is);
    
  if(level1(is)) fetch(is);
  *con = is[TC];
  *val = is[CV];
  }

test(label, parens)  int label, parens;  {
  int is[7];
  int *before, *start;

  null_is (is);
  
  if(parens) need("(");
  while(1) {
    setstage(&before, &start);
    if(level1(is)) fetch(is);
    if(match(",")) clearstage(before, start);
    else break;
    }
  if(parens) need(")");
  if(is[TC]) {             /* constant expression */
    clearstage(before, 0);
    if(is[CV]) return;
    gen(JMPm, label);
    return;
    }
  if(is[SA]) {             /* stage address of "oper 0" code */
    switch(is[OP]) {       /* operator code */
      case EQ12:
      case LE12u: zerojump(EQ10f, label, is); break;
      case NE12:
      case GT12u: zerojump(NE10f, label, is); break;
      case GT12:  zerojump(GT10f, label, is); break;
      case GE12:  zerojump(GE10f, label, is); break;
      case GE12u: clearstage(is[SA], 0);      break;
      case LT12:  zerojump(LT10f, label, is); break;
      case LT12u: zerojump(JMPm,  label, is); break;
      case LE12:  zerojump(LE10f, label, is); break;
      default:    gen(NE10f, label);          break;
      }
    }
  else gen(NE10f, label);
  clearstage(before, start);
  }

/*
** test primary register against zero and jump if false
*/
zerojump(oper, label, is) int oper, label, is[]; {
  clearstage(is[SA], 0);       /* purge conventional code */
  gen(oper, label);
  }

/***************** precedence levels ******************/
#ifdef _MSC_VER
level2();
level3();
level4();
level5();
level6();
level7();
level8();
level9();
level10();
level11();
level12();
level13();
#endif

level1(is) int is[];  {
  int k, is2[7], is3[2], oper, oper2;

  null_is (is2);
  null_is (is3);
  
  k = down1(level2, is);
  if(is[TC])
    {
#ifdef INT32
      gen(GETd1n, is[CV]);
#else
      gen(GETw1n, is[CV]);
#endif
    }
       if(match("|="))  {oper =        oper2 = OR12;}
  else if(match("^="))  {oper =        oper2 = XOR12;}
  else if(match("&="))  {oper =        oper2 = AND12;}
  else if(match("+="))  {oper =        oper2 = ADD12;}
  else if(match("-="))  {oper =        oper2 = SUB12;}
  else if(match("*="))  {oper = MUL12; oper2 = MUL12u;}
  else if(match("/="))  {oper = DIV12; oper2 = DIV12u;}
  else if(match("%="))  {oper = MOD12; oper2 = MOD12u;}
  else if(match(">>=")) {oper =        oper2 = ASR12;}
  else if(match("<<=")) {oper =        oper2 = ASL12;}
  else if(match("="))   {oper =        oper2 = 0;}
  else return k;
                        /* have an assignment operator */
  if(k == 0) {
    needlval();
    return 0;
    }
  is3[ST] = is[ST];
  is3[TI] = is[TI];
  if(is[TI]) {                             /* indirect target */
    if(oper) {                             /* ?= */
      gen(PUSH1, 0);                       /* save address */
      fetch(is);                           /* fetch left side */
      }
    down2(oper, oper2, level1, is, is2);   /* parse right side */
    if(oper) gen(POP2, 0);                 /* retrieve address */
    }
  else {                                   /* direct target */
    if(oper) {                             /* ?= */
      fetch(is);                           /* fetch left side */
      down2(oper, oper2, level1, is, is2); /* parse right side */
      }
    else {                                 /*  = */
      if(level1(is2)) fetch(is2);          /* parse right side */
      }
    }
  store(is3);                              /* store result */
  return 0;
  }

level2(is1)  int is1[]; {
  int is2[7], is3[7], k, flab, endlab, *before, *after;
  
  null_is (is2);
  null_is (is3);
  
  k = down1(level3, is1);                   /* expression 1 */
  if(match("?") == 0) return k;
  dropout(k, NE10f, flab = getlabel(), is1);
  if(down1(level2, is2)) fetch(is2);        /* expression 2 */
  else if(is2[TC])
    {
#ifdef INT32
      gen(GETd1n, is2[CV]);
#else
      gen(GETw1n, is2[CV]);
#endif
    }
  need(":");
  gen(JMPm, endlab = getlabel());
  gen(LABm, flab);
  if(down1(level2, is3)) fetch(is3);        /* expression 3 */
  else if(is3[TC])
    {
#ifdef INT32
      gen(GETd1n, is3[CV]);
#else
      gen(GETw1n, is3[CV]);
#endif
    }
  gen(LABm, endlab);

  is1[TC] = is1[CV] = 0;
  if(is2[TC] && is3[TC]) {                  /* expr1 ? const2 : const3 */
    is1[TA] = is1[TI] = is1[SA] = 0;
    }
  else if(is3[TC]) {                        /* expr1 ? var2 : const3 */
    is1[TA] = is2[TA];
    is1[TI] = is2[TI];
    is1[SA] = is2[SA];
    }
  else if((is2[TC])                         /* expr1 ? const2 : var3 */
       || (is2[TA] == is3[TA])) {           /* expr1 ? same2 : same3 */
    is1[TA] = is3[TA];
    is1[TI] = is3[TI];
    is1[SA] = is3[SA];
    }
  else error("mismatched expressions");
  return 0;
  }

level3 (is) int is[]; {return skim("||", EQ10f, 1, 0, level4,  is);}
level4 (is) int is[]; {return skim("&&", NE10f, 0, 1, level5,  is);}
level5 (is) int is[]; {return down("|",            0, level6,  is);}
level6 (is) int is[]; {return down("^",            1, level7,  is);}
level7 (is) int is[]; {return down("&",            2, level8,  is);}
level8 (is) int is[]; {return down("== !=",        3, level9,  is);}
level9 (is) int is[]; {return down("<= >= < >",    5, level10, is);}
level10(is) int is[]; {return down(">> <<",        9, level11, is);}
level11(is) int is[]; {return down("+ -",         11, level12, is);}
level12(is) int is[]; {return down("* / %",       13, level13, is);}

level13(is)  int is[];  {
  int k;
  char *ptr;
  if(match("++")) {                 /* ++lval */
    if(level13(is) == 0) {
      needlval();
      return 0;
      }
    step(rINC1, is, 0);
    return 0;
    }
  else if(match("--")) {            /* --lval */
    if(level13(is) == 0) {
      needlval();
      return 0;
      }
    step(rDEC1, is, 0);
    return 0;
    }
  else if(match("~")) {             /* ~ */
    if(level13(is)) fetch(is);
    gen(COM1, 0);
    is[CV] = ~ is[CV];
    return (is[SA] = 0);
    }
  else if(match("!")) {             /* ! */
    if(level13(is)) fetch(is);
    gen(LNEG1, 0);
    is[CV] = ! is[CV];
    return (is[SA] = 0);
    }
  else if(match("-")) {             /* unary - */
    if(level13(is)) fetch(is);
    gen(ANEG1, 0);
    is[CV] = -is[CV];
    return (is[SA] = 0);
    }
  else if(match("*")) {             /* unary * */
    if(level13(is)) fetch(is);
    if(ptr = is[ST]) is[TI] = ptr[TYPE];
    else             is[TI] = INT;
    is[SA] =       /* no (op 0) stage address */
    is[TA] =       /* not an address */
    is[TC] = 0;    /* not a constant */
    is[CV] = 1;    /* omit fetch() on func call */
    return 1;
    }
  else if(amatch("sizeof", 6)) {    /* sizeof() */
    int sz, p;  char *ptr, sname[NAMESIZE];
    if(match("(")) p = 1;
    else           p = 0;
    sz = 0;
    if     (amatch("unsigned", 8))  sz = INTSIZE;
    if     (amatch("int",      3))  sz = INTSIZE;
    else if(amatch("char",     4))  sz = 1;
    if(sz) {if(match("*"))          sz = PTRSIZE;}
    else if(symname(sname)
         && ((ptr = findloc(sname)) ||
             (ptr = findglb(sname)))
         && ptr[IDENT] != FUNCTION
         && ptr[IDENT] != LABEL)    sz = getint(ptr+SIZE, INTSIZE);
    else if(sz == 0) error("must be object or type");
    if(p) need(")");
    is[TC] = INT;
    is[CV] = sz;
    is[TA] = is[TI] = is[ST] = 0;
    return 0;
    }
  else if(match("&")) {             /* unary & */
    if(level13(is) == 0) {
      error("illegal address");
      return 0;
      }
    ptr = is[ST];
    is[TA] = ptr[TYPE];
    if(is[TI]) return 0;
    gen(POINT1m, ptr);
    is[TI] = ptr[TYPE];
    return 0;
    }
  else {
    k = level14(is);
    if(match("++")) {               /* lval++ */
      if(k == 0) {
        needlval();
        return 0;
        }
      step(rINC1, is, rDEC1);
      return 0;
      }
    else if(match("--")) {          /* lval-- */
      if(k == 0) {
        needlval();
        return 0;
        }
      step(rDEC1, is, rINC1);
      return 0;
      }
    else return k;
    }
  }

level14(is)  int *is; {
  int k, consta, val;
  char *ptr, *before, *start;
  k = primary(is);
  ptr = is[ST];
  blanks();
  if(ch == '[' || ch == '(') {
    int is2[7];                     /* allocate only if needed */

    null_is (is2);
    
    while(1) {
      if(match("[")) {              /* [subscript] */
        if(ptr == 0) {
          error("can't subscript");
          skip();
          need("]");
          return 0;
          }
        if(is[TA]) {if(k) fetch(is);}
        else       {error("can't subscript"); k = 0;}
        setstage(&before, &start);
        is2[TC] = 0;
        down2(0, 0, level1, is2, is2);
        need("]");
        if(is2[TC]) {
          clearstage(before, 0);
          if(is2[CV])
	    {             /* only add if non-zero */
	      if(ptr[TYPE] >> 2 == BPD)
		{
#ifdef INT32
		  gen(GETd2n, is2[CV] << LBPD);
#else
		  gen(GETw2n, is2[CV] << LBPD);
#endif
		}
	      else if(ptr[TYPE] >> 2 == BPW)
		{
#ifdef INT32
		  gen(GETd2n, is2[CV] << LBPW);
#else
		  gen(GETw2n, is2[CV] << LBPW);
#endif
		}
	      else
		{
#ifdef INT32
		  gen(GETd2n, is2[CV]);
#else
		  gen(GETw2n, is2[CV]);
#endif
		}
	      gen(ADD12, 0);
            }
          }
        else
	  {
	    if(ptr[TYPE] >> 2 == BPD)
	      {
		gen(DBL1, 0);
		gen(DBL1, 0);
	      }
	    else if(ptr[TYPE] >> 2 == BPW)
	      {
		gen(DBL1, 0);
	      }
	    gen(ADD12, 0);
          }
        is[TA] = 0;
        is[TI] = ptr[TYPE];
        k = 1;
        }
      else if(match("(")) {         /* function(...) */
        if(ptr == 0) callfunc(0);
        else if(ptr[IDENT] != FUNCTION) {
          if(k && !is[CV]) fetch(is);
          callfunc(0);
          }
        else callfunc(ptr);
        k = is[ST] = is[TC] = is[CV] = 0;
        }
      else return k;
      }
    }
  if(ptr && ptr[IDENT] == FUNCTION) {
    gen(POINT1m, ptr);
    is[ST] = 0;
    return 0;
    }
  return k;
  }

primary(is)  int *is; {
  char *ptr, sname[NAMESIZE];
  int k;
  if(match("(")) {                  /* (subexpression) */ 
    do k = level1(is); while(match(","));
    need(")");
    return k;
    }
  putint(0, is, 7 << LBPW);         /* clear "is" array */
  if(symname(sname)) {              /* is legal symbol */
    if(ptr = findloc(sname)) {      /* is local */
      if(ptr[IDENT] == LABEL) {
        experr();
        return 0;
        }
      gen(POINT1s, getint(ptr+OFFSET, INTSIZE));
      is[ST] = ptr;
      is[TI] = ptr[TYPE];
      if(ptr[IDENT] == ARRAY) {
        is[TA] = ptr[TYPE];
        return 0;
        }
      if(ptr[IDENT] == POINTER) {
        is[TI] = UINT;
        is[TA] = ptr[TYPE];
        }
      return 1;
      }
    if(ptr = findglb(sname)) {      /* is global */
      is[ST] = ptr;
      if(ptr[IDENT] != FUNCTION) {
        if(ptr[IDENT] == ARRAY) {
          gen(POINT1m, ptr);
          is[TI] = 
          is[TA] = ptr[TYPE];
          return 0;
          }
        if(ptr[IDENT] == POINTER)
          is[TA] = ptr[TYPE];
        return 1;
        }
      }
    else is[ST] = addsym(sname, FUNCTION, INT, 0, 0, &glbptr, AUTOEXT);
    return 0;
    }
  if(constant(is) == 0) experr();
  return 0;
  }

experr() {
  error("invalid expression");
#ifdef INT32
  gen(GETd1n, 0);
#else
  gen(GETw1n, 0);
#endif
  skip();
  }

/* attempt at right to left - do it later */
#ifdef LATER
pushargs (ptr, nargs)
char *ptr;
int nargs;
  {
    if (streq(lptr, ")") != 0)
      return;

    if(endst())
      return;

    if(ptr)
      {
	expression(&consta, &val);
	gen(PUSH1, 0);
      }
    else
      {
	gen(PUSH1, 0);
	expression(&consta, &val);
	gen(SWAP1s, 0);            /* don't push addr */
      }
    nargs = nargs + INTSIZE;         /* count args*BPW */

    if(match(",") == 0) break;
    
  }
#endif

callfunc(ptr)
char *ptr;      /* symbol table entry or 0 */
  {
    int nargs, consta, val;
    
    nargs = 0;
    blanks();                      /* already saw open paren */

    while(streq(lptr, ")") == 0)
      {
	if(endst())
	  break;
	if(ptr)
	  {
	    expression(&consta, &val);
	    gen(PUSH1, 0);
	  }
	else
	  {
	    gen(PUSH1, 0);
	    expression(&consta, &val);
	    gen(SWAP1s, 0);            /* don't push addr */
	  }
	nargs = nargs + INTSIZE;         /* count args*BPW */

	if(match(",") == 0) break;
      }
    need(")");
    if(ptr && (streq(ptr + NAME, "CCARGC") == 0))	/* accessing ... like va_args */
#ifdef INT32
      gen(ARGCNTn, nargs >> LBPD);		/* to get start of frame */
#else
      gen(ARGCNTn, nargs >> LBPW);		/* to get start of frame */
#endif
      
    if(ptr)
      gen(CALLm, ptr);
    else
      gen(CALL1, 0);
    gen(ADDSP, csp + nargs);
  }

/*
** true if is2's operand should be doubled
*/
fdouble (oper, is1, is2)
int oper, is1[], is2[];
{
  //////////////////////////////////////////////////////
  // This is a 'magic' function, its usage and function
  // are not so obvious
  //
  // Purpose:  when indexing an address we must know
  // what we are pointing at so that the indexsize is
  // proper, e,g,
  // charptr++,  should multiply index by a 1
  // shortptr++, should multiply index by a 2
  // intptr++,   should multiply index by a 4
  //
  // Algorithm:
  // IF
  // operation is ADD12 or SUB12
  // AND
  // is1 is an address (pointer, array or via & operator
  // AND
  // is2 is NOT an address (pointer, array or via & operator
  // THEN 
  // return the multiplication factor based on s1 (or true)
  // ELSE
  // return 0 (or false)
  //
  // Usage: The return value is used as a 'boolean'
  // for nonconstant values, indicating that runtime code
  // is necessary to do the necessary multiplication
  // For contant value the return value is used to do a compile-time
  // multiplication (shift actually)
  //

  if ((oper == ADD12 || oper == SUB12)
      && (is2[TA] == 0))
    {
      switch (is1[TA]>>2)
	{
	  default:
	  case 1:		// char
	    return (0);
	  case 2:		// short
	    return (1);
	  case 4:		// int
	    return (2);
	}
    }
  
  return (0);


  
/*
  - original code -
  if((oper != ADD12 && oper != SUB12)
     || (is1[TA] >> 2 != BPW)
     || (is2[TA]))
    
    return 0;

  
  return 1;
*/
}


step (oper, is, oper2) int oper, is[], oper2; {
  fetch(is);
  gen(oper, is[TA] ? (is[TA] >> 2) : 1);
  store(is);
  if(oper2) gen(oper2, is[TA] ? (is[TA] >> 2) : 1);
  }

store(is)
int is[];
  {
    char *ptr;
    
    if(is[TI])
      {                     /* putstk */
	if(is[TI] >> 2 == 1)
	  {
	    gen(PUTbp1, 0);
	  }
	else if(is[TI] >> 2 == 2)
	  {
	    gen(PUTwp1, 0);
	  }
	else
	  {
	    gen(PUTdp1, 0);
	  }
      }
    else
      {                          /* putmem */
	ptr = is[ST];
	if(ptr[IDENT] == POINTER)
	  {
#ifdef INT32	/* int and ptr-size are ALWAYS the same */
	  gen(PUTdm1, ptr);
#else
	  gen(PUTwm1, ptr);
#endif
	  }
	else if (ptr[TYPE] >> 2 == 1)
	  {
	    gen(PUTbm1, ptr);
	  }
	else if (ptr[TYPE] >> 2 == BPW)
	  {
	    gen(PUTwm1, ptr);
	  }
	else
	  {
	    gen(PUTdm1, ptr);
	  }
      }
  }

fetch(is)
int is[];
  {
    char *ptr;
    
    ptr = is[ST];
    if(is[TI])                          /* indirect */
      {
	if(is[TI] >> 2 == BPD)		/* pointer to DWORD */
	  {
	    gen(GETd1p,  0);
	  }
	else if(is[TI] >> 2 == BPW)	/* pointer to WORD */
	  {
	    /* if INT32 must make distinction between signed/unsigned <-- */
	    gen(GETw1p,  0);
	  }
	else
	  {
	    if(ptr[TYPE] & UNSIGNED)
	      gen(GETb1pu, 0);
	    else
	      gen(GETb1p,  0);
	  }
      } 
    else
      {					/* direct */
      
	if(ptr[IDENT] == POINTER)
	  {
#ifdef INT32
	    gen(GETd1m,  ptr);
#else
	    gen(GETw1m,  ptr);
#endif	    
	  }
	else if (ptr[TYPE] >> 2 == BPD)
	  {
	    gen(GETd1m,  ptr);
	  }
	else if (ptr[TYPE] >> 2 == BPW)
	  {
	    gen(GETw1m,  ptr);
	  }
	else
	  {
	    if(ptr[TYPE] & UNSIGNED)
	      gen(GETb1mu, ptr);
	    else
	      gen(GETb1m,  ptr);
	  }
      }
  }

constant(is)
int is[];
  {
    int offset;
    
    if (is[TC] = number(is + CV))
      {
#ifdef INT32
	gen(GETd1n,  is[CV]);
#else
	gen(GETw1n,  is[CV]);
#endif
      }
    else if(is[TC] = chrcon(is + CV))
      {
#ifdef INT32
	gen(GETd1n,  is[CV]);
#else
	gen(GETw1n,  is[CV]);
#endif
      }
    else if(string(&offset))
      {
	gen(POINT1l, offset);
      }
    else
      {
	return 0;
      }
    return 1;
  }

number(value)  int *value; {
  int k, minus;
  k = minus = 0;
  while(1) {
    if(match("+")) ;
    else if(match("-")) minus = 1;
    else break;
    }
  if(isdigit(ch) == 0) return 0;
  if(ch == '0') {
    while(ch == '0') inbyte();
    if(toupper(ch) == 'X') {
      inbyte();
      while(isxdigit(ch)) {
        if(isdigit(ch))
             k = k*16 + (inbyte() - '0');
        else k = k*16 + 10 + (toupper(inbyte()) - 'A');
        }
      }
    else while (ch >= '0' && ch <= '7')
      k = k*8 + (inbyte() - '0');
    }
  else while (isdigit(ch)) k = k*10 + (inbyte() - '0');
  if(minus) {
    *value = -k;
    return (INT);
    }
  if((*value = k) < 0) return (UINT);
  else                 return (INT);
  }

chrcon(value)
int *value;
  {
    int k;
    
    k = 0;
    if(match("'") == 0)
      return 0;
    while(ch != '\'')
      k = (k << 8) + (litchar() & 255);
    gch();
    *value = k;
    return (INT);
  }

string(offset) int *offset; {
  char c;
  if(match(quote) == 0) return 0;
  *offset = litptr;
  while (ch != '"') {
    if(ch == 0) break;
    stowlit(litchar(), 1);
    }
  gch();
  litq[litptr++] = 0;
  return 1;
  }

stowlit(value, size) int value, size; {
  if((litptr+size) >= LITMAX) {
    error("literal queue overflow");
    exit(ERRCODE);
    }
  putint(value, litq+litptr, size);
  litptr += size;
  }

litchar() {
  int i, oct;
  if(ch != '\\' || nch == 0) return gch();
  gch();
  switch(ch) {
    case 'n': gch(); return NEWLINE;
    case 't': gch(); return  9;  /* HT */
    case 'b': gch(); return  8;  /* BS */
    case 'f': gch(); return 12;  /* FF */
    }
  i = 3;
  oct = 0;
  while((i--) > 0 && ch >= '0' && ch <= '7')
    oct = (oct << 3) + gch() - '0';
  if(i == 2) return gch();
  else       return oct;
  }

/***************** pipeline functions ******************/

/*
** skim over terms adjoining || and && operators
*/
skim(opstr, tcode, dropval, endval, level, is)
  char *opstr;
  int tcode, dropval, endval, (*level)(), is[]; {
  int k, droplab, endlab;
  droplab = 0;
  while(1) {
    k = down1(level, is);
    if(nextop(opstr)) {
      bump(opsize);
      if(droplab == 0) droplab = getlabel();
      dropout(k, tcode, droplab, is);
      }
    else if(droplab) {
      dropout(k, tcode, droplab, is);
#ifdef INT32
      gen(GETd1n, endval);
#else
      gen(GETw1n, endval);
#endif
      gen(JMPm, endlab = getlabel());
      gen(LABm, droplab);
#ifdef INT32
      gen(GETd1n, dropval);
#else
      gen(GETw1n, dropval);
#endif
      gen(LABm, endlab);
      is[TI] = is[TA] = is[TC] = is[CV] = is[SA] = 0;
      return 0;
      }
    else return k;
    }
  }

/*
** test for early dropout from || or && sequences
*/
dropout(k, tcode, exit1, is)
  int k, tcode, exit1, is[]; {
  if(k) fetch(is);
  else if(is[TC])
    {
#ifdef INT32
      gen(GETd1n, is[CV]);
#else
      gen(GETw1n, is[CV]);
#endif
    }
  gen(tcode, exit1);          /* jumps on false */
  } 

/*
** drop to a lower level
*/
down(opstr, opoff, level, is)
  char *opstr;  int opoff, (*level)(), is[]; {
  int k;
  k = down1(level, is);
  if(nextop(opstr) == 0) return k;
  if(k) fetch(is);
  while(1) {
    if(nextop(opstr)) {
      int is2[7];     /* allocate only if needed */

      null_is (is2);
      
      bump(opsize);
      opindex += opoff;
      down2(op[opindex], op2[opindex], level, is, is2);
      }
    else return 0;
    }
  }

/*
** unary drop to a lower level
*/
down1(level, is) int (*level)(), is[]; {
  int k, *before, *start;
  setstage(&before, &start);
  k = (*level)(is);
  if(is[TC]) clearstage(before, 0);  /* load constant later */
  return k;
  }

/*
** binary drop to a lower level
*/
down2(oper, oper2, level, is, is2)
  int oper, oper2, (*level)(), is[], is2[]; {
  int *before, *start;
  char *ptr;
  int value;
  setstage(&before, &start);
  is[SA] = 0;                     /* not "... op 0" syntax */
  if(is[TC]) {                    /* consant op unknown */
    if(down1(level, is2)) fetch(is2);
    if(is[CV] == 0) is[SA] = snext;
    gen(GETw2n, is[CV] << fdouble(oper, is2, is));
    }
  else {                          /* variable op unknown */
    gen(PUSH1, 0);                /* at start in the buffer */
    if(down1(level, is2)) fetch(is2);
    if(is2[TC]) {                 /* variable op constant */
      if(is2[CV] == 0) is[SA] = start;
#ifdef INT32
      csp += BPD;                 /* adjust stack and */
#else
      csp += BPW;                 /* adjust stack and */
#endif
      clearstage(before, 0);      /* discard the PUSH */
      if(oper == ADD12) {         /* commutative */
#ifdef INT32
        gen(GETd2n, is2[CV] << fdouble(oper, is, is2));
#else
        gen(GETw2n, is2[CV] << fdouble(oper, is, is2));
#endif
        }
      else {                      /* non-commutative */
        gen(MOVE21, 0);
#ifdef INT32
        gen(GETd1n, is2[CV] << fdouble(oper, is, is2));
#else
        gen(GETw1n, is2[CV] << fdouble(oper, is, is2));
#endif
        }
      }
    else {                        /* variable op variable */
      gen(POP2, 0);
      if(value = fdouble(oper, is, is2))
	{
	  gen(DBL1, 0);		// index size 2
	  if (value > 1)
	    gen(DBL1, 0);	// .. or even 4
	}
      if(value = fdouble(oper, is2, is))
	{
	  gen(DBL2, 0);
	  if (value > 1)
	    gen(DBL2, 0);
	}
      }
    }
  if(oper) {
    if(nosign(is) || nosign(is2)) oper = oper2;
    if(is[TC] = is[TC] & is2[TC]) {               /* constant result */
      is[CV] = calc(is[CV], oper, is2[CV]);
      clearstage(before, 0);  
      if(is2[TC] == UINT) is[TC] = UINT;
      }
    else {                                        /* variable result */
      gen(oper, 0);
      if(oper == SUB12
      && is [TA] >> 2 == BPW
      && is2[TA] >> 2 == BPW) { /* difference of two word addresses */
        gen(SWAP12, 0);
        gen(GETw1n, 1);
        gen(ASR12, 0);          /* div by 2 */
        }
      is[OP] = oper;            /* identify the operator */
      }
    if(oper == SUB12 || oper == ADD12) {
      if(is[TA] && is2[TA])     /*  addr +/- addr */
        is[TA] = 0;
      else if(is2[TA]) {        /* value +/- addr */
        is[ST] = is2[ST];
        is[TI] = is2[TI];
        is[TA] = is2[TA];
        }
      }
    if(is[ST] == 0 || ((ptr = is2[ST]) && (ptr[TYPE] & UNSIGNED)))
      is[ST] = is2[ST];
    }
  }

/*
** unsigned operand?
*/
nosign(is) int is[]; {
  char *ptr;
  if(is[TA]
  || is[TC] == UINT
  || ((ptr = is[ST]) && (ptr[TYPE] & UNSIGNED))
    ) return 1;
  return 0;
  }

/*
** calcualte signed constant result
*/
calc(left, oper, right) int left, oper, right; {
  switch(oper) {
    case ADD12: return (left  +  right); 
    case SUB12: return (left  -  right);
    case MUL12: return (left  *  right); 
    case DIV12: return (left  /  right);
    case MOD12: return (left  %  right); 
    case EQ12:  return (left  == right); 
    case NE12:  return (left  != right);
    case LE12:  return (left  <= right); 
    case GE12:  return (left  >= right);
    case LT12:  return (left  <  right); 
    case GT12:  return (left  >  right);
    case AND12: return (left  &  right);
    case OR12:  return (left  |  right);
    case XOR12: return (left  ^  right); 
    case ASR12: return (left  >> right); 
    case ASL12: return (left  << right);
    } 
  return (calc2(left, oper, right));
  }

/*
** calcualte unsigned constant result
*/
calc2(left, oper, right) unsigned left, right; int oper; {
  switch(oper) {
    case MUL12u: return (left  *  right); 
    case DIV12u: return (left  /  right);
    case MOD12u: return (left  %  right); 
    case LE12u:  return (left  <= right); 
    case GE12u:  return (left  >= right);
    case LT12u:  return (left  <  right); 
    case GT12u:  return (left  >  right);
    } 
  return (0);
  }