/*
* 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 <stdlib.h>
#include <string.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1