/* expr.c
**
** 1998-06-21, Ullrich von Bassewitz
** 2020-01-25, Greg King
*/



#include <stdio.h>
#include <stdlib.h>

/* common */
#include "check.h"
#include "debugflag.h"
#include "xmalloc.h"

/* cc65 */
#include "asmcode.h"
#include "asmlabel.h"
#include "asmstmt.h"
#include "assignment.h"
#include "codegen.h"
#include "declare.h"
#include "error.h"
#include "funcdesc.h"
#include "function.h"
#include "global.h"
#include "litpool.h"
#include "loadexpr.h"
#include "macrotab.h"
#include "preproc.h"
#include "scanner.h"
#include "shiftexpr.h"
#include "stackptr.h"
#include "standard.h"
#include "stdfunc.h"
#include "symtab.h"
#include "typecmp.h"
#include "typeconv.h"
#include "expr.h"



/*****************************************************************************/
/*                                   Data                                    */
/*****************************************************************************/



/* Generator attributes */
#define GEN_NOPUSH      0x01            /* Don't push lhs */
#define GEN_COMM        0x02            /* Operator is commutative */
#define GEN_NOFUNC      0x04            /* Not allowed for function pointers */

/* Map a generator function and its attributes to a token */
typedef struct {
    token_t       Tok;                  /* Token to map to */
    unsigned      Flags;                /* Flags for generator function */
    void          (*Func) (unsigned, unsigned long);    /* Generator func */
} GenDesc;

/* Descriptors for the operations */
static GenDesc GenPASGN  = { TOK_PLUS_ASSIGN,   GEN_NOPUSH,     g_add };
static GenDesc GenSASGN  = { TOK_MINUS_ASSIGN,  GEN_NOPUSH,     g_sub };
static GenDesc GenMASGN  = { TOK_MUL_ASSIGN,    GEN_NOPUSH,     g_mul };
static GenDesc GenDASGN  = { TOK_DIV_ASSIGN,    GEN_NOPUSH,     g_div };
static GenDesc GenMOASGN = { TOK_MOD_ASSIGN,    GEN_NOPUSH,     g_mod };
static GenDesc GenSLASGN = { TOK_SHL_ASSIGN,    GEN_NOPUSH,     g_asl };
static GenDesc GenSRASGN = { TOK_SHR_ASSIGN,    GEN_NOPUSH,     g_asr };
static GenDesc GenAASGN  = { TOK_AND_ASSIGN,    GEN_NOPUSH,     g_and };
static GenDesc GenXOASGN = { TOK_XOR_ASSIGN,    GEN_NOPUSH,     g_xor };
static GenDesc GenOASGN  = { TOK_OR_ASSIGN,     GEN_NOPUSH,     g_or  };



/*****************************************************************************/
/*                             Helper functions                              */
/*****************************************************************************/



static unsigned GlobalModeFlags (const ExprDesc* Expr)
/* Return the addressing mode flags for the given expression */
{
    switch (ED_GetLoc (Expr)) {
        case E_LOC_ABS:         return CF_ABSOLUTE;
        case E_LOC_GLOBAL:      return CF_EXTERNAL;
        case E_LOC_STATIC:      return CF_STATIC;
        case E_LOC_REGISTER:    return CF_REGVAR;
        case E_LOC_STACK:       return CF_NONE;
        case E_LOC_PRIMARY:     return CF_NONE;
        case E_LOC_EXPR:        return CF_NONE;
        case E_LOC_LITERAL:     return CF_STATIC;       /* Same as static */
        default:
            Internal ("GlobalModeFlags: Invalid location flags value: 0x%04X", Expr->Flags);
            /* NOTREACHED */
            return 0;
    }
}



void ExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr)
/* Call an expression function with checks. */
{
    /* Remember the stack pointer */
    int OldSP = StackPtr;

    /* Call the expression function */
    (*Func) (Expr);

    /* Do some checks to see if code generation is still consistent */
    if (StackPtr != OldSP) {
        if (Debug) {
            Error ("Code generation messed up: "
                   "StackPtr is %d, should be %d",
                   StackPtr, OldSP);
        } else {
            Internal ("Code generation messed up: "
                      "StackPtr is %d, should be %d",
                      StackPtr, OldSP);
        }
    }
}



void MarkedExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr)
/* Call an expression function with checks and record start and end of the
** generated code.
*/
{
    CodeMark Start, End;
    GetCodePos (&Start);
    ExprWithCheck (Func, Expr);
    GetCodePos (&End);
    ED_SetCodeRange (Expr, &Start, &End);
}



static Type* promoteint (Type* lhst, Type* rhst)
/* In an expression with two ints, return the type of the result */
{
    /* Rules for integer types:
    **   - If one of the values is a long, the result is long.
    **   - If one of the values is unsigned, the result is also unsigned.
    **   - Otherwise the result is an int.
    */
    if (IsTypeLong (lhst) || IsTypeLong (rhst)) {
        if (IsSignUnsigned (lhst) || IsSignUnsigned (rhst)) {
            return type_ulong;
        } else {
            return type_long;
        }
    } else {
        if (IsSignUnsigned (lhst) || IsSignUnsigned (rhst)) {
            return type_uint;
        } else {
            return type_int;
        }
    }
}



static unsigned typeadjust (ExprDesc* lhs, ExprDesc* rhs, int NoPush)
/* Adjust the two values for a binary operation. lhs is expected on stack or
** to be constant, rhs is expected to be in the primary register or constant.
** The function will put the type of the result into lhs and return the
** code generator flags for the operation.
** If NoPush is given, it is assumed that the operation does not expect the lhs
** to be on stack, and that lhs is in a register instead.
** Beware: The function does only accept int types.
*/
{
    unsigned ltype, rtype;
    unsigned flags;

    /* Get the type strings */
    Type* lhst = lhs->Type;
    Type* rhst = rhs->Type;

    /* Generate type adjustment code if needed */
    ltype = TypeOf (lhst);
    if (ED_IsLocAbs (lhs)) {
        ltype |= CF_CONST;
    }
    if (NoPush) {
        /* Value is in primary register*/
        ltype |= CF_REG;
    }
    rtype = TypeOf (rhst);
    if (ED_IsLocAbs (rhs)) {
        rtype |= CF_CONST;
    }
    flags = g_typeadjust (ltype, rtype);

    /* Set the type of the result */
    lhs->Type = promoteint (lhst, rhst);

    /* Return the code generator flags */
    return flags;
}



static const GenDesc* FindGen (token_t Tok, const GenDesc* Table)
/* Find a token in a generator table */
{
    while (Table->Tok != TOK_INVALID) {
        if (Table->Tok == Tok) {
            return Table;
        }
        ++Table;
    }
    return 0;
}



static int TypeSpecAhead (void)
/* Return true if some sort of type is waiting (helper for cast and sizeof()
** in hie10).
*/
{
    SymEntry* Entry;

    /* There's a type waiting if:
    **
    ** We have an opening paren, and
    **   a.  the next token is a type, or
    **   b.  the next token is a type qualifier, or
    **   c.  the next token is a typedef'd type
    */
    return CurTok.Tok == TOK_LPAREN && (
           TokIsType (&NextTok)                         ||
           TokIsTypeQual (&NextTok)                     ||
           (NextTok.Tok  == TOK_IDENT                   &&
           (Entry = FindSym (NextTok.Ident)) != 0       &&
           SymIsTypeDef (Entry)));
}



void PushAddr (const ExprDesc* Expr)
/* If the expression contains an address that was somehow evaluated,
** push this address on the stack. This is a helper function for all
** sorts of implicit or explicit assignment functions where the lvalue
** must be saved if it's not constant, before evaluating the rhs.
*/
{
    /* Get the address on stack if needed */
    if (ED_IsLocExpr (Expr)) {
        /* Push the address (always a pointer) */
        g_push (CF_PTR, 0);
    }
}



static void WarnConstCompareResult (void)
/* If the result of a comparison is constant, this is suspicious when not in
** preprocessor mode.
*/
{
    if (!Preprocessing && IS_Get (&WarnConstComparison) != 0) {
        Warning ("Result of comparison is constant");
    }
}



/*****************************************************************************/
/*                                   code                                    */
/*****************************************************************************/



static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall)
/* Parse a function parameter list, and pass the parameters to the called
** function. Depending on several criteria, this may be done by just pushing
** each parameter separately, or creating the parameter frame once, and then
** storing into this frame.
** The function returns the size of the parameters pushed.
*/
{
    ExprDesc Expr;

    /* Initialize variables */
    SymEntry* Param       = 0;  /* Keep gcc silent */
    unsigned  ParamSize   = 0;  /* Size of parameters pushed */
    unsigned  ParamCount  = 0;  /* Number of parameters pushed */
    unsigned  FrameSize   = 0;  /* Size of parameter frame */
    unsigned  FrameParams = 0;  /* Number of params in frame */
    int       FrameOffs   = 0;  /* Offset into parameter frame */
    int       Ellipsis    = 0;  /* Function is variadic */

    /* As an optimization, we may allocate the complete parameter frame at
    ** once instead of pushing each parameter as it comes. We may do that,
    ** if...
    **
    **  - optimizations that increase code size are enabled (allocating the
    **    stack frame at once gives usually larger code).
    **  - we have more than one parameter to push (don't count the last param
    **    for __fastcall__ functions).
    **
    ** The FrameSize variable will contain a value > 0 if storing into a frame
    ** (instead of pushing) is enabled.
    **
    */
    if (IS_Get (&CodeSizeFactor) >= 200) {

        /* Calculate the number and size of the parameters */
        FrameParams = Func->ParamCount;
        FrameSize   = Func->ParamSize;
        if (FrameParams > 0 && IsFastcall) {
            /* Last parameter is not pushed */
            FrameSize -= CheckedSizeOf (Func->LastParam->Type);
            --FrameParams;
        }

        /* Do we have more than one parameter in the frame? */
        if (FrameParams > 1) {
            /* Okeydokey, set up the frame */
            FrameOffs = StackPtr;
            g_space (FrameSize);
            StackPtr -= FrameSize;
        } else {
            /* Don't use a preallocated frame */
            FrameSize = 0;
        }
    }

    /* Parse the actual parameter list */
    while (CurTok.Tok != TOK_RPAREN) {

        unsigned Flags;

        /* Count arguments */
        ++ParamCount;

        /* Fetch the pointer to the next argument, check for too many args */
        if (ParamCount <= Func->ParamCount) {
            /* Beware: If there are parameters with identical names, they
            ** cannot go into the same symbol table, which means that, in this
            ** case of errorneous input, the number of nodes in the symbol
            ** table and ParamCount are NOT equal. We have to handle this case
            ** below to avoid segmentation violations. Since we know that this
            ** problem can only occur if there is more than one parameter,
            ** we will just use the last one.
            */
            if (ParamCount == 1) {
                /* First argument */
                Param = Func->SymTab->SymHead;
            } else if (Param->NextSym != 0) {
                /* Next argument */
                Param = Param->NextSym;
                CHECK ((Param->Flags & SC_PARAM) != 0);
            }
        } else if (!Ellipsis) {
            /* Too many arguments. Do we have an open or empty param. list? */
            if ((Func->Flags & (FD_VARIADIC | FD_EMPTY)) == 0) {
                /* End of param list reached, no ellipsis */
                Error ("Too many arguments in function call");
            }
            /* Assume an ellipsis even in case of errors, to avoid an error
            ** message for each other argument.
            */
            Ellipsis = 1;
        }

        /* Evaluate the parameter expression */
        hie1 (&Expr);

        /* If we don't have an argument spec., accept anything; otherwise,
        ** convert the actual argument to the type needed.
        */
        Flags = CF_NONE;
        if (!Ellipsis) {

            /* Convert the argument to the parameter type if needed */
            TypeConversion (&Expr, Param->Type);

            /* If we have a prototype, chars may be pushed as chars */
            Flags |= CF_FORCECHAR;

        } else {

            /* No prototype available. Convert array to "pointer to first
            ** element", and function to "pointer to function".
            */
            Expr.Type = PtrConversion (Expr.Type);

        }

        /* Load the value into the primary if it is not already there */
        LoadExpr (Flags, &Expr);

        /* Use the type of the argument for the push */
        Flags |= TypeOf (Expr.Type);

        /* If this is a fastcall function, don't push the last argument */
        if ((CurTok.Tok == TOK_COMMA && NextTok.Tok != TOK_RPAREN) || !IsFastcall) {
            unsigned ArgSize = sizeofarg (Flags);

            if (FrameSize > 0) {
                /* We have the space already allocated, store in the frame.
                ** Because of invalid type conversions (that have produced an
                ** error before), we can end up here with a non-aligned stack
                ** frame. Since no output will be generated anyway, handle
                ** these cases gracefully instead of doing a CHECK.
                */
                if (FrameSize >= ArgSize) {
                    FrameSize -= ArgSize;
                } else {
                    FrameSize = 0;
                }
                FrameOffs -= ArgSize;
                /* Store */
                g_putlocal (Flags | CF_NOKEEP, FrameOffs, Expr.IVal);
            } else {
                /* Push the argument */
                g_push (Flags, Expr.IVal);
            }

            /* Calculate total parameter size */
            ParamSize += ArgSize;
        }

        /* Check for end of argument list */
        if (CurTok.Tok != TOK_COMMA) {
            break;
        }
        NextToken ();

        /* Check for stray comma */
        if (CurTok.Tok == TOK_RPAREN) {
            Error ("Argument expected after comma");
            break;
        }
    }

    /* Check if we had enough parameters */
    if (ParamCount < Func->ParamCount) {
        Error ("Too few arguments in function call");
    }

    /* The function returns the size of all parameters pushed onto the stack.
    ** However, if there are parameters missing (which is an error, and was
    ** flagged by the compiler), AND a stack frame was preallocated above,
    ** we would loose track of the stackpointer, and generate an internal error
    ** later. So we correct the value by the parameters that should have been
    ** pushed, to avoid an internal compiler error. Since an error was
    ** generated before, no code will be output anyway.
    */
    return ParamSize + FrameSize;
}



static void FunctionCall (ExprDesc* Expr)
/* Perform a function call. */
{
    FuncDesc*     Func;           /* Function descriptor */
    int           IsFuncPtr;      /* Flag */
    unsigned      ParamSize;      /* Number of parameter bytes */
    CodeMark      Mark;
    int           PtrOffs = 0;    /* Offset of function pointer on stack */
    int           IsFastcall = 0; /* True if it's a fast-call function */
    int           PtrOnStack = 0; /* True if a pointer copy is on stack */

    /* Skip the left paren */
    NextToken ();

    /* Get a pointer to the function descriptor from the type string */
    Func = GetFuncDesc (Expr->Type);

    /* Handle function pointers transparently */
    IsFuncPtr = IsTypeFuncPtr (Expr->Type);
    if (IsFuncPtr) {
        /* Check whether it's a fastcall function that has parameters.
        ** Note: if a function is forward-declared in the old K & R style, then
        ** it may be called with any number of arguments, even though its
        ** parameter count is zero.  Handle K & R functions as though there are
        ** parameters.
        */
        IsFastcall = (Func->Flags & FD_VARIADIC) == 0 &&
            (Func->ParamCount > 0 || (Func->Flags & FD_EMPTY)) &&
            (AutoCDecl ?
             IsQualFastcall (Expr->Type + 1) :
             !IsQualCDecl (Expr->Type + 1));

        /* Things may be difficult, depending on where the function pointer
        ** resides. If the function pointer is an expression of some sort
        ** (not a local or global variable), we have to evaluate this
        ** expression now and save the result for later. Since calls to
        ** function pointers may be nested, we must save it onto the stack.
        ** For fastcall functions we do also need to place a copy of the
        ** pointer on stack, since we cannot use a/x.
        */
        PtrOnStack = IsFastcall || !ED_IsConst (Expr);
        if (PtrOnStack) {

            /* Not a global or local variable, or a fastcall function. Load
            ** the pointer into the primary and mark it as an expression.
            */
            LoadExpr (CF_NONE, Expr);
            ED_MakeRValExpr (Expr);

            /* Remember the code position */
            GetCodePos (&Mark);

            /* Push the pointer onto the stack and remember the offset */
            g_push (CF_PTR, 0);
            PtrOffs = StackPtr;
        }

    } else {
        /* Check function attributes */
        if (Expr->Sym && SymHasAttr (Expr->Sym, atNoReturn)) {
            /* For now, handle as if a return statement was encountered */
            F_ReturnFound (CurrentFunc);
        }

        /* Check for known standard functions and inline them */
        if (Expr->Name != 0) {
            int StdFunc = FindStdFunc ((const char*) Expr->Name);
            if (StdFunc >= 0) {
                /* Inline this function */
                HandleStdFunc (StdFunc, Func, Expr);
                return;
            }
        }

        /* If we didn't inline the function, get fastcall info */
        IsFastcall = (Func->Flags & FD_VARIADIC) == 0 &&
            (AutoCDecl ?
             IsQualFastcall (Expr->Type) :
             !IsQualCDecl (Expr->Type));
    }

    /* Parse the parameter list */
    ParamSize = FunctionParamList (Func, IsFastcall);

    /* We need the closing paren here */
    ConsumeRParen ();

    /* Special handling for function pointers */
    if (IsFuncPtr) {

        if (Func->WrappedCall) {
            Warning ("Calling a wrapped function via a pointer, wrapped-call will not be used");
        }

        /* If the function is not a fastcall function, load the pointer to
        ** the function into the primary.
        */
        if (!IsFastcall) {

            /* Not a fastcall function - we may use the primary */
            if (PtrOnStack) {
                /* If we have no parameters, the pointer is still in the
                ** primary. Remove the code to push it and correct the
                ** stack pointer.
                */
                if (ParamSize == 0) {
                    RemoveCode (&Mark);
                    PtrOnStack = 0;
                } else {
                    /* Load from the saved copy */
                    g_getlocal (CF_PTR, PtrOffs);
                }
            } else {
                /* Load from original location */
                LoadExpr (CF_NONE, Expr);
            }

            /* Call the function */
            g_callind (TypeOf (Expr->Type+1), ParamSize, PtrOffs);

        } else {

            /* Fastcall function. We cannot use the primary for the function
            ** pointer and must therefore use an offset to the stack location.
            ** Since fastcall functions may never be variadic, we can use the
            ** index register for this purpose.
            */
            g_callind (CF_LOCAL, ParamSize, PtrOffs);
        }

        /* If we have a pointer on stack, remove it */
        if (PtrOnStack) {
            g_drop (SIZEOF_PTR);
            pop (CF_PTR);
        }

        /* Skip T_PTR */
        ++Expr->Type;

    } else {

        /* Normal function */
        if (Func->WrappedCall) {
            char tmp[64];
            StrBuf S = AUTO_STRBUF_INITIALIZER;

            /* Store the WrappedCall data in tmp4 */
            sprintf(tmp, "ldy #%u", Func->WrappedCallData);
            SB_AppendStr (&S, tmp);
            g_asmcode (&S);
            SB_Clear(&S);

            SB_AppendStr (&S, "sty tmp4");
            g_asmcode (&S);
            SB_Clear(&S);

            /* Store the original function address in ptr4 */
            SB_AppendStr (&S, "ldy #<(_");
            SB_AppendStr (&S, (const char*) Expr->Name);
            SB_AppendChar (&S, ')');
            g_asmcode (&S);
            SB_Clear(&S);

            SB_AppendStr (&S, "sty ptr4");
            g_asmcode (&S);
            SB_Clear(&S);

            SB_AppendStr (&S, "ldy #>(_");
            SB_AppendStr (&S, (const char*) Expr->Name);
            SB_AppendChar (&S, ')');
            g_asmcode (&S);
            SB_Clear(&S);

            SB_AppendStr (&S, "sty ptr4+1");
            g_asmcode (&S);
            SB_Clear(&S);

            SB_Done (&S);

            g_call (TypeOf (Expr->Type), Func->WrappedCall->Name, ParamSize);
        } else {
            g_call (TypeOf (Expr->Type), (const char*) Expr->Name, ParamSize);
        }

    }

    /* The function result is an rvalue in the primary register */
    ED_MakeRValExpr (Expr);
    Expr->Type = GetFuncReturn (Expr->Type);
}



static void Primary (ExprDesc* E)
/* This is the lowest level of the expression parser. */
{
    SymEntry* Sym;

    /* Initialize fields in the expression stucture */
    ED_Init (E);

    /* Character and integer constants. */
    if (CurTok.Tok == TOK_ICONST || CurTok.Tok == TOK_CCONST) {
        E->IVal  = CurTok.IVal;
        E->Flags = E_LOC_ABS | E_RTYPE_RVAL;
        E->Type  = CurTok.Type;
        NextToken ();
        return;
    }

    /* Floating point constant */
    if (CurTok.Tok == TOK_FCONST) {
        E->FVal  = CurTok.FVal;
        E->Flags = E_LOC_ABS | E_RTYPE_RVAL;
        E->Type  = CurTok.Type;
        NextToken ();
        return;
    }

    /* Process parenthesized subexpression by calling the whole parser
    ** recursively.
    */
    if (CurTok.Tok == TOK_LPAREN) {
        NextToken ();
        hie0 (E);
        ConsumeRParen ();
        return;
    }

    /* If we run into an identifier in preprocessing mode, we assume that this
    ** is an undefined macro and replace it by a constant value of zero.
    */
    if (Preprocessing && CurTok.Tok == TOK_IDENT) {
        NextToken ();
        ED_MakeConstAbsInt (E, 0);
        return;
    }

    /* All others may only be used if the expression evaluation is not called
    ** recursively by the preprocessor.
    */
    if (Preprocessing) {
        /* Illegal expression in PP mode */
        Error ("Preprocessor expression expected");
        ED_MakeConstAbsInt (E, 1);
        return;
    }

    switch (CurTok.Tok) {

        case TOK_BOOL_AND:
            /* A computed goto label address */
            if (IS_Get (&Standard) >= STD_CC65) {
                SymEntry* Entry;
                NextToken ();
                Entry = AddLabelSym (CurTok.Ident, SC_REF | SC_GOTO_IND);
                /* output its label */
                E->Flags = E_RTYPE_RVAL | E_LOC_STATIC;
                E->Name = Entry->V.L.Label;
                E->Type = PointerTo (type_void);
                NextToken ();
            } else {
                Error ("Computed gotos are a C extension, not supported with this --standard");
                ED_MakeConstAbsInt (E, 1);
            }
            break;

        case TOK_IDENT:
            /* Identifier. Get a pointer to the symbol table entry */
            Sym = E->Sym = FindSym (CurTok.Ident);

            /* Is the symbol known? */
            if (Sym) {

                /* We found the symbol - skip the name token */
                NextToken ();

                /* Check for illegal symbol types */
                CHECK ((Sym->Flags & SC_LABEL) != SC_LABEL);
                if (Sym->Flags & SC_TYPE) {
                    /* Cannot use type symbols */
                    Error ("Variable identifier expected");
                    /* Assume an int type to make E valid */
                    E->Flags = E_LOC_STACK | E_RTYPE_LVAL;
                    E->Type  = type_int;
                    return;
                }

                /* Mark the symbol as referenced */
                Sym->Flags |= SC_REF;

                /* The expression type is the symbol type */
                E->Type = Sym->Type;

                /* Check for legal symbol types */
                if ((Sym->Flags & SC_CONST) == SC_CONST) {
                    /* Enum or some other numeric constant */
                    E->Flags = E_LOC_ABS | E_RTYPE_RVAL;
                    E->IVal = Sym->V.ConstVal;
                } else if ((Sym->Flags & SC_FUNC) == SC_FUNC) {
                    /* Function */
                    E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL;
                    E->Name = (uintptr_t) Sym->Name;
                } else if ((Sym->Flags & SC_AUTO) == SC_AUTO) {
                    /* Local variable. If this is a parameter for a variadic
                    ** function, we have to add some address calculations, and the
                    ** address is not const.
                    */
                    if ((Sym->Flags & SC_PARAM) == SC_PARAM && F_IsVariadic (CurrentFunc)) {
                        /* Variadic parameter */
                        g_leavariadic (Sym->V.Offs - F_GetParamSize (CurrentFunc));
                        E->Flags = E_LOC_EXPR | E_RTYPE_LVAL;
                    } else {
                        /* Normal parameter */
                        E->Flags = E_LOC_STACK | E_RTYPE_LVAL;
                        E->IVal  = Sym->V.Offs;
                    }
                } else if ((Sym->Flags & SC_REGISTER) == SC_REGISTER) {
                    /* Register variable, zero page based */
                    E->Flags = E_LOC_REGISTER | E_RTYPE_LVAL;
                    E->Name  = Sym->V.R.RegOffs;
                } else if ((Sym->Flags & SC_STATIC) == SC_STATIC) {
                    /* Static variable */
                    if (Sym->Flags & (SC_EXTERN | SC_STORAGE)) {
                        E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL;
                        E->Name = (uintptr_t) Sym->Name;
                    } else {
                        E->Flags = E_LOC_STATIC | E_RTYPE_LVAL;
                        E->Name = Sym->V.L.Label;
                    }
                } else {
                    /* Local static variable */
                    E->Flags = E_LOC_STATIC | E_RTYPE_LVAL;
                    E->Name  = Sym->V.Offs;
                }

                /* We've made all variables lvalues above. However, this is
                ** not always correct: An array is actually the address of its
                ** first element, which is a rvalue, and a function is a
                ** rvalue, too, because we cannot store anything in a function.
                ** So fix the flags depending on the type.
                */
                if (IsTypeArray (E->Type) || IsTypeFunc (E->Type)) {
                    ED_MakeRVal (E);
                }

            } else {

                /* We did not find the symbol. Remember the name, then skip it */
                ident Ident;
                strcpy (Ident, CurTok.Ident);
                NextToken ();

                /* IDENT is either an auto-declared function or an undefined variable. */
                if (CurTok.Tok == TOK_LPAREN) {
                    /* C99 doesn't allow calls to undefined functions, so
                    ** generate an error and otherwise a warning. Declare a
                    ** function returning int. For that purpose, prepare a
                    ** function signature for a function having an empty param
                    ** list and returning int.
                    */
                    if (IS_Get (&Standard) >= STD_C99) {
                        Error ("Call to undefined function '%s'", Ident);
                    } else {
                        Warning ("Call to undefined function '%s'", Ident);
                    }
                    Sym = AddGlobalSym (Ident, GetImplicitFuncType(), SC_EXTERN | SC_REF | SC_FUNC);
                    E->Type  = Sym->Type;
                    E->Flags = E_LOC_GLOBAL | E_RTYPE_RVAL;
                    E->Name  = (uintptr_t) Sym->Name;
                } else {
                    /* Undeclared Variable */
                    Sym = AddLocalSym (Ident, type_int, SC_AUTO | SC_REF, 0);
                    E->Flags = E_LOC_STACK | E_RTYPE_LVAL;
                    E->Type = type_int;
                    Error ("Undefined symbol: '%s'", Ident);
                }

            }
            break;

        case TOK_SCONST:
        case TOK_WCSCONST:
            /* String literal */
            E->LVal  = UseLiteral (CurTok.SVal);
            E->Type  = GetCharArrayType (GetLiteralSize (CurTok.SVal));
            E->Flags = E_LOC_LITERAL | E_RTYPE_RVAL;
            E->IVal  = 0;
            E->Name  = GetLiteralLabel (CurTok.SVal);
            NextToken ();
            break;

        case TOK_ASM:
            /* ASM statement */
            AsmStatement ();
            E->Flags = E_LOC_EXPR | E_RTYPE_RVAL;
            E->Type  = type_void;
            break;

        case TOK_A:
            /* Register pseudo variable */
            E->Type  = type_uchar;
            E->Flags = E_LOC_PRIMARY | E_RTYPE_LVAL;
            NextToken ();
            break;

        case TOK_AX:
            /* Register pseudo variable */
            E->Type  = type_uint;
            E->Flags = E_LOC_PRIMARY | E_RTYPE_LVAL;
            NextToken ();
            break;

        case TOK_EAX:
            /* Register pseudo variable */
            E->Type  = type_ulong;
            E->Flags = E_LOC_PRIMARY | E_RTYPE_LVAL;
            NextToken ();
            break;

        default:
            /* Illegal primary. Be sure to skip the token to avoid endless
            ** error loops.
            */
            Error ("Expression expected");
            NextToken ();
            ED_MakeConstAbsInt (E, 1);
            break;
    }
}



static void ArrayRef (ExprDesc* Expr)
/* Handle an array reference. This function needs a rewrite. */
{
    int         ConstBaseAddr;
    ExprDesc    Subscript;
    CodeMark    Mark1;
    CodeMark    Mark2;
    TypeCode    Qualifiers;
    Type*       ElementType;
    Type*       tptr1;


    /* Skip the bracket */
    NextToken ();

    /* Get the type of left side */
    tptr1 = Expr->Type;

    /* We can apply a special treatment for arrays that have a const base
    ** address. This is true for most arrays and will produce a lot better
    ** code. Check if this is a const base address.
    */
    ConstBaseAddr = ED_IsRVal (Expr) &&
                    (ED_IsLocConst (Expr) || ED_IsLocStack (Expr));

    /* If we have a constant base, we delay the address fetch */
    GetCodePos (&Mark1);
    if (!ConstBaseAddr) {
        /* Get a pointer to the array into the primary */
        LoadExpr (CF_NONE, Expr);

        /* Get the array pointer on stack. Do not push more than 16
        ** bit, even if this value is greater, since we cannot handle
        ** other than 16bit stuff when doing indexing.
        */
        GetCodePos (&Mark2);
        g_push (CF_PTR, 0);
    }

    /* TOS now contains ptr to array elements. Get the subscript. */
    MarkedExprWithCheck (hie0, &Subscript);

    /* Check the types of array and subscript. We can either have a
    ** pointer/array to the left, in which case the subscript must be of an
    ** integer type, or we have an integer to the left, in which case the
    ** subscript must be a pointer/array.
    ** Since we do the necessary checking here, we can rely later on the
    ** correct types.
    */
    Qualifiers = T_QUAL_NONE;
    if (IsClassPtr (Expr->Type)) {
        if (!IsClassInt (Subscript.Type))  {
            Error ("Array subscript is not an integer");
            /* To avoid any compiler errors, make the expression a valid int */
            ED_MakeConstAbsInt (&Subscript, 0);
        }
        if (IsTypeArray (Expr->Type)) {
            Qualifiers = GetQualifier (Expr->Type);
        }
        ElementType = Indirect (Expr->Type);
    } else if (IsClassInt (Expr->Type)) {
        if (!IsClassPtr (Subscript.Type)) {
            Error ("Subscripted value is neither array nor pointer");
            /* To avoid compiler errors, make the subscript a char[] at
            ** address 0.
            */
            ED_MakeConstAbs (&Subscript, 0, GetCharArrayType (1));
        } else if (IsTypeArray (Subscript.Type)) {
            Qualifiers = GetQualifier (Subscript.Type);
        }
        ElementType = Indirect (Subscript.Type);
    } else {
        Error ("Cannot subscript");
        /* To avoid compiler errors, fake both the array and the subscript, so
        ** we can just proceed.
        */
        ED_MakeConstAbs (Expr, 0, GetCharArrayType (1));
        ED_MakeConstAbsInt (&Subscript, 0);
        ElementType = Indirect (Expr->Type);
    }

    /* The element type has the combined qualifiers from itself and the array,
    ** it is a member of (if any).
    */
    if (GetQualifier (ElementType) != (GetQualifier (ElementType) | Qualifiers)) {
        ElementType = TypeDup (ElementType);
        ElementType->C |= Qualifiers;
    }

    /* If the subscript is a bit-field, load it and make it an rvalue */
    if (ED_IsBitField (&Subscript)) {
        LoadExpr (CF_NONE, &Subscript);
        ED_MakeRValExpr (&Subscript);
    }

    /* Check if the subscript is constant absolute value */
    if (ED_IsConstAbs (&Subscript) && ED_CodeRangeIsEmpty (&Subscript)) {

        /* The array subscript is a numeric constant. If we had pushed the
        ** array base address onto the stack before, we can remove this value,
        ** since we can generate expression+offset.
        */
        if (!ConstBaseAddr) {
            RemoveCode (&Mark2);
        } else {
            /* Get an array pointer into the primary */
            LoadExpr (CF_NONE, Expr);
        }

        if (IsClassPtr (Expr->Type)) {

            /* Lhs is pointer/array. Scale the subscript value according to
            ** the element size.
            */
            Subscript.IVal *= CheckedSizeOf (ElementType);

            /* Remove the address load code */
            RemoveCode (&Mark1);

            /* In case of an array, we can adjust the offset of the expression
            ** already in Expr. If the base address was a constant, we can even
            ** remove the code that loaded the address into the primary.
            */
            if (IsTypeArray (Expr->Type)) {

                /* Adjust the offset */
                Expr->IVal += Subscript.IVal;

            } else {

                /* It's a pointer, so we do have to load it into the primary
                ** first (if it's not already there).
                */
                if (ConstBaseAddr || ED_IsLVal (Expr)) {
                    LoadExpr (CF_NONE, Expr);
                    ED_MakeRValExpr (Expr);
                }

                /* Use the offset */
                Expr->IVal = Subscript.IVal;
            }

        } else {

            /* Scale the rhs value according to the element type */
            g_scale (TypeOf (tptr1), CheckedSizeOf (ElementType));

            /* Add the subscript. Since arrays are indexed by integers,
            ** we will ignore the true type of the subscript here and
            ** use always an int. #### Use offset but beware of LoadExpr!
            */
            g_inc (CF_INT | CF_CONST, Subscript.IVal);

        }

    } else {

        /* Array subscript is not constant. Load it into the primary */
        GetCodePos (&Mark2);
        LoadExpr (CF_NONE, &Subscript);

        /* Do scaling */
        if (IsClassPtr (Expr->Type)) {

            /* Indexing is based on unsigneds, so we will just use the integer
            ** portion of the index (which is in (e)ax, so there's no further
            ** action required).
            */
            g_scale (CF_INT, CheckedSizeOf (ElementType));

        } else {

            /* Get the int value on top. If we come here, we're sure, both
            ** values are 16 bit (the first one was truncated if necessary
            ** and the second one is a pointer). Note: If ConstBaseAddr is
            ** true, we don't have a value on stack, so to "swap" both, just
            ** push the subscript.
            */
            if (ConstBaseAddr) {
                g_push (CF_INT, 0);
                LoadExpr (CF_NONE, Expr);
                ConstBaseAddr = 0;
            } else {
                g_swap (CF_INT);
            }

            /* Scale it */
            g_scale (TypeOf (tptr1), CheckedSizeOf (ElementType));

        }

        /* The offset is now in the primary register. It we didn't have a
        ** constant base address for the lhs, the lhs address is already
        ** on stack, and we must add the offset. If the base address was
        ** constant, we call special functions to add the address to the
        ** offset value.
        */
        if (!ConstBaseAddr) {

            /* The array base address is on stack and the subscript is in the
            ** primary. Add both.
            */
            g_add (CF_INT, 0);

        } else {

            /* The subscript is in the primary, and the array base address is
            ** in Expr. If the subscript has itself a constant address, it is
            ** often a better idea to reverse again the order of the
            ** evaluation. This will generate better code if the subscript is
            ** a byte sized variable. But beware: This is only possible if the
            ** subscript was not scaled, that is, if this was a byte array
            ** or pointer.
            */
            if ((ED_IsLocConst (&Subscript) || ED_IsLocStack (&Subscript)) &&
                CheckedSizeOf (ElementType) == SIZEOF_CHAR) {

                unsigned Flags;

                /* Reverse the order of evaluation */
                if (CheckedSizeOf (Subscript.Type) == SIZEOF_CHAR) {
                    Flags = CF_CHAR;
                } else {
                    Flags = CF_INT;
                }
                RemoveCode (&Mark2);

                /* Get a pointer to the array into the primary. */
                LoadExpr (CF_NONE, Expr);

                /* Add the variable */
                if (ED_IsLocStack (&Subscript)) {
                    g_addlocal (Flags, Subscript.IVal);
                } else {
                    Flags |= GlobalModeFlags (&Subscript);
                    g_addstatic (Flags, Subscript.Name, Subscript.IVal);
                }
            } else {

                if (ED_IsLocAbs (Expr)) {
                    /* Constant numeric address. Just add it */
                    g_inc (CF_INT, Expr->IVal);
                } else if (ED_IsLocStack (Expr)) {
                    /* Base address is a local variable address */
                    if (IsTypeArray (Expr->Type)) {
                        g_addaddr_local (CF_INT, Expr->IVal);
                    } else {
                        g_addlocal (CF_PTR, Expr->IVal);
                    }
                } else {
                    /* Base address is a static variable address */
                    unsigned Flags = CF_INT | GlobalModeFlags (Expr);
                    if (ED_IsRVal (Expr)) {
                        /* Add the address of the location */
                        g_addaddr_static (Flags, Expr->Name, Expr->IVal);
                    } else {
                        /* Add the contents of the location */
                        g_addstatic (Flags, Expr->Name, Expr->IVal);
                    }
                }
            }


        }

        /* The result is an expression in the primary */
        ED_MakeRValExpr (Expr);

    }

    /* Result is of element type */
    Expr->Type = ElementType;

    /* An array element is actually a variable. So the rules for variables
    ** with respect to the reference type apply: If it's an array, it is
    ** a rvalue, otherwise it's an lvalue. (A function would also be a rvalue,
    ** but an array cannot contain functions).
    */
    if (IsTypeArray (Expr->Type)) {
        ED_MakeRVal (Expr);
    } else {
        ED_MakeLVal (Expr);
    }

    /* Consume the closing bracket */
    ConsumeRBrack ();
}



static void StructRef (ExprDesc* Expr)
/* Process struct field after . or ->. */
{
    ident Ident;
    SymEntry* Field;
    Type* FinalType;
    TypeCode Q;

    /* Skip the token and check for an identifier */
    NextToken ();
    if (CurTok.Tok != TOK_IDENT) {
        Error ("Identifier expected");
        /* Make the expression an integer at address zero */
        ED_MakeConstAbs (Expr, 0, type_int);
        return;
    }

    /* Get the symbol table entry and check for a struct field */
    strcpy (Ident, CurTok.Ident);
    NextToken ();
    Field = FindStructField (Expr->Type, Ident);
    if (Field == 0) {
        Error ("Struct/union has no field named '%s'", Ident);
        /* Make the expression an integer at address zero */
        ED_MakeConstAbs (Expr, 0, type_int);
        return;
    }

    /* If we have a struct pointer that is an lvalue and not already in the
    ** primary, load it now.
    */
    if (ED_IsLVal (Expr) && IsTypePtr (Expr->Type)) {

        /* Load into the primary */
        LoadExpr (CF_NONE, Expr);

        /* Make it an lvalue expression */
        ED_MakeLValExpr (Expr);
    }

    /* The type is the type of the field plus any qualifiers from the struct */
    if (IsClassStruct (Expr->Type)) {
        Q = GetQualifier (Expr->Type);
    } else {
        Q = GetQualifier (Indirect (Expr->Type));
    }
    if (GetQualifier (Field->Type) == (GetQualifier (Field->Type) | Q)) {
        FinalType = Field->Type;
    } else {
        FinalType = TypeDup (Field->Type);
        FinalType->C |= Q;
    }

    /* A struct is usually an lvalue. If not, it is a struct in the primary
    ** register.
    */
    if (ED_IsRVal (Expr) && ED_IsLocExpr (Expr) && !IsTypePtr (Expr->Type)) {

        unsigned Flags = 0;
        unsigned BitOffs;

        /* Get the size of the type */
        unsigned Size = SizeOf (Expr->Type);

        /* Safety check */
        CHECK (Field->V.Offs + Size <= SIZEOF_LONG);

        /* The type of the operation depends on the type of the struct */
        switch (Size) {
            case 1:     Flags = CF_CHAR | CF_UNSIGNED | CF_CONST;       break;
            case 2:     Flags = CF_INT  | CF_UNSIGNED | CF_CONST;       break;
            case 3:     /* FALLTHROUGH */
            case 4:     Flags = CF_LONG | CF_UNSIGNED | CF_CONST;       break;
            default:    Internal ("Invalid struct size: %u", Size);     break;
        }

        /* Generate a shift to get the field in the proper position in the
        ** primary. For bit fields, mask the value.
        */
        BitOffs = Field->V.Offs * CHAR_BITS;
        if (SymIsBitField (Field)) {
            BitOffs += Field->V.B.BitOffs;
            g_asr (Flags, BitOffs);
            /* Mask the value. This is unnecessary if the shift executed above
            ** moved only zeroes into the value.
            */
            if (BitOffs + Field->V.B.BitWidth != Size * CHAR_BITS) {
                g_and (CF_INT | CF_UNSIGNED | CF_CONST,
                       (0x0001U << Field->V.B.BitWidth) - 1U);
            }
        } else {
            g_asr (Flags, BitOffs);
        }

        /* Use the new type */
        Expr->Type = FinalType;

    } else {

        /* Set the struct field offset */
        Expr->IVal += Field->V.Offs;

        /* Use the new type */
        Expr->Type = FinalType;

        /* An struct member is actually a variable. So the rules for variables
        ** with respect to the reference type apply: If it's an array, it is
        ** a rvalue, otherwise it's an lvalue. (A function would also be a rvalue,
        ** but a struct field cannot be a function).
        */
        if (IsTypeArray (Expr->Type)) {
            ED_MakeRVal (Expr);
        } else {
            ED_MakeLVal (Expr);
        }

        /* Make the expression a bit field if necessary */
        if (SymIsBitField (Field)) {
            ED_MakeBitField (Expr, Field->V.B.BitOffs, Field->V.B.BitWidth);
        }
    }

}



static void hie11 (ExprDesc *Expr)
/* Handle compound types (structs and arrays) */
{
    /* Name value used in invalid function calls */
    static const char IllegalFunc[] = "illegal_function_call";

    /* Evaluate the lhs */
    Primary (Expr);

    /* Check for a rhs */
    while (CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN ||
           CurTok.Tok == TOK_DOT    || CurTok.Tok == TOK_PTR_REF) {

        switch (CurTok.Tok) {

            case TOK_LBRACK:
                /* Array reference */
                ArrayRef (Expr);
                break;

            case TOK_LPAREN:
                /* Function call. */
                if (!IsTypeFunc (Expr->Type) && !IsTypeFuncPtr (Expr->Type)) {
                    /* Not a function */
                    Error ("Illegal function call");
                    /* Force the type to be a implicitly defined function, one
                    ** returning an int and taking any number of arguments.
                    ** Since we don't have a name, invent one.
                    */
                    ED_MakeConstAbs (Expr, 0, GetImplicitFuncType ());
                    Expr->Name = (uintptr_t) IllegalFunc;
                }
                /* Call the function */
                FunctionCall (Expr);
                break;

            case TOK_DOT:
                if (!IsClassStruct (Expr->Type)) {
                    Error ("Struct expected");
                }
                StructRef (Expr);
                break;

            case TOK_PTR_REF:
                /* If we have an array, convert it to pointer to first element */
                if (IsTypeArray (Expr->Type)) {
                    Expr->Type = ArrayToPtr (Expr->Type);
                }
                if (!IsClassPtr (Expr->Type) || !IsClassStruct (Indirect (Expr->Type))) {
                    Error ("Struct pointer expected");
                }
                StructRef (Expr);
                break;

            default:
                Internal ("Invalid token in hie11: %d", CurTok.Tok);

        }
    }
}



void Store (ExprDesc* Expr, const Type* StoreType)
/* Store the primary register into the location denoted by Expr. If StoreType
** is given, use this type when storing instead of Expr->Type. If StoreType
** is NULL, use Expr->Type instead.
*/
{
    unsigned Flags;

    /* If StoreType was not given, use Expr->Type instead */
    if (StoreType == 0) {
        StoreType = Expr->Type;
    }

    /* Prepare the code generator flags */
    Flags = TypeOf (StoreType) | GlobalModeFlags (Expr);

    /* Do the store depending on the location */
    switch (ED_GetLoc (Expr)) {

        case E_LOC_ABS:
            /* Absolute: numeric address or const */
            g_putstatic (Flags, Expr->IVal, 0);
            break;

        case E_LOC_GLOBAL:
            /* Global variable */
            g_putstatic (Flags, Expr->Name, Expr->IVal);
            break;

        case E_LOC_STATIC:
        case E_LOC_LITERAL:
            /* Static variable or literal in the literal pool */
            g_putstatic (Flags, Expr->Name, Expr->IVal);
            break;

        case E_LOC_REGISTER:
            /* Register variable */
            g_putstatic (Flags, Expr->Name, Expr->IVal);
            break;

        case E_LOC_STACK:
            /* Value on the stack */
            g_putlocal (Flags, Expr->IVal, 0);
            break;

        case E_LOC_PRIMARY:
            /* The primary register (value is already there) */
            break;

        case E_LOC_EXPR:
            /* An expression in the primary register */
            g_putind (Flags, Expr->IVal);
            break;

        default:
            Internal ("Invalid location in Store(): 0x%04X", ED_GetLoc (Expr));
    }

    /* Assume that each one of the stores will invalidate CC */
    ED_MarkAsUntested (Expr);
}



static void PreInc (ExprDesc* Expr)
/* Handle the preincrement operators */
{
    unsigned Flags;
    unsigned long Val;

    /* Skip the operator token */
    NextToken ();

    /* Evaluate the expression and check that it is an lvalue */
    hie10 (Expr);
    if (!ED_IsLVal (Expr)) {
        Error ("Invalid lvalue");
        return;
    }

    /* We cannot modify const values */
    if (IsQualConst (Expr->Type)) {
        Error ("Increment of read-only variable");
    }

    /* Get the data type */
    Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST;

    /* Get the increment value in bytes */
    Val = IsTypePtr (Expr->Type)? CheckedPSizeOf (Expr->Type) : 1;

    /* Check the location of the data */
    switch (ED_GetLoc (Expr)) {

        case E_LOC_ABS:
            /* Absolute: numeric address or const */
            g_addeqstatic (Flags, Expr->IVal, 0, Val);
            break;

        case E_LOC_GLOBAL:
            /* Global variable */
            g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val);
            break;

        case E_LOC_STATIC:
        case E_LOC_LITERAL:
            /* Static variable or literal in the literal pool */
            g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val);
            break;

        case E_LOC_REGISTER:
            /* Register variable */
            g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val);
            break;

        case E_LOC_STACK:
            /* Value on the stack */
            g_addeqlocal (Flags, Expr->IVal, Val);
            break;

        case E_LOC_PRIMARY:
            /* The primary register */
            g_inc (Flags, Val);
            break;

        case E_LOC_EXPR:
            /* An expression in the primary register */
            g_addeqind (Flags, Expr->IVal, Val);
            break;

        default:
            Internal ("Invalid location in PreInc(): 0x%04X", ED_GetLoc (Expr));
    }

    /* Result is an expression, no reference */
    ED_MakeRValExpr (Expr);
}



static void PreDec (ExprDesc* Expr)
/* Handle the predecrement operators */
{
    unsigned Flags;
    unsigned long Val;

    /* Skip the operator token */
    NextToken ();

    /* Evaluate the expression and check that it is an lvalue */
    hie10 (Expr);
    if (!ED_IsLVal (Expr)) {
        Error ("Invalid lvalue");
        return;
    }

    /* We cannot modify const values */
    if (IsQualConst (Expr->Type)) {
        Error ("Decrement of read-only variable");
    }

    /* Get the data type */
    Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST;

    /* Get the increment value in bytes */
    Val = IsTypePtr (Expr->Type)? CheckedPSizeOf (Expr->Type) : 1;

    /* Check the location of the data */
    switch (ED_GetLoc (Expr)) {

        case E_LOC_ABS:
            /* Absolute: numeric address or const */
            g_subeqstatic (Flags, Expr->IVal, 0, Val);
            break;

        case E_LOC_GLOBAL:
            /* Global variable */
            g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val);
            break;

        case E_LOC_STATIC:
        case E_LOC_LITERAL:
            /* Static variable or literal in the literal pool */
            g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val);
            break;

        case E_LOC_REGISTER:
            /* Register variable */
            g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val);
            break;

        case E_LOC_STACK:
            /* Value on the stack */
            g_subeqlocal (Flags, Expr->IVal, Val);
            break;

        case E_LOC_PRIMARY:
            /* The primary register */
            g_inc (Flags, Val);
            break;

        case E_LOC_EXPR:
            /* An expression in the primary register */
            g_subeqind (Flags, Expr->IVal, Val);
            break;

        default:
            Internal ("Invalid location in PreDec(): 0x%04X", ED_GetLoc (Expr));
    }

    /* Result is an expression, no reference */
    ED_MakeRValExpr (Expr);
}



static void PostInc (ExprDesc* Expr)
/* Handle the postincrement operator */
{
    unsigned Flags;

    NextToken ();

    /* The expression to increment must be an lvalue */
    if (!ED_IsLVal (Expr)) {
        Error ("Invalid lvalue");
        return;
    }

    /* We cannot modify const values */
    if (IsQualConst (Expr->Type)) {
        Error ("Increment of read-only variable");
    }

    /* Get the data type */
    Flags = TypeOf (Expr->Type);

    /* Emit smaller code if a char variable is at a constant location */
    if ((Flags & CF_CHAR) == CF_CHAR && ED_IsLocConst(Expr)) {

        LoadExpr (CF_NONE, Expr);
        AddCodeLine ("inc %s", ED_GetLabelName(Expr, 0));

    } else {

        /* Push the address if needed */
        PushAddr (Expr);

        /* Fetch the value and save it (since it's the result of the expression) */
        LoadExpr (CF_NONE, Expr);
        g_save (Flags | CF_FORCECHAR);

        /* If we have a pointer expression, increment by the size of the type */
        if (IsTypePtr (Expr->Type)) {
            g_inc (Flags | CF_CONST | CF_FORCECHAR, CheckedSizeOf (Expr->Type + 1));
        } else {
            g_inc (Flags | CF_CONST | CF_FORCECHAR, 1);
        }

        /* Store the result back */
        Store (Expr, 0);

        /* Restore the original value in the primary register */
        g_restore (Flags | CF_FORCECHAR);
    }

    /* The result is always an expression, no reference */
    ED_MakeRValExpr (Expr);
}



static void PostDec (ExprDesc* Expr)
/* Handle the postdecrement operator */
{
    unsigned Flags;

    NextToken ();

    /* The expression to increment must be an lvalue */
    if (!ED_IsLVal (Expr)) {
        Error ("Invalid lvalue");
        return;
    }

    /* We cannot modify const values */
    if (IsQualConst (Expr->Type)) {
        Error ("Decrement of read-only variable");
    }

    /* Get the data type */
    Flags = TypeOf (Expr->Type);

    /* Emit smaller code if a char variable is at a constant location */
    if ((Flags & CF_CHAR) == CF_CHAR && ED_IsLocConst(Expr)) {

        LoadExpr (CF_NONE, Expr);
        AddCodeLine ("dec %s", ED_GetLabelName(Expr, 0));

    } else {

        /* Push the address if needed */
        PushAddr (Expr);

        /* Fetch the value and save it (since it's the result of the expression) */
        LoadExpr (CF_NONE, Expr);
        g_save (Flags | CF_FORCECHAR);

        /* If we have a pointer expression, increment by the size of the type */
        if (IsTypePtr (Expr->Type)) {
            g_dec (Flags | CF_CONST | CF_FORCECHAR, CheckedSizeOf (Expr->Type + 1));
        } else {
            g_dec (Flags | CF_CONST | CF_FORCECHAR, 1);
        }

        /* Store the result back */
        Store (Expr, 0);

        /* Restore the original value in the primary register */
        g_restore (Flags | CF_FORCECHAR);
    }

    /* The result is always an expression, no reference */
    ED_MakeRValExpr (Expr);
}



static void UnaryOp (ExprDesc* Expr)
/* Handle unary -/+ and ~ */
{
    unsigned Flags;

    /* Remember the operator token and skip it */
    token_t Tok = CurTok.Tok;
    NextToken ();

    /* Get the expression */
    hie10 (Expr);

    /* We can only handle integer types */
    if (!IsClassInt (Expr->Type)) {
        Error ("Argument must have integer type");
        ED_MakeConstAbsInt (Expr, 1);
    }

    /* Check for a constant expression */
    if (ED_IsConstAbs (Expr)) {
        /* Value is constant */
        switch (Tok) {
            case TOK_MINUS: Expr->IVal = -Expr->IVal;   break;
            case TOK_PLUS:                              break;
            case TOK_COMP:  Expr->IVal = ~Expr->IVal;   break;
            default:        Internal ("Unexpected token: %d", Tok);
        }
    } else {
        /* Value is not constant */
        LoadExpr (CF_NONE, Expr);

        /* Get the type of the expression */
        Flags = TypeOf (Expr->Type);

        /* Handle the operation */
        switch (Tok) {
            case TOK_MINUS: g_neg (Flags);  break;
            case TOK_PLUS:                  break;
            case TOK_COMP:  g_com (Flags);  break;
            default:        Internal ("Unexpected token: %d", Tok);
        }

        /* The result is a rvalue in the primary */
        ED_MakeRValExpr (Expr);
    }
}



void hie10 (ExprDesc* Expr)
/* Handle ++, --, !, unary - etc. */
{
    unsigned long Size;

    switch (CurTok.Tok) {

        case TOK_INC:
            PreInc (Expr);
            break;

        case TOK_DEC:
            PreDec (Expr);
            break;

        case TOK_PLUS:
        case TOK_MINUS:
        case TOK_COMP:
            UnaryOp (Expr);
            break;

        case TOK_BOOL_NOT:
            NextToken ();
            if (evalexpr (CF_NONE, hie10, Expr) == 0) {
                /* Constant expression */
                Expr->IVal = !Expr->IVal;
            } else {
                g_bneg (TypeOf (Expr->Type));
                ED_MakeRValExpr (Expr);
                ED_TestDone (Expr);             /* bneg will set cc */
            }
            break;

        case TOK_STAR:
            NextToken ();
            ExprWithCheck (hie10, Expr);
            if (ED_IsLVal (Expr) || !(ED_IsLocConst (Expr) || ED_IsLocStack (Expr))) {
                /* Not a const, load it into the primary and make it a
                ** calculated value.
                */
                LoadExpr (CF_NONE, Expr);
                ED_MakeRValExpr (Expr);
            }
            /* If the expression is already a pointer to function, the
            ** additional dereferencing operator must be ignored. A function
            ** itself is represented as "pointer to function", so any number
            ** of dereference operators is legal, since the result will
            ** always be converted to "pointer to function".
            */
            if (IsTypeFuncPtr (Expr->Type) || IsTypeFunc (Expr->Type)) {
                /* Expression not storable */
                ED_MakeRVal (Expr);
            } else {
                if (IsClassPtr (Expr->Type)) {
                    Expr->Type = Indirect (Expr->Type);
                } else {
                    Error ("Illegal indirection");
                }
                /* If the expression points to an array, then don't convert the
                ** address -- it already is the location of the first element.
                */
                if (!IsTypeArray (Expr->Type)) {
                    /* The * operator yields an lvalue */
                    ED_MakeLVal (Expr);
                }
            }
            break;

        case TOK_AND:
            NextToken ();
            ExprWithCheck (hie10, Expr);
            /* The & operator may be applied to any lvalue, and it may be
            ** applied to functions, even if they're no lvalues.
            */
            if (ED_IsRVal (Expr) && !IsTypeFunc (Expr->Type) && !IsTypeArray (Expr->Type)) {
                Error ("Illegal address");
            } else {
                if (ED_IsBitField (Expr)) {
                    Error ("Cannot take address of bit-field");
                    /* Do it anyway, just to avoid further warnings */
                    Expr->Flags &= ~E_BITFIELD;
                }
                Expr->Type = PointerTo (Expr->Type);
                /* The & operator yields an rvalue */
                ED_MakeRVal (Expr);
            }
            break;

        case TOK_SIZEOF:
            NextToken ();
            if (TypeSpecAhead ()) {
                Type T[MAXTYPELEN];
                NextToken ();
                Size = CheckedSizeOf (ParseType (T));
                ConsumeRParen ();
            } else {
                /* Remember the output queue pointer */
                CodeMark Mark;
                GetCodePos (&Mark);
                hie10 (Expr);
                /* If the expression is a literal string, release it, so it
                ** won't be output as data if not used elsewhere.
                */
                if (ED_IsLocLiteral (Expr)) {
                    ReleaseLiteral (Expr->LVal);
                }
                /* Calculate the size */
                Size = CheckedSizeOf (Expr->Type);
                /* Remove any generated code */
                RemoveCode (&Mark);
            }
            ED_MakeConstAbs (Expr, Size, type_size_t);
            ED_MarkAsUntested (Expr);
            break;

        default:
            if (TypeSpecAhead ()) {

                /* A typecast */
                TypeCast (Expr);

            } else {

                /* An expression */
                hie11 (Expr);

                /* Handle post increment */
                switch (CurTok.Tok) {
                    case TOK_INC:   PostInc (Expr); break;
                    case TOK_DEC:   PostDec (Expr); break;
                    default:                        break;
                }

            }
            break;
    }
}



static void hie_internal (const GenDesc* Ops,   /* List of generators */
                          ExprDesc* Expr,
                          void (*hienext) (ExprDesc*),
                          int* UsedGen)
/* Helper function */
{
    ExprDesc Expr2;
    CodeMark Mark1;
    CodeMark Mark2;
    const GenDesc* Gen;
    token_t Tok;                        /* The operator token */
    unsigned ltype, type;
    int lconst;                         /* Left operand is a constant */
    int rconst;                         /* Right operand is a constant */


    ExprWithCheck (hienext, Expr);

    *UsedGen = 0;
    while ((Gen = FindGen (CurTok.Tok, Ops)) != 0) {

        /* Tell the caller that we handled it's ops */
        *UsedGen = 1;

        /* All operators that call this function expect an int on the lhs */
        if (!IsClassInt (Expr->Type)) {
            Error ("Integer expression expected");
            /* To avoid further errors, make Expr a valid int expression */
            ED_MakeConstAbsInt (Expr, 1);
        }

        /* Remember the operator token, then skip it */
        Tok = CurTok.Tok;
        NextToken ();

        /* Get the lhs on stack */
        GetCodePos (&Mark1);
        ltype = TypeOf (Expr->Type);
        lconst = ED_IsConstAbs (Expr);
        if (lconst) {
            /* Constant value */
            GetCodePos (&Mark2);
            /* If the operator is commutative, don't push the left side, if
            ** it's a constant, since we will exchange both operands.
            */
            if ((Gen->Flags & GEN_COMM) == 0) {
                g_push (ltype | CF_CONST, Expr->IVal);
            }
        } else {
            /* Value not constant */
            LoadExpr (CF_NONE, Expr);
            GetCodePos (&Mark2);
            g_push (ltype, 0);
        }

        /* Get the right hand side */
        MarkedExprWithCheck (hienext, &Expr2);

        /* Check for a constant expression */
        rconst = (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2));
        if (!rconst) {
            /* Not constant, load into the primary */
            LoadExpr (CF_NONE, &Expr2);
        }

        /* Check the type of the rhs */
        if (!IsClassInt (Expr2.Type)) {
            Error ("Integer expression expected");
        }

        /* Check for const operands */
        if (lconst && rconst) {

            /* Both operands are constant, remove the generated code */
            RemoveCode (&Mark1);

            /* Get the type of the result */
            Expr->Type = promoteint (Expr->Type, Expr2.Type);

            /* Handle the op differently for signed and unsigned types */
            if (IsSignSigned (Expr->Type)) {

                /* Evaluate the result for signed operands */
                signed long Val1 = Expr->IVal;
                signed long Val2 = Expr2.IVal;
                switch (Tok) {
                    case TOK_OR:
                        Expr->IVal = (Val1 | Val2);
                        break;
                    case TOK_XOR:
                        Expr->IVal = (Val1 ^ Val2);
                        break;
                    case TOK_AND:
                        Expr->IVal = (Val1 & Val2);
                        break;
                    case TOK_STAR:
                        Expr->IVal = (Val1 * Val2);
                        break;
                    case TOK_DIV:
                        if (Val2 == 0) {
                            Error ("Division by zero");
                            Expr->IVal = 0x7FFFFFFF;
                        } else {
                            Expr->IVal = (Val1 / Val2);
                        }
                        break;
                    case TOK_MOD:
                        if (Val2 == 0) {
                            Error ("Modulo operation with zero");
                            Expr->IVal = 0;
                        } else {
                            Expr->IVal = (Val1 % Val2);
                        }
                        break;
                    default:
                        Internal ("hie_internal: got token 0x%X\n", Tok);
                }
            } else {

                /* Evaluate the result for unsigned operands */
                unsigned long Val1 = Expr->IVal;
                unsigned long Val2 = Expr2.IVal;
                switch (Tok) {
                    case TOK_OR:
                        Expr->IVal = (Val1 | Val2);
                        break;
                    case TOK_XOR:
                        Expr->IVal = (Val1 ^ Val2);
                        break;
                    case TOK_AND:
                        Expr->IVal = (Val1 & Val2);
                        break;
                    case TOK_STAR:
                        Expr->IVal = (Val1 * Val2);
                        break;
                    case TOK_DIV:
                        if (Val2 == 0) {
                            Error ("Division by zero");
                            Expr->IVal = 0xFFFFFFFF;
                        } else {
                            Expr->IVal = (Val1 / Val2);
                        }
                        break;
                    case TOK_MOD:
                        if (Val2 == 0) {
                            Error ("Modulo operation with zero");
                            Expr->IVal = 0;
                        } else {
                            Expr->IVal = (Val1 % Val2);
                        }
                        break;
                    default:
                        Internal ("hie_internal: got token 0x%X\n", Tok);
                }
            }

        } else if (lconst && (Gen->Flags & GEN_COMM) && !rconst) {

            /* The left side is constant, the right side is not, and the
            ** operator allows swapping the operands. We haven't pushed the
            ** left side onto the stack in this case, and will reverse the
            ** operation because this allows for better code.
            */
            unsigned rtype = ltype | CF_CONST;
            ltype = TypeOf (Expr2.Type);       /* Expr2 is now left */
            type = CF_CONST;
            if ((Gen->Flags & GEN_NOPUSH) == 0) {
                g_push (ltype, 0);
            } else {
                ltype |= CF_REG;        /* Value is in register */
            }

            /* Determine the type of the operation result. */
            type |= g_typeadjust (ltype, rtype);
            Expr->Type = promoteint (Expr->Type, Expr2.Type);

            /* Generate code */
            Gen->Func (type, Expr->IVal);

            /* We have a rvalue in the primary now */
            ED_MakeRValExpr (Expr);

        } else {

            /* If the right hand side is constant, and the generator function
            ** expects the lhs in the primary, remove the push of the primary
            ** now.
            */
            unsigned rtype = TypeOf (Expr2.Type);
            type = 0;
            if (rconst) {
                /* Second value is constant - check for div */
                type |= CF_CONST;
                rtype |= CF_CONST;
                if (Tok == TOK_DIV && Expr2.IVal == 0) {
                    Error ("Division by zero");
                } else if (Tok == TOK_MOD && Expr2.IVal == 0) {
                    Error ("Modulo operation with zero");
                }
                if ((Gen->Flags & GEN_NOPUSH) != 0) {
                    RemoveCode (&Mark2);
                    ltype |= CF_REG;    /* Value is in register */
                }
            }

            /* Determine the type of the operation result. */
            type |= g_typeadjust (ltype, rtype);
            Expr->Type = promoteint (Expr->Type, Expr2.Type);

            /* Generate code */
            Gen->Func (type, Expr2.IVal);

            /* We have a rvalue in the primary now */
            ED_MakeRValExpr (Expr);
        }
    }
}



static void hie_compare (const GenDesc* Ops,    /* List of generators */
                         ExprDesc* Expr,
                         void (*hienext) (ExprDesc*))
/* Helper function for the compare operators */
{
    ExprDesc Expr2;
    CodeMark Mark0;
    CodeMark Mark1;
    CodeMark Mark2;
    const GenDesc* Gen;
    token_t Tok;                        /* The operator token */
    unsigned ltype;
    int rconst;                         /* Operand is a constant */


    GetCodePos (&Mark0);
    ExprWithCheck (hienext, Expr);

    while ((Gen = FindGen (CurTok.Tok, Ops)) != 0) {

        /* Remember the generator function */
        void (*GenFunc) (unsigned, unsigned long) = Gen->Func;

        /* Remember the operator token, then skip it */
        Tok = CurTok.Tok;
        NextToken ();

        /* If lhs is a function, convert it to pointer to function */
        if (IsTypeFunc (Expr->Type)) {
            Expr->Type = PointerTo (Expr->Type);
        }

        /* Get the lhs on stack */
        GetCodePos (&Mark1);
        ltype = TypeOf (Expr->Type);
        if (ED_IsConstAbs (Expr)) {
            /* Constant value */
            GetCodePos (&Mark2);
            g_push (ltype | CF_CONST, Expr->IVal);
        } else {
            /* Value not constant */
            LoadExpr (CF_NONE, Expr);
            GetCodePos (&Mark2);
            g_push (ltype, 0);
        }

        /* Get the right hand side */
        MarkedExprWithCheck (hienext, &Expr2);

        /* If rhs is a function, convert it to pointer to function */
        if (IsTypeFunc (Expr2.Type)) {
            Expr2.Type = PointerTo (Expr2.Type);
        }

        /* Check for a constant expression */
        rconst = (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2));
        if (!rconst) {
            /* Not constant, load into the primary */
            LoadExpr (CF_NONE, &Expr2);
        }

        /* Some operations aren't allowed on function pointers */
        if ((Gen->Flags & GEN_NOFUNC) != 0) {
            /* Output only one message even if both sides are wrong */
            if (IsTypeFuncPtr (Expr->Type)) {
                Error ("Invalid left operand for relational operator");
                /* Avoid further errors */
                ED_MakeConstAbsInt (Expr, 0);
                ED_MakeConstAbsInt (&Expr2, 0);
            } else if (IsTypeFuncPtr (Expr2.Type)) {
                Error ("Invalid right operand for relational operator");
                /* Avoid further errors */
                ED_MakeConstAbsInt (Expr, 0);
                ED_MakeConstAbsInt (&Expr2, 0);
            }
        }

        /* Make sure, the types are compatible */
        if (IsClassInt (Expr->Type)) {
            if (!IsClassInt (Expr2.Type) && !(IsClassPtr(Expr2.Type) && ED_IsNullPtr(Expr))) {
                Error ("Incompatible types");
            }
        } else if (IsClassPtr (Expr->Type)) {
            if (IsClassPtr (Expr2.Type)) {
                /* Both pointers are allowed in comparison if they point to
                ** the same type, or if one of them is a void pointer.
                */
                Type* left  = Indirect (Expr->Type);
                Type* right = Indirect (Expr2.Type);
                if (TypeCmp (left, right) < TC_QUAL_DIFF && left->C != T_VOID && right->C != T_VOID) {
                    /* Incompatible pointers */
                    Error ("Incompatible types");
                }
            } else if (!ED_IsNullPtr (&Expr2)) {
                Error ("Incompatible types");
            }
        }

        /* Check for const operands */
        if (ED_IsConstAbs (Expr) && rconst) {

            /* If the result is constant, this is suspicious when not in
            ** preprocessor mode.
            */
            WarnConstCompareResult ();

            /* Both operands are constant, remove the generated code */
            RemoveCode (&Mark1);

            /* Determine if this is a signed or unsigned compare */
            if (IsClassInt (Expr->Type) && IsSignSigned (Expr->Type) &&
                IsClassInt (Expr2.Type) && IsSignSigned (Expr2.Type)) {

                /* Evaluate the result for signed operands */
                signed long Val1 = Expr->IVal;
                signed long Val2 = Expr2.IVal;
                switch (Tok) {
                    case TOK_EQ: Expr->IVal = (Val1 == Val2);   break;
                    case TOK_NE: Expr->IVal = (Val1 != Val2);   break;
                    case TOK_LT: Expr->IVal = (Val1 < Val2);    break;
                    case TOK_LE: Expr->IVal = (Val1 <= Val2);   break;
                    case TOK_GE: Expr->IVal = (Val1 >= Val2);   break;
                    case TOK_GT: Expr->IVal = (Val1 > Val2);    break;
                    default:     Internal ("hie_compare: got token 0x%X\n", Tok);
                }

            } else {

                /* Evaluate the result for unsigned operands */
                unsigned long Val1 = Expr->IVal;
                unsigned long Val2 = Expr2.IVal;
                switch (Tok) {
                    case TOK_EQ: Expr->IVal = (Val1 == Val2);   break;
                    case TOK_NE: Expr->IVal = (Val1 != Val2);   break;
                    case TOK_LT: Expr->IVal = (Val1 < Val2);    break;
                    case TOK_LE: Expr->IVal = (Val1 <= Val2);   break;
                    case TOK_GE: Expr->IVal = (Val1 >= Val2);   break;
                    case TOK_GT: Expr->IVal = (Val1 > Val2);    break;
                    default:     Internal ("hie_compare: got token 0x%X\n", Tok);
                }
            }

        } else {

            /* Determine the signedness of the operands */
            int LeftSigned  = IsSignSigned (Expr->Type);
            int RightSigned = IsSignSigned (Expr2.Type);

            /* If the right hand side is constant, and the generator function
            ** expects the lhs in the primary, remove the push of the primary
            ** now.
            */
            unsigned flags = 0;
            if (rconst) {
                flags |= CF_CONST;
                if ((Gen->Flags & GEN_NOPUSH) != 0) {
                    RemoveCode (&Mark2);
                    ltype |= CF_REG;    /* Value is in register */
                }
            }

            /* Determine the type of the operation. */
            if (IsTypeChar (Expr->Type) && rconst) {

                /* Left side is unsigned char, right side is constant.
                ** Determine the minimum and maximum values
                */
                int LeftMin, LeftMax;
                if (LeftSigned) {
                    LeftMin = -128;
                    LeftMax = 127;
                } else {
                    LeftMin = 0;
                    LeftMax = 255;
                }
                /* An integer value is always represented as a signed in the
                ** ExprDesc structure. This may lead to false results below,
                ** if it is actually unsigned, but interpreted as signed
                ** because of the representation. Fortunately, in this case,
                ** the actual value doesn't matter, since it's always greater
                ** than what can be represented in a char. So correct the
                ** value accordingly.
                */
                if (!RightSigned && Expr2.IVal < 0) {
                    /* Correct the value so it is an unsigned. It will then
                    ** anyway match one of the cases below.
                    */
                    Expr2.IVal = LeftMax + 1;
                }

                /* Comparing a char against a constant may have a constant
                ** result. Please note: It is not possible to remove the code
                ** for the compare alltogether, because it may have side
                ** effects.
                */
                switch (Tok) {

                    case TOK_EQ:
                        if (Expr2.IVal < LeftMin || Expr2.IVal > LeftMax) {
                            ED_MakeConstAbsInt (Expr, 0);
                            WarnConstCompareResult ();
                            goto Done;
                        }
                        break;

                    case TOK_NE:
                        if (Expr2.IVal < LeftMin || Expr2.IVal > LeftMax) {
                            ED_MakeConstAbsInt (Expr, 1);
                            WarnConstCompareResult ();
                            goto Done;
                        }
                        break;

                    case TOK_LT:
                        if (Expr2.IVal <= LeftMin || Expr2.IVal > LeftMax) {
                            ED_MakeConstAbsInt (Expr, Expr2.IVal > LeftMax);
                            WarnConstCompareResult ();
                            goto Done;
                        }
                        break;

                    case TOK_LE:
                        if (Expr2.IVal < LeftMin || Expr2.IVal >= LeftMax) {
                            ED_MakeConstAbsInt (Expr, Expr2.IVal >= LeftMax);
                            WarnConstCompareResult ();
                            goto Done;
                        }
                        break;

                    case TOK_GE:
                        if (Expr2.IVal <= LeftMin || Expr2.IVal > LeftMax) {
                            ED_MakeConstAbsInt (Expr, Expr2.IVal <= LeftMin);
                            WarnConstCompareResult ();
                            goto Done;
                        }
                        break;

                    case TOK_GT:
                        if (Expr2.IVal < LeftMin || Expr2.IVal >= LeftMax) {
                            ED_MakeConstAbsInt (Expr, Expr2.IVal < LeftMin);
                            WarnConstCompareResult ();
                            goto Done;
                        }
                        break;

                    default:
                        Internal ("hie_compare: got token 0x%X\n", Tok);
                }

                /* If the result is not already constant (as evaluated in the
                ** switch above), we can execute the operation as a char op,
                ** since the right side constant is in a valid range.
                */
                flags |= (CF_CHAR | CF_FORCECHAR);
                if (!LeftSigned) {
                    flags |= CF_UNSIGNED;
                }

            } else if (IsTypeChar (Expr->Type) && IsTypeChar (Expr2.Type) &&
                GetSignedness (Expr->Type) == GetSignedness (Expr2.Type)) {

                /* Both are chars with the same signedness. We can encode the
                ** operation as a char operation.
                */
                flags |= CF_CHAR;
                if (rconst) {
                    flags |= CF_FORCECHAR;
                }
                if (!LeftSigned) {
                    flags |= CF_UNSIGNED;
                }
            } else {
                unsigned rtype = TypeOf (Expr2.Type) | (flags & CF_CONST);
                flags |= g_typeadjust (ltype, rtype);
            }

            /* If the left side is an unsigned and the right is a constant,
            ** we may be able to change the compares to something more
            ** effective.
            */
            if (!LeftSigned && rconst) {

                switch (Tok) {

                    case TOK_LT:
                        if (Expr2.IVal == 1) {
                            /* An unsigned compare to one means that the value
                            ** must be zero.
                            */
                            GenFunc = g_eq;
                            Expr2.IVal = 0;
                        }
                        break;

                    case TOK_LE:
                        if (Expr2.IVal == 0) {
                            /* An unsigned compare to zero means that the value
                            ** must be zero.
                            */
                            GenFunc = g_eq;
                        }
                        break;

                    case TOK_GE:
                        if (Expr2.IVal == 1) {
                            /* An unsigned compare to one means that the value
                            ** must not be zero.
                            */
                            GenFunc = g_ne;
                            Expr2.IVal = 0;
                        }
                        break;

                    case TOK_GT:
                        if (Expr2.IVal == 0) {
                            /* An unsigned compare to zero means that the value
                            ** must not be zero.
                            */
                            GenFunc = g_ne;
                        }
                        break;

                    default:
                        break;

                }

            }

            /* Generate code */
            GenFunc (flags, Expr2.IVal);

            /* The result is an rvalue in the primary */
            ED_MakeRValExpr (Expr);
        }

        /* Result type is always int */
        Expr->Type = type_int;

Done:   /* Condition codes are set */
        ED_TestDone (Expr);
    }
}



static void hie9 (ExprDesc *Expr)
/* Process * and / operators. */
{
    static const GenDesc hie9_ops[] = {
        { TOK_STAR,     GEN_NOPUSH | GEN_COMM,  g_mul   },
        { TOK_DIV,      GEN_NOPUSH,             g_div   },
        { TOK_MOD,      GEN_NOPUSH,             g_mod   },
        { TOK_INVALID,  0,                      0       }
    };
    int UsedGen;

    hie_internal (hie9_ops, Expr, hie10, &UsedGen);
}



static void parseadd (ExprDesc* Expr)
/* Parse an expression with the binary plus operator. Expr contains the
** unprocessed left hand side of the expression and will contain the
** result of the expression on return.
*/
{
    ExprDesc Expr2;
    unsigned flags;             /* Operation flags */
    CodeMark Mark;              /* Remember code position */
    Type* lhst;                 /* Type of left hand side */
    Type* rhst;                 /* Type of right hand side */

    /* Skip the PLUS token */
    NextToken ();

    /* Get the left hand side type, initialize operation flags */
    lhst = Expr->Type;
    flags = 0;

    /* Check for constness on both sides */
    if (ED_IsConst (Expr)) {

        /* The left hand side is a constant of some sort. Good. Get rhs */
        ExprWithCheck (hie9, &Expr2);
        if (ED_IsConstAbs (&Expr2)) {

            /* Right hand side is a constant numeric value. Get the rhs type */
            rhst = Expr2.Type;

            /* Both expressions are constants. Check for pointer arithmetic */
            if (IsClassPtr (lhst) && IsClassInt (rhst)) {
                /* Left is pointer, right is int, must scale rhs */
                Expr->IVal += Expr2.IVal * CheckedPSizeOf (lhst);
                /* Result type is a pointer */
            } else if (IsClassInt (lhst) && IsClassPtr (rhst)) {
                /* Left is int, right is pointer, must scale lhs */
                Expr->IVal = Expr->IVal * CheckedPSizeOf (rhst) + Expr2.IVal;
                /* Result type is a pointer */
                Expr->Type = Expr2.Type;
            } else if (IsClassInt (lhst) && IsClassInt (rhst)) {
                /* Integer addition */
                Expr->IVal += Expr2.IVal;
                typeadjust (Expr, &Expr2, 1);
            } else {
                /* OOPS */
                Error ("Invalid operands for binary operator '+'");
            }

        } else {

            /* lhs is a constant and rhs is not constant. Load rhs into
            ** the primary.
            */
            LoadExpr (CF_NONE, &Expr2);

            /* Beware: The check above (for lhs) lets not only pass numeric
            ** constants, but also constant addresses (labels), maybe even
            ** with an offset. We have to check for that here.
            */

            /* First, get the rhs type. */
            rhst = Expr2.Type;

            /* Setup flags */
            if (ED_IsLocAbs (Expr)) {
                /* A numerical constant */
                flags |= CF_CONST;
            } else {
                /* Constant address label */
                flags |= GlobalModeFlags (Expr) | CF_CONSTADDR;
            }

            /* Check for pointer arithmetic */
            if (IsClassPtr (lhst) && IsClassInt (rhst)) {
                /* Left is pointer, right is int, must scale rhs */
                g_scale (CF_INT, CheckedPSizeOf (lhst));
                /* Operate on pointers, result type is a pointer */
                flags |= CF_PTR;
                /* Generate the code for the add */
                if (ED_GetLoc (Expr) == E_LOC_ABS) {
                    /* Numeric constant */
                    g_inc (flags, Expr->IVal);
                } else {
                    /* Constant address */
                    g_addaddr_static (flags, Expr->Name, Expr->IVal);
                }
            } else if (IsClassInt (lhst) && IsClassPtr (rhst)) {

                /* Left is int, right is pointer, must scale lhs. */
                unsigned ScaleFactor = CheckedPSizeOf (rhst);

                /* Operate on pointers, result type is a pointer */
                flags |= CF_PTR;
                Expr->Type = Expr2.Type;

                /* Since we do already have rhs in the primary, if lhs is
                ** not a numeric constant, and the scale factor is not one
                ** (no scaling), we must take the long way over the stack.
                */
                if (ED_IsLocAbs (Expr)) {
                    /* Numeric constant, scale lhs */
                    Expr->IVal *= ScaleFactor;
                    /* Generate the code for the add */
                    g_inc (flags, Expr->IVal);
                } else if (ScaleFactor == 1) {
                    /* Constant address but no need to scale */
                    g_addaddr_static (flags, Expr->Name, Expr->IVal);
                } else {
                    /* Constant address that must be scaled */
                    g_push (TypeOf (Expr2.Type), 0);    /* rhs --> stack */
                    g_getimmed (flags, Expr->Name, Expr->IVal);
                    g_scale (CF_PTR, ScaleFactor);
                    g_add (CF_PTR, 0);
                }
            } else if (IsClassInt (lhst) && IsClassInt (rhst)) {
                /* Integer addition */
                flags |= typeadjust (Expr, &Expr2, 1);
                /* Generate the code for the add */
                if (ED_IsLocAbs (Expr)) {
                    /* Numeric constant */
                    g_inc (flags, Expr->IVal);
                } else {
                    /* Constant address */
                    g_addaddr_static (flags, Expr->Name, Expr->IVal);
                }
            } else {
                /* OOPS */
                Error ("Invalid operands for binary operator '+'");
                flags = CF_INT;
            }

            /* Result is a rvalue in primary register */
            ED_MakeRValExpr (Expr);
        }

    } else {

        /* Left hand side is not constant. Get the value onto the stack. */
        LoadExpr (CF_NONE, Expr);              /* --> primary register */
        GetCodePos (&Mark);
        g_push (TypeOf (Expr->Type), 0);        /* --> stack */

        /* Evaluate the rhs */
        MarkedExprWithCheck (hie9, &Expr2);

        /* Check for a constant rhs expression */
        if (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) {

            /* Right hand side is a constant. Get the rhs type */
            rhst = Expr2.Type;

            /* Remove pushed value from stack */
            RemoveCode (&Mark);

            /* Check for pointer arithmetic */
            if (IsClassPtr (lhst) && IsClassInt (rhst)) {
                /* Left is pointer, right is int, must scale rhs */
                Expr2.IVal *= CheckedPSizeOf (lhst);
                /* Operate on pointers, result type is a pointer */
                flags = CF_PTR;
            } else if (IsClassInt (lhst) && IsClassPtr (rhst)) {
                /* Left is int, right is pointer, must scale lhs (ptr only) */
                g_scale (CF_INT | CF_CONST, CheckedPSizeOf (rhst));
                /* Operate on pointers, result type is a pointer */
                flags = CF_PTR;
                Expr->Type = Expr2.Type;
            } else if (IsClassInt (lhst) && IsClassInt (rhst)) {
                /* Integer addition */
                flags = typeadjust (Expr, &Expr2, 1);
            } else {
                /* OOPS */
                Error ("Invalid operands for binary operator '+'");
                flags = CF_INT;
            }

            /* Generate code for the add */
            g_inc (flags | CF_CONST, Expr2.IVal);

        } else {

            /* Not constant, load into the primary */
            LoadExpr (CF_NONE, &Expr2);

            /* lhs and rhs are not constant. Get the rhs type. */
            rhst = Expr2.Type;

            /* Check for pointer arithmetic */
            if (IsClassPtr (lhst) && IsClassInt (rhst)) {
                /* Left is pointer, right is int, must scale rhs */
                g_scale (CF_INT, CheckedPSizeOf (lhst));
                /* Operate on pointers, result type is a pointer */
                flags = CF_PTR;
            } else if (IsClassInt (lhst) && IsClassPtr (rhst)) {
                /* Left is int, right is pointer, must scale lhs */
                g_tosint (TypeOf (lhst));       /* Make sure TOS is int */
                g_swap (CF_INT);                /* Swap TOS and primary */
                g_scale (CF_INT, CheckedPSizeOf (rhst));
                /* Operate on pointers, result type is a pointer */
                flags = CF_PTR;
                Expr->Type = Expr2.Type;
            } else if (IsClassInt (lhst) && IsClassInt (rhst)) {
                /* Integer addition. Note: Result is never constant.
                ** Problem here is that typeadjust does not know if the
                ** variable is an rvalue or lvalue, so if both operands
                ** are dereferenced constant numeric addresses, typeadjust
                ** thinks the operation works on constants. Removing
                ** CF_CONST here means handling the symptoms, however, the
                ** whole parser is such a mess that I fear to break anything
                ** when trying to apply another solution.
                */
                flags = typeadjust (Expr, &Expr2, 0) & ~CF_CONST;
            } else {
                /* OOPS */
                Error ("Invalid operands for binary operator '+'");
                flags = CF_INT;
            }

            /* Generate code for the add */
            g_add (flags, 0);

        }

        /* Result is a rvalue in primary register */
        ED_MakeRValExpr (Expr);
    }

    /* Condition codes not set */
    ED_MarkAsUntested (Expr);
}



static void parsesub (ExprDesc* Expr)
/* Parse an expression with the binary minus operator. Expr contains the
** unprocessed left hand side of the expression and will contain the
** result of the expression on return.
*/
{
    ExprDesc Expr2;
    unsigned flags;             /* Operation flags */
    Type* lhst;                 /* Type of left hand side */
    Type* rhst;                 /* Type of right hand side */
    CodeMark Mark1;             /* Save position of output queue */
    CodeMark Mark2;             /* Another position in the queue */
    int rscale;                 /* Scale factor for the result */


    /* lhs cannot be function or pointer to function */
    if (IsTypeFunc (Expr->Type) || IsTypeFuncPtr (Expr->Type)) {
        Error ("Invalid left operand for binary operator '-'");
        /* Make it pointer to char to avoid further errors */
        Expr->Type = type_uchar;
    }

    /* Skip the MINUS token */
    NextToken ();

    /* Get the left hand side type, initialize operation flags */
    lhst = Expr->Type;
    rscale = 1;                 /* Scale by 1, that is, don't scale */

    /* Remember the output queue position, then bring the value onto the stack */
    GetCodePos (&Mark1);
    LoadExpr (CF_NONE, Expr);  /* --> primary register */
    GetCodePos (&Mark2);
    g_push (TypeOf (lhst), 0);  /* --> stack */

    /* Parse the right hand side */
    MarkedExprWithCheck (hie9, &Expr2);

    /* rhs cannot be function or pointer to function */
    if (IsTypeFunc (Expr2.Type) || IsTypeFuncPtr (Expr2.Type)) {
        Error ("Invalid right operand for binary operator '-'");
        /* Make it pointer to char to avoid further errors */
        Expr2.Type = type_uchar;
    }

    /* Check for a constant rhs expression */
    if (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) {

        /* The right hand side is constant. Get the rhs type. */
        rhst = Expr2.Type;

        /* Check left hand side */
        if (ED_IsConstAbs (Expr)) {

            /* Both sides are constant, remove generated code */
            RemoveCode (&Mark1);

            /* Check for pointer arithmetic */
            if (IsClassPtr (lhst) && IsClassInt (rhst)) {
                /* Left is pointer, right is int, must scale rhs */
                Expr->IVal -= Expr2.IVal * CheckedPSizeOf (lhst);
                /* Operate on pointers, result type is a pointer */
            } else if (IsClassPtr (lhst) && IsClassPtr (rhst)) {
                /* Left is pointer, right is pointer, must scale result */
                if (TypeCmp (Indirect (lhst), Indirect (rhst)) < TC_QUAL_DIFF) {
                    Error ("Incompatible pointer types");
                } else {
                    Expr->IVal = (Expr->IVal - Expr2.IVal) /
                                      CheckedPSizeOf (lhst);
                }
                /* Operate on pointers, result type is an integer */
                Expr->Type = type_int;
            } else if (IsClassInt (lhst) && IsClassInt (rhst)) {
                /* Integer subtraction */
                typeadjust (Expr, &Expr2, 1);
                Expr->IVal -= Expr2.IVal;
            } else {
                /* OOPS */
                Error ("Invalid operands for binary operator '-'");
            }

            /* Result is constant, condition codes not set */
            ED_MarkAsUntested (Expr);

        } else {

            /* Left hand side is not constant, right hand side is.
            ** Remove pushed value from stack.
            */
            RemoveCode (&Mark2);

            if (IsClassPtr (lhst) && IsClassInt (rhst)) {
                /* Left is pointer, right is int, must scale rhs */
                Expr2.IVal *= CheckedPSizeOf (lhst);
                /* Operate on pointers, result type is a pointer */
                flags = CF_PTR;
            } else if (IsClassPtr (lhst) && IsClassPtr (rhst)) {
                /* Left is pointer, right is pointer, must scale result */
                if (TypeCmp (Indirect (lhst), Indirect (rhst)) < TC_QUAL_DIFF) {
                    Error ("Incompatible pointer types");
                } else {
                    rscale = CheckedPSizeOf (lhst);
                }
                /* Operate on pointers, result type is an integer */
                flags = CF_PTR;
                Expr->Type = type_int;
            } else if (IsClassInt (lhst) && IsClassInt (rhst)) {
                /* Integer subtraction */
                flags = typeadjust (Expr, &Expr2, 1);
            } else {
                /* OOPS */
                Error ("Invalid operands for binary operator '-'");
                flags = CF_INT;
            }

            /* Do the subtraction */
            g_dec (flags | CF_CONST, Expr2.IVal);

            /* If this was a pointer subtraction, we must scale the result */
            if (rscale != 1) {
                g_scale (flags, -rscale);
            }

            /* Result is a rvalue in the primary register */
            ED_MakeRValExpr (Expr);
            ED_MarkAsUntested (Expr);

        }

    } else {

        /* Not constant, load into the primary */
        LoadExpr (CF_NONE, &Expr2);

        /* Right hand side is not constant. Get the rhs type. */
        rhst = Expr2.Type;

        /* Check for pointer arithmetic */
        if (IsClassPtr (lhst) && IsClassInt (rhst)) {
            /* Left is pointer, right is int, must scale rhs */
            g_scale (CF_INT, CheckedPSizeOf (lhst));
            /* Operate on pointers, result type is a pointer */
            flags = CF_PTR;
        } else if (IsClassPtr (lhst) && IsClassPtr (rhst)) {
            /* Left is pointer, right is pointer, must scale result */
            if (TypeCmp (Indirect (lhst), Indirect (rhst)) < TC_QUAL_DIFF) {
                Error ("Incompatible pointer types");
            } else {
                rscale = CheckedPSizeOf (lhst);
            }
            /* Operate on pointers, result type is an integer */
            flags = CF_PTR;
            Expr->Type = type_int;
        } else if (IsClassInt (lhst) && IsClassInt (rhst)) {
            /* Integer subtraction. If the left hand side descriptor says that
            ** the lhs is const, we have to remove this mark, since this is no
            ** longer true, lhs is on stack instead.
            */
            if (ED_IsLocAbs (Expr)) {
                ED_MakeRValExpr (Expr);
            }
            /* Adjust operand types */
            flags = typeadjust (Expr, &Expr2, 0);
        } else {
            /* OOPS */
            Error ("Invalid operands for binary operator '-'");
            flags = CF_INT;
        }

        /* Generate code for the sub (the & is a hack here) */
        g_sub (flags & ~CF_CONST, 0);

        /* If this was a pointer subtraction, we must scale the result */
        if (rscale != 1) {
            g_scale (flags, -rscale);
        }

        /* Result is a rvalue in the primary register */
        ED_MakeRValExpr (Expr);
        ED_MarkAsUntested (Expr);
    }
}



void hie8 (ExprDesc* Expr)
/* Process + and - binary operators. */
{
    ExprWithCheck (hie9, Expr);
    while (CurTok.Tok == TOK_PLUS || CurTok.Tok == TOK_MINUS) {
        if (CurTok.Tok == TOK_PLUS) {
            parseadd (Expr);
        } else {
            parsesub (Expr);
        }
    }
}



static void hie6 (ExprDesc* Expr)
/* Handle greater-than type comparators */
{
    static const GenDesc hie6_ops [] = {
        { TOK_LT,       GEN_NOPUSH | GEN_NOFUNC,     g_lt    },
        { TOK_LE,       GEN_NOPUSH | GEN_NOFUNC,     g_le    },
        { TOK_GE,       GEN_NOPUSH | GEN_NOFUNC,     g_ge    },
        { TOK_GT,       GEN_NOPUSH | GEN_NOFUNC,     g_gt    },
        { TOK_INVALID,  0,                           0       }
    };
    hie_compare (hie6_ops, Expr, ShiftExpr);
}



static void hie5 (ExprDesc* Expr)
/* Handle == and != */
{
    static const GenDesc hie5_ops[] = {
        { TOK_EQ,       GEN_NOPUSH,     g_eq    },
        { TOK_NE,       GEN_NOPUSH,     g_ne    },
        { TOK_INVALID,  0,              0       }
    };
    hie_compare (hie5_ops, Expr, hie6);
}



static void hie4 (ExprDesc* Expr)
/* Handle & (bitwise and) */
{
    static const GenDesc hie4_ops[] = {
        { TOK_AND,      GEN_NOPUSH | GEN_COMM,  g_and   },
        { TOK_INVALID,  0,                      0       }
    };
    int UsedGen;

    hie_internal (hie4_ops, Expr, hie5, &UsedGen);
}



static void hie3 (ExprDesc* Expr)
/* Handle ^ (bitwise exclusive or) */
{
    static const GenDesc hie3_ops[] = {
        { TOK_XOR,      GEN_NOPUSH | GEN_COMM,  g_xor   },
        { TOK_INVALID,  0,                      0       }
    };
    int UsedGen;

    hie_internal (hie3_ops, Expr, hie4, &UsedGen);
}



static void hie2 (ExprDesc* Expr)
/* Handle | (bitwise or) */
{
    static const GenDesc hie2_ops[] = {
        { TOK_OR,       GEN_NOPUSH | GEN_COMM,  g_or    },
        { TOK_INVALID,  0,                      0       }
    };
    int UsedGen;

    hie_internal (hie2_ops, Expr, hie3, &UsedGen);
}



static void hieAndPP (ExprDesc* Expr)
/* Process "exp && exp" in preprocessor mode (that is, when the parser is
** called recursively from the preprocessor.
*/
{
    ExprDesc Expr2;

    ConstAbsIntExpr (hie2, Expr);
    while (CurTok.Tok == TOK_BOOL_AND) {

        /* Skip the && */
        NextToken ();

        /* Get rhs */
        ConstAbsIntExpr (hie2, &Expr2);

        /* Combine the two */
        Expr->IVal = (Expr->IVal && Expr2.IVal);
    }
}



static void hieOrPP (ExprDesc *Expr)
/* Process "exp || exp" in preprocessor mode (that is, when the parser is
** called recursively from the preprocessor.
*/
{
    ExprDesc Expr2;

    ConstAbsIntExpr (hieAndPP, Expr);
    while (CurTok.Tok == TOK_BOOL_OR) {

        /* Skip the && */
        NextToken ();

        /* Get rhs */
        ConstAbsIntExpr (hieAndPP, &Expr2);

        /* Combine the two */
        Expr->IVal = (Expr->IVal || Expr2.IVal);
    }
}



static void hieAnd (ExprDesc* Expr, unsigned TrueLab, int* BoolOp)
/* Process "exp && exp" */
{
    int FalseLab;
    ExprDesc Expr2;

    ExprWithCheck (hie2, Expr);
    if (CurTok.Tok == TOK_BOOL_AND) {

        /* Tell our caller that we're evaluating a boolean */
        *BoolOp = 1;

        /* Get a label that we will use for false expressions */
        FalseLab = GetLocalLabel ();

        /* If the expr hasn't set condition codes, set the force-test flag */
        if (!ED_IsTested (Expr)) {
            ED_MarkForTest (Expr);
        }

        /* Load the value */
        LoadExpr (CF_FORCECHAR, Expr);

        /* Generate the jump */
        g_falsejump (CF_NONE, FalseLab);

        /* Parse more boolean and's */
        while (CurTok.Tok == TOK_BOOL_AND) {

            /* Skip the && */
            NextToken ();

            /* Get rhs */
            hie2 (&Expr2);
            if (!ED_IsTested (&Expr2)) {
                ED_MarkForTest (&Expr2);
            }
            LoadExpr (CF_FORCECHAR, &Expr2);

            /* Do short circuit evaluation */
            if (CurTok.Tok == TOK_BOOL_AND) {
                g_falsejump (CF_NONE, FalseLab);
            } else {
                /* Last expression - will evaluate to true */
                g_truejump (CF_NONE, TrueLab);
            }
        }

        /* Define the false jump label here */
        g_defcodelabel (FalseLab);

        /* The result is an rvalue in primary */
        ED_MakeRValExpr (Expr);
        ED_TestDone (Expr);     /* Condition codes are set */
    }
}



static void hieOr (ExprDesc *Expr)
/* Process "exp || exp". */
{
    ExprDesc Expr2;
    int BoolOp = 0;             /* Did we have a boolean op? */
    int AndOp;                  /* Did we have a && operation? */
    unsigned TrueLab;           /* Jump to this label if true */
    unsigned DoneLab;

    /* Get a label */
    TrueLab = GetLocalLabel ();

    /* Call the next level parser */
    hieAnd (Expr, TrueLab, &BoolOp);

    /* Any boolean or's? */
    if (CurTok.Tok == TOK_BOOL_OR) {

        /* If the expr hasn't set condition codes, set the force-test flag */
        if (!ED_IsTested (Expr)) {
            ED_MarkForTest (Expr);
        }

        /* Get first expr */
        LoadExpr (CF_FORCECHAR, Expr);

        /* For each expression jump to TrueLab if true. Beware: If we
        ** had && operators, the jump is already in place!
        */
        if (!BoolOp) {
            g_truejump (CF_NONE, TrueLab);
        }

        /* Remember that we had a boolean op */
        BoolOp = 1;

        /* while there's more expr */
        while (CurTok.Tok == TOK_BOOL_OR) {

            /* skip the || */
            NextToken ();

            /* Get a subexpr */
            AndOp = 0;
            hieAnd (&Expr2, TrueLab, &AndOp);
            if (!ED_IsTested (&Expr2)) {
                ED_MarkForTest (&Expr2);
            }
            LoadExpr (CF_FORCECHAR, &Expr2);

            /* If there is more to come, add shortcut boolean eval. */
            g_truejump (CF_NONE, TrueLab);

        }

        /* The result is an rvalue in primary */
        ED_MakeRValExpr (Expr);
        ED_TestDone (Expr);                     /* Condition codes are set */
    }

    /* If we really had boolean ops, generate the end sequence */
    if (BoolOp) {
        DoneLab = GetLocalLabel ();
        g_getimmed (CF_INT | CF_CONST, 0, 0);   /* Load FALSE */
        g_falsejump (CF_NONE, DoneLab);
        g_defcodelabel (TrueLab);
        g_getimmed (CF_INT | CF_CONST, 1, 0);   /* Load TRUE */
        g_defcodelabel (DoneLab);
    }
}



static void hieQuest (ExprDesc* Expr)
/* Parse the ternary operator */
{
    int         FalseLab;
    int         TrueLab;
    CodeMark    TrueCodeEnd;
    ExprDesc    Expr2;          /* Expression 2 */
    ExprDesc    Expr3;          /* Expression 3 */
    int         Expr2IsNULL;    /* Expression 2 is a NULL pointer */
    int         Expr3IsNULL;    /* Expression 3 is a NULL pointer */
    Type*       ResultType;     /* Type of result */


    /* Call the lower level eval routine */
    if (Preprocessing) {
        ExprWithCheck (hieOrPP, Expr);
    } else {
        ExprWithCheck (hieOr, Expr);
    }

    /* Check if it's a ternary expression */
    if (CurTok.Tok == TOK_QUEST) {
        NextToken ();
        if (!ED_IsTested (Expr)) {
            /* Condition codes not set, request a test */
            ED_MarkForTest (Expr);
        }
        LoadExpr (CF_NONE, Expr);
        FalseLab = GetLocalLabel ();
        g_falsejump (CF_NONE, FalseLab);

        /* Parse second expression. Remember for later if it is a NULL pointer
        ** expression, then load it into the primary.
        */
        ExprWithCheck (hie1, &Expr2);
        Expr2IsNULL = ED_IsNullPtr (&Expr2);
        if (!IsTypeVoid (Expr2.Type)) {
            /* Load it into the primary */
            LoadExpr (CF_NONE, &Expr2);
            ED_MakeRValExpr (&Expr2);
            Expr2.Type = PtrConversion (Expr2.Type);
        }

        /* Remember the current code position */
        GetCodePos (&TrueCodeEnd);

        /* Jump around the evaluation of the third expression */
        TrueLab = GetLocalLabel ();
        ConsumeColon ();
        g_jump (TrueLab);

        /* Jump here if the first expression was false */
        g_defcodelabel (FalseLab);

        /* Parse third expression. Remember for later if it is a NULL pointer
        ** expression, then load it into the primary.
        */
        ExprWithCheck (hie1, &Expr3);
        Expr3IsNULL = ED_IsNullPtr (&Expr3);
        if (!IsTypeVoid (Expr3.Type)) {
            /* Load it into the primary */
            LoadExpr (CF_NONE, &Expr3);
            ED_MakeRValExpr (&Expr3);
            Expr3.Type = PtrConversion (Expr3.Type);
        }

        /* Check if any conversions are needed, if so, do them.
        ** Conversion rules for ?: expression are:
        **   - if both expressions are int expressions, default promotion
        **     rules for ints apply.
        **   - if both expressions are pointers of the same type, the
        **     result of the expression is of this type.
        **   - if one of the expressions is a pointer and the other is
        **     a zero constant, the resulting type is that of the pointer
        **     type.
        **   - if both expressions are void expressions, the result is of
        **     type void.
        **   - all other cases are flagged by an error.
        */
        if (IsClassInt (Expr2.Type) && IsClassInt (Expr3.Type)) {

            CodeMark    CvtCodeStart;
            CodeMark    CvtCodeEnd;


            /* Get common type */
            ResultType = promoteint (Expr2.Type, Expr3.Type);

            /* Convert the third expression to this type if needed */
            TypeConversion (&Expr3, ResultType);

            /* Emit conversion code for the second expression, but remember
            ** where it starts end ends.
            */
            GetCodePos (&CvtCodeStart);
            TypeConversion (&Expr2, ResultType);
            GetCodePos (&CvtCodeEnd);

            /* If we had conversion code, move it to the right place */
            if (!CodeRangeIsEmpty (&CvtCodeStart, &CvtCodeEnd)) {
                MoveCode (&CvtCodeStart, &CvtCodeEnd, &TrueCodeEnd);
            }

        } else if (IsClassPtr (Expr2.Type) && IsClassPtr (Expr3.Type)) {
            /* Must point to same type */
            if (TypeCmp (Indirect (Expr2.Type), Indirect (Expr3.Type)) < TC_EQUAL) {
                Error ("Incompatible pointer types");
            }
            /* Result has the common type */
            ResultType = Expr2.Type;
        } else if (IsClassPtr (Expr2.Type) && Expr3IsNULL) {
            /* Result type is pointer, no cast needed */
            ResultType = Expr2.Type;
        } else if (Expr2IsNULL && IsClassPtr (Expr3.Type)) {
            /* Result type is pointer, no cast needed */
            ResultType = Expr3.Type;
        } else if (IsTypeVoid (Expr2.Type) && IsTypeVoid (Expr3.Type)) {
            /* Result type is void */
            ResultType = Expr3.Type;
        } else {
            Error ("Incompatible types");
            ResultType = Expr2.Type;            /* Doesn't matter here */
        }

        /* Define the final label */
        g_defcodelabel (TrueLab);

        /* Setup the target expression */
        ED_MakeRValExpr (Expr);
        Expr->Type  = ResultType;
    }
}



static void opeq (const GenDesc* Gen, ExprDesc* Expr, const char* Op)
/* Process "op=" operators. */
{
    ExprDesc Expr2;
    unsigned flags;
    CodeMark Mark;
    int MustScale;

    /* op= can only be used with lvalues */
    if (!ED_IsLVal (Expr)) {
        Error ("Invalid lvalue in assignment");
        return;
    }

    /* The left side must not be const qualified */
    if (IsQualConst (Expr->Type)) {
        Error ("Assignment to const");
    }

    /* There must be an integer or pointer on the left side */
    if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) {
        Error ("Invalid left operand type");
        /* Continue. Wrong code will be generated, but the compiler won't
        ** break, so this is the best error recovery.
        */
    }

    /* Skip the operator token */
    NextToken ();

    /* Determine the type of the lhs */
    flags = TypeOf (Expr->Type);
    MustScale = (Gen->Func == g_add || Gen->Func == g_sub) && IsTypePtr (Expr->Type);

    /* Get the lhs address on stack (if needed) */
    PushAddr (Expr);

    /* Fetch the lhs into the primary register if needed */
    LoadExpr (CF_NONE, Expr);

    /* Bring the lhs on stack */
    GetCodePos (&Mark);
    g_push (flags, 0);

    /* Evaluate the rhs */
    MarkedExprWithCheck (hie1, &Expr2);

    /* The rhs must be an integer (or a float, but we don't support that yet */
    if (!IsClassInt (Expr2.Type)) {
        Error ("Invalid right operand for binary operator '%s'", Op);
        /* Continue. Wrong code will be generated, but the compiler won't
        ** break, so this is the best error recovery.
        */
    }

    /* Check for a constant expression */
    if (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) {
        /* The resulting value is a constant. If the generator has the NOPUSH
        ** flag set, don't push the lhs.
        */
        if (Gen->Flags & GEN_NOPUSH) {
            RemoveCode (&Mark);
        }
        if (MustScale) {
            /* lhs is a pointer, scale rhs */
            Expr2.IVal *= CheckedSizeOf (Expr->Type+1);
        }

        /* If the lhs is character sized, the operation may be later done
        ** with characters.
        */
        if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) {
            flags |= CF_FORCECHAR;
        }

        /* Special handling for add and sub - some sort of a hack, but short code */
        if (Gen->Func == g_add) {
            g_inc (flags | CF_CONST, Expr2.IVal);
        } else if (Gen->Func == g_sub) {
            g_dec (flags | CF_CONST, Expr2.IVal);
        } else {
            if (Expr2.IVal == 0) {
                /* Check for div by zero/mod by zero */
                if (Gen->Func == g_div) {
                    Error ("Division by zero");
                } else if (Gen->Func == g_mod) {
                    Error ("Modulo operation with zero");
                }
            }
            Gen->Func (flags | CF_CONST, Expr2.IVal);
        }
    } else {

        /* rhs is not constant. Load into the primary */
        LoadExpr (CF_NONE, &Expr2);
        if (MustScale) {
            /* lhs is a pointer, scale rhs */
            g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Expr->Type+1));
        }

        /* If the lhs is character sized, the operation may be later done
        ** with characters.
        */
        if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) {
            flags |= CF_FORCECHAR;
        }

        /* Adjust the types of the operands if needed */
        Gen->Func (g_typeadjust (flags, TypeOf (Expr2.Type)), 0);
    }
    Store (Expr, 0);
    ED_MakeRValExpr (Expr);
}



static void addsubeq (const GenDesc* Gen, ExprDesc *Expr, const char* Op)
/* Process the += and -= operators */
{
    ExprDesc Expr2;
    unsigned lflags;
    unsigned rflags;
    int      MustScale;


    /* We're currently only able to handle some adressing modes */
    if (ED_GetLoc (Expr) == E_LOC_EXPR || ED_GetLoc (Expr) == E_LOC_PRIMARY) {
        /* Use generic routine */
        opeq (Gen, Expr, Op);
        return;
    }

    /* We must have an lvalue */
    if (ED_IsRVal (Expr)) {
        Error ("Invalid lvalue in assignment");
        return;
    }

    /* The left side must not be const qualified */
    if (IsQualConst (Expr->Type)) {
        Error ("Assignment to const");
    }

    /* There must be an integer or pointer on the left side */
    if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) {
        Error ("Invalid left operand type");
        /* Continue. Wrong code will be generated, but the compiler won't
        ** break, so this is the best error recovery.
        */
    }

    /* Skip the operator */
    NextToken ();

    /* Check if we have a pointer expression and must scale rhs */
    MustScale = IsTypePtr (Expr->Type);

    /* Initialize the code generator flags */
    lflags = 0;
    rflags = 0;

    /* Evaluate the rhs. We expect an integer here, since float is not
    ** supported
    */
    hie1 (&Expr2);
    if (!IsClassInt (Expr2.Type)) {
        Error ("Invalid right operand for binary operator '%s'", Op);
        /* Continue. Wrong code will be generated, but the compiler won't
        ** break, so this is the best error recovery.
        */
    }
    if (ED_IsConstAbs (&Expr2)) {
        /* The resulting value is a constant. Scale it. */
        if (MustScale) {
            Expr2.IVal *= CheckedSizeOf (Indirect (Expr->Type));
        }
        rflags |= CF_CONST;
        lflags |= CF_CONST;
    } else {
        /* Not constant, load into the primary */
        LoadExpr (CF_NONE, &Expr2);
        if (MustScale) {
            /* lhs is a pointer, scale rhs */
            g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Indirect (Expr->Type)));
        }
    }

    /* Setup the code generator flags */
    lflags |= TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR;
    rflags |= TypeOf (Expr2.Type) | CF_FORCECHAR;

    /* Convert the type of the lhs to that of the rhs */
    g_typecast (lflags, rflags);

    /* Output apropriate code depending on the location */
    switch (ED_GetLoc (Expr)) {

        case E_LOC_ABS:
            /* Absolute: numeric address or const */
            if (Gen->Tok == TOK_PLUS_ASSIGN) {
                g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal);
            } else {
                g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal);
            }
            break;

        case E_LOC_GLOBAL:
            /* Global variable */
            if (Gen->Tok == TOK_PLUS_ASSIGN) {
                g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal);
            } else {
                g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal);
            }
            break;

        case E_LOC_STATIC:
        case E_LOC_LITERAL:
            /* Static variable or literal in the literal pool */
            if (Gen->Tok == TOK_PLUS_ASSIGN) {
                g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal);
            } else {
                g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal);
            }
            break;

        case E_LOC_REGISTER:
            /* Register variable */
            if (Gen->Tok == TOK_PLUS_ASSIGN) {
                g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal);
            } else {
                g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal);
            }
            break;

        case E_LOC_STACK:
            /* Value on the stack */
            if (Gen->Tok == TOK_PLUS_ASSIGN) {
                g_addeqlocal (lflags, Expr->IVal, Expr2.IVal);
            } else {
                g_subeqlocal (lflags, Expr->IVal, Expr2.IVal);
            }
            break;

        default:
            Internal ("Invalid location in Store(): 0x%04X", ED_GetLoc (Expr));
    }

    /* Expression is a rvalue in the primary now */
    ED_MakeRValExpr (Expr);
}



void hie1 (ExprDesc* Expr)
/* Parse first level of expression hierarchy. */
{
    hieQuest (Expr);
    switch (CurTok.Tok) {

        case TOK_ASSIGN:
            Assignment (Expr);
            break;

        case TOK_PLUS_ASSIGN:
            addsubeq (&GenPASGN, Expr, "+=");
            break;

        case TOK_MINUS_ASSIGN:
            addsubeq (&GenSASGN, Expr, "-=");
            break;

        case TOK_MUL_ASSIGN:
            opeq (&GenMASGN, Expr, "*=");
            break;

        case TOK_DIV_ASSIGN:
            opeq (&GenDASGN, Expr, "/=");
            break;

        case TOK_MOD_ASSIGN:
            opeq (&GenMOASGN, Expr, "%=");
            break;

        case TOK_SHL_ASSIGN:
            opeq (&GenSLASGN, Expr, "<<=");
            break;

        case TOK_SHR_ASSIGN:
            opeq (&GenSRASGN, Expr, ">>=");
            break;

        case TOK_AND_ASSIGN:
            opeq (&GenAASGN, Expr, "&=");
            break;

        case TOK_XOR_ASSIGN:
            opeq (&GenXOASGN, Expr, "^=");
            break;

        case TOK_OR_ASSIGN:
            opeq (&GenOASGN, Expr, "|=");
            break;

        default:
            break;
    }
}



void hie0 (ExprDesc *Expr)
/* Parse comma operator. */
{
    hie1 (Expr);
    while (CurTok.Tok == TOK_COMMA) {
        NextToken ();
        hie1 (Expr);
    }
}



int evalexpr (unsigned Flags, void (*Func) (ExprDesc*), ExprDesc* Expr)
/* Will evaluate an expression via the given function. If the result is a
** constant, 0 is returned and the value is put in the Expr struct. If the
** result is not constant, LoadExpr is called to bring the value into the
** primary register and 1 is returned.
*/
{
    /* Evaluate */
    ExprWithCheck (Func, Expr);

    /* Check for a constant expression */
    if (ED_IsConstAbs (Expr)) {
        /* Constant expression */
        return 0;
    } else {
        /* Not constant, load into the primary */
        LoadExpr (Flags, Expr);
        return 1;
    }
}



void Expression0 (ExprDesc* Expr)
/* Evaluate an expression via hie0 and put the result into the primary register */
{
    ExprWithCheck (hie0, Expr);
    LoadExpr (CF_NONE, Expr);
}



void ConstExpr (void (*Func) (ExprDesc*), ExprDesc* Expr)
/* Will evaluate an expression via the given function. If the result is not
** a constant of some sort, a diagnostic will be printed, and the value is
** replaced by a constant one to make sure there are no internal errors that
** result from this input error.
*/
{
    ExprWithCheck (Func, Expr);
    if (!ED_IsConst (Expr)) {
        Error ("Constant expression expected");
        /* To avoid any compiler errors, make the expression a valid const */
        ED_MakeConstAbsInt (Expr, 1);
    }
}



void BoolExpr (void (*Func) (ExprDesc*), ExprDesc* Expr)
/* Will evaluate an expression via the given function. If the result is not
** something that may be evaluated in a boolean context, a diagnostic will be
** printed, and the value is replaced by a constant one to make sure there
** are no internal errors that result from this input error.
*/
{
    ExprWithCheck (Func, Expr);
    if (!ED_IsBool (Expr)) {
        Error ("Boolean expression expected");
        /* To avoid any compiler errors, make the expression a valid int */
        ED_MakeConstAbsInt (Expr, 1);
    }
}



void ConstAbsIntExpr (void (*Func) (ExprDesc*), ExprDesc* Expr)
/* Will evaluate an expression via the given function. If the result is not
** a constant numeric integer value, a diagnostic will be printed, and the
** value is replaced by a constant one to make sure there are no internal
** errors that result from this input error.
*/
{
    ExprWithCheck (Func, Expr);
    if (!ED_IsConstAbsInt (Expr)) {
        Error ("Constant integer expression expected");
        /* To avoid any compiler errors, make the expression a valid const */
        ED_MakeConstAbsInt (Expr, 1);
    }
}
