]> git.sur5r.net Git - cc65/blobdiff - src/cc65/swstmt.c
Removed (pretty inconsistently used) tab chars from source code base.
[cc65] / src / cc65 / swstmt.c
index c1317449fedc55487cf660f0869a32c56844d846..7e48b58d2cf0daf5677f3f3283a3407c3e479b38 100644 (file)
@@ -1,14 +1,14 @@
 /*****************************************************************************/
 /*                                                                           */
-/*                                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 ();
 }