From affc26674dd8f90a608cb0f0d96ce369a36dedb3 Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Mon, 2 Jan 2017 20:12:08 -0700 Subject: [PATCH] PP: Recognize <> style #include header names. I.e., #include . Also correctly test and handle missing newline. --- .../preprocessor.include.disabled.vert.err | 8 +- .../preprocessor.include.enabled.vert.err | 15 ++- Test/preprocessor.include.disabled.vert | 3 +- Test/preprocessor.include.enabled.vert | 8 +- glslang/Include/revision.h | 4 +- .../MachineIndependent/preprocessor/Pp.cpp | 107 +++++++++++++----- .../preprocessor/PpContext.h | 1 + .../preprocessor/PpScanner.cpp | 3 + 8 files changed, 105 insertions(+), 44 deletions(-) diff --git a/Test/baseResults/preprocessor.include.disabled.vert.err b/Test/baseResults/preprocessor.include.disabled.vert.err index 7d1f0a005..da1704796 100644 --- a/Test/baseResults/preprocessor.include.disabled.vert.err +++ b/Test/baseResults/preprocessor.include.disabled.vert.err @@ -1,13 +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:8000: '#include' : must be followed by a header name 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:8001: '#include' : must be followed by a header name 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:8003: '#include' : extra content after header name ERROR: 0:8004: '#include' : required extension not requested: GL_GOOGLE_include_directive -ERROR: 0:8004: '#include' : unexpected include directive +ERROR: 0:8004: '#include' : expected newline ERROR: 10 compilation errors. No code generated. diff --git a/Test/baseResults/preprocessor.include.enabled.vert.err b/Test/baseResults/preprocessor.include.enabled.vert.err index be8c5cf3a..b19b835cf 100644 --- a/Test/baseResults/preprocessor.include.enabled.vert.err +++ b/Test/baseResults/preprocessor.include.enabled.vert.err @@ -1,8 +1,13 @@ -ERROR: 0:8000: '#include' : must be followed by a file designation -ERROR: 0:8001: '#include' : must be followed by a file designation +ERROR: 0:8000: '#include' : must be followed by a header name +ERROR: 0:8001: '#include' : must be followed by a header name 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. +ERROR: 0:8003: '#include' : unexpected include directive +ERROR: 0:8004: '#include' : extra content after header name +ERROR: 0:8005: '#include' : extra content after header name +ERROR: 0:8007: '#include' : unexpected include directive +ERROR: 0:8009: '' : header name too long +ERROR: 0:8009: '#include' : unexpected include directive +ERROR: 0:8010: '#include' : expected newline +ERROR: 10 compilation errors. No code generated. diff --git a/Test/preprocessor.include.disabled.vert b/Test/preprocessor.include.disabled.vert index 130d928e5..865baa189 100644 --- a/Test/preprocessor.include.disabled.vert +++ b/Test/preprocessor.include.disabled.vert @@ -3,5 +3,4 @@ #include 123 #include "foo" #include "foo" garbage -#include "no-eol" - +#include "no-eol" \ No newline at end of file diff --git a/Test/preprocessor.include.enabled.vert b/Test/preprocessor.include.enabled.vert index ecdf466e1..6d64b2301 100644 --- a/Test/preprocessor.include.enabled.vert +++ b/Test/preprocessor.include.enabled.vert @@ -3,5 +3,11 @@ #include #include 123 #include "foo" +#include #include "foo" garbage -#include "no-eol" +#include garbage +// max length +#include +// too long +#include +#include "no-eol" \ No newline at end of file diff --git a/glslang/Include/revision.h b/glslang/Include/revision.h index cccc79677..cee8a8b79 100644 --- a/glslang/Include/revision.h +++ b/glslang/Include/revision.h @@ -2,5 +2,5 @@ // For the version, it uses the latest git tag followed by the number of commits. // For the date, it uses the current date (when then script is run). -#define GLSLANG_REVISION "Overload400-PrecQual.1728" -#define GLSLANG_DATE "02-Jan-2017" +#define GLSLANG_REVISION "Overload400-PrecQual.1730" +#define GLSLANG_DATE "03-Jan-2017" diff --git a/glslang/MachineIndependent/preprocessor/Pp.cpp b/glslang/MachineIndependent/preprocessor/Pp.cpp index eb2f3dfaf..02e452112 100644 --- a/glslang/MachineIndependent/preprocessor/Pp.cpp +++ b/glslang/MachineIndependent/preprocessor/Pp.cpp @@ -571,46 +571,61 @@ int TPpContext::CPPifdef(int defined, TPpToken* ppToken) return token; } -// Handle #include +// Handle #include ... +// TODO: Handle macro expansions for the header name int TPpContext::CPPinclude(TPpToken* ppToken) { const TSourceLoc directiveLoc = ppToken->loc; + TShader::Includer::IncludeType includeType = TShader::Includer::EIncludeRelative; int token = scanToken(ppToken); + + // handle -style #include + if (token == '<') { + includeType = TShader::Includer::EIncludeStandard; + token = scanHeaderName(ppToken, '>'); + } + // otherwise ppToken already has the header name and it was "header-name" style + if (token != PpAtomConstString) { - // TODO: handle angle brackets. - parseContext.ppError(directiveLoc, "must be followed by a file designation", "#include", ""); + parseContext.ppError(directiveLoc, "must be followed by a header name", "#include", ""); + return token; + } + + // 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') { + if (token == EndOfInput) + parseContext.ppError(ppToken->loc, "expected newline", "#include", ""); + else + parseContext.ppError(ppToken->loc, "extra content after header name", "#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", ""); + TShader::Includer::IncludeResult* res = includer.include(filename.c_str(), includeType, currentSourceFile.c_str(), includeStack.size() + 1); + if (res && !res->file_name.empty()) { + if (res->file_data && res->file_length) { + const bool forNextLine = parseContext.lineDirectiveShouldSetNextLine(); + std::ostringstream prologue; + std::ostringstream epilogue; + prologue << "#line " << forNextLine << " " << "\"" << res->file_name << "\"\n"; + epilogue << (res->file_data[res->file_length - 1] == '\n'? "" : "\n") << "#line " << directiveLoc.line + forNextLine << " " << directiveLoc.getStringNameOrNum() << "\n"; + pushInput(new TokenizableIncludeFile(directiveLoc, prologue.str(), res, epilogue.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 { - TShader::Includer::IncludeResult* res = includer.include(filename.c_str(), TShader::Includer::EIncludeRelative, currentSourceFile.c_str(), includeStack.size() + 1); - if (res && !res->file_name.empty()) { - if (res->file_data && res->file_length) { - const bool forNextLine = parseContext.lineDirectiveShouldSetNextLine(); - std::ostringstream prologue; - std::ostringstream epilogue; - prologue << "#line " << forNextLine << " " << "\"" << res->file_name << "\"\n"; - epilogue << (res->file_data[res->file_length - 1] == '\n'? "" : "\n") << "#line " << directiveLoc.line + forNextLine << " " << directiveLoc.getStringNameOrNum() << "\n"; - pushInput(new TokenizableIncludeFile(directiveLoc, prologue.str(), res, epilogue.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 { - std::string message = - res ? std::string(res->file_data, res->file_length) - : std::string("Could not process include directive"); - parseContext.ppError(directiveLoc, message.c_str(), "#include", ""); - if (res) { - includer.releaseInclude(res); - } + std::string message = + res ? std::string(res->file_data, res->file_length) + : std::string("Could not process include directive"); + parseContext.ppError(directiveLoc, message.c_str(), "#include", ""); + if (res) { + includer.releaseInclude(res); } } } + return token; } @@ -911,6 +926,38 @@ int TPpContext::readCPPline(TPpToken* ppToken) return token; } +// Context-dependent parsing of a #include . +// Assumes no macro expansions etc. are being done; the name is just on the current input. +// Always creates a name and returns PpAtomicConstString, unless we run out of input. +int TPpContext::scanHeaderName(TPpToken* ppToken, char delimit) +{ + bool tooLong = false; + + if (inputStack.empty()) + return EndOfInput; + + int len = 0; + ppToken->name[0] = '\0'; + do { + int ch = inputStack.back()->getch(); + + // done yet? + if (ch == delimit) { + ppToken->name[len] = '\0'; + if (tooLong) + parseContext.ppError(ppToken->loc, "header name too long", "", ""); + return PpAtomConstString; + } else if (ch == EndOfInput) + return EndOfInput; + + // found a character to expand the name with + if (len < MaxTokenLength) + ppToken->name[len++] = ch; + else + tooLong = true; + } while (true); +} + // Macro-expand a macro argument 'arg' to create 'expandedArg'. // Does not replace 'arg'. // Returns nullptr if no expanded argument is created. diff --git a/glslang/MachineIndependent/preprocessor/PpContext.h b/glslang/MachineIndependent/preprocessor/PpContext.h index 64f99efee..84c1bef1f 100644 --- a/glslang/MachineIndependent/preprocessor/PpContext.h +++ b/glslang/MachineIndependent/preprocessor/PpContext.h @@ -368,6 +368,7 @@ protected: int CPPversion(TPpToken * ppToken); int CPPextension(TPpToken * ppToken); int readCPPline(TPpToken * ppToken); + int scanHeaderName(TPpToken* ppToken, char delimit); TokenStream* PrescanMacroArg(TokenStream&, TPpToken*, bool newLineOkay); int MacroExpand(TPpToken* ppToken, bool expandUndef, bool newLineOkay); diff --git a/glslang/MachineIndependent/preprocessor/PpScanner.cpp b/glslang/MachineIndependent/preprocessor/PpScanner.cpp index 4bb7c932b..426e44ed8 100644 --- a/glslang/MachineIndependent/preprocessor/PpScanner.cpp +++ b/glslang/MachineIndependent/preprocessor/PpScanner.cpp @@ -688,6 +688,9 @@ int TPpContext::tStringInput::scan(TPpToken* ppToken) } break; case '"': + // TODO: If this gets enhanced to handle escape sequences, or + // anything that is different than what #include needs, then + // #include needs to use scanHeaderName() for this. ch = getch(); while (ch != '"' && ch != '\n' && ch != EndOfInput) { if (len < MaxTokenLength) {