1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* stmt.c */ |
4 | /* */ |
5 | /* Parse a statement */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 1998-2010, Ullrich von Bassewitz */ |
10 | /* Roemerstrasse 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 "loadexpr.h" |
55 | #include "locals.h" |
56 | #include "loop.h" |
57 | #include "pragma.h" |
58 | #include "scanner.h" |
59 | #include "stackptr.h" |
60 | #include "stmt.h" |
61 | #include "swstmt.h" |
62 | #include "symtab.h" |
63 | #include "testexpr.h" |
64 | #include "typeconv.h" |
65 | |
66 | |
67 | |
68 | /*****************************************************************************/ |
69 | /* Helper functions */ |
70 | /*****************************************************************************/ |
71 | |
72 | |
73 | |
74 | static int CheckLabelWithoutStatement (void) |
75 | /* Called from Statement() after a label definition. Will check for a |
76 | ** following closing curly brace. This means that a label is not followed |
77 | ** by a statement which is required by the standard. Output an error if so. |
78 | */ |
79 | { |
80 | if (CurTok.Tok == TOK_RCURLY) { |
81 | Error ("Label at end of compound statement" ); |
82 | return 1; |
83 | } else { |
84 | return 0; |
85 | } |
86 | } |
87 | |
88 | |
89 | |
90 | static void CheckTok (token_t Tok, const char* Msg, int* PendingToken) |
91 | /* Helper function for Statement. Will check for Tok and print Msg if not |
92 | ** found. If PendingToken is NULL, it will the skip the token, otherwise |
93 | ** it will store one to PendingToken. |
94 | */ |
95 | { |
96 | if (CurTok.Tok != Tok) { |
97 | Error ("%s" , Msg); |
98 | } else if (PendingToken) { |
99 | *PendingToken = 1; |
100 | } else { |
101 | NextToken (); |
102 | } |
103 | } |
104 | |
105 | |
106 | |
107 | static void CheckSemi (int* PendingToken) |
108 | /* Helper function for Statement. Will check for a semicolon and print an |
109 | ** error message if not found (plus some error recovery). If PendingToken is |
110 | ** NULL, it will the skip the token, otherwise it will store one to |
111 | ** PendingToken. |
112 | ** This function is a special version of CheckTok with the addition of the |
113 | ** error recovery. |
114 | */ |
115 | { |
116 | int HaveToken = (CurTok.Tok == TOK_SEMI); |
117 | if (!HaveToken) { |
118 | Error ("';' expected" ); |
119 | /* Try to be smart about errors */ |
120 | if (CurTok.Tok == TOK_COLON || CurTok.Tok == TOK_COMMA) { |
121 | HaveToken = 1; |
122 | } |
123 | } |
124 | if (HaveToken) { |
125 | if (PendingToken) { |
126 | *PendingToken = 1; |
127 | } else { |
128 | NextToken (); |
129 | } |
130 | } |
131 | } |
132 | |
133 | |
134 | |
135 | static void SkipPending (int PendingToken) |
136 | /* Skip the pending token if we have one */ |
137 | { |
138 | if (PendingToken) { |
139 | NextToken (); |
140 | } |
141 | } |
142 | |
143 | |
144 | |
145 | /*****************************************************************************/ |
146 | /* Code */ |
147 | /*****************************************************************************/ |
148 | |
149 | |
150 | |
151 | static int IfStatement (void) |
152 | /* Handle an 'if' statement */ |
153 | { |
154 | unsigned Label1; |
155 | unsigned TestResult; |
156 | int GotBreak; |
157 | |
158 | /* Skip the if */ |
159 | NextToken (); |
160 | |
161 | /* Generate a jump label and parse the condition */ |
162 | Label1 = GetLocalLabel (); |
163 | TestResult = TestInParens (Label1, 0); |
164 | |
165 | /* Parse the if body */ |
166 | GotBreak = Statement (0); |
167 | |
168 | /* Else clause present? */ |
169 | if (CurTok.Tok != TOK_ELSE) { |
170 | |
171 | g_defcodelabel (Label1); |
172 | |
173 | /* Since there's no else clause, we're not sure, if the a break |
174 | ** statement is really executed. |
175 | */ |
176 | return 0; |
177 | |
178 | } else { |
179 | |
180 | /* Generate a jump around the else branch */ |
181 | unsigned Label2 = GetLocalLabel (); |
182 | g_jump (Label2); |
183 | |
184 | /* Skip the else */ |
185 | NextToken (); |
186 | |
187 | /* If the if expression was always true, the code in the else branch |
188 | ** is never executed. Output a warning if this is the case. |
189 | */ |
190 | if (TestResult == TESTEXPR_TRUE) { |
191 | Warning ("Unreachable code" ); |
192 | } |
193 | |
194 | /* Define the target for the first test */ |
195 | g_defcodelabel (Label1); |
196 | |
197 | /* Total break only if both branches had a break. */ |
198 | GotBreak &= Statement (0); |
199 | |
200 | /* Generate the label for the else clause */ |
201 | g_defcodelabel (Label2); |
202 | |
203 | /* Done */ |
204 | return GotBreak; |
205 | } |
206 | } |
207 | |
208 | |
209 | |
210 | static void DoStatement (void) |
211 | /* Handle the 'do' statement */ |
212 | { |
213 | /* Get the loop control labels */ |
214 | unsigned LoopLabel = GetLocalLabel (); |
215 | unsigned BreakLabel = GetLocalLabel (); |
216 | unsigned ContinueLabel = GetLocalLabel (); |
217 | |
218 | /* Skip the while token */ |
219 | NextToken (); |
220 | |
221 | /* Add the loop to the loop stack */ |
222 | AddLoop (BreakLabel, ContinueLabel); |
223 | |
224 | /* Define the loop label */ |
225 | g_defcodelabel (LoopLabel); |
226 | |
227 | /* Parse the loop body */ |
228 | Statement (0); |
229 | |
230 | /* Output the label for a continue */ |
231 | g_defcodelabel (ContinueLabel); |
232 | |
233 | /* Parse the end condition */ |
234 | Consume (TOK_WHILE, "'while' expected" ); |
235 | TestInParens (LoopLabel, 1); |
236 | ConsumeSemi (); |
237 | |
238 | /* Define the break label */ |
239 | g_defcodelabel (BreakLabel); |
240 | |
241 | /* Remove the loop from the loop stack */ |
242 | DelLoop (); |
243 | } |
244 | |
245 | |
246 | |
247 | static void WhileStatement (void) |
248 | /* Handle the 'while' statement */ |
249 | { |
250 | int PendingToken; |
251 | CodeMark CondCodeStart; /* Start of condition evaluation code */ |
252 | CodeMark CondCodeEnd; /* End of condition evaluation code */ |
253 | CodeMark Here; /* "Here" location of code */ |
254 | |
255 | /* Get the loop control labels */ |
256 | unsigned LoopLabel = GetLocalLabel (); |
257 | unsigned BreakLabel = GetLocalLabel (); |
258 | unsigned CondLabel = GetLocalLabel (); |
259 | |
260 | /* Skip the while token */ |
261 | NextToken (); |
262 | |
263 | /* Add the loop to the loop stack. In case of a while loop, the condition |
264 | ** label is used for continue statements. |
265 | */ |
266 | AddLoop (BreakLabel, CondLabel); |
267 | |
268 | /* We will move the code that evaluates the while condition to the end of |
269 | ** the loop, so generate a jump here. |
270 | */ |
271 | g_jump (CondLabel); |
272 | |
273 | /* Remember the current position */ |
274 | GetCodePos (&CondCodeStart); |
275 | |
276 | /* Test the loop condition */ |
277 | TestInParens (LoopLabel, 1); |
278 | |
279 | /* Remember the end of the condition evaluation code */ |
280 | GetCodePos (&CondCodeEnd); |
281 | |
282 | /* Define the head label */ |
283 | g_defcodelabel (LoopLabel); |
284 | |
285 | /* Loop body */ |
286 | Statement (&PendingToken); |
287 | |
288 | /* Emit the while condition label */ |
289 | g_defcodelabel (CondLabel); |
290 | |
291 | /* Move the test code here */ |
292 | GetCodePos (&Here); |
293 | MoveCode (&CondCodeStart, &CondCodeEnd, &Here); |
294 | |
295 | /* Exit label */ |
296 | g_defcodelabel (BreakLabel); |
297 | |
298 | /* Eat remaining tokens that were delayed because of line info |
299 | ** correctness |
300 | */ |
301 | SkipPending (PendingToken); |
302 | |
303 | /* Remove the loop from the loop stack */ |
304 | DelLoop (); |
305 | } |
306 | |
307 | |
308 | |
309 | static void ReturnStatement (void) |
310 | /* Handle the 'return' statement */ |
311 | { |
312 | ExprDesc Expr; |
313 | |
314 | NextToken (); |
315 | if (CurTok.Tok != TOK_SEMI) { |
316 | |
317 | /* Evaluate the return expression */ |
318 | hie0 (&Expr); |
319 | |
320 | /* If we return something in a void function, print an error and |
321 | ** ignore the value. Otherwise convert the value to the type of the |
322 | ** return. |
323 | */ |
324 | if (F_HasVoidReturn (CurrentFunc)) { |
325 | Error ("Returning a value in function with return type void" ); |
326 | } else { |
327 | /* Convert the return value to the type of the function result */ |
328 | TypeConversion (&Expr, F_GetReturnType (CurrentFunc)); |
329 | |
330 | /* Load the value into the primary */ |
331 | LoadExpr (CF_NONE, &Expr); |
332 | } |
333 | |
334 | } else if (!F_HasVoidReturn (CurrentFunc) && !F_HasOldStyleIntRet (CurrentFunc)) { |
335 | Error ("Function '%s' must return a value" , F_GetFuncName (CurrentFunc)); |
336 | } |
337 | |
338 | /* Mark the function as having a return statement */ |
339 | F_ReturnFound (CurrentFunc); |
340 | |
341 | /* Cleanup the stack in case we're inside a block with locals */ |
342 | g_space (StackPtr - F_GetTopLevelSP (CurrentFunc)); |
343 | |
344 | /* Output a jump to the function exit code */ |
345 | g_jump (F_GetRetLab (CurrentFunc)); |
346 | } |
347 | |
348 | |
349 | |
350 | static void BreakStatement (void) |
351 | /* Handle the 'break' statement */ |
352 | { |
353 | LoopDesc* L; |
354 | |
355 | /* Skip the break */ |
356 | NextToken (); |
357 | |
358 | /* Get the current loop descriptor */ |
359 | L = CurrentLoop (); |
360 | |
361 | /* Check if we are inside a loop */ |
362 | if (L == 0) { |
363 | /* Error: No current loop */ |
364 | Error ("'break' statement not within loop or switch" ); |
365 | return; |
366 | } |
367 | |
368 | /* Correct the stack pointer if needed */ |
369 | g_space (StackPtr - L->StackPtr); |
370 | |
371 | /* Jump to the exit label of the loop */ |
372 | g_jump (L->BreakLabel); |
373 | } |
374 | |
375 | |
376 | |
377 | static void ContinueStatement (void) |
378 | /* Handle the 'continue' statement */ |
379 | { |
380 | LoopDesc* L; |
381 | |
382 | /* Skip the continue */ |
383 | NextToken (); |
384 | |
385 | /* Get the current loop descriptor */ |
386 | L = CurrentLoop (); |
387 | if (L) { |
388 | /* Search for a loop that has a continue label. */ |
389 | do { |
390 | if (L->ContinueLabel) { |
391 | break; |
392 | } |
393 | L = L->Next; |
394 | } while (L); |
395 | } |
396 | |
397 | /* Did we find it? */ |
398 | if (L == 0) { |
399 | Error ("'continue' statement not within a loop" ); |
400 | return; |
401 | } |
402 | |
403 | /* Correct the stackpointer if needed */ |
404 | g_space (StackPtr - L->StackPtr); |
405 | |
406 | /* Jump to next loop iteration */ |
407 | g_jump (L->ContinueLabel); |
408 | } |
409 | |
410 | |
411 | |
412 | static void ForStatement (void) |
413 | /* Handle a 'for' statement */ |
414 | { |
415 | ExprDesc lval1; |
416 | ExprDesc lval3; |
417 | int HaveIncExpr; |
418 | CodeMark IncExprStart; |
419 | CodeMark IncExprEnd; |
420 | int PendingToken; |
421 | |
422 | /* Get several local labels needed later */ |
423 | unsigned TestLabel = GetLocalLabel (); |
424 | unsigned BreakLabel = GetLocalLabel (); |
425 | unsigned IncLabel = GetLocalLabel (); |
426 | unsigned BodyLabel = GetLocalLabel (); |
427 | |
428 | /* Skip the FOR token */ |
429 | NextToken (); |
430 | |
431 | /* Add the loop to the loop stack. A continue jumps to the start of the |
432 | ** the increment condition. |
433 | */ |
434 | AddLoop (BreakLabel, IncLabel); |
435 | |
436 | /* Skip the opening paren */ |
437 | ConsumeLParen (); |
438 | |
439 | /* Parse the initializer expression */ |
440 | if (CurTok.Tok != TOK_SEMI) { |
441 | Expression0 (&lval1); |
442 | } |
443 | ConsumeSemi (); |
444 | |
445 | /* Label for the test expressions */ |
446 | g_defcodelabel (TestLabel); |
447 | |
448 | /* Parse the test expression */ |
449 | if (CurTok.Tok != TOK_SEMI) { |
450 | Test (BodyLabel, 1); |
451 | g_jump (BreakLabel); |
452 | } else { |
453 | g_jump (BodyLabel); |
454 | } |
455 | ConsumeSemi (); |
456 | |
457 | /* Remember the start of the increment expression */ |
458 | GetCodePos (&IncExprStart); |
459 | |
460 | /* Label for the increment expression */ |
461 | g_defcodelabel (IncLabel); |
462 | |
463 | /* Parse the increment expression */ |
464 | HaveIncExpr = (CurTok.Tok != TOK_RPAREN); |
465 | if (HaveIncExpr) { |
466 | Expression0 (&lval3); |
467 | } |
468 | |
469 | /* Jump to the test */ |
470 | g_jump (TestLabel); |
471 | |
472 | /* Remember the end of the increment expression */ |
473 | GetCodePos (&IncExprEnd); |
474 | |
475 | /* Skip the closing paren */ |
476 | ConsumeRParen (); |
477 | |
478 | /* Loop body */ |
479 | g_defcodelabel (BodyLabel); |
480 | Statement (&PendingToken); |
481 | |
482 | /* If we had an increment expression, move the code to the bottom of |
483 | ** the loop. In this case we don't need to jump there at the end of |
484 | ** the loop body. |
485 | */ |
486 | if (HaveIncExpr) { |
487 | CodeMark Here; |
488 | GetCodePos (&Here); |
489 | MoveCode (&IncExprStart, &IncExprEnd, &Here); |
490 | } else { |
491 | /* Jump back to the increment expression */ |
492 | g_jump (IncLabel); |
493 | } |
494 | |
495 | /* Skip a pending token if we have one */ |
496 | SkipPending (PendingToken); |
497 | |
498 | /* Declare the break label */ |
499 | g_defcodelabel (BreakLabel); |
500 | |
501 | /* Remove the loop from the loop stack */ |
502 | DelLoop (); |
503 | } |
504 | |
505 | |
506 | |
507 | static int CompoundStatement (void) |
508 | /* Compound statement. Allow any number of statements inside braces. The |
509 | ** function returns true if the last statement was a break or return. |
510 | */ |
511 | { |
512 | int GotBreak; |
513 | |
514 | /* Remember the stack at block entry */ |
515 | int OldStack = StackPtr; |
516 | unsigned OldBlockStackSize = CollCount (&CurrentFunc->LocalsBlockStack); |
517 | |
518 | /* Enter a new lexical level */ |
519 | EnterBlockLevel (); |
520 | |
521 | /* Parse local variable declarations if any */ |
522 | DeclareLocals (); |
523 | |
524 | /* Now process statements in this block */ |
525 | GotBreak = 0; |
526 | while (CurTok.Tok != TOK_RCURLY) { |
527 | if (CurTok.Tok != TOK_CEOF) { |
528 | GotBreak = Statement (0); |
529 | } else { |
530 | break; |
531 | } |
532 | } |
533 | |
534 | /* Clean up the stack. */ |
535 | if (!GotBreak) { |
536 | g_space (StackPtr - OldStack); |
537 | } |
538 | |
539 | /* If the segment had autoinited variables, let's pop it of a stack |
540 | ** of such blocks. |
541 | */ |
542 | if (OldBlockStackSize != CollCount (&CurrentFunc->LocalsBlockStack)) { |
543 | CollPop (&CurrentFunc->LocalsBlockStack); |
544 | } |
545 | |
546 | StackPtr = OldStack; |
547 | |
548 | /* Emit references to imports/exports for this block */ |
549 | EmitExternals (); |
550 | |
551 | /* Leave the lexical level */ |
552 | LeaveBlockLevel (); |
553 | |
554 | return GotBreak; |
555 | } |
556 | |
557 | |
558 | |
559 | int Statement (int* PendingToken) |
560 | /* Statement parser. Returns 1 if the statement does a return/break, returns |
561 | ** 0 otherwise. If the PendingToken pointer is not NULL, the function will |
562 | ** not skip the terminating token of the statement (closing brace or |
563 | ** semicolon), but store true if there is a pending token, and false if there |
564 | ** is none. The token is always checked, so there is no need for the caller to |
565 | ** check this token, it must be skipped, however. If the argument pointer is |
566 | ** NULL, the function will skip the token. |
567 | */ |
568 | { |
569 | ExprDesc Expr; |
570 | int GotBreak; |
571 | CodeMark Start, End; |
572 | |
573 | /* Assume no pending token */ |
574 | if (PendingToken) { |
575 | *PendingToken = 0; |
576 | } |
577 | |
578 | /* Check for a label. A label is always part of a statement, it does not |
579 | ** replace one. |
580 | */ |
581 | while (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) { |
582 | /* Handle the label */ |
583 | DoLabel (); |
584 | if (CheckLabelWithoutStatement ()) { |
585 | return 0; |
586 | } |
587 | } |
588 | |
589 | switch (CurTok.Tok) { |
590 | |
591 | case TOK_LCURLY: |
592 | NextToken (); |
593 | GotBreak = CompoundStatement (); |
594 | CheckTok (TOK_RCURLY, "'{' expected" , PendingToken); |
595 | return GotBreak; |
596 | |
597 | case TOK_IF: |
598 | return IfStatement (); |
599 | |
600 | case TOK_WHILE: |
601 | WhileStatement (); |
602 | break; |
603 | |
604 | case TOK_DO: |
605 | DoStatement (); |
606 | break; |
607 | |
608 | case TOK_SWITCH: |
609 | SwitchStatement (); |
610 | break; |
611 | |
612 | case TOK_RETURN: |
613 | ReturnStatement (); |
614 | CheckSemi (PendingToken); |
615 | return 1; |
616 | |
617 | case TOK_BREAK: |
618 | BreakStatement (); |
619 | CheckSemi (PendingToken); |
620 | return 1; |
621 | |
622 | case TOK_CONTINUE: |
623 | ContinueStatement (); |
624 | CheckSemi (PendingToken); |
625 | return 1; |
626 | |
627 | case TOK_FOR: |
628 | ForStatement (); |
629 | break; |
630 | |
631 | case TOK_GOTO: |
632 | GotoStatement (); |
633 | CheckSemi (PendingToken); |
634 | return 1; |
635 | |
636 | case TOK_SEMI: |
637 | /* Ignore it */ |
638 | CheckSemi (PendingToken); |
639 | break; |
640 | |
641 | case TOK_PRAGMA: |
642 | DoPragma (); |
643 | break; |
644 | |
645 | case TOK_CASE: |
646 | CaseLabel (); |
647 | CheckLabelWithoutStatement (); |
648 | break; |
649 | |
650 | case TOK_DEFAULT: |
651 | DefaultLabel (); |
652 | CheckLabelWithoutStatement (); |
653 | break; |
654 | |
655 | default: |
656 | /* Remember the current code position */ |
657 | GetCodePos (&Start); |
658 | /* Actual statement */ |
659 | ExprWithCheck (hie0, &Expr); |
660 | /* Load the result only if it is an lvalue and the type is |
661 | ** marked as volatile. Otherwise the load is useless. |
662 | */ |
663 | if (ED_IsLVal (&Expr) && IsQualVolatile (Expr.Type)) { |
664 | LoadExpr (CF_NONE, &Expr); |
665 | } |
666 | /* If the statement didn't generate code, and is not of type |
667 | ** void, emit a warning. |
668 | */ |
669 | GetCodePos (&End); |
670 | if (CodeRangeIsEmpty (&Start, &End) && |
671 | !IsTypeVoid (Expr.Type) && |
672 | IS_Get (&WarnNoEffect)) { |
673 | Warning ("Statement has no effect" ); |
674 | } |
675 | CheckSemi (PendingToken); |
676 | } |
677 | return 0; |
678 | } |
679 | |