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