+/**
+ * check for the allowed MS alignment values.
+ */
+static bool check_elignment_value(long long intvalue) {
+ if(intvalue < 1 || intvalue > 8192) {
+ errorf(HERE, "illegal alignment value");
+ return false;
+ }
+ unsigned v = (unsigned)intvalue;
+ for(unsigned i = 1; i <= 8192; i += i) {
+ if (i == v)
+ return true;
+ }
+ errorf(HERE, "alignment must be power of two");
+ return false;
+}
+
+#define DET_MOD(name, tag) do { \
+ if(*modifiers & tag) warningf(HERE, #name " used more than once"); \
+ *modifiers |= tag; \
+} while(0)
+
+static void parse_microsoft_extended_decl_modifier(declaration_specifiers_t *specifiers)
+{
+ decl_modifiers_t *modifiers = &specifiers->decl_modifiers;
+
+ while(true) {
+ if(token.type == T_restrict) {
+ next_token();
+ DET_MOD(restrict, DM_RESTRICT);
+ goto end_loop;
+ } else if(token.type != T_IDENTIFIER)
+ break;
+ symbol_t *symbol = token.v.symbol;
+ if(symbol == sym_align) {
+ next_token();
+ expect('(');
+ if(token.type != T_INTEGER)
+ goto end_error;
+ if(check_elignment_value(token.v.intvalue)) {
+ if(specifiers->alignment != 0)
+ warningf(HERE, "align used more than once");
+ specifiers->alignment = (unsigned char)token.v.intvalue;
+ }
+ next_token();
+ expect(')');
+ } else if(symbol == sym_allocate) {
+ next_token();
+ expect('(');
+ if(token.type != T_IDENTIFIER)
+ goto end_error;
+ (void)token.v.symbol;
+ expect(')');
+ } else if(symbol == sym_dllimport) {
+ next_token();
+ DET_MOD(dllimport, DM_DLLIMPORT);
+ } else if(symbol == sym_dllexport) {
+ next_token();
+ DET_MOD(dllexport, DM_DLLEXPORT);
+ } else if(symbol == sym_thread) {
+ next_token();
+ DET_MOD(thread, DM_THREAD);
+ } else if(symbol == sym_naked) {
+ next_token();
+ DET_MOD(naked, DM_NAKED);
+ } else if(symbol == sym_noinline) {
+ next_token();
+ DET_MOD(noinline, DM_NOINLINE);
+ } else if(symbol == sym_noreturn) {
+ next_token();
+ DET_MOD(noreturn, DM_NORETURN);
+ } else if(symbol == sym_nothrow) {
+ next_token();
+ DET_MOD(nothrow, DM_NOTHROW);
+ } else if(symbol == sym_novtable) {
+ next_token();
+ DET_MOD(novtable, DM_NOVTABLE);
+ } else if(symbol == sym_property) {
+ next_token();
+ expect('(');
+ for(;;) {
+ bool is_get = false;
+ if(token.type != T_IDENTIFIER)
+ goto end_error;
+ if(token.v.symbol == sym_get) {
+ is_get = true;
+ } else if(token.v.symbol == sym_put) {
+ } else {
+ errorf(HERE, "Bad property name '%Y'", token.v.symbol);
+ goto end_error;
+ }
+ next_token();
+ expect('=');
+ if(token.type != T_IDENTIFIER)
+ goto end_error;
+ if(is_get) {
+ if(specifiers->get_property_sym != NULL) {
+ errorf(HERE, "get property name already specified");
+ } else {
+ specifiers->get_property_sym = token.v.symbol;
+ }
+ } else {
+ if(specifiers->put_property_sym != NULL) {
+ errorf(HERE, "put property name already specified");
+ } else {
+ specifiers->put_property_sym = token.v.symbol;
+ }
+ }
+ next_token();
+ if(token.type == ',') {
+ next_token();
+ continue;
+ }
+ break;
+ }
+ expect(')');
+ } else if(symbol == sym_selectany) {
+ next_token();
+ DET_MOD(selectany, DM_SELECTANY);
+ } else if(symbol == sym_uuid) {
+ next_token();
+ expect('(');
+ if(token.type != T_STRING_LITERAL)
+ goto end_error;
+ next_token();
+ expect(')');
+ } else if(symbol == sym_deprecated) {
+ next_token();
+ if(specifiers->deprecated != 0)
+ warningf(HERE, "deprecated used more than once");
+ specifiers->deprecated = 1;
+ if(token.type == '(') {
+ next_token();
+ if(token.type == T_STRING_LITERAL) {
+ specifiers->deprecated_string = token.v.string.begin;
+ next_token();
+ } else {
+ errorf(HERE, "string literal expected");
+ }
+ expect(')');
+ }
+ } else if(symbol == sym_noalias) {
+ next_token();
+ DET_MOD(noalias, DM_NOALIAS);
+ } else {
+ warningf(HERE, "Unknown modifier %Y ignored", token.v.symbol);
+ next_token();
+ if(token.type == '(')
+ skip_until(')');
+ }
+end_loop:
+ if (token.type == ',')
+ next_token();
+ }
+end_error:
+ return;
+}
+