mirror of
https://github.com/RPCS3/glslang.git
synced 2024-12-01 07:01:12 +00:00
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:
parent
8e5425745f
commit
0576126005
64
Test/switch.frag
Normal file
64
Test/switch.frag
Normal 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
|
||||
}
|
@ -33,3 +33,4 @@ constFold.frag
|
||||
errors.frag
|
||||
forwardRef.frag
|
||||
uint.frag
|
||||
switch.frag
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user