Add switch/case/default statements, using a switch node that contains a sequence of case/default nodes and top-level nodes of the code chunks in between them.

git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@21131 e7fa87d3-cd2b-0410-9028-fcbf551c1848
This commit is contained in:
John Kessenich 2013-04-12 03:57:02 +00:00
parent 8e5425745f
commit 0576126005
10 changed files with 244 additions and 23 deletions

64
Test/switch.frag Normal file
View File

@ -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
}

View File

@ -33,3 +33,4 @@ constFold.frag
errors.frag
forwardRef.frag
uint.frag
switch.frag

View 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:
@ -553,6 +560,24 @@ protected:
TIntermNode* falseBlock;
};
//
// 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
@ -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;

View 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);
}

View 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;

View 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);

View 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;

View 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;
}

View File

@ -188,9 +188,9 @@ extern void yyerror(const char*);
%type <interm.intermNode> translation_unit function_definition
%type <interm.intermNode> statement simple_statement
%type <interm.intermAggregate> statement_list compound_statement
%type <interm.intermAggregate> statement_list switch_statement_list compound_statement
%type <interm.intermNode> declaration_statement selection_statement expression_statement
%type <interm.intermNode> switch_statement case_label switch_statement_list
%type <interm.intermNode> switch_statement case_label
%type <interm.intermNode> declaration external_declaration
%type <interm.intermNode> for_init_statement compound_statement_no_new_scope
%type <interm.nodePair> selection_rest_statement for_rest_statement
@ -2735,9 +2735,19 @@ compound_statement_no_new_scope
statement_list
: statement {
$$ = parseContext.intermediate.makeAggregate($1, 0);
if ($1 && $1->getAsBranchNode() && ($1->getAsBranchNode()->getFlowOp() == EOpCase ||
$1->getAsBranchNode()->getFlowOp() == EOpDefault)) {
parseContext.wrapupSwitchSubsequence(0, $1);
$$ = 0; // start a fresh subsequence for what's after this case
}
}
| statement_list statement {
$$ = parseContext.intermediate.growAggregate($1, $2, 0);
if ($2 && $2->getAsBranchNode() && ($2->getAsBranchNode()->getFlowOp() == EOpCase ||
$2->getAsBranchNode()->getFlowOp() == EOpDefault)) {
parseContext.wrapupSwitchSubsequence($1, $2);
$$ = 0; // start a fresh subsequence for what's after this case
} else
$$ = parseContext.intermediate.growAggregate($1, $2, 0);
}
;
@ -2787,14 +2797,20 @@ condition
;
switch_statement
: SWITCH LEFT_PAREN expression RIGHT_PAREN { ++parseContext.switchNestingLevel; } LEFT_BRACE switch_statement_list RIGHT_BRACE {
$$ = 0;
--parseContext.switchNestingLevel;
: SWITCH LEFT_PAREN expression RIGHT_PAREN {
// start new switch sequence on the switch stack
parseContext.switchSequenceStack.push_back(new TIntermSequence);
}
LEFT_BRACE switch_statement_list RIGHT_BRACE {
$$ = parseContext.addSwitch($1.line, $3, $7);
delete parseContext.switchSequenceStack.back();
parseContext.switchSequenceStack.pop_back();
}
;
switch_statement_list
: /* nothing */ {
$$ = 0;
}
| statement_list {
$$ = $1;
@ -2803,10 +2819,10 @@ switch_statement_list
case_label
: CASE expression COLON {
$$ = 0;
$$ = parseContext.intermediate.addBranch(EOpCase, $2, $1.line);
}
| DEFAULT COLON {
$$ = 0;
$$ = parseContext.intermediate.addBranch(EOpDefault, $1.line);
}
;
@ -2881,7 +2897,7 @@ jump_statement
$$ = parseContext.intermediate.addBranch(EOpContinue, $1.line);
}
| BREAK SEMICOLON {
if (parseContext.loopNestingLevel + parseContext.switchNestingLevel <= 0) {
if (parseContext.loopNestingLevel + parseContext.switchSequenceStack.size() <= 0) {
parseContext.error($1.line, "break statement only allowed in switch and loops", "", "");
parseContext.recover();
}

View 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);
}