From b971ddec3a7bb56ac6c9bc094916828283838adf Mon Sep 17 00:00:00 2001
From: Dale Weiler <killfieldengine@gmail.com>
Date: Fri, 8 Mar 2013 08:01:45 +0000
Subject: [PATCH] Implemented concept of enumeration attributes (can be further
 extended, but currently only "flag" is implemented as an attribute).  An
 enumeration with a flag attribute will act as a "flagged enumeration", one
 that automatically handles exponentiation of the constants defined inside it,
 i.e enum : flag { A, B, C }, A,B,C will equal 2, 4, 8.

---
 parser.c        | 26 +++++++++++++++++++++++---
 tests/enum.qc   | 10 ++++++++++
 tests/enum.tmpl |  3 +++
 3 files changed, 36 insertions(+), 3 deletions(-)

diff --git a/parser.c b/parser.c
index 98c0c9f..bd4f1c1 100644
--- a/parser.c
+++ b/parser.c
@@ -3861,6 +3861,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
 
 static bool parse_enum(parser_t *parser)
 {
+    bool        flag = false;
     qcfloat     num = 0;
     ast_value **values = NULL;
     ast_value  *var = NULL;
@@ -3868,11 +3869,28 @@ static bool parse_enum(parser_t *parser)
 
     ast_expression *old;
 
-    if (!parser_next(parser) || parser->tok != '{') {
-        parseerror(parser, "expected `{` after `enum` keyword");
+    if (!parser_next(parser) || (parser->tok != '{' && parser->tok != ':')) {
+        parseerror(parser, "expected `{` or `:` after `enum` keyword");
         return false;
     }
 
+    /* enumeration attributes (can add more later) */
+    if (parser->tok == ':') {
+        if (!parser_next(parser) || parser->tok != TOKEN_IDENT || strcmp(parser_tokval(parser), "flag")) {
+            parseerror(parser, "expected `flag` after enumeration attribute ':'");
+            return false;
+        }
+
+        if (!parser_next(parser) || parser->tok != '{') {
+            parseerror(parser, "expected `{` after enum attribute `flag`");
+            return false;
+        }
+
+        /* flagged enumeration start from 1 */
+        num  = 1;
+        flag = true;
+    }
+
     while (true) {
         if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
             if (parser->tok == '}') {
@@ -3896,7 +3914,9 @@ static bool parse_enum(parser_t *parser)
         vec_push(values, var);
         var->cvq             = CV_CONST;
         var->hasvalue        = true;
-        var->constval.vfloat = num++;
+
+        /* for flagged enumerations increment in POTs of TWO */
+        var->constval.vfloat = (flag) ? (num *= 2) : (num ++);
 
         parser_addglobal(parser, var->name, (ast_expression*)var);
 
diff --git a/tests/enum.qc b/tests/enum.qc
index da08cee..1691661 100644
--- a/tests/enum.qc
+++ b/tests/enum.qc
@@ -27,6 +27,12 @@ enum {
     N
 };
 
+enum : flag {
+    F1, /* = 1 << 1 */
+    F2, /* = 1 << 2 */
+    F3  /* = 1 << 3 */
+};
+
 void main() {
     print(ftos(A), "\n");
     print(ftos(B), "\n");
@@ -42,4 +48,8 @@ void main() {
     print(ftos(L), "\n");
     print(ftos(M), "\n");
     print(ftos(N), "\n");
+
+    print(ftos(F1), "\n");
+    print(ftos(F2), "\n");
+    print(ftos(F3), "\n");
 };
diff --git a/tests/enum.tmpl b/tests/enum.tmpl
index 28cbd57..c39537a 100644
--- a/tests/enum.tmpl
+++ b/tests/enum.tmpl
@@ -16,3 +16,6 @@ M: 10
 M: 11
 M: 12
 M: 13
+M: 2
+M: 4
+M: 8
-- 
2.39.5