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