Extend the syntax of #line and __FILE__ to support filename strings.

According to the GLSL spec, the second parameter to #line should be
an integer source string number and __FILE__ will be substituted
with the integer source string number currently processed. This
patch extends the syntax of #line and __FILE__. Now #line accepts
as the second parameter a filename string quoted by double quotation
marks. And if such a #line is set, __FILE__ will be substituted with
the currently set filename string. The implementation is done via
introducing a new extension GL_GOOGLE_cpp_style_line_directive using
the extension framework.

The purpose is to support cpp-style #line directives, which is
required by #include.
This commit is contained in:
Lei Zhang 2015-06-25 17:53:54 -04:00
parent c777fc2c4c
commit 5011fbebc3
16 changed files with 176 additions and 19 deletions

View File

@ -0,0 +1,26 @@
#extension GL_GOOGLE_cpp_style_line_directive : enable
0
#line 150 a.h
"a.h"
#line 24
"a.h"
#line 42
"a.h"
#line 30 b.cc
"b.cc"
#line 10 3
3
#line 48
3
#line 4
3
#line 55 100
100
#line 1000 c
"c"
#line 42 1
1
#line 42 this-is-a-quite-long-name-maybe-i-should-shorten-it
"this-is-a-quite-long-name-maybe-i-should-shorten-it"

View File

@ -0,0 +1,15 @@
ERROR: 0:3: '#error' : at 0:3
ERROR: a.h:150: '#error' : at a.h:150
ERROR: a.h:24: '#error' : at a.h:24
ERROR: a.h:42: '#error' : at a.h:42
ERROR: b.cc:30: '#error' : at b.cc:30
ERROR: 3:10: '#error' : at 3:10
ERROR: 3:48: '#error' : at 3:48
ERROR: 3:4: '#error' : at 3:4
ERROR: 100:55: '#error' : at 100:55
ERROR: c:1000: '#error' : at c:1000
ERROR: 1:42: '#error' : at 1:42
ERROR: this-is-a-quite-long-name-maybe-i-should-shorten-it:42: '#error' : at this-is-a-quite-long-name-maybe-i-should-shorten-it:42
ERROR: 12 compilation errors. No code generated.

View File

@ -0,0 +1,36 @@
#extension GL_GOOGLE_cpp_style_line_directive : enable
__FILE__
#line 150 "a.h"
__FILE__
#line 24
__FILE__
#line 42
__FILE__
#line 30 "b.cc"
__FILE__
#line 10 3
__FILE__
#line 48
__FILE__
#line 4
__FILE__
#line 55 100
__FILE__
#line 1000 "c"
__FILE__
#line 42 1
__FILE__
#line 42 "this-is-a-quite-long-name-maybe-i-should-shorten-it"
__FILE__

View File

@ -0,0 +1,36 @@
#extension GL_GOOGLE_cpp_style_line_directive : enable
#error at "0:3"
#line 150 "a.h"
#error at "a.h:150"
#line 24
#error at "a.h:24"
#line 42
#error at "a.h:42"
#line 30 "b.cc"
#error at "b.cc:30"
#line 10 3
#error at "3:10"
#line 48
#error at "3:48"
#line 4
#error at "3:4"
#line 55 100
#error at "100:55"
#line 1000 "c"
#error at "c:1000"
#line 42 1
#error at "1:42"
#line 42 "this-is-a-quite-long-name-maybe-i-should-shorten-it"
#error at "this-is-a-quite-long-name-maybe-i-should-shorten-it:42"

View File

@ -1,3 +1,4 @@
preprocessor.cpp_style_line_directive.vert
preprocessor.edge_cases.vert
preprocessor.errors.vert
preprocessor.extensions.vert

View File

@ -189,7 +189,8 @@ inline const TString String(const int i, const int base = 10)
}
struct TSourceLoc {
void init() { string = 0; line = 0; column = 0; }
void init() { name = nullptr; string = 0; line = 0; column = 0; }
const char* name; // descriptive name for this string
int string;
int line;
int column;

View File

@ -98,7 +98,12 @@ public:
void location(TSourceLoc loc) {
const int maxSize = 24;
char locText[maxSize];
if (loc.name != nullptr) {
append(loc.name);
snprintf(locText, maxSize, ":%d", loc.line);
} else {
snprintf(locText, maxSize, "%d:%d", loc.string, loc.line);
}
append(locText);
append(": ");
}

View File

@ -5346,10 +5346,10 @@ void TParseContext::notifyErrorDirective(int line, const char* error_message)
}
}
void TParseContext::notifyLineDirective(int curLineNo, int newLineNo, bool hasSource, int sourceNum)
void TParseContext::notifyLineDirective(int curLineNo, int newLineNo, bool hasSource, int sourceNum, const char* sourceName)
{
if (lineCallback) {
lineCallback(curLineNo, newLineNo, hasSource, sourceNum);
lineCallback(curLineNo, newLineNo, hasSource, sourceNum, sourceName);
}
}

View File

@ -214,6 +214,7 @@ public:
int getNumErrors() const { return numErrors; }
const TSourceLoc& getCurrentLoc() const { return currentScanner->getSourceLoc(); }
void setCurrentLine(int line) { currentScanner->setLine(line); }
void setCurrentSourceName(const char* name) { currentScanner->setFile(name); }
void setCurrentString(int string) { currentScanner->setString(string); }
void setScanner(TInputScanner* scanner) { currentScanner = scanner; }
@ -221,7 +222,7 @@ public:
void notifyVersion(int line, int version, const char* type_string);
void notifyErrorDirective(int line, const char* error_message);
void notifyLineDirective(int curLineNo, int newLineNo, bool hasSource, int sourceNum);
void notifyLineDirective(int curLineNo, int newLineNo, bool hasSource, int sourceNum, const char* sourceName);
void notifyExtensionDirective(int line, const char* extension, const char* behavior);
// The following are implemented in Versions.cpp to localize version/profile/stage/extensions control
@ -243,7 +244,7 @@ public:
void setVersionCallback(const std::function<void(int, int, const char*)>& func) { versionCallback = func; }
void setPragmaCallback(const std::function<void(int, const TVector<TString>&)>& func) { pragmaCallback = func; }
void setLineCallback(const std::function<void(int, int, bool, int)>& func) { lineCallback = func; }
void setLineCallback(const std::function<void(int, int, bool, int, const char*)>& func) { lineCallback = func; }
void setExtensionCallback(const std::function<void(int, const char*, const char*)>& func) { extensionCallback = func; }
void setErrorCallback(const std::function<void(int, const char*)>& func) { errorCallback = func; }
@ -357,7 +358,7 @@ protected:
// These, if set, will be called when a line, pragma ... is preprocessed.
// They will be called with any parameters to the original directive.
std::function<void(int, int, bool, int)> lineCallback;
std::function<void(int, int, bool, int, const char*)> lineCallback;
std::function<void(int, const TVector<TString>&)> pragmaCallback;
std::function<void(int, int, const char*)> versionCallback;
std::function<void(int, const char*, const char*)> extensionCallback;

View File

@ -57,6 +57,9 @@ public:
lengths(L), currentSource(0), currentChar(0), stringBias(b), finale(f)
{
loc = new TSourceLoc[numSources];
for (int i = 0; i < numSources; ++i) {
loc[i].init();
}
loc[currentSource].string = -stringBias;
loc[currentSource].line = 1;
loc[currentSource].column = 0;
@ -140,7 +143,12 @@ public:
// for #line override
void setLine(int newLine) { loc[getLastValidSourceIndex()].line = newLine; }
void setString(int newString) { loc[getLastValidSourceIndex()].string = newString; }
void setFile(const char* filename) { loc[getLastValidSourceIndex()].name = filename; }
void setString(int newString)
{
loc[getLastValidSourceIndex()].string = newString;
loc[getLastValidSourceIndex()].name = nullptr;
}
const TSourceLoc& getSourceLoc() const { return loc[std::max(0, std::min(currentSource, numSources - finale - 1))]; }
// Returns the index (starting from 0) of the most recent valid source string we are reading from.

View File

@ -690,12 +690,17 @@ struct DoPreprocessing {
});
parseContext.setLineCallback([&lineSync, &outputStream, &parseContext](
int curLineNum, int newLineNum, bool hasSource, int sourceNum) {
int curLineNum, int newLineNum, bool hasSource, int sourceNum, const char* sourceName) {
// SourceNum is the number of the source-string that is being parsed.
lineSync.syncToLine(curLineNum);
outputStream << "#line " << newLineNum;
if (hasSource) {
outputStream << " " << sourceNum;
outputStream << " ";
if (sourceName != nullptr) {
outputStream << "\"" << sourceName << "\"";
} else {
outputStream << sourceNum;
}
}
if (parseContext.lineDirectiveShouldSetNextLine()) {
// newLineNum is the new line number for the line following the #line

View File

@ -190,6 +190,7 @@ void TParseContext::initializeExtensionBehavior()
extensionBehavior[E_GL_EXT_tessellation_point_size] = EBhDisable;
extensionBehavior[E_GL_EXT_texture_buffer] = EBhDisablePartial;
extensionBehavior[E_GL_EXT_texture_cube_map_array] = EBhDisablePartial;
extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhDisable;
// OES matching AEP
extensionBehavior[E_GL_OES_geometry_shader] = EBhDisable;
@ -233,6 +234,7 @@ const char* TParseContext::getPreamble()
"#define GL_EXT_tessellation_point_size 1\n"
"#define GL_EXT_texture_buffer 1\n"
"#define GL_EXT_texture_cube_map_array 1\n"
"#define GL_GOOGLE_cpp_style_line_directive 1\n"
// OES matching AEP
"#define GL_OES_geometry_shader 1\n"
@ -264,6 +266,7 @@ const char* TParseContext::getPreamble()
"#define GL_ARB_derivative_control 1\n"
"#define GL_ARB_shader_texture_image_samples 1\n"
"#define GL_ARB_viewport_array 1\n"
"#define GL_GOOGLE_cpp_style_line_directive 1\n"
// "#define GL_ARB_cull_distance 1\n" // present for 4.5, but need extension control over block members
;
}

View File

@ -129,6 +129,8 @@ const char* const E_GL_EXT_tessellation_point_size = "GL_EXT_tessel
const char* const E_GL_EXT_texture_buffer = "GL_EXT_texture_buffer";
const char* const E_GL_EXT_texture_cube_map_array = "GL_EXT_texture_cube_map_array";
const char* const E_GL_GOOGLE_cpp_style_line_directive = "GL_GOOGLE_cpp_style_line_directive";
// OES matching AEP
const char* const E_GL_OES_geometry_shader = "GL_OES_geometry_shader";
const char* const E_GL_OES_geometry_point_size = "GL_OES_geometry_point_size";

View File

@ -612,8 +612,9 @@ int TPpContext::CPPline(TPpToken* ppToken)
int lineRes = 0; // Line number after macro expansion.
int lineToken = 0;
int fileRes = 0; // Source file number after macro expansion.
bool hasFile = false;
int fileRes = 0; // Source file number after macro expansion.
const char* sourceName = nullptr; // Optional source file name.
bool lineErr = false;
bool fileErr = false;
token = eval(token, MIN_PRECEDENCE, false, lineRes, lineErr, ppToken);
@ -627,14 +628,25 @@ int TPpContext::CPPline(TPpToken* ppToken)
parseContext.setCurrentLine(lineRes);
if (token != '\n') {
if (parseContext.extensionTurnedOn(E_GL_GOOGLE_cpp_style_line_directive) && token == PpAtomConstString) {
// We need to save a copy of the string instead of pointing
// to the name field of the token since the name field
// will likely be overwritten by the next token scan.
sourceName = GetAtomString(LookUpAddString(ppToken->name));
parseContext.setCurrentSourceName(sourceName);
hasFile = true;
token = scanToken(ppToken);
} else {
token = eval(token, MIN_PRECEDENCE, false, fileRes, fileErr, ppToken);
if (! fileErr)
if (! fileErr) {
parseContext.setCurrentString(fileRes);
hasFile = true;
}
}
}
}
if (!fileErr && !lineErr) {
parseContext.notifyLineDirective(directiveLoc, lineToken, hasFile, fileRes);
parseContext.notifyLineDirective(directiveLoc, lineToken, hasFile, fileRes, sourceName);
}
token = extraTokenCheck(PpAtomLine, ppToken, token);
@ -952,11 +964,17 @@ int TPpContext::MacroExpand(int atom, TPpToken* ppToken, bool expandUndef, bool
UngetToken(PpAtomConstInt, ppToken);
return 1;
case PpAtomFileMacro:
case PpAtomFileMacro: {
const char* current_file = parseContext.getCurrentLoc().name;
if (parseContext.extensionTurnedOn(E_GL_GOOGLE_cpp_style_line_directive) && current_file != nullptr) {
sprintf(ppToken->name, "\"%s\"", current_file);
} else {
ppToken->ival = parseContext.getCurrentLoc().string;
sprintf(ppToken->name, "%d", ppToken->ival);
}
UngetToken(PpAtomConstInt, ppToken);
return 1;
}
case PpAtomVersionMacro:
ppToken->ival = parseContext.version;