/*
 * Awk code generator
 *
 * Copyright (C) 1988, 1989, 1990, 1991 by Rob Duff
 * All rights reserved
 */

#include <stdio.h>
#include <mem.h>
#include "awk.h"
#include "awklex.h"

short linenum = 0;                  /* line number for next opcode */
short lastptr = 0;                  /* index to previous opcode */
short codeptr = 0;                  /* index into code buffer */
short lastlabel = 0;                /* index into jump target list */
short nextlabel = 1;
LABLE labels[MAXLABEL];             /* table to hold jump targets */

TRIX trix;

void putline(void);
void genline(void);

int
getlabel()
{
    int     label;

    if (lastlabel >= MAXLABEL)
        yyerror("label overflow");
    label = lastlabel++;
    labels[label].where = 0;
    labels[label].label = nextlabel++;
#ifdef LDEBUG
    print_label("get", label);
#endif
    return(label);
}

void putlabel(int label)
{
#ifdef LDEBUG
    print_label("put", label);
#endif
    lastlabel--;
    if (labels[label].where < 0)
        yyerror("undefined label");
    if (label != lastlabel)
        yyerror("lost label");
}

void uselabel(int label, int value)
{
    int     i;

#ifdef LDEBUG
    print_label("use", label);
    print_label("for", value);
#endif
    i = labels[label].where;
    labels[label].where = labels[value].where;
    labels[value].where = i;
}

void genlabel(int label)
{
    int     i, loc, tmp;

    loc = -labels[label].where;
    labels[label].where = codeptr + 1;
    while (loc > 0) {
        for (i = 0; i < sizeof(short); i++)
            trix.sval[i] = code[loc+i];
        tmp = trix.ival;
        trix.ival = codeptr - (loc + sizeof(short));
        for (i = 0; i < sizeof(short); i++)
            code[loc+i] = trix.sval[i];
        loc = tmp;
    }
#ifdef LDEBUG
    printf("set %d(%d)\n", labels[label].label, labels[label].where);
#endif
    genline();
}

LINK*
genact(char *cp)
{
    LINK *lp;

    lp = yyalloc(sizeof(LINK));
    lp->cnext = NULL;
    lp->ccode = cp;
    return lp;
}

RULE*
genrule(char *start, char *stop)
{
    RULE *rp;

    rp = yyalloc(sizeof(RULE));
    rp->start = start;
    rp->stop = stop;
    rp->seen = 0;
    rp->flag = 0;
    rp->action = NULL;
    rp->next = NULL;
    return rp;
}

char*
gencode()
{
    char    *cp;

#ifdef CDEBUG
    print_code(code);
#endif
    cp = yyalloc(codeptr);
    memcpy(cp, code, codeptr);
    code[0] = C_END;
    codeptr = 0;
    lastptr = 0;
    lastlabel = 0;
    stackptr = stacktop;
    return cp;
}

void putline()
{
    int     i;

    if (awkline > 0 && linenum > 0) {
        code[codeptr++] = C_LINE;
        trix.sptr = yyname;
        for (i = 0; i < sizeof(char*); i++)
            code[codeptr++] = trix.sval[i];
        trix.ival = linenum;
        for (i = 0; i < sizeof(short); i++)
            code[codeptr++] = trix.sval[i];
        linenum = 0;
    }
}

void genline()
{
    linenum = yyline;
}

void gendrop(void)
{
    int     op;

    op = code[lastptr];
    if (op == C__PRE || op == C__POST || op == C__STORE || op == C__COPY)
        code[lastptr] = op + 1; 
    else {
        putline();
        lastptr = codeptr;
        code[codeptr++] = C_DROP;
    }
}

void genstore(int arg)
{
    int     op;

    putline();
    if (code[lastptr] == C_LOAD) {
        codeptr = lastptr;
        op = C__COPY;
    }
    else if (code[lastptr] == C_FETCH) {
        code[lastptr] = C_ADDR;
        op = C__COPY;
    }
    else if (code[lastptr] == C_PLUCK) {
        code[lastptr] = C_FIELD;
        op = C__COPY;
    }
    else
        op = C__STORE;
    lastptr = codeptr;
    code[codeptr++] = op;
    code[codeptr++] = arg;
}

void genbyte(int op)
{
    putline();
    lastptr = codeptr;
    code[codeptr++] = op;
}

void gentwo(int op, int arg)
{
    putline();
    lastptr = codeptr;
    code[codeptr++] = op;
    code[codeptr++] = arg;
}

void gendcon(double dcon)
{
    int     i;

    putline();
    lastptr = codeptr;
    code[codeptr++] = C_DCON;
    trix.dval = dcon;
    for (i = 0; i < sizeof(double); i++)
        code[codeptr++] = trix.sval[i];
}

void genicon(int icon)
{
    int     i;

    putline();
    lastptr = codeptr;
    if (icon <= 255)  {
        code[codeptr++] = C_CCON;
        trix.cval = (char)icon;
        for (i = 0; i < sizeof(char); i++)
            code[codeptr++] = trix.sval[i];
    }
    else {
        code[codeptr++] = C_ICON;
        trix.ival = (int)icon;
        for (i = 0; i < sizeof(short); i++)
            code[codeptr++] = trix.sval[i];
    }
}

void genlcon(long lcon)
{
    int     i;

    putline();
    lastptr = codeptr;
    code[codeptr++] = C_LCON;
    trix.lval = (long)lcon;
    for (i = 0; i < sizeof(long); i++)
        code[codeptr++] = trix.sval[i];
}

void genscon(char *scon)
{
    int     i;

    putline();
    lastptr = codeptr;
    code[codeptr++] = C_SCON;
    trix.sptr = scon;
    for (i = 0; i < sizeof(char*); i++)
        code[codeptr++] = trix.sval[i];
}

void genrcon(char *rcon)
{
    int     i;

    putline();
    lastptr = codeptr;
    code[codeptr++] = C_RCON;
    trix.vptr = rcon;
    for (i = 0; i < sizeof(void*); i++)
        code[codeptr++] = trix.sval[i];
}

void genfield(double field)
{
    int     i;

    if ( field < 0 || field >= MAXFIELD )
        yyerror("Field number out of range");
    putline();
    lastptr = codeptr;
    code[codeptr++] = C_FIELD;
    trix.vptr = fieldtab + (int)field;
    for (i = 0; i < sizeof(void*); i++)
        code[codeptr++] = trix.sval[i];
}

void genfcon(int fcon)
{
    int     i;

    putline();
    lastptr = codeptr;
    code[codeptr++] = C_FCON;
    trix.vptr = files + fcon;
    for (i = 0; i < sizeof(void*); i++)
        code[codeptr++] = trix.sval[i];
}

void genaddr(IDENT *var)
{
    int     i;
    LIST    *lp;

    putline();
    lastptr = codeptr;
    i = 0;
    for (lp = yydisplay; lp != NULL; lp = lp->lnext) {
        if (lp->litem == var) {
            code[codeptr++] = C_AUTO;
            trix.ival = i;
            for (i = 0; i < sizeof(int); i++)
                code[codeptr++] = trix.sval[i];
            return;
        }
        i++;
    }
    if (var->vitem == NULL) {
        var->vitem = nextvar++;
        code[codeptr++] = C_ADDR;
    }
    else if ((ITEM*)(var->vitem) - vartab < builtin)
        code[codeptr++] = C_BUILT;
    else
        code[codeptr++] = C_ADDR;
    trix.vptr = var->vitem;
    for (i = 0; i < sizeof(void*); i++)
        code[codeptr++] = trix.sval[i];
}

void gencall(int func, int args)
{
    putline();
    lastptr = codeptr;
    code[codeptr++] = C_CALL;
    code[codeptr++] = func;
    code[codeptr++] = args;
}

void genuser(IDENT *func, int args)
{
    int     i;

    putline();
    lastptr = codeptr;
    code[codeptr++] = C_USER;
    trix.vptr = func;
    for (i = 0; i < sizeof(void*); i++)
        code[codeptr++] = trix.sval[i];
    code[codeptr++] = args;
}

void genjump(int kind, int label)
{
    int     i, loc;

    putline();
    lastptr = codeptr;
    code[codeptr++] = kind;
    loc = labels[label].where;
    if (loc <= 0) {
        trix.ival = -loc;
        labels[label].where = -codeptr;
    }
    else
        trix.ival = loc - (codeptr + sizeof(short) + 1);
    for (i = 0; i < sizeof(short); i++)
        code[codeptr++] = trix.sval[i];
}

int
lastcode()
{
    return code[lastptr];
}

double
lastdcon()
{
    codeptr = lastptr;
    return *(double*)(&code[lastptr+1]);
}

void
lastvoid()
{
    codeptr = lastptr;
}

void
lastop(int op)
{
    code[lastptr] = op;
}
