/* expr.c expression handling for vasm */
/* (c) in 2002-2026 by Volker Barthelmann and Frank Wille */

#include "vasm.h"

char current_pc_char='$';
int unsigned_shift;
int charsperexp;

static char *s;
static symbol *cpc;
static int make_tmp_lab;
static int exp_type;
static int bitspertaddr,charspertaddr;

static expr *expression(void);


int init_expr(void)
{
  /* we always assume 8-bit bytes on the host system */
  bitspertaddr = sizeof(taddr) * CHAR_BIT;

  /* maximum number of characters in expression strings */
  charspertaddr=bytespertaddr*BITSPERBYTE/8;
  if(!charsperexp) charsperexp=charspertaddr;
  return 1;
}

#ifndef EXPSKIP
#define EXPSKIP(p) expskip(p)
static char *expskip(char *s)
{
  s=skip(s);
  if(*s==commentchar) *s='\0';  /* rest of line is commented out */
  return s;
}
#endif

expr *new_expr(void)
{
  expr *new=mymalloc(sizeof(*new));
  new->left=new->right=0;
  return new;
}

expr *make_expr(int type,expr *left,expr *right)
{
  expr *new=mymalloc(sizeof(*new));
  new->left=left;
  new->right=right;
  new->type=type;
  return new;
}

expr *copy_tree(expr *old)
{
  expr *new=0;
  if(old){
    new=make_expr(old->type,copy_tree(old->left),copy_tree(old->right));
    new->c=old->c;
  }
  return new;
}

expr *new_sym_expr(symbol *sym)
{
  expr *new=new_expr();
  new->type=SYM;
  new->c.sym=sym;
  return new;
}

expr *curpc_expr(void)
{
  if(!cpc){
    cpc=new_import(" *current pc dummy*");
    cpc->type=LABSYM;
    cpc->flags|=VASMINTERN|PROTECTED;
  }
  return new_sym_expr(cpc);
}

static void update_curpc(expr *exp,section *sec,taddr pc)
{
  if(exp->c.sym==cpc&&sec!=NULL){
    cpc->sec=sec;
    cpc->pc=pc;
    if(sec->flags&ABSOLUTE)
      cpc->flags|=ABSLABEL;
    else
      cpc->flags&=~ABSLABEL;
  }
}

static expr *primary_expr(void)
{
  strbuf *buf;
  expr *new;
  char *m;
  int base;

  if(*s=='('){
    s=EXPSKIP(++s);
    new=expression();
    if(*s!=')')
      general_error(6,')');
    else
      s++;
    s=EXPSKIP(s);
    return new;
  }
  if(buf=get_local_label(EXPBUFNO,&s)){
    symbol *sym=find_symbol(buf->str);
    if(!sym)
      sym=new_import(buf->str);
    sym->flags|=USED;
    new=(sym->type!=EXPRESSION)?new_sym_expr(sym):copy_tree(sym->expr);
    return new;
  }
  m=const_prefix(s,&base);
  if(base!=0){
    char *start=s;
    utaddr val,nval;
    thuge huge;
#if FLOAT_PARSER
    tfloat flt;
#endif
    switch(exp_type){
    case NUM:
      s=m;
      val=0;
      if(base<=10){
        while(*s>='0'&&*s<base+'0'){
          nval=base*val;
          if (nval/base!=val)
            goto hugeval;  /* taddr overflow, read a thuge value instead */
          val=nval+*s++-'0';
        }
#if FLOAT_PARSER
        if(base==10&&(*s=='e'||*s=='E'||(*s=='.'&&*(s+1)>='0'&&*(s+1)<='9')))
          goto fltval;  /* decimal point or exponent: read floating point */
#endif
      }else if(base==16){
#ifdef BROKEN_HEXCONST
        int msign=*(s-1)=='-'&&*(s-2)=='$';
#endif
        for(;;){
          nval=val<<4;
          if(*s>='0'&&*s<='9')
            nval+=*s++-'0';
          else if(*s>='a'&&*s<='f')
            nval+=*s++-'a'+10;
          else if(*s>='A'&&*s<='F')
            nval+=*s++-'A'+10;
          else break;
          if (nval>>4!=val)
            goto hugeval;  /* taddr overflow, read a thuge value instead */
          else
            val=nval;
        }
#ifdef BROKEN_HEXCONST
        if(msign) val=-val;
#endif
      }else ierror(0);
      break;
    hugeval:
      exp_type=HUG;
    case HUG:
      s=m;
      huge=huge_zero();
      if(base<=10){
        while(*s>='0'&&*s<base+'0')
          huge=haddi(hmuli(huge,base),*s++-'0');
#if FLOAT_PARSER
        if(base==10&&(*s=='e'||*s=='E'||
                      (*s=='.'&&*(s+1)>='0'&&*(s+1)<='9')))
          goto fltval;  /* decimal point or exponent: read floating point */
#endif
      }else if(base==16){
        for(;;){
          if(*s>='0'&&*s<='9')
            huge=haddi(hmuli(huge,16),*s++-'0');
          else if(*s>='a'&&*s<='f')
            huge=haddi(hmuli(huge,16),*s++-'a'+10);
          else if(*s>='A'&&*s<='F')
            huge=haddi(hmuli(huge,16),*s++-'A'+10);
          else break;
        }
      }else ierror(0);
      break;
#if FLOAT_PARSER
    fltval:
      exp_type=FLT;
    case FLT:
      if(base!=10) goto hugeval;
      s=m;
      flt=strtotfloat(s,&s);
      break;
#endif
    default:
      ierror(0);
      break;
    }
    if(s==m){
      general_error(75,base);
      goto dummyexp;
    }
    s=const_suffix(start,s);
    s=EXPSKIP(s);
    new=new_expr();
    switch(new->type=exp_type){
      case NUM: new->c.val=val; break;
      case HUG: new->c.huge=huge; break;
#if FLOAT_PARSER
      case FLT: new->c.flt=flt; break;
#endif
    }
    return new;
  }
  s=m;
  if(*s==current_pc_char && !ISIDCHAR(*(s+1))){
    s=EXPSKIP(++s);
    if(make_tmp_lab){
      new=new_sym_expr(new_tmplabel(0));
      add_atom(0,new_label_atom(new->c.sym));
    }else new=curpc_expr();
    return new;
  }
  if(buf=parse_identifier(EXPBUFNO,&s)){
    symbol *sym;    
    s=EXPSKIP(s);
    sym=find_symbol(buf->str);
    if(!sym)
      sym=new_import(buf->str);
    sym->flags|=USED;
    new=(sym->type!=EXPRESSION)?new_sym_expr(sym):copy_tree(sym->expr);
    return new;
  }
  if(*s=='\''||*s=='\"'){
    taddr val=0;
    int shift=0,cnt=0;
    char quote=*s++;
    for(;;){
      char c;
      if(*s=='\\')
        s=escape(s,&c);
      else{
        if (!(c=*s++)) {
          general_error(6,quote);  /* closing-quote expected */
          break;
        }
        if(c==quote){
          if(*s==quote)
            s++;  /* allow """" to be recognized as " and '''' as ' */
          else break;
        }
      }
      if(++cnt>charspertaddr){
        general_error(21,bytespertaddr*BITSPERBYTE); /* target data type overflow */
        break;
      }
      if(BIGENDIAN){
        val=(val<<8)+(unsigned char)CHAR_CONST_TRANSFORM(c,quote);
      }else if(LITTLEENDIAN){
        val+=((unsigned char)CHAR_CONST_TRANSFORM(c,quote))<<shift;
        shift+=8;
      }else
        ierror(0);
      if(cnt>=charsperexp&&*s!=quote)
        break;
    }
    s=EXPSKIP(s);
    new=new_expr();
    new->type=NUM;
    new->c.val=val;
    return new;
  }
  general_error(9);
dummyexp:
  new=new_expr();
  new->type=NUM;
  new->c.val=-1;
  return new;
}
    
static expr *unary_expr(void)
{
  expr *new;
  int len,type;
#if defined(EXT_UNARY_NAME) && defined(EXT_UNARY_TYPE)
  if(len=EXT_UNARY_NAME(s))
    type=EXT_UNARY_TYPE(s);
  else
#endif
  if(len=T_PLUS(s)){
    s=EXPSKIP(s+len);
    return primary_expr();
  }else if(len=T_MINUS(s))  /* - */
    type=NEG;
  else if(len=T_NOT(s))  /* ! */
    type=NOT;
  else if(len=T_CPL(s))  /* ~ */
    type=CPL;
  else
    return primary_expr();
  s=EXPSKIP(s+len);
  new=new_expr();
  new->type=type;
  new->left=primary_expr();
  return new;
}  

static expr *shift_expr(void)
{
  expr *left,*new;
  int len;
  left=unary_expr();
  s=EXPSKIP(s);
  while((len=T_LSH(s))||(len=T_RSH(s))){  /* << or >> */
    new=new_expr();
    if(T_LSH(s))
      new->type=LSH;
    else
      new->type=unsigned_shift?RSHU:RSH;
    new->left=left;
    s=EXPSKIP(s+len);
    new->right=unary_expr();
    left=new;
  }
  return left;
}

static expr *and_expr(void)
{
  expr *left,*new;
  int len;
  left=shift_expr();
  s=EXPSKIP(s);
  while(len=T_BAND(s)){  /* & */
    s=EXPSKIP(s+len);
    new=new_expr();
    new->type=BAND;
    new->left=left;
    new->right=shift_expr();
    left=new;
  }
  return left;
}

static expr *exclusive_or_expr(void)
{
  expr *left,*new;
  int len;
  left=and_expr();
  s=EXPSKIP(s);
  while((len=T_XOR(s))||(len=T_BORN(s))){  /* ^ or OR-NOT */
    new=new_expr();
    if(T_XOR(s))
      new->type=XOR;
    else
      new->type=BORN;
    new->left=left;
    s=EXPSKIP(s+len);
    new->right=and_expr();
    left=new;
  }
  return left;
}

static expr *inclusive_or_expr(void)
{
  expr *left,*new;
  int len;
  left=exclusive_or_expr();
  s=EXPSKIP(s);
  while(len=T_BOR(s)){  /* | */
    s=EXPSKIP(s+len);
    new=new_expr();
    new->type=BOR;
    new->left=left;
    new->right=exclusive_or_expr();
    left=new;
  }
  return left;
}

static expr *multiplicative_expr(void)
{
  expr *left,*new;
  int len;
  left=inclusive_or_expr();
  s=EXPSKIP(s);
  while((len=T_MUL(s))||(len=T_DIV(s))||(len=T_MOD(s))){  /* * or / or % */
    new=new_expr();
    if(T_MUL(s))
      new->type=MUL;
    else if(T_DIV(s))
      new->type=DIV;
    else
      new->type=MOD;
    new->left=left;
    s=EXPSKIP(s+len);
    new->right=inclusive_or_expr();
    left=new;
  }
  return left;
}

static expr *additive_expr(void)
{
  expr *left,*new;
  int len;
  left=multiplicative_expr();
  s=EXPSKIP(s);
  while((len=T_ADD(s))||(len=T_SUB(s))){  /* + or - */
    new=new_expr();
    new->type=T_ADD(s)?ADD:SUB;
    new->left=left;
    s=EXPSKIP(s+len);
    new->right=multiplicative_expr();
    left=new;
  }
  return left;
}

static expr *relational_expr(void)
{
  expr *left,*new;
  int len;
  left=additive_expr();
  s=EXPSKIP(s);
  while((len=T_LEQ(s))||(len=T_GEQ(s))||(len=T_LT(s))||(len=T_GT(s))){
    new=new_expr();
    if(T_LEQ(s))  /* <= */
      new->type=LEQ;
    else if(T_GEQ(s))  /* >= */
      new->type=GEQ;
    else if(T_LT(s))  /* < */
      new->type=LT;
    else  /* > */
      new->type=GT;
    new->left=left;
    s=EXPSKIP(s+len);
    new->right=additive_expr();
    left=new;
  }
  return left;
}

static expr *equality_expr(void)
{
  expr *left,*new;
  int len;
  left=relational_expr();
  s=EXPSKIP(s);
  while((len=T_EQ(s))||(len=T_NEQ(s))){  /* == or != */
    new=new_expr();
    new->type=T_EQ(s)?EQ:NEQ;
    new->left=left;
    s=EXPSKIP(s+len);
    new->right=relational_expr();
    left=new;
  }
  return left;
}

static expr *logical_and_expr(void)
{
  expr *left,*new;
  int len;
  left=equality_expr();
  s=EXPSKIP(s);
  while(len=T_LAND(s)){  /* && */
    s=EXPSKIP(s+len);
    new=new_expr();
    new->type=LAND;
    new->left=left;
    new->right=equality_expr();
    left=new;
  }
  return left;
}

static expr *expression(void)
{
  expr *left,*new;
  int len;
  left=logical_and_expr();
  s=EXPSKIP(s);
  while(len=T_LOR(s)){  /* || */
    s=EXPSKIP(s+len);
    new=new_expr();
    new->type=LOR;
    new->left=left;
    new->right=logical_and_expr();
    left=new;
  }
  return left;
}

/* Tries to parse the string as a constant value. Sets pp to the
   end of the parse. Already defined absolute symbols are
   recognized.
   Automatically switches to a HUG expression type, when encountering a
   constant which doesn't fit into taddr.
   Automatically switches to a FLT expression type, when reading a decimal
   point or an exponent in a decimal constant (requires FLOAT_PARSER). */
expr *parse_expr(char **pp)
{
  expr *tree;
  s=*pp;
  make_tmp_lab=0;
  exp_type=NUM;
  tree=expression();
  simplify_expr(tree);
  *pp=s;
  return tree;
}

expr *parse_expr_tmplab(char **pp)
{
  expr *tree;
  s=*pp;
  make_tmp_lab=1;
  exp_type=NUM;
  tree=expression();
  simplify_expr(tree);
  *pp=s;
  return tree;
}

/* Tries to parse the string as a huge-integer (128 bits) constant (thuge).
   No labels allowed in this mode. */
expr *parse_expr_huge(char **pp)
{
  expr *tree;
  s=*pp;
  make_tmp_lab=0;
  exp_type=HUG;
  tree=expression();
  simplify_expr(tree);
  *pp=s;
  return tree;
}

/* Tries to parse the string as a floating point constant (tfloat).
   No labels allowed in this mode.
   When the constant is not decimal switch to reading a HUG expression type. */
expr *parse_expr_float(char **pp)
{
#if FLOAT_PARSER
  expr *tree;
  s=*pp;
  make_tmp_lab=0;
  exp_type=FLT;
  tree=expression();
  simplify_expr(tree);
  *pp=s;
  return tree;
#else
  general_error(79,cpuname);  /* backend does not support floating point */
  return NULL;
#endif
}

void free_expr(expr *tree)
{
  if(!tree)
    return;
  free_expr(tree->left);
  free_expr(tree->right);
  myfree(tree);
}

/* Return type of expression.
   Either NUM, HUG or FLT. Labels or unknown symbols default to NUM.
   Returns 0 in case of an error (e.g. epxression is NULL pointer). */
int type_of_expr(expr *tree)
{
  int ltype,rtype;
  if(tree==NULL)
    return 0;
  ltype=tree->type;
  if(ltype==SYM){
    symbol *sym=tree->c.sym;
    if(sym->type==EXPRESSION){
      if(sym->flags&INEVAL)
        general_error(18,sym->name);
      sym->flags|=INEVAL;
      ltype=type_of_expr(sym->expr);
      sym->flags&=~INEVAL;
      return ltype;
    }else return NUM;
  }else if(ltype<SYM)  /* NUM, HUG, FLT */
    return ltype;
  ltype=type_of_expr(tree->left);
  rtype=type_of_expr(tree->right);
  return rtype>ltype?rtype:ltype;
}

/* Find pointer to first symbol occurrence inside expression tree */
expr **find_sym_expr(expr **ptree,char *name)
{
  expr **psym;

  if(*ptree==NULL)
    return NULL;
  if((*ptree)->left!=NULL &&
     (*ptree)->left->type==SYM&&!strcmp((*ptree)->left->c.sym->name,name))
    return &(*ptree)->left;
  if(psym=find_sym_expr(&(*ptree)->left,name))
    return psym;
  if((*ptree)->right!=NULL &&
     (*ptree)->right->type==SYM&&!strcmp((*ptree)->right->c.sym->name,name))
    return &(*ptree)->right;
  if(psym=find_sym_expr(&(*ptree)->right,name))
    return psym;
  return NULL;
}

static taddr lshift(taddr val,int sh)
{
  if (sh>=bitspertaddr) return 0;
  return val<<sh;
}

static taddr rshift(taddr val,int sh)
{
  if (sh>=bitspertaddr) return val<0 ? -1 : 0;
  return val>>sh;
}

static utaddr urshift(utaddr val,int sh)
{
  if (sh>=bitspertaddr) return 0;
  return val>>sh;
}

/* Try to evaluate expression as far as possible. Subexpressions
   only containing constants or absolute symbols are simplified. */
void simplify_expr(expr *tree)
{
  taddr ival;
  thuge hval;
#if FLOAT_PARSER
  tfloat fval;
#endif
  int type=0;
  if(!tree)
    return;
  simplify_expr(tree->left);
  simplify_expr(tree->right);
  if(tree->type==SUB&&tree->right->type==SYM&&tree->left->type==SUB&&
     tree->left->left->type!=SYM&&tree->left->right->type==SYM){
    /* Rearrange nodes from "const-symbol-symbol", so that "symbol-symbol"
       is evaluated first, as it may yield a constant. */
    expr *x=tree->right;
    tree->right=tree->left;
    tree->left=tree->right->left;
    tree->right->left=tree->right->right;
    tree->right->right=x;
  }
  if(tree->left){
    if(tree->left->type<SYM)  /* NUM, HUG, FLT */
      type=tree->left->type;
    else return;
  }
  if(tree->right){
    if(tree->right->type<SYM){  /* NUM, HUG, FLT */
      if(tree->right->type>type)
        type=tree->right->type;
    } else return;
  }
  if(type==NUM){
    switch(tree->type){
    case ADD:
      ival=(tree->left->c.val+tree->right->c.val);
      break;
    case SUB:
      ival=(tree->left->c.val-tree->right->c.val);
      break;
    case MUL:
      ival=(tree->left->c.val*tree->right->c.val);
      break;
    case DIV:
      if(tree->right->c.val==0){
        general_error(41);
        ival=0;
      }else if(tree->left->c.val==taddrmin&&tree->right->c.val==-1){
        general_error(21,sizeof(taddr)*CHAR_BIT); /* target data type overflow */
        ival=taddrmin;
      }else
        ival=(tree->left->c.val/tree->right->c.val);
      break;
    case MOD:
      if(tree->right->c.val==0){
        general_error(41);
        ival=0;
      }else if(tree->left->c.val==taddrmin&&tree->right->c.val==-1){
        general_error(21,sizeof(taddr)*CHAR_BIT); /* target data type overflow */
        ival=taddrmin;
      }else
        ival=(tree->left->c.val%tree->right->c.val);
      break;
    case NEG:
      ival=(-tree->left->c.val);
      break;
    case CPL:
      ival=(~tree->left->c.val);
      break;
    case LAND:
      ival=BOOLEAN(tree->left->c.val&&tree->right->c.val);
      break;
    case LOR:
      ival=BOOLEAN(tree->left->c.val||tree->right->c.val);
      break;
    case BAND:
      ival=(tree->left->c.val&tree->right->c.val);
      break;
    case BOR:
      ival=(tree->left->c.val|tree->right->c.val);
      break;
    case XOR:
      ival=(tree->left->c.val^tree->right->c.val);
      break;
    case BORN:
      ival=(tree->left->c.val|~tree->right->c.val);
      break;
    case NOT:
      ival=!tree->left->c.val;  /* do not use BOOLEAN() here */
      break;
    case LSH:
      ival=lshift(tree->left->c.val,(int)tree->right->c.val);
      break;
    case RSH:
      ival=rshift(tree->left->c.val,(int)tree->right->c.val);
      break;
    case RSHU:
      ival=urshift((utaddr)tree->left->c.val,(int)tree->right->c.val);
      break;
    case LT:
      ival=BOOLEAN(tree->left->c.val<tree->right->c.val);
      break;
    case GT:
      ival=BOOLEAN(tree->left->c.val>tree->right->c.val);
      break;
    case LEQ:
      ival=BOOLEAN(tree->left->c.val<=tree->right->c.val);
      break;
    case GEQ:
      ival=BOOLEAN(tree->left->c.val>=tree->right->c.val);
      break;
    case NEQ:
      ival=BOOLEAN(tree->left->c.val!=tree->right->c.val);
      break;
    case EQ:
      ival=BOOLEAN(tree->left->c.val==tree->right->c.val);
      break;
    default:
#ifdef EXT_UNARY_EVAL
      if (tree->left && EXT_UNARY_EVAL(tree->type,tree->left->c.val,&ival,1))
        break;
#endif
      return;
    }
  }
  else if(type==HUG){
    thuge lval,rval;
    if(tree->left){
      if(tree->left->type==NUM)
        lval=huge_from_int(tree->left->c.val);
      else
        lval=tree->left->c.huge;
    }
    if(tree->right){
      if(tree->right->type==NUM)
        rval=huge_from_int(tree->right->c.val);
      else
        rval=tree->right->c.huge;
    }
    switch(tree->type){
    case ADD:
      hval=hadd(lval,rval);
      break;
    case SUB:
      hval=hsub(lval,rval);
      break;
    case MUL:
      hval=hmul(lval,rval);
      break;
    case DIV:
      if(hcmp(rval,huge_zero())==0){
        general_error(41);
        hval=huge_zero();
      }else
        hval=hdiv(lval,rval);
      break;
    case MOD:
      if(hcmp(rval,huge_zero())==0){
        general_error(41);
        hval=huge_zero();
      }else
        hval=hmod(lval,rval);
      break;
    case NEG:
      hval=hneg(lval);
      break;
    case CPL:
      hval=hcpl(lval);
      break;
    case LAND:
      ival=BOOLEAN(hcmp(lval,huge_zero())!=0&&hcmp(rval,huge_zero())!=0);
      type=NUM;
      break;
    case LOR:
      ival=BOOLEAN(hcmp(lval,huge_zero())!=0||hcmp(rval,huge_zero())!=0);
      type=NUM;
      break;
    case BAND:
      hval=hand(lval,rval);
      break;
    case BOR:
      hval=hor(lval,rval);
      break;
    case XOR:
      hval=hxor(lval,rval);
      break;
    case BORN:
      hval=hor(lval,hcpl(rval));
      break;
    case NOT:
      ival=hcmp(lval,huge_zero())==0;  /* do not use BOOLEAN() here */
      type=NUM;
      break;
    case LSH:
      hval=hshl(lval,huge_to_int(rval));
      break;
    case RSH:
      hval=hshra(lval,huge_to_int(rval));
      break;
    case RSHU:
      hval=hshr(lval,huge_to_int(rval));
      break;
    case LT:
      ival=BOOLEAN(hcmp(lval,rval)<0);
      type=NUM;
      break;
    case GT:
      ival=BOOLEAN(hcmp(lval,rval)>0);
      type=NUM;
      break;
    case LEQ:
      ival=BOOLEAN(hcmp(lval,rval)<=0);
      type=NUM;
      break;
    case GEQ:
      ival=BOOLEAN(hcmp(lval,rval)>=0);
      type=NUM;
      break;
    case NEQ:
      ival=BOOLEAN(hcmp(lval,rval)!=0);
      type=NUM;
      break;
    case EQ:
      ival=BOOLEAN(hcmp(lval,rval)==0);
      type=NUM;
      break;
    default:
      return;
    }
  }
#if FLOAT_PARSER
  else if(type==FLT){
    tfloat lval,rval;
    if(tree->left){
      switch(tree->left->type){
        case NUM: lval=(tfloat)tree->left->c.val; break;
        case HUG: lval=huge_to_float(tree->left->c.huge); break;
        case FLT: lval=tree->left->c.flt; break;
      }
    }
    if(tree->right){
      switch(tree->right->type){
        case NUM: rval=(tfloat)tree->right->c.val; break;
        case HUG: rval=huge_to_float(tree->right->c.huge); break;
        case FLT: rval=tree->right->c.flt; break;
      }
    }
    switch(tree->type){
    case ADD:
      fval=(lval+rval);
      break;
    case SUB:
      fval=(lval-rval);
      break;
    case MUL:
      fval=(lval*rval);
      break;
    case DIV:
      if(rval==0.0){
        general_error(41);
        fval=0.0;
      }else
        fval=(lval/rval);
      break;
    case NEG:
      fval=(-lval);
      break;
    case LT:
      ival=BOOLEAN(lval<rval);
      type=NUM;
      break;
    case GT:
      ival=BOOLEAN(lval>rval);
      type=NUM;
      break;
    case LEQ:
      ival=BOOLEAN(lval<=rval);
      type=NUM;
      break;
    case GEQ:
      ival=BOOLEAN(lval>=rval);
      type=NUM;
      break;
    case NEQ:
      ival=BOOLEAN(lval!=rval);
      type=NUM;
      break;
    case EQ:
      ival=BOOLEAN(lval==rval);
      type=NUM;
      break;
    default:
      return;
    }
  }
#endif  /* FLOAT_PARSER */
  else{
    if(tree->type==SYM&&tree->c.sym->type==EXPRESSION){
      switch(tree->c.sym->expr->type){
      case NUM:
        ival=tree->c.sym->expr->c.val;
        type=NUM;
        break;
      case HUG:
        hval=tree->c.sym->expr->c.huge;
        type=HUG;
        break;
#if FLOAT_PARSER
      case FLT:
        fval=tree->c.sym->expr->c.flt;
        type=FLT;
        break;
#endif
      default:
        return;
      }
    }else return;
  }
  free_expr(tree->left);
  free_expr(tree->right);
  tree->left=tree->right=NULL;
  switch(type){
    case HUG:
      if(!huge_chkrange(hval,bitspertaddr)){
        tree->c.huge=hval;
        break;
      }
      ival=huge_to_int(hval);
      type=NUM;
    case NUM: tree->c.val=ival; break;
#if FLOAT_PARSER
    case FLT: tree->c.flt=fval; break;
#endif
    default: ierror(0); break;
  }
  tree->type=type;
}

static void add_dep(section *src,section *dest)
{
  if(!parse_finished)
    return;
  if(num_secs&&src!=NULL&&src!=dest){
    if(debug&&(!dest->deps||!BTST(dest->deps,src->idx)))
      printf("sec %s might depend on %s\n",src->name,dest->name);
    if(!dest->deps){
      dest->deps=mymalloc(BVSIZE(num_secs));
      memset(dest->deps,0,BVSIZE(num_secs));
    }
    BSET(dest->deps,src->idx);
  }
}

/* Evaluate an expression using current values of all symbols.
   Result is written to *result. The return value specifies
   whether the result is constant (i.e. only depending on
   constants or absolute symbols). */
int eval_expr(expr *tree,taddr *result,section *sec,taddr pc)
{
  taddr val,lval,rval;
  symbol *lsym,*rsym;
  int cnst=1,lcnst,rcnst,lbok;

  if(!tree)
    ierror(0);
  if(tree->left&&!(lcnst=eval_expr(tree->left,&lval,sec,pc)))
    cnst=0;
  if(tree->right&&!(rcnst=eval_expr(tree->right,&rval,sec,pc)))
    cnst=0;

  switch(tree->type){
  case ADD:
    val=(lval+rval);
    break;
  case SUB:
    if(cnst==0&&find_base(tree->right,&rsym,sec,pc)==BASE_OK&&LOCREF(rsym)){
      lbok=find_base(tree->left,&lsym,sec,pc)==BASE_OK;
      if(lbok&&LOCREF(lsym)&&lsym->sec==rsym->sec){
        /* l2-l1 is constant when both have a valid symbol-base, and both
           symbols are LABSYMs from the same section, e.g. (sym1+x)-(sym2-y) */
        cnst=1;
        add_dep(sec,lsym->sec);
      }else if(lbok&&(rsym->sec==sec&&(EXTREF(lsym)||LOCREF(lsym)))){
        /* Difference between symbols from different sections or between an
           external symbol and a symbol from the current section can be
           represented by a REL_PC, so we calculate the addend. */
        val=(pc-rval+lval-(lsym->sec?lsym->sec->org:0));
        break;
      }
    }
    val=(lval-rval);
    break;
  case MUL:
    val=(lval*rval);
    break;
  case DIV:
    if(rval==0){
      if (final_pass)
        general_error(41);
      val=0;
    }else if(lval==taddrmin&&rval==-1){
      if (final_pass)
        general_error(21,sizeof(taddr)*CHAR_BIT); /* target data type overflow */
      val=taddrmin;
    }else
      val=(lval/rval);
    break;
  case MOD:
    if(rval==0){
      if (final_pass)
        general_error(41);
      val=0;
    }else if(lval==taddrmin&&rval==-1){
      if (final_pass)
        general_error(21,sizeof(taddr)*CHAR_BIT); /* target data type overflow */
      val=taddrmin;
    }else
      val=(lval%rval);
    break;
  case NEG:
    val=(-lval);
    break;
  case CPL:
    val=(~lval);
    break;
  case LAND:
    val=BOOLEAN(lval&&rval);
    break;
  case LOR:
    val=BOOLEAN(lval||rval);
    break;
  case BAND:
    if(cnst==0){
      if((rcnst&&find_base(tree->left,&lsym,sec,pc)==BASE_OK&&LOCREF(lsym)&&
          (rval<lsym->sec->align&&rval>=0))||
         (lcnst&&find_base(tree->right,&lsym,sec,pc)==BASE_OK&&LOCREF(lsym)&&
          (lval<lsym->sec->align&&lval>=0))){
        /* if the operation is with a local label and the section's
           alignment is higher than the requested and-operation,
           then the result can be assumed constant */
        cnst=1;
        add_dep(sec,lsym->sec);
      }
    }
    val=(lval&rval);
    break;
  case BOR:
    val=(lval|rval);
    break;
  case XOR:
    val=(lval^rval);
    break;
  case BORN:
    val=(lval|~rval);
    break;
  case NOT:
    val=!lval;  /* do not use BOOLEAN() here */
    break;
  case LSH:
    val=lshift(lval,(int)rval);
    break;
  case RSH:
    val=rshift(lval,(int)rval);
    break;
  case RSHU:
    val=urshift((utaddr)lval,(int)rval);
    break;
  case LT:
    val=BOOLEAN(lval<rval);
    break;
  case GT:
    val=BOOLEAN(lval>rval);
    break;
  case LEQ:
    val=BOOLEAN(lval<=rval);
    break;
  case GEQ:
    val=BOOLEAN(lval>=rval);
    break;
  case NEQ:
    val=BOOLEAN(lval!=rval);
    break;
  case EQ:
    val=BOOLEAN(lval==rval);
    break;
  case SYM:
    lsym=tree->c.sym;
    if(lsym->type==EXPRESSION){
      if(lsym->flags&INEVAL)
        general_error(18,lsym->name);
      lsym->flags|=INEVAL;
      cnst=eval_expr(lsym->expr,&val,sec,pc);
      lsym->flags&=~INEVAL;
    }else if(LOCREF(lsym)){
      update_curpc(tree,sec,pc);
      val=lsym->pc;
      if(lsym->flags&ABSLABEL)
        cnst=1;
      else
        cnst=lsym->sec==NULL?0:(lsym->sec->flags&UNALLOCATED)!=0;
      if(cnst) add_dep(sec,lsym->sec);
    }else{
      /* IMPORT */
      cnst=0;
      val=0;
    }
    break;
  case NUM:
    val=tree->c.val;
    break;
  case HUG:
    if (final_pass && !huge_chkrange(tree->c.huge,bytespertaddr*BITSPERBYTE))
      general_error(21,bytespertaddr*BITSPERBYTE); /* target data type overflow */
    val=huge_to_int(tree->c.huge);
    break;
#if FLOAT_PARSER
  case FLT:
    if (final_pass && !flt_chkrange(tree->c.flt,bytespertaddr*BITSPERBYTE))
      general_error(21,bytespertaddr*BITSPERBYTE); /* target data type overflow */
    val=(taddr)tree->c.flt;
    break;
#endif
  default:
#ifdef EXT_UNARY_EVAL
    if (EXT_UNARY_EVAL(tree->type,lval,&val,cnst))
      break;
#endif
    ierror(0);
  }
  *result=val;
  return cnst;
}

/* Evaluate a huge integer expression using current values of all symbols.
   Result is written to *result. The return value specifies whether all
   operations were valid. */
int eval_expr_huge(expr *tree,thuge *result)
{
  thuge val,lval,rval;

  if(!tree)
    ierror(0);
  if(tree->left&&!eval_expr_huge(tree->left,&lval))
    return 0;
  if(tree->right&&!eval_expr_huge(tree->right,&rval))
    return 0;

  switch (tree->type){
  case ADD:
    val=hadd(lval,rval);
    break;
  case SUB:
    val=hsub(lval,rval);
    break;
  case MUL:
    val=hmul(lval,rval);
    break;
  case DIV:
    if(hcmp(rval,huge_zero())==0){
      if (final_pass)
        general_error(41);
      val=huge_zero();
    }else
      val=hdiv(lval,rval);
    break;
  case MOD:
    if(hcmp(rval,huge_zero())==0){
      if (final_pass)
        general_error(41);
      val=huge_zero();
    }else
      val=hmod(lval,rval);
    break;
  case NEG:
    val=hneg(lval);
    break;
  case CPL:
    val=hcpl(lval);
    break;
  case LAND:
    val=huge_from_int(BOOLEAN(hcmp(lval,huge_zero())!=0
                      &&hcmp(rval,huge_zero())!=0));
    break;
  case LOR:
    val=huge_from_int(BOOLEAN(hcmp(lval,huge_zero())!=0
                      ||hcmp(rval,huge_zero())!=0));
    break;
  case BAND:
    val=hand(lval,rval);
    break;
  case BOR:
    val=hor(lval,rval);
    break;
  case XOR:
    val=hxor(lval,rval);
    break;
  case BORN:
    val=hor(lval,hcpl(rval));
    break;
  case NOT:
    val=huge_from_int(hcmp(lval,huge_zero())==0); /* do not use BOOLEAN() here */
    break;
  case LSH:
    val=hshl(lval,huge_to_int(rval));
    break;
  case RSH:
    val=hshra(lval,huge_to_int(rval));
    break;
  case RSHU:
    val=hshr(lval,huge_to_int(rval));
    break;
  case LT:
    val=huge_from_int(BOOLEAN(hcmp(lval,rval)<0));
    break;
  case GT:
    val=huge_from_int(BOOLEAN(hcmp(lval,rval)>0));
    break;
  case LEQ:
    val=huge_from_int(BOOLEAN(hcmp(lval,rval)<=0));
    break;
  case GEQ:
    val=huge_from_int(BOOLEAN(hcmp(lval,rval)>=0));
    break;
  case NEQ:
    val=huge_from_int(BOOLEAN(hcmp(lval,rval)!=0));
    break;
  case EQ:
    val=huge_from_int(BOOLEAN(hcmp(lval,rval)==0));
    break;
  case SYM:
    if(tree->c.sym->type==EXPRESSION){
      int ok;
      if(tree->c.sym->flags&INEVAL)
        general_error(18,tree->c.sym->name);
      tree->c.sym->flags|=INEVAL;
      ok=eval_expr_huge(tree->c.sym->expr,&val);
      tree->c.sym->flags&=~INEVAL;
      if(!ok) return 0;
    }
    else if(tree->c.sym->type==LABSYM&&(tree->c.sym->flags&ABSLABEL))
      val=huge_from_int(tree->c.sym->pc);  /* allow absolute labels */
#if 0 /* all relocations should be representable by taddr */
    else if(EXTREF(tree->c.sym))
      val=huge_zero();
#endif
    else
      return 0;
    break;
  case NUM:
    val=huge_from_int(tree->c.val);
    break;
  case HUG:
    val=tree->c.huge;
    break;
#if FLOAT_PARSER
  case FLT:
    val=huge_from_float(tree->c.flt);
    break;
#endif
  default:
    return 0;
  }
  *result=val;
  return 1;
}

#if FLOAT_PARSER
/* Evaluate a floating point expression using current values of all symbols.
   Result is written to *result. The return value specifies whether all
   operations were valid. */
int eval_expr_float(expr *tree,tfloat *result)
{
  tfloat val,lval,rval;

  if(!tree)
    ierror(0);
  if(tree->left&&!eval_expr_float(tree->left,&lval))
    return 0;
  if(tree->right&&!eval_expr_float(tree->right,&rval))
    return 0;

  switch(tree->type){
  case ADD:
    val=(lval+rval);
    break;
  case SUB:
    val=(lval-rval);
    break;
  case MUL:
    val=(lval*rval);
    break;
  case DIV:
    if(rval==0.0){
      if (final_pass)
        general_error(41);
      val=0.0;
    }else
      val=(lval/rval);
    break;
  case NEG:
    val=(-lval);
    break;
  case LT:
    val=BOOLEAN(lval<rval);
    break;
  case GT:
    val=BOOLEAN(lval>rval);
    break;
  case LEQ:
    val=BOOLEAN(lval<=rval);
    break;
  case GEQ:
    val=BOOLEAN(lval>=rval);
    break;
  case NEQ:
    val=BOOLEAN(lval!=rval);
    break;
  case EQ:
    val=BOOLEAN(lval==rval);
    break;
  case SYM:
    if(tree->c.sym->type==EXPRESSION){
      int ok;
      if(tree->c.sym->flags&INEVAL)
        general_error(18,tree->c.sym->name);
      tree->c.sym->flags|=INEVAL;
      ok=eval_expr_float(tree->c.sym->expr,&val);
      tree->c.sym->flags&=~INEVAL;
      if(!ok) return 0;
    }else
      return 0;
    break;
  case NUM:
    val=(tfloat)tree->c.val;
    break;
  case HUG:
    val=huge_to_float(tree->c.huge);
    break;
  case FLT:
    val=tree->c.flt;
    break;
  default:
    return 0;
  }
  *result=val;
  return 1;
}
#endif  /* FLOAT_PARSER */

void print_expr(FILE *f,expr *p)
{
  if(p==NULL)
    ierror(0);
  simplify_expr(p);
  if(p->type==NUM)
    fprintf(f,"%lld=%#llx",(long long)p->c.val,ULLTVAL(p->c.val));
  else if(p->type==HUG)
    fprintf(f,"%#016llx%016llx",
            (unsigned long long)p->c.huge.hi,(unsigned long long)p->c.huge.lo);
#if FLOAT_PARSER
  else if(p->type==FLT)
    fprintf(f,"%.8g",(double)p->c.flt);
#endif
  else
    fprintf(f,"complex expression");
}

static int _find_base(expr *p,symbol **base,section *sec,taddr pc)
{
#ifdef EXT_FIND_BASE
  int ret;
  if(ret=EXT_FIND_BASE(base,p,sec,pc))
    return ret;
#endif
  if(p->type==SYM){
    update_curpc(p,sec,pc);
    if(p->c.sym->type==EXPRESSION)
      return _find_base(p->c.sym->expr,base,sec,pc);
    else{
      if(base)
        *base=p->c.sym;  /* set base to symbol, also when BASE_ILLEGAL later */
      return BASE_OK;
    }
  }else if(p->type==ADD){
    taddr val;
    if(eval_expr(p->left,&val,sec,pc)&&
       _find_base(p->right,base,sec,pc)==BASE_OK)
      return BASE_OK;
    if(eval_expr(p->right,&val,sec,pc)&&
       _find_base(p->left,base,sec,pc)==BASE_OK)
      return BASE_OK;
  }else if(p->type==SUB){
    taddr val;
    symbol *pcsym;
    if(eval_expr(p->right,&val,sec,pc)&&
       _find_base(p->left,base,sec,pc)==BASE_OK)
      return BASE_OK;
    if(_find_base(p->left,base,sec,pc)==BASE_OK&&
       _find_base(p->right,&pcsym,sec,pc)==BASE_OK) {
      if(LOCREF(pcsym)&&pcsym->sec==sec&&(LOCREF(*base)||EXTREF(*base)))
        return BASE_PCREL;
    }
  }
  return BASE_ILLEGAL;
}

/* Tests, if an expression is based only on one non-absolute
   symbol plus constants. Returns that symbol or zero.
   Note: Does not find all possible solutions. */
int find_base(expr *p,symbol **base,section *sec,taddr pc)
{
  if(base)
    *base=NULL;
  return _find_base(p,base,sec,pc);
}

expr *number_expr(taddr val)
{
  expr *new=new_expr();
  new->type=NUM;
  new->c.val=val;
  return new;
}

expr *huge_expr(thuge val)
{
  expr *new=new_expr();
  new->type=HUG;
  new->c.huge=val;
  return new;
}

#if FLOAT_PARSER
expr *float_expr(tfloat val)
{
  expr *new=new_expr();
  new->type=FLT;
  new->c.flt=val;
  return new;
}
#endif

taddr parse_constexpr(char **s)
{
  expr *tree;
  taddr val = 0;

  if (tree = parse_expr(s)) {
    switch(tree->type){
      case NUM:
        val = tree->c.val;
        break;
      case HUG:
        general_error(59);  /* cannot evaluate huge integer */
        break;
      case FLT:
        general_error(60);  /* cannot evaluate floating point */
        break;
      default:
        general_error(30);  /* expression must be a constant */
        break;
    }
    free_expr(tree);
  }
  return val;
}
