diff --git a/include/Nazara/Shader/ShaderLangErrorList.hpp b/include/Nazara/Shader/ShaderLangErrorList.hpp index e6e3c4e9b..d53a2ea56 100644 --- a/include/Nazara/Shader/ShaderLangErrorList.hpp +++ b/include/Nazara/Shader/ShaderLangErrorList.hpp @@ -4,10 +4,18 @@ // no header guards -#if !defined(NAZARA_SHADERLANG_ERROR) && (!defined(NAZARA_SHADERLANG_LEXER_ERROR) || !defined(NAZARA_SHADERLANG_PARSER_ERROR) || !defined(NAZARA_SHADERLANG_COMPILER_ERROR)) +#if !defined(NAZARA_SHADERLANG_ERROR) && (!defined(NAZARA_SHADERLANG_LEXER_ERROR) || !defined(NAZARA_SHADERLANG_PARSER_ERROR) || !defined(NAZARA_SHADERLANG_COMPILER_ERROR) || !defined(NAZARA_SHADERLANG_AST_ERROR)) #error You must define NAZARA_SHADERLANG_ERROR or NAZARA_SHADERLANG_LEXER_ERROR/NAZARA_SHADERLANG_PARSER_ERROR/NAZARA_SHADERLANG_COMPILER_ERROR before including this file #endif +#ifndef NAZARA_SHADERLANG_AST_ERROR +#define NAZARA_SHADERLANG_AST_ERROR(...) NAZARA_SHADERLANG_ERROR(A, ...) +#endif + +#ifndef NAZARA_SHADERLANG_COMPILER_ERROR +#define NAZARA_SHADERLANG_COMPILER_ERROR(...) NAZARA_SHADERLANG_ERROR(C, ...) +#endif + #ifndef NAZARA_SHADERLANG_LEXER_ERROR #define NAZARA_SHADERLANG_LEXER_ERROR(...) NAZARA_SHADERLANG_ERROR(L, ...) #endif @@ -16,10 +24,6 @@ #define NAZARA_SHADERLANG_PARSER_ERROR(...) NAZARA_SHADERLANG_ERROR(P, ...) #endif -#ifndef NAZARA_SHADERLANG_COMPILER_ERROR -#define NAZARA_SHADERLANG_COMPILER_ERROR(...) NAZARA_SHADERLANG_COMPILER_ERROR(C, ...) -#endif - // Lexer errors NAZARA_SHADERLANG_LEXER_ERROR(1, BadNumber, "bad number") NAZARA_SHADERLANG_LEXER_ERROR(2, NumberOutOfRange, "number is out of range") @@ -28,18 +32,33 @@ NAZARA_SHADERLANG_LEXER_ERROR(4, UnrecognizedChar, "unrecognized character") NAZARA_SHADERLANG_LEXER_ERROR(5, UnrecognizedToken, "unrecognized token") // Parser errors -NAZARA_SHADERLANG_PARSER_ERROR(1, AttributeError, "attribute error") -NAZARA_SHADERLANG_PARSER_ERROR(2, ExpectedToken, "expected token") -NAZARA_SHADERLANG_PARSER_ERROR(3, DuplicateIdentifier, "duplicate identifier") -NAZARA_SHADERLANG_PARSER_ERROR(4, DuplicateModule, "duplicate module") -NAZARA_SHADERLANG_PARSER_ERROR(5, ReservedKeyword, "reserved keyword") -NAZARA_SHADERLANG_PARSER_ERROR(6, UnknownAttribute, "unknown attribute") -NAZARA_SHADERLANG_PARSER_ERROR(7, UnknownType, "unknown type") -NAZARA_SHADERLANG_PARSER_ERROR(8, UnexpectedToken, "unexpected token") +NAZARA_SHADERLANG_PARSER_ERROR( 1, AttributeExpectString, "attribute {} requires a string parameter", ShaderAst::AttributeType) +NAZARA_SHADERLANG_PARSER_ERROR( 2, AttributeInvalidParameter, "invalid parameter {} for attribute {}", std::string, ShaderAst::AttributeType) +NAZARA_SHADERLANG_PARSER_ERROR( 3, AttributeMissingParameter, "attribute {} requires a parameter", ShaderAst::AttributeType) +NAZARA_SHADERLANG_PARSER_ERROR( 4, AttributeMultipleUnique, "attribute {} can only be present once", ShaderAst::AttributeType) +NAZARA_SHADERLANG_PARSER_ERROR( 5, AttributeParameterIdentifier, "attribute {} parameter can only be an identifier", ShaderAst::AttributeType) +NAZARA_SHADERLANG_PARSER_ERROR( 6, ExpectedToken, "expected token {}, got {}", ShaderLang::TokenType, ShaderLang::TokenType) +NAZARA_SHADERLANG_PARSER_ERROR( 7, DuplicateIdentifier, "duplicate identifier") +NAZARA_SHADERLANG_PARSER_ERROR( 8, DuplicateModule, "duplicate module") +NAZARA_SHADERLANG_PARSER_ERROR( 9, InvalidVersion, "\"{}\" is not a valid version", std::string) +NAZARA_SHADERLANG_PARSER_ERROR(10, InvalidUuid, "\"{}\" is not a valid UUID", std::string) +NAZARA_SHADERLANG_PARSER_ERROR(11, MissingAttribute, "missing attribute {}", ShaderAst::AttributeType) +NAZARA_SHADERLANG_PARSER_ERROR(12, ReservedKeyword, "reserved keyword") +NAZARA_SHADERLANG_PARSER_ERROR(13, UnknownAttribute, "unknown attribute") +NAZARA_SHADERLANG_PARSER_ERROR(14, UnknownType, "unknown type") +NAZARA_SHADERLANG_PARSER_ERROR(15, UnexpectedAttribute, "unexpected attribute {}", ShaderAst::AttributeType) +NAZARA_SHADERLANG_PARSER_ERROR(16, UnexpectedEndOfFile, "unexpected end of file") +NAZARA_SHADERLANG_PARSER_ERROR(17, UnexpectedToken, "unexpected token {}", ShaderLang::TokenType) // Compiler errors +NAZARA_SHADERLANG_COMPILER_ERROR(1, InvalidSwizzle, "invalid swizzle {}", std::string) + +// AST errors +NAZARA_SHADERLANG_AST_ERROR(1, AlreadyUsedIndex, "index {} is already used", std::size_t) +NAZARA_SHADERLANG_AST_ERROR(2, InvalidIndex, "invalid index {}", std::size_t) #undef NAZARA_SHADERLANG_ERROR +#undef NAZARA_SHADERLANG_AST_ERROR #undef NAZARA_SHADERLANG_COMPILER_ERROR #undef NAZARA_SHADERLANG_LEXER_ERROR #undef NAZARA_SHADERLANG_PARSER_ERROR diff --git a/include/Nazara/Shader/ShaderLangErrors.hpp b/include/Nazara/Shader/ShaderLangErrors.hpp new file mode 100644 index 000000000..4c67287aa --- /dev/null +++ b/include/Nazara/Shader/ShaderLangErrors.hpp @@ -0,0 +1,127 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Shader module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_SHADER_SHADERLANGERRORS_HPP +#define NAZARA_SHADER_SHADERLANGERRORS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz::ShaderLang +{ + struct SourceLocation + { + inline SourceLocation(); + inline SourceLocation(unsigned int line, unsigned int column, std::shared_ptr file); + inline SourceLocation(unsigned int line, unsigned int startColumn, unsigned int endColumn, std::shared_ptr file); + inline SourceLocation(unsigned int startLine, unsigned int endLine, unsigned int startColumn, unsigned int endColumn, std::shared_ptr file); + + inline bool IsValid() const; + + std::shared_ptr file; //< Since the same file will be used for every node, prevent holding X time the same path + unsigned int endColumn; + unsigned int endLine; + unsigned int startColumn; + unsigned int startLine; + }; + + enum class ErrorCategory + { + Ast, + Compilation, + Lexing, + Parsing, + + Max = Parsing + }; + + class Error : public std::exception + { + public: + inline Error(SourceLocation sourceLocation, ErrorCategory errorCategory, unsigned int errorType) noexcept; + Error(const Error&) = delete; + Error(Error&&) = delete; + ~Error() = default; + + inline ErrorCategory GetErrorCategory() const; + const std::string& GetErrorMessage() const; + inline unsigned int GetErrorType() const; + inline const SourceLocation& GetSourceLocation() const; + + const char* what() const noexcept override; + + Error& operator=(const Error&) = delete; + Error& operator=(Error&&) = delete; + + protected: + virtual std::string BuildErrorMessage() const = 0; + + private: + mutable std::string m_errorMessage; + ErrorCategory m_errorCategory; + SourceLocation m_sourceLocation; + unsigned int m_errorType; + }; + + class AstError : public Error + { + public: + inline AstError(SourceLocation sourceLocation, unsigned int errorType) noexcept; + }; + + class CompilationError : public Error + { + public: + inline CompilationError(SourceLocation sourceLocation, unsigned int errorType) noexcept; + }; + + class LexingError : public Error + { + public: + inline LexingError(SourceLocation sourceLocation, unsigned int errorType) noexcept; + }; + + class ParsingError : public Error + { + public: + inline ParsingError(SourceLocation sourceLocation, unsigned int errorType) noexcept; + }; + +#define NAZARA_SHADERLANG_NEWERRORTYPE(Prefix, BaseClass, ErrorType, ErrorName, ErrorString, ...) \ + class Prefix ## ErrorName ## Error : public BaseClass \ + { \ + public: \ + template Prefix ## ErrorName ## Error(SourceLocation sourceLocation, Args&&... args) : \ + BaseClass(std::move(sourceLocation), ErrorType), \ + m_parameters(std::forward(args)...) \ + { \ + } \ + \ + private: \ + std::string BuildErrorMessage() const override; \ + \ + std::tuple<__VA_ARGS__> m_parameters; \ + }; + +#define NAZARA_SHADERLANG_AST_ERROR(ErrorType, ErrorName, ErrorString, ...) NAZARA_SHADERLANG_NEWERRORTYPE(Ast, AstError, ErrorType, ErrorName, ErrorString, __VA_ARGS__) +#define NAZARA_SHADERLANG_LEXER_ERROR(ErrorType, ErrorName, ErrorString, ...) NAZARA_SHADERLANG_NEWERRORTYPE(Lexer, LexingError, ErrorType, ErrorName, ErrorString, __VA_ARGS__) +#define NAZARA_SHADERLANG_PARSER_ERROR(ErrorType, ErrorName, ErrorString, ...) NAZARA_SHADERLANG_NEWERRORTYPE(Parser, ParsingError, ErrorType, ErrorName, ErrorString, __VA_ARGS__) +#define NAZARA_SHADERLANG_COMPILER_ERROR(ErrorType, ErrorName, ErrorString, ...) NAZARA_SHADERLANG_NEWERRORTYPE(Compiler, CompilationError, ErrorType, ErrorName, ErrorString, __VA_ARGS__) + +#include + +#undef NAZARA_SHADERLANG_NEWERRORTYPE +} + +#include + +#endif // NAZARA_SHADER_SHADERLANGERRORS_HPP diff --git a/include/Nazara/Shader/ShaderLangErrors.inl b/include/Nazara/Shader/ShaderLangErrors.inl new file mode 100644 index 000000000..33bcdbeb8 --- /dev/null +++ b/include/Nazara/Shader/ShaderLangErrors.inl @@ -0,0 +1,94 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Shader module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz::ShaderLang +{ + inline SourceLocation::SourceLocation() : + endColumn(0), + endLine(0), + startColumn(0), + startLine(0) + { + } + + inline SourceLocation::SourceLocation(unsigned int Line, unsigned int Column, std::shared_ptr File) : + file(std::move(File)), + endColumn(Column), + endLine(Line), + startColumn(Column), + startLine(Line) + { + } + + inline SourceLocation::SourceLocation(unsigned int Line, unsigned int StartColumn, unsigned int EndColumn, std::shared_ptr File) : + file(std::move(File)), + endColumn(EndColumn), + endLine(Line), + startColumn(StartColumn), + startLine(Line) + { + } + + inline SourceLocation::SourceLocation(unsigned int StartLine, unsigned int EndLine, unsigned int StartColumn, unsigned int EndColumn, std::shared_ptr File) : + file(std::move(File)), + endColumn(EndColumn), + endLine(EndLine), + startColumn(StartColumn), + startLine(StartLine) + { + } + + inline bool SourceLocation::IsValid() const + { + return startLine != 0 && endLine != 0 && endColumn != 0 && startColumn != 0; + } + + inline Error::Error(SourceLocation sourceLocation, ErrorCategory errorCategory, unsigned int errorType) noexcept : + m_errorCategory(errorCategory), + m_sourceLocation(std::move(sourceLocation)), + m_errorType(errorType) + { + } + + inline ErrorCategory Error::GetErrorCategory() const + { + return m_errorCategory; + } + + inline unsigned int Error::GetErrorType() const + { + return m_errorType; + } + + inline const SourceLocation& Error::GetSourceLocation() const + { + return m_sourceLocation; + } + + + inline AstError::AstError(SourceLocation sourceLocation, unsigned int errorType) noexcept : + Error(std::move(sourceLocation), ErrorCategory::Ast, errorType) + { + } + + inline CompilationError::CompilationError(SourceLocation sourceLocation, unsigned int errorType) noexcept : + Error(std::move(sourceLocation), ErrorCategory::Compilation, errorType) + { + } + + inline ParsingError::ParsingError(SourceLocation sourceLocation, unsigned int errorType) noexcept : + Error(std::move(sourceLocation), ErrorCategory::Parsing, errorType) + { + } + + inline LexingError::LexingError(SourceLocation sourceLocation, unsigned int errorType) noexcept : + Error(std::move(sourceLocation), ErrorCategory::Lexing, errorType) + { + } +} + +#include diff --git a/include/Nazara/Shader/ShaderLangLexer.hpp b/include/Nazara/Shader/ShaderLangLexer.hpp index 39aa363e0..4cdd5e815 100644 --- a/include/Nazara/Shader/ShaderLangLexer.hpp +++ b/include/Nazara/Shader/ShaderLangLexer.hpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -28,35 +29,11 @@ namespace Nz::ShaderLang unsigned int column; unsigned int line; TokenType type; + std::shared_ptr file; std::variant data; }; - class BadNumber : public std::exception - { - using exception::exception; - }; - - class NumberOutOfRange : public std::exception - { - using exception::exception; - }; - - class UnfinishedString : public std::exception - { - using exception::exception; - }; - - class UnrecognizedChar : public std::exception - { - using exception::exception; - }; - - class UnrecognizedToken : public std::exception - { - using exception::exception; - }; - - NAZARA_SHADER_API std::vector Tokenize(const std::string_view& str); + NAZARA_SHADER_API std::vector Tokenize(const std::string_view& str, const std::string& filePath = std::string{}); NAZARA_SHADER_API const char* ToString(TokenType tokenType); NAZARA_SHADER_API std::string ToString(const std::vector& tokens, bool pretty = true); } diff --git a/include/Nazara/Shader/ShaderLangParser.hpp b/include/Nazara/Shader/ShaderLangParser.hpp index 900e57ca1..05746709f 100644 --- a/include/Nazara/Shader/ShaderLangParser.hpp +++ b/include/Nazara/Shader/ShaderLangParser.hpp @@ -16,54 +16,6 @@ namespace Nz::ShaderLang { - class AttributeError : public std::runtime_error - { - public: - using runtime_error::runtime_error; - }; - - class ExpectedToken : public std::exception - { - public: - using exception::exception; - }; - - class DuplicateIdentifier : public std::runtime_error - { - public: - using runtime_error::runtime_error; - }; - - class DuplicateModule : public std::runtime_error - { - public: - using runtime_error::runtime_error; - }; - - class ReservedKeyword : public std::exception - { - public: - using exception::exception; - }; - - class UnknownAttribute : public std::exception - { - public: - using exception::exception; - }; - - class UnknownType : public std::exception - { - public: - using exception::exception; - }; - - class UnexpectedToken : public std::exception - { - public: - using exception::exception; - }; - class NAZARA_SHADER_API Parser { public: @@ -142,7 +94,7 @@ namespace Nz::ShaderLang Context* m_context; }; - inline ShaderAst::ModulePtr Parse(const std::string_view& source); + inline ShaderAst::ModulePtr Parse(const std::string_view& source, const std::string& filePath = std::string{}); inline ShaderAst::ModulePtr Parse(const std::vector& tokens); NAZARA_SHADER_API ShaderAst::ModulePtr ParseFromFile(const std::filesystem::path& sourcePath); } diff --git a/include/Nazara/Shader/ShaderLangParser.inl b/include/Nazara/Shader/ShaderLangParser.inl index 8c2d3d9ed..86679f20e 100644 --- a/include/Nazara/Shader/ShaderLangParser.inl +++ b/include/Nazara/Shader/ShaderLangParser.inl @@ -12,9 +12,9 @@ namespace Nz::ShaderLang { } - inline ShaderAst::ModulePtr Parse(const std::string_view& source) + inline ShaderAst::ModulePtr Parse(const std::string_view& source, const std::string& filePath) { - return Parse(Tokenize(source)); + return Parse(Tokenize(source, filePath)); } inline ShaderAst::ModulePtr Parse(const std::vector& tokens) diff --git a/src/Nazara/Shader/Ast/AstRecursiveVisitor.cpp b/src/Nazara/Shader/Ast/AstRecursiveVisitor.cpp index b14768094..bb086f568 100644 --- a/src/Nazara/Shader/Ast/AstRecursiveVisitor.cpp +++ b/src/Nazara/Shader/Ast/AstRecursiveVisitor.cpp @@ -109,7 +109,7 @@ namespace Nz::ShaderAst node.expression->Visit(*this); } - void AstRecursiveVisitor::Visit(TypeExpression& node) + void AstRecursiveVisitor::Visit(TypeExpression& /*node*/) { /* Nothing to do */ } diff --git a/src/Nazara/Shader/Ast/SanitizeVisitor.cpp b/src/Nazara/Shader/Ast/SanitizeVisitor.cpp index 26fa2f4bd..b00f7dd3d 100644 --- a/src/Nazara/Shader/Ast/SanitizeVisitor.cpp +++ b/src/Nazara/Shader/Ast/SanitizeVisitor.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -420,6 +421,7 @@ namespace Nz::ShaderAst { if (ToSwizzleIndex(identifier[j]) != 0) throw AstError{ "invalid swizzle" }; + //throw ShaderLang::CompilerInvalidSwizzleError{}; } if (swizzleComponentCount == 1) @@ -1250,6 +1252,9 @@ namespace Nz::ShaderAst if (member.locationIndex.HasValue()) ComputeExprValue(member.locationIndex); + if (member.builtin.HasValue() && member.locationIndex.HasValue()) + throw AstError{ "A struct field cannot have both builtin and location attributes" }; + if (declaredMembers.find(member.name) != declaredMembers.end()) { if ((!member.cond.HasValue() || !member.cond.IsResultingValue()) && !m_context->options.allowPartialSanitization) @@ -3195,7 +3200,7 @@ namespace Nz::ShaderAst } node.cachedExpressionType = targetType; - node.targetType = std::move(targetType); + node.targetType = targetType; return ValidationResult::Validated; } diff --git a/src/Nazara/Shader/ShaderLangErrors.cpp b/src/Nazara/Shader/ShaderLangErrors.cpp new file mode 100644 index 000000000..9ff685095 --- /dev/null +++ b/src/Nazara/Shader/ShaderLangErrors.cpp @@ -0,0 +1,116 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Shader module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include + +// https://fmt.dev/latest/api.html#udt +template <> +struct fmt::formatter : formatter +{ + template + auto format(const Nz::ShaderAst::AttributeType& p, FormatContext& ctx) -> decltype(ctx.out()) + { + // TODO: Add ToString + std::string_view name = ""; + switch (p) + { + case Nz::ShaderAst::AttributeType::Binding: name = "binding"; break; + case Nz::ShaderAst::AttributeType::Builtin: name = "builtin"; break; + case Nz::ShaderAst::AttributeType::Cond: name = "cond"; break; + case Nz::ShaderAst::AttributeType::DepthWrite: name = "depth_write"; break; + case Nz::ShaderAst::AttributeType::EarlyFragmentTests: name = "early_fragment_tests"; break; + case Nz::ShaderAst::AttributeType::Entry: name = "entry"; break; + case Nz::ShaderAst::AttributeType::Export: name = "export"; break; + case Nz::ShaderAst::AttributeType::Layout: name = "layout"; break; + case Nz::ShaderAst::AttributeType::Location: name = "location"; break; + case Nz::ShaderAst::AttributeType::LangVersion: name = "nzsl_version"; break; + case Nz::ShaderAst::AttributeType::Set: name = "set"; break; + case Nz::ShaderAst::AttributeType::Unroll: name = "unroll"; break; + case Nz::ShaderAst::AttributeType::Uuid: name = "uuid"; break; + } + + return formatter::format(name, ctx); + } +}; + +template <> +struct fmt::formatter : formatter +{ + template + auto format(const Nz::ShaderLang::ErrorCategory& p, FormatContext& ctx) -> decltype(ctx.out()) + { + // TODO: Add ToString + std::string_view name = ""; + switch (p) + { + case Nz::ShaderLang::ErrorCategory::Ast: name = "Ast"; break; + case Nz::ShaderLang::ErrorCategory::Compilation: name = "Compilation"; break; + case Nz::ShaderLang::ErrorCategory::Lexing: name = "Lexing"; break; + case Nz::ShaderLang::ErrorCategory::Parsing: name = "Parsing"; break; + } + + return formatter::format(name, ctx); + } +}; + +template <> +struct fmt::formatter : formatter +{ + template + auto format(const Nz::ShaderLang::TokenType& p, FormatContext& ctx) -> decltype(ctx.out()) + { + return formatter::format(ToString(p), ctx); + } +}; + +namespace Nz::ShaderLang +{ + const std::string& Error::GetErrorMessage() const + { + if (m_errorMessage.empty()) + { + if (m_sourceLocation.IsValid()) + { + std::string_view sourceFile; + if (m_sourceLocation.file) + 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()); + 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()); + else + m_errorMessage = fmt::format("{}({}, {}): {} error {}: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.startColumn, m_errorCategory, m_errorType, BuildErrorMessage()); + } + else + m_errorMessage = fmt::format("?: {} error {}: {}", m_errorCategory, m_errorType, BuildErrorMessage()); + } + + return m_errorMessage; + } + + const char* Error::what() const noexcept + { + return GetErrorMessage().c_str(); + } + +#define NAZARA_SHADERLANG_NEWERRORTYPE(Prefix, ErrorType, ErrorName, ErrorString, ...) \ + std::string Prefix ## ErrorName ## Error::BuildErrorMessage() const \ + { \ + return std::apply([&](const auto... args) { return fmt::format(ErrorString, args...); }, m_parameters); \ + } + +#define NAZARA_SHADERLANG_AST_ERROR(ErrorType, ErrorName, ErrorString, ...) NAZARA_SHADERLANG_NEWERRORTYPE(Ast, ErrorType, ErrorName, ErrorString, __VA_ARGS__) +#define NAZARA_SHADERLANG_LEXER_ERROR(ErrorType, ErrorName, ErrorString, ...) NAZARA_SHADERLANG_NEWERRORTYPE(Lexer, ErrorType, ErrorName, ErrorString, __VA_ARGS__) +#define NAZARA_SHADERLANG_PARSER_ERROR(ErrorType, ErrorName, ErrorString, ...) NAZARA_SHADERLANG_NEWERRORTYPE(Parser, ErrorType, ErrorName, ErrorString, __VA_ARGS__) +#define NAZARA_SHADERLANG_COMPILER_ERROR(ErrorType, ErrorName, ErrorString, ...) NAZARA_SHADERLANG_NEWERRORTYPE(Compiler, ErrorType, ErrorName, ErrorString, __VA_ARGS__) + +#include + +#undef NAZARA_SHADERLANG_NEWERRORTYPE +} diff --git a/src/Nazara/Shader/ShaderLangLexer.cpp b/src/Nazara/Shader/ShaderLangLexer.cpp index feb52257a..cb2468974 100644 --- a/src/Nazara/Shader/ShaderLangLexer.cpp +++ b/src/Nazara/Shader/ShaderLangLexer.cpp @@ -3,6 +3,8 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include +#include #include #include #include @@ -34,7 +36,7 @@ namespace Nz::ShaderLang }; } - std::vector Tokenize(const std::string_view& str) + std::vector Tokenize(const std::string_view& str, const std::string& filePath) { // Can't use std::from_chars for double, thanks to libc++ and libstdc++ developers for being lazy, so we have to force C locale ForceCLocale forceCLocale; @@ -76,16 +78,21 @@ namespace Nz::ShaderLang return std::isalnum(c) || c == '_'; }; - unsigned int lineNumber = 0; + unsigned int lineNumber = 1; std::size_t lastLineFeed = 0; std::vector tokens; + std::shared_ptr currentFile; + if (!filePath.empty()) + currentFile = std::make_shared(filePath); + for (;;) { char c = Peek(0); Token token; token.column = static_cast(currentPos - lastLineFeed); + token.file = currentFile; token.line = lineNumber; if (c == '\0') @@ -227,7 +234,10 @@ namespace Nz::ShaderLang char* end; double value = std::strtod(ptr, &end); if (end != &ptr[valueStr.size()]) - throw BadNumber{}; + { + unsigned int columnOffset = SafeCast(end - ptr); + throw LexerBadNumberError{ SourceLocation{ token.line, token.column + columnOffset, token.file } }; + } token.data = value; } @@ -239,10 +249,11 @@ namespace Nz::ShaderLang std::from_chars_result r = std::from_chars(&str[start], &str[currentPos + 1], value); if (r.ptr != &str[currentPos + 1]) { + unsigned int columnOffset = SafeCast(r.ptr - &str[start]); if (r.ec == std::errc::result_out_of_range) - throw NumberOutOfRange{}; + throw LexerNumberOutOfRangeError{ SourceLocation{ token.line, token.column, token.column + columnOffset, token.file } }; - throw BadNumber{}; + throw LexerBadNumberError{ SourceLocation{ token.line, token.column, token.column + columnOffset, token.file } }; } token.data = value; @@ -281,7 +292,7 @@ namespace Nz::ShaderLang tokenType = TokenType::LogicalOr; } else - throw UnrecognizedToken{}; //< TODO: Add BOR (a | b) + throw LexerUnrecognizedTokenError{ SourceLocation{ token.line, token.column, token.file } }; //< TODO: Add BOR (a | b) break; } @@ -302,7 +313,7 @@ namespace Nz::ShaderLang tokenType = TokenType::LogicalAnd; } else - throw UnrecognizedToken{}; //< TODO: Add BAND (a & b) + throw LexerUnrecognizedTokenError{ SourceLocation{ token.line, token.column, token.file } }; //< TODO: Add BAND (a & b) break; } @@ -404,7 +415,7 @@ namespace Nz::ShaderLang case '\0': case '\n': case '\r': - throw UnfinishedString{}; + throw LexerUnfinishedStringError{ SourceLocation{ token.line, SafeCast(currentPos - lastLineFeed), token.file } }; case '\\': { @@ -418,7 +429,7 @@ namespace Nz::ShaderLang case '"': character = '"'; break; case '\\': character = '\\'; break; default: - throw UnrecognizedChar{}; + throw LexerUnrecognizedCharError{ SourceLocation{ token.line, SafeCast(currentPos - lastLineFeed), token.file } }; } break; } @@ -458,7 +469,7 @@ namespace Nz::ShaderLang break; } else - throw UnrecognizedToken{}; + throw LexerUnrecognizedTokenError{ SourceLocation{ token.line, token.column, token.file } }; } } diff --git a/src/Nazara/Shader/ShaderLangParser.cpp b/src/Nazara/Shader/ShaderLangParser.cpp index 5e1ae37b6..50f154415 100644 --- a/src/Nazara/Shader/ShaderLangParser.cpp +++ b/src/Nazara/Shader/ShaderLangParser.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -59,22 +60,22 @@ namespace Nz::ShaderLang }; template - void HandleUniqueAttribute(const std::string_view& attributeName, ShaderAst::ExpressionValue& targetAttribute, ShaderAst::ExprValue::Param&& param) + void HandleUniqueAttribute(const Token& token, ShaderAst::AttributeType attributeType, ShaderAst::ExpressionValue& targetAttribute, ShaderAst::ExprValue::Param&& param) { if (targetAttribute.HasValue()) - throw AttributeError{ "attribute " + std::string(attributeName) + " must be present once" }; + throw ParserAttributeMultipleUniqueError{ SourceLocation{ token.line, token.column, token.file }, attributeType }; //< TODO: Improve source location for attributes if (param) targetAttribute = std::move(*param); else - throw AttributeError{ "attribute " + std::string(attributeName) + " requires a parameter" }; + throw ParserAttributeMissingParameterError{ SourceLocation{ token.line, token.column, token.file }, attributeType }; //< TODO: Improve source location for attributes } template - void HandleUniqueAttribute(const std::string_view& attributeName, ShaderAst::ExpressionValue& targetAttribute, ShaderAst::ExprValue::Param&& param, T defaultValue) + void HandleUniqueAttribute(const Token& token, ShaderAst::AttributeType attributeType, ShaderAst::ExpressionValue& targetAttribute, ShaderAst::ExprValue::Param&& param, T defaultValue) { if (targetAttribute.HasValue()) - throw AttributeError{ "attribute " + std::string(attributeName) + " must be present once" }; + throw ParserAttributeMultipleUniqueError{ SourceLocation{ token.line, token.column, token.file }, attributeType }; //< TODO: Improve source location for attributes if (param) targetAttribute = std::move(*param); @@ -83,30 +84,30 @@ namespace Nz::ShaderLang } template - void HandleUniqueStringAttribute(const std::string_view& attributeName, const std::unordered_map& map, ShaderAst::ExpressionValue& targetAttribute, ShaderAst::ExprValue::Param&& param, std::optional defaultValue = {}) + void HandleUniqueStringAttribute(const Token& token, ShaderAst::AttributeType attributeType, const std::unordered_map& map, ShaderAst::ExpressionValue& targetAttribute, ShaderAst::ExprValue::Param&& param, std::optional defaultValue = {}) { if (targetAttribute.HasValue()) - throw AttributeError{ "attribute " + std::string(attributeName) + " must be present once" }; + throw ParserAttributeMultipleUniqueError{ SourceLocation{ token.line, token.column, token.file }, attributeType }; //< TODO: Improve source location for attributes //FIXME: This should be handled with global values at sanitization stage if (param) { const ShaderAst::ExpressionPtr& expr = *param; if (expr->GetType() != ShaderAst::NodeType::IdentifierExpression) - throw AttributeError{ "attribute " + std::string(attributeName) + " can only be an identifier for now" }; + throw ParserAttributeParameterIdentifierError{ SourceLocation{ token.line, token.column, token.file }, attributeType }; const std::string& exprStr = static_cast(*expr).identifier; auto it = map.find(exprStr); if (it == map.end()) - throw AttributeError{ ("invalid parameter " + exprStr + " for " + std::string(attributeName) + " attribute").c_str() }; + throw ParserAttributeInvalidParameterError{ SourceLocation{ token.line, token.column, token.file }, exprStr, attributeType }; targetAttribute = it->second; } else { if (!defaultValue) - throw AttributeError{ "attribute " + std::string(attributeName) + " requires a value" }; + throw ParserAttributeMissingParameterError{ SourceLocation{ token.line, token.column, token.file }, attributeType }; //< TODO: Improve source location for attributes targetAttribute = defaultValue.value(); } @@ -127,7 +128,10 @@ namespace Nz::ShaderLang { ShaderAst::StatementPtr statement = ParseRootStatement(); if (!m_context->module) - throw UnexpectedToken{}; //< "unexpected token before module declaration" + { + const Token& nextToken = Peek(); + throw ParserUnexpectedTokenError{ SourceLocation{ nextToken.line, nextToken.column, nextToken.file } }; + } if (!statement) break; @@ -155,7 +159,7 @@ namespace Nz::ShaderLang const Token& Parser::Expect(const Token& token, TokenType type) { if (token.type != type) - throw ExpectedToken{}; + throw ParserExpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, type, token.type }; return token; } @@ -163,7 +167,7 @@ namespace Nz::ShaderLang const Token& Parser::ExpectNot(const Token& token, TokenType type) { if (token.type == type) - throw ExpectedToken{}; + throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, type }; return token; } @@ -238,9 +242,12 @@ namespace Nz::ShaderLang void Parser::ParseModuleStatement(std::vector attributes) { if (m_context->parsingImportedModule) - throw UnexpectedToken{}; + { + const Token& token = Peek(); + throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type }; + } - Expect(Advance(), TokenType::Module); + const Token& moduleToken = Expect(Advance(), TokenType::Module); std::optional moduleVersion; std::optional moduleId; @@ -253,18 +260,18 @@ namespace Nz::ShaderLang { // Version parsing if (moduleVersion.has_value()) - throw AttributeError{ "attribute " + std::string("nzsl_version") + " must be present once" }; + throw ParserAttributeMultipleUniqueError{ SourceLocation{ moduleToken.line, moduleToken.column, moduleToken.file }, attributeType }; //< TODO: Improve source location for attributes if (!arg) - throw AttributeError{ "attribute " + std::string("nzsl_version") + " requires a parameter"}; + throw ParserAttributeMissingParameterError{ SourceLocation{ moduleToken.line, moduleToken.column, moduleToken.file }, attributeType }; //< TODO: Improve source location for attributes const ShaderAst::ExpressionPtr& expr = *arg; if (expr->GetType() != ShaderAst::NodeType::ConstantValueExpression) - throw AttributeError{ "attribute " + std::string("nzsl_version") + " expect a single string parameter" }; + throw ParserAttributeExpectStringError{ SourceLocation{ moduleToken.line, moduleToken.column, moduleToken.file }, attributeType }; auto& constantValue = SafeCast(*expr); if (ShaderAst::GetConstantType(constantValue.value) != ShaderAst::ExpressionType{ ShaderAst::PrimitiveType::String }) - throw AttributeError{ "attribute " + std::string("nzsl_version") + " expect a single string parameter" }; + throw ParserAttributeExpectStringError{ SourceLocation{ moduleToken.line, moduleToken.column, moduleToken.file }, attributeType }; const std::string& versionStr = std::get(constantValue.value); @@ -272,7 +279,7 @@ namespace Nz::ShaderLang std::smatch versionMatch; if (!std::regex_match(versionStr, versionMatch, versionRegex)) - throw AttributeError("invalid version for attribute nzsl"); + throw ParserInvalidVersionError{ SourceLocation{ moduleToken.line, moduleToken.column, moduleToken.file }, versionStr }; assert(versionMatch.size() == 6); @@ -292,36 +299,36 @@ namespace Nz::ShaderLang case ShaderAst::AttributeType::Uuid: { if (moduleId.has_value()) - throw AttributeError{ "attribute" + std::string("uuid") + " can only be present once" }; + throw ParserAttributeMultipleUniqueError{ SourceLocation{ moduleToken.line, moduleToken.column, moduleToken.file }, attributeType }; //< TODO: Improve source location for attributes if (!arg) - throw AttributeError{ "attribute " + std::string("uuid") + " requires a parameter" }; + throw ParserAttributeMissingParameterError{ SourceLocation{ moduleToken.line, moduleToken.column, moduleToken.file }, attributeType }; //< TODO: Improve source location for attributes const ShaderAst::ExpressionPtr& expr = *arg; if (expr->GetType() != ShaderAst::NodeType::ConstantValueExpression) - throw AttributeError{ "attribute " + std::string("uuid") + " expect a single string parameter" }; + throw ParserAttributeExpectStringError{ SourceLocation{ moduleToken.line, moduleToken.column, moduleToken.file }, attributeType }; auto& constantValue = SafeCast(*expr); if (ShaderAst::GetConstantType(constantValue.value) != ShaderAst::ExpressionType{ ShaderAst::PrimitiveType::String }) - throw AttributeError{ "attribute " + std::string("uuid") + " expect a single string parameter" }; + throw ParserAttributeExpectStringError{ SourceLocation{ moduleToken.line, moduleToken.column, moduleToken.file }, attributeType }; const std::string& uuidStr = std::get(constantValue.value); Uuid uuid = Uuid::FromString(uuidStr); if (uuid.IsNull()) - throw AttributeError{ "attribute " + std::string("uuid") + " value is not a valid UUID" }; + throw ParserInvalidUuidError{ SourceLocation{ moduleToken.line, moduleToken.column, moduleToken.file }, uuidStr }; moduleId = uuid; break; } default: - throw AttributeError{ "unhandled attribute for module" }; + throw ParserUnexpectedAttributeError{ SourceLocation{ moduleToken.line, moduleToken.column, moduleToken.file }, attributeType }; } } if (!moduleVersion.has_value()) - throw AttributeError{ "missing module version" }; + throw ParserMissingAttributeError{ SourceLocation{ moduleToken.line, moduleToken.column, moduleToken.file } }; if (!moduleId) moduleId = Uuid::Generate(); @@ -340,7 +347,10 @@ namespace Nz::ShaderLang { ShaderAst::StatementPtr statement = ParseRootStatement(); if (!statement) - throw UnexpectedToken{}; //< "unexpected end of file" + { + const Token& token = Peek(); + throw ParserUnexpectedEndOfFileError{ SourceLocation{ token.line, token.column, token.file } }; + } module->rootNode->statements.push_back(std::move(statement)); } @@ -364,7 +374,7 @@ namespace Nz::ShaderLang Expect(Advance(), TokenType::Semicolon); if (m_context->module) - throw DuplicateModule{ "you must set one module statement per file" }; + throw ParserDuplicateModuleError{ SourceLocation{ moduleToken.line, moduleToken.column, moduleToken.file } }; m_context->module = std::move(module); } @@ -464,7 +474,8 @@ namespace Nz::ShaderLang { Expect(Advance(), TokenType::Const); - switch (Peek().type) + const Token& token = Peek(); + switch (token.type) { case TokenType::Identifier: { @@ -486,7 +497,7 @@ namespace Nz::ShaderLang } default: - throw UnexpectedToken{}; + throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type }; } } @@ -502,7 +513,7 @@ namespace Nz::ShaderLang { NAZARA_USE_ANONYMOUS_NAMESPACE - Expect(Advance(), TokenType::External); + const Token& externalToken = Expect(Advance(), TokenType::External); Expect(Advance(), TokenType::OpenCurlyBracket); std::unique_ptr externalStatement = std::make_unique(); @@ -514,15 +525,15 @@ namespace Nz::ShaderLang switch (attributeType) { case ShaderAst::AttributeType::Cond: - HandleUniqueAttribute("cond", condition, std::move(arg)); + HandleUniqueAttribute(externalToken, attributeType, condition, std::move(arg)); break; case ShaderAst::AttributeType::Set: - HandleUniqueAttribute("set", externalStatement->bindingSet, std::move(arg)); + HandleUniqueAttribute(externalToken, attributeType, externalStatement->bindingSet, std::move(arg)); break; default: - throw AttributeError{ "unhandled attribute for external block" }; + throw ParserUnexpectedAttributeError{ SourceLocation{ externalToken.line, externalToken.column, externalToken.file }, attributeType }; } } @@ -556,15 +567,15 @@ namespace Nz::ShaderLang switch (attributeType) { case ShaderAst::AttributeType::Binding: - HandleUniqueAttribute("binding", extVar.bindingIndex, std::move(arg)); + HandleUniqueAttribute(externalToken, attributeType, extVar.bindingIndex, std::move(arg)); break; case ShaderAst::AttributeType::Set: - HandleUniqueAttribute("set", extVar.bindingSet, std::move(arg)); + HandleUniqueAttribute(externalToken, attributeType, extVar.bindingSet, std::move(arg)); break; default: - throw AttributeError{ "unhandled attribute for external variable" }; + throw ParserUnexpectedAttributeError{ SourceLocation{ token.line, token.column, token.file }, attributeType }; } } } @@ -586,7 +597,7 @@ namespace Nz::ShaderLang { NAZARA_USE_ANONYMOUS_NAMESPACE - Expect(Advance(), TokenType::For); + const Token& forToken = Expect(Advance(), TokenType::For); std::string varName = ParseIdentifierAsName(); @@ -618,11 +629,11 @@ namespace Nz::ShaderLang switch (attributeType) { case ShaderAst::AttributeType::Unroll: - HandleUniqueStringAttribute("unroll", s_unrollModes, forNode->unroll, std::move(arg), std::make_optional(ShaderAst::LoopUnroll::Always)); + HandleUniqueStringAttribute(forToken, attributeType, s_unrollModes, forNode->unroll, std::move(arg), std::make_optional(ShaderAst::LoopUnroll::Always)); break; default: - throw AttributeError{ "unhandled attribute for numerical for" }; + throw ParserUnexpectedAttributeError{ SourceLocation{ forToken.line, forToken.column, forToken.file }, attributeType }; } } @@ -641,11 +652,11 @@ namespace Nz::ShaderLang switch (attributeType) { case ShaderAst::AttributeType::Unroll: - HandleUniqueStringAttribute("unroll", s_unrollModes, forEachNode->unroll, std::move(arg), std::make_optional(ShaderAst::LoopUnroll::Always)); + HandleUniqueStringAttribute(forToken, attributeType, s_unrollModes, forEachNode->unroll, std::move(arg), std::make_optional(ShaderAst::LoopUnroll::Always)); break; default: - throw AttributeError{ "unhandled attribute for for-each" }; + throw ParserUnexpectedAttributeError{ SourceLocation{ forToken.line, forToken.column, forToken.file }, attributeType }; } } @@ -662,7 +673,7 @@ namespace Nz::ShaderLang { NAZARA_USE_ANONYMOUS_NAMESPACE - Expect(Advance(), TokenType::FunctionDeclaration); + const auto& funcToken = Expect(Advance(), TokenType::FunctionDeclaration); std::string functionName = ParseIdentifierAsName(); @@ -706,27 +717,27 @@ namespace Nz::ShaderLang switch (attributeType) { case ShaderAst::AttributeType::Cond: - HandleUniqueAttribute("cond", condition, std::move(attributeParam)); + HandleUniqueAttribute(funcToken, attributeType, condition, std::move(attributeParam)); break; case ShaderAst::AttributeType::Entry: - HandleUniqueStringAttribute("entry", s_entryPoints, func->entryStage, std::move(attributeParam)); + HandleUniqueStringAttribute(funcToken, attributeType, s_entryPoints, func->entryStage, std::move(attributeParam)); break; case ShaderAst::AttributeType::Export: - HandleUniqueAttribute("export", func->isExported, std::move(attributeParam), true); + HandleUniqueAttribute(funcToken, attributeType, func->isExported, std::move(attributeParam), true); break; case ShaderAst::AttributeType::DepthWrite: - HandleUniqueStringAttribute("depth_write", s_depthWriteModes, func->depthWrite, std::move(attributeParam)); + HandleUniqueStringAttribute(funcToken, attributeType, s_depthWriteModes, func->depthWrite, std::move(attributeParam)); break; case ShaderAst::AttributeType::EarlyFragmentTests: - HandleUniqueAttribute("early_fragment_tests", func->earlyFragmentTests, std::move(attributeParam), true); + HandleUniqueAttribute(funcToken, attributeType, func->earlyFragmentTests, std::move(attributeParam), true); break; default: - throw AttributeError{ "unhandled attribute for function" }; + throw ParserUnexpectedAttributeError{ SourceLocation{ funcToken.line, funcToken.column, funcToken.file }, attributeType }; } } @@ -803,19 +814,19 @@ namespace Nz::ShaderLang { case TokenType::Alias: if (!attributes.empty()) - throw UnexpectedToken{}; + throw ParserUnexpectedTokenError{ SourceLocation{ nextToken.line, nextToken.column, nextToken.file }, nextToken.type }; return ParseAliasDeclaration(); case TokenType::Const: if (!attributes.empty()) - throw UnexpectedToken{}; + throw ParserUnexpectedTokenError{ SourceLocation{ nextToken.line, nextToken.column, nextToken.file }, nextToken.type }; return ParseConstStatement(); case TokenType::EndOfStream: if (!attributes.empty()) - throw UnexpectedToken{}; + throw ParserUnexpectedTokenError{ SourceLocation{ nextToken.line, nextToken.column, nextToken.file }, nextToken.type }; return {}; @@ -824,7 +835,7 @@ namespace Nz::ShaderLang case TokenType::Import: if (!attributes.empty()) - throw UnexpectedToken{}; + throw ParserUnexpectedTokenError{ SourceLocation{ nextToken.line, nextToken.column, nextToken.file }, nextToken.type }; return ParseImportStatement(); @@ -839,7 +850,7 @@ namespace Nz::ShaderLang case TokenType::Option: { if (!attributes.empty()) - throw UnexpectedToken{}; + throw ParserUnexpectedTokenError{ SourceLocation{ nextToken.line, nextToken.column, nextToken.file }, nextToken.type }; return ParseOptionDeclaration(); } @@ -851,7 +862,7 @@ namespace Nz::ShaderLang return ParseStructDeclaration(std::move(attributes)); default: - throw UnexpectedToken{}; + throw ParserUnexpectedTokenError{ SourceLocation{ nextToken.line, nextToken.column, nextToken.file }, nextToken.type }; } } @@ -866,14 +877,14 @@ namespace Nz::ShaderLang { case TokenType::Const: if (!attributes.empty()) - throw UnexpectedToken{}; + throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type }; statement = ParseConstStatement(); break; case TokenType::Discard: if (!attributes.empty()) - throw UnexpectedToken{}; + throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type }; statement = ParseDiscardStatement(); break; @@ -885,14 +896,14 @@ namespace Nz::ShaderLang case TokenType::Let: if (!attributes.empty()) - throw UnexpectedToken{}; + throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type }; statement = ParseVariableDeclaration(); break; case TokenType::Identifier: if (!attributes.empty()) - throw UnexpectedToken{}; + throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type }; statement = ShaderBuilder::ExpressionStatement(ParseVariableAssignation()); Expect(Advance(), TokenType::Semicolon); @@ -900,7 +911,7 @@ namespace Nz::ShaderLang case TokenType::If: if (!attributes.empty()) - throw UnexpectedToken{}; + throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type }; statement = ParseBranchStatement(); break; @@ -912,7 +923,7 @@ namespace Nz::ShaderLang case TokenType::Return: if (!attributes.empty()) - throw UnexpectedToken{}; + throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type }; statement = ParseReturnStatement(); break; @@ -923,7 +934,7 @@ namespace Nz::ShaderLang break; default: - throw UnexpectedToken{}; + throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type }; } } while (!statement); //< small trick to repeat parsing once we got attributes @@ -958,7 +969,7 @@ namespace Nz::ShaderLang { NAZARA_USE_ANONYMOUS_NAMESPACE - Expect(Advance(), TokenType::Struct); + const auto& structToken = Expect(Advance(), TokenType::Struct); ShaderAst::StructDescription description; description.name = ParseIdentifierAsName(); @@ -971,19 +982,19 @@ namespace Nz::ShaderLang switch (attributeType) { case ShaderAst::AttributeType::Cond: - HandleUniqueAttribute("cond", condition, std::move(attributeParam)); + HandleUniqueAttribute(structToken, attributeType, condition, std::move(attributeParam)); break; case ShaderAst::AttributeType::Export: - HandleUniqueAttribute("export", exported, std::move(attributeParam), true); + HandleUniqueAttribute(structToken, attributeType, exported, std::move(attributeParam), true); break; case ShaderAst::AttributeType::Layout: - HandleUniqueStringAttribute("layout", s_layoutMapping, description.layout, std::move(attributeParam)); + HandleUniqueStringAttribute(structToken, attributeType, s_layoutMapping, description.layout, std::move(attributeParam)); break; default: - throw AttributeError{ "unexpected attribute" }; + throw ParserUnexpectedAttributeError{ SourceLocation{ structToken.line, structToken.column, structToken.file }, attributeType }; } } @@ -1020,24 +1031,21 @@ namespace Nz::ShaderLang switch (attributeType) { case ShaderAst::AttributeType::Builtin: - HandleUniqueStringAttribute("builtin", s_builtinMapping, structField.builtin, std::move(arg)); + HandleUniqueStringAttribute(token, attributeType, s_builtinMapping, structField.builtin, std::move(arg)); break; case ShaderAst::AttributeType::Cond: - HandleUniqueAttribute("cond", structField.cond, std::move(arg)); + HandleUniqueAttribute(token, attributeType, structField.cond, std::move(arg)); break; case ShaderAst::AttributeType::Location: - HandleUniqueAttribute("location", structField.locationIndex, std::move(arg)); + HandleUniqueAttribute(token, attributeType, structField.locationIndex, std::move(arg)); break; default: - throw AttributeError{ "unexpected attribute" }; + throw ParserUnexpectedAttributeError{ SourceLocation{ token.line, token.column, token.file }, attributeType }; } } - - if (structField.builtin.HasValue() && structField.locationIndex.HasValue()) - throw AttributeError{ "A struct field cannot have both builtin and location attributes" }; } structField.name = ParseIdentifierAsName(); @@ -1063,7 +1071,8 @@ namespace Nz::ShaderLang // Assignation type ShaderAst::AssignType assignType; - switch (Peek().type) + const Token& token = Peek(); + switch (token.type) { case TokenType::Assign: assignType = ShaderAst::AssignType::Simple; break; case TokenType::DivideAssign: assignType = ShaderAst::AssignType::CompoundDivide; break; @@ -1074,7 +1083,7 @@ namespace Nz::ShaderLang case TokenType::PlusAssign: assignType = ShaderAst::AssignType::CompoundAdd; break; default: - throw UnexpectedToken{}; + throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file } }; } Consume(); @@ -1102,7 +1111,7 @@ namespace Nz::ShaderLang { NAZARA_USE_ANONYMOUS_NAMESPACE - Expect(Advance(), TokenType::While); + const Token& whileToken = Expect(Advance(), TokenType::While); Expect(Advance(), TokenType::OpenParenthesis); @@ -1119,11 +1128,11 @@ namespace Nz::ShaderLang switch (attributeType) { case ShaderAst::AttributeType::Unroll: - HandleUniqueStringAttribute("unroll", s_unrollModes, whileStatement->unroll, std::move(arg), std::make_optional(ShaderAst::LoopUnroll::Always)); + HandleUniqueStringAttribute(whileToken, attributeType, s_unrollModes, whileStatement->unroll, std::move(arg), std::make_optional(ShaderAst::LoopUnroll::Always)); break; default: - throw AttributeError{ "unhandled attribute for while" }; + throw ParserUnexpectedAttributeError{ SourceLocation{ whileToken.line, whileToken.column, whileToken.file }, attributeType }; } } @@ -1134,9 +1143,10 @@ namespace Nz::ShaderLang { for (;;) { - TokenType currentTokenType = Peek().type; + const Token& token = Peek(); + TokenType currentTokenType = token.type; if (currentTokenType == TokenType::EndOfStream) - throw UnexpectedToken{}; + throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type }; int tokenPrecedence = GetTokenPrecedence(currentTokenType); if (tokenPrecedence < exprPrecedence) @@ -1191,8 +1201,7 @@ namespace Nz::ShaderLang case TokenType::NotEqual: return BuildBinary(ShaderAst::BinaryType::CompNe, std::move(lhs), std::move(rhs)); case TokenType::Plus: return BuildBinary(ShaderAst::BinaryType::Add, std::move(lhs), std::move(rhs)); default: - throw UnexpectedToken{}; - + throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type }; } }(); } @@ -1326,7 +1335,7 @@ namespace Nz::ShaderLang return ParseStringExpression(); default: - throw UnexpectedToken{}; + throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type }; } } @@ -1345,7 +1354,7 @@ namespace Nz::ShaderLang auto it = s_identifierToAttributeType.find(identifier); if (it == s_identifierToAttributeType.end()) - throw UnknownAttribute{}; + throw ParserUnknownAttributeError{ SourceLocation{ identifierToken.line, identifierToken.column, identifierToken.file } }; return it->second; } @@ -1426,6 +1435,6 @@ namespace Nz::ShaderLang return {}; } - return Parse(std::string_view(reinterpret_cast(source.data()), source.size())); + return Parse(std::string_view(reinterpret_cast(source.data()), source.size()), sourcePath.generic_u8string()); } }