Shader: Fixes some errors and add unit tests

This commit is contained in:
SirLynix 2022-04-02 16:07:13 +02:00
parent 1c7a3a96e5
commit aa43db956c
5 changed files with 49 additions and 17 deletions

View File

@ -40,7 +40,7 @@ namespace Nz::ShaderLang
{
public:
inline Error(SourceLocation sourceLocation, ErrorCategory errorCategory, ErrorType errorType) noexcept;
Error(const Error&) = delete;
Error(const Error&) = default;
Error(Error&&) noexcept = default;
~Error() = default;
@ -51,7 +51,7 @@ namespace Nz::ShaderLang
const char* what() const noexcept override;
Error& operator=(const Error&) = delete;
Error& operator=(const Error&) = default;
Error& operator=(Error&&) noexcept = default;
protected:

View File

@ -118,14 +118,14 @@ namespace Nz::ShaderLang
sourceFile = *m_sourceLocation.file;
if (m_sourceLocation.startLine != m_sourceLocation.endLine)
m_errorMessage = fmt::format("{}({} -> {},{} -> {}): {} error {}: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.endLine, m_sourceLocation.startColumn, m_sourceLocation.endColumn, m_errorCategory, m_errorType, BuildErrorMessage());
m_errorMessage = fmt::format("{}({} -> {},{} -> {}): {} error: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.endLine, m_sourceLocation.startColumn, m_sourceLocation.endColumn, m_errorType, BuildErrorMessage());
else if (m_sourceLocation.startColumn != m_sourceLocation.endColumn)
m_errorMessage = fmt::format("{}({},{} -> {}): {} error {}: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.startColumn, m_sourceLocation.endColumn, m_errorCategory, m_errorType, BuildErrorMessage());
m_errorMessage = fmt::format("{}({},{} -> {}): {} error: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.startColumn, m_sourceLocation.endColumn, m_errorType, BuildErrorMessage());
else
m_errorMessage = fmt::format("{}({}, {}): {} error {}: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.startColumn, m_errorCategory, m_errorType, BuildErrorMessage());
m_errorMessage = fmt::format("{}({}, {}): {} error: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.startColumn, m_errorType, BuildErrorMessage());
}
else
m_errorMessage = fmt::format("?: {} error {}: {}", m_errorCategory, m_errorType, BuildErrorMessage());
m_errorMessage = fmt::format("?: {} error: {}", m_errorType, BuildErrorMessage());
}
return m_errorMessage;

View File

@ -224,6 +224,7 @@ namespace Nz::ShaderLang
}
token.location.endColumn = SafeCast<UInt32>(currentPos - lastLineFeed) + 1;
token.location.endLine = currentLine;
if (floatingPoint)
{
@ -244,17 +245,18 @@ namespace Nz::ShaderLang
{
tokenType = TokenType::IntegerValue;
// avoid std::string_view operator[] assertions (if &str[currentPos + 1] is out of the string)
const char* first = &str[start];
const char* last = first + (currentPos - start + 1);
long long value;
std::from_chars_result r = std::from_chars(&str[start], &str[currentPos + 1], value);
if (r.ptr != &str[currentPos + 1])
{
if (r.ec == std::errc::result_out_of_range)
throw LexerNumberOutOfRangeError{ token.location };
std::from_chars_result r = std::from_chars(first, last, value);
if (r.ptr == last && r.ec == std::errc{})
token.data = value;
else if (r.ec == std::errc::result_out_of_range)
throw LexerNumberOutOfRangeError{ token.location };
else
throw LexerBadNumberError{ token.location };
}
token.data = value;
}
break;
@ -414,12 +416,13 @@ namespace Nz::ShaderLang
case '\n':
case '\r':
token.location.endColumn = SafeCast<UInt32>(currentPos - lastLineFeed) + 1;
token.location.endLine = currentLine;
throw LexerUnfinishedStringError{ token.location };
case '\\':
{
currentPos++;
char next = Peek();
char next = Peek(0);
switch (next)
{
case 'n': character = '\n'; break;
@ -429,6 +432,7 @@ namespace Nz::ShaderLang
case '\\': character = '\\'; break;
default:
token.location.endColumn = SafeCast<UInt32>(currentPos - lastLineFeed) + 1;
token.location.endLine = currentLine;
throw LexerUnrecognizedCharError{ token.location };
}
break;
@ -469,7 +473,11 @@ namespace Nz::ShaderLang
break;
}
else
{
token.location.endColumn = SafeCast<UInt32>(currentPos - lastLineFeed) + 1;
token.location.endLine = currentLine;
throw LexerUnrecognizedTokenError{ token.location };
}
}
}

View File

@ -285,7 +285,7 @@ namespace Nz::ShaderLang
}
if (!moduleVersion.has_value())
throw ParserMissingAttributeError{ moduleToken.location };
throw ParserMissingAttributeError{ moduleToken.location, ShaderAst::AttributeType::LangVersion };
if (!moduleId)
moduleId = Uuid::Generate();

View File

@ -0,0 +1,24 @@
#include <Engine/Shader/ShaderUtils.hpp>
#include <Nazara/Shader/ShaderLangParser.hpp>
#include <catch2/catch.hpp>
TEST_CASE("errors", "[Shader]")
{
SECTION("Checking lexer errors")
{
CHECK_THROWS_WITH(Nz::ShaderLang::Tokenize("1x42"), "(1,1 -> 4): LBadNumber error: bad number");
CHECK_THROWS_WITH(Nz::ShaderLang::Tokenize("123456789876543210123456789"), "(1,1 -> 27): LNumberOutOfRange error: number is out of range");
CHECK_THROWS_WITH(Nz::ShaderLang::Tokenize("\"Hello world"), "(1,1 -> 13): LUnfinishedString error: unfinished string");
CHECK_THROWS_WITH(Nz::ShaderLang::Tokenize(R"("hello \p")"), "(1,1 -> 9): LUnrecognizedChar error: unrecognized character");
CHECK_THROWS_WITH(Nz::ShaderLang::Tokenize("$"), "(1, 1): LUnrecognizedToken error: unrecognized token");
}
SECTION("Checking parser errors")
{
CHECK_THROWS_WITH(Nz::ShaderLang::Parse("nazara"), "(1,1 -> 6): PUnexpectedToken error: unexpected token Identifier");
CHECK_THROWS_WITH(Nz::ShaderLang::Parse("module;"), "(1,1 -> 6): PMissingAttribute error: missing attribute nzsl_version");
CHECK_THROWS_WITH(Nz::ShaderLang::Parse("[nzsl_version] module;"), "(1,2 -> 13): PAttributeMissingParameter error: attribute nzsl_version requires a parameter");
CHECK_THROWS_WITH(Nz::ShaderLang::Parse("[nzsl_version(\"1.0\"), nzsl_version(\"1.0\")] module;"), "(1,23 -> 41): PAttributeMultipleUnique error: attribute nzsl_version can only be present once");
CHECK_THROWS_WITH(Nz::ShaderLang::Parse("[nzsl_version(\"1.0\"), uuid(\"Nazara\")] module;"), "(1,23 -> 36): PInvalidUuid error: \"Nazara\" is not a valid UUID");
}
}