]> git.sur5r.net Git - cc65/blob - src/cc65/stmt.c
Move the increment code of a for loop after the loop body.
[cc65] / src / cc65 / stmt.c
1 /*
2  * stmt.c
3  *
4  * Ullrich von Bassewitz, 06.08.1998
5  *
6  * Original by John R. Dunning - see copyleft.jrd
7  */
8
9
10
11 #include <stdio.h>
12 #include <string.h>
13
14 /* common */
15 #include "xmalloc.h"
16
17 /* cc65 */
18 #include "asmcode.h"
19 #include "asmlabel.h"
20 #include "codegen.h"
21 #include "datatype.h"
22 #include "error.h"
23 #include "expr.h"
24 #include "function.h"
25 #include "global.h"
26 #include "goto.h"
27 #include "litpool.h"
28 #include "locals.h"
29 #include "loop.h"
30 #include "pragma.h"
31 #include "scanner.h"
32 #include "symtab.h"
33 #include "stmt.h"
34
35
36
37 /*****************************************************************************/
38 /*                                   Data                                    */
39 /*****************************************************************************/
40
41
42
43 /* Maximum count of cases */
44 #define CASE_MAX        257
45
46
47
48 /*****************************************************************************/
49 /*                             Helper functions                              */
50 /*****************************************************************************/
51
52
53
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.
58  */
59 {
60     if (CurTok.Tok != Tok) {
61         Error (Msg);
62     } else if (PendingToken) {
63         *PendingToken = 1;
64     } else {
65         NextToken ();
66     }
67 }
68
69
70
71 static void CheckSemi (int* PendingToken)
72 /* Helper function for Statement. Will call CheckTok with the parameters
73  * for a semicolon.
74  */
75 {
76     CheckTok (TOK_SEMI, "`;' expected", PendingToken);
77 }
78
79
80
81 static void SkipPending (int PendingToken)
82 /* Skip the pending token if we have one */
83 {
84     if (PendingToken) {
85         NextToken ();
86     }
87 }
88
89
90
91 /*****************************************************************************/
92 /*                                   Code                                    */
93 /*****************************************************************************/
94
95
96
97 static int IfStatement (void)
98 /* Handle an 'if' statement */
99 {
100     unsigned Label1;
101     int GotBreak;
102
103     /* Skip the if */
104     NextToken ();
105
106     /* Generate a jump label and parse the condition */
107     Label1 = GetLocalLabel ();
108     test (Label1, 0);
109
110     /* Parse the if body */
111     GotBreak = Statement (0);
112
113     /* Else clause present? */
114     if (CurTok.Tok != TOK_ELSE) {
115
116         g_defcodelabel (Label1);
117
118         /* Since there's no else clause, we're not sure, if the a break
119          * statement is really executed.
120          */
121         return 0;
122
123     } else {
124
125         /* Generate a jump around the else branch */
126         unsigned Label2 = GetLocalLabel ();
127         g_jump (Label2);
128
129         /* Skip the else */
130         NextToken ();
131
132         /* Define the target for the first test */
133         g_defcodelabel (Label1);
134
135         /* Total break only if both branches had a break. */
136         GotBreak &= Statement (0);
137
138         /* Generate the label for the else clause */
139         g_defcodelabel (Label2);
140
141         /* Done */
142         return GotBreak;
143     }
144 }
145
146
147
148 static void DoStatement (void)
149 /* Handle the 'do' statement */
150 {
151     /* Get the loop control labels */
152     unsigned loop = GetLocalLabel ();
153     unsigned lab = GetLocalLabel ();
154
155     /* Skip the while token */
156     NextToken ();
157
158     /* Add the loop to the loop stack */
159     AddLoop (oursp, loop, lab, 0, 0);
160
161     /* Define the head label */
162     g_defcodelabel (loop);
163
164     /* Parse the loop body */
165     Statement (0);
166
167     /* Parse the end condition */
168     Consume (TOK_WHILE, "`while' expected");
169     test (loop, 1);
170     ConsumeSemi ();
171
172     /* Define the break label */
173     g_defcodelabel (lab);
174
175     /* Remove the loop from the loop stack */
176     DelLoop ();
177 }
178
179
180
181 static void WhileStatement (void)
182 /* Handle the 'while' statement */
183 {
184     int PendingToken;
185
186     /* Get the loop control labels */
187     unsigned loop = GetLocalLabel ();
188     unsigned lab = GetLocalLabel ();
189
190     /* Skip the while token */
191     NextToken ();
192
193     /* Add the loop to the loop stack */
194     AddLoop (oursp, loop, lab, 0, 0);
195
196     /* Define the head label */
197     g_defcodelabel (loop);
198
199     /* Test the loop condition */
200     test (lab, 0);
201
202     /* If the statement following the while loop is empty, that is, we have
203      * something like "while (1) ;", the test function ommitted the jump as
204      * an optimization. Since we know, the condition codes are set, we can
205      * do another small optimization here, and use a conditional jump
206      * instead an absolute one.
207      */
208     if (CurTok.Tok == TOK_SEMI) {
209         /* Use a conditional jump */
210         g_truejump (CF_NONE, loop);
211         /* Shortcut */
212         NextToken ();
213     } else {
214         /* There is code inside the while loop, parse the body */
215         Statement (&PendingToken);
216         g_jump (loop);
217         g_defcodelabel (lab);
218         SkipPending (PendingToken);
219     }
220
221     /* Remove the loop from the loop stack */
222     DelLoop ();
223 }
224
225
226
227 static void ReturnStatement (void)
228 /* Handle the 'return' statement */
229 {
230     struct expent lval;
231
232     NextToken ();
233     if (CurTok.Tok != TOK_SEMI) {
234         if (HasVoidReturn (CurrentFunc)) {
235             Error ("Returning a value in function with return type void");
236         }
237
238         /* Evaluate the return expression. Result will be in primary */
239         expression (&lval);
240
241         /* Convert the return value to the type of the function result */
242         if (!HasVoidReturn (CurrentFunc)) {
243             assignadjust (GetReturnType (CurrentFunc), &lval);
244         }
245     } else if (!HasVoidReturn (CurrentFunc)) {
246         Error ("Function `%s' must return a value", GetFuncName (CurrentFunc));
247     }
248
249     /* Cleanup the stack in case we're inside a block with locals */
250     g_space (oursp - GetTopLevelSP (CurrentFunc));
251
252     /* Output a jump to the function exit code */
253     g_jump (GetRetLab (CurrentFunc));
254 }
255
256
257
258 static void BreakStatement (void)
259 /* Handle the 'break' statement */
260 {
261     LoopDesc* L;
262
263     /* Skip the break */
264     NextToken ();
265
266     /* Get the current loop descriptor */
267     L = CurrentLoop ();
268
269     /* Check if we are inside a loop */
270     if (L == 0) {
271         /* Error: No current loop */
272         Error ("`break' statement not within loop or switch");
273         return;
274     }
275
276     /* Correct the stack pointer if needed */
277     g_space (oursp - L->StackPtr);
278
279     /* Jump to the exit label of the loop */
280     g_jump (L->Label);
281 }
282
283
284
285 static void ContinueStatement (void)
286 /* Handle the 'continue' statement */
287 {
288     LoopDesc* L;
289
290     /* Skip the continue */
291     NextToken ();
292
293     /* Get the current loop descriptor */
294     L = CurrentLoop ();
295     if (L) {
296         /* Search for the correct loop */
297         do {
298             if (L->Loop) {
299                 break;
300             }
301             L = L->Next;
302         } while (L);
303     }
304
305     /* Did we find it? */
306     if (L == 0) {
307         Error ("`continue' statement not within a loop");
308         return;
309     }
310
311     /* Correct the stackpointer if needed */
312     g_space (oursp - L->StackPtr);
313
314     /* Output the loop code */
315     if (L->linc) {
316         g_jump (L->linc);
317     } else {
318         g_jump (L->Loop);
319     }
320 }
321
322
323
324 static void CascadeSwitch (struct expent* eval)
325 /* Handle a switch statement for chars with a cmp cascade for the selector */
326 {
327     unsigned ExitLab;           /* Exit label */
328     unsigned NextLab;           /* Next case label */
329     unsigned CodeLab;           /* Label that starts the actual selector code */
330     int HaveBreak;              /* Remember if we exited with break */
331     int HaveDefault;            /* Remember if we had a default label */
332     int lcount;                 /* Label count */
333     unsigned Flags;             /* Code generator flags */
334     struct expent lval;         /* Case label expression */
335     long Val;                   /* Case label value */
336
337
338     /* Create a loop so we may break out, init labels */
339     ExitLab = GetLocalLabel ();
340     AddLoop (oursp, 0, ExitLab, 0, 0);
341
342     /* Setup some variables needed in the loop  below */
343     Flags = TypeOf (eval->e_tptr) | CF_CONST | CF_FORCECHAR;
344     CodeLab = NextLab = 0;
345     HaveBreak = 1;
346     HaveDefault = 0;
347
348     /* Parse the labels */
349     lcount = 0;
350     while (CurTok.Tok != TOK_RCURLY) {
351
352         if (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT) {
353
354             /* If the code for the previous selector did not end with a
355              * break statement, we must jump over the next selector test.
356              */
357             if (!HaveBreak) {
358                 /* Define a label for the code */
359                 if (CodeLab == 0) {
360                     CodeLab = GetLocalLabel ();
361                 }
362                 g_jump (CodeLab);
363             }
364
365             /* If we have a cascade label, emit it */
366             if (NextLab) {
367                 g_defcodelabel (NextLab);
368                 NextLab = 0;
369             }
370
371             while (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT) {
372
373                 /* Parse the selector */
374                 if (CurTok.Tok == TOK_CASE) {
375
376                     /* Count labels */
377                     ++lcount;
378
379                     /* Skip the "case" token */
380                     NextToken ();
381
382                     /* Read the selector expression */
383                     constexpr (&lval);
384                     if (!IsClassInt (lval.e_tptr)) {
385                         Error ("Switch quantity not an integer");
386                     }
387
388                     /* Check the range of the expression */
389                     Val = lval.e_const;
390                     switch (*eval->e_tptr) {
391
392                         case T_SCHAR:
393                             /* Signed char */
394                             if (Val < -128 || Val > 127) {
395                                 Error ("Range error");
396                             }
397                             break;
398
399                         case T_UCHAR:
400                             if (Val < 0 || Val > 255) {
401                                 Error ("Range error");
402                             }
403                             break;
404
405                         case T_INT:
406                             if (Val < -32768 || Val > 32767) {
407                                 Error ("Range error");
408                             }
409                             break;
410
411                         case T_UINT:
412                             if (Val < 0 || Val > 65535) {
413                                 Error ("Range error");
414                             }
415                             break;
416
417                         default:
418                             Internal ("Invalid type: %02X", *eval->e_tptr & 0xFF);
419                     }
420
421                     /* Emit a compare */
422                     g_cmp (Flags, Val);
423
424                     /* If another case follows, we will jump to the code if
425                      * the condition is true.
426                      */
427                     if (CurTok.Tok == TOK_CASE) {
428                         /* Create a code label if needed */
429                         if (CodeLab == 0) {
430                             CodeLab = GetLocalLabel ();
431                         }
432                         g_falsejump (CF_NONE, CodeLab);
433                     } else if (CurTok.Tok != TOK_DEFAULT) {
434                         /* No case follows, jump to next selector */
435                         if (NextLab == 0) {
436                             NextLab = GetLocalLabel ();
437                         }
438                         g_truejump (CF_NONE, NextLab);
439                     }
440
441                     /* Skip the colon */
442                     ConsumeColon ();
443
444                 } else {
445
446                     /* Default case */
447                     NextToken ();
448
449                     /* Handle the pathologic case: DEFAULT followed by CASE */
450                     if (CurTok.Tok == TOK_CASE) {
451                         if (CodeLab == 0) {
452                             CodeLab = GetLocalLabel ();
453                         }
454                         g_jump (CodeLab);
455                     }
456
457                     /* Skip the colon */
458                     ConsumeColon ();
459
460                     /* Remember that we had a default label */
461                     HaveDefault = 1;
462                 }
463
464             }
465
466         }
467
468         /* Emit a code label if we have one */
469         if (CodeLab) {
470             g_defcodelabel (CodeLab);
471             CodeLab = 0;
472         }
473
474         /* Parse statements */
475         if (CurTok.Tok != TOK_RCURLY) {
476             HaveBreak = Statement (0);
477         }
478     }
479
480     /* Check if we have any labels */
481     if (lcount == 0 && !HaveDefault) {
482         Warning ("No case labels");
483     }
484
485     /* Define the exit label and, if there's a next label left, create this
486      * one, too.
487      */
488     if (NextLab) {
489         g_defcodelabel (NextLab);
490     }
491     g_defcodelabel (ExitLab);
492
493     /* Eat the closing curly brace */
494     NextToken ();
495
496     /* End the loop */
497     DelLoop ();
498 }
499
500
501
502 static void TableSwitch (struct expent* eval)
503 /* Handle a switch statement via table based selector */
504 {
505     /* Entry for one case in a switch statement */
506     struct swent {
507         long     sw_const;      /* selector value */
508         unsigned sw_lab;        /* label for this selector */
509     };
510
511     int dlabel;                 /* for default */
512     int lab;                    /* exit label */
513     int label;                  /* label for case */
514     int lcase;                  /* label for compares */
515     int lcount;                 /* Label count */
516     int HaveBreak;              /* Last statement has a break */
517     int HaveDefault;            /* Remember if we had a default label */
518     unsigned Flags;             /* Code generator flags */
519     struct expent lval;         /* Case label expression */
520     struct swent *p;
521     struct swent *swtab;
522
523     /* Allocate memory for the switch table */
524     swtab = xmalloc (CASE_MAX * sizeof (struct swent));
525
526     /* Create a look so we may break out, init labels */
527     HaveBreak = 0;              /* Keep gcc silent */
528     HaveDefault = 0;            /* No default case until now */
529     dlabel = 0;                 /* init */
530     lab = GetLocalLabel ();     /* get exit */
531     p = swtab;
532     AddLoop (oursp, 0, lab, 0, 0);
533
534     /* Jump behind the code for the CASE labels */
535     g_jump (lcase = GetLocalLabel ());
536     lcount = 0;
537     while (CurTok.Tok != TOK_RCURLY) {
538         if (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT) {
539             if (lcount >= CASE_MAX) {
540                 Fatal ("Too many case labels");
541             }
542             label = GetLocalLabel ();
543             do {
544                 if (CurTok.Tok == TOK_CASE) {
545                     NextToken ();
546                     constexpr (&lval);
547                     if (!IsClassInt (lval.e_tptr)) {
548                         Error ("Switch quantity not an integer");
549                     }
550                     p->sw_const = lval.e_const;
551                     p->sw_lab = label;
552                     ++p;
553                     ++lcount;
554                 } else {
555                     NextToken ();
556                     dlabel = label;
557                     HaveDefault = 1;
558                 }
559                 ConsumeColon ();
560             } while (CurTok.Tok == TOK_CASE || CurTok.Tok == TOK_DEFAULT);
561             g_defcodelabel (label);
562             HaveBreak = 0;
563         }
564         if (CurTok.Tok != TOK_RCURLY) {
565             HaveBreak = Statement (0);
566         }
567     }
568
569     /* Check if we have any labels */
570     if (lcount == 0 && !HaveDefault) {
571         Warning ("No case labels");
572     }
573
574     /* Eat the closing curly brace */
575     NextToken ();
576
577     /* If the last statement doesn't have a break or return, add one */
578     if (!HaveBreak) {
579         g_jump (lab);
580     }
581
582     /* Actual selector code goes here */
583     g_defcodelabel (lcase);
584
585     /* Create the call to the switch subroutine */
586     Flags = TypeOf (eval->e_tptr);
587     g_switch (Flags);
588
589     /* First entry is negative of label count */
590     g_defdata (CF_INT | CF_CONST, -((int)lcount)-1, 0);
591
592     /* Create the case selector table */
593     p = swtab;
594     while (lcount) {
595         g_case (Flags, p->sw_lab, p->sw_const); /* Create one label */
596         --lcount;
597         ++p;
598     }
599
600     if (dlabel) {
601         g_jump (dlabel);
602     }
603     g_defcodelabel (lab);
604     DelLoop ();
605
606     /* Free the allocated space for the labels */
607     xfree (swtab);
608 }
609
610
611
612 static void SwitchStatement (void)
613 /* Handle a 'switch' statement */
614 {
615     struct expent eval;         /* Switch statement expression */
616
617     /* Eat the "switch" */
618     NextToken ();
619
620     /* Read the switch expression */
621     ConsumeLParen ();
622     intexpr (&eval);
623     ConsumeRParen ();
624
625     /* result of expr is in P */
626     ConsumeLCurly ();
627
628     /* Now decide which sort of switch we will create: */
629     if (IsTypeChar (eval.e_tptr) || (CodeSizeFactor >= 200 && IsClassInt (eval.e_tptr))) {
630         CascadeSwitch (&eval);
631     } else {
632         TableSwitch (&eval);
633     }
634 }
635
636
637
638 static void ForStatement (void)
639 /* Handle a 'for' statement */
640 {
641     struct expent lval1;
642     struct expent lval2;
643     struct expent lval3;
644     int HaveIncExpr;  
645     CodeMark IncExprStart;
646     CodeMark IncExprEnd;
647     int PendingToken;
648
649     /* Get several local labels needed later */
650     unsigned TestLabel = GetLocalLabel ();
651     unsigned lab       = GetLocalLabel ();
652     unsigned IncLabel  = GetLocalLabel ();
653     unsigned lstat     = GetLocalLabel ();
654
655     /* Skip the FOR token */           
656     NextToken ();
657
658     /* Add the loop to the loop stack */
659     AddLoop (oursp, TestLabel, lab, IncLabel, lstat);
660
661     /* Skip the opening paren */
662     ConsumeLParen ();
663
664     /* Parse the initializer expression */
665     if (CurTok.Tok != TOK_SEMI) {
666         expression (&lval1);
667     }
668     ConsumeSemi ();
669
670     /* Label for the test expressions */
671     g_defcodelabel (TestLabel);
672
673     /* Parse the test expression */
674     if (CurTok.Tok != TOK_SEMI) {
675         boolexpr (&lval2);
676         g_truejump (CF_NONE, lstat);
677         g_jump (lab);
678     } else {
679         g_jump (lstat);
680     }
681     ConsumeSemi ();
682
683     /* Remember the start of the increment expression */
684     IncExprStart = GetCodePos();
685
686     /* Label for the increment expression */
687     g_defcodelabel (IncLabel);
688
689     /* Parse the increment expression */
690     HaveIncExpr = (CurTok.Tok != TOK_RPAREN);
691     if (HaveIncExpr) {
692         expression (&lval3);
693     }
694
695     /* Jump to the test */
696     g_jump (TestLabel);
697
698     /* Remember the end of the increment expression */
699     IncExprEnd = GetCodePos();
700
701     /* Skip the closing paren */
702     ConsumeRParen ();
703
704     /* Loop body */
705     g_defcodelabel (lstat);
706     Statement (&PendingToken);
707
708     /* If we had an increment expression, move the code to the bottom of
709      * the loop. In this case we don't need to jump there at the end of
710      * the loop body.
711      */
712     if (HaveIncExpr) {
713         MoveCode (IncExprStart, IncExprEnd, GetCodePos());
714     } else {
715         /* Jump back to the increment expression */
716         g_jump (IncLabel);
717     }
718     
719     /* Skip a pending token if we have one */
720     SkipPending (PendingToken);
721
722     /* Declare the break label */
723     g_defcodelabel (lab);
724
725     /* Remove the loop from the loop stack */
726     DelLoop ();
727 }
728
729
730
731 static int CompoundStatement (void)
732 /* Compound statement. Allow any number of statements inside braces. The
733  * function returns true if the last statement was a break or return.
734  */
735 {
736     int GotBreak;
737
738     /* Remember the stack at block entry */
739     int OldStack = oursp;
740
741     /* Enter a new lexical level */
742     EnterBlockLevel ();
743
744     /* Parse local variable declarations if any */
745     DeclareLocals ();
746
747     /* Now process statements in this block */
748     GotBreak = 0;
749     while (CurTok.Tok != TOK_RCURLY) {
750         if (CurTok.Tok != TOK_CEOF) {
751             GotBreak = Statement (0);
752         } else {
753             break;
754         }
755     }
756
757     /* Clean up the stack. */
758     if (!GotBreak) {
759         g_space (oursp - OldStack);
760     }
761     oursp = OldStack;
762
763     /* Emit references to imports/exports for this block */
764     EmitExternals ();
765
766     /* Leave the lexical level */
767     LeaveBlockLevel ();
768
769     return GotBreak;
770 }
771
772
773
774 int Statement (int* PendingToken)
775 /* Statement parser. Returns 1 if the statement does a return/break, returns
776  * 0 otherwise. If the PendingToken pointer is not NULL, the function will
777  * not skip the terminating token of the statement (closing brace or
778  * semicolon), but store true if there is a pending token, and false if there
779  * is none. The token is always checked, so there is no need for the caller to
780  * check this token, it must be skipped, however. If the argument pointer is
781  * NULL, the function will skip the token.
782  */
783 {
784     struct expent lval;
785     int GotBreak;
786
787     /* Assume no pending token */
788     if (PendingToken) {
789         *PendingToken = 0;
790     }
791
792     /* Check for a label */
793     if (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) {
794
795         /* Special handling for a label */
796         DoLabel ();
797
798     } else {
799
800         switch (CurTok.Tok) {
801
802             case TOK_LCURLY:
803                 NextToken ();
804                 GotBreak = CompoundStatement ();
805                 CheckTok (TOK_RCURLY, "`{' expected", PendingToken);
806                 return GotBreak;
807
808             case TOK_IF:
809                 return IfStatement ();
810
811             case TOK_WHILE:
812                 WhileStatement ();
813                 break;
814
815             case TOK_DO:
816                 DoStatement ();
817                 break;
818
819             case TOK_SWITCH:
820                 SwitchStatement ();
821                 break;
822
823             case TOK_RETURN:
824                 ReturnStatement ();
825                 CheckSemi (PendingToken);
826                 return 1;
827
828             case TOK_BREAK:
829                 BreakStatement ();
830                 CheckSemi (PendingToken);
831                 return 1;
832
833             case TOK_CONTINUE:
834                 ContinueStatement ();
835                 CheckSemi (PendingToken);
836                 return 1;
837
838             case TOK_FOR:
839                 ForStatement ();
840                 break;
841
842             case TOK_GOTO:
843                 GotoStatement ();
844                 CheckSemi (PendingToken);
845                 return 1;
846
847             case TOK_SEMI:
848                 /* Ignore it */
849                 NextToken ();
850                 break;
851
852             case TOK_PRAGMA:
853                 DoPragma ();
854                 break;
855
856             default:
857                 /* Actual statement */
858                 expression (&lval);
859                 CheckSemi (PendingToken);
860         }
861     }
862     return 0;
863 }
864
865
866