/*****************************************************************************/
/* */
-/* swstmt.c */
+/* swstmt.c */
/* */
-/* Parse the switch statement */
+/* Parse the switch statement */
/* */
/* */
/* */
-/* (C) 1998-2001 Ullrich von Bassewitz */
-/* Wacholderweg 14 */
-/* D-70597 Stuttgart */
+/* (C) 1998-2008 Ullrich von Bassewitz */
+/* Roemerstrasse 52 */
+/* D-70794 Filderstadt */
/* EMail: uz@cc65.org */
/* */
/* */
/*****************************************************************************/
-/* Code */
+/* 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 */
{
- Collection* Nodes; /* CaseNode tree */
- ExprDesc SwitchExpr; /* Switch statement expression */
- ExprDesc CaseExpr; /* Case label expression */
- type SwitchExprType; /* Basic switch expression type */
- CodeMark CaseCodeStart; /* Start of code marker */
- unsigned Depth; /* Number of bytes the selector type has */
- unsigned ExitLabel; /* Exit label */
- unsigned CaseLabel; /* Label for case */
- unsigned DefaultLabel; /* Label for the default branch */
- long Val; /* Case label value */
+ 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 */
+ /* Read the switch expression and load it into the primary. It must have
+ * integer type.
+ */
ConsumeLParen ();
- intexpr (&SwitchExpr);
+ 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 ();
- /* Opening curly brace */
- ConsumeLCurly ();
-
- /* Remember the current code position */
- CaseCodeStart = GetCodePos();
-
- /* Get the unqualified type of the switch expression */
- SwitchExprType = UnqualifiedType (SwitchExpr.Type[0]);
-
- /* Get the number of bytes the selector type has */
- Depth = SizeOf (SwitchExpr.Type);
- CHECK (Depth == 1 || Depth == 2 || Depth == 4);
+ /* 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 (oursp, 0, ExitLabel, 0, 0);
-
- /* Create the collection for the case node tree */
- Nodes = NewCollection ();
-
- /* Clear the label for the default branch */
- DefaultLabel = 0;
-
- /* Parse the labels */
- while (CurTok.Tok != TOK_RCURLY) {
-
- while (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT) {
-
- /* Parse the selector */
- if (CurTok.Tok == TOK_CASE) {
-
- /* Skip the "case" token */
- NextToken ();
-
- /* Read the selector expression */
- constexpr (&CaseExpr);
- if (!IsClassInt (CaseExpr.Type)) {
- Error ("Switch quantity not an integer");
- }
-
- /* Check the range of the expression */
- Val = CaseExpr.ConstVal;
- switch (SwitchExprType) {
-
- 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: %04X", SwitchExprType);
- }
-
- /* Insert the case selector into the selector table */
- CaseLabel = InsertCaseValue (Nodes, Val, Depth);
-
- /* Define this label */
- g_defcodelabel (CaseLabel);
-
- /* Skip the colon */
- ConsumeColon ();
-
- } else {
-
- /* Default case */
- NextToken ();
-
- /* Check if we do already have a default branch */
- if (DefaultLabel == 0) {
-
- /* Generate and emit the default label */
- DefaultLabel = GetLocalLabel ();
- g_defcodelabel (DefaultLabel);
-
- } else {
- /* We had the default label already */
- Error ("Duplicate `default' case");
- }
-
- /* Skip the colon */
- ConsumeColon ();
-
- }
-
- }
-
- /* Parse statements */
- if (CurTok.Tok != TOK_RCURLY) {
- Statement (0);
- }
- }
+ AddLoop (ExitLabel, 0);
- /* Check if we had any labels */
- if (CollCount (Nodes) == 0 && DefaultLabel == 0) {
+ /* Make sure a curly brace follows */
+ if (CurTok.Tok != TOK_LCURLY) {
+ Error ("`{' expected");
+ }
- Warning ("No case labels");
+ /* Parse the following statement, which will actually be a compound
+ * statement because of the curly brace at the current input position
+ */
+ HaveBreak = Statement (&RCurlyBrace);
- } else {
+ /* Check if we had any labels */
+ if (CollCount (SwitchData.Nodes) == 0 && SwitchData.DefaultLabel == 0) {
+ Warning ("No case labels");
+ }
- /* Remember the current position */
- CodeMark SwitchCodeStart = GetCodePos();
+ /* 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);
+ }
- /* Generate code */
- g_switch (Nodes, DefaultLabel? DefaultLabel : ExitLabel, Depth);
+ /* Remember the current position */
+ GetCodePos (&SwitchCodeStart);
- /* Move the code to the front */
- MoveCode (SwitchCodeStart, GetCodePos(), CaseCodeStart);
+ /* 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);
- /* Eat the closing curly brace */
- NextToken ();
+ /* Exit the loop */
+ DelLoop ();
+
+ /* Switch back to the enclosing switch statement if any */
+ Switch = OldSwitch;
/* Free the case value tree */
- FreeCaseNodeColl (Nodes);
+ FreeCaseNodeColl (SwitchData.Nodes);
- /* End the loop */
- DelLoop ();
+ /* If the case statement was (correctly) 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 ();
}