/* * Copyright (c) 2004 - 2007, Nils R. Weller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Parsing of control structures * XXX This stuff is ugly */ #include "control.h" #include #include #include "token.h" #include "error.h" #include "expr.h" #include "decl.h" #include "functions.h" #include "inlineasm.h" #include "misc.h" #include "debug.h" #include "icode.h" #include "type.h" #include "scope.h" #include "n_libc.h" struct control *curcont = NULL; struct control *curloop = NULL; struct control * alloc_control(void) { struct control *ret = n_xmalloc(sizeof *ret); static struct control nullctrl; *ret = nullctrl; return ret; } static struct label * alloc_label(void) { struct label *ret = n_xmalloc(sizeof *ret); static struct label nulllabel; *ret = nulllabel; return ret; } void append_label_list(struct label **head, struct label **tail, struct label *l) { if (*head == NULL) { *head = *tail = l; } else { (*tail)->next = l; *tail = (*tail)->next; } } static void set_parent_statement(struct control *c, struct control *parent) { if (c == parent) { printf("BUG: Control structure %d cannot be its own parent\n", c->type); abort(); } c->parent = parent; } static void find_parent_statement(struct control *ctrl, struct token *nexttok,int compound); static struct control *elseok = NULL; static struct control *is_dowhile = 0; struct control * parse_ctrl(struct control *parent, struct token **tok, int putscope, int *compound0) { struct token *t = *tok; struct token *starttok = t; struct control *cont; struct control *tmpcont; struct control *cont2; struct statement *stmt; struct expr *ex; struct label *label; char *p; int err = 0; int finishing = 0; if (is_dowhile) { cont = is_dowhile; finishing = 1; } else { cont = alloc_control(); cont->type = t->type; #if 0 cont->parent = /*curcont*/parent; #endif set_parent_statement(cont, parent); /* * Record whether the current ctrl structure should be * stored in the current scope. I think this only applies * to do { } while, and could perhaps be solved better */ cont->putscope = putscope; } cont->tok = *tok; /* * All of these control structures may only be used within a * function. We're at the top level (invalid) if func = NULL */ if (curfunc == NULL) { /* * Error! But continue processing statement in order to prevent * further syntax errors */ errorfl(*tok, "Invalid `%s' statement at top-level. May only be used in functions!", (*tok)->ascii); err = 1; /* XXX this sux */ } switch (t->type) { case TOK_KEY_DO: cont->startlabel = icode_make_label(NULL); cont->endlabel = icode_make_label(NULL); cont->do_cond = icode_make_label(NULL); break; case TOK_KEY_WHILE: if (expect_token(&t, TOK_PAREN_OPEN, 1) != 0) { goto fail; } free(t->prev); ex = parse_expr(&t, TOK_PAREN_CLOSE, 0, 0, 1); if (is_dowhile) { /* End of do ... while */ if (expect_token(&t, TOK_SEMICOLON, 0) != 0) { goto fail; } cont->cond = ex; is_dowhile = NULL; /* * Check whether we need to complete an outer * do-while or if-else, or whether we have to * complete an outer compound statement */ find_parent_statement(/*is_dowhile*/cont, t->next, cont->compound_body); if (cont->putscope) { put_ctrl_scope(cont); } *tok = t; return cont; } else { /* New while loop */ cont->cond = ex; if (putscope) put_ctrl_scope(cont); } cont->startlabel = icode_make_label(NULL); cont->endlabel = icode_make_label(NULL); break; case TOK_KEY_FOR: if (expect_token(&t, TOK_PAREN_OPEN, 1) != 0) { goto fail; } free(t->prev); #if 0 /* c99 style, jeah */ if (IS_TYPE(t)) { struct decl **d = parse_decl(&t, DECL_VARINIT); } else { #endif cont->finit = parse_expr(&t, TOK_SEMICOLON, 0, 0, 1); if (cont->finit == NULL || next_token(&t) != 0) { goto fail; } free(t->prev); cont->cond = parse_expr(&t, TOK_SEMICOLON, 0, 0, 1); if (cont->cond == NULL || next_token(&t) != 0) { goto fail; } free(t->prev); if (t->type != TOK_PAREN_CLOSE) { cont->fcont = parse_expr(&t, TOK_PAREN_CLOSE, 0, 0, 1); cont->fcont_label = icode_make_label(NULL); if (cont->fcont == NULL) { goto fail; } } else { cont->fcont = NULL; } cont->startlabel = icode_make_label(NULL); cont->endlabel = icode_make_label(NULL); if (putscope) put_ctrl_scope(cont); break; case TOK_KEY_IF: if (expect_token(&t, TOK_PAREN_OPEN, 1) != 0) { goto fail; } free(t->prev); cont->cond = parse_expr(&t, TOK_PAREN_CLOSE, 0, 0, 1); cont->endlabel = icode_make_label(NULL); if (putscope) put_ctrl_scope(cont); break; case TOK_KEY_ELSE: if (is_dowhile) { abort(); } if (!elseok) { errorfl(t, "Parse error at `else'"); goto fail; } cont->endlabel = icode_make_label(NULL); cont->prev = elseok; cont->prev->next = cont; elseok = NULL; #if 0 cont->parent = cont->prev->parent; #endif set_parent_statement(cont, cont->prev->parent); break; case TOK_KEY_SWITCH: if (expect_token(&t, TOK_PAREN_OPEN, 1) != 0) { goto fail; } free(t->prev); cont->cond = parse_expr(&t, TOK_PAREN_CLOSE, 0, 0, 1); cont->endlabel = icode_make_label(NULL); if (putscope) put_ctrl_scope(cont); break; case TOK_KEY_BREAK: case TOK_KEY_CONTINUE: p = cont->type == TOK_KEY_BREAK? "break": "continue"; if (expect_token(&t, TOK_SEMICOLON, 0) != 0) { goto fail; } for (tmpcont = cont->parent; tmpcont != NULL; tmpcont = tmpcont->parent) { if (tmpcont->type == TOK_KEY_DO || tmpcont->type == TOK_KEY_WHILE || tmpcont->type == TOK_KEY_FOR) { break; } else if (cont->type == TOK_KEY_BREAK && tmpcont->type == TOK_KEY_SWITCH) { break; } } if (tmpcont == NULL) { errorfl(t, "`%s' used outside of loop", p); goto fail; } if (cont->type == TOK_KEY_BREAK) { cont->endlabel = tmpcont->endlabel; } else { /* continue */ if (tmpcont->type == TOK_KEY_DO) { cont->startlabel = tmpcont->do_cond; } else if (tmpcont->type == TOK_KEY_FOR) { if (tmpcont->fcont_label != NULL) { cont->startlabel = tmpcont->fcont_label; } else { cont->startlabel = tmpcont->startlabel; } } else { cont->startlabel = tmpcont->startlabel; } } *tok = t; if (putscope) put_ctrl_scope(cont); return cont; case TOK_KEY_RETURN: if (next_token(&t) != 0) { goto fail; } if (t->type != TOK_SEMICOLON) { cont->cond = parse_expr(&t, TOK_SEMICOLON, 0, 0, 1); if (cont->cond == NULL) { goto fail; } } else { cont->cond = NULL; } if (putscope) put_ctrl_scope(cont); *tok = t; return cont; case TOK_KEY_GOTO: if (next_token(&t) != 0) { goto fail; } if (t->type != TOK_IDENTIFIER) { errorfl(t, "Invalid argument to `goto' - must be label"); goto fail; } cont->stmt = (struct statement *)t; /* XXX change void *data */ if (expect_token(&t, TOK_SEMICOLON, 0) != 0) { goto fail; } *tok = t; if (putscope) put_ctrl_scope(cont); return cont; case TOK_KEY_CASE: case TOK_KEY_DEFAULT: if (cont->type == TOK_KEY_DEFAULT) { if (expect_token(&t, TOK_OP_AMB_COND2, 0) != 0) { goto fail; } } else { if (next_token(&t) != 0) { goto fail; } cont->cond = parse_expr(&t, TOK_OP_AMB_COND2, 0, EXPR_CONST, 1); if (cont->cond == NULL) { goto fail; } else { struct type *tmp; tmp = cont->cond->const_value->type; if (!is_integral_type(tmp)) { errorfl(*tok, "Switch case expression " "doesn't have integral " "type"); goto fail; } } } while (parent) { if (parent->type == TOK_KEY_SWITCH) { break; } else { parent = parent->parent; } } if (parent == NULL || parent->type != TOK_KEY_SWITCH) { errorfl(t, "`%s' keyword used outside of switch statement", starttok->ascii); goto fail; } for (label = parent->labels; label != NULL; label = label->next) { if (0/* hm */) { errorfl(*tok, "Duplicate switch-case value"); goto fail; } } *tok = t; label = alloc_label(); label->instr = icode_make_label(NULL); label->value = cont->cond; stmt = alloc_statement(); stmt->type = ST_LABEL; stmt->data = label; cont->stmt = stmt; append_label_list(&parent->labels, &parent->labels_tail, label); if (putscope) put_ctrl_scope(cont); return cont; } if (err) goto fail; /* Only loop/if/switch statements reach this point! */ if (next_token(&t) != 0) { goto fail; } /* * Now we read the statement body. Labels are allowed to be placed * in front of any statement, e.g. * * if (foo) label: { * ...code... * } * * In particular, the gcc code contains some such constructs */ for (;;) { if (t->type == TOK_IDENTIFIER) { struct label *l; if (try_label(&t, &l)) { /* Was label! skip ``:'' */ t = t->next; if (cont->body_labels == NULL) { cont->body_labels = alloc_icode_list(); } append_label_list(&curfunc->labels_head, &curfunc->labels_tail, l); append_icode_list(cont->body_labels, l->instr); } else { break; } } else { break; } } if (t->type != TOK_COMP_OPEN) { /* * We can already complete the control * structure here because it only has an * expression or control structure as body, * as opposed to a compound statement */ struct statement *stmt; int compound = 0; if (IS_CONTROL(t->type)) { struct control *parent; if (cont->type == TOK_KEY_ELSE) { parent = cont->parent; } else { parent = cont; } /* * XXX below was contr. that breaks for * if (hm) { if (foo) {} else if (bar) {.. } * ^^ parent should be hm-if */ if ((cont2 = parse_ctrl(cont, &t, 0, &compound)) == NULL) { goto fail; } stmt = alloc_statement(); stmt->type = ST_CTRL; stmt->data = cont2; cont->stmt = stmt; } else if (t->type == TOK_KEY_ASM) { struct inline_asm_stmt *inl; if ((inl = parse_inline_asm(&t)) == NULL) { goto fail; } stmt = alloc_statement(); stmt->type = ST_ASM; stmt->data = inl; cont->stmt = stmt; } else { struct expr *ex; ex = parse_expr(&t, TOK_SEMICOLON, 0, 0, 1); if (ex == NULL) { goto fail; } /* XXX t = t->next ??? */ stmt = alloc_statement(); stmt->type = ST_CODE; stmt->data = ex; cont->stmt = stmt; } if (cont->type == TOK_KEY_IF) { if (t->next == NULL) { goto fail; } if (t->next->type == TOK_KEY_ELSE && !compound) { /* * If the if-body is a non-compound * statement, we can already read the * corresponding else here */ t = t->next; elseok = cont; if ((cont2 = parse_ctrl(cont->parent /* hmm*/, &t, 0, &compound)) == NULL) { goto fail; } cont->next = cont2; } } else if (cont->type == TOK_KEY_DO) { if (!compound) { if (next_token(&t) != 0) { goto fail; } is_dowhile = cont; if (parse_ctrl(cont, &t, 0, NULL) == NULL) { goto fail; } } } /* * All of these tons and tons of immensely complex code * do not suffice to make it all work. As a last resort, * we now check whether a parent statement has not yet * been completed, and if that's the case, prepare to * complete it. If we have something like * * do * if (1) for (;;) { puts("hello"); } * else puts("hmm"); * while (0); * * ... then the compound ``for()'' causes the outer do- * while not to be completed. When we're done reading * the ``else'', it is also necessary to look for * non-compound parent do-while statements, such as the * one above, or if-else statements. Because if those * are not completed, the ``while (0);'' above will be * read as a while-loop with an empty statement as * body */ if (!compound) { find_parent_statement(cont, t->next, 0); } if (compound0 != NULL) { *compound0 = compound; } } else { /* Is compound statement - complete_ctrl() does the rest */ curcont = cont; t = t->prev; if (compound0 != NULL) { *compound0 = 1; } } *tok = t; return cont; fail: *tok = t; if (cont) free(cont); return NULL; } static void find_parent_statement(struct control *ctrl, struct token *nexttok, int compound) { struct control *saved_curcont = curcont; if (ctrl->parent) { /* * curcont is used to match a closing brace (}) with the * opening brace of a control structure. So to match with * the next control structure above, we need to find one * that has a compound statement ({ ... }) as body. I * really really hope this approach FINALLY correct */ curcont = ctrl->parent; while (!curcont->compound_body) { if (elseok != NULL || is_dowhile != NULL) { /* * We have already found something that * must be completed; now we're only * interested in the compound parent * anymore */ if ((curcont = curcont->parent) == NULL) { break; } continue; } if (curcont->type == TOK_KEY_IF && nexttok->type == TOK_KEY_ELSE) { /* * We're at the end of an if-statement, * so there may be an ``else'' part * following it. Note that this * doesn't work for do-while because the * do-while parse isn't complete when * this function is called */ if (elseok == NULL) { /* * Next parse_ctrl() may read an * else */ /* elseok = ctrl;*/ elseok = curcont; } } else if (curcont->type == TOK_KEY_DO) { /* * Finishing do-while with control * structure containing a compound * statement as body; * * do if (foo) { stuff; } while (0); */ is_dowhile = curcont; } if ((curcont = curcont->parent) == NULL) { break; } } } else { curcont = NULL; } if (!compound) { /* * We are only checking whether we have to complete a * parent do-while (by reading the while part) or an * if (by reading the else part, if any.) We are only * interested in direct, noncompound parents */ if (is_dowhile != NULL) { if (is_dowhile->compound_body) { is_dowhile = NULL; } else { /* Found a match */ } } else if (elseok) { if (elseok->compound_body) { elseok = NULL; } else { /* Found a match */ } } curcont = saved_curcont; } } void complete_ctrl(struct token **tok, struct control *ctrl) { elseok = NULL, is_dowhile = 0; if ((*tok)->next == NULL) { errorfl(*tok, "Premature end of file"); return; } if (ctrl->type == TOK_KEY_IF) { if ((*tok)->next->type == TOK_KEY_ELSE) { /* Wait for next parse_ctrl() */ elseok = ctrl; } } else if (ctrl->type == TOK_KEY_DO) { if ((*tok)->next->type == TOK_KEY_WHILE) { is_dowhile = ctrl; } } /* * If we're dealing with do-while, the next parse_ctrl() * cannot pick up a possible ``else'' because if first has * to get the ``while (...)'' part. So of we have a do- * while, we call find_parent_statement() in parse_ctrl() */ if (ctrl->type != TOK_KEY_DO) { find_parent_statement(ctrl, (*tok)->next, 1 /* compound */); } } int try_label(struct token **tok, struct label **resp) { struct token *t = *tok; if (t->next != NULL && (t->next->type == TOK_OPERATOR && *(int *)t->next->data == TOK_OP_AMB_COND2)) { struct icode_instr *ii; struct label *l; for (l = curfunc->labels_head; l != NULL; l = l->next) { if (strcmp(l->/*instr->dat*/name, t->data) == 0) { errorfl(t, "Duplicate label `%s'", t->ascii); return 1; } } ii = icode_make_label(/*t->data*/NULL); l = n_xmalloc(sizeof *l); l->name = t->data; l->next = NULL; l->value = NULL; l->instr = ii; if (resp) { *resp = l; } else { put_label_scope(l); } (void) next_token(tok); return 1; } return 0; }