#include "c.h"

static char rcsid[] = "$Id: expr.nw,v 2.21 1997/05/23 22:25:35 drh Exp $";

static char prec[] = {
#define xx(a,b,c,d,e,f,g) c,
#define yy(a,b,c,d,e,f,g) c,
#include "token.h"
};
static int oper[] = {
#define xx(a,b,c,d,e,f,g) d,
#define yy(a,b,c,d,e,f,g) d,
#include "token.h"
};
float refinc = 1.0;
static Tree expr2(void);
static Tree expr3(int);
static Tree nullcheck(Tree);
static Tree postfix(Tree);
static Tree unary(void);
static Tree primary(void);
static Type super(Type ty);

static Type super(Type ty) {
  switch (ty->op) {
  case INT:
    if (ty->size < inttype->size)
      return inttype;
    break;
  case UNSIGNED:
    if (ty->size < unsignedtype->size)
      return unsignedtype;
    break;
  case POINTER:
    return unsignedptr;
  }
  return ty;
}
Tree expr(int tok) {
  static char stop[] = { IF, ID, '}', 0 };
  Tree p = expr1(0);

  while (t == ',') {
    Tree q;
    t = gettok();
    q = pointer(expr1(0));
    p = tree(RIGHT, q->type, root(value(p)), q);
  }
  if (tok)
    test(tok, stop);
  return p;
}
Tree expr0(int tok) {
  return root(expr(tok));
}
Tree expr1(int tok) {
  static char stop[] = { IF, ID, 0 };
  Tree p = expr2();

  if (t == '='
  || (prec[t] >=  6 && prec[t] <=  8)
  || (prec[t] >= 11 && prec[t] <= 13)) {
    int op = t;
    t = gettok();
    if (oper[op] == ASGN)
      p = asgntree(ASGN, p, value(expr1(0)));
    else
      {
        expect('=');
        p = incr(op, p, expr1(0));
      }
  }
  if (tok)
    test(tok, stop);
  return p;
}
Tree incr(int op, Tree v, Tree e) {
  return asgntree(ASGN, v, (*optree[op])(oper[op], v, e));
}
static Tree expr2(void) {
  Tree p = expr3(4);

  if (t == '?') {
    Tree l, r;
    Coordinate pts[2];
    if (Aflag > 1 && isfunc(p->type))
      warning("%s used in a conditional expression\n",
        funcname(p));
    p = pointer(p);
    t = gettok();
    pts[0] = src;
    l = pointer(expr(':'));
    pts[1] = src;
    r = pointer(expr2());
    p = condtree(p, l, r);
    if (events.points)
      if (p->op == COND) {
        Symbol t1 = p->u.sym;
        assert(p->kids[1]);
        assert(p->kids[1]->op == RIGHT);
        apply(events.points, &pts[0], &p->kids[1]->kids[0]);
        apply(events.points, &pts[1], &p->kids[1]->kids[1]);
        p = tree(COND, p->type, p->kids[0],
          tree(RIGHT, p->type,
            p->kids[1]->kids[0],
            p->kids[1]->kids[1]));
        p->u.sym = t1;
      }
  }
  return p;
}
Tree value(Tree p) {
  int op = generic(rightkid(p)->op);

  if (op==AND || op==OR || op==NOT || op==EQ || op==NE
  ||  op== LE || op==LT || op== GE || op==GT)
    p = condtree(p, consttree(1, inttype),
      consttree(0, inttype));
  return p;
}
static Tree expr3(int k) {
  int k1;
  Tree p = unary();

  for (k1 = prec[t]; k1 >= k; k1--)
    while (prec[t] == k1 && *cp != '=') {
      Tree r;
      Coordinate pt;
      int op = t;
      t = gettok();
      pt = src;
      p = pointer(p);
      if (op == ANDAND || op == OROR) {
        r = pointer(expr3(k1));
        if (events.points)
          apply(events.points, &pt, &r);
      } else
        r = pointer(expr3(k1 + 1));
      p = (*optree[op])(oper[op], p, r);
    }
  return p;
}
static Tree unary(void) {
  Tree p;

  switch (t) {
  case '*':    t = gettok(); p = unary(); p = pointer(p);
              if (isptr(p->type)
              && (isfunc(p->type->type) || isarray(p->type->type)))
                p = retype(p, p->type->type);
              else {
                if (YYnull)
                  p = nullcheck(p);
                p = rvalue(p);
              } break;
  case '&':    t = gettok(); p = unary(); if (isarray(p->type) || isfunc(p->type))
                p = retype(p, ptr(p->type));
              else
                p = lvalue(p);
              if (isaddrop(p->op) && p->u.sym->sclass == REGISTER)
                error("invalid operand of unary &; `%s' is declared register\n", p->u.sym->name);

              else if (isaddrop(p->op))
                p->u.sym->addressed = 1;
 break;
  case '+':    t = gettok(); p = unary(); p = pointer(p);
              if (isarith(p->type))
                p = cast(p, promote(p->type));
              else
                typeerror(ADD, p, NULL);  break;
  case '-':    t = gettok(); p = unary(); p = pointer(p);
              if (isarith(p->type)) {
                Type ty = promote(p->type);
                p = cast(p, ty);
                if (isunsigned(ty)) {
                  warning("unsigned operand of unary -\n");
                  p = simplify(ADD, ty, simplify(BCOM, ty, p, NULL), cnsttree(ty, 1UL));
                } else
                  p = simplify(NEG, ty, p, NULL);
              } else
                typeerror(SUB, p, NULL); break;
  case '~':    t = gettok(); p = unary(); p = pointer(p);
              if (isint(p->type)) {
                Type ty = promote(p->type);
                p = simplify(BCOM, ty, cast(p, ty), NULL);
              } else
                typeerror(BCOM, p, NULL);  break;
  case '!':    t = gettok(); p = unary(); p = pointer(p);
              if (isscalar(p->type))
                p = simplify(NOT, inttype, cond(p), NULL);
              else
                typeerror(NOT, p, NULL); break;
  case INCR:   t = gettok(); p = unary(); p = incr(INCR, pointer(p), consttree(1, inttype)); break;
  case DECR:   t = gettok(); p = unary(); p = incr(DECR, pointer(p), consttree(1, inttype)); break;
  case TYPECODE: case SIZEOF: { int op = t;
              Type ty;
              p = NULL;
              t = gettok();
              if (t == '(') {
                t = gettok();
                if (istypename(t, tsym)) {
                  ty = typename();
                  expect(')');
                } else {
                  p = postfix(expr(')'));
                  ty = p->type;
                }
              } else {
                p = unary();
                ty = p->type;
              }
              assert(ty);
              if (op == TYPECODE)
                p = cnsttree(inttype, (long)ty->op);
              else {
                if (isfunc(ty) || ty->size == 0)
                  error("invalid type argument `%t' to `sizeof'\n", ty);
                else if (p && rightkid(p)->op == FIELD)
                  error("`sizeof' applied to a bit field\n");
                p = cnsttree(unsignedlong, (unsigned long)ty->size);
              } } break;
  case '(':
    t = gettok();
    if (istypename(t, tsym)) {
      Type ty, ty1 = typename(), pty;
      expect(')');
      ty = unqual(ty1);
      if (isenum(ty)) {
        Type ty2 = ty->type;
        if (isconst(ty1))
          ty2 = qual(CONST, ty2);
        if (isvolatile(ty1))
          ty2 = qual(VOLATILE, ty2);
        ty1 = ty2;
        ty = ty->type;
      }
      p = pointer(unary());
      pty = p->type;
      if (isenum(pty))
        pty = pty->type;
      if (isarith(pty) && isarith(ty)
      ||  isptr(pty)   && isptr(ty))
        p = cast(p, ty);
      else if (isptr(pty) && isint(ty)
      ||       isint(pty) && isptr(ty)) {
        if (Aflag >= 1 && ty->size < pty->size)
          warning("conversion from `%t' to `%t' is compiler dependent\n", p->type, ty);

        p = cast(p, ty);
      } else if (ty != voidtype) {
        error("cast from `%t' to `%t' is illegal\n",
          p->type, ty1);
        ty1 = inttype;
      }
      if (generic(p->op) == INDIR || ty->size == 0)
        p = tree(RIGHT, ty1, NULL, p);
      else
        p = retype(p, ty1);
    } else
      p = postfix(expr(')'));
    break;
  default:
    p = postfix(primary());
  }
  return p;
}

static Tree postfix(Tree p) {
  for (;;)
    switch (t) {
    case INCR:  p = tree(RIGHT, p->type,
            tree(RIGHT, p->type,
              p,
              incr(t, p, consttree(1, inttype))),
            p);
          t = gettok(); break;
    case DECR:  p = tree(RIGHT, p->type,
            tree(RIGHT, p->type,
              p,
              incr(t, p, consttree(1, inttype))),
            p);
          t = gettok(); break;
    case '[':   {
            Tree q;
            t = gettok();
            q = expr(']');
            if (YYnull)
              if (isptr(p->type))
                p = nullcheck(p);
              else if (isptr(q->type))
                q = nullcheck(q);
            p = (*optree['+'])(ADD, pointer(p), pointer(q));
            if (isptr(p->type) && isarray(p->type->type))
              p = retype(p, p->type->type);
            else
              p = rvalue(p);
          } break;
    case '(':   {
            Type ty;
            Coordinate pt;
            p = pointer(p);
            if (isptr(p->type) && isfunc(p->type->type))
              ty = p->type->type;
            else {
              error("found `%t' expected a function\n", p->type);
              ty = func(voidtype, NULL, 1);
              p = retype(p, ptr(ty));
            }
            pt = src;
            t = gettok();
            p = call(p, ty, pt);
          } break;
    case '.':   t = gettok();
          if (t == ID) {
            if (isstruct(p->type)) {
              Tree q = addrof(p);
              p = field(q, token);
              q = rightkid(q);
              if (isaddrop(q->op) && q->u.sym->temporary)
                p = tree(RIGHT, p->type, p, NULL);
            } else
              error("left operand of . has incompatible type `%t'\n",
                p->type);
            t = gettok();
          } else
            error("field name expected\n"); break;
    case DEREF: t = gettok();
          p = pointer(p);
          if (t == ID) {
            if (isptr(p->type) && isstruct(p->type->type)) {
              if (YYnull)
                p = nullcheck(p);
              p = field(p, token);
            } else
              error("left operand of -> has incompatible type `%t'\n", p->type);

            t = gettok();
          } else
            error("field name expected\n"); break;
    default:
      return p;
    }
}
static Tree primary(void) {
  Tree p;

  assert(t != '(');
  switch (t) {
  case ICON:
  case FCON: p = tree(mkop(CNST,tsym->type), tsym->type, NULL, NULL);
       p->u.v = tsym->u.c.v;
 break;
  case SCON: tsym->u.c.v.p = stringn(tsym->u.c.v.p, tsym->type->size);
       tsym = constant(tsym->type, tsym->u.c.v);
       if (tsym->u.c.loc == NULL)
         tsym->u.c.loc = genident(STATIC, tsym->type, GLOBAL);
       p = idtree(tsym->u.c.loc); break;
  case ID:   if (tsym == NULL)
         {
           Symbol q = install(token, &identifiers, level, level < LOCAL ? PERM : FUNC);
           q->src = src;
           t = gettok();
           if (t == '(') {
             Symbol r;
             q->sclass = EXTERN;
             q->type = func(inttype, NULL, 1);
             if (Aflag >= 1)
               warning("missing prototype\n");
             (*IR->defsymbol)(q);
             if ((r = lookup(q->name, externals)) != NULL) {
               q->defined = r->defined;
               q->temporary = r->temporary;
               q->generated = r->generated;
               q->computed = r->computed;
               q->addressed = r->addressed;
             } else {
               r = install(q->name, &externals, GLOBAL, PERM);
               r->src = q->src;
               r->type = q->type;
               r->sclass = EXTERN;
             }
           } else {
             error("undeclared identifier `%s'\n", q->name);
             q->sclass = AUTO;
             q->type = inttype;
             if (q->scope == GLOBAL)
               (*IR->defsymbol)(q);
             else
               addlocal(q);
           }
           if (xref)
             use(q, src);
           return idtree(q);
         }
       if (xref)
         use(tsym, src);
       if (tsym->sclass == ENUM)
         p = consttree(tsym->u.value, inttype);
       else {
         if (tsym->sclass == TYPEDEF)
           error("illegal use of type name `%s'\n", tsym->name);
         p = idtree(tsym);
       } break;
  case FIRSTARG:
    if (level > PARAM && cfunc && cfunc->u.f.callee[0])
      p = idtree(cfunc->u.f.callee[0]);
    else {
      error("illegal use of `%k'\n", FIRSTARG);
      p = cnsttree(inttype, 0L);
    }
    break;
  default:
    error("illegal expression\n");
      p = cnsttree(inttype, 0L);
  }
  t = gettok();
  return p;
}
Tree idtree(Symbol p) {
  int op;
  Tree e;
  Type ty = p->type ? unqual(p->type) : voidptype;

  p->ref += refinc;
  if (p->scope  == GLOBAL
  ||  p->sclass == STATIC || p->sclass == EXTERN)
    op = ADDRG;
  else if (p->scope == PARAM) {
    op = ADDRF;
    if (isstruct(p->type) && !IR->wants_argb)
      {
        e = tree(mkop(op,voidptype), ptr(ptr(p->type)), NULL, NULL);
        e->u.sym = p;
        return rvalue(rvalue(e));
      }
  } else
    op = ADDRL;
  if (isarray(ty))
    e = tree(mkop(op,voidptype), p->type,      NULL, NULL);
  else if (isfunc(ty))
    e = tree(mkop(op,funcptype), p->type,      NULL, NULL);
  else
    e = tree(mkop(op,voidptype), ptr(p->type), NULL, NULL);
  e->u.sym = p;
  if (isptr(e->type))
    e = rvalue(e);
  return e;
}

Tree rvalue(Tree p) {
  Type ty = deref(p->type);

  ty = unqual(ty);
  return tree(mkop(INDIR,ty), ty, p, NULL);
}
Tree lvalue(Tree p) {
  if (generic(p->op) != INDIR) {
    error("lvalue required\n");
    return value(p);
  } else if (unqual(p->type) == voidtype)
    warning("`%t' used as an lvalue\n", p->type);
  return p->kids[0];
}
Tree retype(Tree p, Type ty) {
  Tree q;

  if (p->type == ty)
    return p;
  q = tree(p->op, ty, p->kids[0], p->kids[1]);
  q->node = p->node;
  q->u = p->u;
  return q;
}
Tree rightkid(Tree p) {
  while (p && p->op == RIGHT)
    if (p->kids[1])
      p = p->kids[1];
    else if (p->kids[0])
      p = p->kids[0];
    else
      assert(0);
  assert(p);
  return p;
}
int hascall(Tree p) {
  if (p == 0)
    return 0;
  if (generic(p->op) == CALL || (IR->mulops_calls &&
    (p->op == DIV+I || p->op == MOD+I || p->op == MUL+I
  || p->op == DIV+U || p->op == MOD+U || p->op == MUL+U)))
    return 1;
  return hascall(p->kids[0]) || hascall(p->kids[1]);
}
Type binary(Type xty, Type yty) {
#define xx(t) if (xty == t || yty == t) return t
  xx(longdouble);
  xx(doubletype);
  xx(floattype);
  xx(unsignedlong);
  if (xty == longtype     && yty == unsignedtype
  ||  xty == unsignedtype && yty == longtype)
    if (longtype->size > unsignedtype->size)
      return longtype;
    else
      return unsignedlong;
  xx(longtype);
  xx(unsignedtype);
  return inttype;
#undef xx
}
Tree pointer(Tree p) {
  if (isarray(p->type))
    /* assert(p->op != RIGHT || p->u.sym == NULL), */
    p = retype(p, atop(p->type));
  else if (isfunc(p->type))
    p = retype(p, ptr(p->type));
  return p;
}
Tree cond(Tree p) {
  int op = generic(rightkid(p)->op);

  if (op == AND || op == OR || op == NOT
  ||  op == EQ  || op == NE
  ||  op == LE  || op == LT || op == GE || op == GT)
    return p;
  p = pointer(p);
  return (*optree[NEQ])(NE, p, consttree(0, inttype));
}
Tree cast(Tree p, Type type) {
  Type src, dst;

  p = value(p);
  if (p->type == type)
    return p;
  dst = unqual(type);
  src = unqual(p->type);
  if (src->op != dst->op || src->size != dst->size) {
    switch (src->op) {
    case INT:
      if (src->size < inttype->size)
        p = simplify(CVI, inttype, p, NULL);
      break;
    case UNSIGNED:
      if (src->size < inttype->size)
        p = simplify(CVU, inttype, p, NULL);
      else if (src->size < unsignedtype->size)
        p = simplify(CVU, unsignedtype, p, NULL);
      break;
    case ENUM:
      p = retype(p, inttype);
      break;
    case POINTER:
      if (isint(dst) && src->size > dst->size)
        warning("conversion from `%t' to `%t' is undefined\n", p->type, type);
      p = simplify(CVP, super(src), p, NULL);
      break;
    case FLOAT:
      break;
    default: assert(0);
    }
    {
      src = p->type;
      dst = super(dst);
      if (src->op != dst->op)
        switch (src->op) {
        case INT:
          p = simplify(CVI, dst, p, NULL);
          break;
        case UNSIGNED:
          if (isfloat(dst)) {
            Type ssrc = signedint(src);
            Tree two = cnsttree(longdouble, (long double)2.0);
            p = (*optree['+'])(ADD,
              (*optree['*'])(MUL,
                two,
                simplify(CVU, ssrc,
                  simplify(RSH, src,
                    p, consttree(1, inttype)), NULL)),
              simplify(CVU, ssrc,
                simplify(BAND, src,
                  p, consttree(1, unsignedtype)), NULL));
          } else
            p = simplify(CVU, dst, p, NULL);
          break;
        case FLOAT:
          if (isunsigned(dst)) {
            Type sdst = signedint(dst);
            Tree c = cast(cnsttree(longdouble, (long double)sdst->u.sym->u.limits.max.i + 1), src);
            p = condtree(
              simplify(GE, src, p, c),
              (*optree['+'])(ADD,
                cast(cast(simplify(SUB, src, p, c), sdst), dst),
                cast(cnsttree(unsignedlong, (unsigned long)sdst->u.sym->u.limits.max.i + 1), dst)),
              simplify(CVF, sdst, p, NULL));
          } else
            p = simplify(CVF, dst, p, NULL);
          break;
        default: assert(0);
        }
      dst = unqual(type);
    }
  }
  src = p->type;
  switch (src->op) {
  case INT:
    if (src->op != dst->op || src->size != dst->size)
      p = simplify(CVI, dst, p, NULL);
    break;
  case UNSIGNED:
    if (src->op != dst->op || src->size != dst->size)
      p = simplify(CVU, dst, p, NULL);
    break;
  case FLOAT:
    if (src->op != dst->op || src->size != dst->size)
      p = simplify(CVF, dst, p, NULL);
    break;
  case POINTER:
    if (src->op != dst->op)
      p = simplify(CVP, dst, p, NULL);
    else {
      if (isfunc(src->type) && !isfunc(dst->type)
      || !isfunc(src->type) &&  isfunc(dst->type))
        warning("conversion from `%t' to `%t' is compiler dependent\n", p->type, type);

      if (src->size != dst->size)
        p = simplify(CVP, dst, p, NULL);
    }
    break;
  default: assert(0);
  }
  return retype(p, type);
}
Tree field(Tree p, const char *name) {
  Field q;
  Type ty1, ty = p->type;

  if (isptr(ty))
    ty = deref(ty);
  ty1 = ty;
  ty = unqual(ty);
  if ((q = fieldref(name, ty)) != NULL) {
    if (isarray(q->type)) {
      ty = q->type->type;
      if (isconst(ty1) && !isconst(ty))
        ty = qual(CONST, ty);
      if (isvolatile(ty1) && !isvolatile(ty))
        ty = qual(VOLATILE, ty);
      ty = array(ty, q->type->size/ty->size, q->type->align);
    } else {
      ty = q->type;
      if (isconst(ty1) && !isconst(ty))
        ty = qual(CONST, ty);
      if (isvolatile(ty1) && !isvolatile(ty))
        ty = qual(VOLATILE, ty);
      ty = ptr(ty);
    }
    if (YYcheck && !isaddrop(p->op) && q->offset > 0)       /* omit */
      p = nullcall(ty, YYcheck, p, consttree(q->offset, inttype));    /* omit */
    else            /* omit */
    p = simplify(ADD+P, ty, p, consttree(q->offset, inttype));

    if (q->lsb) {
      p = tree(FIELD, ty->type, rvalue(p), NULL);
      p->u.field = q;
    } else if (!isarray(q->type))
      p = rvalue(p);

  } else {
    error("unknown field `%s' of `%t'\n", name, ty);
    p = rvalue(retype(p, ptr(inttype)));
  }
  return p;
}
/* funcname - return name of function f or a function' */
char *funcname(Tree f) {
  if (isaddrop(f->op))
    return stringf("`%s'", f->u.sym->name);
  return "a function";
}
static Tree nullcheck(Tree p) {
  if (!needconst && YYnull && isptr(p->type)) {
    p = value(p);
    if (strcmp(YYnull->name, "_YYnull") == 0) {
      Symbol t1 = temporary(REGISTER, voidptype);
      p = tree(RIGHT, p->type,
        tree(OR, voidtype,
          cond(asgn(t1, cast(p, voidptype))),
          vcall(YYnull, voidtype, (file && *file ? pointer(idtree(mkstr(file)->u.c.loc)) : cnsttree(voidptype, NULL)), cnsttree(inttype, (long)lineno)   , NULL)),
        idtree(t1));
    }

    else
      p = nullcall(p->type, YYnull, p, cnsttree(inttype, 0L));

  }
  return p;
}
Tree nullcall(Type pty, Symbol f, Tree p, Tree e) {
  Type ty;

  if (isarray(pty))
    return retype(nullcall(atop(pty), f, p, e), pty);
  ty = unqual(unqual(p->type)->type);
  return vcall(f, pty,
    p, e,
    cnsttree(inttype, (long)ty->size),
    cnsttree(inttype, (long)ty->align),
    (file && *file ? pointer(idtree(mkstr(file)->u.c.loc)) : cnsttree(voidptype, NULL)), cnsttree(inttype, (long)lineno)   , NULL);
}
