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