--- /dev/null
+#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
+}
errors.frag
forwardRef.frag
uint.frag
+switch.frag
EOpReturn,
EOpBreak,
EOpContinue,
+ EOpCase,
+ EOpDefault,
//
// Constructors
class TIntermBinary;
class TIntermConstantUnion;
class TIntermSelection;
+class TIntermSwitch;
+class TIntermBranch;
class TIntermTyped;
class TIntermMethod;
class TIntermSymbol;
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;
};
//
-// 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;
};
//
};
//
-// For if tests. Simplified since there is no switch statement.
+// For if tests.
//
class TIntermSelection : public TIntermTyped {
public:
};
//
+// 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.
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;
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);
}
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)
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);
+}
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)
{
}
}
+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;
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
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);
return true;
}
+bool RemoveSwitch(bool /*preVisit*/ , TIntermSwitch* node, TIntermTraverser*)
+{
+ delete node;
+
+ return true;
+}
+
void RemoveConstantUnion(TIntermConstantUnion* node, TIntermTraverser*)
{
delete node;
it.visitSelection = RemoveSelection;
it.visitSymbol = RemoveSymbol;
it.visitUnary = RemoveUnary;
+ it.visitSwitch = RemoveSwitch;
it.preVisit = false;
it.postVisit = true;
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;
}
\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
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
;\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
\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
$$ = 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
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;
}
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
it.visitUnary = OutputUnary;
it.visitLoop = OutputLoop;
it.visitBranch = OutputBranch;
+ it.visitSwitch = OutputSwitch;
root->traverse(&it);
}