Re-land of skslc switch support
authorEthan Nicholas <ethannicholas@google.com>
Mon, 27 Feb 2017 18:26:45 +0000 (13:26 -0500)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Mon, 27 Feb 2017 19:01:55 +0000 (19:01 +0000)
This reverts commit 7d975fc200bbbea991ec4c04c08f3a5ea7b847af.

BUG=skia:

Change-Id: I57521f7a291a35cfed58d623ea4f8da29582d2c5
Reviewed-on: https://skia-review.googlesource.com/8993
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>

19 files changed:
src/sksl/SkSLCFGGenerator.cpp
src/sksl/SkSLCompiler.cpp
src/sksl/SkSLGLSLCodeGenerator.cpp
src/sksl/SkSLGLSLCodeGenerator.h
src/sksl/SkSLIRGenerator.cpp
src/sksl/SkSLIRGenerator.h
src/sksl/SkSLParser.cpp
src/sksl/SkSLParser.h
src/sksl/SkSLToken.h
src/sksl/ast/SkSLASTStatement.h
src/sksl/ast/SkSLASTSwitchCase.h [new file with mode: 0644]
src/sksl/ast/SkSLASTSwitchStatement.h [new file with mode: 0644]
src/sksl/ir/SkSLStatement.h
src/sksl/ir/SkSLSwitchCase.h [new file with mode: 0644]
src/sksl/ir/SkSLSwitchStatement.h [new file with mode: 0644]
src/sksl/lex.sksl.c
src/sksl/sksl.flex
tests/SkSLErrorTest.cpp
tests/SkSLGLSLTest.cpp

index 31bace9..28533df 100644 (file)
@@ -20,6 +20,7 @@
 #include "ir/SkSLPrefixExpression.h"
 #include "ir/SkSLReturnStatement.h"
 #include "ir/SkSLSwizzle.h"
+#include "ir/SkSLSwitchStatement.h"
 #include "ir/SkSLTernaryExpression.h"
 #include "ir/SkSLVarDeclarationsStatement.h"
 #include "ir/SkSLWhileStatement.h"
@@ -152,13 +153,17 @@ void CFGGenerator::addExpression(CFG& cfg, std::unique_ptr<Expression>* e, bool
             cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind,
                                                          constantPropagate, e, nullptr });
             break;
-        case Expression::kPrefix_Kind:
-            this->addExpression(cfg, &((PrefixExpression*) e->get())->fOperand, constantPropagate);
+        case Expression::kPrefix_Kind: {
+            PrefixExpression* p = (PrefixExpression*) e->get();
+            this->addExpression(cfg, &p->fOperand, constantPropagate &&
+                                                   p->fOperator != Token::PLUSPLUS &&
+                                                   p->fOperator != Token::MINUSMINUS);
             cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind,
                                                          constantPropagate, e, nullptr });
             break;
+        }
         case Expression::kPostfix_Kind:
-            this->addExpression(cfg, &((PostfixExpression*) e->get())->fOperand, constantPropagate);
+            this->addExpression(cfg, &((PostfixExpression*) e->get())->fOperand, false);
             cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind,
                                                          constantPropagate, e, nullptr });
             break;
@@ -345,6 +350,34 @@ void CFGGenerator::addStatement(CFG& cfg, const Statement* s) {
             cfg.fCurrent = loopExit;
             break;
         }
+        case Statement::kSwitch_Kind: {
+            SwitchStatement* ss = (SwitchStatement*) s;
+            this->addExpression(cfg, &ss->fValue, true);
+            BlockId start = cfg.fCurrent;
+            BlockId switchExit = cfg.newIsolatedBlock();
+            fLoopExits.push(switchExit);
+            for (const auto& c : ss->fCases) {
+                cfg.newBlock();
+                cfg.addExit(start, cfg.fCurrent);
+                if (c->fValue) {
+                    // technically this should go in the start block, but it doesn't actually matter
+                    // because it must be constant. Not worth running two loops for.
+                    this->addExpression(cfg, &c->fValue, true);
+                }
+                for (const auto& caseStatement : c->fStatements) {
+                    this->addStatement(cfg, caseStatement.get());
+                }
+            }
+            cfg.addExit(cfg.fCurrent, switchExit);
+            // note that unlike GLSL, our grammar requires the default case to be last
+            if (0 == ss->fCases.size() || ss->fCases[ss->fCases.size() - 1]->fValue) {
+                // switch does not have a default clause, mark that it can skip straight to the end
+                cfg.addExit(start, switchExit);
+            }
+            fLoopExits.pop();
+            cfg.fCurrent = switchExit;
+            break;
+        }
         default:
             printf("statement: %s\n", s->description().c_str());
             ABORT("unsupported statement kind");
index 004eab7..d574239 100644 (file)
@@ -286,8 +286,12 @@ void Compiler::scanCFG(CFG* cfg, BlockId blockId, std::set<BlockId>* workList) {
                 if (e1 != e2) {
                     // definition has changed, merge and add exit block to worklist
                     workList->insert(exitId);
-                    exit.fBefore[pair.first] =
+                    if (e1 && e2) {
+                        exit.fBefore[pair.first] =
                                        (std::unique_ptr<Expression>*) &fContext.fDefined_Expression;
+                    } else {
+                        exit.fBefore[pair.first] = nullptr;
+                    }
                 }
             }
         }
index 3f5f9d1..35a2f62 100644 (file)
@@ -681,6 +681,9 @@ void GLSLCodeGenerator::writeStatement(const Statement& s) {
         case Statement::kDo_Kind:
             this->writeDoStatement((DoStatement&) s);
             break;
+        case Statement::kSwitch_Kind:
+            this->writeSwitchStatement((SwitchStatement&) s);
+            break;
         case Statement::kBreak_Kind:
             this->write("break;");
             break;
@@ -750,6 +753,30 @@ void GLSLCodeGenerator::writeDoStatement(const DoStatement& d) {
     this->write(");");
 }
 
+void GLSLCodeGenerator::writeSwitchStatement(const SwitchStatement& s) {
+    this->write("switch (");
+    this->writeExpression(*s.fValue, kTopLevel_Precedence);
+    this->writeLine(") {");
+    fIndentation++;
+    for (const auto& c : s.fCases) {
+        if (c->fValue) {
+            this->write("case ");
+            this->writeExpression(*c->fValue, kTopLevel_Precedence);
+            this->writeLine(":");
+        } else {
+            this->writeLine("default:");
+        }
+        fIndentation++;
+        for (const auto& stmt : c->fStatements) {
+            this->writeStatement(*stmt);
+            this->writeLine();
+        }
+        fIndentation--;
+    }
+    fIndentation--;
+    this->write("}");
+}
+
 void GLSLCodeGenerator::writeReturnStatement(const ReturnStatement& r) {
     this->write("return");
     if (r.fExpression) {
index 0ae2c5c..907c305 100644 (file)
@@ -34,6 +34,7 @@
 #include "ir/SkSLProgramElement.h"
 #include "ir/SkSLReturnStatement.h"
 #include "ir/SkSLStatement.h"
+#include "ir/SkSLSwitchStatement.h"
 #include "ir/SkSLSwizzle.h"
 #include "ir/SkSLTernaryExpression.h"
 #include "ir/SkSLVarDeclarations.h"
@@ -155,6 +156,8 @@ private:
 
     void writeDoStatement(const DoStatement& d);
 
+    void writeSwitchStatement(const SwitchStatement& s);
+
     void writeReturnStatement(const ReturnStatement& r);
 
     const Context& fContext;
index 247766f..ae2a90f 100644 (file)
@@ -8,6 +8,7 @@
 #include "SkSLIRGenerator.h"
 
 #include "limits.h"
+#include <unordered_set>
 
 #include "SkSLCompiler.h"
 #include "ast/SkSLASTBoolLiteral.h"
@@ -39,6 +40,8 @@
 #include "ir/SkSLPostfixExpression.h"
 #include "ir/SkSLPrefixExpression.h"
 #include "ir/SkSLReturnStatement.h"
+#include "ir/SkSLSwitchCase.h"
+#include "ir/SkSLSwitchStatement.h"
 #include "ir/SkSLSwizzle.h"
 #include "ir/SkSLTernaryExpression.h"
 #include "ir/SkSLUnresolvedFunction.h"
@@ -81,12 +84,27 @@ public:
     IRGenerator* fIR;
 };
 
+class AutoSwitchLevel {
+public:
+    AutoSwitchLevel(IRGenerator* ir)
+    : fIR(ir) {
+        fIR->fSwitchLevel++;
+    }
+
+    ~AutoSwitchLevel() {
+        fIR->fSwitchLevel--;
+    }
+
+    IRGenerator* fIR;
+};
+
 IRGenerator::IRGenerator(const Context* context, std::shared_ptr<SymbolTable> symbolTable,
                          ErrorReporter& errorReporter)
 : fContext(*context)
 , fCurrentFunction(nullptr)
 , fSymbolTable(std::move(symbolTable))
 , fLoopLevel(0)
+, fSwitchLevel(0)
 , fErrors(errorReporter) {}
 
 void IRGenerator::pushSymbolTable() {
@@ -153,6 +171,8 @@ std::unique_ptr<Statement> IRGenerator::convertStatement(const ASTStatement& sta
             return this->convertWhile((ASTWhileStatement&) statement);
         case ASTStatement::kDo_Kind:
             return this->convertDo((ASTDoStatement&) statement);
+        case ASTStatement::kSwitch_Kind:
+            return this->convertSwitch((ASTSwitchStatement&) statement);
         case ASTStatement::kReturn_Kind:
             return this->convertReturn((ASTReturnStatement&) statement);
         case ASTStatement::kBreak_Kind:
@@ -357,6 +377,60 @@ std::unique_ptr<Statement> IRGenerator::convertDo(const ASTDoStatement& d) {
                                                       std::move(test)));
 }
 
+std::unique_ptr<Statement> IRGenerator::convertSwitch(const ASTSwitchStatement& s) {
+    AutoSwitchLevel level(this);
+    std::unique_ptr<Expression> value = this->convertExpression(*s.fValue);
+    if (!value) {
+        return nullptr;
+    }
+    if (value->fType != *fContext.fUInt_Type) {
+        value = this->coerce(std::move(value), *fContext.fInt_Type);
+        if (!value) {
+            return nullptr;
+        }
+    }
+    AutoSymbolTable table(this);
+    std::unordered_set<int> caseValues;
+    std::vector<std::unique_ptr<SwitchCase>> cases;
+    for (const auto& c : s.fCases) {
+        std::unique_ptr<Expression> caseValue;
+        if (c->fValue) {
+            caseValue = this->convertExpression(*c->fValue);
+            if (!caseValue) {
+                return nullptr;
+            }
+            if (caseValue->fType != *fContext.fUInt_Type) {
+                caseValue = this->coerce(std::move(caseValue), *fContext.fInt_Type);
+                if (!caseValue) {
+                    return nullptr;
+                }
+            }
+            if (!caseValue->isConstant()) {
+                fErrors.error(caseValue->fPosition, "case value must be a constant");
+                return nullptr;
+            }
+            ASSERT(caseValue->fKind == Expression::kIntLiteral_Kind);
+            int64_t v = ((IntLiteral&) *caseValue).fValue;
+            if (caseValues.find(v) != caseValues.end()) {
+                fErrors.error(caseValue->fPosition, "duplicate case value");
+            }
+            caseValues.insert(v);
+        }
+        std::vector<std::unique_ptr<Statement>> statements;
+        for (const auto& s : c->fStatements) {
+            std::unique_ptr<Statement> converted = this->convertStatement(*s);
+            if (!converted) {
+                return nullptr;
+            }
+            statements.push_back(std::move(converted));
+        }
+        cases.emplace_back(new SwitchCase(c->fPosition, std::move(caseValue),
+                                          std::move(statements)));
+    }
+    return std::unique_ptr<Statement>(new SwitchStatement(s.fPosition, std::move(value),
+                                                          std::move(cases)));
+}
+
 std::unique_ptr<Statement> IRGenerator::convertExpressionStatement(
                                                                   const ASTExpressionStatement& s) {
     std::unique_ptr<Expression> e = this->convertExpression(*s.fExpression);
@@ -393,10 +467,10 @@ std::unique_ptr<Statement> IRGenerator::convertReturn(const ASTReturnStatement&
 }
 
 std::unique_ptr<Statement> IRGenerator::convertBreak(const ASTBreakStatement& b) {
-    if (fLoopLevel > 0) {
+    if (fLoopLevel > 0 || fSwitchLevel > 0) {
         return std::unique_ptr<Statement>(new BreakStatement(b.fPosition));
     } else {
-        fErrors.error(b.fPosition, "break statement must be inside a loop");
+        fErrors.error(b.fPosition, "break statement must be inside a loop or switch");
         return nullptr;
     }
 }
index 1336b68..fb79cda 100644 (file)
@@ -29,6 +29,7 @@
 #include "ast/SkSLASTReturnStatement.h"
 #include "ast/SkSLASTStatement.h"
 #include "ast/SkSLASTSuffixExpression.h"
+#include "ast/SkSLASTSwitchStatement.h"
 #include "ast/SkSLASTTernaryExpression.h"
 #include "ast/SkSLASTVarDeclaration.h"
 #include "ast/SkSLASTVarDeclarationStatement.h"
@@ -140,6 +141,7 @@ private:
     std::unique_ptr<Statement> convertContinue(const ASTContinueStatement& c);
     std::unique_ptr<Statement> convertDiscard(const ASTDiscardStatement& d);
     std::unique_ptr<Statement> convertDo(const ASTDoStatement& d);
+    std::unique_ptr<Statement> convertSwitch(const ASTSwitchStatement& s);
     std::unique_ptr<Expression> convertBinaryExpression(const ASTBinaryExpression& expression);
     std::unique_ptr<Extension> convertExtension(const ASTExtension& e);
     std::unique_ptr<Statement> convertExpressionStatement(const ASTExpressionStatement& s);
@@ -170,10 +172,12 @@ private:
     std::unordered_map<SkString, CapValue> fCapsMap;
     std::shared_ptr<SymbolTable> fSymbolTable;
     int fLoopLevel;
+    int fSwitchLevel;
     ErrorReporter& fErrors;
 
     friend class AutoSymbolTable;
     friend class AutoLoopLevel;
+    friend class AutoSwitchLevel;
     friend class Compiler;
 };
 
index cc47577..478d6fd 100644 (file)
@@ -63,6 +63,8 @@
 #include "ast/SkSLASTReturnStatement.h"
 #include "ast/SkSLASTStatement.h"
 #include "ast/SkSLASTSuffixExpression.h"
+#include "ast/SkSLASTSwitchCase.h"
+#include "ast/SkSLASTSwitchStatement.h"
 #include "ast/SkSLASTTernaryExpression.h"
 #include "ast/SkSLASTType.h"
 #include "ast/SkSLASTVarDeclaration.h"
@@ -770,6 +772,8 @@ std::unique_ptr<ASTStatement> Parser::statement() {
             return this->doStatement();
         case Token::WHILE:
             return this->whileStatement();
+        case Token::SWITCH:
+            return this->switchStatement();
         case Token::RETURN:
             return this->returnStatement();
         case Token::BREAK:
@@ -975,6 +979,86 @@ std::unique_ptr<ASTWhileStatement> Parser::whileStatement() {
                                                                     std::move(statement)));
 }
 
+/* CASE expression COLON statement* */
+std::unique_ptr<ASTSwitchCase> Parser::switchCase() {
+    Token start;
+    if (!this->expect(Token::CASE, "'case'", &start)) {
+        return nullptr;
+    }
+    std::unique_ptr<ASTExpression> value = this->expression();
+    if (!value) {
+        return nullptr;
+    }
+    if (!this->expect(Token::COLON, "':'")) {
+        return nullptr;
+    }
+    std::vector<std::unique_ptr<ASTStatement>> statements;
+    while (this->peek().fKind != Token::RBRACE && this->peek().fKind != Token::CASE &&
+           this->peek().fKind != Token::DEFAULT) {
+        std::unique_ptr<ASTStatement> s = this->statement();
+        if (!s) {
+            return nullptr;
+        }
+        statements.push_back(std::move(s));
+    }
+    return std::unique_ptr<ASTSwitchCase>(new ASTSwitchCase(start.fPosition, std::move(value),
+                                                            std::move(statements)));
+}
+
+/* SWITCH LPAREN expression RPAREN LBRACE switchCase* (DEFAULT COLON statement*)? RBRACE */
+std::unique_ptr<ASTStatement> Parser::switchStatement() {
+    Token start;
+    if (!this->expect(Token::SWITCH, "'switch'", &start)) {
+        return nullptr;
+    }
+    if (!this->expect(Token::LPAREN, "'('")) {
+        return nullptr;
+    }
+    std::unique_ptr<ASTExpression> value(this->expression());
+    if (!value) {
+        return nullptr;
+    }
+    if (!this->expect(Token::RPAREN, "')'")) {
+        return nullptr;
+    }
+    if (!this->expect(Token::LBRACE, "'{'")) {
+        return nullptr;
+    }
+    std::vector<std::unique_ptr<ASTSwitchCase>> cases;
+    while (this->peek().fKind == Token::CASE) {
+        std::unique_ptr<ASTSwitchCase> c = this->switchCase();
+        if (!c) {
+            return nullptr;
+        }
+        cases.push_back(std::move(c));
+    }
+    // Requiring default: to be last (in defiance of C and GLSL) was a deliberate decision. Other
+    // parts of the compiler may rely upon this assumption.
+    if (this->peek().fKind == Token::DEFAULT) {
+        Token defaultStart;
+        SkAssertResult(this->expect(Token::DEFAULT, "'default'", &defaultStart));
+        if (!this->expect(Token::COLON, "':'")) {
+            return nullptr;
+        }
+        std::vector<std::unique_ptr<ASTStatement>> statements;
+        while (this->peek().fKind != Token::RBRACE) {
+            std::unique_ptr<ASTStatement> s = this->statement();
+            if (!s) {
+                return nullptr;
+            }
+            statements.push_back(std::move(s));
+        }
+        cases.emplace_back(new ASTSwitchCase(defaultStart.fPosition, nullptr,
+                                             std::move(statements)));
+    }
+    if (!this->expect(Token::RBRACE, "'}'")) {
+        return nullptr;
+    }
+    return std::unique_ptr<ASTStatement>(new ASTSwitchStatement(start.fPosition,
+                                                                std::move(value),
+                                                                std::move(cases)));
+}
+
 /* FOR LPAREN (declaration | expression)? SEMICOLON expression? SEMICOLON expression? RPAREN
    STATEMENT */
 std::unique_ptr<ASTForStatement> Parser::forStatement() {
index 78d9933..63894e0 100644 (file)
@@ -37,6 +37,8 @@ struct ASTPrecision;
 struct ASTReturnStatement;
 struct ASTStatement;
 struct ASTSuffix;
+struct ASTSwitchCase;
+struct ASTSwitchStatement;
 struct ASTType;
 struct ASTWhileStatement;
 struct ASTVarDeclarations;
@@ -143,6 +145,10 @@ private:
 
     std::unique_ptr<ASTForStatement> forStatement();
 
+    std::unique_ptr<ASTSwitchCase> switchCase();
+
+    std::unique_ptr<ASTStatement> switchStatement();
+
     std::unique_ptr<ASTReturnStatement> returnStatement();
 
     std::unique_ptr<ASTBreakStatement> breakStatement();
index 197781f..70b4ed2 100644 (file)
@@ -82,6 +82,9 @@ struct Token {
         FOR,
         WHILE,
         DO,
+        SWITCH,
+        CASE,
+        DEFAULT,
         RETURN,
         BREAK,
         CONTINUE,
index 9ddde06..6ce320e 100644 (file)
@@ -26,6 +26,7 @@ struct ASTStatement : public ASTPositionNode {
         kFor_Kind,
         kWhile_Kind,
         kDo_Kind,
+        kSwitch_Kind,
         kReturn_Kind,
         kBreak_Kind,
         kContinue_Kind,
diff --git a/src/sksl/ast/SkSLASTSwitchCase.h b/src/sksl/ast/SkSLASTSwitchCase.h
new file mode 100644 (file)
index 0000000..2c0a01c
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTSWITCHCASE
+#define SKSL_ASTSWITCHCASE
+
+#include "SkSLASTStatement.h"
+
+namespace SkSL {
+
+/**
+ * A single case of a 'switch' statement.
+ */
+struct ASTSwitchCase : public ASTStatement {
+    // a null value means "default:"
+    ASTSwitchCase(Position position, std::unique_ptr<ASTExpression> value,
+                  std::vector<std::unique_ptr<ASTStatement>> statements)
+    : INHERITED(position, kSwitch_Kind)
+    , fValue(std::move(value))
+    , fStatements(std::move(statements)) {}
+
+    SkString description() const override {
+        SkString result;
+        if (fValue) {
+            result.appendf("case %s:\n", fValue->description().c_str());
+        } else {
+            result += "default:\n";
+        }
+        for (const auto& s : fStatements) {
+            result += s->description() + "\n";
+        }
+        return result;
+    }
+
+    // null value implies "default" case
+    const std::unique_ptr<ASTExpression> fValue;
+    const std::vector<std::unique_ptr<ASTStatement>> fStatements;
+
+    typedef ASTStatement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTSwitchStatement.h b/src/sksl/ast/SkSLASTSwitchStatement.h
new file mode 100644 (file)
index 0000000..3031a7d
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTSWITCHSTATEMENT
+#define SKSL_ASTSWITCHSTATEMENT
+
+#include "SkSLASTStatement.h"
+#include "SkSLASTSwitchCase.h"
+
+namespace SkSL {
+
+/**
+ * A 'switch' statement.
+ */
+struct ASTSwitchStatement : public ASTStatement {
+    ASTSwitchStatement(Position position, std::unique_ptr<ASTExpression> value,
+                       std::vector<std::unique_ptr<ASTSwitchCase>> cases)
+    : INHERITED(position, kSwitch_Kind)
+    , fValue(std::move(value))
+    , fCases(std::move(cases)) {}
+
+    SkString description() const override {
+        SkString result = SkStringPrintf("switch (%s) {\n", + fValue->description().c_str());
+        for (const auto& c : fCases) {
+            result += c->description();
+        }
+        result += "}";
+        return result;
+    }
+
+    const std::unique_ptr<ASTExpression> fValue;
+    const std::vector<std::unique_ptr<ASTSwitchCase>> fCases;
+
+    typedef ASTStatement INHERITED;
+};
+
+} // namespace
+
+#endif
index 012311f..c3a6f95 100644 (file)
@@ -27,6 +27,7 @@ struct Statement : public IRNode {
         kFor_Kind,
         kIf_Kind,
         kReturn_Kind,
+        kSwitch_Kind,
         kVarDeclarations_Kind,
         kWhile_Kind
     };
diff --git a/src/sksl/ir/SkSLSwitchCase.h b/src/sksl/ir/SkSLSwitchCase.h
new file mode 100644 (file)
index 0000000..3f1c3ac
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_SWITCHCASE
+#define SKSL_SWITCHCASE
+
+#include "SkSLStatement.h"
+
+namespace SkSL {
+
+/**
+ * A single case of a 'switch' statement.
+ */
+struct SwitchCase : public Statement {
+    SwitchCase(Position position, std::unique_ptr<Expression> value,
+               std::vector<std::unique_ptr<Statement>> statements)
+    : INHERITED(position, kSwitch_Kind)
+    , fValue(std::move(value))
+    , fStatements(std::move(statements)) {}
+
+    SkString description() const override {
+        SkString result;
+        if (fValue) {
+            result.appendf("case %s:\n", fValue->description().c_str());
+        } else {
+            result += "default:\n";
+        }
+        for (const auto& s : fStatements) {
+            result += s->description() + "\n";
+        }
+        return result;
+    }
+
+    // null value implies "default" case
+    std::unique_ptr<Expression> fValue;
+    std::vector<std::unique_ptr<Statement>> fStatements;
+
+    typedef Statement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLSwitchStatement.h b/src/sksl/ir/SkSLSwitchStatement.h
new file mode 100644 (file)
index 0000000..31765c4
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_SWITCHSTATEMENT
+#define SKSL_SWITCHSTATEMENT
+
+#include "SkSLStatement.h"
+#include "SkSLSwitchCase.h"
+
+namespace SkSL {
+
+/**
+ * A 'switch' statement.
+ */
+struct SwitchStatement : public Statement {
+    SwitchStatement(Position position, std::unique_ptr<Expression> value,
+                    std::vector<std::unique_ptr<SwitchCase>> cases)
+    : INHERITED(position, kSwitch_Kind)
+    , fValue(std::move(value))
+    , fCases(std::move(cases)) {}
+
+    SkString description() const override {
+        SkString result = SkStringPrintf("switch (%s) {\n", + fValue->description().c_str());
+        for (const auto& c : fCases) {
+            result += c->description();
+        }
+        result += "}";
+        return result;
+    }
+
+    std::unique_ptr<Expression> fValue;
+    std::vector<std::unique_ptr<SwitchCase>> fCases;
+
+    typedef Statement INHERITED;
+};
+
+} // namespace
+
+#endif
index 4cff376..2c5eb73 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Google Inc.
+ * Copyright 2017 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
@@ -13,8 +13,8 @@
 
 #define FLEX_SCANNER
 #define YY_FLEX_MAJOR_VERSION 2
-#define YY_FLEX_MINOR_VERSION 6
-#define YY_FLEX_SUBMINOR_VERSION 0
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 39
 #if YY_FLEX_SUBMINOR_VERSION > 0
 #define FLEX_BETA
 #endif
@@ -248,7 +248,7 @@ struct yy_buffer_state
        /* Number of characters read into yy_ch_buf, not including EOB
         * characters.
         */
-       int yy_n_chars;
+       yy_size_t yy_n_chars;
 
        /* Whether we "own" the buffer - i.e., we know we created it,
         * and can realloc() it to grow it, and should free() it to
@@ -368,9 +368,6 @@ typedef int yy_state_type;
 static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
 static yy_state_type yy_try_NUL_trans (yy_state_type current_state  ,yyscan_t yyscanner);
 static int yy_get_next_buffer (yyscan_t yyscanner );
-#if defined(__GNUC__) && __GNUC__ >= 3
-__attribute__((__noreturn__))
-#endif
 static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
 
 /* Done after the current pattern has been matched and before the
@@ -383,8 +380,8 @@ static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
        *yy_cp = '\0'; \
        yyg->yy_c_buf_p = yy_cp;
 
-#define YY_NUM_RULES 89
-#define YY_END_OF_BUFFER 90
+#define YY_NUM_RULES 92
+#define YY_END_OF_BUFFER 93
 /* This struct is not used in this scanner,
    but its presence is necessary. */
 struct yy_trans_info
@@ -392,37 +389,39 @@ struct yy_trans_info
        flex_int32_t yy_verify;
        flex_int32_t yy_nxt;
        };
-static yyconst flex_int16_t yy_accept[239] =
+static yyconst flex_int16_t yy_accept[253] =
     {   0,
-        0,    0,   90,   88,   87,   87,   61,   88,   35,   51,
-       56,   37,   38,   49,   47,   44,   48,   43,   50,    4,
-        4,   63,   84,   68,   64,   67,   62,   41,   42,   55,
-       35,   35,   35,   35,   35,   35,   35,   35,   35,   35,
-       35,   35,   35,   35,   35,   35,   35,   35,   39,   54,
-       40,   57,   87,   66,   36,   35,   75,   60,   80,   73,
-       45,   71,   46,   72,    1,    0,   85,   74,    2,    4,
-        0,    0,   52,   70,   65,   69,   53,   79,   59,   35,
-       35,   35,   12,   35,   35,   35,   35,   35,    8,   17,
-       35,   35,   35,   35,   35,   35,   35,   35,   35,   35,
-
-       35,   35,   35,   78,   58,   36,   83,    0,    0,    0,
-       85,    1,    0,    0,    3,    5,   76,   77,   82,   35,
-       35,   35,   35,   35,   35,   35,   10,   35,   35,   35,
-       35,   35,   35,   18,   35,   35,   35,   35,   35,   35,
-       35,   35,   35,   35,   81,    0,    1,   86,    0,    0,
-        2,   35,   35,   35,   35,   35,    9,   35,   25,   35,
-       35,   35,   22,   35,   35,   35,   35,   35,   35,   35,
-        6,   35,   35,   35,   35,    0,    1,   13,   35,   21,
-       35,   35,    7,   24,   19,   35,   35,   35,   35,   35,
-       35,   35,   35,   35,   35,   11,   35,   35,   35,   35,
-
-       33,   35,   35,   35,   35,   35,   16,   32,   35,   35,
-       35,   35,   35,   15,   23,   35,   35,   35,   35,   20,
-       35,   35,   29,   14,   35,   35,   27,   31,   30,   35,
-       35,   34,   28,   35,   35,   35,   26,    0
+        0,    0,   93,   91,   90,   90,   64,   91,   38,   54,
+       59,   40,   41,   52,   50,   47,   51,   46,   53,    4,
+        4,   66,   87,   71,   67,   70,   65,   44,   45,   58,
+       38,   38,   38,   38,   38,   38,   38,   38,   38,   38,
+       38,   38,   38,   38,   38,   38,   38,   38,   42,   57,
+       43,   60,   90,   69,   39,   38,   78,   63,   83,   76,
+       48,   74,   49,   75,    1,    0,   88,   77,    2,    4,
+        0,    0,   55,   73,   68,   72,   56,   82,   62,   38,
+       38,   38,   38,   38,   12,   38,   38,   38,   38,   38,
+        8,   20,   38,   38,   38,   38,   38,   38,   38,   38,
+
+       38,   38,   38,   38,   38,   38,   81,   61,   39,   86,
+        0,    0,    0,   88,    1,    0,    0,    3,    5,   79,
+       80,   85,   38,   38,   38,   38,   38,   38,   38,   38,
+       38,   10,   38,   38,   38,   38,   38,   38,   21,   38,
+       38,   38,   38,   38,   38,   38,   38,   38,   38,   38,
+       84,    0,    1,   89,    0,    0,    2,   38,   14,   38,
+       38,   38,   38,   38,    9,   38,   28,   38,   38,   38,
+       25,   38,   38,   38,   38,   38,   38,   38,   38,    6,
+       38,   38,   38,   38,    0,    1,   16,   38,   24,   38,
+       38,   38,    7,   27,   22,   38,   38,   38,   38,   38,
+
+       38,   38,   38,   38,   38,   38,   11,   38,   38,   38,
+       38,   38,   36,   38,   38,   38,   38,   38,   19,   35,
+       13,   38,   38,   38,   38,   38,   15,   18,   26,   38,
+       38,   38,   38,   23,   38,   38,   32,   17,   38,   38,
+       30,   34,   33,   38,   38,   37,   31,   38,   38,   38,
+       29,    0
     } ;
 
-static yyconst YY_CHAR yy_ec[256] =
+static yyconst flex_int32_t yy_ec[256] =
     {   0,
         1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
         1,    1,    2,    1,    1,    1,    1,    1,    1,    1,
@@ -454,7 +453,7 @@ static yyconst YY_CHAR yy_ec[256] =
         1,    1,    1,    1,    1
     } ;
 
-static yyconst YY_CHAR yy_meta[57] =
+static yyconst flex_int32_t yy_meta[57] =
     {   0,
         1,    1,    2,    1,    1,    3,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    4,    4,    1,    1,
@@ -464,69 +463,71 @@ static yyconst YY_CHAR yy_meta[57] =
         3,    3,    1,    1,    1,    1
     } ;
 
-static yyconst flex_uint16_t yy_base[245] =
+static yyconst flex_int16_t yy_base[259] =
     {   0,
-        0,    0,  306,  307,   55,   57,  283,    0,    0,  282,
-       53,  307,  307,  281,   50,  307,   49,   47,   57,   52,
-       59,  307,  307,   59,  280,   60,  307,  307,  307,   62,
-      256,  257,   49,  259,   58,  260,   54,   64,  263,  253,
-      247,  249,  259,  245,  246,  248,  246,   60,  307,   68,
-      307,  307,   97,  307,    0,    0,  307,  266,  307,  307,
-      307,  307,  307,  307,   91,  276,    0,  307,   93,   97,
-      112,    0,  264,  307,  307,  307,  263,  307,  262,  249,
-       76,  236,    0,  235,  240,  249,  233,  241,    0,  233,
-      223,  224,  240,  228,  224,  236,   74,  224,  220,  229,
-
-      226,  227,  226,  307,  241,    0,  307,  120,  251,  245,
-        0,  118,  128,  130,  132,    0,  307,  307,  307,  230,
-      225,  107,  226,  223,  210,  208,    0,  217,  205,  209,
-      207,  212,  215,    0,  216,  214,  199,  197,  196,  209,
-      207,  211,  200,  192,  307,  138,  140,  307,  147,  145,
-      149,  199,  192,  189,  197,  204,    0,  199,    0,  188,
-      184,  182,    0,  181,  183,  189,  183,  180,  179,  191,
-        0,  179,  174,  186,  185,  151,  153,    0,  184,    0,
-      175,  171,    0,    0,    0,  168,  173,  167,  166,  169,
-      172,  167,  161,  162,  168,    0,  162,  162,  155,  169,
-
-        0,  157,  156,  161,  158,  165,    0,    0,  155,  155,
-      152,  146,  153,    0,    0,  139,  117,   99,   96,    0,
-      107,   99,    0,    0,  101,   86,    0,    0,    0,   64,
-       59,    0,    0,   64,   46,   32,    0,  307,  169,  172,
-      175,  180,  185,  187
+        0,    0,  320,  321,   55,   57,  297,    0,    0,  296,
+       53,  321,  321,  295,   50,  321,   49,   47,   57,   52,
+       59,  321,  321,   59,  294,   60,  321,  321,  321,   62,
+      270,   57,   54,  274,   59,  275,   59,   65,  278,  268,
+      262,  264,  274,   57,  262,  264,  262,   53,  321,   74,
+      321,  321,  103,  321,    0,    0,  321,  282,  321,  321,
+      321,  321,  321,  321,   92,  292,    0,  321,   95,   99,
+      118,    0,  280,  321,  321,  321,  279,  321,  278,  265,
+      252,   78,  262,  250,    0,  249,  254,  263,  247,  255,
+        0,  247,  237,  238,  254,  242,  238,  250,   92,  238,
+
+      244,  233,  242,  239,  240,  239,  321,  254,    0,  321,
+      128,  264,  258,    0,  126,  136,  106,  138,    0,  321,
+      321,  321,  243,  238,  237,  111,  240,  237,  234,  221,
+      219,    0,  228,  216,  220,  218,  223,  226,    0,  227,
+      225,  210,  208,  207,  207,  219,  217,  221,  210,  202,
+      321,  144,  146,  321,  153,  151,  155,  209,    0,  202,
+      199,  207,  196,  213,    0,  208,    0,  197,  193,  191,
+        0,  190,  192,  198,  192,  189,  188,  200,  199,    0,
+      187,  182,  194,  193,  157,  159,    0,  192,    0,  183,
+      184,  178,    0,    0,    0,  175,  180,  174,  173,  176,
+
+      179,  174,  168,  177,  168,  174,    0,  168,  168,  161,
+      161,  174,    0,  162,  161,  166,  163,  170,    0,    0,
+        0,  160,  160,  157,  146,  145,    0,    0,    0,  132,
+      116,   99,  102,    0,  113,  101,    0,    0,  105,   92,
+        0,    0,    0,   79,   80,    0,    0,   81,   62,   32,
+        0,  321,  175,  178,  181,  186,  191,  193
     } ;
 
-static yyconst flex_int16_t yy_def[245] =
+static yyconst flex_int16_t yy_def[259] =
     {   0,
-      238,    1,  238,  238,  238,  238,  238,  239,  240,  238,
-      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
-      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
-      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
-      240,  240,  240,  240,  240,  240,  240,  240,  238,  238,
-      238,  238,  238,  238,  241,  240,  238,  238,  238,  238,
-      238,  238,  238,  238,  238,  242,  243,  238,  238,  238,
-      238,  244,  238,  238,  238,  238,  238,  238,  238,  240,
-      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
-      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
-
-      240,  240,  240,  238,  238,  241,  238,  238,  242,  242,
-      243,  238,  238,  238,  238,  244,  238,  238,  238,  240,
-      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
-      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
-      240,  240,  240,  240,  238,  238,  238,  238,  238,  238,
-      238,  240,  240,  240,  240,  240,  240,  240,  240,  240,
-      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
-      240,  240,  240,  240,  240,  238,  238,  240,  240,  240,
-      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
-      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
-
-      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
-      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
-      240,  240,  240,  240,  240,  240,  240,  240,  240,  240,
-      240,  240,  240,  240,  240,  240,  240,    0,  238,  238,
-      238,  238,  238,  238
+      252,    1,  252,  252,  252,  252,  252,  253,  254,  252,
+      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
+      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
+      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
+      254,  254,  254,  254,  254,  254,  254,  254,  252,  252,
+      252,  252,  252,  252,  255,  254,  252,  252,  252,  252,
+      252,  252,  252,  252,  252,  256,  257,  252,  252,  252,
+      252,  258,  252,  252,  252,  252,  252,  252,  252,  254,
+      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
+      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
+
+      254,  254,  254,  254,  254,  254,  252,  252,  255,  252,
+      252,  256,  256,  257,  252,  252,  252,  252,  258,  252,
+      252,  252,  254,  254,  254,  254,  254,  254,  254,  254,
+      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
+      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
+      252,  252,  252,  252,  252,  252,  252,  254,  254,  254,
+      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
+      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
+      254,  254,  254,  254,  252,  252,  254,  254,  254,  254,
+      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
+
+      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
+      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
+      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
+      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
+      254,  254,  254,  254,  254,  254,  254,  254,  254,  254,
+      254,    0,  252,  252,  252,  252,  252,  252
     } ;
 
-static yyconst flex_uint16_t yy_nxt[364] =
+static yyconst flex_int16_t yy_nxt[378] =
     {   0,
         4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
        14,   15,   16,   17,   18,   19,   20,   21,   22,   23,
@@ -534,43 +535,44 @@ static yyconst flex_uint16_t yy_nxt[364] =
        31,   32,   33,   34,   35,    9,   36,   37,    9,   38,
        39,   40,   41,   42,   43,   44,   45,   46,   47,   48,
         9,    9,   49,   50,   51,   52,   53,   53,   53,   53,
-       58,   61,   63,   65,   65,  237,   69,   66,   70,   70,
+       58,   61,   63,   65,   65,  251,   69,   66,   70,   70,
        64,   62,   67,   69,   59,   70,   70,   71,   68,   73,
-       74,   76,   77,   78,   71,   71,   82,   85,   89,  104,
-       79,   83,   71,   91,  236,   90,  102,   86,   53,   53,
-
-       87,  235,   72,  136,  103,  234,   92,   65,   65,  112,
-      112,   69,  121,   70,   70,  233,  108,  122,  113,  137,
-      138,  105,   71,  114,  108,  114,  113,  232,  115,  115,
-       71,  146,  231,  146,  112,  112,  147,  147,  230,  150,
-      229,  150,  228,  149,  151,  151,  115,  115,  115,  115,
-      227,  149,  154,  155,  147,  147,  147,  147,  176,  226,
-      176,  151,  151,  177,  177,  151,  151,  177,  177,  177,
-      177,   55,  225,   55,   56,   56,   56,  106,  106,  106,
-      109,  109,  109,  109,  109,  111,  224,  111,  111,  111,
-      116,  116,  223,  222,  221,  220,  219,  218,  217,  216,
-
-      215,  214,  213,  212,  211,  210,  209,  208,  207,  206,
-      205,  204,  203,  202,  201,  200,  199,  198,  197,  196,
-      195,  194,  193,  192,  191,  190,  189,  188,  187,  186,
-      185,  184,  183,  182,  181,  180,  179,  178,  175,  174,
-      173,  172,  171,  170,  169,  168,  167,  166,  165,  164,
-      163,  162,  161,  160,  159,  158,  157,  156,  153,  152,
-      148,  110,  145,  144,  143,  142,  141,  140,  139,  135,
-      134,  133,  132,  131,  130,  129,  128,  127,  126,  125,
-      124,  123,  120,  119,  118,  117,  110,  107,  101,  100,
-       99,   98,   97,   96,   95,   94,   93,   88,   84,   81,
-
-       80,   75,   60,   57,   54,  238,    3,  238,  238,  238,
-      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
-      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
-      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
-      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
-      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
-      238,  238,  238
+       74,   76,   77,   78,   71,   71,   81,   83,   87,  105,
+       79,   84,   71,   91,   93,  107,   85,  106,   88,   82,
+
+       92,   89,   72,  100,   53,   53,  101,   94,   65,   65,
+      250,  115,  115,   69,  125,   70,   70,  111,  249,  126,
+      116,  141,  118,  118,   71,  111,  248,  108,  116,  117,
+      247,  117,   71,  246,  118,  118,  245,  142,  143,  152,
+      244,  152,  115,  115,  153,  153,  243,  156,  242,  156,
+      241,  155,  157,  157,  118,  118,  161,  162,  240,  155,
+      153,  153,  153,  153,  185,  239,  185,  157,  157,  186,
+      186,  157,  157,  186,  186,  186,  186,   55,  238,   55,
+       56,   56,   56,  109,  109,  109,  112,  112,  112,  112,
+      112,  114,  237,  114,  114,  114,  119,  119,  236,  235,
+
+      234,  233,  232,  231,  230,  229,  228,  227,  226,  225,
+      224,  223,  222,  221,  220,  219,  218,  217,  216,  215,
+      214,  213,  212,  211,  210,  209,  208,  207,  206,  205,
+      204,  203,  202,  201,  200,  199,  198,  197,  196,  195,
+      194,  193,  192,  191,  190,  189,  188,  187,  184,  183,
+      182,  181,  180,  179,  178,  177,  176,  175,  174,  173,
+      172,  171,  170,  169,  168,  167,  166,  165,  164,  163,
+      160,  159,  158,  154,  113,  151,  150,  149,  148,  147,
+      146,  145,  144,  140,  139,  138,  137,  136,  135,  134,
+      133,  132,  131,  130,  129,  128,  127,  124,  123,  122,
+
+      121,  120,  113,  110,  104,  103,  102,   99,   98,   97,
+       96,   95,   90,   86,   80,   75,   60,   57,   54,  252,
+        3,  252,  252,  252,  252,  252,  252,  252,  252,  252,
+      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
+      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
+      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
+      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
+      252,  252,  252,  252,  252,  252,  252
     } ;
 
-static yyconst flex_int16_t yy_chk[364] =
+static yyconst flex_int16_t yy_chk[378] =
     {   0,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
@@ -578,50 +580,51 @@ static yyconst flex_int16_t yy_chk[364] =
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    5,    5,    6,    6,
-       11,   15,   17,   18,   18,  236,   20,   19,   20,   20,
+       11,   15,   17,   18,   18,  250,   20,   19,   20,   20,
        17,   15,   19,   21,   11,   21,   21,   20,   19,   24,
-       24,   26,   26,   30,   21,   20,   33,   35,   37,   50,
-       30,   33,   21,   38,  235,   37,   48,   35,   53,   53,
-
-       35,  234,   20,   97,   48,  231,   38,   65,   65,   69,
-       69,   70,   81,   70,   70,  230,   65,   81,   69,   97,
-       97,   50,   70,   71,   65,   71,   69,  226,   71,   71,
-       70,  108,  225,  108,  112,  112,  108,  108,  222,  113,
-      221,  113,  219,  112,  113,  113,  114,  114,  115,  115,
-      218,  112,  122,  122,  146,  146,  147,  147,  149,  217,
-      149,  150,  150,  149,  149,  151,  151,  176,  176,  177,
-      177,  239,  216,  239,  240,  240,  240,  241,  241,  241,
-      242,  242,  242,  242,  242,  243,  213,  243,  243,  243,
-      244,  244,  212,  211,  210,  209,  206,  205,  204,  203,
-
-      202,  200,  199,  198,  197,  195,  194,  193,  192,  191,
-      190,  189,  188,  187,  186,  182,  181,  179,  175,  174,
-      173,  172,  170,  169,  168,  167,  166,  165,  164,  162,
-      161,  160,  158,  156,  155,  154,  153,  152,  144,  143,
-      142,  141,  140,  139,  138,  137,  136,  135,  133,  132,
-      131,  130,  129,  128,  126,  125,  124,  123,  121,  120,
-      110,  109,  105,  103,  102,  101,  100,   99,   98,   96,
-       95,   94,   93,   92,   91,   90,   88,   87,   86,   85,
-       84,   82,   80,   79,   77,   73,   66,   58,   47,   46,
-       45,   44,   43,   42,   41,   40,   39,   36,   34,   32,
-
-       31,   25,   14,   10,    7,    3,  238,  238,  238,  238,
-      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
-      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
-      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
-      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
-      238,  238,  238,  238,  238,  238,  238,  238,  238,  238,
-      238,  238,  238
+       24,   26,   26,   30,   21,   20,   32,   33,   35,   48,
+       30,   33,   21,   37,   38,   50,   33,   48,   35,   32,
+
+       37,   35,   20,   44,   53,   53,   44,   38,   65,   65,
+      249,   69,   69,   70,   82,   70,   70,   65,  248,   82,
+       69,   99,  117,  117,   70,   65,  245,   50,   69,   71,
+      244,   71,   70,  240,   71,   71,  239,   99,   99,  111,
+      236,  111,  115,  115,  111,  111,  235,  116,  233,  116,
+      232,  115,  116,  116,  118,  118,  126,  126,  231,  115,
+      152,  152,  153,  153,  155,  230,  155,  156,  156,  155,
+      155,  157,  157,  185,  185,  186,  186,  253,  226,  253,
+      254,  254,  254,  255,  255,  255,  256,  256,  256,  256,
+      256,  257,  225,  257,  257,  257,  258,  258,  224,  223,
+
+      222,  218,  217,  216,  215,  214,  212,  211,  210,  209,
+      208,  206,  205,  204,  203,  202,  201,  200,  199,  198,
+      197,  196,  192,  191,  190,  188,  184,  183,  182,  181,
+      179,  178,  177,  176,  175,  174,  173,  172,  170,  169,
+      168,  166,  164,  163,  162,  161,  160,  158,  150,  149,
+      148,  147,  146,  145,  144,  143,  142,  141,  140,  138,
+      137,  136,  135,  134,  133,  131,  130,  129,  128,  127,
+      125,  124,  123,  113,  112,  108,  106,  105,  104,  103,
+      102,  101,  100,   98,   97,   96,   95,   94,   93,   92,
+       90,   89,   88,   87,   86,   84,   83,   81,   80,   79,
+
+       77,   73,   66,   58,   47,   46,   45,   43,   42,   41,
+       40,   39,   36,   34,   31,   25,   14,   10,    7,    3,
+      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
+      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
+      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
+      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
+      252,  252,  252,  252,  252,  252,  252,  252,  252,  252,
+      252,  252,  252,  252,  252,  252,  252
     } ;
 
 /* Table of booleans, true if rule could match eol. */
-static yyconst flex_int32_t yy_rule_can_match_eol[90] =
+static yyconst flex_int32_t yy_rule_can_match_eol[93] =
     {   0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-    0, 0, 0, 0, 0, 0, 1, 1, 0, 0,     };
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,     };
 
 /* The intent behind this definition is that it'll catch
  * any uses of REJECT which flex missed.
@@ -644,7 +647,7 @@ static yyconst flex_int32_t yy_rule_can_match_eol[90] =
     
 */
 #define YY_NO_UNISTD_H 1
-#line 642 "lex.sksl.c"
+#line 645 "lex.sksl.c"
 
 #define INITIAL 0
 
@@ -673,7 +676,7 @@ struct yyguts_t
     size_t yy_buffer_stack_max; /**< capacity of stack. */
     YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
     char yy_hold_char;
-    int yy_n_chars;
+    yy_size_t yy_n_chars;
     yy_size_t yyleng_r;
     char *yy_c_buf_p;
     int yy_init;
@@ -715,11 +718,11 @@ void skslset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
 
 FILE *skslget_in (yyscan_t yyscanner );
 
-void skslset_in  (FILE * _in_str ,yyscan_t yyscanner );
+void skslset_in  (FILE * in_str ,yyscan_t yyscanner );
 
 FILE *skslget_out (yyscan_t yyscanner );
 
-void skslset_out  (FILE * _out_str ,yyscan_t yyscanner );
+void skslset_out  (FILE * out_str ,yyscan_t yyscanner );
 
 yy_size_t skslget_leng (yyscan_t yyscanner );
 
@@ -727,11 +730,11 @@ char *skslget_text (yyscan_t yyscanner );
 
 int skslget_lineno (yyscan_t yyscanner );
 
-void skslset_lineno (int _line_number ,yyscan_t yyscanner );
+void skslset_lineno (int line_number ,yyscan_t yyscanner );
 
 int skslget_column  (yyscan_t yyscanner );
 
-void skslset_column (int _column_no ,yyscan_t yyscanner );
+void skslset_column (int column_no ,yyscan_t yyscanner );
 
 /* Macros after this point can all be overridden by user definitions in
  * section 1.
@@ -745,12 +748,8 @@ extern int skslwrap (yyscan_t yyscanner );
 #endif
 #endif
 
-#ifndef YY_NO_UNPUT
-    
     static void yyunput (int c,char *buf_ptr  ,yyscan_t yyscanner);
     
-#endif
-
 #ifndef yytext_ptr
 static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
 #endif
@@ -863,7 +862,7 @@ extern int sksllex (yyscan_t yyscanner);
 
 /* Code executed at the end of each rule. */
 #ifndef YY_BREAK
-#define YY_BREAK /*LINTED*/break;
+#define YY_BREAK break;
 #endif
 
 #define YY_RULE_SETUP \
@@ -873,9 +872,9 @@ extern int sksllex (yyscan_t yyscanner);
  */
 YY_DECL
 {
-       yy_state_type yy_current_state;
-       char *yy_cp, *yy_bp;
-       int yy_act;
+       register yy_state_type yy_current_state;
+       register char *yy_cp, *yy_bp;
+       register int yy_act;
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
        if ( !yyg->yy_init )
@@ -908,9 +907,9 @@ YY_DECL
 #line 23 "sksl.flex"
 
 
-#line 906 "lex.sksl.c"
+#line 905 "lex.sksl.c"
 
-       while ( /*CONSTCOND*/1 )                /* loops until end-of-file is reached */
+       while ( 1 )             /* loops until end-of-file is reached */
                {
                yy_cp = yyg->yy_c_buf_p;
 
@@ -926,7 +925,7 @@ YY_DECL
 yy_match:
                do
                        {
-                       YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
+                       register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
                        if ( yy_accept[yy_current_state] )
                                {
                                yyg->yy_last_accepting_state = yy_current_state;
@@ -935,13 +934,13 @@ yy_match:
                        while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
                                {
                                yy_current_state = (int) yy_def[yy_current_state];
-                               if ( yy_current_state >= 239 )
+                               if ( yy_current_state >= 253 )
                                        yy_c = yy_meta[(unsigned int) yy_c];
                                }
                        yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
                        ++yy_cp;
                        }
-               while ( yy_current_state != 238 );
+               while ( yy_current_state != 252 );
                yy_cp = yyg->yy_last_accepting_cpos;
                yy_current_state = yyg->yy_last_accepting_state;
 
@@ -1036,391 +1035,406 @@ YY_RULE_SETUP
 case 13:
 YY_RULE_SETUP
 #line 49 "sksl.flex"
-{ return SkSL::Token::BREAK; }
+{ return SkSL::Token::SWITCH; }
        YY_BREAK
 case 14:
 YY_RULE_SETUP
 #line 51 "sksl.flex"
-{ return SkSL::Token::CONTINUE; }
+{ return SkSL::Token::CASE; }
        YY_BREAK
 case 15:
 YY_RULE_SETUP
 #line 53 "sksl.flex"
-{ return SkSL::Token::DISCARD; }
+{ return SkSL::Token::DEFAULT; }
        YY_BREAK
 case 16:
 YY_RULE_SETUP
 #line 55 "sksl.flex"
-{ return SkSL::Token::RETURN; }
+{ return SkSL::Token::BREAK; }
        YY_BREAK
 case 17:
 YY_RULE_SETUP
 #line 57 "sksl.flex"
-{ return SkSL::Token::IN; }
+{ return SkSL::Token::CONTINUE; }
        YY_BREAK
 case 18:
 YY_RULE_SETUP
 #line 59 "sksl.flex"
-{ return SkSL::Token::OUT; }
+{ return SkSL::Token::DISCARD; }
        YY_BREAK
 case 19:
 YY_RULE_SETUP
 #line 61 "sksl.flex"
-{ return SkSL::Token::INOUT; }
+{ return SkSL::Token::RETURN; }
        YY_BREAK
 case 20:
 YY_RULE_SETUP
 #line 63 "sksl.flex"
-{ return SkSL::Token::UNIFORM; }
+{ return SkSL::Token::IN; }
        YY_BREAK
 case 21:
 YY_RULE_SETUP
 #line 65 "sksl.flex"
-{ return SkSL::Token::CONST; }
+{ return SkSL::Token::OUT; }
        YY_BREAK
 case 22:
 YY_RULE_SETUP
 #line 67 "sksl.flex"
-{ return SkSL::Token::LOWP; }
+{ return SkSL::Token::INOUT; }
        YY_BREAK
 case 23:
 YY_RULE_SETUP
 #line 69 "sksl.flex"
-{ return SkSL::Token::MEDIUMP; }
+{ return SkSL::Token::UNIFORM; }
        YY_BREAK
 case 24:
 YY_RULE_SETUP
 #line 71 "sksl.flex"
-{ return SkSL::Token::HIGHP; }
+{ return SkSL::Token::CONST; }
        YY_BREAK
 case 25:
 YY_RULE_SETUP
 #line 73 "sksl.flex"
-{ return SkSL::Token::FLAT; }
+{ return SkSL::Token::LOWP; }
        YY_BREAK
 case 26:
 YY_RULE_SETUP
 #line 75 "sksl.flex"
-{ return SkSL::Token::NOPERSPECTIVE; }
+{ return SkSL::Token::MEDIUMP; }
        YY_BREAK
 case 27:
 YY_RULE_SETUP
 #line 77 "sksl.flex"
-{ return SkSL::Token::READONLY; }
+{ return SkSL::Token::HIGHP; }
        YY_BREAK
 case 28:
 YY_RULE_SETUP
 #line 79 "sksl.flex"
-{ return SkSL::Token::WRITEONLY; }
+{ return SkSL::Token::FLAT; }
        YY_BREAK
 case 29:
 YY_RULE_SETUP
 #line 81 "sksl.flex"
-{ return SkSL::Token::COHERENT; }
+{ return SkSL::Token::NOPERSPECTIVE; }
        YY_BREAK
 case 30:
 YY_RULE_SETUP
 #line 83 "sksl.flex"
-{ return SkSL::Token::VOLATILE; }
+{ return SkSL::Token::READONLY; }
        YY_BREAK
 case 31:
 YY_RULE_SETUP
 #line 85 "sksl.flex"
-{ return SkSL::Token::RESTRICT; }
+{ return SkSL::Token::WRITEONLY; }
        YY_BREAK
 case 32:
 YY_RULE_SETUP
 #line 87 "sksl.flex"
-{ return SkSL::Token::STRUCT; }
+{ return SkSL::Token::COHERENT; }
        YY_BREAK
 case 33:
 YY_RULE_SETUP
 #line 89 "sksl.flex"
-{ return SkSL::Token::LAYOUT; }
+{ return SkSL::Token::VOLATILE; }
        YY_BREAK
 case 34:
 YY_RULE_SETUP
 #line 91 "sksl.flex"
-{ return SkSL::Token::PRECISION; }
+{ return SkSL::Token::RESTRICT; }
        YY_BREAK
 case 35:
 YY_RULE_SETUP
 #line 93 "sksl.flex"
-{ return SkSL::Token::IDENTIFIER; }
+{ return SkSL::Token::STRUCT; }
        YY_BREAK
 case 36:
 YY_RULE_SETUP
 #line 95 "sksl.flex"
-{ return SkSL::Token::DIRECTIVE; }
+{ return SkSL::Token::LAYOUT; }
        YY_BREAK
 case 37:
 YY_RULE_SETUP
 #line 97 "sksl.flex"
-{ return SkSL::Token::LPAREN; }
+{ return SkSL::Token::PRECISION; }
        YY_BREAK
 case 38:
 YY_RULE_SETUP
 #line 99 "sksl.flex"
-{ return SkSL::Token::RPAREN; }
+{ return SkSL::Token::IDENTIFIER; }
        YY_BREAK
 case 39:
 YY_RULE_SETUP
 #line 101 "sksl.flex"
-{ return SkSL::Token::LBRACE; }
+{ return SkSL::Token::DIRECTIVE; }
        YY_BREAK
 case 40:
 YY_RULE_SETUP
 #line 103 "sksl.flex"
-{ return SkSL::Token::RBRACE; }
+{ return SkSL::Token::LPAREN; }
        YY_BREAK
 case 41:
 YY_RULE_SETUP
 #line 105 "sksl.flex"
-{ return SkSL::Token::LBRACKET; }
+{ return SkSL::Token::RPAREN; }
        YY_BREAK
 case 42:
 YY_RULE_SETUP
 #line 107 "sksl.flex"
-{ return SkSL::Token::RBRACKET; }
+{ return SkSL::Token::LBRACE; }
        YY_BREAK
 case 43:
 YY_RULE_SETUP
 #line 109 "sksl.flex"
-{ return SkSL::Token::DOT; }
+{ return SkSL::Token::RBRACE; }
        YY_BREAK
 case 44:
 YY_RULE_SETUP
 #line 111 "sksl.flex"
-{ return SkSL::Token::COMMA; }
+{ return SkSL::Token::LBRACKET; }
        YY_BREAK
 case 45:
 YY_RULE_SETUP
 #line 113 "sksl.flex"
-{ return SkSL::Token::PLUSPLUS; }
+{ return SkSL::Token::RBRACKET; }
        YY_BREAK
 case 46:
 YY_RULE_SETUP
 #line 115 "sksl.flex"
-{ return SkSL::Token::MINUSMINUS; }
+{ return SkSL::Token::DOT; }
        YY_BREAK
 case 47:
 YY_RULE_SETUP
 #line 117 "sksl.flex"
-{ return SkSL::Token::PLUS; }
+{ return SkSL::Token::COMMA; }
        YY_BREAK
 case 48:
 YY_RULE_SETUP
 #line 119 "sksl.flex"
-{ return SkSL::Token::MINUS; }
+{ return SkSL::Token::PLUSPLUS; }
        YY_BREAK
 case 49:
 YY_RULE_SETUP
 #line 121 "sksl.flex"
-{ return SkSL::Token::STAR; }
+{ return SkSL::Token::MINUSMINUS; }
        YY_BREAK
 case 50:
 YY_RULE_SETUP
 #line 123 "sksl.flex"
-{ return SkSL::Token::SLASH; }
+{ return SkSL::Token::PLUS; }
        YY_BREAK
 case 51:
 YY_RULE_SETUP
 #line 125 "sksl.flex"
-{ return SkSL::Token::PERCENT; }
+{ return SkSL::Token::MINUS; }
        YY_BREAK
 case 52:
 YY_RULE_SETUP
 #line 127 "sksl.flex"
-{ return SkSL::Token::SHL; }
+{ return SkSL::Token::STAR; }
        YY_BREAK
 case 53:
 YY_RULE_SETUP
 #line 129 "sksl.flex"
-{ return SkSL::Token::SHR; }
+{ return SkSL::Token::SLASH; }
        YY_BREAK
 case 54:
 YY_RULE_SETUP
 #line 131 "sksl.flex"
-{ return SkSL::Token::BITWISEOR; }
+{ return SkSL::Token::PERCENT; }
        YY_BREAK
 case 55:
 YY_RULE_SETUP
 #line 133 "sksl.flex"
-{ return SkSL::Token::BITWISEXOR; }
+{ return SkSL::Token::SHL; }
        YY_BREAK
 case 56:
 YY_RULE_SETUP
 #line 135 "sksl.flex"
-{ return SkSL::Token::BITWISEAND; }
+{ return SkSL::Token::SHR; }
        YY_BREAK
 case 57:
 YY_RULE_SETUP
 #line 137 "sksl.flex"
-{ return SkSL::Token::BITWISENOT; }
+{ return SkSL::Token::BITWISEOR; }
        YY_BREAK
 case 58:
 YY_RULE_SETUP
 #line 139 "sksl.flex"
-{ return SkSL::Token::LOGICALOR; }
+{ return SkSL::Token::BITWISEXOR; }
        YY_BREAK
 case 59:
 YY_RULE_SETUP
 #line 141 "sksl.flex"
-{ return SkSL::Token::LOGICALXOR; }
+{ return SkSL::Token::BITWISEAND; }
        YY_BREAK
 case 60:
 YY_RULE_SETUP
 #line 143 "sksl.flex"
-{ return SkSL::Token::LOGICALAND; }
+{ return SkSL::Token::BITWISENOT; }
        YY_BREAK
 case 61:
 YY_RULE_SETUP
 #line 145 "sksl.flex"
-{ return SkSL::Token::LOGICALNOT; }
+{ return SkSL::Token::LOGICALOR; }
        YY_BREAK
 case 62:
 YY_RULE_SETUP
 #line 147 "sksl.flex"
-{ return SkSL::Token::QUESTION; }
+{ return SkSL::Token::LOGICALXOR; }
        YY_BREAK
 case 63:
 YY_RULE_SETUP
 #line 149 "sksl.flex"
-{ return SkSL::Token::COLON; }
+{ return SkSL::Token::LOGICALAND; }
        YY_BREAK
 case 64:
 YY_RULE_SETUP
 #line 151 "sksl.flex"
-{ return SkSL::Token::EQ; }
+{ return SkSL::Token::LOGICALNOT; }
        YY_BREAK
 case 65:
 YY_RULE_SETUP
 #line 153 "sksl.flex"
-{ return SkSL::Token::EQEQ; }
+{ return SkSL::Token::QUESTION; }
        YY_BREAK
 case 66:
 YY_RULE_SETUP
 #line 155 "sksl.flex"
-{ return SkSL::Token::NEQ; }
+{ return SkSL::Token::COLON; }
        YY_BREAK
 case 67:
 YY_RULE_SETUP
 #line 157 "sksl.flex"
-{ return SkSL::Token::GT; }
+{ return SkSL::Token::EQ; }
        YY_BREAK
 case 68:
 YY_RULE_SETUP
 #line 159 "sksl.flex"
-{ return SkSL::Token::LT; }
+{ return SkSL::Token::EQEQ; }
        YY_BREAK
 case 69:
 YY_RULE_SETUP
 #line 161 "sksl.flex"
-{ return SkSL::Token::GTEQ; }
+{ return SkSL::Token::NEQ; }
        YY_BREAK
 case 70:
 YY_RULE_SETUP
 #line 163 "sksl.flex"
-{ return SkSL::Token::LTEQ; }
+{ return SkSL::Token::GT; }
        YY_BREAK
 case 71:
 YY_RULE_SETUP
 #line 165 "sksl.flex"
-{ return SkSL::Token::PLUSEQ; }
+{ return SkSL::Token::LT; }
        YY_BREAK
 case 72:
 YY_RULE_SETUP
 #line 167 "sksl.flex"
-{ return SkSL::Token::MINUSEQ; }
+{ return SkSL::Token::GTEQ; }
        YY_BREAK
 case 73:
 YY_RULE_SETUP
 #line 169 "sksl.flex"
-{ return SkSL::Token::STAREQ; }
+{ return SkSL::Token::LTEQ; }
        YY_BREAK
 case 74:
 YY_RULE_SETUP
 #line 171 "sksl.flex"
-{ return SkSL::Token::SLASHEQ; }
+{ return SkSL::Token::PLUSEQ; }
        YY_BREAK
 case 75:
 YY_RULE_SETUP
 #line 173 "sksl.flex"
-{ return SkSL::Token::PERCENTEQ; }
+{ return SkSL::Token::MINUSEQ; }
        YY_BREAK
 case 76:
 YY_RULE_SETUP
 #line 175 "sksl.flex"
-{ return SkSL::Token::SHLEQ; }
+{ return SkSL::Token::STAREQ; }
        YY_BREAK
 case 77:
 YY_RULE_SETUP
 #line 177 "sksl.flex"
-{ return SkSL::Token::SHREQ; }
+{ return SkSL::Token::SLASHEQ; }
        YY_BREAK
 case 78:
 YY_RULE_SETUP
 #line 179 "sksl.flex"
-{ return SkSL::Token::BITWISEOREQ; }
+{ return SkSL::Token::PERCENTEQ; }
        YY_BREAK
 case 79:
 YY_RULE_SETUP
 #line 181 "sksl.flex"
-{ return SkSL::Token::BITWISEXOREQ; }
+{ return SkSL::Token::SHLEQ; }
        YY_BREAK
 case 80:
 YY_RULE_SETUP
 #line 183 "sksl.flex"
-{ return SkSL::Token::BITWISEANDEQ; }
+{ return SkSL::Token::SHREQ; }
        YY_BREAK
 case 81:
 YY_RULE_SETUP
 #line 185 "sksl.flex"
-{ return SkSL::Token::LOGICALOREQ; }
+{ return SkSL::Token::BITWISEOREQ; }
        YY_BREAK
 case 82:
 YY_RULE_SETUP
 #line 187 "sksl.flex"
-{ return SkSL::Token::LOGICALXOREQ; }
+{ return SkSL::Token::BITWISEXOREQ; }
        YY_BREAK
 case 83:
 YY_RULE_SETUP
 #line 189 "sksl.flex"
-{ return SkSL::Token::LOGICALANDEQ; }
+{ return SkSL::Token::BITWISEANDEQ; }
        YY_BREAK
 case 84:
 YY_RULE_SETUP
 #line 191 "sksl.flex"
-{ return SkSL::Token::SEMICOLON; }
+{ return SkSL::Token::LOGICALOREQ; }
        YY_BREAK
 case 85:
 YY_RULE_SETUP
 #line 193 "sksl.flex"
-/* line comment */
+{ return SkSL::Token::LOGICALXOREQ; }
        YY_BREAK
 case 86:
-/* rule 86 can match eol */
 YY_RULE_SETUP
 #line 195 "sksl.flex"
-/* block comment */
+{ return SkSL::Token::LOGICALANDEQ; }
        YY_BREAK
 case 87:
-/* rule 87 can match eol */
 YY_RULE_SETUP
 #line 197 "sksl.flex"
-/* whitespace */
+{ return SkSL::Token::SEMICOLON; }
        YY_BREAK
 case 88:
 YY_RULE_SETUP
 #line 199 "sksl.flex"
-{ return SkSL::Token::INVALID_TOKEN; }
+/* line comment */
        YY_BREAK
 case 89:
+/* rule 89 can match eol */
 YY_RULE_SETUP
 #line 201 "sksl.flex"
+/* block comment */
+       YY_BREAK
+case 90:
+/* rule 90 can match eol */
+YY_RULE_SETUP
+#line 203 "sksl.flex"
+/* whitespace */
+       YY_BREAK
+case 91:
+YY_RULE_SETUP
+#line 205 "sksl.flex"
+{ return SkSL::Token::INVALID_TOKEN; }
+       YY_BREAK
+case 92:
+YY_RULE_SETUP
+#line 207 "sksl.flex"
 ECHO;
        YY_BREAK
-#line 1418 "lex.sksl.c"
+#line 1432 "lex.sksl.c"
 case YY_STATE_EOF(INITIAL):
        yyterminate();
 
@@ -1565,9 +1579,9 @@ case YY_STATE_EOF(INITIAL):
 static int yy_get_next_buffer (yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-       char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
-       char *source = yyg->yytext_ptr;
-       yy_size_t number_to_move, i;
+       register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+       register char *source = yyg->yytext_ptr;
+       register int number_to_move, i;
        int ret_val;
 
        if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
@@ -1596,7 +1610,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
        /* Try to read more data. */
 
        /* First move last chars to start of buffer. */
-       number_to_move = (yy_size_t) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+       number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
 
        for ( i = 0; i < number_to_move; ++i )
                *(dest++) = *(source++);
@@ -1678,9 +1692,9 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
        else
                ret_val = EOB_ACT_CONTINUE_SCAN;
 
-       if ((int) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+       if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
                /* Extend the array by 50%, plus the number we really need. */
-               int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+               yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
                YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) skslrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
                if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
                        YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
@@ -1699,15 +1713,15 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
 
     static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
 {
-       yy_state_type yy_current_state;
-       char *yy_cp;
+       register yy_state_type yy_current_state;
+       register char *yy_cp;
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
        yy_current_state = yyg->yy_start;
 
        for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
                {
-               YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+               register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
                if ( yy_accept[yy_current_state] )
                        {
                        yyg->yy_last_accepting_state = yy_current_state;
@@ -1716,7 +1730,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
                while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
                        {
                        yy_current_state = (int) yy_def[yy_current_state];
-                       if ( yy_current_state >= 239 )
+                       if ( yy_current_state >= 253 )
                                yy_c = yy_meta[(unsigned int) yy_c];
                        }
                yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
@@ -1732,11 +1746,11 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
  */
     static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state , yyscan_t yyscanner)
 {
-       int yy_is_jam;
+       register int yy_is_jam;
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
-       char *yy_cp = yyg->yy_c_buf_p;
+       register char *yy_cp = yyg->yy_c_buf_p;
 
-       YY_CHAR yy_c = 1;
+       register YY_CHAR yy_c = 1;
        if ( yy_accept[yy_current_state] )
                {
                yyg->yy_last_accepting_state = yy_current_state;
@@ -1745,21 +1759,19 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
        while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
                {
                yy_current_state = (int) yy_def[yy_current_state];
-               if ( yy_current_state >= 239 )
+               if ( yy_current_state >= 253 )
                        yy_c = yy_meta[(unsigned int) yy_c];
                }
        yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
-       yy_is_jam = (yy_current_state == 238);
+       yy_is_jam = (yy_current_state == 252);
 
        (void)yyg;
        return yy_is_jam ? 0 : yy_current_state;
 }
 
-#ifndef YY_NO_UNPUT
-
-    static void yyunput (int c, char * yy_bp , yyscan_t yyscanner)
+    static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
 {
-       char *yy_cp;
+       register char *yy_cp;
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
     yy_cp = yyg->yy_c_buf_p;
@@ -1770,10 +1782,10 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
        if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
                { /* need to shift things up to make room */
                /* +2 for EOB chars. */
-               yy_size_t number_to_move = yyg->yy_n_chars + 2;
-               char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+               register yy_size_t number_to_move = yyg->yy_n_chars + 2;
+               register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
                                        YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
-               char *source =
+               register char *source =
                                &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
 
                while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
@@ -1799,8 +1811,6 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
        yyg->yy_c_buf_p = yy_cp;
 }
 
-#endif
-
 #ifndef YY_NO_INPUT
 #ifdef __cplusplus
     static int yyinput (yyscan_t yyscanner)
@@ -1961,7 +1971,7 @@ static void sksl_load_buffer_state  (yyscan_t yyscanner)
        if ( ! b )
                YY_FATAL_ERROR( "out of dynamic memory in sksl_create_buffer()" );
 
-       b->yy_buf_size = (yy_size_t)size;
+       b->yy_buf_size = size;
 
        /* yy_ch_buf has to be 2 characters longer than the size given because
         * we need to put in 2 end-of-buffer characters.
@@ -2122,7 +2132,7 @@ static void skslensure_buffer_stack (yyscan_t yyscanner)
                 * scanner will even need a stack. We use 2 instead of 1 to avoid an
                 * immediate realloc on the next call.
          */
-               num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */
+               num_to_alloc = 1;
                yyg->yy_buffer_stack = (struct yy_buffer_state**)skslalloc
                                                                (num_to_alloc * sizeof(struct yy_buffer_state*)
                                                                , yyscanner);
@@ -2139,7 +2149,7 @@ static void skslensure_buffer_stack (yyscan_t yyscanner)
        if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
 
                /* Increase the buffer to prepare for a possible push. */
-               yy_size_t grow_size = 8 /* arbitrary grow size */;
+               int grow_size = 8 /* arbitrary grow size */;
 
                num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
                yyg->yy_buffer_stack = (struct yy_buffer_state**)skslrealloc
@@ -2247,9 +2257,7 @@ YY_BUFFER_STATE sksl_scan_bytes  (yyconst char * yybytes, yy_size_t  _yybytes_le
 
 static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
 {
-       struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-       (void)yyg;
-       (void) fprintf( stderr, "%s\n", msg );
+       (void) fprintf( stderr, "%s\n", msg );
        exit( YY_EXIT_FAILURE );
 }
 
@@ -2355,10 +2363,10 @@ void skslset_extra (YY_EXTRA_TYPE  user_defined , yyscan_t yyscanner)
 }
 
 /** Set the current line number.
- * @param _line_number line number
+ * @param line_number
  * @param yyscanner The scanner object.
  */
-void skslset_lineno (int  _line_number , yyscan_t yyscanner)
+void skslset_lineno (int  line_number , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
@@ -2366,14 +2374,14 @@ void skslset_lineno (int  _line_number , yyscan_t yyscanner)
         if (! YY_CURRENT_BUFFER )
            YY_FATAL_ERROR( "skslset_lineno called with no buffer" );
     
-    yylineno = _line_number;
+    yylineno = line_number;
 }
 
 /** Set the current column.
- * @param _column_no column number
+ * @param line_number
  * @param yyscanner The scanner object.
  */
-void skslset_column (int  _column_no , yyscan_t yyscanner)
+void skslset_column (int  column_no , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
@@ -2381,25 +2389,25 @@ void skslset_column (int  _column_no , yyscan_t yyscanner)
         if (! YY_CURRENT_BUFFER )
            YY_FATAL_ERROR( "skslset_column called with no buffer" );
     
-    yycolumn = _column_no;
+    yycolumn = column_no;
 }
 
 /** Set the input stream. This does not discard the current
  * input buffer.
- * @param _in_str A readable stream.
+ * @param in_str A readable stream.
  * @param yyscanner The scanner object.
  * @see sksl_switch_to_buffer
  */
-void skslset_in (FILE *  _in_str , yyscan_t yyscanner)
+void skslset_in (FILE *  in_str , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-    yyin = _in_str ;
+    yyin = in_str ;
 }
 
-void skslset_out (FILE *  _out_str , yyscan_t yyscanner)
+void skslset_out (FILE *  out_str , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-    yyout = _out_str ;
+    yyout = out_str ;
 }
 
 int skslget_debug  (yyscan_t yyscanner)
@@ -2408,10 +2416,10 @@ int skslget_debug  (yyscan_t yyscanner)
     return yy_flex_debug;
 }
 
-void skslset_debug (int  _bdebug , yyscan_t yyscanner)
+void skslset_debug (int  bdebug , yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-    yy_flex_debug = _bdebug ;
+    yy_flex_debug = bdebug ;
 }
 
 /* Accessor methods for yylval and yylloc */
@@ -2550,10 +2558,7 @@ int sksllex_destroy  (yyscan_t yyscanner)
 #ifndef yytext_ptr
 static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
 {
-       struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-       (void)yyg;
-
-       int i;
+       register int i;
        for ( i = 0; i < n; ++i )
                s1[i] = s2[i];
 }
@@ -2562,7 +2567,7 @@ static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yysca
 #ifdef YY_NEED_STRLEN
 static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
 {
-       int n;
+       register int n;
        for ( n = 0; s[n]; ++n )
                ;
 
@@ -2572,16 +2577,11 @@ static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
 
 void *skslalloc (yy_size_t  size , yyscan_t yyscanner)
 {
-       struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-       (void)yyg;
        return (void *) malloc( size );
 }
 
 void *skslrealloc  (void * ptr, yy_size_t  size , yyscan_t yyscanner)
 {
-       struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-       (void)yyg;
-
        /* The cast to (char *) in the following accommodates both
         * implementations that use char* generic pointers, and those
         * that use void* generic pointers.  It works with the latter
@@ -2594,14 +2594,12 @@ void *skslrealloc  (void * ptr, yy_size_t  size , yyscan_t yyscanner)
 
 void skslfree (void * ptr , yyscan_t yyscanner)
 {
-       struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-       (void)yyg;
        free( (char *) ptr );   /* see skslrealloc() for (char *) cast */
 }
 
 #define YYTABLES_NAME "yytables"
 
-#line 201 "sksl.flex"
+#line 206 "sksl.flex"
 
 
 
index 25216bd..adecbcf 100644 (file)
@@ -46,6 +46,12 @@ while { return SkSL::Token::WHILE; }
 
 do { return SkSL::Token::DO; }
 
+switch { return SkSL::Token::SWITCH; }
+
+case { return SkSL::Token::CASE; }
+
+default { return SkSL::Token::DEFAULT; }
+
 break { return SkSL::Token::BREAK; }
 
 continue { return SkSL::Token::CONTINUE; }
index 301281c..bde5e70 100644 (file)
@@ -337,6 +337,10 @@ DEF_TEST(SkSLUseWithoutInitialize, r) {
     test_failure(r,
                  "void main() { bool x; if (true && (false || x)) return; }",
                  "error: 1: 'x' has not been assigned\n1 error\n");
+    test_failure(r,
+                 "void main() { int x; switch (3) { case 0: x = 0; case 1: x = 1; }"
+                               "sk_FragColor = vec4(x); }",
+                 "error: 1: 'x' has not been assigned\n1 error\n");
 }
 
 DEF_TEST(SkSLUnreachable, r) {
@@ -366,13 +370,16 @@ DEF_TEST(SkSLNoReturn, r) {
 DEF_TEST(SkSLBreakOutsideLoop, r) {
     test_failure(r,
                  "void foo() { while(true) {} if (true) break; }",
-                 "error: 1: break statement must be inside a loop\n1 error\n");
+                 "error: 1: break statement must be inside a loop or switch\n1 error\n");
 }
 
 DEF_TEST(SkSLContinueOutsideLoop, r) {
     test_failure(r,
                  "void foo() { for(;;); continue; }",
                  "error: 1: continue statement must be inside a loop\n1 error\n");
+    test_failure(r,
+                 "void foo() { switch (1) { default: continue; } }",
+                 "error: 1: continue statement must be inside a loop\n1 error\n");
 }
 
 DEF_TEST(SkSLStaticIfError, r) {
@@ -430,4 +437,25 @@ DEF_TEST(SkSLUnsupportedGLSLIdentifiers, r) {
                  "error: 1: unknown identifier 'gl_FragColor'\n1 error\n");
 }
 
+DEF_TEST(SkSLWrongSwitchTypes, r) {
+    test_failure(r,
+                 "void main() { switch (vec2(1)) { case 1: break; } }",
+                 "error: 1: expected 'int', but found 'vec2'\n1 error\n");
+    test_failure(r,
+                 "void main() { switch (1) { case vec2(1): break; } }",
+                 "error: 1: expected 'int', but found 'vec2'\n1 error\n");
+}
+
+DEF_TEST(SkSLNonConstantCase, r) {
+    test_failure(r,
+                 "void main() { int x = 1; switch (1) { case x: break; } }",
+                 "error: 1: case value must be a constant\n1 error\n");
+}
+
+DEF_TEST(SkSLDuplicateCase, r) {
+    test_failure(r,
+                 "void main() { switch (1) { case 0: case 1: case 0: break; } }",
+                 "error: 1: duplicate case value\n1 error\n");
+}
+
 #endif
index a0fdb98..53e5c6b 100644 (file)
@@ -777,4 +777,91 @@ DEF_TEST(SkSLGeometry, r) {
          SkSL::Program::kGeometry_Kind);
 }
 
+DEF_TEST(SkSLSwitch, r) {
+    test(r,
+         "void main() {"
+         "    float x;"
+         "    switch (1) {"
+         "        case 0:"
+         "            x = 0.0;"
+         "            break;"
+         "        case 1:"
+         "            x = 1.0;"
+         "            break;"
+         "        default:"
+         "            x = 2.0;"
+         "    }"
+         "    sk_FragColor = vec4(x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    float x;\n"
+         "    switch (1) {\n"
+         "        case 0:\n"
+         "            x = 0.0;\n"
+         "            break;\n"
+         "        case 1:\n"
+         "            x = 1.0;\n"
+         "            break;\n"
+         "        default:\n"
+         "            x = 2.0;\n"
+         "    }\n"
+         "    sk_FragColor = vec4(x);\n"
+         "}\n");
+    test(r,
+         "void main() {"
+         "    float x;"
+         "    switch (2) {"
+         "        case 0:"
+         "            x = 0.0;"
+         "        case 1:"
+         "            x = 1.0;"
+         "        default:"
+         "            x = 2.0;"
+         "    }"
+         "    sk_FragColor = vec4(x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    float x;\n"
+         "    switch (2) {\n"
+         "        case 0:\n"
+         "            x = 0.0;\n"
+         "        case 1:\n"
+         "            x = 1.0;\n"
+         "        default:\n"
+         "            x = 2.0;\n"
+         "    }\n"
+         "    sk_FragColor = vec4(2.0);\n"
+         "}\n");
+    test(r,
+         "void main() {"
+         "    float x = 0.0;"
+         "    switch (3) {"
+         "        case 0:"
+         "            x = 0.0;"
+         "        case 1:"
+         "            x = 1.0;"
+         "    }"
+         "    sk_FragColor = vec4(x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    float x = 0.0;\n"
+         "    switch (3) {\n"
+         "        case 0:\n"
+         "            x = 0.0;\n"
+         "        case 1:\n"
+         "            x = 1.0;\n"
+         "    }\n"
+         "    sk_FragColor = vec4(x);\n"
+         "}\n");
+}
+
 #endif