Changeset View
Changeset View
Standalone View
Standalone View
source/graphics/PreprocessorWrapper.cpp
Show All 16 Lines | |||||
#include "precompiled.h" | #include "precompiled.h" | ||||
#include "PreprocessorWrapper.h" | #include "PreprocessorWrapper.h" | ||||
#include "graphics/ShaderDefines.h" | #include "graphics/ShaderDefines.h" | ||||
#include "ps/CLogger.h" | #include "ps/CLogger.h" | ||||
#include <cctype> | |||||
namespace | |||||
{ | |||||
struct MatchIncludeResult | |||||
{ | |||||
bool found; | |||||
bool error; | |||||
size_t nextLineStart; | |||||
size_t pathFirst, pathLast; | |||||
static MatchIncludeResult MakeNotFound(const CStr& source, size_t pos) | |||||
{ | |||||
while (pos < source.size() && source[pos] != '\n') | |||||
Stan: std::find maybe ? or indexOf -1 ? | |||||
vladislavbelovAuthorUnsubmitted Done Inline Actionsstd::endl isn't a character, it's I/O manipulator. std::find might work, there is no indexOf in CStr. vladislavbelov: `std::endl` isn't a character, it's I/O manipulator.
`std::find` might work, there is no… | |||||
++pos; | |||||
return MatchIncludeResult{ | |||||
false, false, pos < source.size() ? pos + 1 : source.size(), 0, 0}; | |||||
} | |||||
static MatchIncludeResult MakeError( | |||||
const char* message, const CStr& source, const size_t lineStart, const size_t currentPos) | |||||
{ | |||||
ENSURE(currentPos >= lineStart); | |||||
size_t lineEnd = currentPos; | |||||
while (lineEnd < source.size() && source[lineEnd] != '\n' && source[lineEnd] != '\r') | |||||
++lineEnd; | |||||
const CStr line = source.substr(lineStart, lineEnd - lineStart); | |||||
while (lineEnd < source.size() && source[lineEnd] != '\n') | |||||
++lineEnd; | |||||
const size_t nextLineStart = lineEnd < source.size() ? lineEnd + 1 : source.size(); | |||||
LOGERROR("Preprocessor error: %s: '%s'\n", message, line.c_str()); | |||||
return MatchIncludeResult{false, true, nextLineStart, 0, 0}; | |||||
} | |||||
}; | |||||
MatchIncludeResult MatchIncludeUntilEOLorEOS(const CStr& source, const size_t lineStart) | |||||
{ | |||||
// We need to match a line like this: | |||||
StanUnsubmitted Done Inline ActionsWhy not use ICU for Regex? https://github.com/0ad/0ad/blob/4b35ac33f7f30e6bb0a674a6996357a8a75cf1a3/libraries/win32/icu/include/unicode/regex.h Stan: Why not use ICU for Regex? https://github.com/0ad/0ad/blob/4b35ac33f7f30e6bb0a674a6996357a8a75c… | |||||
vladislavbelovAuthorUnsubmitted Done Inline ActionsI'm not sure about performance, but I might check. vladislavbelov: I'm not sure about performance, but I might check. | |||||
// ^[ \t]*#[ \t]*include[ \t]*"[^"]+".*$ | |||||
// ^ ^^ ^ ^ ^^ ^ | |||||
// 1 23 4 5 67 8 <- steps | |||||
const CStr INCLUDE = "include"; | |||||
size_t pos = lineStart; | |||||
// Matching step #1. | |||||
while (pos < source.size() && std::isblank(source[pos])) | |||||
++pos; | |||||
// Matching step #2. | |||||
if (pos == source.size() || source[pos] != '#') | |||||
return MatchIncludeResult::MakeNotFound(source, pos); | |||||
++pos; | |||||
// Matching step #3. | |||||
while (pos < source.size() && std::isblank(source[pos])) | |||||
++pos; | |||||
// Matching step #4. | |||||
if (pos + INCLUDE.size() >= source.size() || source.substr(pos, INCLUDE.size()) != INCLUDE) | |||||
return MatchIncludeResult::MakeNotFound(source, pos); | |||||
pos += INCLUDE.size(); | |||||
// Matching step #5. | |||||
while (pos < source.size() && std::isblank(source[pos])) | |||||
++pos; | |||||
// Matching step #6. | |||||
if (pos == source.size() || source[pos] != '"') | |||||
return MatchIncludeResult::MakeError("#include should be followed by quote", source, lineStart, pos); | |||||
++pos; | |||||
// Matching step #7. | |||||
const size_t pathFirst = pos; | |||||
while (pos < source.size() && source[pos] != '"' && source[pos] != '\n') | |||||
++pos; | |||||
const size_t pathLast = pos; | |||||
// Matching step #8. | |||||
if (pos == source.size() || source[pos] != '"') | |||||
return MatchIncludeResult::MakeError("#include has invalid quote pair", source, lineStart, pos); | |||||
if (pathLast - pathFirst <= 1) | |||||
return MatchIncludeResult::MakeError("#include path shouldn't be empty", source, lineStart, pos); | |||||
while (pos < source.size() && source[pos] != '\n') | |||||
++pos; | |||||
return MatchIncludeResult{true, false, pos < source.size() ? pos + 1 : source.size(), pathFirst, pathLast}; | |||||
} | |||||
} // anonymous namespace | |||||
void CPreprocessorWrapper::PyrogenesisShaderError(int iLine, const char* iError, const Ogre::CPreprocessor::Token* iToken) | void CPreprocessorWrapper::PyrogenesisShaderError(int iLine, const char* iError, const Ogre::CPreprocessor::Token* iToken) | ||||
{ | { | ||||
if (iToken) | if (iToken) | ||||
LOGERROR("Preprocessor error: line %d: %s: '%s'\n", iLine, iError, std::string(iToken->String, iToken->Length).c_str()); | LOGERROR("Preprocessor error: line %d: %s: '%s'\n", iLine, iError, std::string(iToken->String, iToken->Length).c_str()); | ||||
else | else | ||||
LOGERROR("Preprocessor error: line %d: %s\n", iLine, iError); | LOGERROR("Preprocessor error: line %d: %s\n", iLine, iError); | ||||
} | } | ||||
CPreprocessorWrapper::CPreprocessorWrapper() | CPreprocessorWrapper::CPreprocessorWrapper() | ||||
: CPreprocessorWrapper(IncludeRetrieverCallback{}) | |||||
{ | |||||
} | |||||
CPreprocessorWrapper::CPreprocessorWrapper(const IncludeRetrieverCallback& includeCallback) | |||||
: m_IncludeCallback(includeCallback) | |||||
{ | { | ||||
Ogre::CPreprocessor::ErrorHandler = CPreprocessorWrapper::PyrogenesisShaderError; | Ogre::CPreprocessor::ErrorHandler = CPreprocessorWrapper::PyrogenesisShaderError; | ||||
} | } | ||||
void CPreprocessorWrapper::AddDefine(const char* name, const char* value) | void CPreprocessorWrapper::AddDefine(const char* name, const char* value) | ||||
{ | { | ||||
m_Preprocessor.Define(name, strlen(name), value, strlen(value)); | m_Preprocessor.Define(name, strlen(name), value, strlen(value)); | ||||
} | } | ||||
Show All 31 Lines | bool CPreprocessorWrapper::TestConditional(const CStr& expr) | ||||
// Free output if it's not inside the source string | // Free output if it's not inside the source string | ||||
if (!(output >= input.c_str() && output < input.c_str() + input.size())) | if (!(output >= input.c_str() && output < input.c_str() + input.size())) | ||||
free(output); | free(output); | ||||
return ret; | return ret; | ||||
} | } | ||||
CStr CPreprocessorWrapper::ResolveIncludes(CStr source) | |||||
{ | |||||
const CStr lineDirective = "#line "; | |||||
for (size_t lineStart = 0, line = 1; lineStart < source.size(); ++line) | |||||
{ | |||||
MatchIncludeResult match = MatchIncludeUntilEOLorEOS(source, lineStart); | |||||
if (match.error) | |||||
return {}; | |||||
else if (!match.found) | |||||
{ | |||||
if (lineStart + lineDirective.size() < source.size() && | |||||
source.substr(lineStart, lineDirective.size()) == lineDirective) | |||||
{ | |||||
size_t newLineNumber = 0; | |||||
size_t pos = lineStart + lineDirective.size(); | |||||
while (pos < match.nextLineStart && std::isdigit(source[pos])) | |||||
{ | |||||
newLineNumber = newLineNumber * 10 + (source[pos] - '0'); | |||||
++pos; | |||||
} | |||||
if (newLineNumber > 0) | |||||
line = newLineNumber - 1; | |||||
} | |||||
lineStart = match.nextLineStart; | |||||
continue; | |||||
} | |||||
const CStr path = source.substr(match.pathFirst, match.pathLast - match.pathFirst); | |||||
auto it = m_IncludeCache.find(path); | |||||
if (it == m_IncludeCache.end()) | |||||
{ | |||||
CStr includeContent; | |||||
if (!m_IncludeCallback(path, includeContent)) | |||||
{ | |||||
LOGERROR("Preprocessor error: line %zu: Can't load #include file: '%s'", line, path.c_str()); | |||||
return {}; | |||||
} | |||||
it = m_IncludeCache.emplace(path, std::move(includeContent)).first; | |||||
} | |||||
// We need to insert #line directives to have correct line numbers in errors. | |||||
source = | |||||
source.substr(0, lineStart) + | |||||
lineDirective + "1\n" + it->second + "\n" + lineDirective + CStr::FromUInt(line + 1) + "\n" + | |||||
source.substr(match.nextLineStart); | |||||
--line; | |||||
} | |||||
return source; | |||||
} | |||||
CStr CPreprocessorWrapper::Preprocess(const CStr& input) | CStr CPreprocessorWrapper::Preprocess(const CStr& input) | ||||
{ | { | ||||
PROFILE("Preprocess shader source"); | |||||
CStr source = ResolveIncludes(input); | |||||
size_t len = 0; | size_t len = 0; | ||||
char* output = m_Preprocessor.Parse(input.c_str(), input.size(), len); | char* output = m_Preprocessor.Parse(source.c_str(), source.size(), len); | ||||
StanUnsubmitted Not Done Inline Actionsconst ? Stan: const ? | |||||
vladislavbelovAuthorUnsubmitted Done Inline ActionsIt returns char*, but might be const here in-place. vladislavbelov: It returns `char*`, but might be `const` here in-place. | |||||
if (!output) | if (!output) | ||||
{ | { | ||||
LOGERROR("Shader preprocessing failed"); | LOGERROR("Shader preprocessing failed"); | ||||
return ""; | return ""; | ||||
} | } | ||||
CStr ret(output, len); | CStr ret(output, len); | ||||
// Free output if it's not inside the source string | // Free output if it's not inside the source string | ||||
if (!(output >= input.c_str() && output < input.c_str() + input.size())) | if (!(output >= source.c_str() && output < source.c_str() + source.size())) | ||||
free(output); | free(output); | ||||
return ret; | return ret; | ||||
} | } |
Wildfire Games · Phabricator
std::find maybe ? or indexOf -1 ?