]> git.sur5r.net Git - cc65/blob - src/cc65/stmt.c
A continue statement within a do loop did not work. Cleaned up the loop
[cc65] / src / cc65 / stmt.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                  stmt.c                                   */
4 /*                                                                           */
5 /*                             Parse a statement                             */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2003 Ullrich von Bassewitz                                       */
10 /*               Römerstraße 52                                              */
11 /*               D-70794 Filderstadt                                         */
12 /* EMail:        uz@cc65.org                                                 */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdio.h>
37 #include <string.h>
38
39 /* common */
40 #include "coll.h"
41 #include "xmalloc.h"
42
43 /* cc65 */
44 #include "asmcode.h"
45 #include "asmlabel.h"
46 #include "codegen.h"
47 #include "datatype.h"
48 #include "error.h"
49 #include "expr.h"
50 #include "function.h"
51 #include "global.h"
52 #include "goto.h"
53 #include "litpool.h"
54 #include "locals.h"
55 #include "loop.h"
56 #include "pragma.h"
57 #include "scanner.h"
58 #include "swstmt.h"
59 #include "symtab.h"
60 #include "stmt.h"
61 #include "typeconv.h"
62
63
64
65 /*****************************************************************************/
66 /*                             Helper functions                              */
67 /*****************************************************************************/
68
69
70
71 static void CheckTok (token_t Tok, const char* Msg, int* PendingToken)
72 /* Helper function for Statement. Will check for Tok and print Msg if not
73  * found. If PendingToken is NULL, it will the skip the token, otherwise
74  * it will store one to PendingToken.
75  */
76 {
77     if (CurTok.Tok != Tok) {
78         Error (Msg);
79     } else if (PendingToken) {
80         *PendingToken = 1;
81     } else {
82         NextToken ();
83     }
84 }
85
86
87
88 static void CheckSemi (int* PendingToken)
89 /* Helper function for Statement. Will check for a semicolon and print an
90  * error message if not found (plus some error recovery). If PendingToken is
91  * NULL, it will the skip the token, otherwise it will store one to
92  * PendingToken.
93  * This function is a special version of CheckTok with the addition of the
94  * error recovery.
95  */
96 {
97     int HaveToken = (CurTok.Tok == TOK_SEMI);
98     if (!HaveToken) {
99         Error ("`;' expected");
100         /* Try to be smart about errors */
101         if (CurTok.Tok == TOK_COLON || CurTok.Tok == TOK_COMMA) {
102             HaveToken = 1;
103         }
104     }
105     if (HaveToken) {
106         if (PendingToken) {
107             *PendingToken = 1;
108         } else {
109             NextToken ();
110         }
111     }
112 }
113
114
115
116 static void SkipPending (int PendingToken)
117 /* Skip the pending token if we have one */
118 {
119     if (PendingToken) {
120         NextToken ();
121     }
122 }
123
124
125
126 /*****************************************************************************/
127 /*                                   Code                                    */
128 /*****************************************************************************/
129
130
131
132 static int IfStatement (void)
133 /* Handle an 'if' statement */
134 {
135     unsigned Label1;
136     int GotBreak;
137
138     /* Skip the if */
139     NextToken ();
140
141     /* Generate a jump label and parse the condition */
142     Label1 = GetLocalLabel ();
143     TestInParens (Label1, 0);
144
145     /* Parse the if body */
146     GotBreak = Statement (0);
147
148     /* Else clause present? */
149     if (CurTok.Tok != TOK_ELSE) {
150
151         g_defcodelabel (Label1);
152
153         /* Since there's no else clause, we're not sure, if the a break
154          * statement is really executed.
155          */
156         return 0;
157
158     } else {
159
160         /* Generate a jump around the else branch */
161         unsigned Label2 = GetLocalLabel ();
162         g_jump (Label2);
163
164         /* Skip the else */
165         NextToken ();
166
167         /* Define the target for the first test */
168         g_defcodelabel (Label1);
169
170         /* Total break only if both branches had a break. */
171         GotBreak &= Statement (0);
172
173         /* Generate the label for the else clause */
174         g_defcodelabel (Label2);
175
176         /* Done */
177         return GotBreak;
178     }
179 }
180
181
182
183 static void DoStatement (void)
184 /* Handle the 'do' statement */
185 {
186     /* Get the loop control labels */
187     unsigned LoopLabel      = GetLocalLabel ();
188     unsigned BreakLabel     = GetLocalLabel ();
189     unsigned ContinueLabel  = GetLocalLabel ();
190
191     /* Skip the while token */
192     NextToken ();
193
194     /* Add the loop to the loop stack */
195     AddLoop (oursp, BreakLabel, ContinueLabel);
196
197     /* Define the loop label */
198     g_defcodelabel (LoopLabel);
199
200     /* Parse the loop body */
201     Statement (0);
202
203     /* Output the label for a continue */
204     g_defcodelabel (ContinueLabel);
205
206     /* Parse the end condition */
207     Consume (TOK_WHILE, "`while' expected");
208     TestInParens (LoopLabel, 1);
209     ConsumeSemi ();
210
211     /* Define the break label */
212     g_defcodelabel (BreakLabel);
213
214     /* Remove the loop from the loop stack */
215     DelLoop ();
216 }
217
218
219
220 static void WhileStatement (void)
221 /* Handle the 'while' statement */
222 {
223     int PendingToken;
224
225     /* Get the loop control labels */
226     unsigned LoopLabel  = GetLocalLabel ();
227     unsigned BreakLabel = GetLocalLabel ();
228
229     /* Skip the while token */
230     NextToken ();
231
232     /* Add the loop to the loop stack. In case of a while loop, the loop head
233      * label is used for continue statements.
234      */
235     AddLoop (oursp, BreakLabel, LoopLabel);
236
237     /* Define the head label */
238     g_defcodelabel (LoopLabel);
239
240     /* Test the loop condition */
241     TestInParens (BreakLabel, 0);
242
243     /* Loop body */
244     Statement (&PendingToken);
245
246     /* Jump back to loop top */
247     g_jump (LoopLabel);
248
249     /* Exit label */
250     g_defcodelabel (BreakLabel);
251
252     /* Eat remaining tokens that were delayed because of line info
253      * correctness
254      */
255     SkipPending (PendingToken);
256
257     /* Remove the loop from the loop stack */
258     DelLoop ();
259 }
260
261
262
263 static void ReturnStatement (void)
264 /* Handle the 'return' statement */
265 {
266     ExprDesc Expr;
267     int k;
268
269     NextToken ();
270     if (CurTok.Tok != TOK_SEMI) {
271
272         /* Check if the function has a return value declared */
273         if (F_HasVoidReturn (CurrentFunc)) {
274             Error ("Returning a value in function with return type void");
275         }
276
277         /* Evaluate the return expression */
278         k = hie0 (InitExprDesc (&Expr));
279
280         /* Ignore the return expression if the function returns void */
281         if (!F_HasVoidReturn (CurrentFunc)) {
282
283             /* Convert the return value to the type of the function result */
284             k = TypeConversion (&Expr, k, F_GetReturnType (CurrentFunc));
285
286             /* Load the value into the primary */
287             ExprLoad (CF_NONE, k, &Expr);
288         }
289
290     } else if (!F_HasVoidReturn (CurrentFunc) && !F_HasOldStyleIntRet (CurrentFunc)) {
291         Error ("Function `%s' must return a value", F_GetFuncName (CurrentFunc));
292     }
293
294     /* Cleanup the stack in case we're inside a block with locals */
295     g_space (oursp - F_GetTopLevelSP (CurrentFunc));
296
297     /* Output a jump to the function exit code */
298     g_jump (F_GetRetLab (CurrentFunc));
299 }
300
301
302
303 static void BreakStatement (void)
304 /* Handle the 'break' statement */
305 {
306     LoopDesc* L;
307
308     /* Skip the break */
309     NextToken ();
310
311     /* Get the current loop descriptor */
312     L = CurrentLoop ();
313
314     /* Check if we are inside a loop */
315     if (L == 0) {
316         /* Error: No current loop */
317         Error ("`break' statement not within loop or switch");
318         return;
319     }
320
321     /* Correct the stack pointer if needed */
322     g_space (oursp - L->StackPtr);
323
324     /* Jump to the exit label of the loop */
325     g_jump (L->BreakLabel);
326 }
327
328
329
330 static void ContinueStatement (void)
331 /* Handle the 'continue' statement */
332 {
333     LoopDesc* L;
334
335     /* Skip the continue */
336     NextToken ();
337
338     /* Get the current loop descriptor */
339     L = CurrentLoop ();
340     if (L) {
341         /* Search for a loop that has a continue label. */
342         do {
343             if (L->ContinueLabel) {
344                 break;
345             }
346             L = L->Next;
347         } while (L);
348     }
349
350     /* Did we find it? */
351     if (L == 0) {
352         Error ("`continue' statement not within a loop");
353         return;
354     }
355
356     /* Correct the stackpointer if needed */
357     g_space (oursp - L->StackPtr);
358
359     /* Jump to next loop iteration */
360     g_jump (L->ContinueLabel);
361 }
362
363
364
365 static void ForStatement (void)
366 /* Handle a 'for' statement */
367 {
368     ExprDesc lval1;
369     ExprDesc lval3;
370     int HaveIncExpr;
371     CodeMark IncExprStart;
372     CodeMark IncExprEnd;
373     int PendingToken;
374
375     /* Get several local labels needed later */
376     unsigned TestLabel    = GetLocalLabel ();
377     unsigned BreakLabel   = GetLocalLabel ();
378     unsigned IncLabel     = GetLocalLabel ();
379     unsigned BodyLabel    = GetLocalLabel ();
380
381     /* Skip the FOR token */
382     NextToken ();
383
384     /* Add the loop to the loop stack. A continue jumps to the start of the
385      * the increment condition.
386      */
387     AddLoop (oursp, BreakLabel, IncLabel);
388
389     /* Skip the opening paren */
390     ConsumeLParen ();
391
392     /* Parse the initializer expression */
393     if (CurTok.Tok != TOK_SEMI) {
394         expression (&lval1);
395     }
396     ConsumeSemi ();
397
398     /* Label for the test expressions */
399     g_defcodelabel (TestLabel);
400
401     /* Parse the test expression */
402     if (CurTok.Tok != TOK_SEMI) {
403         Test (BodyLabel, 1);
404         g_jump (BreakLabel);
405     } else {
406         g_jump (BodyLabel);
407     }
408     ConsumeSemi ();
409
410     /* Remember the start of the increment expression */
411     IncExprStart = GetCodePos();
412
413     /* Label for the increment expression */
414     g_defcodelabel (IncLabel);
415
416     /* Parse the increment expression */
417     HaveIncExpr = (CurTok.Tok != TOK_RPAREN);
418     if (HaveIncExpr) {
419         expression (&lval3);
420     }
421
422     /* Jump to the test */
423     g_jump (TestLabel);
424
425     /* Remember the end of the increment expression */
426     IncExprEnd = GetCodePos();
427
428     /* Skip the closing paren */
429     ConsumeRParen ();
430
431     /* Loop body */
432     g_defcodelabel (BodyLabel);
433     Statement (&PendingToken);
434
435     /* If we had an increment expression, move the code to the bottom of
436      * the loop. In this case we don't need to jump there at the end of
437      * the loop body.
438      */
439     if (HaveIncExpr) {
440         MoveCode (IncExprStart, IncExprEnd, GetCodePos());
441     } else {
442         /* Jump back to the increment expression */
443         g_jump (IncLabel);
444     }
445
446     /* Skip a pending token if we have one */
447     SkipPending (PendingToken);
448
449     /* Declare the break label */
450     g_defcodelabel (BreakLabel);
451
452     /* Remove the loop from the loop stack */
453     DelLoop ();
454 }
455
456
457
458 static int CompoundStatement (void)
459 /* Compound statement. Allow any number of statements inside braces. The
460  * function returns true if the last statement was a break or return.
461  */
462 {
463     int GotBreak;
464
465     /* Remember the stack at block entry */
466     int OldStack = oursp;
467
468     /* Enter a new lexical level */
469     EnterBlockLevel ();
470
471     /* Parse local variable declarations if any */
472     DeclareLocals ();
473
474     /* Now process statements in this block */
475     GotBreak = 0;
476     while (CurTok.Tok != TOK_RCURLY) {
477         if (CurTok.Tok != TOK_CEOF) {
478             GotBreak = Statement (0);
479         } else {
480             break;
481         }
482     }
483
484     /* Clean up the stack. */
485     if (!GotBreak) {
486         g_space (oursp - OldStack);
487     }
488     oursp = OldStack;
489
490     /* Emit references to imports/exports for this block */
491     EmitExternals ();
492
493     /* Leave the lexical level */
494     LeaveBlockLevel ();
495
496     return GotBreak;
497 }
498
499
500
501 int Statement (int* PendingToken)
502 /* Statement parser. Returns 1 if the statement does a return/break, returns
503  * 0 otherwise. If the PendingToken pointer is not NULL, the function will
504  * not skip the terminating token of the statement (closing brace or
505  * semicolon), but store true if there is a pending token, and false if there
506  * is none. The token is always checked, so there is no need for the caller to
507  * check this token, it must be skipped, however. If the argument pointer is
508  * NULL, the function will skip the token.
509  */
510 {
511     ExprDesc lval;
512     int GotBreak;
513
514     /* Assume no pending token */
515     if (PendingToken) {
516         *PendingToken = 0;
517     }
518
519     /* Check for a label */
520     if (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) {
521
522         /* Special handling for a label */
523         DoLabel ();
524
525     } else {
526
527         switch (CurTok.Tok) {
528
529             case TOK_LCURLY:
530                 NextToken ();
531                 GotBreak = CompoundStatement ();
532                 CheckTok (TOK_RCURLY, "`{' expected", PendingToken);
533                 return GotBreak;
534
535             case TOK_IF:
536                 return IfStatement ();
537
538             case TOK_WHILE:
539                 WhileStatement ();
540                 break;
541
542             case TOK_DO:
543                 DoStatement ();
544                 break;
545
546             case TOK_SWITCH:
547                 SwitchStatement ();
548                 break;
549
550             case TOK_RETURN:
551                 ReturnStatement ();
552                 CheckSemi (PendingToken);
553                 return 1;
554
555             case TOK_BREAK:
556                 BreakStatement ();
557                 CheckSemi (PendingToken);
558                 return 1;
559
560             case TOK_CONTINUE:
561                 ContinueStatement ();
562                 CheckSemi (PendingToken);
563                 return 1;
564
565             case TOK_FOR:
566                 ForStatement ();
567                 break;
568
569             case TOK_GOTO:
570                 GotoStatement ();
571                 CheckSemi (PendingToken);
572                 return 1;
573
574             case TOK_SEMI:
575                 /* Ignore it */
576                 CheckSemi (PendingToken);
577                 break;
578
579             case TOK_PRAGMA:
580                 DoPragma ();
581                 break;
582
583             default:
584                 /* Actual statement */
585                 expression (&lval);
586                 CheckSemi (PendingToken);
587         }
588     }
589     return 0;
590 }
591
592
593
594