#include #include #include #include "util.h" #include "cc.h" enum attrprefix { PREFIXNONE = 1, /* standard attribute */ PREFIXGNU, }; static char * strip(char *name) { size_t len; len = strlen(name); if (len >= 4 && name[0] == '_' && name[1] == '_' && name[len - 2] == '_' && name[len - 1] == '_') { name[len - 2] = '\0'; name += 2; } return name; } static bool parseattr(struct attr *a, enum attrkind allowed, enum attrprefix prefix) { char *name, *prefixname = ""; enum attrkind kind; int paren; if (tok.kind != TIDENT) return false; name = strip(tok.lit); next(); if (!prefix) { if (consume(TCOLONCOLON)) { if (strcmp(name, "gnu") == 0) prefix = PREFIXGNU; else prefix = 0; name = strip(expect(TIDENT, "after attribute prefix")); } else { prefix = PREFIXNONE; } } kind = 0; switch (prefix) { case PREFIXGNU: prefixname = "GNU "; break; } if (kind) { if (!(kind & allowed)) error(&tok.loc, "%sattribute '%s' is not supported here", prefixname, name); if (a) a->kind |= kind; } else if (consume(TLPAREN)) { /* skip arguments */ for (paren = 1; paren > 0; next()) { switch (tok.kind) { case TLPAREN: ++paren; break; case TRPAREN: --paren; break; } } } return true; } static bool attrspec(struct attr *a, enum attrkind allowed) { if (tok.kind != TLBRACK || !peek(TLBRACK)) return false; while (parseattr(a, allowed, 0) || consume(TCOMMA)) ; expect(TRBRACK, "to end attribute specifier"); expect(TRBRACK, "to end attribute specifier"); return true; } bool attr(struct attr *a, enum attrkind allowed) { if (!attrspec(a, allowed)) return false; while (attrspec(a, allowed)) ; return true; }