/*****************************************************************************/
/*                                                                           */
/*                                 swstmt.c                                  */
/*                                                                           */
/*                        Parse the switch statement                         */
/*                                                                           */
/*                                                                           */
/*                                                                           */
/* (C) 1998-2008 Ullrich von Bassewitz                                       */
/*               Roemerstrasse 52                                            */
/*               D-70794 Filderstadt                                         */
/* EMail:        uz@cc65.org                                                 */
/*                                                                           */
/*                                                                           */
/* This software is provided 'as-is', without any expressed or implied       */
/* warranty.  In no event will the authors be held liable for any damages    */
/* arising from the use of this software.                                    */
/*                                                                           */
/* Permission is granted to anyone to use this software for any purpose,     */
/* including commercial applications, and to alter it and redistribute it    */
/* freely, subject to the following restrictions:                            */
/*                                                                           */
/* 1. The origin of this software must not be misrepresented; you must not   */
/*    claim that you wrote the original software. If you use this software   */
/*    in a product, an acknowledgment in the product documentation would be  */
/*    appreciated but is not required.                                       */
/* 2. Altered source versions must be plainly marked as such, and must not   */
/*    be misrepresented as being the original software.                      */
/* 3. This notice may not be removed or altered from any source              */
/*    distribution.                                                          */
/*                                                                           */
/*****************************************************************************/



#include <limits.h>

/* common */
#include "coll.h"
#include "xmalloc.h"

/* cc65 */
#include "asmcode.h"
#include "asmlabel.h"
#include "casenode.h"
#include "codegen.h"
#include "datatype.h"
#include "error.h"
#include "expr.h"
#include "global.h"
#include "loop.h"
#include "scanner.h"
#include "stmt.h"
#include "swstmt.h"



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



typedef struct SwitchCtrl SwitchCtrl;
struct SwitchCtrl {
    Collection* Nodes;          /* CaseNode tree */
    TypeCode    ExprType;       /* Basic switch expression type */
    unsigned    Depth;          /* Number of bytes the selector type has */
    unsigned    DefaultLabel;   /* Label for the default branch */



};

/* Pointer to current switch control struct */
static SwitchCtrl* Switch = 0;



/*****************************************************************************/
/*                                   Code                                    */
/*****************************************************************************/



void SwitchStatement (void)
/* Handle a switch statement for chars with a cmp cascade for the selector */
{
    ExprDesc    SwitchExpr;     /* Switch statement expression */
    CodeMark    CaseCodeStart;  /* Start of code marker */
    CodeMark    SwitchCodeStart;/* Start of switch code */
    CodeMark    SwitchCodeEnd;  /* End of switch code */
    unsigned    ExitLabel;      /* Exit label */
    unsigned    SwitchCodeLabel;/* Label for the switch code */
    int         HaveBreak = 0;  /* True if the last statement had a break */
    int         RCurlyBrace;    /* True if last token is right curly brace */
    SwitchCtrl* OldSwitch;      /* Pointer to old switch control data */
    SwitchCtrl  SwitchData;     /* New switch data */


    /* Eat the "switch" token */
    NextToken ();

    /* Read the switch expression and load it into the primary. It must have
    ** integer type.
    */
    ConsumeLParen ();
    Expression0 (&SwitchExpr);
    if (!IsClassInt (SwitchExpr.Type))  {
        Error ("Switch quantity is not an integer");
        /* To avoid any compiler errors, make the expression a valid int */
        ED_MakeConstAbsInt (&SwitchExpr, 1);
    }
    ConsumeRParen ();

    /* Add a jump to the switch code. This jump is usually unnecessary,
    ** because the switch code will moved up just behind the switch
    ** expression. However, in rare cases, there's a label at the end of
    ** the switch expression. This label will not get moved, so the code
    ** jumps around the switch code, and after moving the switch code,
    ** things look really weird. If we add a jump here, we will never have
    ** a label attached to the current code position, and the jump itself
    ** will get removed by the optimizer if it is unnecessary.
    */
    SwitchCodeLabel = GetLocalLabel ();
    g_jump (SwitchCodeLabel);

    /* Remember the current code position. We will move the switch code
    ** to this position later.
    */
    GetCodePos (&CaseCodeStart);

    /* Setup the control structure, save the old and activate the new one */
    SwitchData.Nodes        = NewCollection ();
    SwitchData.ExprType     = UnqualifiedType (SwitchExpr.Type[0].C);
    SwitchData.Depth        = SizeOf (SwitchExpr.Type);
    SwitchData.DefaultLabel = 0;
    OldSwitch = Switch;
    Switch = &SwitchData;

    /* Get the exit label for the switch statement */
    ExitLabel = GetLocalLabel ();

    /* Create a loop so we may use break. */
    AddLoop (ExitLabel, 0);

    /* Parse the following statement, which may actually be a compound
    ** statement if there is a curly brace at the current input position
    */
    HaveBreak = Statement (&RCurlyBrace);

    /* Check if we had any labels */
    if (CollCount (SwitchData.Nodes) == 0 && SwitchData.DefaultLabel == 0) {
        Warning ("No case labels");
    }

    /* If the last statement did not have a break, we may have an open
    ** label (maybe from an if or similar). Emitting code and then moving
    ** this code to the top will also move the label to the top which is
    ** wrong. So if the last statement did not have a break (which would
    ** carry the label), add a jump to the exit. If it is useless, the
    ** optimizer will remove it later.
    */
    if (!HaveBreak) {
        g_jump (ExitLabel);
    }

    /* Remember the current position */
    GetCodePos (&SwitchCodeStart);

    /* Output the switch code label */
    g_defcodelabel (SwitchCodeLabel);

    /* Generate code */
    if (SwitchData.DefaultLabel == 0) {
        /* No default label, use switch exit */
        SwitchData.DefaultLabel = ExitLabel;
    }
    g_switch (SwitchData.Nodes, SwitchData.DefaultLabel, SwitchData.Depth);

    /* Move the code to the front */
    GetCodePos (&SwitchCodeEnd);
    MoveCode (&SwitchCodeStart, &SwitchCodeEnd, &CaseCodeStart);

    /* Define the exit label */
    g_defcodelabel (ExitLabel);

    /* Exit the loop */
    DelLoop ();

    /* Switch back to the enclosing switch statement if any */
    Switch = OldSwitch;

    /* Free the case value tree */
    FreeCaseNodeColl (SwitchData.Nodes);

    /* If the case statement was terminated by a closing curly
    ** brace, skip it now.
    */
    if (RCurlyBrace) {
        NextToken ();
    }
}



void CaseLabel (void)
/* Handle a case sabel */
{
    ExprDesc CaseExpr;          /* Case label expression */
    long     Val;               /* Case label value */
    unsigned CodeLabel;         /* Code label for this case */


    /* Skip the "case" token */
    NextToken ();

    /* Read the selector expression */
    ConstAbsIntExpr (hie1, &CaseExpr);
    Val = CaseExpr.IVal;

    /* Now check if we're inside a switch statement */
    if (Switch != 0) {

        /* Check the range of the expression */
        switch (Switch->ExprType) {

            case T_SCHAR:
                /* Signed char */
                if (Val < -128 || Val > 127) {
                    Error ("Range error");
                }
                break;

            case T_UCHAR:
                if (Val < 0 || Val > 255) {
                    Error ("Range error");
                }
                break;

            case T_SHORT:
            case T_INT:
                if (Val < -32768 || Val > 32767) {
                    Error ("Range error");
                }
                break;

            case T_USHORT:
            case T_UINT:
                if (Val < 0 || Val > 65535) {
                    Error ("Range error");
                }
                break;

            case T_LONG:
            case T_ULONG:
                break;

            default:
                Internal ("Invalid type: %06lX", Switch->ExprType);
        }

        /* Insert the case selector into the selector table */
        CodeLabel = InsertCaseValue (Switch->Nodes, Val, Switch->Depth);

        /* Define this label */
        g_defcodelabel (CodeLabel);

    } else {

        /* case keyword outside a switch statement */
        Error ("Case label not within a switch statement");

    }

    /* Skip the colon */
    ConsumeColon ();
}



void DefaultLabel (void)
/* Handle a default label */
{
    /* Default case */
    NextToken ();

    /* Now check if we're inside a switch statement */
    if (Switch != 0) {

        /* Check if we do already have a default branch */
        if (Switch->DefaultLabel == 0) {

            /* Generate and emit the default label */
            Switch->DefaultLabel = GetLocalLabel ();
            g_defcodelabel (Switch->DefaultLabel);

        } else {
            /* We had the default label already */
            Error ("Multiple default labels in one switch");
        }

    } else {

        /* case keyword outside a switch statement */
        Error ("'default' label not within a switch statement");

    }

    /* Skip the colon */
    ConsumeColon ();
}
