]> git.sur5r.net Git - cc65/blob - src/cc65/stmt.c
Module breakup
[cc65] / src / cc65 / stmt.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                  stmt.c                                   */
4 /*                                                                           */
5 /*                             Parse a statement                             */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2001 Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
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
62
63
64 /*****************************************************************************/
65 /*                                   Data                                    */
66 /*****************************************************************************/
67
68
69
70 /* Maximum count of cases */
71 #define CASE_MAX        257
72
73
74
75 /*****************************************************************************/
76 /*                             Helper functions                              */
77 /*****************************************************************************/
78
79
80
81 static void CheckTok (token_t Tok, const char* Msg, int* PendingToken)
82 /* Helper function for Statement. Will check for Tok and print Msg if not
83  * found. If PendingToken is NULL, it will the skip the token, otherwise
84  * it will store one to PendingToken.
85  */
86 {
87     if (CurTok.Tok != Tok) {
88         Error (Msg);
89     } else if (PendingToken) {
90         *PendingToken = 1;
91     } else {
92         NextToken ();
93     }
94 }
95
96
97
98 static void CheckSemi (int* PendingToken)
99 /* Helper function for Statement. Will call CheckTok with the parameters
100  * for a semicolon.
101  */
102 {
103     CheckTok (TOK_SEMI, "`;' expected", PendingToken);
104 }
105
106
107
108 static void SkipPending (int PendingToken)
109 /* Skip the pending token if we have one */
110 {
111     if (PendingToken) {
112         NextToken ();
113     }
114 }
115
116
117
118 /*****************************************************************************/
119 /*                                   Code                                    */
120 /*****************************************************************************/
121
122
123
124 static int IfStatement (void)
125 /* Handle an 'if' statement */
126 {
127     unsigned Label1;
128     int GotBreak;
129
130     /* Skip the if */
131     NextToken ();
132
133     /* Generate a jump label and parse the condition */
134     Label1 = GetLocalLabel ();
135     test (Label1, 0);
136
137     /* Parse the if body */
138     GotBreak = Statement (0);
139
140     /* Else clause present? */
141     if (CurTok.Tok != TOK_ELSE) {
142
143         g_defcodelabel (Label1);
144
145         /* Since there's no else clause, we're not sure, if the a break
146          * statement is really executed.
147          */
148         return 0;
149
150     } else {
151
152         /* Generate a jump around the else branch */
153         unsigned Label2 = GetLocalLabel ();
154         g_jump (Label2);
155
156         /* Skip the else */
157         NextToken ();
158
159         /* Define the target for the first test */
160         g_defcodelabel (Label1);
161
162         /* Total break only if both branches had a break. */
163         GotBreak &= Statement (0);
164
165         /* Generate the label for the else clause */
166         g_defcodelabel (Label2);
167
168         /* Done */
169         return GotBreak;
170     }
171 }
172
173
174
175 static void DoStatement (void)
176 /* Handle the 'do' statement */
177 {
178     /* Get the loop control labels */
179     unsigned loop = GetLocalLabel ();
180     unsigned lab = GetLocalLabel ();
181
182     /* Skip the while token */
183     NextToken ();
184
185     /* Add the loop to the loop stack */
186     AddLoop (oursp, loop, lab, 0, 0);
187
188     /* Define the head label */
189     g_defcodelabel (loop);
190
191     /* Parse the loop body */
192     Statement (0);
193
194     /* Parse the end condition */
195     Consume (TOK_WHILE, "`while' expected");
196     test (loop, 1);
197     ConsumeSemi ();
198
199     /* Define the break label */
200     g_defcodelabel (lab);
201
202     /* Remove the loop from the loop stack */
203     DelLoop ();
204 }
205
206
207
208 static void WhileStatement (void)
209 /* Handle the 'while' statement */
210 {
211     int PendingToken;
212
213     /* Get the loop control labels */
214     unsigned loop = GetLocalLabel ();
215     unsigned lab = GetLocalLabel ();
216
217     /* Skip the while token */
218     NextToken ();
219
220     /* Add the loop to the loop stack */
221     AddLoop (oursp, loop, lab, 0, 0);
222
223     /* Define the head label */
224     g_defcodelabel (loop);
225
226     /* Test the loop condition */
227     test (lab, 0);
228
229     /* Loop body */
230     Statement (&PendingToken);
231
232     /* Jump back to loop top */
233     g_jump (loop);
234
235     /* Exit label */
236     g_defcodelabel (lab);
237
238     /* Eat remaining tokens that were delayed because of line info 
239      * correctness 
240      */
241     SkipPending (PendingToken);
242
243     /* Remove the loop from the loop stack */
244     DelLoop ();
245 }
246
247
248
249 static void ReturnStatement (void)
250 /* Handle the 'return' statement */
251 {
252     ExprDesc lval;
253
254     NextToken ();
255     if (CurTok.Tok != TOK_SEMI) {
256         if (HasVoidReturn (CurrentFunc)) {
257             Error ("Returning a value in function with return type void");
258         }
259
260         /* Evaluate the return expression. Result will be in primary */
261         expression (&lval);
262
263         /* Convert the return value to the type of the function result */
264         if (!HasVoidReturn (CurrentFunc)) {
265             assignadjust (GetReturnType (CurrentFunc), &lval);
266         }
267     } else if (!HasVoidReturn (CurrentFunc)) {
268         Error ("Function `%s' must return a value", GetFuncName (CurrentFunc));
269     }
270
271     /* Cleanup the stack in case we're inside a block with locals */
272     g_space (oursp - GetTopLevelSP (CurrentFunc));
273
274     /* Output a jump to the function exit code */
275     g_jump (GetRetLab (CurrentFunc));
276 }
277
278
279
280 static void BreakStatement (void)
281 /* Handle the 'break' statement */
282 {
283     LoopDesc* L;
284
285     /* Skip the break */
286     NextToken ();
287
288     /* Get the current loop descriptor */
289     L = CurrentLoop ();
290
291     /* Check if we are inside a loop */
292     if (L == 0) {
293         /* Error: No current loop */
294         Error ("`break' statement not within loop or switch");
295         return;
296     }
297
298     /* Correct the stack pointer if needed */
299     g_space (oursp - L->StackPtr);
300
301     /* Jump to the exit label of the loop */
302     g_jump (L->Label);
303 }
304
305
306
307 static void ContinueStatement (void)
308 /* Handle the 'continue' statement */
309 {
310     LoopDesc* L;
311
312     /* Skip the continue */
313     NextToken ();
314
315     /* Get the current loop descriptor */
316     L = CurrentLoop ();
317     if (L) {
318         /* Search for the correct loop */
319         do {
320             if (L->Loop) {
321                 break;
322             }
323             L = L->Next;
324         } while (L);
325     }
326
327     /* Did we find it? */
328     if (L == 0) {
329         Error ("`continue' statement not within a loop");
330         return;
331     }
332
333     /* Correct the stackpointer if needed */
334     g_space (oursp - L->StackPtr);
335
336     /* Output the loop code */
337     if (L->linc) {
338         g_jump (L->linc);
339     } else {
340         g_jump (L->Loop);
341     }
342 }
343
344
345
346 static void ForStatement (void)
347 /* Handle a 'for' statement */
348 {
349     ExprDesc lval1;
350     ExprDesc lval2;
351     ExprDesc lval3;
352     int HaveIncExpr;
353     CodeMark IncExprStart;
354     CodeMark IncExprEnd;
355     int PendingToken;
356
357     /* Get several local labels needed later */
358     unsigned TestLabel = GetLocalLabel ();
359     unsigned lab       = GetLocalLabel ();
360     unsigned IncLabel  = GetLocalLabel ();
361     unsigned lstat     = GetLocalLabel ();
362
363     /* Skip the FOR token */
364     NextToken ();
365
366     /* Add the loop to the loop stack */
367     AddLoop (oursp, TestLabel, lab, IncLabel, lstat);
368
369     /* Skip the opening paren */
370     ConsumeLParen ();
371
372     /* Parse the initializer expression */
373     if (CurTok.Tok != TOK_SEMI) {
374         expression (&lval1);
375     }
376     ConsumeSemi ();
377
378     /* Label for the test expressions */
379     g_defcodelabel (TestLabel);
380
381     /* Parse the test expression */
382     if (CurTok.Tok != TOK_SEMI) {
383         boolexpr (&lval2);
384         g_truejump (CF_NONE, lstat);
385         g_jump (lab);
386     } else {
387         g_jump (lstat);
388     }
389     ConsumeSemi ();
390
391     /* Remember the start of the increment expression */
392     IncExprStart = GetCodePos();
393
394     /* Label for the increment expression */
395     g_defcodelabel (IncLabel);
396
397     /* Parse the increment expression */
398     HaveIncExpr = (CurTok.Tok != TOK_RPAREN);
399     if (HaveIncExpr) {
400         expression (&lval3);
401     }
402
403     /* Jump to the test */
404     g_jump (TestLabel);
405
406     /* Remember the end of the increment expression */
407     IncExprEnd = GetCodePos();
408
409     /* Skip the closing paren */
410     ConsumeRParen ();
411
412     /* Loop body */
413     g_defcodelabel (lstat);
414     Statement (&PendingToken);
415
416     /* If we had an increment expression, move the code to the bottom of
417      * the loop. In this case we don't need to jump there at the end of
418      * the loop body.
419      */
420     if (HaveIncExpr) {
421         MoveCode (IncExprStart, IncExprEnd, GetCodePos());
422     } else {
423         /* Jump back to the increment expression */
424         g_jump (IncLabel);
425     }
426
427     /* Skip a pending token if we have one */
428     SkipPending (PendingToken);
429
430     /* Declare the break label */
431     g_defcodelabel (lab);
432
433     /* Remove the loop from the loop stack */
434     DelLoop ();
435 }
436
437
438
439 static int CompoundStatement (void)
440 /* Compound statement. Allow any number of statements inside braces. The
441  * function returns true if the last statement was a break or return.
442  */
443 {
444     int GotBreak;
445
446     /* Remember the stack at block entry */
447     int OldStack = oursp;
448
449     /* Enter a new lexical level */
450     EnterBlockLevel ();
451
452     /* Parse local variable declarations if any */
453     DeclareLocals ();
454
455     /* Now process statements in this block */
456     GotBreak = 0;
457     while (CurTok.Tok != TOK_RCURLY) {
458         if (CurTok.Tok != TOK_CEOF) {
459             GotBreak = Statement (0);
460         } else {
461             break;
462         }
463     }
464
465     /* Clean up the stack. */
466     if (!GotBreak) {
467         g_space (oursp - OldStack);
468     }
469     oursp = OldStack;
470
471     /* Emit references to imports/exports for this block */
472     EmitExternals ();
473
474     /* Leave the lexical level */
475     LeaveBlockLevel ();
476
477     return GotBreak;
478 }
479
480
481
482 int Statement (int* PendingToken)
483 /* Statement parser. Returns 1 if the statement does a return/break, returns
484  * 0 otherwise. If the PendingToken pointer is not NULL, the function will
485  * not skip the terminating token of the statement (closing brace or
486  * semicolon), but store true if there is a pending token, and false if there
487  * is none. The token is always checked, so there is no need for the caller to
488  * check this token, it must be skipped, however. If the argument pointer is
489  * NULL, the function will skip the token.
490  */
491 {
492     ExprDesc lval;
493     int GotBreak;
494
495     /* Assume no pending token */
496     if (PendingToken) {
497         *PendingToken = 0;
498     }
499
500     /* Check for a label */
501     if (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) {
502
503         /* Special handling for a label */
504         DoLabel ();
505
506     } else {
507
508         switch (CurTok.Tok) {
509
510             case TOK_LCURLY:
511                 NextToken ();
512                 GotBreak = CompoundStatement ();
513                 CheckTok (TOK_RCURLY, "`{' expected", PendingToken);
514                 return GotBreak;
515
516             case TOK_IF:
517                 return IfStatement ();
518
519             case TOK_WHILE:
520                 WhileStatement ();
521                 break;
522
523             case TOK_DO:
524                 DoStatement ();
525                 break;
526
527             case TOK_SWITCH:
528                 SwitchStatement ();
529                 break;
530
531             case TOK_RETURN:
532                 ReturnStatement ();
533                 CheckSemi (PendingToken);
534                 return 1;
535
536             case TOK_BREAK:
537                 BreakStatement ();
538                 CheckSemi (PendingToken);
539                 return 1;
540
541             case TOK_CONTINUE:
542                 ContinueStatement ();
543                 CheckSemi (PendingToken);
544                 return 1;
545
546             case TOK_FOR:
547                 ForStatement ();
548                 break;
549
550             case TOK_GOTO:
551                 GotoStatement ();
552                 CheckSemi (PendingToken);
553                 return 1;
554
555             case TOK_SEMI:
556                 /* Ignore it */
557                 NextToken ();
558                 break;
559
560             case TOK_PRAGMA:
561                 DoPragma ();
562                 break;
563
564             default:
565                 /* Actual statement */
566                 expression (&lval);
567                 CheckSemi (PendingToken);
568         }
569     }
570     return 0;
571 }
572
573
574
575