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