1 /*****************************************************************************/
5 /* Parse a statement */
9 /* (C) 1998-2003 Ullrich von Bassewitz */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
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. */
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: */
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 */
32 /*****************************************************************************/
65 /*****************************************************************************/
66 /* Helper functions */
67 /*****************************************************************************/
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.
77 if (CurTok.Tok != Tok) {
79 } else if (PendingToken) {
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
93 * This function is a special version of CheckTok with the addition of the
97 int HaveToken = (CurTok.Tok == TOK_SEMI);
99 Error ("`;' expected");
100 /* Try to be smart about errors */
101 if (CurTok.Tok == TOK_COLON || CurTok.Tok == TOK_COMMA) {
116 static void SkipPending (int PendingToken)
117 /* Skip the pending token if we have one */
126 /*****************************************************************************/
128 /*****************************************************************************/
132 static int IfStatement (void)
133 /* Handle an 'if' statement */
141 /* Generate a jump label and parse the condition */
142 Label1 = GetLocalLabel ();
143 TestInParens (Label1, 0);
145 /* Parse the if body */
146 GotBreak = Statement (0);
148 /* Else clause present? */
149 if (CurTok.Tok != TOK_ELSE) {
151 g_defcodelabel (Label1);
153 /* Since there's no else clause, we're not sure, if the a break
154 * statement is really executed.
160 /* Generate a jump around the else branch */
161 unsigned Label2 = GetLocalLabel ();
167 /* Define the target for the first test */
168 g_defcodelabel (Label1);
170 /* Total break only if both branches had a break. */
171 GotBreak &= Statement (0);
173 /* Generate the label for the else clause */
174 g_defcodelabel (Label2);
183 static void DoStatement (void)
184 /* Handle the 'do' statement */
186 /* Get the loop control labels */
187 unsigned LoopLabel = GetLocalLabel ();
188 unsigned BreakLabel = GetLocalLabel ();
189 unsigned ContinueLabel = GetLocalLabel ();
191 /* Skip the while token */
194 /* Add the loop to the loop stack */
195 AddLoop (oursp, BreakLabel, ContinueLabel);
197 /* Define the loop label */
198 g_defcodelabel (LoopLabel);
200 /* Parse the loop body */
203 /* Output the label for a continue */
204 g_defcodelabel (ContinueLabel);
206 /* Parse the end condition */
207 Consume (TOK_WHILE, "`while' expected");
208 TestInParens (LoopLabel, 1);
211 /* Define the break label */
212 g_defcodelabel (BreakLabel);
214 /* Remove the loop from the loop stack */
220 static void WhileStatement (void)
221 /* Handle the 'while' statement */
225 /* Get the loop control labels */
226 unsigned LoopLabel = GetLocalLabel ();
227 unsigned BreakLabel = GetLocalLabel ();
229 /* Skip the while token */
232 /* Add the loop to the loop stack. In case of a while loop, the loop head
233 * label is used for continue statements.
235 AddLoop (oursp, BreakLabel, LoopLabel);
237 /* Define the head label */
238 g_defcodelabel (LoopLabel);
240 /* Test the loop condition */
241 TestInParens (BreakLabel, 0);
244 Statement (&PendingToken);
246 /* Jump back to loop top */
250 g_defcodelabel (BreakLabel);
252 /* Eat remaining tokens that were delayed because of line info
255 SkipPending (PendingToken);
257 /* Remove the loop from the loop stack */
263 static void ReturnStatement (void)
264 /* Handle the 'return' statement */
270 if (CurTok.Tok != TOK_SEMI) {
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");
277 /* Evaluate the return expression */
278 k = hie0 (InitExprDesc (&Expr));
280 /* Ignore the return expression if the function returns void */
281 if (!F_HasVoidReturn (CurrentFunc)) {
283 /* Convert the return value to the type of the function result */
284 k = TypeConversion (&Expr, k, F_GetReturnType (CurrentFunc));
286 /* Load the value into the primary */
287 ExprLoad (CF_NONE, k, &Expr);
290 } else if (!F_HasVoidReturn (CurrentFunc) && !F_HasOldStyleIntRet (CurrentFunc)) {
291 Error ("Function `%s' must return a value", F_GetFuncName (CurrentFunc));
294 /* Cleanup the stack in case we're inside a block with locals */
295 g_space (oursp - F_GetTopLevelSP (CurrentFunc));
297 /* Output a jump to the function exit code */
298 g_jump (F_GetRetLab (CurrentFunc));
303 static void BreakStatement (void)
304 /* Handle the 'break' statement */
311 /* Get the current loop descriptor */
314 /* Check if we are inside a loop */
316 /* Error: No current loop */
317 Error ("`break' statement not within loop or switch");
321 /* Correct the stack pointer if needed */
322 g_space (oursp - L->StackPtr);
324 /* Jump to the exit label of the loop */
325 g_jump (L->BreakLabel);
330 static void ContinueStatement (void)
331 /* Handle the 'continue' statement */
335 /* Skip the continue */
338 /* Get the current loop descriptor */
341 /* Search for a loop that has a continue label. */
343 if (L->ContinueLabel) {
350 /* Did we find it? */
352 Error ("`continue' statement not within a loop");
356 /* Correct the stackpointer if needed */
357 g_space (oursp - L->StackPtr);
359 /* Jump to next loop iteration */
360 g_jump (L->ContinueLabel);
365 static void ForStatement (void)
366 /* Handle a 'for' statement */
371 CodeMark IncExprStart;
375 /* Get several local labels needed later */
376 unsigned TestLabel = GetLocalLabel ();
377 unsigned BreakLabel = GetLocalLabel ();
378 unsigned IncLabel = GetLocalLabel ();
379 unsigned BodyLabel = GetLocalLabel ();
381 /* Skip the FOR token */
384 /* Add the loop to the loop stack. A continue jumps to the start of the
385 * the increment condition.
387 AddLoop (oursp, BreakLabel, IncLabel);
389 /* Skip the opening paren */
392 /* Parse the initializer expression */
393 if (CurTok.Tok != TOK_SEMI) {
398 /* Label for the test expressions */
399 g_defcodelabel (TestLabel);
401 /* Parse the test expression */
402 if (CurTok.Tok != TOK_SEMI) {
410 /* Remember the start of the increment expression */
411 IncExprStart = GetCodePos();
413 /* Label for the increment expression */
414 g_defcodelabel (IncLabel);
416 /* Parse the increment expression */
417 HaveIncExpr = (CurTok.Tok != TOK_RPAREN);
422 /* Jump to the test */
425 /* Remember the end of the increment expression */
426 IncExprEnd = GetCodePos();
428 /* Skip the closing paren */
432 g_defcodelabel (BodyLabel);
433 Statement (&PendingToken);
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
440 MoveCode (IncExprStart, IncExprEnd, GetCodePos());
442 /* Jump back to the increment expression */
446 /* Skip a pending token if we have one */
447 SkipPending (PendingToken);
449 /* Declare the break label */
450 g_defcodelabel (BreakLabel);
452 /* Remove the loop from the loop stack */
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.
465 /* Remember the stack at block entry */
466 int OldStack = oursp;
468 /* Enter a new lexical level */
471 /* Parse local variable declarations if any */
474 /* Now process statements in this block */
476 while (CurTok.Tok != TOK_RCURLY) {
477 if (CurTok.Tok != TOK_CEOF) {
478 GotBreak = Statement (0);
484 /* Clean up the stack. */
486 g_space (oursp - OldStack);
490 /* Emit references to imports/exports for this block */
493 /* Leave the lexical level */
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.
514 /* Assume no pending token */
519 /* Check for a label */
520 if (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) {
522 /* Special handling for a label */
527 switch (CurTok.Tok) {
531 GotBreak = CompoundStatement ();
532 CheckTok (TOK_RCURLY, "`{' expected", PendingToken);
536 return IfStatement ();
552 CheckSemi (PendingToken);
557 CheckSemi (PendingToken);
561 ContinueStatement ();
562 CheckSemi (PendingToken);
571 CheckSemi (PendingToken);
576 CheckSemi (PendingToken);
584 /* Actual statement */
586 CheckSemi (PendingToken);