static declaration_t *last_declaration = NULL;
static declaration_t *current_function = NULL;
static switch_statement_t *current_switch = NULL;
+static statement_t *current_loop = NULL;
+static goto_statement_t *goto_first = NULL;
+static goto_statement_t *goto_last = NULL;
static struct obstack temp_obst;
/** The current source position. */
declaration->type = type;
}
+/**
+ * Check if all labels are defined in the current function.
+ */
+static void check_for_missing_labels(void)
+{
+ bool first_err = true;
+ for (const goto_statement_t *goto_statement = goto_first;
+ goto_statement != NULL;
+ goto_statement = goto_statement->next) {
+ const declaration_t *label = goto_statement->label;
+
+ if (label->source_position.input_name == NULL) {
+ if (first_err) {
+ first_err = false;
+ diagnosticf("%s: In function '%Y':\n",
+ current_function->source_position.input_name,
+ current_function->symbol);
+ }
+ errorf(goto_statement->statement.source_position,
+ "label '%Y' used but not defined", label->symbol);
+ }
+ }
+ goto_first = goto_last = NULL;
+}
+
static void parse_external_declaration(void)
{
/* function-definitions and declarations both start with declaration
current_function = declaration;
declaration->init.statement = parse_compound_statement();
+ check_for_missing_labels();
assert(current_function == declaration);
current_function = old_current_function;
expect(':');
- if (current_switch != NULL) {
- /* link all cases into the switch statement */
- if (current_switch->last_case == NULL) {
- current_switch->first_case =
- current_switch->last_case = &statement->case_label;
+ if (! is_constant_expression(statement->case_label.expression)) {
+ errorf(statement->base.source_position,
+ "case label does not reduce to an integer constant");
+ } else {
+ /* TODO: check if the case label is already known */
+ if (current_switch != NULL) {
+ /* link all cases into the switch statement */
+ if (current_switch->last_case == NULL) {
+ current_switch->first_case =
+ current_switch->last_case = &statement->case_label;
+ } else {
+ current_switch->last_case->next = &statement->case_label;
+ }
} else {
- current_switch->last_case->next = &statement->case_label;
+ errorf(statement->base.source_position,
+ "case label not within a switch statement");
}
- } else {
- errorf(statement->base.source_position,
- "case label not within a switch statement");
}
statement->case_label.label_statement = parse_statement();
return statement;
}
+/**
+ * Finds an existing default label of a switch statement.
+ */
+static case_label_statement_t *
+find_default_label(const switch_statement_t *statement)
+{
+ for (case_label_statement_t *label = statement->first_case;
+ label != NULL;
+ label = label->next) {
+ if (label->expression == NULL)
+ return label;
+ }
+ return NULL;
+}
+
/**
* Parse a default statement.
*/
statement->base.source_position = token.source_position;
expect(':');
+ if (current_switch != NULL) {
+ const case_label_statement_t *def_label = find_default_label(current_switch);
+ if (def_label != NULL) {
+ errorf(HERE, "multiple default labels in one switch");
+ errorf(def_label->statement.source_position,
+ "this is the first default label");
+ } else {
+ /* link all cases into the switch statement */
+ if (current_switch->last_case == NULL) {
+ current_switch->first_case =
+ current_switch->last_case = &statement->case_label;
+ } else {
+ current_switch->last_case->next = &statement->case_label;
+ }
+ }
+ } else {
+ errorf(statement->base.source_position,
+ "'default' label not within a switch statement");
+ }
statement->label.label_statement = parse_statement();
return statement;
expect(')');
switch_statement_t *rem = current_switch;
- current_switch = &statement;
+ current_switch = statement;
statement->body = parse_statement();
current_switch = rem;
return (statement_t*) statement;
}
+static statement_t *parse_loop_body(statement_t *const loop)
+{
+ statement_t *const rem = current_loop;
+ current_loop = loop;
+ statement_t *const body = parse_statement();
+ current_loop = rem;
+ return body;
+}
+
/**
* Parse a while statement.
*/
expect('(');
statement->condition = parse_expression();
expect(')');
- statement->body = parse_statement();
+
+ statement->body = parse_loop_body((statement_t*)statement);
return (statement_t*) statement;
}
statement->statement.kind = STATEMENT_DO_WHILE;
statement->statement.source_position = token.source_position;
- statement->body = parse_statement();
+ statement->body = parse_loop_body((statement_t*)statement);
expect(T_while);
expect('(');
statement->condition = parse_expression();
statement->step = parse_expression();
}
expect(')');
- statement->body = parse_statement();
+ statement->body = parse_loop_body((statement_t*)statement);
assert(context == &statement->context);
set_context(last_context);
statement->label = label;
+ /* remember the goto's in a list for later checking */
+ if (goto_last == NULL) {
+ goto_first = goto_last = statement;
+ } else {
+ goto_last->next = statement;
+ }
+
expect(';');
return (statement_t*) statement;
*/
static statement_t *parse_continue(void)
{
+ statement_base_t *statement;
+ if (current_loop == NULL) {
+ errorf(HERE, "continue statement not within loop");
+ statement = NULL;
+ } else {
+ statement = allocate_ast_zero(sizeof(statement[0]));
+ statement->kind = STATEMENT_CONTINUE;
+ statement->source_position = token.source_position;
+ }
+
eat(T_continue);
expect(';');
- statement_t *statement = allocate_ast_zero(sizeof(statement[0]));
- statement->kind = STATEMENT_CONTINUE;
- statement->base.source_position = token.source_position;
-
- return statement;
+ return (statement_t*)statement;
}
/**
*/
static statement_t *parse_break(void)
{
+ statement_base_t *statement;
+ if (current_switch == NULL && current_loop == NULL) {
+ errorf(HERE, "break statement not within loop or switch");
+ statement = NULL;
+ } else {
+ statement = allocate_ast_zero(sizeof(statement[0]));
+ statement->kind = STATEMENT_BREAK;
+ statement->source_position = token.source_position;
+ }
+
eat(T_break);
expect(';');
- statement_t *statement = allocate_ast_zero(sizeof(statement[0]));
- statement->kind = STATEMENT_BREAK;
- statement->base.source_position = token.source_position;
-
- return statement;
+ return (statement_t*)statement;
}
/**