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