4 * Ullrich von Bassewitz, 06.08.1998
6 * Original by John R. Dunning - see copyleft.jrd
37 /*****************************************************************************/
39 /*****************************************************************************/
43 /* Maximum count of cases */
48 /*****************************************************************************/
49 /* Helper functions */
50 /*****************************************************************************/
54 static void CheckTok (token_t Tok, const char* Msg, int* PendingToken)
55 /* Helper function for Statement. Will check for Tok and print Msg if not
56 * found. If PendingToken is NULL, it will the skip the token, otherwise
57 * it will store one to PendingToken.
60 if (CurTok.Tok != Tok) {
62 } else if (PendingToken) {
71 static void CheckSemi (int* PendingToken)
72 /* Helper function for Statement. Will call CheckTok with the parameters
76 CheckTok (TOK_SEMI, "`;' expected", PendingToken);
81 static void SkipPending (int PendingToken)
82 /* Skip the pending token if we have one */
91 /*****************************************************************************/
93 /*****************************************************************************/
97 static int IfStatement (void)
98 /* Handle an 'if' statement */
106 /* Generate a jump label and parse the condition */
107 Label1 = GetLocalLabel ();
110 /* Parse the if body */
111 GotBreak = Statement (0);
113 /* Else clause present? */
114 if (CurTok.Tok != TOK_ELSE) {
116 g_defcodelabel (Label1);
118 /* Since there's no else clause, we're not sure, if the a break
119 * statement is really executed.
125 /* Generate a jump around the else branch */
126 unsigned Label2 = GetLocalLabel ();
132 /* Define the target for the first test */
133 g_defcodelabel (Label1);
135 /* Total break only if both branches had a break. */
136 GotBreak &= Statement (0);
138 /* Generate the label for the else clause */
139 g_defcodelabel (Label2);
148 static void DoStatement (void)
149 /* Handle the 'do' statement */
151 /* Get the loop control labels */
152 unsigned loop = GetLocalLabel ();
153 unsigned lab = GetLocalLabel ();
155 /* Skip the while token */
158 /* Add the loop to the loop stack */
159 AddLoop (oursp, loop, lab, 0, 0);
161 /* Define the head label */
162 g_defcodelabel (loop);
164 /* Parse the loop body */
167 /* Parse the end condition */
168 Consume (TOK_WHILE, "`while' expected");
172 /* Define the break label */
173 g_defcodelabel (lab);
175 /* Remove the loop from the loop stack */
181 static void WhileStatement (void)
182 /* Handle the 'while' statement */
186 /* Get the loop control labels */
187 unsigned loop = GetLocalLabel ();
188 unsigned lab = GetLocalLabel ();
190 /* Skip the while token */
193 /* Add the loop to the loop stack */
194 AddLoop (oursp, loop, lab, 0, 0);
196 /* Define the head label */
197 g_defcodelabel (loop);
199 /* Test the loop condition */
203 Statement (&PendingToken);
205 /* Jump back to loop top */
209 g_defcodelabel (lab);
211 /* Eat remaining tokens that were delay because of line info correctness */
212 SkipPending (PendingToken);
214 /* Remove the loop from the loop stack */
220 static void ReturnStatement (void)
221 /* Handle the 'return' statement */
226 if (CurTok.Tok != TOK_SEMI) {
227 if (HasVoidReturn (CurrentFunc)) {
228 Error ("Returning a value in function with return type void");
231 /* Evaluate the return expression. Result will be in primary */
234 /* Convert the return value to the type of the function result */
235 if (!HasVoidReturn (CurrentFunc)) {
236 assignadjust (GetReturnType (CurrentFunc), &lval);
238 } else if (!HasVoidReturn (CurrentFunc)) {
239 Error ("Function `%s' must return a value", GetFuncName (CurrentFunc));
242 /* Cleanup the stack in case we're inside a block with locals */
243 g_space (oursp - GetTopLevelSP (CurrentFunc));
245 /* Output a jump to the function exit code */
246 g_jump (GetRetLab (CurrentFunc));
251 static void BreakStatement (void)
252 /* Handle the 'break' statement */
259 /* Get the current loop descriptor */
262 /* Check if we are inside a loop */
264 /* Error: No current loop */
265 Error ("`break' statement not within loop or switch");
269 /* Correct the stack pointer if needed */
270 g_space (oursp - L->StackPtr);
272 /* Jump to the exit label of the loop */
278 static void ContinueStatement (void)
279 /* Handle the 'continue' statement */
283 /* Skip the continue */
286 /* Get the current loop descriptor */
289 /* Search for the correct loop */
298 /* Did we find it? */
300 Error ("`continue' statement not within a loop");
304 /* Correct the stackpointer if needed */
305 g_space (oursp - L->StackPtr);
307 /* Output the loop code */
317 static void CascadeSwitch (ExprDesc* eval)
318 /* Handle a switch statement for chars with a cmp cascade for the selector */
320 unsigned ExitLab; /* Exit label */
321 unsigned NextLab; /* Next case label */
322 unsigned CodeLab; /* Label that starts the actual selector code */
323 int HaveBreak; /* Remember if we exited with break */
324 int HaveDefault; /* Remember if we had a default label */
325 int lcount; /* Label count */
326 unsigned Flags; /* Code generator flags */
327 ExprDesc lval; /* Case label expression */
328 long Val; /* Case label value */
331 /* Create a loop so we may break out, init labels */
332 ExitLab = GetLocalLabel ();
333 AddLoop (oursp, 0, ExitLab, 0, 0);
335 /* Setup some variables needed in the loop below */
336 Flags = TypeOf (eval->e_tptr) | CF_CONST | CF_FORCECHAR;
337 CodeLab = NextLab = 0;
341 /* Parse the labels */
343 while (CurTok.Tok != TOK_RCURLY) {
345 if (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT) {
347 /* If the code for the previous selector did not end with a
348 * break statement, we must jump over the next selector test.
351 /* Define a label for the code */
353 CodeLab = GetLocalLabel ();
358 /* If we have a cascade label, emit it */
360 g_defcodelabel (NextLab);
364 while (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT) {
366 /* Parse the selector */
367 if (CurTok.Tok == TOK_CASE) {
372 /* Skip the "case" token */
375 /* Read the selector expression */
377 if (!IsClassInt (lval.e_tptr)) {
378 Error ("Switch quantity not an integer");
381 /* Check the range of the expression */
383 switch (*eval->e_tptr) {
387 if (Val < -128 || Val > 127) {
388 Error ("Range error");
393 if (Val < 0 || Val > 255) {
394 Error ("Range error");
399 if (Val < -32768 || Val > 32767) {
400 Error ("Range error");
405 if (Val < 0 || Val > 65535) {
406 Error ("Range error");
411 Internal ("Invalid type: %02X", *eval->e_tptr & 0xFF);
417 /* If another case follows, we will jump to the code if
418 * the condition is true.
420 if (CurTok.Tok == TOK_CASE) {
421 /* Create a code label if needed */
423 CodeLab = GetLocalLabel ();
425 g_falsejump (CF_NONE, CodeLab);
426 } else if (CurTok.Tok != TOK_DEFAULT) {
427 /* No case follows, jump to next selector */
429 NextLab = GetLocalLabel ();
431 g_truejump (CF_NONE, NextLab);
442 /* Handle the pathologic case: DEFAULT followed by CASE */
443 if (CurTok.Tok == TOK_CASE) {
445 CodeLab = GetLocalLabel ();
453 /* Remember that we had a default label */
461 /* Emit a code label if we have one */
463 g_defcodelabel (CodeLab);
467 /* Parse statements */
468 if (CurTok.Tok != TOK_RCURLY) {
469 HaveBreak = Statement (0);
473 /* Check if we have any labels */
474 if (lcount == 0 && !HaveDefault) {
475 Warning ("No case labels");
478 /* Define the exit label and, if there's a next label left, create this
482 g_defcodelabel (NextLab);
484 g_defcodelabel (ExitLab);
486 /* Eat the closing curly brace */
495 static void TableSwitch (ExprDesc* eval)
496 /* Handle a switch statement via table based selector */
498 /* Entry for one case in a switch statement */
500 long sw_const; /* selector value */
501 unsigned sw_lab; /* label for this selector */
504 int dlabel; /* for default */
505 int lab; /* exit label */
506 int label; /* label for case */
507 int lcase; /* label for compares */
508 int lcount; /* Label count */
509 int HaveBreak; /* Last statement has a break */
510 int HaveDefault; /* Remember if we had a default label */
511 unsigned Flags; /* Code generator flags */
512 ExprDesc lval; /* Case label expression */
516 /* Allocate memory for the switch table */
517 swtab = xmalloc (CASE_MAX * sizeof (struct swent));
519 /* Create a look so we may break out, init labels */
520 HaveBreak = 0; /* Keep gcc silent */
521 HaveDefault = 0; /* No default case until now */
522 dlabel = 0; /* init */
523 lab = GetLocalLabel (); /* get exit */
525 AddLoop (oursp, 0, lab, 0, 0);
527 /* Jump behind the code for the CASE labels */
528 g_jump (lcase = GetLocalLabel ());
530 while (CurTok.Tok != TOK_RCURLY) {
531 if (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT) {
532 if (lcount >= CASE_MAX) {
533 Fatal ("Too many case labels");
535 label = GetLocalLabel ();
537 if (CurTok.Tok == TOK_CASE) {
540 if (!IsClassInt (lval.e_tptr)) {
541 Error ("Switch quantity not an integer");
543 p->sw_const = lval.e_const;
553 } while (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT);
554 g_defcodelabel (label);
557 if (CurTok.Tok != TOK_RCURLY) {
558 HaveBreak = Statement (0);
562 /* Check if we have any labels */
563 if (lcount == 0 && !HaveDefault) {
564 Warning ("No case labels");
567 /* Eat the closing curly brace */
570 /* If the last statement doesn't have a break or return, add one */
575 /* Actual selector code goes here */
576 g_defcodelabel (lcase);
578 /* Create the call to the switch subroutine */
579 Flags = TypeOf (eval->e_tptr);
582 /* First entry is negative of label count */
583 g_defdata (CF_INT | CF_CONST, -((int)lcount)-1, 0);
585 /* Create the case selector table */
588 g_case (Flags, p->sw_lab, p->sw_const); /* Create one label */
596 g_defcodelabel (lab);
599 /* Free the allocated space for the labels */
605 static void SwitchStatement (void)
606 /* Handle a 'switch' statement */
608 ExprDesc eval; /* Switch statement expression */
610 /* Eat the "switch" */
613 /* Read the switch expression */
618 /* result of expr is in P */
621 /* Now decide which sort of switch we will create: */
622 if (IsTypeChar (eval.e_tptr) || (CodeSizeFactor >= 200 && IsClassInt (eval.e_tptr))) {
623 CascadeSwitch (&eval);
631 static void ForStatement (void)
632 /* Handle a 'for' statement */
638 CodeMark IncExprStart;
642 /* Get several local labels needed later */
643 unsigned TestLabel = GetLocalLabel ();
644 unsigned lab = GetLocalLabel ();
645 unsigned IncLabel = GetLocalLabel ();
646 unsigned lstat = GetLocalLabel ();
648 /* Skip the FOR token */
651 /* Add the loop to the loop stack */
652 AddLoop (oursp, TestLabel, lab, IncLabel, lstat);
654 /* Skip the opening paren */
657 /* Parse the initializer expression */
658 if (CurTok.Tok != TOK_SEMI) {
663 /* Label for the test expressions */
664 g_defcodelabel (TestLabel);
666 /* Parse the test expression */
667 if (CurTok.Tok != TOK_SEMI) {
669 g_truejump (CF_NONE, lstat);
676 /* Remember the start of the increment expression */
677 IncExprStart = GetCodePos();
679 /* Label for the increment expression */
680 g_defcodelabel (IncLabel);
682 /* Parse the increment expression */
683 HaveIncExpr = (CurTok.Tok != TOK_RPAREN);
688 /* Jump to the test */
691 /* Remember the end of the increment expression */
692 IncExprEnd = GetCodePos();
694 /* Skip the closing paren */
698 g_defcodelabel (lstat);
699 Statement (&PendingToken);
701 /* If we had an increment expression, move the code to the bottom of
702 * the loop. In this case we don't need to jump there at the end of
706 MoveCode (IncExprStart, IncExprEnd, GetCodePos());
708 /* Jump back to the increment expression */
712 /* Skip a pending token if we have one */
713 SkipPending (PendingToken);
715 /* Declare the break label */
716 g_defcodelabel (lab);
718 /* Remove the loop from the loop stack */
724 static int CompoundStatement (void)
725 /* Compound statement. Allow any number of statements inside braces. The
726 * function returns true if the last statement was a break or return.
731 /* Remember the stack at block entry */
732 int OldStack = oursp;
734 /* Enter a new lexical level */
737 /* Parse local variable declarations if any */
740 /* Now process statements in this block */
742 while (CurTok.Tok != TOK_RCURLY) {
743 if (CurTok.Tok != TOK_CEOF) {
744 GotBreak = Statement (0);
750 /* Clean up the stack. */
752 g_space (oursp - OldStack);
756 /* Emit references to imports/exports for this block */
759 /* Leave the lexical level */
767 int Statement (int* PendingToken)
768 /* Statement parser. Returns 1 if the statement does a return/break, returns
769 * 0 otherwise. If the PendingToken pointer is not NULL, the function will
770 * not skip the terminating token of the statement (closing brace or
771 * semicolon), but store true if there is a pending token, and false if there
772 * is none. The token is always checked, so there is no need for the caller to
773 * check this token, it must be skipped, however. If the argument pointer is
774 * NULL, the function will skip the token.
780 /* Assume no pending token */
785 /* Check for a label */
786 if (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) {
788 /* Special handling for a label */
793 switch (CurTok.Tok) {
797 GotBreak = CompoundStatement ();
798 CheckTok (TOK_RCURLY, "`{' expected", PendingToken);
802 return IfStatement ();
818 CheckSemi (PendingToken);
823 CheckSemi (PendingToken);
827 ContinueStatement ();
828 CheckSemi (PendingToken);
837 CheckSemi (PendingToken);
850 /* Actual statement */
852 CheckSemi (PendingToken);