Merge pull request #46 from google/include-directive

#include directive support
This commit is contained in:
John Kessenich 2015-08-05 10:29:23 -06:00
commit d618070ab0
19 changed files with 249 additions and 31 deletions

View File

@ -701,8 +701,8 @@ void CompileAndLinkShaders()
shader->setStrings(shaderStrings, 1); shader->setStrings(shaderStrings, 1);
if (Options & EOptionOutputPreprocessed) { if (Options & EOptionOutputPreprocessed) {
std::string str; std::string str;
if (shader->preprocess(&Resources, defaultVersion, ENoProfile, if (shader->preprocess(&Resources, defaultVersion, ENoProfile, false, false,
false, false, messages, &str)) { messages, &str, glslang::TShader::ForbidInclude())) {
PutsIfNonEmpty(str.c_str()); PutsIfNonEmpty(str.c_str());
} else { } else {
CompileFailed = true; CompileFailed = true;

View File

@ -0,0 +1,13 @@
ERROR: 0:8000: '#include' : required extension not requested: GL_GOOGLE_include_directive
ERROR: 0:8000: '#include' : must be followed by a file designation
ERROR: 0:8001: '#include' : required extension not requested: GL_GOOGLE_include_directive
ERROR: 0:8001: '#include' : must be followed by a file designation
ERROR: 0:8002: '#include' : required extension not requested: GL_GOOGLE_include_directive
ERROR: 0:8002: '#include' : unexpected include directive
ERROR: 0:8003: '#include' : required extension not requested: GL_GOOGLE_include_directive
ERROR: 0:8003: '#include' : extra content after file designation
ERROR: 0:8004: '#include' : required extension not requested: GL_GOOGLE_include_directive
ERROR: 0:8004: '#include' : unexpected include directive
ERROR: 10 compilation errors. No code generated.

View File

@ -0,0 +1,8 @@
ERROR: 0:8000: '#include' : must be followed by a file designation
ERROR: 0:8001: '#include' : must be followed by a file designation
ERROR: 0:8002: '#include' : unexpected include directive
ERROR: 0:8003: '#include' : extra content after file designation
ERROR: 0:8004: '#include' : unexpected include directive
ERROR: 5 compilation errors. No code generated.

View File

@ -0,0 +1,7 @@
#line 8000
#include
#include 123
#include "foo"
#include "foo" garbage
#include "no-eol"

View File

@ -0,0 +1,7 @@
#extension GL_GOOGLE_include_directive : enable
#line 8000
#include
#include 123
#include "foo"
#include "foo" garbage
#include "no-eol"

View File

@ -4,6 +4,8 @@ preprocessor.edge_cases.vert
preprocessor.errors.vert preprocessor.errors.vert
preprocessor.extensions.vert preprocessor.extensions.vert
preprocessor.function_macro.vert preprocessor.function_macro.vert
preprocessor.include.enabled.vert
preprocessor.include.disabled.vert
preprocessor.line.vert preprocessor.line.vert
preprocessor.line.frag preprocessor.line.frag
preprocessor.pragma.vert preprocessor.pragma.vert

View File

@ -214,9 +214,11 @@ public:
int getNumErrors() const { return numErrors; } int getNumErrors() const { return numErrors; }
const TSourceLoc& getCurrentLoc() const { return currentScanner->getSourceLoc(); } const TSourceLoc& getCurrentLoc() const { return currentScanner->getSourceLoc(); }
void setCurrentLine(int line) { currentScanner->setLine(line); } void setCurrentLine(int line) { currentScanner->setLine(line); }
void setCurrentColumn(int col) { currentScanner->setColumn(col); }
void setCurrentSourceName(const char* name) { currentScanner->setFile(name); } void setCurrentSourceName(const char* name) { currentScanner->setFile(name); }
void setCurrentString(int string) { currentScanner->setString(string); } void setCurrentString(int string) { currentScanner->setString(string); }
void setScanner(TInputScanner* scanner) { currentScanner = scanner; } void setScanner(TInputScanner* scanner) { currentScanner = scanner; }
TInputScanner* getScanner() const { return currentScanner; }
bool lineDirectiveShouldSetNextLine() const; bool lineDirectiveShouldSetNextLine() const;
@ -235,6 +237,7 @@ public:
void checkDeprecated(const TSourceLoc&, int queryProfiles, int depVersion, const char* featureDesc); void checkDeprecated(const TSourceLoc&, int queryProfiles, int depVersion, const char* featureDesc);
void requireNotRemoved(const TSourceLoc&, int queryProfiles, int removedVersion, const char* featureDesc); void requireNotRemoved(const TSourceLoc&, int queryProfiles, int removedVersion, const char* featureDesc);
void requireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], const char* featureDesc); void requireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], const char* featureDesc);
void ppRequireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], const char* featureDesc);
TExtensionBehavior getExtensionBehavior(const char*); TExtensionBehavior getExtensionBehavior(const char*);
bool extensionTurnedOn(const char* const extension); bool extensionTurnedOn(const char* const extension);
bool extensionsTurnedOn(int numExtensions, const char* const extensions[]); bool extensionsTurnedOn(int numExtensions, const char* const extensions[]);
@ -250,13 +253,14 @@ public:
protected: protected:
void nonInitConstCheck(const TSourceLoc&, TString& identifier, TType& type); void nonInitConstCheck(const TSourceLoc&, TString& identifier, TType& type);
void inheritGlobalDefaults(TQualifier& dst) const; void inheritGlobalDefaults(TQualifier& dst) const;
TVariable* makeInternalVariable(const char* name, const TType&) const; TVariable* makeInternalVariable(const char* name, const TType&) const;
TVariable* declareNonArray(const TSourceLoc&, TString& identifier, TType&, bool& newDeclaration); TVariable* declareNonArray(const TSourceLoc&, TString& identifier, TType&, bool& newDeclaration);
void declareArray(const TSourceLoc&, TString& identifier, const TType&, TSymbol*&, bool& newDeclaration); void declareArray(const TSourceLoc&, TString& identifier, const TType&, TSymbol*&, bool& newDeclaration);
TIntermNode* executeInitializer(const TSourceLoc&, TIntermTyped* initializer, TVariable* variable); TIntermNode* executeInitializer(const TSourceLoc&, TIntermTyped* initializer, TVariable* variable);
TIntermTyped* convertInitializerList(const TSourceLoc&, const TType&, TIntermTyped* initializer); TIntermTyped* convertInitializerList(const TSourceLoc&, const TType&, TIntermTyped* initializer);
TOperator mapTypeToConstructorOp(const TType&) const; TOperator mapTypeToConstructorOp(const TType&) const;
bool checkExtensionsRequested(const TSourceLoc&, int numExtensions, const char* const extensions[], const char* featureDesc);
void updateExtensionBehavior(const char* const extension, TExtensionBehavior); void updateExtensionBehavior(const char* const extension, TExtensionBehavior);
void finalErrorCheck(); void finalErrorCheck();
void outputMessage(const TSourceLoc&, const char* szReason, const char* szToken, void outputMessage(const TSourceLoc&, const char* szReason, const char* szToken,

View File

@ -154,6 +154,9 @@ public:
loc[getLastValidSourceIndex()].name = nullptr; loc[getLastValidSourceIndex()].name = nullptr;
} }
// for #include content indentation
void setColumn(int col) { loc[getLastValidSourceIndex()].column = col; }
const TSourceLoc& getSourceLoc() const { return loc[std::max(0, std::min(currentSource, numSources - finale - 1))]; } 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. // Returns the index (starting from 0) of the most recent valid source string we are reading from.
int getLastValidSourceIndex() const { return std::min(currentSource, numSources - 1); } int getLastValidSourceIndex() const { return std::min(currentSource, numSources - 1); }

View File

@ -130,7 +130,7 @@ bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profil
TIntermediate intermediate(language, version, profile); TIntermediate intermediate(language, version, profile);
TParseContext parseContext(symbolTable, intermediate, true, version, profile, language, infoSink); TParseContext parseContext(symbolTable, intermediate, true, version, profile, language, infoSink);
TPpContext ppContext(parseContext); TPpContext ppContext(parseContext, TShader::ForbidInclude());
TScanContext scanContext(parseContext); TScanContext scanContext(parseContext);
parseContext.setScanContext(&scanContext); parseContext.setScanContext(&scanContext);
parseContext.setPpContext(&ppContext); parseContext.setPpContext(&ppContext);
@ -463,7 +463,8 @@ bool ProcessDeferred(
EShMessages messages, // warnings/errors/AST; things to print out EShMessages messages, // warnings/errors/AST; things to print out
TIntermediate& intermediate, // returned tree, etc. TIntermediate& intermediate, // returned tree, etc.
ProcessingContext& processingContext, ProcessingContext& processingContext,
bool requireNonempty bool requireNonempty,
const TShader::Includer& includer
) )
{ {
if (! InitThread()) if (! InitThread())
@ -565,7 +566,7 @@ bool ProcessDeferred(
TParseContext parseContext(symbolTable, intermediate, false, version, profile, compiler->getLanguage(), compiler->infoSink, forwardCompatible, messages); TParseContext parseContext(symbolTable, intermediate, false, version, profile, compiler->getLanguage(), compiler->infoSink, forwardCompatible, messages);
glslang::TScanContext scanContext(parseContext); glslang::TScanContext scanContext(parseContext);
TPpContext ppContext(parseContext); TPpContext ppContext(parseContext, includer);
parseContext.setScanContext(&scanContext); parseContext.setScanContext(&scanContext);
parseContext.setPpContext(&ppContext); parseContext.setPpContext(&ppContext);
parseContext.setLimits(*resources); parseContext.setLimits(*resources);
@ -838,6 +839,7 @@ bool PreprocessDeferred(
bool forceDefaultVersionAndProfile, bool forceDefaultVersionAndProfile,
bool forwardCompatible, // give errors for use of deprecated features bool forwardCompatible, // give errors for use of deprecated features
EShMessages messages, // warnings/errors/AST; things to print out EShMessages messages, // warnings/errors/AST; things to print out
const TShader::Includer& includer,
TIntermediate& intermediate, // returned tree, etc. TIntermediate& intermediate, // returned tree, etc.
std::string* outputString) std::string* outputString)
{ {
@ -845,7 +847,8 @@ bool PreprocessDeferred(
return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames, return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames,
preamble, optLevel, resources, defaultVersion, preamble, optLevel, resources, defaultVersion,
defaultProfile, forceDefaultVersionAndProfile, defaultProfile, forceDefaultVersionAndProfile,
forwardCompatible, messages, intermediate, parser, false); forwardCompatible, messages, intermediate, parser,
false, includer);
} }
@ -874,13 +877,15 @@ bool CompileDeferred(
bool forceDefaultVersionAndProfile, bool forceDefaultVersionAndProfile,
bool forwardCompatible, // give errors for use of deprecated features bool forwardCompatible, // give errors for use of deprecated features
EShMessages messages, // warnings/errors/AST; things to print out EShMessages messages, // warnings/errors/AST; things to print out
TIntermediate& intermediate) // returned tree, etc. TIntermediate& intermediate,// returned tree, etc.
const TShader::Includer& includer)
{ {
DoFullParse parser; DoFullParse parser;
return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames, return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames,
preamble, optLevel, resources, defaultVersion, preamble, optLevel, resources, defaultVersion,
defaultProfile, forceDefaultVersionAndProfile, defaultProfile, forceDefaultVersionAndProfile,
forwardCompatible, messages, intermediate, parser, true); forwardCompatible, messages, intermediate, parser,
true, includer);
} }
} // end anonymous namespace for local functions } // end anonymous namespace for local functions
@ -1024,7 +1029,7 @@ int ShCompile(
TIntermediate intermediate(compiler->getLanguage()); TIntermediate intermediate(compiler->getLanguage());
bool success = CompileDeferred(compiler, shaderStrings, numStrings, inputLengths, nullptr, bool success = CompileDeferred(compiler, shaderStrings, numStrings, inputLengths, nullptr,
"", optLevel, resources, defaultVersion, ENoProfile, false, "", optLevel, resources, defaultVersion, ENoProfile, false,
forwardCompatible, messages, intermediate); forwardCompatible, messages, intermediate, TShader::ForbidInclude());
// //
// Call the machine dependent compiler // Call the machine dependent compiler
@ -1327,7 +1332,7 @@ void TShader::setStringsWithLengthsAndNames(
// Returns true for success. // Returns true for success.
// //
bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile,
bool forwardCompatible, EShMessages messages) bool forwardCompatible, EShMessages messages, const Includer& includer)
{ {
if (! InitThread()) if (! InitThread())
return false; return false;
@ -1340,7 +1345,7 @@ bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion
return CompileDeferred(compiler, strings, numStrings, lengths, stringNames, return CompileDeferred(compiler, strings, numStrings, lengths, stringNames,
preamble, EShOptNone, builtInResources, defaultVersion, preamble, EShOptNone, builtInResources, defaultVersion,
defaultProfile, forceDefaultVersionAndProfile, defaultProfile, forceDefaultVersionAndProfile,
forwardCompatible, messages, *intermediate); forwardCompatible, messages, *intermediate, includer);
} }
bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, bool forwardCompatible, EShMessages messages) bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, bool forwardCompatible, EShMessages messages)
@ -1351,9 +1356,11 @@ bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion
// Fill in a string with the result of preprocessing ShaderStrings // Fill in a string with the result of preprocessing ShaderStrings
// Returns true if all extensions, pragmas and version strings were valid. // Returns true if all extensions, pragmas and version strings were valid.
bool TShader::preprocess(const TBuiltInResource* builtInResources, bool TShader::preprocess(const TBuiltInResource* builtInResources,
int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, int defaultVersion, EProfile defaultProfile,
bool forwardCompatible, bool forceDefaultVersionAndProfile,
EShMessages message, std::string* output_string) bool forwardCompatible, EShMessages message,
std::string* output_string,
const TShader::Includer& includer)
{ {
if (! InitThread()) if (! InitThread())
return false; return false;
@ -1366,7 +1373,7 @@ bool TShader::preprocess(const TBuiltInResource* builtInResources,
return PreprocessDeferred(compiler, strings, numStrings, lengths, stringNames, preamble, return PreprocessDeferred(compiler, strings, numStrings, lengths, stringNames, preamble,
EShOptNone, builtInResources, defaultVersion, EShOptNone, builtInResources, defaultVersion,
defaultProfile, forceDefaultVersionAndProfile, defaultProfile, forceDefaultVersionAndProfile,
forwardCompatible, message, *intermediate, output_string); forwardCompatible, message, includer, *intermediate, output_string);
} }
const char* TShader::getInfoLog() const char* TShader::getInfoLog()

View File

@ -174,7 +174,9 @@ void TParseContext::initializeExtensionBehavior()
extensionBehavior[E_GL_ARB_viewport_array] = EBhDisable; extensionBehavior[E_GL_ARB_viewport_array] = EBhDisable;
// extensionBehavior[E_GL_ARB_cull_distance] = EBhDisable; // present for 4.5, but need extension control over block members // extensionBehavior[E_GL_ARB_cull_distance] = EBhDisable; // present for 4.5, but need extension control over block members
// #line and #include
extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhDisable; extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhDisable;
extensionBehavior[E_GL_GOOGLE_include_directive] = EBhDisable;
// AEP // AEP
extensionBehavior[E_GL_ANDROID_extension_pack_es31a] = EBhDisablePartial; extensionBehavior[E_GL_ANDROID_extension_pack_es31a] = EBhDisablePartial;
@ -219,7 +221,9 @@ const char* TParseContext::getPreamble()
"#define GL_OES_EGL_image_external 1\n" "#define GL_OES_EGL_image_external 1\n"
"#define GL_EXT_shader_texture_lod 1\n" "#define GL_EXT_shader_texture_lod 1\n"
// #line and #include
"#define GL_GOOGLE_cpp_style_line_directive 1\n" "#define GL_GOOGLE_cpp_style_line_directive 1\n"
"#define GL_GOOGLE_include_directive 1\n"
// AEP // AEP
"#define GL_ANDROID_extension_pack_es31a 1\n" "#define GL_ANDROID_extension_pack_es31a 1\n"
@ -270,6 +274,7 @@ const char* TParseContext::getPreamble()
"#define GL_ARB_viewport_array 1\n" "#define GL_ARB_viewport_array 1\n"
"#define GL_GOOGLE_cpp_style_line_directive 1\n" "#define GL_GOOGLE_cpp_style_line_directive 1\n"
"#define GL_GOOGLE_include_directive 1\n"
// "#define GL_ARB_cull_distance 1\n" // present for 4.5, but need extension control over block members // "#define GL_ARB_cull_distance 1\n" // present for 4.5, but need extension control over block members
; ;
} }
@ -406,17 +411,15 @@ void TParseContext::requireNotRemoved(const TSourceLoc& loc, int profileMask, in
} }
} }
// // Returns true if at least one of the extensions in the extensions parameter is requested. Otherwise, returns false.
// Use when there are no profile/version to check, it's just an error if one of the // Warns appropriately if the requested behavior of an extension is "warn".
// extensions is not present. bool TParseContext::checkExtensionsRequested(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc)
//
void TParseContext::requireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc)
{ {
// First, see if any of the extensions are enabled // First, see if any of the extensions are enabled
for (int i = 0; i < numExtensions; ++i) { for (int i = 0; i < numExtensions; ++i) {
TExtensionBehavior behavior = getExtensionBehavior(extensions[i]); TExtensionBehavior behavior = getExtensionBehavior(extensions[i]);
if (behavior == EBhEnable || behavior == EBhRequire) if (behavior == EBhEnable || behavior == EBhRequire)
return; return true;
} }
// See if any extensions want to give a warning on use; give warnings for all such extensions // See if any extensions want to give a warning on use; give warnings for all such extensions
@ -433,7 +436,17 @@ void TParseContext::requireExtensions(const TSourceLoc& loc, int numExtensions,
} }
} }
if (warned) if (warned)
return; return true;
return false;
}
//
// Use when there are no profile/version to check, it's just an error if one of the
// extensions is not present.
//
void TParseContext::requireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc)
{
if (checkExtensionsRequested(loc, numExtensions, extensions, featureDesc)) return;
// If we get this far, give errors explaining what extensions are needed // If we get this far, give errors explaining what extensions are needed
if (numExtensions == 1) if (numExtensions == 1)
@ -445,6 +458,24 @@ void TParseContext::requireExtensions(const TSourceLoc& loc, int numExtensions,
} }
} }
//
// Use by preprocessor when there are no profile/version to check, it's just an error if one of the
// extensions is not present.
//
void TParseContext::ppRequireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc)
{
if (checkExtensionsRequested(loc, numExtensions, extensions, featureDesc)) return;
// If we get this far, give errors explaining what extensions are needed
if (numExtensions == 1)
ppError(loc, "required extension not requested:", featureDesc, extensions[0]);
else {
ppError(loc, "required extension not requested:", featureDesc, "Possible extensions include:");
for (int i = 0; i < numExtensions; ++i)
infoSink.info.message(EPrefixNone, extensions[i]);
}
}
TExtensionBehavior TParseContext::getExtensionBehavior(const char* extension) TExtensionBehavior TParseContext::getExtensionBehavior(const char* extension)
{ {
auto iter = extensionBehavior.find(TString(extension)); auto iter = extensionBehavior.find(TString(extension));
@ -525,6 +556,8 @@ void TParseContext::updateExtensionBehavior(int line, const char* extension, con
updateExtensionBehavior(line, "GL_EXT_shader_io_blocks", behaviorString); updateExtensionBehavior(line, "GL_EXT_shader_io_blocks", behaviorString);
else if (strcmp(extension, "GL_OES_tessellation_shader") == 0) else if (strcmp(extension, "GL_OES_tessellation_shader") == 0)
updateExtensionBehavior(line, "GL_OES_shader_io_blocks", behaviorString); updateExtensionBehavior(line, "GL_OES_shader_io_blocks", behaviorString);
else if (strcmp(extension, "GL_GOOGLE_include_directive") == 0)
updateExtensionBehavior(line, "GL_GOOGLE_cpp_style_line_directive", behaviorString);
} }
void TParseContext::updateExtensionBehavior(const char* extension, TExtensionBehavior behavior) void TParseContext::updateExtensionBehavior(const char* extension, TExtensionBehavior behavior)

View File

@ -112,7 +112,9 @@ const char* const E_GL_ARB_shader_texture_image_samples = "GL_ARB_shader_texture
const char* const E_GL_ARB_viewport_array = "GL_ARB_viewport_array"; const char* const E_GL_ARB_viewport_array = "GL_ARB_viewport_array";
//const char* const E_GL_ARB_cull_distance = "GL_ARB_cull_distance"; // present for 4.5, but need extension control over block members //const char* const E_GL_ARB_cull_distance = "GL_ARB_cull_distance"; // present for 4.5, but need extension control over block members
// #line and #include
const char* const E_GL_GOOGLE_cpp_style_line_directive = "GL_GOOGLE_cpp_style_line_directive"; const char* const E_GL_GOOGLE_cpp_style_line_directive = "GL_GOOGLE_cpp_style_line_directive";
const char* const E_GL_GOOGLE_include_directive = "GL_GOOGLE_include_directive";
// AEP // AEP
const char* const E_GL_ANDROID_extension_pack_es31a = "GL_ANDROID_extension_pack_es31a"; const char* const E_GL_ANDROID_extension_pack_es31a = "GL_ANDROID_extension_pack_es31a";

View File

@ -83,6 +83,7 @@ NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <sstream>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
@ -596,6 +597,51 @@ int TPpContext::CPPifdef(int defined, TPpToken* ppToken)
return token; return token;
} }
// Handle #include
int TPpContext::CPPinclude(TPpToken* ppToken)
{
const TSourceLoc directiveLoc = ppToken->loc;
int token = scanToken(ppToken);
if (token != PpAtomConstString) {
// TODO: handle angle brackets.
parseContext.ppError(directiveLoc, "must be followed by a file designation", "#include", "");
} else {
// Make a copy of the name because it will be overwritten by the next token scan.
const std::string filename = ppToken->name;
token = scanToken(ppToken);
if (token != '\n' && token != EndOfInput) {
parseContext.ppError(ppToken->loc, "extra content after file designation", "#include", "");
} else {
std::string sourceName;
std::string replacement;
std::tie(sourceName, replacement) = includer.include(filename.c_str());
if (!sourceName.empty()) {
if (!replacement.empty()) {
const bool forNextLine = parseContext.lineDirectiveShouldSetNextLine();
std::ostringstream content;
content << "#line " << forNextLine << " " << "\"" << sourceName << "\"\n";
content << replacement << (replacement.back() == '\n' ? "" : "\n");
content << "#line " << directiveLoc.line + forNextLine << " ";
if (directiveLoc.name != nullptr) {
content << "\"" << directiveLoc.name << "\"";
} else {
content << directiveLoc.string;
}
content << "\n";
pushInput(new TokenizableString(directiveLoc, content.str(), this));
}
// At EOF, there's no "current" location anymore.
if (token != EndOfInput) parseContext.setCurrentColumn(0);
// Don't accidentally return EndOfInput, which will end all preprocessing.
return '\n';
} else {
parseContext.ppError(directiveLoc, replacement.c_str(), "#include", "");
}
}
}
return token;
}
// Handle #line // Handle #line
int TPpContext::CPPline(TPpToken* ppToken) int TPpContext::CPPline(TPpToken* ppToken)
{ {
@ -629,7 +675,7 @@ int TPpContext::CPPline(TPpToken* ppToken)
if (token != '\n') { if (token != '\n') {
if (token == PpAtomConstString) { if (token == PpAtomConstString) {
parseContext.requireExtensions(directiveLoc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based #line"); parseContext.ppRequireExtensions(directiveLoc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based #line");
// We need to save a copy of the string instead of pointing // We need to save a copy of the string instead of pointing
// to the name field of the token since the name field // to the name field of the token since the name field
// will likely be overwritten by the next token scan. // will likely be overwritten by the next token scan.
@ -845,6 +891,10 @@ int TPpContext::readCPPline(TPpToken* ppToken)
case PpAtomIfndef: case PpAtomIfndef:
token = CPPifdef(0, ppToken); token = CPPifdef(0, ppToken);
break; break;
case PpAtomInclude:
parseContext.ppRequireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_include_directive, "#include");
token = CPPinclude(ppToken);
break;
case PpAtomLine: case PpAtomLine:
token = CPPline(ppToken); token = CPPline(ppToken);
break; break;
@ -967,7 +1017,7 @@ int TPpContext::MacroExpand(int atom, TPpToken* ppToken, bool expandUndef, bool
case PpAtomFileMacro: { case PpAtomFileMacro: {
if (const char* current_file = parseContext.getCurrentLoc().name) { if (const char* current_file = parseContext.getCurrentLoc().name) {
parseContext.requireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based __FILE__"); parseContext.ppRequireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based __FILE__");
sprintf(ppToken->name, "\"%s\"", current_file); sprintf(ppToken->name, "\"%s\"", current_file);
} else { } else {
ppToken->ival = parseContext.getCurrentLoc().string; ppToken->ival = parseContext.getCurrentLoc().string;

View File

@ -120,6 +120,9 @@ const struct {
{ PpAtomLineMacro, "__LINE__" }, { PpAtomLineMacro, "__LINE__" },
{ PpAtomFileMacro, "__FILE__" }, { PpAtomFileMacro, "__FILE__" },
{ PpAtomVersionMacro, "__VERSION__" }, { PpAtomVersionMacro, "__VERSION__" },
{ PpAtomInclude, "include" },
}; };
} // end anonymous namespace } // end anonymous namespace

View File

@ -83,8 +83,8 @@ NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace glslang { namespace glslang {
TPpContext::TPpContext(TParseContext& pc) : TPpContext::TPpContext(TParseContext& pc, const TShader::Includer& inclr) :
preamble(0), strings(0), parseContext(pc), inComment(false) preamble(0), strings(0), parseContext(pc), includer(inclr), inComment(false)
{ {
InitAtomTable(); InitAtomTable();
InitScanner(); InitScanner();

View File

@ -118,7 +118,7 @@ class TInputScanner;
// Don't expect too much in terms of OO design. // Don't expect too much in terms of OO design.
class TPpContext { class TPpContext {
public: public:
TPpContext(TParseContext&); TPpContext(TParseContext&, const TShader::Includer&);
virtual ~TPpContext(); virtual ~TPpContext();
void setPreamble(const char* preamble, size_t length); void setPreamble(const char* preamble, size_t length);
@ -134,6 +134,10 @@ public:
virtual int getch() = 0; virtual int getch() = 0;
virtual void ungetch() = 0; virtual void ungetch() = 0;
// Will be called when we start reading tokens from this instance
virtual void notifyActivated() {}
// Will be called when we do not read tokens from this instance anymore
virtual void notifyDeleted() {}
protected: protected:
bool done; bool done;
TPpContext* pp; TPpContext* pp;
@ -144,9 +148,11 @@ public:
void pushInput(tInput* in) void pushInput(tInput* in)
{ {
inputStack.push_back(in); inputStack.push_back(in);
in->notifyActivated();
} }
void popInput() void popInput()
{ {
inputStack.back()->notifyDeleted();
delete inputStack.back(); delete inputStack.back();
inputStack.pop_back(); inputStack.pop_back();
} }
@ -281,6 +287,8 @@ protected:
// from Pp.cpp // from Pp.cpp
// //
TSourceLoc ifloc; /* outermost #if */ TSourceLoc ifloc; /* outermost #if */
// Used to obtain #include content.
const TShader::Includer& includer;
int InitCPP(); int InitCPP();
int CPPdefine(TPpToken * ppToken); int CPPdefine(TPpToken * ppToken);
@ -291,6 +299,7 @@ protected:
int evalToToken(int token, 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 CPPinclude(TPpToken * ppToken);
int CPPline(TPpToken * ppToken); int CPPline(TPpToken * ppToken);
int CPPerror(TPpToken * ppToken); int CPPerror(TPpToken * ppToken);
int CPPpragma(TPpToken * ppToken); int CPPpragma(TPpToken * ppToken);
@ -419,6 +428,51 @@ protected:
TInputScanner* input; TInputScanner* input;
}; };
// Holds a string that can be tokenized via the tInput interface.
class TokenizableString : public tInput {
public:
// Copies str, which must be non-empty.
TokenizableString(const TSourceLoc& startLoc, const std::string& str, TPpContext* pp)
: tInput(pp),
str_(str),
strings(str_.data()),
length(str_.size()),
scanner(1, &strings, &length),
prevScanner(nullptr),
stringInput(pp, scanner) {
scanner.setLine(startLoc.line);
scanner.setString(startLoc.string);
scanner.setFile(startLoc.name);
}
// tInput methods:
int scan(TPpToken* t) override { return stringInput.scan(t); }
int getch() override { return stringInput.getch(); }
void ungetch() override { stringInput.ungetch(); }
void notifyActivated() override
{
prevScanner = pp->parseContext.getScanner();
pp->parseContext.setScanner(&scanner);
}
void notifyDeleted() override { pp->parseContext.setScanner(prevScanner); }
private:
// Stores the titular string.
const std::string str_;
// Will point to str_[0] and be passed to scanner constructor.
const char* const strings;
// Length of str_, passed to scanner constructor.
size_t length;
// Scans over str_.
TInputScanner scanner;
// The previous effective scanner before the scanner in this instance
// has been activated.
TInputScanner* prevScanner;
// Delegate object implementing the tInput interface.
tStringInput stringInput;
};
int InitScanner(); int InitScanner();
int ScanFromString(char* s); int ScanFromString(char* s);
void missingEndifCheck(); void missingEndifCheck();

View File

@ -158,6 +158,9 @@ enum EFixedAtoms {
PpAtomFileMacro, PpAtomFileMacro,
PpAtomVersionMacro, PpAtomVersionMacro,
// #include
PpAtomInclude,
PpAtomLast, PpAtomLast,
}; };

View File

@ -249,6 +249,7 @@ SH_IMPORT_EXPORT int ShGetUniformLocation(const ShHandle uniformMap, const char*
#include <list> #include <list>
#include <string> #include <string>
#include <utility>
class TCompiler; class TCompiler;
class TInfoSink; class TInfoSink;
@ -288,14 +289,35 @@ public:
void setStringsWithLengthsAndNames( void setStringsWithLengthsAndNames(
const char* const* s, const int* l, const char* const* names, int n); const char* const* s, const int* l, const char* const* names, int n);
void setPreamble(const char* s) { preamble = s; } void setPreamble(const char* s) { preamble = s; }
bool parse(const TBuiltInResource*, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, bool forwardCompatible, EShMessages);
// Interface to #include handlers.
class Includer {
public:
// On success, returns the full path and content of the file with the given
// filename that replaces "#include filename". On failure, returns an empty
// string and an error message.
virtual std::pair<std::string, std::string> include(const char* filename) const = 0;
};
// Returns an error message for any #include directive.
class ForbidInclude : public Includer {
public:
std::pair<std::string, std::string> include(const char* filename) const override
{
return std::make_pair<std::string, std::string>("", "unexpected include directive");
}
};
bool parse(const TBuiltInResource*, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile,
bool forwardCompatible, EShMessages, const Includer& = ForbidInclude());
// Equivalent to parse() without a default profile and without forcing defaults. // Equivalent to parse() without a default profile and without forcing defaults.
// Provided for backwards compatibility. // Provided for backwards compatibility.
bool parse(const TBuiltInResource*, int defaultVersion, bool forwardCompatible, EShMessages); bool parse(const TBuiltInResource*, int defaultVersion, bool forwardCompatible, EShMessages);
bool preprocess(const TBuiltInResource* builtInResources, bool preprocess(const TBuiltInResource* builtInResources,
int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile,
bool forwardCompatible, bool forwardCompatible, EShMessages message, std::string* outputString,
EShMessages message, std::string* outputString); const TShader::Includer& includer);
const char* getInfoLog(); const char* getInfoLog();
const char* getInfoDebugLog(); const char* getInfoDebugLog();