+ add_anchor_token('(');
+ if(token.type != T_STRING_LITERAL) {
+ parse_error_expected("while parsing attribute directive",
+ T_STRING_LITERAL, NULL);
+ goto end_error;
+ }
+ *string = parse_string_literals();
+ rem_anchor_token('(');
+ expect(')');
+ return;
+end_error:
+ attribute->invalid = true;
+}
+
+/**
+ * parse one tls model.
+ */
+static void parse_gnu_attribute_tls_model_arg(gnu_attribute_t *attribute) {
+ static const char *tls_models[] = {
+ "global-dynamic",
+ "local-dynamic",
+ "initial-exec",
+ "local-exec"
+ };
+ string_t string = { NULL, 0 };
+ parse_gnu_attribute_string_arg(attribute, &string);
+ if(string.begin != NULL) {
+ for(size_t i = 0; i < 4; ++i) {
+ if(strcmp(tls_models[i], string.begin) == 0) {
+ attribute->u.value = i;
+ return;
+ }
+ }
+ }
+ errorf(HERE, "'%s' is an unrecognized tls model", string.begin);
+ attribute->invalid = true;
+}
+
+/**
+ * parse one tls model.
+ */
+static void parse_gnu_attribute_visibility_arg(gnu_attribute_t *attribute) {
+ static const char *visibilities[] = {
+ "default",
+ "protected",
+ "hidden",
+ "internal"
+ };
+ string_t string = { NULL, 0 };
+ parse_gnu_attribute_string_arg(attribute, &string);
+ if(string.begin != NULL) {
+ for(size_t i = 0; i < 4; ++i) {
+ if(strcmp(visibilities[i], string.begin) == 0) {
+ attribute->u.value = i;
+ return;
+ }
+ }
+ }
+ errorf(HERE, "'%s' is an unrecognized visibility", string.begin);
+ attribute->invalid = true;
+}
+
+/**
+ * parse one (code) model.
+ */
+static void parse_gnu_attribute_model_arg(gnu_attribute_t *attribute) {
+ static const char *visibilities[] = {
+ "small",
+ "medium",
+ "large"
+ };
+ string_t string = { NULL, 0 };
+ parse_gnu_attribute_string_arg(attribute, &string);
+ if(string.begin != NULL) {
+ for(int i = 0; i < 3; ++i) {
+ if(strcmp(visibilities[i], string.begin) == 0) {
+ attribute->u.value = i;
+ return;
+ }
+ }
+ }
+ errorf(HERE, "'%s' is an unrecognized model", string.begin);
+ attribute->invalid = true;
+}
+
+static void parse_gnu_attribute_mode_arg(gnu_attribute_t *attribute)
+{
+ /* TODO: find out what is allowed here... */
+
+ /* at least: byte, word, pointer, list of machine modes
+ * __XXX___ is interpreted as XXX */
+ add_anchor_token(')');
+ expect(T_IDENTIFIER);
+ rem_anchor_token(')');
+ expect(')');
+ return;
+end_error:
+ attribute->invalid = true;
+}
+
+/**
+ * parse one interrupt argument.
+ */
+static void parse_gnu_attribute_interrupt_arg(gnu_attribute_t *attribute) {
+ static const char *interrupts[] = {
+ "IRQ",
+ "FIQ",
+ "SWI",
+ "ABORT",
+ "UNDEF"
+ };
+ string_t string = { NULL, 0 };
+ parse_gnu_attribute_string_arg(attribute, &string);
+ if(string.begin != NULL) {
+ for(size_t i = 0; i < 5; ++i) {
+ if(strcmp(interrupts[i], string.begin) == 0) {
+ attribute->u.value = i;
+ return;
+ }
+ }
+ }
+ errorf(HERE, "'%s' is an interrupt", string.begin);
+ attribute->invalid = true;
+}
+
+/**
+ * parse ( identifier, const expression, const expression )
+ */
+static void parse_gnu_attribute_format_args(gnu_attribute_t *attribute) {
+ static const char *format_names[] = {
+ "printf",
+ "scanf",
+ "strftime",
+ "strfmon"
+ };
+ int i;
+
+ if(token.type != T_IDENTIFIER) {
+ parse_error_expected("while parsing format attribute directive", T_IDENTIFIER, NULL);
+ goto end_error;
+ }
+ const char *name = token.v.symbol->string;
+ for(i = 0; i < 4; ++i) {
+ if(strcmp_underscore(format_names[i], name) == 0)
+ break;
+ }
+ if(i >= 4) {
+ if(warning.attribute)
+ warningf(HERE, "'%s' is an unrecognized format function type", name);
+ }
+ next_token();
+
+ expect(',');
+ add_anchor_token(')');
+ add_anchor_token(',');
+ parse_constant_expression();
+ rem_anchor_token(',');
+ rem_anchor_token('(');
+
+ expect(',');
+ add_anchor_token(')');
+ parse_constant_expression();
+ rem_anchor_token('(');
+ expect(')');
+ return;
+end_error:
+ attribute->u.value = true;
+}
+
+static void check_no_argument(gnu_attribute_t *attribute, const char *name)
+{
+ if(!attribute->have_arguments)
+ return;
+
+ /* should have no arguments */
+ errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
+ eat_until_matching_token('(');
+ /* we have already consumed '(', so we stop before ')', eat it */
+ eat(')');
+ attribute->invalid = true;
+}
+
+/**
+ * Parse one GNU attribute.
+ *
+ * Note that attribute names can be specified WITH or WITHOUT
+ * double underscores, ie const or __const__.
+ *
+ * The following attributes are parsed without arguments
+ * const
+ * volatile
+ * cdecl
+ * stdcall
+ * fastcall
+ * deprecated
+ * noinline
+ * noreturn
+ * naked
+ * pure
+ * always_inline
+ * malloc
+ * weak
+ * constructor
+ * destructor
+ * nothrow
+ * transparent_union
+ * common
+ * nocommon
+ * packed
+ * shared
+ * notshared
+ * used
+ * unused
+ * no_instrument_function
+ * warn_unused_result
+ * longcall
+ * shortcall
+ * long_call
+ * short_call
+ * function_vector
+ * interrupt_handler
+ * nmi_handler
+ * nesting
+ * near
+ * far
+ * signal
+ * eightbit_data
+ * tiny_data
+ * saveall
+ * flatten
+ * sseregparm
+ * externally_visible
+ * return_twice
+ * may_alias
+ * ms_struct
+ * gcc_struct
+ * dllimport
+ * dllexport
+ *
+ * The following attributes are parsed with arguments
+ * aligned( const expression )
+ * alias( string literal )
+ * section( string literal )
+ * format( identifier, const expression, const expression )
+ * format_arg( const expression )
+ * tls_model( string literal )
+ * visibility( string literal )
+ * regparm( const expression )
+ * model( string leteral )
+ * trap_exit( const expression )
+ * sp_switch( string literal )
+ *
+ * The following attributes might have arguments
+ * weak_ref( string literal )
+ * non_null( const expression // ',' )
+ * interrupt( string literal )
+ * sentinel( constant expression )
+ */
+static decl_modifiers_t parse_gnu_attribute(gnu_attribute_t **attributes)
+{
+ gnu_attribute_t *head = *attributes;
+ gnu_attribute_t *last = *attributes;
+ decl_modifiers_t modifiers = 0;
+ gnu_attribute_t *attribute;
+
+ eat(T___attribute__);
+ expect('(');
+ expect('(');
+
+ if(token.type != ')') {
+ /* find the end of the list */
+ if(last != NULL) {
+ while(last->next != NULL)
+ last = last->next;
+ }
+
+ /* non-empty attribute list */
+ while(true) {
+ const char *name;
+ if(token.type == T_const) {
+ name = "const";
+ } else if(token.type == T_volatile) {
+ name = "volatile";
+ } else if(token.type == T_cdecl) {
+ /* __attribute__((cdecl)), WITH ms mode */
+ name = "cdecl";
+ } else if(token.type != T_IDENTIFIER) {
+ parse_error_expected("while parsing GNU attribute", T_IDENTIFIER, NULL);
+ break;
+ }
+ const symbol_t *sym = token.v.symbol;
+ name = sym->string;