+typedef enum gnu_attribute_kind_t {
+ GNU_AK_CONST,
+ GNU_AK_VOLATILE,
+ GNU_AK_CDECL,
+ GNU_AK_STDCALL,
+ GNU_AK_FASTCALL,
+ GNU_AK_DEPRECATED,
+ GNU_AK_NOINLINE,
+ GNU_AK_NORETURN,
+ GNU_AK_NAKED,
+ GNU_AK_PURE,
+ GNU_AK_ALWAYS_INLINE,
+ GNU_AK_MALLOC,
+ GNU_AK_WEAK,
+ GNU_AK_CONSTRUCTOR,
+ GNU_AK_DESTRUCTOR,
+ GNU_AK_NOTHROW,
+ GNU_AK_ALIGNED,
+ GNU_AK_ALIAS,
+ GNU_AK_SECTION,
+ GNU_AK_FORMAT,
+ GNU_AK_FORMAT_ARG,
+ GNU_AK_WEAKREF,
+ GNU_AK_NONNULL,
+ GNU_AK_LAST
+} gnu_attribute_kind_t;
+
+static const char *gnu_attribute_names[GNU_AK_LAST] = {
+ [GNU_AK_CONST] = "const",
+ [GNU_AK_VOLATILE] = "volatile",
+ [GNU_AK_CDECL] = "cdecl",
+ [GNU_AK_STDCALL] = "stdcall",
+ [GNU_AK_FASTCALL] = "fastcall",
+ [GNU_AK_DEPRECATED] = "deprecated",
+ [GNU_AK_NOINLINE] = "noinline",
+ [GNU_AK_NORETURN] = "noreturn",
+ [GNU_AK_NAKED] = "naked",
+ [GNU_AK_PURE] = "pure",
+ [GNU_AK_ALWAYS_INLINE] = "always_inline",
+ [GNU_AK_MALLOC] = "malloc",
+ [GNU_AK_WEAK] = "weak",
+ [GNU_AK_CONSTRUCTOR] = "constructor",
+ [GNU_AK_DESTRUCTOR] = "destructor",
+ [GNU_AK_NOTHROW] = "nothrow",
+ [GNU_AK_ALIGNED] = "aligned",
+ [GNU_AK_ALIAS] = "alias",
+ [GNU_AK_SECTION] = "section",
+ [GNU_AK_FORMAT] = "format",
+ [GNU_AK_FORMAT_ARG] = "format_arg",
+ [GNU_AK_WEAKREF] = "weakref",
+ [GNU_AK_NONNULL] = "nonnull"
+};
+
+/**
+ * compare two string, ignoring double underscores on the second.
+ */
+static int strcmp_underscore(const char *s1, const char *s2) {
+ if(s2[0] == '_' && s2[1] == '_') {
+ s2 += 2;
+ size_t l1 = strlen(s1);
+ if(l1 + 2 != strlen(s2)) {
+ /* not equal */
+ return 1;
+ }
+ return strncmp(s1, s2, l1);
+ }
+ return strcmp(s1, s2);
+}
+
+/**
+ * parse one constant expression argument.
+ */
+static expression_t *parse_gnu_attribute_const_arg(void) {
+ expression_t *expression;
+ add_anchor_token(')');
+ expression = parse_constant_expression();
+ rem_anchor_token(')');
+ expect(')');
+ return expression;
+end_error:
+ return create_invalid_expression();
+}
+
+/**
+ * parse a list of constant expressions argumnets.
+ */
+static expression_t *parse_gnu_attribute_const_arg_list(void) {
+ expression_t *expression;
+ add_anchor_token(')');
+ add_anchor_token(',');
+ while(true){
+ expression = parse_constant_expression();
+ if(token.type != ',')
+ break;
+ next_token();
+ }
+ rem_anchor_token(',');
+ rem_anchor_token(')');
+ expect(')');
+ return expression;
+end_error:
+ return create_invalid_expression();
+}
+
+/**
+ * parse one string literal argument.
+ */
+static string_t parse_gnu_attribute_string_arg(void) {
+ string_t string = { NULL, 0 };
+ add_anchor_token('(');
+ if(token.type != T_STRING_LITERAL) {
+ parse_error_expected("while parsing attribute directive", T_STRING_LITERAL);
+ goto end_error;
+ }
+ string = parse_string_literals();
+ rem_anchor_token('(');
+ expect(')');
+end_error:
+ return string;
+}
+
+/**
+ * parse ( identifier, const expression, const expression )
+ */
+static void parse_gnu_attribute_format_args(void) {
+ 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);
+ 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:
+ return;
+}
+
+/**
+ * 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
+ *
+ * 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 )
+ *
+ * The following attributes might have arguments
+ * weak_ref( string literal )
+ * non_null( const expression // ',' )
+ */
+static void parse_gnu_attribute(void)