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