Preprocessor: Implement skipping of macros that evaluate to nothing while evaluating expressions. They had been causing early termination of an expression's evaluation.

Also includes some unrelated updates of documentation.


git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@24420 e7fa87d3-cd2b-0410-9028-fcbf551c1848
This commit is contained in:
John Kessenich
2013-12-10 00:25:14 +00:00
parent c719481d43
commit 2d2f31625d
10 changed files with 120 additions and 50 deletions

Binary file not shown.

View File

@@ -66,22 +66,54 @@ shell.
Note: Despite appearances, the use of a DLL is currently disabled; it Note: Despite appearances, the use of a DLL is currently disabled; it
simply makes a standalone executable from a statically linked library. simply makes a standalone executable from a statically linked library.
Basic external programmatic interface Programmatic Interfaces
------------------------------------- -----------------------
Another piece of software can programmatically translate shaders to an AST Another piece of software can programmatically translate shaders to an AST
using the C-style ShInitialize(), ShCompile(), et. al. interface. The main() in using one of two different interfaces:
StandAlone/StandAlone.cpp shows an example way of using these. - A new C++ class-oriented interface, or
- The original C functional interface
The Sh*() interface takes a "compiler" call-back object, which it calls after The main() in StandAlone/StandAlone.cpp shows examples using both styles.
building call back that is passed the AST and can then execute a backend on it.
The following is a simplified resulting run-time call stack: C++ Class Interface (new, preferred):
ShCompile(shader, compiler) -> compiler(AST) -> <back end> This interface is in roughly the last 1/3 of ShaderLang.h. It is in the
glslang namespace and contains the following.
In practice, ShCompile() takes shader strings, default version, and const char* GetEsslVersionString();
warning/error and other options for controling compilation. const char* GetGlslVersionString();
bool InitializeProcess();
void FinalizeProcess();
class TShader
bool parse(...);
void setStrings(...);
const char* getInfoLog();
class TProgram
void addShader(...);
bool link(...);
const char* getInfoLog();
Reflection queries
See ShaderLang.h and the usage of it in StandAlone/StandAlone.cpp for more
details.
C Functional Interface (orginal):
This interface is in roughly the first 2/3 of ShaderLang.h, and referred to
as the Sh*() interface, as all the entry points start "Sh".
The Sh*() interface takes a "compiler" call-back object, which it calls after
building call back that is passed the AST and can then execute a backend on it.
The following is a simplified resulting run-time call stack:
ShCompile(shader, compiler) -> compiler(AST) -> <back end>
In practice, ShCompile() takes shader strings, default version, and
warning/error and other options for controling compilation.
Testing Testing
------- -------
@@ -100,7 +132,7 @@ missing, those tests just won't run.
Basic Internal Operation Basic Internal Operation
------------------------ ------------------------
- Initial lexical analysis is done be the preprocessor in - Initial lexical analysis is done by the preprocessor in
MachineIndependent/Preprocessor, and then refined by a GLSL scanner MachineIndependent/Preprocessor, and then refined by a GLSL scanner
in MachineIndependent/Scan.cpp. There is currently no use of flex. in MachineIndependent/Scan.cpp. There is currently no use of flex.

View File

@@ -67,12 +67,15 @@ ERROR: 7:14014: '#error' : line should be 14014 , string 7
ERROR: 12:14014: '#error' : line should be 14014 , string 12 ERROR: 12:14014: '#error' : line should be 14014 , string 12
ERROR: 12:14026: '#error' : line should be 14026 , string 12 ERROR: 12:14026: '#error' : line should be 14026 , string 12
ERROR: 12:1234: '#line' : unexpected tokens following directive ERROR: 12:1234: '#line' : unexpected tokens following directive
ERROR: 12:1237: '#line' : unexpected tokens following directive
ERROR: 12:20001: '#error' : line should be 20001 ERROR: 12:20001: '#error' : line should be 20001
ERROR: 12:20011: '#error' : line should be 20011 ERROR: 12:20011: '#error' : line should be 20011
ERROR: 12:20021: '#error' : line should be 20021 ERROR: 12:20021: '#error' : line should be 20021
ERROR: 12:20046: '#define' : Macro redefined; different substitutions: SPACE_IN_MIDDLE ERROR: 12:20046: '#define' : Macro redefined; different substitutions: SPACE_IN_MIDDLE
ERROR: 12:20052: '#error' : good evaluation 1
ERROR: 12:20056: '#error' : good evaluation 2
ERROR: 12:10003: '' : missing #endif ERROR: 12:10003: '' : missing #endif
ERROR: 72 compilation errors. No code generated. ERROR: 75 compilation errors. No code generated.
ERROR: node is still EOpNull! ERROR: node is still EOpNull!

View File

@@ -251,11 +251,18 @@ double f = f1;
#define F2 7 #define F2 7
#line L1 + L2 #line L1 + L2
#error line should be 14014, string 7 #error line should be 14014, string 7
#line L1 + L2 F1 + F2 #line L1 + L2 F1 + F2 // antoeuh sat comment
#error line should be 14014, string 12 #error line should be 14014, string 12
#line L1 + L2 + F1 + F2 #line L1 + L2 + F1 + F2
#error line should be 14026, string 12 #error line should be 14026, string 12
#line 1234 F1 + F2 extra #line 1234 F1 + F2 extra
#define empty_extra
#line 1235 F1 + F2 empty_extra
#define moreEmpty empty_extra
#line 1236 F1 + F2 moreEmpty empty_extra // okay, lots of nothin
#line 1237 F1 + F2 moreEmpty empty_extra extra // ERROR, 'extra'
#line 1238 F1 + F2 moreEmpty empty_extra
#line 1239 empty_extra F1 empty_extra + empty_extra F2 empty_extra moreEmpty empty_extra
#line (20000) #line (20000)
#error line should be 20001 #error line should be 20001
#line (20000+10) #line (20000+10)
@@ -288,6 +295,17 @@ void foo234()
#define SPACE_IN_MIDDLE(a,b) space +in middle #define SPACE_IN_MIDDLE(a,b) space +in middle
#define SPACE_IN_MIDDLE(a,b) space + in middle #define SPACE_IN_MIDDLE(a,b) space + in middle
#define FIRSTPART 17
#define SECONDPART + 5
#if FIRSTPART SECONDPART == 22
#error good evaluation 1
#endif
#if moreEmpty FIRSTPART moreEmpty SECONDPART moreEmpty == moreEmpty 22 moreEmpty
#error good evaluation 2
#endif
#line 10000 #line 10000
#if 1 #if 1
#else #else

View File

@@ -9,5 +9,5 @@
// source have to figure out how to create revision.h just to get a build // source have to figure out how to create revision.h just to get a build
// going. However, if it is not updated, it can be a version behind. // going. However, if it is not updated, it can be a version behind.
#define GLSLANG_REVISION "24400" #define GLSLANG_REVISION "24406"
#define GLSLANG_DATE "2013/12/06 17:28:07" #define GLSLANG_DATE "2013/12/08 17:37:46"

View File

@@ -376,7 +376,7 @@ bool DeduceVersionProfile(TInfoSink& infoSink, EShLanguage stage, bool versionNo
} }
// A metecheck on the condition the compiler itself... // A metecheck on the condition the compiler itself...
switch(version) { switch (version) {
case 100: case 100:
case 300: case 300:

View File

@@ -473,28 +473,8 @@ int TPpContext::eval(int token, int precedence, bool shortCircuit, int& res, boo
token = currentInput->scan(this, currentInput, ppToken); token = currentInput->scan(this, currentInput, ppToken);
} }
} else { } else {
int macroReturn = MacroExpand(ppToken->atom, ppToken, 1); token = evalToToken(token, shortCircuit, res, err, ppToken);
if (macroReturn == 0) { return eval(token, precedence, shortCircuit, res, err, ppToken);
parseContext.error(ppToken->loc, "can't evaluate expression", "preprocessor evaluation", "");
err = true;
res = 0;
return token;
} else {
if (macroReturn == -1) {
if (! shortCircuit && parseContext.profile == EEsProfile) {
const char* message = "undefined macro in expression not allowed in es profile";
const char* name = GetAtomString(ppToken->atom);
if (parseContext.messages & EShMsgRelaxedErrors)
parseContext.warn(ppToken->loc, message, "preprocessor evaluation", name);
else
parseContext.error(ppToken->loc, message, "preprocessor evaluation", name);
}
}
token = currentInput->scan(this, currentInput, ppToken);
return eval(token, precedence, shortCircuit, res, err, ppToken);
}
} }
} else if (token == CPP_INTCONSTANT) { } else if (token == CPP_INTCONSTANT) {
res = ppToken->ival; res = ppToken->ival;
@@ -530,6 +510,10 @@ int TPpContext::eval(int token, int precedence, bool shortCircuit, int& res, boo
return token; return token;
} }
} }
token = evalToToken(token, shortCircuit, res, err, ppToken);
// Perform evaluation of binary operation, if there is one, otherwise we are done.
while (! err) { while (! err) {
if (token == ')' || token == '\n') if (token == ')' || token == '\n')
break; break;
@@ -558,6 +542,33 @@ int TPpContext::eval(int token, int precedence, bool shortCircuit, int& res, boo
return token; return token;
} }
// Expand macros, skipping empty expansions, to get to the first real token in those expansions.
int TPpContext::evalToToken(int token, bool shortCircuit, int& res, bool& err, TPpToken* ppToken)
{
while (token == CPP_IDENTIFIER && ppToken->atom != definedAtom) {
int macroReturn = MacroExpand(ppToken->atom, ppToken, true);
if (macroReturn == 0) {
parseContext.error(ppToken->loc, "can't evaluate expression", "preprocessor evaluation", "");
err = true;
res = 0;
break;
}
if (macroReturn == -1) {
if (! shortCircuit && parseContext.profile == EEsProfile) {
const char* message = "undefined macro in expression not allowed in es profile";
const char* name = GetAtomString(ppToken->atom);
if (parseContext.messages & EShMsgRelaxedErrors)
parseContext.warn(ppToken->loc, message, "preprocessor evaluation", name);
else
parseContext.error(ppToken->loc, message, "preprocessor evaluation", name);
}
}
token = currentInput->scan(this, currentInput, ppToken);
}
return token;
}
// Handle #if // Handle #if
int TPpContext::CPPif(TPpToken* ppToken) int TPpContext::CPPif(TPpToken* ppToken)
{ {
@@ -612,6 +623,10 @@ int TPpContext::CPPifdef(int defined, TPpToken* ppToken)
// Handle #line // Handle #line
int TPpContext::CPPline(TPpToken* ppToken) int TPpContext::CPPline(TPpToken* ppToken)
{ {
// "#line must have, after macro substitution, one of the following forms:
// "#line line
// "#line line source-string-number"
int token = currentInput->scan(this, currentInput, ppToken); int token = currentInput->scan(this, currentInput, ppToken);
if (token == '\n') { if (token == '\n') {
parseContext.error(ppToken->loc, "must by followed by an integral literal", "#line", ""); parseContext.error(ppToken->loc, "must by followed by an integral literal", "#line", "");
@@ -633,6 +648,7 @@ int TPpContext::CPPline(TPpToken* ppToken)
parseContext.setCurrentString(fileRes); parseContext.setCurrentString(fileRes);
} }
} }
token = extraTokenCheck(lineAtom, ppToken, token); token = extraTokenCheck(lineAtom, ppToken, token);
return token; return token;
@@ -886,7 +902,7 @@ TPpContext::TokenStream* TPpContext::PrescanMacroArg(TokenStream *a, TPpToken* p
PushEofSrc(); PushEofSrc();
ReadFromTokenStream(a, 0, 0); ReadFromTokenStream(a, 0, 0);
while ((token = currentInput->scan(this, currentInput, ppToken)) > 0) { while ((token = currentInput->scan(this, currentInput, ppToken)) > 0) {
if (token == CPP_IDENTIFIER && MacroExpand(ppToken->atom, ppToken, 0) == 1) if (token == CPP_IDENTIFIER && MacroExpand(ppToken->atom, ppToken, false) == 1)
continue; continue;
RecordToken(n, token, ppToken); RecordToken(n, token, ppToken);
} }
@@ -952,13 +968,13 @@ int TPpContext::zero_scan(TPpContext* pp, InputSrc *inInput, TPpToken* ppToken)
/* /*
** Check an identifier (atom) to see if it is a macro that should be expanded. ** Check an identifier (atom) to see if it is a macro that should be expanded.
** If it is, push an InputSrc that will produce the appropriate expansion ** If it is, and defined, push an InputSrc that will produce the appropriate expansion
** and return 1. ** and return 1.
** If it is, but undefined, it should expand to 0, push an InputSrc that will ** If it is, but undefined, and expandUndef is requested, push an InputSrc that will
** expand to 0 and return -1. ** expand to 0 and return -1.
** Otherwise, return 0. ** Otherwise, return 0.
*/ */
int TPpContext::MacroExpand(int atom, TPpToken* ppToken, int expandUndef) int TPpContext::MacroExpand(int atom, TPpToken* ppToken, bool expandUndef)
{ {
Symbol *sym = LookUpSymbol(atom); Symbol *sym = LookUpSymbol(atom);
MacroInputSrc *in; MacroInputSrc *in;

View File

@@ -247,6 +247,7 @@ protected:
int CPPelse(int matchelse, TPpToken * ppToken); int CPPelse(int matchelse, TPpToken * ppToken);
int extraTokenCheck(int atom, TPpToken* ppToken, int token); int extraTokenCheck(int atom, TPpToken* ppToken, int token);
int eval(int token, int precedence, bool shortCircuit, int& res, bool& err, TPpToken * ppToken); int eval(int token, int precedence, bool shortCircuit, int& res, bool& err, TPpToken * ppToken);
int evalToToken(int token, bool shortCircuit, int& res, bool& err, TPpToken * ppToken);
int CPPif (TPpToken * ppToken); int CPPif (TPpToken * ppToken);
int CPPifdef(int defined, TPpToken * ppToken); int CPPifdef(int defined, TPpToken * ppToken);
int CPPline(TPpToken * ppToken); int CPPline(TPpToken * ppToken);
@@ -260,7 +261,7 @@ protected:
TokenStream* PrescanMacroArg(TokenStream *a, TPpToken * ppToken); TokenStream* PrescanMacroArg(TokenStream *a, TPpToken * ppToken);
static int macro_scan(TPpContext* pp, InputSrc *inInput, TPpToken * ppToken); static int macro_scan(TPpContext* pp, InputSrc *inInput, TPpToken * ppToken);
static int zero_scan(TPpContext* pp, InputSrc *inInput, TPpToken * ppToken); static int zero_scan(TPpContext* pp, InputSrc *inInput, TPpToken * ppToken);
int MacroExpand(int atom, TPpToken* ppToken, int expandUndef); int MacroExpand(int atom, TPpToken* ppToken, bool expandUndef);
// //
// from PpSymbols.cpp // from PpSymbols.cpp

View File

@@ -744,7 +744,7 @@ const char* TPpContext::tokenize(TPpToken* ppToken)
continue; continue;
// expand macros // expand macros
if (token == CPP_IDENTIFIER && MacroExpand(ppToken->atom, ppToken, 0) == 1) if (token == CPP_IDENTIFIER && MacroExpand(ppToken->atom, ppToken, false) == 1)
continue; continue;
if (token == CPP_IDENTIFIER) if (token == CPP_IDENTIFIER)