X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=parser.c;h=7d9b3347762a90999398158718d0e5bf4fbc0460;hb=65dfc72272eeb6beba7d3a53ebd9a46488000c81;hp=d34696a735cd30a95dd757150edb2991c5fd137a;hpb=64bf64a7a86bc49d31bb0d7a1e9f90f94cf259bb;p=cparser diff --git a/parser.c b/parser.c index d34696a..7d9b334 100644 --- a/parser.c +++ b/parser.c @@ -49,6 +49,9 @@ static context_t *context = NULL; 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. */ @@ -2685,6 +2688,31 @@ static void parse_kr_declaration_list(declaration_t *declaration) 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 @@ -2778,6 +2806,7 @@ static void parse_external_declaration(void) current_function = declaration; declaration->init.statement = parse_compound_statement(); + check_for_missing_labels(); assert(current_function == declaration); current_function = old_current_function; @@ -4849,23 +4878,44 @@ static statement_t *parse_case_statement(void) 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. */ @@ -4878,6 +4928,25 @@ static statement_t *parse_default_statement(void) 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; @@ -4989,13 +5058,22 @@ static statement_t *parse_switch(void) 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. */ @@ -5010,7 +5088,8 @@ static statement_t *parse_while(void) expect('('); statement->condition = parse_expression(); expect(')'); - statement->body = parse_statement(); + + statement->body = parse_loop_body((statement_t*)statement); return (statement_t*) statement; } @@ -5026,7 +5105,7 @@ static statement_t *parse_do(void) 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(); @@ -5072,7 +5151,7 @@ static statement_t *parse_for(void) 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); @@ -5105,6 +5184,13 @@ static statement_t *parse_goto(void) 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; @@ -5115,14 +5201,20 @@ static statement_t *parse_goto(void) */ 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; } /** @@ -5130,14 +5222,20 @@ static statement_t *parse_continue(void) */ 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; } /**