+#define expect_void(expected) \
+ if(UNLIKELY(token.type != (expected))) { \
+ parse_error_expected(NULL, (expected), 0); \
+ eat_until(';'); \
+ return; \
+ } \
+ next_token();
+
+static void set_context(context_t *new_context)
+{
+ context = new_context;
+
+ declaration_t *declaration = new_context->declarations;
+ if(declaration != NULL) {
+ while(1) {
+ if(declaration->next == NULL)
+ break;
+ declaration = declaration->next;
+ }
+ }
+
+ last_declaration = declaration;
+}
+
+/**
+ * pushs an environment_entry on the environment stack and links the
+ * corresponding symbol to the new entry
+ */
+static inline
+void environment_push(declaration_t *declaration, const void *context)
+{
+ environment_entry_t *entry
+ = obstack_alloc(&environment_obstack, sizeof(entry[0]));
+ memset(entry, 0, sizeof(entry[0]));
+
+ int top = ARR_LEN(environment_stack);
+ ARR_RESIZE(environment_stack, top + 1);
+ environment_stack[top] = entry;
+
+ assert(declaration->source_position.input_name != NULL);
+
+ symbol_t *symbol = declaration->symbol;
+ assert(declaration != symbol->declaration);
+
+ if(symbol->context == context) {
+ if(symbol->declaration != NULL) {
+ assert(symbol->declaration != NULL);
+ parser_print_error_prefix_pos(declaration->source_position);
+ fprintf(stderr, "multiple definitions for symbol '%s'.\n",
+ symbol->string);
+ parser_print_error_prefix_pos(symbol->declaration->source_position);
+ fprintf(stderr, "this is the location of the previous declaration.\n");
+ }
+ }
+
+ entry->old_declaration = symbol->declaration;
+ entry->old_context = symbol->context;
+ entry->symbol = symbol;
+ symbol->declaration = declaration;
+ symbol->context = context;
+}
+
+/**
+ * pops symbols from the environment stack until @p new_top is the top element
+ */
+static inline
+void environment_pop_to(size_t new_top)
+{
+ environment_entry_t *entry = NULL;
+ size_t top = ARR_LEN(environment_stack);
+ size_t i;
+
+ if(new_top == top)
+ return;
+
+ assert(new_top < top);
+ i = top;
+ do {
+ entry = environment_stack[i - 1];
+
+ symbol_t *symbol = entry->symbol;
+
+ symbol->declaration = entry->old_declaration;
+ symbol->context = entry->old_context;
+
+ --i;
+ } while(i != new_top);
+ obstack_free(&environment_obstack, entry);
+
+ ARR_SHRINKLEN(environment_stack, (int) new_top);
+}
+
+
+
+static expression_t *parse_constant_expression(void)
+{
+ /* TODO: not correct yet */
+ return parse_expression();
+}
+
+static expression_t *parse_assignment_expression(void)
+{
+ /* TODO: not correct yet */
+ return parse_expression();
+}
+
+static void parse_compound_type_entries(void);
+static void parse_declarator(declaration_t *declaration,
+ storage_class_t storage_class, type_t *type,
+ int may_omit_identifier);
+static void maybe_push_declaration(declaration_t *declaration);
+static void record_declaration(declaration_t *declaration);
+
+typedef struct declaration_specifiers_t declaration_specifiers_t;
+struct declaration_specifiers_t {
+ storage_class_t storage_class;
+ type_t *type;
+};
+
+static type_t *parse_struct_specifier(void)
+{
+ eat(T_struct);
+
+ compound_type_t *struct_type = allocate_type_zero(sizeof(struct_type[0]));
+ struct_type->type.type = TYPE_COMPOUND_STRUCT;
+ struct_type->source_position = token.source_position;
+
+ int top = environment_top();
+ context_t *last_context = context;
+ set_context(&struct_type->context);
+
+ if(token.type == T_IDENTIFIER) {
+ next_token();
+ if(token.type == '{') {
+ parse_compound_type_entries();
+ }
+ } else if(token.type == '{') {
+ parse_compound_type_entries();
+ } else {
+ parse_error_expected("problem while parsing struct type specifiers",
+ T_IDENTIFIER, '{', 0);
+ struct_type = NULL;
+ }
+
+ assert(context == &struct_type->context);
+ set_context(last_context);
+ environment_pop_to(top);
+
+ return (type_t*) struct_type;
+}
+
+static type_t *parse_union_specifier(void)
+{
+ eat(T_union);
+
+ compound_type_t *union_type = allocate_type_zero(sizeof(union_type[0]));
+ union_type->type.type = TYPE_COMPOUND_UNION;
+ union_type->source_position = token.source_position;
+
+ int top = environment_top();
+ context_t *last_context = context;
+ set_context(&union_type->context);
+
+ if(token.type == T_IDENTIFIER) {
+ union_type->symbol = token.v.symbol;
+ next_token();
+ if(token.type == '{') {
+ parse_compound_type_entries();
+ }
+ } else if(token.type == '{') {
+ parse_compound_type_entries();
+ } else {
+ parse_error_expected("problem while parsing union type specifiers",
+ T_IDENTIFIER, '{');
+ union_type = NULL;
+ }
+
+ assert(context == &union_type->context);
+ set_context(last_context);
+ environment_pop_to(top);
+
+ return (type_t*) union_type;
+}
+
+static void parse_enum_type_entries()
+{
+ eat('{');
+
+ if(token.type == '}') {
+ next_token();
+ parse_error("empty enum not allowed");
+ return;
+ }
+
+ do {
+ if(token.type != T_IDENTIFIER) {
+ parse_error_expected("problem while parsing enum entry",
+ T_IDENTIFIER, 0);
+ eat_until('}');
+ return;
+ }
+ next_token();
+
+ if(token.type == '=') {
+ parse_constant_expression();
+ }
+
+ if(token.type != ',')
+ break;
+ next_token();
+ } while(token.type != '}');
+
+ expect_void('}');
+}
+
+static type_t *parse_enum_specifier(void)
+{
+ eat(T_enum);
+
+ enum_type_t *enum_type = allocate_type_zero(sizeof(enum_type[0]));
+ enum_type->type.type = TYPE_ENUM;
+ enum_type->source_position = token.source_position;
+
+ if(token.type == T_IDENTIFIER) {
+ enum_type->symbol = token.v.symbol;
+ next_token();
+ if(token.type == '{') {
+ parse_enum_type_entries();
+ }
+ } else if(token.type == '{') {
+ parse_enum_type_entries();
+ } else {
+ parse_error_expected("problem while parsing enum type specifiers",
+ T_IDENTIFIER, '{');
+ }
+
+ return (type_t*) enum_type;
+}
+