#include <stdio.h>
#include <stdlib.h>
#include "adltypes.h"
#include "adlprog.h"
#include "builtins.h"
#include "adldef.h"
#include "adlcomp.h"

/*
 * Grammar for new-style ADL procedures:
 *
 * proc         : 'Proc' '(' optnames ')' outerblock
 *              ;
 *
 * optnames     : // NULL
 *              | names
 *              ;
 *
 * names        : IDENT
 *              | IDENT ',' names
 *              ;
 *
 * outerblock   : '{' locals stmts '}'
 *              ;
 *
 * block        : '{' stmts '}'
 *              ;
 *
 * locals       : // NULL
 *              | 'Var' vars ';' locals
 *              ;
 *
 * vars         : var
 *              | var ',' vars
 *              ;
 *
 * var          : IDENT
 *              | IDENT '[' NUMBER ']'
 *              ;
 *
 * stmts        : // NULL
 *              | stmt stmts
 *              ;
 *
 * stmt         : callassgn ';'
 *              | return ';'
 *              | 'Break' ';'
 *              | ifstmt
 *              | whilestmt
 *              | block
 *              ;
 *
 * callassgn    : expr '=' expr
 *              | expr
 *              | STRING exprs  // Implicit $say
 *              ;
 *
 * exprs        : // NULL
 *              | expr exprs
 *              ;
 *
 * return       : 'Return' expr
 *              | 'Return'
 *              ;
 *
 * ifstmt       : 'If' '(' expr ')' stmt
 *              | 'If' '(' expr ')' stmt 'Else' stmt
 *              ;
 *
 * whilestmt    : 'While' '(' expr ')' stmt
 *              ;
 *
 * expr         : expr '||' logandexp
 *              | logandexp
 *              ;
 *
 * logandexp    : logandexp '&&' orexp
 *              | orexp
 *              ;
 *
 * orexp        : orexp '|' xorexp
 *              | xorexp
 *              ;
 *
 * xorexp       : xorexp '^' andexp
 *              | andexp
 *              ;
 *
 * andexp       : andexp '&' eqexp
 *              | eqexp
 *              ;
 *
 * eqexp        : eqexp '==' cmpexp
 *              | eqexp '!=' cmpexp
 *              | eqexp '?=' cmpexp
 *              | cmpexp
 *              ;
 *
 * cmpexp       : cmpexp '<' addexp
 *              | cmpexp '<=' addexp
 *              | cmpexp '>=' addexp
 *              | cmpexp '>' addexp
 *              | addexp
 *              ;
 *
 * addexp       : addexp '+' mulexp
 *              | addexp '-' mulexp
 *              | mulexp
 *              ;
 *
 * mulexp       : mulexp '*' unary
 *              | mulexp '/' unary
 *              | mulexp '%' unary
 *              | unary
 *              ;
 *
 * unary        : '-' dotexp
 *              | '~' dotexp
 *              | '!' dotexp
 *              | '*' dotexp
 *              | '&' dotexp
 *              | dotexp
 *              ;
 *
 * dotexp       : dotexp '.' term
 *              | dotexp '[' expr ']'
 *              | dotexp '(' funargs ')'
 *              | term
 *              ;
 *
 * funargs      : // NULL
 *              | exprlist
 *              ;
 *
 * exprlist     : expr
 *              | exprlist ',' expr
 *              ;
 *
 *
 * term         : NUMBER
 *              | STRING
 *              | IDENT
 *              | IDENT IDENT
 *              | '(' expr ')'
 *              ;
 */

static void getblock(int outer);
static void getstmt(void);
static void getblock(int outer);

static Expr allocExpr(int numArgs);
static Expr mkbinary(int func, Expr lhs, Expr rhs);
static Expr mkunary(int func, Expr arg);
static Expr mkaddr(Expr arg);
static void putexpr(Expr expr, Expr rhs);

static Expr expr(void);
static Expr logandexp(void);
static Expr orexp(void);
static Expr xorexp(void);
static Expr andexp(void);
static Expr eqexp(void);
static Expr cmpexp(void);
static Expr addexp(void);
static Expr mulexp(void);
static Expr unary(void);
static Expr dotexp(void);
static Expr term(void);

/* proc         : 'proc' '(' optnames ')' outerblock
 *              ;
 */

void getproc(void)
{
    int16 num_args = 0;
    extern int16 inrout;

    inproc++;

    lexer();
    if( t_type != LPAR ) {
        error( LEFT_EXPECTED );
    }
    do {
        lexer();
        if( t_type == UNDECLARED ) {
            insert_local( token, ARGUMENT, num_args+1 );
            num_args++;
            lexer();
        }
        else if( t_type != RPAR ) {
            if( t_type < 256 ) {
                inproc--;
                _ERR_FIX( ILLEGAL_SYMBOL, RPAR );
            }
            else {
                error( REDECLARATION );
                lexer();
            }
        }
    } while( t_type == ',' );
    if( t_type != RPAR ) {
        inproc--;
        _ERR_FIX( RIGHT_EXPECTED, RPAR );
    }
    lexer();
    if( t_type != LBRACE ) {
        inproc--;
        _ERR_FIX( "'{' expected.\n", RBRACE );
    }

    inrout = 1;
    emit_file();

    getblock(1);
    newcode( PUSH, 0L );        /* Don't underflow... */
    newcode( RET, 0L );
    del_locals();

    inrout = 0;
    inproc--;
}

/* outerblock   : '{' locals stmts '}'
 *              ;
 *
 * block        : '{' stmts '}'
 *              ;
 *
 * locals       : // NULL
 *              | 'var' vars ';' locals
 *              ;
 *
 * vars         : var
 *              | var ',' vars
 *              ;
 *
 * var          : IDENT
 *              | IDENT '[' NUMBER ']'
 *              ;
 *
 * stmts        : // NULL
 *              | stmt stmts
 *              ;
 */
static void getblock(int outer)
{
    int16 num_locals = 0;
    int16 n;
    int16 done;

    if( outer ) {
        lexer();
        while( t_type == VAR_D ) {
            do {
                lexer();
                if( t_type == UNDECLARED ) {
                    insert_local( token, LOCAL, num_locals );
                }
                else if( t_type >= 256 ) {
                    error( REDECLARATION );
                }
                else if( t_type != ';' ) {
                    error( SEMI_EXPECTED );
                }
                lexer();
                if( t_type == LBRAK ) {
                    /* Array */
                    lexer();
                    if( t_type == CONST ) {
                        n = t_val;
                    }
                    else {
                        error( CONST_EXPECTED );
                    }
                    lexer();
                    if( t_type != RBRAK ) {
                        _ERR_FIX( BRACKET_EXPECTED, ',' );
                    }
                    lexer();
                }
                else {
                    n = 1;
                }
                num_locals += n;
                while( n-- ) {
                    /* Allocate var space on stack */
                    newcode( PUSH, 0L );
                }
            } while( t_type == ',' );
            if( t_type != ';' ) {
                _ERR_FIX( SEMI_EXPECTED, ';' );
            }
            lexer();
        }
        unlex();
    }
    done = 0;
    do {
	lexer();
        if( t_type == RBRACE ) {
            done = 1;
        }
        else if( t_type == EOF ) {
            error( "Unexpected EOF\n" );
            done = 1;
        }
        else {
            unlex();
            getstmt();
        }
    } while( !done );
}

/* stmt         : block
 *              | ifstmt
 *              | whilestmt
 *              | 'Break' ';'
 *              | return ';'
 *              | callassgn ';'
 *              ;
 *
 * callassgn    : STRING exprs  // Implicit $say
 *              | expr '=' expr
 *              | expr
 *              ;
 *
 * exprs        : // NULL
 *              | expr exprs
 *              ;
 *
 * return       : 'Return' expr
 *              | 'Return'
 *              ;
 *
 * ifstmt       : 'If' '(' expr ')' stmt
 *              | 'If' '(' expr ')' stmt 'Else' stmt
 *              ;
 *
 * whilestmt    : 'While' '(' expr ')' stmt
 *              ;
 */

void getstmt(void)
{
    address oldaddr;
    address breakaddr;
    address head;
    Expr lhs;
    int16 nargs;

    lexer();
    switch( t_type ) {
    case LBRACE :
        getblock(0);
        break;
    case IF :
        lexer();
        if( t_type != LPAR ) {
            _ERR_FIX(LEFT_EXPECTED, ';');
        }
        putexpr(expr(), (Expr)NULL);
        lexer();
        if( t_type != RPAR ) {
            _ERR_FIX(RIGHT_EXPECTED, ';');
        }
        oldaddr = newcode( JMPZ, 0 );
        newcode( POP, 0 );
        getstmt();
        lexer();
        if( t_type == ELSE ) {
            /* Need to jump over body of else */
            breakaddr = newcode( JMP, 0 );
            /* Fix up first jump */
            oldcode( oldaddr, JMPZ, currcode() );
            newcode( POP, 0 ); // Need to pop the condition code result
            getstmt();
            /* Fix up second jump */
            oldcode( breakaddr, JMP, currcode() );
        }
        else {
            unlex();
            /* Fix up first jump */
            oldcode( oldaddr, JMPZ, currcode() );
        }
        break;
    case WHILE :
        lexer();
        if( t_type != LPAR ) {
            _ERR_FIX(LEFT_EXPECTED, ';');
        }
        head = currcode();
        putexpr(expr(), (Expr)NULL);
        lexer();
        if( t_type != RPAR ) {
            _ERR_FIX(RIGHT_EXPECTED, ';');
        }
        oldaddr = newcode( JMPZ, 0 );
        newcode( POP, 0 );
        getstmt();
        newcode( JMP, head );
        oldcode( oldaddr, JMPZ, currcode() );
        newcode( POP, 0 );
        break;
    case BREAK :
        /* TBD: codegen */
        lexer();
        if( t_type != ';' ) {
            _ERR_FIX(SEMI_EXPECTED, ';');
        }
        break;
    case RETURN :
        lexer();
        newcode( PUSH, _RETURN );
        if( t_type == ';' ) {
            newcode( PUSH, 0 );
        }
        else {
            unlex();
            putexpr(expr(), (Expr)NULL);
            lexer();
            if( t_type != ';' ) {
                _ERR_FIX(SEMI_EXPECTED, ';');
            }
        }
        newcode( CALL, (int16)2 );
        break;
    case STRING :
        /* Implicit $say */
        unlex();
        do {
            lhs = expr();
            newcode( PUSH, _SAY );
            putexpr(lhs, (Expr)NULL);
            newcode( CALL, (int16)2 );
            newcode( POP, 0L );
            lexer();
            if (t_type != ';') {
                unlex();
            }
        } while (t_type != ';');
        break;
    default :
        unlex();
        lhs = expr();
        lexer();
	if( t_type == '=' ) {
	    /* Assignment */
            putexpr(lhs, expr());
            lexer();
	}
        else {
            putexpr(lhs, NULL);       /* TBD - check for validity */
        }
        newcode( POP, 0L );     /* Don't forget to keep the stack clean */
        if( t_type != ';' ) {
            error(SEMI_EXPECTED);
            eatuntil(';');
        }
	break;
    }
}

/* expr         : expr '||' logandexp
 *              | logandexp
 *              ;
 */
static Expr expr(void)
{
    Expr result;
    int done = 0;

    result = logandexp();
    do {
	lexer();
	switch( t_type ) {
	case OROR :
	    result = mkbinary( OROR, result, logandexp() );
	    break;
	default :
	    done = 1;
	    break;
	}
    } while( !done );
    unlex();
    return result;
}

/* logandexp    : logandexp '&&' orexp
 *              | orexp
 *              ;
 */
static Expr logandexp(void)
{
    Expr result;
    int done = 0;

    result = orexp();
    do {
	lexer();
	switch( t_type ) {
	case ANDAND :
	    result = mkbinary( ANDAND, result, orexp() );
	    break;
	default :
	    done = 1;
	    break;
	}
    } while( !done );
    unlex();
    return result;
}

/* orexp        : orexp '|' xorexp
 *              | xorexp
 *              ;
 */
static Expr orexp(void)
{
    Expr result;
    int done = 0;

    result = xorexp();
    do {
	lexer();
	switch( t_type ) {
	case '|' :
	    result = mkbinary( _OR, result, xorexp() );
	    break;
	default :
	    done = 1;
	    break;
	}
    } while( !done );
    unlex();
    return result;
}

/* xorexp       : xorexp '^' andexp
 *              | andexp
 *              ;
 */
static Expr xorexp(void)
{
    Expr result;
    int done = 0;

    result = andexp();
    do {
	lexer();
	switch( t_type ) {
	case '^' :
	    result = mkbinary( _OR, result, andexp() );
	    break;
	default :
	    done = 1;
	    break;
	}
    } while( !done );
    unlex();
    return result;
}

/* andexp       : andexp '&' eqexp
 *              | eqexp
 *              ;
 */
static Expr andexp(void)
{
    Expr result;
    int done = 0;

    result = eqexp();
    do {
	lexer();
	switch( t_type ) {
	case '&' :
	    result = mkbinary( _AND, result, eqexp() );
	    break;
	default :
	    done = 1;
	    break;
	}
    } while( !done );
    unlex();
    return result;
}

/* eqexp        : eqexp '==' cmpexp
 *              | eqexp '!=' cmpexp
 *              | eqexp '?=' cmpexp
 *              | cmpexp
 *              ;
 */
static Expr eqexp(void)
{
    Expr result;
    int done = 0;

    result = cmpexp();
    do {
	lexer();
	switch( t_type ) {
	case EQEQ :
	    result = mkbinary( _EQ, result, cmpexp() );
	    break;
	case NEQ :
	    result = mkbinary( _NE, result, cmpexp() );
	    break;
	case QEQ :
	    result = mkbinary( _EQST, result, cmpexp() );
	    break;
	default :
	    done = 1;
	    break;
	}
    } while( !done );
    unlex();
    return result;
}

/* cmpexp       : cmpexp '<' addexp
 *              | cmpexp '<=' addexp
 *              | cmpexp '>=' addexp
 *              | cmpexp '>' addexp
 *              | addexp
 *              ;
 */
static Expr cmpexp(void)
{
    Expr result;
    int done = 0;

    result = addexp();
    do {
	lexer();
	switch( t_type ) {
	case '<' :
	    result = mkbinary( _LT, result, addexp() );
	    break;
	case LEQ :
	    result = mkbinary( _LE, result, addexp() );
	    break;
	case GEQ :
	    result = mkbinary( _GE, result, addexp() );
	    break;
	case '>' :
	    result = mkbinary( _GT, result, addexp() );
	    break;
	default :
	    done = 1;
	    break;
	}
    } while( !done );
    unlex();
    return result;
}

/* addexp       : addexp '+' mulexp
 *              | addexp '-' mulexp
 *              | mulexp
 *              ;
 */
static Expr addexp(void)
{
    Expr result;
    int done = 0;

    result = mulexp();
    do {
	lexer();
	switch( t_type ) {
	case '+' :
	    result = mkbinary( _PLUS, result, mulexp() );
	    break;
	case '-' :
	    result = mkbinary( _MINUS, result, mulexp() );
	    break;
	default :
	    done = 1;
	    break;
	}
    } while( !done );
    unlex();
    return result;
}

/* mulexp       : mulexp '*' unary
 *              | mulexp '/' unary
 *              | mulexp '%' unary
 *              | unary
 *              ;
 */
static Expr mulexp(void)
{
    Expr result;
    int done = 0;

    result = unary();
    do {
	lexer();
	switch( t_type ) {
	case '*' :
	    result = mkbinary( _TIMES, result, unary() );
	    break;
	case '/' :
	    result = mkbinary( _DIV, result, unary() );
	    break;
	case '%' :
	    result = mkbinary( _MOD, result, unary() );
	    break;
	default :
	    done = 1;
	    break;
	}
    } while( !done );
    unlex();
    return result;
}

/* unary        : '-' dotexp
 *              | '~' dotexp
 *              | '!' dotexp
 *              | '*' dotexp
 *              | '&' dotexp
 *              | dotexp
 *              ;
 */
static Expr unary(void)
{
    Expr result, rhs;

    lexer();
    switch( t_type ) {
    case '-' :
        result = mkbinary(_MINUS, allocExpr(0), dotexp());
        break;
    case '~' :
        result = mkunary(_NOT, dotexp());
        break;
    case '!' :
        result = mkunary(_NOT, dotexp()); /* TBD */
        break;
    case '*' :
        result = allocExpr(1);
        result->exprType = EXP_DEREF;
        result->exprInfo = 1;
        result->exprArgs[0] = dotexp();
        break;
    case '&' :
        result = mkaddr(dotexp());
        break;
    default :
        unlex();
        result = dotexp();
    }
    return result;
}

/* dotexp       : dotexp '.' term
 *              | dotexp '[' expr ']'
 *              | dotexp '(' funargs ')'
 *              | term
 *              ;
 *
 * funargs      : // NULL
 *              | exprlist
 *              ;
 *
 * exprlist     : expr
 *              | exprlist ',' expr
 *              ;
 *
 */
static Expr dotexp(void)
{
    Expr result, lhs;
    Expr arglist[256];
    int done = 0;
    int i, nargs;

    result = term();
    do {
	lexer();
	switch( t_type ) {
	case '.' :
            lhs = result;
	    result = allocExpr(2);
	    result->exprType = EXP_DOT;
	    result->exprInfo = 2;
	    result->exprArgs[0] = lhs;
	    result->exprArgs[1] = term();
	    break;
	case '[' :
            lhs = result;
	    result = allocExpr(2);
	    result->exprType = EXP_INDEX;
	    result->exprInfo = 2;
	    result->exprArgs[0] = mkaddr(lhs);
	    result->exprArgs[1] = expr();
            lexer();
            if( t_type != ']' ) {
		error( "']' expected" );
            }
	    break;
        case LPAR :
            nargs = 0;
            arglist[nargs++] = result;
            lexer();
            if( t_type != RPAR ) {
                unlex();
                do {
                    arglist[nargs++] = expr();
                    lexer();
                } while( t_type == ',' );
            }
            if( t_type != RPAR ) {
                error(RIGHT_EXPECTED);
            }
            result = allocExpr(nargs);
            result->exprType = EXP_CALL;
            result->exprInfo = nargs;
            for(i = 0; i < nargs; i++) {
                result->exprArgs[i] = arglist[i];
            }
            break;
	default :
	    done = 1;
	    break;
	}
    } while( !done );
    unlex();
    return result;
}

/* term         : ARGUMENT
 *              | LOCAL
 *              | VAR
 *              | MYVAL
 *              | NOUN
 *              | STRING
 *              | NUMBER
 *              | '<' modif NOUN '>'
 *              | '(' expr ')'
 *              ;
 */
static Expr term(void)
{
    Expr result = 0;

    lexer();
    switch( t_type ) {
    case ARGUMENT :
        result = allocExpr(0);
	result->exprType = EXP_ARG;
	result->exprInfo = t_val;
	break;
    case LOCAL :
        result = allocExpr(0);
	result->exprType = EXP_LOCAL;
	result->exprInfo = t_val;
	break;
    case VAR :
        result = allocExpr(0);
	result->exprType = EXP_VAR;
	result->exprInfo = t_val;
	break;
    case MYVAL :
        result = allocExpr(0);
	result->exprType = EXP_ME;
	result->exprInfo = 0;
	break;
    case NOUN :
        result = allocExpr(0);
	t_val = noun_exists( 0, t_val );
	if( t_val < 0 ) {
	    error(ATTEMPT);
	}
	result->exprInfo = t_val;
	break;
    case '<' :
        result = allocExpr(0);
	result->exprInfo = getold(0, 0);

	lexer();
	if( t_type != '>' ) {
	    error( "'>' expected");
	}
        break;
    case LPAR :
        result = expr();
        lexer();
        if( t_type != RPAR ) {
            error( RIGHT_EXPECTED );
        }
        break;
    case UNDECLARED :
	error( "Undeclared identifier\n" );
	break;
    default :
	if( (t_type >= MIN_LEGAL) && (t_type <= MAX_LEGAL) ) {
            result = allocExpr(0);
	    result->exprInfo = t_val;
	}
	else {
	    error( ILLEGAL_SYMBOL );
	}
	break;
    }

    return result;
}

static Expr allocExpr(int numArgs)
{
    Expr result;
    result = malloc(sizeof(struct _Expr) + (numArgs-1)*sizeof(struct _Expr *));
    if( !result ) {
        /* TBD: Out of memory */
    }
    result->exprType = EXP_CONST;
    result->exprInfo = 0;
    return result;
}

static Expr mkbinary(int func, Expr lhs, Expr rhs)
{
    Expr efunc, result;

    efunc = allocExpr(0);
    efunc->exprInfo = func;

    result = allocExpr(3);
    result->exprType = EXP_CALL;
    result->exprInfo = 3;
    result->exprArgs[0] = efunc;
    result->exprArgs[1] = lhs;
    result->exprArgs[2] = rhs;

    return result;
}

static Expr mkunary(int func, Expr arg)
{
    Expr efunc, result;

    efunc = allocExpr(0);
    efunc->exprInfo = func;

    result = allocExpr(2);
    result->exprType = EXP_CALL;
    result->exprInfo = 2;
    result->exprArgs[0] = efunc;
    result->exprArgs[1] = arg;

    return result;
}

static Expr mkaddr(Expr arg)
{
    Expr result;

    result = allocExpr(1);
    result->exprType = EXP_ADDR;
    result->exprInfo = 1;
    result->exprArgs[0] = arg;
    return result;
}

static void putexpr(Expr expr, Expr rhs)
{
    int i;

    if( !expr ) return;

    switch( expr->exprType ) {
    case EXP_CONST :
        if( rhs ) {
            error("Illegal assignment");
        }
        newcode( PUSH, expr->exprInfo );
        break;
    case EXP_CALL :
        if( rhs ) {
            error("Illegal assignment");
        }
        for(i = 0; i < expr->exprInfo; i++) {
            putexpr(expr->exprArgs[i], (Expr)NULL);
        }
        newcode( CALL, expr->exprInfo );
        break;
    case EXP_DOT :
        if( rhs ) {
            newcode( PUSH, _SETP );
            putexpr(expr->exprArgs[0], (Expr)NULL);
            putexpr(expr->exprArgs[1], (Expr)NULL);
            putexpr(rhs, (Expr)NULL);
            newcode( CALL, 4 );
        }
        else {
            newcode( PUSH, _PROP );
            putexpr(expr->exprArgs[0], (Expr)NULL);
            putexpr(expr->exprArgs[1], (Expr)NULL);
            newcode( CALL, 3 );
        }
        break;
    case EXP_INDEX :
        if( rhs ) {
            newcode( PUSH, _SETG );
        }
        else {
            newcode( PUSH, _GLOBAL );
        }
        newcode( PUSH, _PLUS );
        putexpr(expr->exprArgs[0], (Expr)NULL);
        putexpr(expr->exprArgs[1], (Expr)NULL);
        newcode(CALL, 3);
        if( rhs ) {
            putexpr(rhs, (Expr)NULL);
            newcode(CALL, 3);
        }
        else {
            newcode(CALL, 2);
        }
        break;
    case EXP_VAR :
        if( rhs ) {
            newcode( PUSH, _SETG );
            newcode( PUSH, expr->exprInfo );
            putexpr(rhs, (Expr)NULL);
            newcode( CALL, 3 );
        }
        else {
            newcode( PUSH, _GLOBAL );
            newcode( PUSH, expr->exprInfo );
            newcode( CALL, 2 );
        }
        break;
    case EXP_ARG :
        if( rhs ) {
            error("Illegal assignment");
        }
        newcode( PUSHARG, expr->exprInfo );
        break;
    case EXP_LOCAL :
        if( rhs ) {
            newcode( PUSH, _SETG );
            newcode( PUSHLOCL, expr->exprInfo );
            putexpr(rhs, (Expr)NULL);
            newcode( CALL, 3 );
        }
        else {
            newcode( PUSH, _GLOBAL );
            newcode( PUSHLOCL, expr->exprInfo );
            newcode( CALL, 2 );
        }
        break;
    case EXP_ME :
        if( rhs ) {
            error("Illegal assignment");
        }
        newcode( PUSHME, 0 );
        break;
    case EXP_ADDR :
        if( rhs ) {
            error("Illegal assignment");
        }
        rhs = expr->exprArgs[0];
        switch( rhs->exprType ) {
        case EXP_DEREF :
            putexpr(rhs->exprArgs[0], (Expr)NULL);
            break;
        case EXP_LOCAL :
            newcode( PUSHLOCL, rhs->exprInfo );
            break;
        case EXP_VAR :
            newcode( PUSH, rhs->exprInfo );
            break;
        case EXP_INDEX :
            newcode( PUSH, _PLUS );
            putexpr(rhs->exprArgs[0], (Expr)NULL);
            putexpr(rhs->exprArgs[1], (Expr)NULL);
            newcode(CALL, 3);
            break;
        default :
            error("Illegal address operator\n");
            break;
        }
        free(rhs);
        break;
    case EXP_DEREF :
        if( rhs ) {
            newcode( PUSH, _SETG );
            putexpr(expr->exprArgs[0], (Expr)NULL);
            putexpr(rhs, (Expr)NULL);
            newcode( CALL, 3 );
        }
        else {
            newcode( PUSH, _GLOBAL );
            putexpr(expr->exprArgs[0], (Expr)NULL);
        }
        break;
    }
    free(expr);
    /*if( rhs ) {
        free(rhs);
    }*/
}
