Add switch/case/default statements, using a switch node that contains a sequence...
authorJohn Kessenich <cepheus@frii.com>
Fri, 12 Apr 2013 03:57:02 +0000 (03:57 +0000)
committerJohn Kessenich <cepheus@frii.com>
Fri, 12 Apr 2013 03:57:02 +0000 (03:57 +0000)
git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@21131 e7fa87d3-cd2b-0410-9028-fcbf551c1848

Test/switch.frag [new file with mode: 0644]
Test/testlist
glslang/Include/intermediate.h
glslang/MachineIndependent/IntermTraverse.cpp
glslang/MachineIndependent/ParseHelper.cpp
glslang/MachineIndependent/ParseHelper.h
glslang/MachineIndependent/RemoveTree.cpp
glslang/MachineIndependent/ShaderLang.cpp
glslang/MachineIndependent/glslang.y
glslang/MachineIndependent/intermOut.cpp

diff --git a/Test/switch.frag b/Test/switch.frag
new file mode 100644 (file)
index 0000000..690562a
--- /dev/null
@@ -0,0 +1,64 @@
+#version 300 es
+
+uniform int c, d;
+in float x;
+
+void main()
+{
+    float f;
+    int a[2];
+
+    switch(f) { // ERROR
+    }
+
+    switch(a) { // ERROR
+    }
+
+    switch(c)
+    {
+    }
+
+    switch(c)
+    {
+    case 2:    // ERROR, not enough stuff
+    }
+
+    switch(c)
+    {
+        f = sin(x); // ERRROR
+    case 2:    // ERROR, not enough stuff
+        f = cos(x);
+        break;
+    }
+
+    switch (c) {
+    case 1:
+        f = sin(x);
+        break;
+    case 2:
+        f = cos(x);
+        break;
+    default:
+        f = tan(x);
+    }
+
+    switch (c) {
+    case 1:
+        f = sin(x);
+        break;
+    case 2:
+        switch (d) {
+        case 1:
+            f = x * x * x;
+            break;
+        case 2:
+            f = x * x;
+            break;
+        }
+        break;
+    default:
+        f = tan(x);
+    }
+
+    break; // ERROR
+}
index 36c3bc9..3127f9b 100644 (file)
@@ -33,3 +33,4 @@ constFold.frag
 errors.frag
 forwardRef.frag
 uint.frag
+switch.frag
index 69229ce..d35ae8f 100644 (file)
@@ -217,6 +217,8 @@ enum TOperator {
     EOpReturn,
     EOpBreak,
     EOpContinue,
+    EOpCase,
+    EOpDefault,
 
     //
     // Constructors
@@ -297,6 +299,8 @@ class TIntermUnary;
 class TIntermBinary;
 class TIntermConstantUnion;
 class TIntermSelection;
+class TIntermSwitch;
+class TIntermBranch;
 class TIntermTyped;
 class TIntermMethod;
 class TIntermSymbol;
@@ -319,8 +323,10 @@ public:
     virtual TIntermUnary*     getAsUnaryNode()     { return 0; }
     virtual TIntermBinary*    getAsBinaryNode()    { return 0; }
     virtual TIntermSelection* getAsSelectionNode() { return 0; }
+    virtual TIntermSwitch*    getAsSwitchNode()    { return 0; }
     virtual TIntermMethod*    getAsMethodNode()    { return 0; }
     virtual TIntermSymbol*    getAsSymbolNode()    { return 0; }
+    virtual TIntermBranch*    getAsBranchNode()    { return 0; }
     virtual ~TIntermNode() { }
 protected:
     TSourceLoc line;
@@ -383,19 +389,20 @@ protected:
 };
 
 //
-// Handle break, continue, return, and kill.
+// Handle case, break, continue, return, and kill.
 //
 class TIntermBranch : public TIntermNode {
 public:
     TIntermBranch(TOperator op, TIntermTyped* e) :
         flowOp(op),
         expression(e) { }
+    virtual TIntermBranch* getAsBranchNode() { return this; }
     virtual void traverse(TIntermTraverser*);
     TOperator getFlowOp() { return flowOp; }
     TIntermTyped* getExpression() { return expression; }
 protected:
     TOperator flowOp;
-    TIntermTyped* expression;  // non-zero except for "return exp;" statements
+    TIntermTyped* expression;
 };
 
 //
@@ -534,7 +541,7 @@ protected:
 };
 
 //
-// For if tests.  Simplified since there is no switch statement.
+// For if tests.
 //
 class TIntermSelection : public TIntermTyped {
 public:
@@ -554,6 +561,24 @@ protected:
 };
 
 //
+// For switch statements.  Designed use is that a switch will have sequence of nodes
+// that are either case/default nodes or a *single* node that represents all the code
+// in between (if any) consecutive case/defaults.  So, a traversal need only deal with
+// 0 or 1 nodes per case/default statement.
+//
+class TIntermSwitch : public TIntermAggregate {
+public:
+    TIntermSwitch(TIntermTyped* cond, TIntermAggregate* b) : condition(cond), body(b) { }
+    virtual void traverse(TIntermTraverser*);
+    virtual TIntermNode* getCondition() const { return condition; }
+    virtual TIntermAggregate* getBody() const { return body; }
+    virtual TIntermSwitch* getAsSwitchNode() { return this; }
+protected:
+    TIntermTyped* condition;
+    TIntermAggregate* body;
+};
+
+//
 // For traversing the tree.  User should derive from this, 
 // put their traversal specific data in it, and then pass
 // it to a Traverse method.
@@ -587,6 +612,7 @@ public:
     bool (*visitAggregate)(bool preVisit, TIntermAggregate*, TIntermTraverser*);
     bool (*visitLoop)(bool preVisit, TIntermLoop*, TIntermTraverser*);
     bool (*visitBranch)(bool preVisit, TIntermBranch*,  TIntermTraverser*);
+    bool (*visitSwitch)(bool preVisit, TIntermSwitch*,  TIntermTraverser*);
 
     int  depth;
     bool preVisit;
index eb87bf6..8b8d188 100644 (file)
@@ -176,13 +176,13 @@ void TIntermSelection::traverse(TIntermTraverser* it)
         if (it->rightToLeft) {
             if (falseBlock)
                 falseBlock->traverse(it);
-                       if (trueBlock)
-                               trueBlock->traverse(it);
+            if (trueBlock)
+                trueBlock->traverse(it);
             condition->traverse(it);
         } else {
             condition->traverse(it);
-                       if (trueBlock)
-                               trueBlock->traverse(it);
+            if (trueBlock)
+                trueBlock->traverse(it);
             if (falseBlock)
                 falseBlock->traverse(it);
         }
@@ -210,11 +210,11 @@ void TIntermLoop::traverse(TIntermTraverser* it)
                 terminal->traverse(it);
             if (body)
                 body->traverse(it);
-                       if (test)
-                               test->traverse(it);
+            if (test)
+                test->traverse(it);
         } else {
-                       if (test)
-                               test->traverse(it);
+            if (test)
+                test->traverse(it);
             if (body)
                 body->traverse(it);
             if (terminal)
@@ -247,3 +247,28 @@ void TIntermBranch::traverse(TIntermTraverser* it)
         it->visitBranch(false, this, it);
 }
 
+//
+// Traverse a switch node.
+//
+void TIntermSwitch::traverse(TIntermTraverser* it)
+{
+    bool visit = true;
+
+    if (it->preVisit && it->visitSwitch)
+        visit = it->visitSwitch(true, this, it);
+    
+    if (visit) {
+        ++it->depth;
+        if (it->rightToLeft) {
+            body->traverse(it);
+            condition->traverse(it);
+        } else {
+            condition->traverse(it);
+            body->traverse(it);
+        }
+        --it->depth;
+    }
+
+    if (visit && it->postVisit && it->visitSwitch)
+        it->visitSwitch(false, this, it);
+}
index b247cbf..033a8c1 100644 (file)
@@ -44,7 +44,7 @@ TParseContext::TParseContext(TSymbolTable& symt, TIntermediate& interm, int v, E
                              bool fc, EShMessages m) : 
             intermediate(interm), symbolTable(symt), infoSink(is), language(L), treeRoot(0),
             recoveredFromError(false), numErrors(0), lexAfterType(false), loopNestingLevel(0),
-            switchNestingLevel(0), inTypeParen(false), 
+            inTypeParen(false),
             version(v), profile(p), forwardCompatible(fc), messages(m),
             contextPragma(true, false)
 {
@@ -1574,6 +1574,58 @@ void TParseContext::addBlock(int line, TPublicType& publicType, const TString& b
     }
 }
 
+void TParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode)
+{
+    auto switchSequence = switchSequenceStack.back();
+
+    if (statements) {
+        if (switchSequence->size() == 0) {
+            error(statements->getLine(), "cannot have statements before first case/default label", "switch", "");
+            recover();
+        }
+        statements->setOperator(EOpSequence);
+        switchSequence->push_back(statements);
+    }
+    if (branchNode)
+        switchSequence->push_back(branchNode);
+}
+
+TIntermNode* TParseContext::addSwitch(int line, TIntermTyped* expression, TIntermAggregate* lastStatements)
+{
+    profileRequires(line, EEsProfile, 300, 0, "switch statements");
+    profileRequires(line, ENoProfile, 130, 0, "switch statements");
+
+    wrapupSwitchSubsequence(lastStatements, 0);
+
+    if (expression == 0 || 
+        expression->getBasicType() != EbtInt && expression->getBasicType() != EbtUint ||
+        expression->getType().isArray() || expression->getType().isMatrix() || expression->getType().isVector()) {
+            error(line, "condition must be a scalar integer expression", "switch", "");
+            recover();
+    }
+
+    // If there is nothing to do, drop the switch but still execute the expression
+    auto switchSequence = switchSequenceStack.back();
+    if (switchSequence->size() == 0)
+        return expression;
+
+    if (lastStatements == 0) {
+        error(line, "last case/default label must be followed by statements", "switch", "");
+        recover();
+
+        return expression;
+    }
+
+    TIntermAggregate* body = new TIntermAggregate(EOpSequence);
+    body->getSequence() = *switchSequenceStack.back();
+    body->setLine(line);
+
+    TIntermSwitch* switchNode = new TIntermSwitch(expression, body);
+    switchNode->setLine(line);
+
+    return switchNode;
+}
+
 void TParseContext::updateDefaults(int line, const TPublicType& publicType, const TString* id)
 {
     bool cantHaveId = false;
index 66c75ec..10b3135 100644 (file)
@@ -78,7 +78,7 @@ struct TParseContext {
     int numErrors;
     bool lexAfterType;           // true if we've recognized a type, so can only be looking for an identifier
     int loopNestingLevel;        // 0 if outside all loops
-    int switchNestingLevel;      // 0 if outside all switch statements
+    TList<TIntermSequence*> switchSequenceStack;  // case, node, case, case, node, ...; ensure only one node between cases;   stack of them for nesting
     bool inTypeParen;            // true if in parentheses, looking only for an identifier
     const TType* currentFunctionType;  // the return type of the function that's currently being parsed
     bool functionReturnsValue;   // true if a non-void function has a return
@@ -143,6 +143,8 @@ struct TParseContext {
     TIntermTyped* constructStruct(TIntermNode*, const TType&, int, TSourceLoc);
     TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermNode*, TSourceLoc, bool subset);
     void addBlock(int line, TPublicType& qualifier, const TString& blockName, TTypeList& typeList, const TString* instanceName = 0, TArraySizes arraySizes = 0);
+    void wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode);
+    TIntermNode* addSwitch(int line, TIntermTyped* expression, TIntermAggregate* body);
     void updateDefaults(int line, const TPublicType&, const TString* id);
     TIntermTyped* addConstVectorNode(TVectorFields&, TIntermTyped*, TSourceLoc);
     TIntermTyped* addConstMatrixNode(int , TIntermTyped*, TSourceLoc);
index 196279e..e4f4dc6 100644 (file)
@@ -71,6 +71,13 @@ bool RemoveSelection(bool  /*preVisit*/ , TIntermSelection* node, TIntermTravers
        return true;
 }
 
+bool RemoveSwitch(bool  /*preVisit*/ , TIntermSwitch* node, TIntermTraverser*)
+{
+       delete node;
+
+       return true;
+}
+
 void RemoveConstantUnion(TIntermConstantUnion* node, TIntermTraverser*)
 {
        delete node;
@@ -89,6 +96,7 @@ void RemoveAllTreeNodes(TIntermNode* root)
     it.visitSelection     = RemoveSelection;
     it.visitSymbol        = RemoveSymbol;
     it.visitUnary         = RemoveUnary;
+    it.visitSwitch        = RemoveSwitch;
 
        it.preVisit = false;
        it.postVisit = true;
index ea4a31d..fb89172 100644 (file)
@@ -131,9 +131,9 @@ bool InitializeSymbolTable(TBuiltInStrings* BuiltInStrings, int version, EProfil
 
         builtInShaders[0] = (*i).c_str();
         builtInLengths[0] = (int) (*i).size();
-
         if (PaParseStrings(const_cast<char**>(builtInShaders), builtInLengths, 1, parseContext, 0) != 0) {
             infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins");
+            printf("Unable to parse built-ins\n");
 
             return false;
         }
index 1bf9c00..4a7f950 100644 (file)
@@ -188,9 +188,9 @@ extern void yyerror(const char*);
 \r
 %type <interm.intermNode> translation_unit function_definition\r
 %type <interm.intermNode> statement simple_statement\r
-%type <interm.intermAggregate>  statement_list compound_statement\r
+%type <interm.intermAggregate>  statement_list switch_statement_list compound_statement\r
 %type <interm.intermNode> declaration_statement selection_statement expression_statement\r
-%type <interm.intermNode> switch_statement case_label switch_statement_list\r
+%type <interm.intermNode> switch_statement case_label\r
 %type <interm.intermNode> declaration external_declaration\r
 %type <interm.intermNode> for_init_statement compound_statement_no_new_scope\r
 %type <interm.nodePair> selection_rest_statement for_rest_statement\r
@@ -2735,9 +2735,19 @@ compound_statement_no_new_scope
 statement_list\r
     : statement {\r
         $$ = parseContext.intermediate.makeAggregate($1, 0);\r
+        if ($1 && $1->getAsBranchNode() && ($1->getAsBranchNode()->getFlowOp() == EOpCase ||\r
+                                            $1->getAsBranchNode()->getFlowOp() == EOpDefault)) {\r
+            parseContext.wrapupSwitchSubsequence(0, $1);\r
+            $$ = 0;  // start a fresh subsequence for what's after this case\r
+        }\r
     }\r
     | statement_list statement {\r
-        $$ = parseContext.intermediate.growAggregate($1, $2, 0);\r
+        if ($2 && $2->getAsBranchNode() && ($2->getAsBranchNode()->getFlowOp() == EOpCase || \r
+                                            $2->getAsBranchNode()->getFlowOp() == EOpDefault)) {\r
+            parseContext.wrapupSwitchSubsequence($1, $2);\r
+            $$ = 0;  // start a fresh subsequence for what's after this case\r
+        } else\r
+            $$ = parseContext.intermediate.growAggregate($1, $2, 0);\r
     }\r
     ;\r
 \r
@@ -2787,14 +2797,20 @@ condition
     ;\r
 \r
 switch_statement\r
-    : SWITCH LEFT_PAREN expression RIGHT_PAREN { ++parseContext.switchNestingLevel; } LEFT_BRACE switch_statement_list RIGHT_BRACE {\r
-        $$ = 0;\r
-        --parseContext.switchNestingLevel;\r
+    : SWITCH LEFT_PAREN expression RIGHT_PAREN {\r
+        // start new switch sequence on the switch stack\r
+        parseContext.switchSequenceStack.push_back(new TIntermSequence);\r
+    } \r
+    LEFT_BRACE switch_statement_list RIGHT_BRACE {\r
+        $$ = parseContext.addSwitch($1.line, $3, $7);\r
+        delete parseContext.switchSequenceStack.back();\r
+        parseContext.switchSequenceStack.pop_back();\r
     }\r
     ;\r
 \r
 switch_statement_list\r
     : /* nothing */ {\r
+        $$ = 0;\r
     }\r
     | statement_list {\r
         $$ = $1;\r
@@ -2803,10 +2819,10 @@ switch_statement_list
 \r
 case_label\r
     : CASE expression COLON {\r
-        $$ = 0;\r
+        $$ = parseContext.intermediate.addBranch(EOpCase, $2, $1.line);\r
     }\r
     | DEFAULT COLON {\r
-        $$ = 0;\r
+        $$ = parseContext.intermediate.addBranch(EOpDefault, $1.line);\r
     }\r
     ;\r
 \r
@@ -2881,7 +2897,7 @@ jump_statement
         $$ = parseContext.intermediate.addBranch(EOpContinue, $1.line);\r
     }\r
     | BREAK SEMICOLON {\r
-        if (parseContext.loopNestingLevel + parseContext.switchNestingLevel <= 0) {\r
+        if (parseContext.loopNestingLevel + parseContext.switchSequenceStack.size() <= 0) {\r
             parseContext.error($1.line, "break statement only allowed in switch and loops", "", "");\r
             parseContext.recover();\r
         }\r
index ace8154..980f108 100644 (file)
@@ -503,6 +503,8 @@ bool OutputBranch(bool /* previsit*/, TIntermBranch* node, TIntermTraverser* it)
     case EOpBreak:     out.debug << "Branch: Break";          break;
     case EOpContinue:  out.debug << "Branch: Continue";       break;
     case EOpReturn:    out.debug << "Branch: Return";         break;
+    case EOpCase:      out.debug << "case: ";                 break;
+    case EOpDefault:   out.debug << "default: ";              break;
     default:               out.debug << "Branch: Unknown Branch"; break;
     }
 
@@ -517,6 +519,30 @@ bool OutputBranch(bool /* previsit*/, TIntermBranch* node, TIntermTraverser* it)
     return false;
 }
 
+bool OutputSwitch(bool /* preVisit */, TIntermSwitch* node, TIntermTraverser* it)
+{
+    TOutputTraverser* oit = static_cast<TOutputTraverser*>(it);
+    TInfoSink& out = oit->infoSink;
+
+    OutputTreeText(out, node, oit->depth);
+    out.debug << "switch\n";
+
+    OutputTreeText(out, node, oit->depth);
+    out.debug << "condition\n";
+    ++oit->depth;
+    node->getCondition()->traverse(it);
+
+    --oit->depth;
+    OutputTreeText(out, node, oit->depth);
+    out.debug << "body\n";
+    ++oit->depth;
+    node->getBody()->traverse(it);
+
+    --oit->depth;
+
+    return false;
+}
+
 //
 // This function is the one to call externally to start the traversal.
 // Individual functions can be initialized to 0 to skip processing of that
@@ -537,6 +563,7 @@ void TIntermediate::outputTree(TIntermNode* root)
     it.visitUnary = OutputUnary;
     it.visitLoop = OutputLoop;
     it.visitBranch = OutputBranch;
+    it.visitSwitch = OutputSwitch;
 
     root->traverse(&it);
 }