/*****************************************************************************/ /* */ /* 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 /* 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 (); ED_Init (&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 (); /* 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 = GetUnderlyingTypeCode (&SwitchExpr.Type[0]); 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 = AnyStatement (&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 label */ { 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 */ CaseExpr = NoCodeConstAbsIntExpr (hie1); 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 (); }