/* preexpr.c
 *
 *	(C) Copyright Feb  2 1996, Edmond J. Breen.
 *		   ALL RIGHTS RESERVED.
 * This code may be copied for personal, non-profit use only.
 *
 */

#ifndef _STANDALONE

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <limits.h>
#include "xalloc.h"
#include "preproc.h"

#endif


/*ppint expr(int k);*/

/* typedef unsigned long ppint;   the preprocessor integer */

typedef struct {
    union {
	long s;           /* signed value */
	unsigned long u;  /* unsigned value */
    }v;
    int type;
}ppint;
/* methods */
#define sval(x)  ((x).v.s)
#define uval(x)  ((x).v.u)
#define tval(x)  ((x).type)

#define USIGN  1
#define SIGN   2

static ppint do_binary(int tk, ppint left, ppint right);
static int getTok(int k);
static ppint EiC_expr_unary(void);
static ppint get_number(void);
static ppint EiC_ifexpr(int k);


enum { LOR = 1, LAND, BOR,  XOR,   AND,    EQ,  NEQ,  LT, LEQ,
        GT, GEQ, LSHFT, RSHFT,PLUS, MINUS, TIMES, DIV, MOD};

static char *S;
static int TK =0;

#define _StrSz_ 100

static void replaceDefines(char *S)
{
    char str[50];
    int cmode = 0;
    char *p;
    p = S;
    while(*S != '\0') {
        if(!cmode && (isalpha(*S) || *S == '_')) {
            if(S[0] == 'd' && S[1] == 'e' &&
               S[2] == 'f' && S[3] == 'i' &&
               S[4] == 'n' && S[5] == 'e' &&
               S[6] == 'd' && !isalpha(S[7]) &&
               S[7] != '_') {
                int br = 0;
                int i;
                S+=7;
                skipfws(S);
                if(*S=='(') {
                    S++;
                    br = 1;
                    skipfws(S);
                }
                i = 0;
                while(i < 50 && (isalpha(*S) || *S == '_' || isdigit(*S)))
                    str[i++] = *S++;
                str[i] = '\0';
                if(br) {
                    skipfws(S);
                    if(*S != ')') 
                        EiC_pre_error("Missing ')'");
                    else
                        S++;
                }
                if(str[0] != '\0') {
                    if(EiC_ismacroid(str) > -1)
                        *p = '1';
                    else
                        *p = '0';
                    ++p;
                } else
                    EiC_pre_error("Missing identifier");
                continue;
            } 
            do 
                *p++ = *S++;
            while(isalpha(*S) || *S == '_' );
	    continue;
        } if(*S == '\'')
            cmode = !cmode;
        *p++ = *S++;
    }
    *p = '\0';
}

static void replaceIdentifiers(char *S)
{
    int i;
    char str[50];
    char *p;
    int cmode = 0;

    p = S;    
    
    while(1) {
	while(isspace(*S))
	    *p++ = *S++;
	if(!*S)
	    return;
	if(!isalpha(*S) && *S != '_') {
	    if(isdigit(*S) || *S == '\'') { /* skip throu numbers or literals*/
		while(*S && !isspace(*S))
		    *p++ = *S++;
	    } else 
		while(*S && !isspace(*S) && *S != '_' && ispunct(*S))
		    *p++ =  *S++;
	    continue;
	}
	if(!cmode) {
	    i = 0;
	    while(i < 50 && (isalpha(*S) || *S == '_' || isdigit(*S)) )
		str[i++] = *S++;
	    str[i] = '\0';
	    if(strcmp(str,"sizeof") == 0)
		EiC_pre_error("Illegal sizeof operator");
	    else
		*p++ = '0';
	} else
	    *p++ = *S++;
    }
}


int EiC_cpp_parse(char *s)
{

    ppint res;
    S = s;
    replaceDefines(S);

#ifdef DEBUG
    printf("return [%s]\n",S);
#endif

    S = s  = EiC_process2(S,0,0);

    replaceIdentifiers(S);

#ifdef DEBUG
    printf("return [%s]\n",S);
#endif

    res = EiC_ifexpr(0);

    if(s)
        xfree(s);

    if(tval(res) == SIGN)
	return sval(res);
    else
	return uval(res);
}

static ppint EiC_ifexpr(int k)
{
    ppint res;
    int k1, tk;
    res = EiC_expr_unary();
    for(k1 = 10; k1 >= k; k1--)
	while((tk = getTok(k1))) 
	    res = do_binary(tk, res,EiC_ifexpr(k1>8?k1:k1+1));
    return res;
}

#define eval(a,l,r,op)\
{\
     if(tval(l) == SIGN)\
	 sval(a) = sval(l) op sval(r);\
     else\
	 uval(a) = uval(l) op uval(r);\
}
		 

static ppint do_binary(int tk, ppint left, ppint right)
{
    ppint r;

    if(tval(left) == USIGN || tval(right) == USIGN)
	tval(r) = tval(left) = tval(right) = USIGN;
    else
	tval(r) = tval(left) = tval(right) = SIGN;
    

	
    switch(tk) {
      case BOR: eval(r,left,right, | ); break;
      case XOR: eval(r,left,right, ^ ); break;
      case AND: eval(r,left,right, & ); break;
      case LT:  eval(r,left,right, < ); break;
      case LEQ: eval(r,left,right, <= ); break;
      case EQ:  eval(r,left,right,  == ); break;
      case NEQ: eval(r,left,right,  != ); break;
      case GT:  eval(r,left,right, > ); break;
      case GEQ: eval(r,left,right, >= ); break;
      case LOR: eval(r,left,right, || ); break;
      case LAND: eval(r,left,right, && ); break;
      case LSHFT: eval(r,left,right, << ); break;
      case RSHFT: eval(r,left,right, >> ); break; 	
      case PLUS: eval(r,left,right, + ); break;
      case MINUS: eval(r,left,right, - ); break;
      case TIMES: eval(r,left,right, * ); break;
      case DIV: eval(r,left,right, / ); break;
      case MOD: eval(r,left,right, % ); break;
    }
    return r;
}

static int getTok(int k)
{
    TK = 0;
    
    while(isspace(*S))
	S++;
	
    switch(k) {
      case 1: /* LOR */
	if(*S == '|' && *(S+1) == '|') {S+=2; TK = LOR;}
	break;
      case 2: /* LAND */
	if(*S == '&' && *(S+1) == '&') {S+=2; TK = LAND;}
      case 3: /* BOR */
	if(*S == '|' && *(S+1) != '|') {S++; TK = BOR;}
	break;
      case 4: /* XOR */
	if(*S == '^') {S++; TK = GEQ;}
	break;
      case 5: /* AND */
	if(*S == '&' && *(S+1) != '&') {S++; TK = GEQ;}
	break;
      case 6: /* EQ, NEQ */
	if(*S == '=' && *(S+1) == '=') {S+=2; TK = EQ;}
	else if(*S == '!' && *(S+1) == '=') {S+=2; TK = NEQ;}
	break;
      case 7: /* LT, LEQ, GT, GEQ */
	if(*S == '<') {
	    S++;
	    if(*S == '='){S++;TK = LEQ;}
	    else TK = LT;
	} else if(*S == '>') {
	    S++;
	    if(*S == '='){S++; TK = GEQ;}
	    else TK = GT;
	}
	break;
      case 8:  /* LSHFT, RSHFT */
	if(*S == '<' && *(S+1) == '<') {S+=2; TK = LSHFT;}
	else if(*S == '>' && *(S+1) == '>') {S+=2; TK = RSHFT;}
	break;
      case 9: /* PLUS, MINUS */
	if(*S == '-') {S++;TK = MINUS;}
	else if(*S == '+') {S++; TK = PLUS;}
	break;
      case 10: /* TIMES, DIV, MOD */
	if(*S == '*') {S++;TK = TIMES;}
	else if(*S == '/') {S++; TK = DIV;}
	else if(*S == '%') {S++; TK = MOD;}
	break;
    }
    return TK;
}

static int get_oct(int x)
{
    return x>='0'&&x<='7'? x-'0':-1;
}

static int get_hex(int x)
{
    
    if (x >= '0' && x <= '9')
	x -= '0';
    else if (x >= 'a' && x <= 'f')
	 x = x - 'a' + 10;
    else if (x >= 'A' && x <= 'F')
	x = x - 'A' + 10;
    else
	x = -1;
    return x;
}

static int get_dec(int x)
{
    return x >= '0' && x <= '9' ? x-'0':-1;
}


static ppint get_number()   /* collect hex, octal and decimal integers */
{
    int (*f)(int x);
    int radix,val;
    ppint res = {{0},SIGN};

    if(*S == '0') {
	S++;
	if(*S == 'x' || *S == 'X') { /* get hex number */
	    S++;
	    radix = 16;
	    f = get_hex;
	} else { /* get octal number */
	    radix = 8;
	    f = get_oct;
	}
    } else { /* get decimal number */
	radix = 10;
	f = get_dec;
    }
    while((val = (*f)(*S++)) >= 0)
	uval(res) = uval(res) * radix + val;
    S--;

    if(uval(res) > LONG_MAX)
	tval(res) = USIGN;
    
    /* check for prefix */    
    if(*S=='u' || *S=='U') {
	S++;
	tval(res) = USIGN;
    } if(*S=='l' || *S=='L')
	S++;
    return res;
}
	

static int get_charConst()
{
    
    int c;
    switch (*S) {
      case 'n': c = '\n'; break; /* newline */
      case 't': c = '\t'; break; /* tabspace */
      case 'v': c = '\v'; break; /* vertical tab */
      case 'b': c = '\b'; break; /* backspace */
      case 'r': c = '\r'; break; /* carriage return */
      case 'f': c = '\f'; break; /* formfeed */
      case 'a': c = '\a'; break; /* bell */
      case '\\': c = '\\'; break; /* backslash */
      case '\'': c = '\''; break; /* single quote */
      case '"': c = '\"'; break; /* double quote */
      case 'x':			/* string of hex characters */
      case 'X':{
	  int i, val = 0;
	  S++;
	  while ((i = get_hex(*S)) > -1) {
	      S++;
	      val = val * 16 + i;
	  }
	  if (val > 255)
	      EiC_pre_error("Illegal character hex value");
	  c = val;
      }
	break;
      default:
	if (isdigit(*S)) {	/* treat as octal characters */
	    int i, val = 0;
	    while ((i = get_oct(*S)) > -1) {
		val = val * 8 + i;
		S++;
	    }
	    if (val > 255)
		EiC_pre_error("Illegal character octal value");
	    c = val;
	} else {
	    EiC_pre_error("Illegal character escape sequence `\\%c'", *S);
	    c = *S++;
	}
	break;
    }
    return c;
}

static ppint EiC_expr_unary()
{
    ppint res;
    
    while(isspace(*S))
	S++;
    if(isdigit(*S)) {
	res = get_number();
    } else if( *S == '(') {
	S++;
	res = EiC_ifexpr(0);
	if(*S != ')')
	    EiC_pre_error("Unbalanced parenthesis");
	S++;
    } else if(*S == '!') {
	S++;
	res = EiC_expr_unary();
	uval(res) = !uval(res);
    } else if(*S == '-') {
	S++;
	if(*S == '-')
	    EiC_pre_error("-- not allowed in operand of #if");
	res = EiC_expr_unary();	
	tval(res) = SIGN;
	sval(res) = -uval(res);
    } else if(*S == '+') {
	S++;
	if(*S == '+')
	    EiC_pre_error("++ not allowed in operand of #if");
	res = EiC_expr_unary();
    } else if(*S == '~') {
	S++;
	res = EiC_expr_unary();
	uval(res) = ~uval(res);
    } else if(*S == '\'') { /* char constants */
	S++;
	if(*S == '\\') {
	    S++;
	    uval(res) = get_charConst();
	} else
	    uval(res) = *S++;
	if(*S != '\'')
	    EiC_pre_error("Missing closing single quote '");
	else
	    S++;
	tval(res) = SIGN;
    } else
	EiC_pre_error("Illegal constant expression");
    return res;
}








