#include        "cc.h"

typedef struct  Ftab    Ftab;
struct  Ftab
{
       char    op;
       char*   name;
       char    typ;
};
typedef struct  Gtab    Gtab;
struct  Gtab
{
       char    etype;
       char*   name;
};

Ftab    ftabinit[OEND];
Gtab    gtabinit[NTYPE];

int
isfunct(Node *n)
{
       Type *t, *t1;
       Funct *f;
       Node *l;
       Sym *s;
       int o;

       o = n->op;
       if(n->left == Z)
               goto no;
       t = n->left->type;
       if(t == T)
               goto no;
       f = t->funct;

       switch(o) {
       case OAS:       // put cast on rhs
       case OASI:
       case OASADD:
       case OASAND:
       case OASASHL:
       case OASASHR:
       case OASDIV:
       case OASLDIV:
       case OASLMOD:
       case OASLMUL:
       case OASLSHR:
       case OASMOD:
       case OASMUL:
       case OASOR:
       case OASSUB:
       case OASXOR:
               if(n->right == Z)
                       goto no;
               t1 = n->right->type;
               if(t1 == T)
                       goto no;
               if(t1->funct == f)
                       break;

               l = new(OXXX, Z, Z);
               *l = *n->right;

               n->right->left = l;
               n->right->right = Z;
               n->right->type = t;
               n->right->op = OCAST;

               if(!isfunct(n->right))
                       prtree(n, "isfunc !");
               break;

       case OCAST:     // t f(T) or T f(t)
               t1 = n->type;
               if(t1 == T)
                       goto no;
               if(f != nil) {
                       s = f->castfr[t1->etype];
                       if(s == S)
                               goto no;
                       n->right = n->left;
                       goto build;
               }
               f = t1->funct;
               if(f != nil) {
                       s = f->castto[t->etype];
                       if(s == S)
                               goto no;
                       n->right = n->left;
                       goto build;
               }
               goto no;
       }

       if(f == nil)
               goto no;
       s = f->sym[o];
       if(s == S)
               goto no;

       /*
        * the answer is yes,
        * now we rewrite the node
        * and give diagnostics
        */
       switch(o) {
       default:
               diag(n, "isfunct op missing %O\n", o);
               goto bad;

       case OADD:      // T f(T, T)
       case OAND:
       case OASHL:
       case OASHR:
       case ODIV:
       case OLDIV:
       case OLMOD:
       case OLMUL:
       case OLSHR:
       case OMOD:
       case OMUL:
       case OOR:
       case OSUB:
       case OXOR:

       case OEQ:       // int f(T, T)
       case OGE:
       case OGT:
       case OHI:
       case OHS:
       case OLE:
       case OLO:
       case OLS:
       case OLT:
       case ONE:
               if(n->right == Z)
                       goto bad;
               t1 = n->right->type;
               if(t1 == T)
                       goto bad;
               if(t1->funct != f)
                       goto bad;
               n->right = new(OLIST, n->left, n->right);
               break;

       case OAS:       // structure copies done by the compiler
       case OASI:
               goto no;

       case OASADD:    // T f(T*, T)
       case OASAND:
       case OASASHL:
       case OASASHR:
       case OASDIV:
       case OASLDIV:
       case OASLMOD:
       case OASLMUL:
       case OASLSHR:
       case OASMOD:
       case OASMUL:
       case OASOR:
       case OASSUB:
       case OASXOR:
               if(n->right == Z)
                       goto bad;
               t1 = n->right->type;
               if(t1 == T)
                       goto bad;
               if(t1->funct != f)
                       goto bad;
               n->right = new(OLIST, new(OADDR, n->left, Z), n->right);
               break;

       case OPOS:      // T f(T)
       case ONEG:
       case ONOT:
       case OCOM:
               n->right = n->left;
               break;


       }

build:
       l = new(ONAME, Z, Z);
       l->sym = s;
       l->type = s->type;
       l->etype = s->type->etype;
       l->xoffset = s->offset;
       l->class = s->class;
       tcomo(l, 0);

       n->op = OFUNC;
       n->left = l;
       n->type = l->type->link;
       if(tcompat(n, T, l->type, tfunct))
               goto bad;
       if(tcoma(n->left, n->right, l->type->down, 1))
               goto bad;
       return 1;

no:
       return 0;

bad:
       diag(n, "cant rewrite typestr for op %O\n", o);
       prtree(n, "isfunct");
       n->type = T;
       return 1;
}

void
dclfunct(Type *t, Sym *s)
{
       Funct *f;
       Node *n;
       Type *f1, *f2, *f3, *f4;
       int o, i, c;
       char str[100];

       if(t->funct)
               return;

       // recognize generated tag of dorm _%d_
       if(t->tag == S)
               goto bad;
       for(i=0; c = t->tag->name[i]; i++) {
               if(c == '_') {
                       if(i == 0 || t->tag->name[i+1] == 0)
                               continue;
                       break;
               }
               if(c < '0' || c > '9')
                       break;
       }
       if(c == 0)
               goto bad;

       f = alloc(sizeof(*f));
       for(o=0; o<sizeof(f->sym); o++)
               f->sym[o] = S;

       t->funct = f;

       f1 = typ(TFUNC, t);
       f1->down = copytyp(t);
       f1->down->down = t;

       f2 = typ(TFUNC, types[TINT]);
       f2->down = copytyp(t);
       f2->down->down = t;

       f3 = typ(TFUNC, t);
       f3->down = typ(TIND, t);
       f3->down->down = t;

       f4 = typ(TFUNC, t);
       f4->down = t;

       for(i=0;; i++) {
               o = ftabinit[i].op;
               if(o == OXXX)
                       break;
               sprint(str, "%s_%s_", t->tag->name, ftabinit[i].name);
               n = new(ONAME, Z, Z);
               n->sym = slookup(str);
               f->sym[o] = n->sym;
               switch(ftabinit[i].typ) {
               default:
                       diag(Z, "dclfunct op missing %d\n", ftabinit[i].typ);
                       break;

               case 1: // T f(T,T)     +
                       dodecl(xdecl, CEXTERN, f1, n);
                       break;

               case 2: // int f(T,T)   ==
                       dodecl(xdecl, CEXTERN, f2, n);
                       break;

               case 3: // void f(T*,T) +=
                       dodecl(xdecl, CEXTERN, f3, n);
                       break;

               case 4: // T f(T)       ~
                       dodecl(xdecl, CEXTERN, f4, n);
                       break;
               }
       }
       for(i=0;; i++) {
               o = gtabinit[i].etype;
               if(o == TXXX)
                       break;

               /*
                * OCAST types T1 _T2_T1_(T2)
                */
               sprint(str, "_%s%s_", gtabinit[i].name, t->tag->name);
               n = new(ONAME, Z, Z);
               n->sym = slookup(str);
               f->castto[o] = n->sym;

               f1 = typ(TFUNC, t);
               f1->down = types[o];
               dodecl(xdecl, CEXTERN, f1, n);

               sprint(str, "%s_%s_", t->tag->name, gtabinit[i].name);
               n = new(ONAME, Z, Z);
               n->sym = slookup(str);
               f->castfr[o] = n->sym;

               f1 = typ(TFUNC, types[o]);
               f1->down = t;
               dodecl(xdecl, CEXTERN, f1, n);
       }
       return;
bad:
       diag(Z, "dclfunct bad %T %s\n", t, s->name);
}

Gtab    gtabinit[NTYPE] =
{
       TCHAR,          "c",
       TUCHAR,         "uc",
       TSHORT,         "h",
       TUSHORT,        "uh",
       TINT,           "i",
       TUINT,          "ui",
       TLONG,          "l",
       TULONG,         "ul",
       TVLONG,         "v",
       TUVLONG,        "uv",
       TFLOAT,         "f",
       TDOUBLE,        "d",
       TXXX
};

Ftab    ftabinit[OEND] =
{
       OADD,           "add",          1,
       OAND,           "and",          1,
       OASHL,          "ashl",         1,
       OASHR,          "ashr",         1,
       ODIV,           "div",          1,
       OLDIV,          "ldiv",         1,
       OLMOD,          "lmod",         1,
       OLMUL,          "lmul",         1,
       OLSHR,          "lshr",         1,
       OMOD,           "mod",          1,
       OMUL,           "mul",          1,
       OOR,            "or",           1,
       OSUB,           "sub",          1,
       OXOR,           "xor",          1,

       OEQ,            "eq",           2,
       OGE,            "ge",           2,
       OGT,            "gt",           2,
       OHI,            "hi",           2,
       OHS,            "hs",           2,
       OLE,            "le",           2,
       OLO,            "lo",           2,
       OLS,            "ls",           2,
       OLT,            "lt",           2,
       ONE,            "ne",           2,

       OASADD,         "asadd",        3,
       OASAND,         "asand",        3,
       OASASHL,        "asashl",       3,
       OASASHR,        "asashr",       3,
       OASDIV,         "asdiv",        3,
       OASLDIV,        "asldiv",       3,
       OASLMOD,        "aslmod",       3,
       OASLMUL,        "aslmul",       3,
       OASLSHR,        "aslshr",       3,
       OASMOD,         "asmod",        3,
       OASMUL,         "asmul",        3,
       OASOR,          "asor",         3,
       OASSUB,         "assub",        3,
       OASXOR,         "asxor",        3,

       OPOS,           "pos",          4,
       ONEG,           "neg",          4,
       OCOM,           "com",          4,
       ONOT,           "not",          4,

//      OPOSTDEC,
//      OPOSTINC,
//      OPREDEC,
//      OPREINC,

       OXXX,
};

//      Node*   nodtestv;

//      Node*   nodvpp;
//      Node*   nodppv;
//      Node*   nodvmm;
//      Node*   nodmmv;