sksl enum support

Bug: skia:
Change-Id: I4d505b31cf8b59de12bcdbca410aafc085977ba9
Reviewed-on: https://skia-review.googlesource.com/68621
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
diff --git a/src/sksl/SkSLParser.cpp b/src/sksl/SkSLParser.cpp
index 267d802..d140ce2 100644
--- a/src/sksl/SkSLParser.cpp
+++ b/src/sksl/SkSLParser.cpp
@@ -15,6 +15,7 @@
 #include "ast/SkSLASTContinueStatement.h"
 #include "ast/SkSLASTDiscardStatement.h"
 #include "ast/SkSLASTDoStatement.h"
+#include "ast/SkSLASTEnum.h"
 #include "ast/SkSLASTExpression.h"
 #include "ast/SkSLASTExpressionStatement.h"
 #include "ast/SkSLASTExtension.h"
@@ -279,11 +280,75 @@
                                                           text));
 }
 
-/* modifiers (structVarDeclaration | type IDENTIFIER ((LPAREN parameter
+/* ENUM CLASS IDENTIFIER LBRACE (IDENTIFIER (EQ expression)? (COMMA IDENTIFIER (EQ expression))*)?
+   RBRACE */
+std::unique_ptr<ASTDeclaration> Parser::enumDeclaration() {
+    Token start;
+    if (!this->expect(Token::ENUM, "'enum'", &start)) {
+        return nullptr;
+    }
+    if (!this->expect(Token::CLASS, "'class'")) {
+        return nullptr;
+    }
+    Token name;
+    if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) {
+        return nullptr;
+    }
+    if (!this->expect(Token::LBRACE, "'{'")) {
+        return nullptr;
+    }
+    fTypes.add(this->text(name), std::unique_ptr<Symbol>(new Type(this->text(name),
+                                                                  Type::kEnum_Kind)));
+    std::vector<StringFragment> names;
+    std::vector<std::unique_ptr<ASTExpression>> values;
+    if (!this->checkNext(Token::RBRACE)) {
+        Token id;
+        if (!this->expect(Token::IDENTIFIER, "an identifier", &id)) {
+            return nullptr;
+        }
+        names.push_back(this->text(id));
+        if (this->checkNext(Token::EQ)) {
+            std::unique_ptr<ASTExpression> value = this->assignmentExpression();
+            if (!value) {
+                return nullptr;
+            }
+            values.push_back(std::move(value));
+        } else {
+            values.push_back(nullptr);
+        }
+        while (!this->checkNext(Token::RBRACE)) {
+            if (!this->expect(Token::COMMA, "','")) {
+                return nullptr;
+            }
+            if (!this->expect(Token::IDENTIFIER, "an identifier", &id)) {
+                return nullptr;
+            }
+            names.push_back(this->text(id));
+            if (this->checkNext(Token::EQ)) {
+                std::unique_ptr<ASTExpression> value = this->assignmentExpression();
+                if (!value) {
+                    return nullptr;
+                }
+                values.push_back(std::move(value));
+            } else {
+                values.push_back(nullptr);
+            }
+        }
+    }
+    this->expect(Token::SEMICOLON, "';'");
+    return std::unique_ptr<ASTDeclaration>(new ASTEnum(name.fOffset, this->text(name), names,
+                                                       std::move(values)));
+}
+
+/* enumDeclaration | modifiers (structVarDeclaration | type IDENTIFIER ((LPAREN parameter
    (COMMA parameter)* RPAREN (block | SEMICOLON)) | SEMICOLON) | interfaceBlock) */
 std::unique_ptr<ASTDeclaration> Parser::declaration() {
-    Modifiers modifiers = this->modifiers();
     Token lookahead = this->peek();
+    if (lookahead.fKind == Token::ENUM) {
+        return this->enumDeclaration();
+    }
+    Modifiers modifiers = this->modifiers();
+    lookahead = this->peek();
     if (lookahead.fKind == Token::IDENTIFIER && !this->isType(this->text(lookahead))) {
         // we have an identifier that's not a type, could be the start of an interface block
         return this->interfaceBlock(modifiers);
@@ -1286,7 +1351,7 @@
     return nullptr;
 }
 
-/* assignmentExpression */
+/* commaExpression */
 std::unique_ptr<ASTExpression> Parser::expression() {
     AutoDepth depth(this);
     if (!depth.checkValid()) {
@@ -1633,11 +1698,12 @@
     }
     for (;;) {
         switch (this->peek().fKind) {
-            case Token::LBRACKET: // fall through
-            case Token::DOT:      // fall through
-            case Token::LPAREN:   // fall through
-            case Token::PLUSPLUS: // fall through
-            case Token::MINUSMINUS: {
+            case Token::LBRACKET:   // fall through
+            case Token::DOT:        // fall through
+            case Token::LPAREN:     // fall through
+            case Token::PLUSPLUS:   // fall through
+            case Token::MINUSMINUS: // fall through
+            case Token::COLONCOLON: {
                 std::unique_ptr<ASTSuffix> s = this->suffix();
                 if (!s) {
                     return nullptr;
@@ -1652,7 +1718,7 @@
 }
 
 /* LBRACKET expression? RBRACKET | DOT IDENTIFIER | LPAREN parameters RPAREN |
-   PLUSPLUS | MINUSMINUS */
+   PLUSPLUS | MINUSMINUS | COLONCOLON IDENTIFIER */
 std::unique_ptr<ASTSuffix> Parser::suffix() {
     Token next = this->nextToken();
     switch (next.fKind) {
@@ -1667,7 +1733,8 @@
             this->expect(Token::RBRACKET, "']' to complete array access expression");
             return std::unique_ptr<ASTSuffix>(new ASTIndexSuffix(std::move(e)));
         }
-        case Token::DOT: {
+        case Token::DOT: // fall through
+        case Token::COLONCOLON: {
             int offset = this->peek().fOffset;
             StringFragment text;
             if (this->identifier(&text)) {