Shader: Add support for error messages (WIP)
This commit is contained in:
parent
f14b10baf4
commit
ebad7b5e8c
|
|
@ -4,10 +4,18 @@
|
||||||
|
|
||||||
// no header guards
|
// 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
|
#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
|
#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
|
#ifndef NAZARA_SHADERLANG_LEXER_ERROR
|
||||||
#define NAZARA_SHADERLANG_LEXER_ERROR(...) NAZARA_SHADERLANG_ERROR(L, ...)
|
#define NAZARA_SHADERLANG_LEXER_ERROR(...) NAZARA_SHADERLANG_ERROR(L, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -16,10 +24,6 @@
|
||||||
#define NAZARA_SHADERLANG_PARSER_ERROR(...) NAZARA_SHADERLANG_ERROR(P, ...)
|
#define NAZARA_SHADERLANG_PARSER_ERROR(...) NAZARA_SHADERLANG_ERROR(P, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef NAZARA_SHADERLANG_COMPILER_ERROR
|
|
||||||
#define NAZARA_SHADERLANG_COMPILER_ERROR(...) NAZARA_SHADERLANG_COMPILER_ERROR(C, ...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Lexer errors
|
// Lexer errors
|
||||||
NAZARA_SHADERLANG_LEXER_ERROR(1, BadNumber, "bad number")
|
NAZARA_SHADERLANG_LEXER_ERROR(1, BadNumber, "bad number")
|
||||||
NAZARA_SHADERLANG_LEXER_ERROR(2, NumberOutOfRange, "number is out of range")
|
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")
|
NAZARA_SHADERLANG_LEXER_ERROR(5, UnrecognizedToken, "unrecognized token")
|
||||||
|
|
||||||
// Parser errors
|
// Parser errors
|
||||||
NAZARA_SHADERLANG_PARSER_ERROR(1, AttributeError, "attribute error")
|
NAZARA_SHADERLANG_PARSER_ERROR( 1, AttributeExpectString, "attribute {} requires a string parameter", ShaderAst::AttributeType)
|
||||||
NAZARA_SHADERLANG_PARSER_ERROR(2, ExpectedToken, "expected token")
|
NAZARA_SHADERLANG_PARSER_ERROR( 2, AttributeInvalidParameter, "invalid parameter {} for attribute {}", std::string, ShaderAst::AttributeType)
|
||||||
NAZARA_SHADERLANG_PARSER_ERROR(3, DuplicateIdentifier, "duplicate identifier")
|
NAZARA_SHADERLANG_PARSER_ERROR( 3, AttributeMissingParameter, "attribute {} requires a parameter", ShaderAst::AttributeType)
|
||||||
NAZARA_SHADERLANG_PARSER_ERROR(4, DuplicateModule, "duplicate module")
|
NAZARA_SHADERLANG_PARSER_ERROR( 4, AttributeMultipleUnique, "attribute {} can only be present once", ShaderAst::AttributeType)
|
||||||
NAZARA_SHADERLANG_PARSER_ERROR(5, ReservedKeyword, "reserved keyword")
|
NAZARA_SHADERLANG_PARSER_ERROR( 5, AttributeParameterIdentifier, "attribute {} parameter can only be an identifier", ShaderAst::AttributeType)
|
||||||
NAZARA_SHADERLANG_PARSER_ERROR(6, UnknownAttribute, "unknown attribute")
|
NAZARA_SHADERLANG_PARSER_ERROR( 6, ExpectedToken, "expected token {}, got {}", ShaderLang::TokenType, ShaderLang::TokenType)
|
||||||
NAZARA_SHADERLANG_PARSER_ERROR(7, UnknownType, "unknown type")
|
NAZARA_SHADERLANG_PARSER_ERROR( 7, DuplicateIdentifier, "duplicate identifier")
|
||||||
NAZARA_SHADERLANG_PARSER_ERROR(8, UnexpectedToken, "unexpected token")
|
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
|
// 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_ERROR
|
||||||
|
#undef NAZARA_SHADERLANG_AST_ERROR
|
||||||
#undef NAZARA_SHADERLANG_COMPILER_ERROR
|
#undef NAZARA_SHADERLANG_COMPILER_ERROR
|
||||||
#undef NAZARA_SHADERLANG_LEXER_ERROR
|
#undef NAZARA_SHADERLANG_LEXER_ERROR
|
||||||
#undef NAZARA_SHADERLANG_PARSER_ERROR
|
#undef NAZARA_SHADERLANG_PARSER_ERROR
|
||||||
|
|
|
||||||
|
|
@ -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 <Nazara/Prerequisites.hpp>
|
||||||
|
#include <Nazara/Shader/Config.hpp>
|
||||||
|
#include <Nazara/Shader/ShaderLangParser.hpp>
|
||||||
|
#include <Nazara/Shader/Ast/Enums.hpp>
|
||||||
|
#include <exception>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace Nz::ShaderLang
|
||||||
|
{
|
||||||
|
struct SourceLocation
|
||||||
|
{
|
||||||
|
inline SourceLocation();
|
||||||
|
inline SourceLocation(unsigned int line, unsigned int column, std::shared_ptr<const std::string> file);
|
||||||
|
inline SourceLocation(unsigned int line, unsigned int startColumn, unsigned int endColumn, std::shared_ptr<const std::string> file);
|
||||||
|
inline SourceLocation(unsigned int startLine, unsigned int endLine, unsigned int startColumn, unsigned int endColumn, std::shared_ptr<const std::string> file);
|
||||||
|
|
||||||
|
inline bool IsValid() const;
|
||||||
|
|
||||||
|
std::shared_ptr<const std::string> 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<typename... Args> Prefix ## ErrorName ## Error(SourceLocation sourceLocation, Args&&... args) : \
|
||||||
|
BaseClass(std::move(sourceLocation), ErrorType), \
|
||||||
|
m_parameters(std::forward<Args>(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 <Nazara/Shader/ShaderLangErrorList.hpp>
|
||||||
|
|
||||||
|
#undef NAZARA_SHADERLANG_NEWERRORTYPE
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <Nazara/Shader/ShaderLangErrors.inl>
|
||||||
|
|
||||||
|
#endif // NAZARA_SHADER_SHADERLANGERRORS_HPP
|
||||||
|
|
@ -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 <Nazara/Shader/ShaderLangErrors.hpp>
|
||||||
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
||||||
|
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<const std::string> 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<const std::string> 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<const std::string> 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 <Nazara/Shader/DebugOff.hpp>
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include <Nazara/Prerequisites.hpp>
|
#include <Nazara/Prerequisites.hpp>
|
||||||
#include <Nazara/Shader/Config.hpp>
|
#include <Nazara/Shader/Config.hpp>
|
||||||
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
@ -28,35 +29,11 @@ namespace Nz::ShaderLang
|
||||||
unsigned int column;
|
unsigned int column;
|
||||||
unsigned int line;
|
unsigned int line;
|
||||||
TokenType type;
|
TokenType type;
|
||||||
|
std::shared_ptr<const std::string> file;
|
||||||
std::variant<double, long long, std::string> data;
|
std::variant<double, long long, std::string> data;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BadNumber : public std::exception
|
NAZARA_SHADER_API std::vector<Token> Tokenize(const std::string_view& str, const std::string& filePath = std::string{});
|
||||||
{
|
|
||||||
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<Token> Tokenize(const std::string_view& str);
|
|
||||||
NAZARA_SHADER_API const char* ToString(TokenType tokenType);
|
NAZARA_SHADER_API const char* ToString(TokenType tokenType);
|
||||||
NAZARA_SHADER_API std::string ToString(const std::vector<Token>& tokens, bool pretty = true);
|
NAZARA_SHADER_API std::string ToString(const std::vector<Token>& tokens, bool pretty = true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,54 +16,6 @@
|
||||||
|
|
||||||
namespace Nz::ShaderLang
|
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
|
class NAZARA_SHADER_API Parser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -142,7 +94,7 @@ namespace Nz::ShaderLang
|
||||||
Context* m_context;
|
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<Token>& tokens);
|
inline ShaderAst::ModulePtr Parse(const std::vector<Token>& tokens);
|
||||||
NAZARA_SHADER_API ShaderAst::ModulePtr ParseFromFile(const std::filesystem::path& sourcePath);
|
NAZARA_SHADER_API ShaderAst::ModulePtr ParseFromFile(const std::filesystem::path& sourcePath);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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<Token>& tokens)
|
inline ShaderAst::ModulePtr Parse(const std::vector<Token>& tokens)
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ namespace Nz::ShaderAst
|
||||||
node.expression->Visit(*this);
|
node.expression->Visit(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AstRecursiveVisitor::Visit(TypeExpression& node)
|
void AstRecursiveVisitor::Visit(TypeExpression& /*node*/)
|
||||||
{
|
{
|
||||||
/* Nothing to do */
|
/* Nothing to do */
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include <Nazara/Core/StackVector.hpp>
|
#include <Nazara/Core/StackVector.hpp>
|
||||||
#include <Nazara/Core/Hash/SHA256.hpp>
|
#include <Nazara/Core/Hash/SHA256.hpp>
|
||||||
#include <Nazara/Shader/ShaderBuilder.hpp>
|
#include <Nazara/Shader/ShaderBuilder.hpp>
|
||||||
|
#include <Nazara/Shader/ShaderLangErrors.hpp>
|
||||||
#include <Nazara/Shader/Ast/AstConstantPropagationVisitor.hpp>
|
#include <Nazara/Shader/Ast/AstConstantPropagationVisitor.hpp>
|
||||||
#include <Nazara/Shader/Ast/AstExportVisitor.hpp>
|
#include <Nazara/Shader/Ast/AstExportVisitor.hpp>
|
||||||
#include <Nazara/Shader/Ast/AstRecursiveVisitor.hpp>
|
#include <Nazara/Shader/Ast/AstRecursiveVisitor.hpp>
|
||||||
|
|
@ -420,6 +421,7 @@ namespace Nz::ShaderAst
|
||||||
{
|
{
|
||||||
if (ToSwizzleIndex(identifier[j]) != 0)
|
if (ToSwizzleIndex(identifier[j]) != 0)
|
||||||
throw AstError{ "invalid swizzle" };
|
throw AstError{ "invalid swizzle" };
|
||||||
|
//throw ShaderLang::CompilerInvalidSwizzleError{};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (swizzleComponentCount == 1)
|
if (swizzleComponentCount == 1)
|
||||||
|
|
@ -1250,6 +1252,9 @@ namespace Nz::ShaderAst
|
||||||
if (member.locationIndex.HasValue())
|
if (member.locationIndex.HasValue())
|
||||||
ComputeExprValue(member.locationIndex);
|
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 (declaredMembers.find(member.name) != declaredMembers.end())
|
||||||
{
|
{
|
||||||
if ((!member.cond.HasValue() || !member.cond.IsResultingValue()) && !m_context->options.allowPartialSanitization)
|
if ((!member.cond.HasValue() || !member.cond.IsResultingValue()) && !m_context->options.allowPartialSanitization)
|
||||||
|
|
@ -3195,7 +3200,7 @@ namespace Nz::ShaderAst
|
||||||
}
|
}
|
||||||
|
|
||||||
node.cachedExpressionType = targetType;
|
node.cachedExpressionType = targetType;
|
||||||
node.targetType = std::move(targetType);
|
node.targetType = targetType;
|
||||||
|
|
||||||
return ValidationResult::Validated;
|
return ValidationResult::Validated;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 <Nazara/Shader/ShaderLangErrors.hpp>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
||||||
|
// https://fmt.dev/latest/api.html#udt
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<Nz::ShaderAst::AttributeType> : formatter<string_view>
|
||||||
|
{
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const Nz::ShaderAst::AttributeType& p, FormatContext& ctx) -> decltype(ctx.out())
|
||||||
|
{
|
||||||
|
// TODO: Add ToString
|
||||||
|
std::string_view name = "<unhandled attribute type>";
|
||||||
|
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<string_view>::format(name, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<Nz::ShaderLang::ErrorCategory> : formatter<string_view>
|
||||||
|
{
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const Nz::ShaderLang::ErrorCategory& p, FormatContext& ctx) -> decltype(ctx.out())
|
||||||
|
{
|
||||||
|
// TODO: Add ToString
|
||||||
|
std::string_view name = "<unhandled error category>";
|
||||||
|
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<string_view>::format(name, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<Nz::ShaderLang::TokenType> : formatter<string_view>
|
||||||
|
{
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const Nz::ShaderLang::TokenType& p, FormatContext& ctx) -> decltype(ctx.out())
|
||||||
|
{
|
||||||
|
return formatter<string_view>::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 <Nazara/Shader/ShaderLangErrorList.hpp>
|
||||||
|
|
||||||
|
#undef NAZARA_SHADERLANG_NEWERRORTYPE
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
#include <Nazara/Shader/ShaderLangLexer.hpp>
|
#include <Nazara/Shader/ShaderLangLexer.hpp>
|
||||||
|
#include <Nazara/Core/Algorithm.hpp>
|
||||||
|
#include <Nazara/Shader/ShaderLangErrors.hpp>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
|
|
@ -34,7 +36,7 @@ namespace Nz::ShaderLang
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Token> Tokenize(const std::string_view& str)
|
std::vector<Token> 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
|
// 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;
|
ForceCLocale forceCLocale;
|
||||||
|
|
@ -76,16 +78,21 @@ namespace Nz::ShaderLang
|
||||||
return std::isalnum(c) || c == '_';
|
return std::isalnum(c) || c == '_';
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned int lineNumber = 0;
|
unsigned int lineNumber = 1;
|
||||||
std::size_t lastLineFeed = 0;
|
std::size_t lastLineFeed = 0;
|
||||||
std::vector<Token> tokens;
|
std::vector<Token> tokens;
|
||||||
|
|
||||||
|
std::shared_ptr<const std::string> currentFile;
|
||||||
|
if (!filePath.empty())
|
||||||
|
currentFile = std::make_shared<std::string>(filePath);
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
char c = Peek(0);
|
char c = Peek(0);
|
||||||
|
|
||||||
Token token;
|
Token token;
|
||||||
token.column = static_cast<unsigned int>(currentPos - lastLineFeed);
|
token.column = static_cast<unsigned int>(currentPos - lastLineFeed);
|
||||||
|
token.file = currentFile;
|
||||||
token.line = lineNumber;
|
token.line = lineNumber;
|
||||||
|
|
||||||
if (c == '\0')
|
if (c == '\0')
|
||||||
|
|
@ -227,7 +234,10 @@ namespace Nz::ShaderLang
|
||||||
char* end;
|
char* end;
|
||||||
double value = std::strtod(ptr, &end);
|
double value = std::strtod(ptr, &end);
|
||||||
if (end != &ptr[valueStr.size()])
|
if (end != &ptr[valueStr.size()])
|
||||||
throw BadNumber{};
|
{
|
||||||
|
unsigned int columnOffset = SafeCast<unsigned int>(end - ptr);
|
||||||
|
throw LexerBadNumberError{ SourceLocation{ token.line, token.column + columnOffset, token.file } };
|
||||||
|
}
|
||||||
|
|
||||||
token.data = value;
|
token.data = value;
|
||||||
}
|
}
|
||||||
|
|
@ -239,10 +249,11 @@ namespace Nz::ShaderLang
|
||||||
std::from_chars_result r = std::from_chars(&str[start], &str[currentPos + 1], value);
|
std::from_chars_result r = std::from_chars(&str[start], &str[currentPos + 1], value);
|
||||||
if (r.ptr != &str[currentPos + 1])
|
if (r.ptr != &str[currentPos + 1])
|
||||||
{
|
{
|
||||||
|
unsigned int columnOffset = SafeCast<unsigned int>(r.ptr - &str[start]);
|
||||||
if (r.ec == std::errc::result_out_of_range)
|
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;
|
token.data = value;
|
||||||
|
|
@ -281,7 +292,7 @@ namespace Nz::ShaderLang
|
||||||
tokenType = TokenType::LogicalOr;
|
tokenType = TokenType::LogicalOr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw UnrecognizedToken{}; //< TODO: Add BOR (a | b)
|
throw LexerUnrecognizedTokenError{ SourceLocation{ token.line, token.column, token.file } }; //< TODO: Add BOR (a | b)
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -302,7 +313,7 @@ namespace Nz::ShaderLang
|
||||||
tokenType = TokenType::LogicalAnd;
|
tokenType = TokenType::LogicalAnd;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw UnrecognizedToken{}; //< TODO: Add BAND (a & b)
|
throw LexerUnrecognizedTokenError{ SourceLocation{ token.line, token.column, token.file } }; //< TODO: Add BAND (a & b)
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -404,7 +415,7 @@ namespace Nz::ShaderLang
|
||||||
case '\0':
|
case '\0':
|
||||||
case '\n':
|
case '\n':
|
||||||
case '\r':
|
case '\r':
|
||||||
throw UnfinishedString{};
|
throw LexerUnfinishedStringError{ SourceLocation{ token.line, SafeCast<unsigned int>(currentPos - lastLineFeed), token.file } };
|
||||||
|
|
||||||
case '\\':
|
case '\\':
|
||||||
{
|
{
|
||||||
|
|
@ -418,7 +429,7 @@ namespace Nz::ShaderLang
|
||||||
case '"': character = '"'; break;
|
case '"': character = '"'; break;
|
||||||
case '\\': character = '\\'; break;
|
case '\\': character = '\\'; break;
|
||||||
default:
|
default:
|
||||||
throw UnrecognizedChar{};
|
throw LexerUnrecognizedCharError{ SourceLocation{ token.line, SafeCast<unsigned int>(currentPos - lastLineFeed), token.file } };
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -458,7 +469,7 @@ namespace Nz::ShaderLang
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw UnrecognizedToken{};
|
throw LexerUnrecognizedTokenError{ SourceLocation{ token.line, token.column, token.file } };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include <Nazara/Core/Algorithm.hpp>
|
#include <Nazara/Core/Algorithm.hpp>
|
||||||
#include <Nazara/Core/File.hpp>
|
#include <Nazara/Core/File.hpp>
|
||||||
#include <Nazara/Shader/ShaderBuilder.hpp>
|
#include <Nazara/Shader/ShaderBuilder.hpp>
|
||||||
|
#include <Nazara/Shader/ShaderLangErrors.hpp>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <Nazara/Shader/Debug.hpp>
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
@ -59,22 +60,22 @@ namespace Nz::ShaderLang
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void HandleUniqueAttribute(const std::string_view& attributeName, ShaderAst::ExpressionValue<T>& targetAttribute, ShaderAst::ExprValue::Param&& param)
|
void HandleUniqueAttribute(const Token& token, ShaderAst::AttributeType attributeType, ShaderAst::ExpressionValue<T>& targetAttribute, ShaderAst::ExprValue::Param&& param)
|
||||||
{
|
{
|
||||||
if (targetAttribute.HasValue())
|
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)
|
if (param)
|
||||||
targetAttribute = std::move(*param);
|
targetAttribute = std::move(*param);
|
||||||
else
|
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<typename T>
|
template<typename T>
|
||||||
void HandleUniqueAttribute(const std::string_view& attributeName, ShaderAst::ExpressionValue<T>& targetAttribute, ShaderAst::ExprValue::Param&& param, T defaultValue)
|
void HandleUniqueAttribute(const Token& token, ShaderAst::AttributeType attributeType, ShaderAst::ExpressionValue<T>& targetAttribute, ShaderAst::ExprValue::Param&& param, T defaultValue)
|
||||||
{
|
{
|
||||||
if (targetAttribute.HasValue())
|
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)
|
if (param)
|
||||||
targetAttribute = std::move(*param);
|
targetAttribute = std::move(*param);
|
||||||
|
|
@ -83,30 +84,30 @@ namespace Nz::ShaderLang
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void HandleUniqueStringAttribute(const std::string_view& attributeName, const std::unordered_map<std::string, T>& map, ShaderAst::ExpressionValue<T>& targetAttribute, ShaderAst::ExprValue::Param&& param, std::optional<T> defaultValue = {})
|
void HandleUniqueStringAttribute(const Token& token, ShaderAst::AttributeType attributeType, const std::unordered_map<std::string, T>& map, ShaderAst::ExpressionValue<T>& targetAttribute, ShaderAst::ExprValue::Param&& param, std::optional<T> defaultValue = {})
|
||||||
{
|
{
|
||||||
if (targetAttribute.HasValue())
|
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
|
//FIXME: This should be handled with global values at sanitization stage
|
||||||
if (param)
|
if (param)
|
||||||
{
|
{
|
||||||
const ShaderAst::ExpressionPtr& expr = *param;
|
const ShaderAst::ExpressionPtr& expr = *param;
|
||||||
if (expr->GetType() != ShaderAst::NodeType::IdentifierExpression)
|
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<ShaderAst::IdentifierExpression&>(*expr).identifier;
|
const std::string& exprStr = static_cast<ShaderAst::IdentifierExpression&>(*expr).identifier;
|
||||||
|
|
||||||
auto it = map.find(exprStr);
|
auto it = map.find(exprStr);
|
||||||
if (it == map.end())
|
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;
|
targetAttribute = it->second;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!defaultValue)
|
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();
|
targetAttribute = defaultValue.value();
|
||||||
}
|
}
|
||||||
|
|
@ -127,7 +128,10 @@ namespace Nz::ShaderLang
|
||||||
{
|
{
|
||||||
ShaderAst::StatementPtr statement = ParseRootStatement();
|
ShaderAst::StatementPtr statement = ParseRootStatement();
|
||||||
if (!m_context->module)
|
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)
|
if (!statement)
|
||||||
break;
|
break;
|
||||||
|
|
@ -155,7 +159,7 @@ namespace Nz::ShaderLang
|
||||||
const Token& Parser::Expect(const Token& token, TokenType type)
|
const Token& Parser::Expect(const Token& token, TokenType type)
|
||||||
{
|
{
|
||||||
if (token.type != type)
|
if (token.type != type)
|
||||||
throw ExpectedToken{};
|
throw ParserExpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, type, token.type };
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
@ -163,7 +167,7 @@ namespace Nz::ShaderLang
|
||||||
const Token& Parser::ExpectNot(const Token& token, TokenType type)
|
const Token& Parser::ExpectNot(const Token& token, TokenType type)
|
||||||
{
|
{
|
||||||
if (token.type == type)
|
if (token.type == type)
|
||||||
throw ExpectedToken{};
|
throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, type };
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
@ -238,9 +242,12 @@ namespace Nz::ShaderLang
|
||||||
void Parser::ParseModuleStatement(std::vector<ShaderAst::ExprValue> attributes)
|
void Parser::ParseModuleStatement(std::vector<ShaderAst::ExprValue> attributes)
|
||||||
{
|
{
|
||||||
if (m_context->parsingImportedModule)
|
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<UInt32> moduleVersion;
|
std::optional<UInt32> moduleVersion;
|
||||||
std::optional<Uuid> moduleId;
|
std::optional<Uuid> moduleId;
|
||||||
|
|
@ -253,18 +260,18 @@ namespace Nz::ShaderLang
|
||||||
{
|
{
|
||||||
// Version parsing
|
// Version parsing
|
||||||
if (moduleVersion.has_value())
|
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)
|
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;
|
const ShaderAst::ExpressionPtr& expr = *arg;
|
||||||
if (expr->GetType() != ShaderAst::NodeType::ConstantValueExpression)
|
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<ShaderAst::ConstantValueExpression&>(*expr);
|
auto& constantValue = SafeCast<ShaderAst::ConstantValueExpression&>(*expr);
|
||||||
if (ShaderAst::GetConstantType(constantValue.value) != ShaderAst::ExpressionType{ ShaderAst::PrimitiveType::String })
|
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<std::string>(constantValue.value);
|
const std::string& versionStr = std::get<std::string>(constantValue.value);
|
||||||
|
|
||||||
|
|
@ -272,7 +279,7 @@ namespace Nz::ShaderLang
|
||||||
|
|
||||||
std::smatch versionMatch;
|
std::smatch versionMatch;
|
||||||
if (!std::regex_match(versionStr, versionMatch, versionRegex))
|
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);
|
assert(versionMatch.size() == 6);
|
||||||
|
|
||||||
|
|
@ -292,36 +299,36 @@ namespace Nz::ShaderLang
|
||||||
case ShaderAst::AttributeType::Uuid:
|
case ShaderAst::AttributeType::Uuid:
|
||||||
{
|
{
|
||||||
if (moduleId.has_value())
|
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)
|
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;
|
const ShaderAst::ExpressionPtr& expr = *arg;
|
||||||
if (expr->GetType() != ShaderAst::NodeType::ConstantValueExpression)
|
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<ShaderAst::ConstantValueExpression&>(*expr);
|
auto& constantValue = SafeCast<ShaderAst::ConstantValueExpression&>(*expr);
|
||||||
if (ShaderAst::GetConstantType(constantValue.value) != ShaderAst::ExpressionType{ ShaderAst::PrimitiveType::String })
|
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<std::string>(constantValue.value);
|
const std::string& uuidStr = std::get<std::string>(constantValue.value);
|
||||||
|
|
||||||
Uuid uuid = Uuid::FromString(uuidStr);
|
Uuid uuid = Uuid::FromString(uuidStr);
|
||||||
if (uuid.IsNull())
|
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;
|
moduleId = uuid;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw AttributeError{ "unhandled attribute for module" };
|
throw ParserUnexpectedAttributeError{ SourceLocation{ moduleToken.line, moduleToken.column, moduleToken.file }, attributeType };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!moduleVersion.has_value())
|
if (!moduleVersion.has_value())
|
||||||
throw AttributeError{ "missing module version" };
|
throw ParserMissingAttributeError{ SourceLocation{ moduleToken.line, moduleToken.column, moduleToken.file } };
|
||||||
|
|
||||||
if (!moduleId)
|
if (!moduleId)
|
||||||
moduleId = Uuid::Generate();
|
moduleId = Uuid::Generate();
|
||||||
|
|
@ -340,7 +347,10 @@ namespace Nz::ShaderLang
|
||||||
{
|
{
|
||||||
ShaderAst::StatementPtr statement = ParseRootStatement();
|
ShaderAst::StatementPtr statement = ParseRootStatement();
|
||||||
if (!statement)
|
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));
|
module->rootNode->statements.push_back(std::move(statement));
|
||||||
}
|
}
|
||||||
|
|
@ -364,7 +374,7 @@ namespace Nz::ShaderLang
|
||||||
Expect(Advance(), TokenType::Semicolon);
|
Expect(Advance(), TokenType::Semicolon);
|
||||||
|
|
||||||
if (m_context->module)
|
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);
|
m_context->module = std::move(module);
|
||||||
}
|
}
|
||||||
|
|
@ -464,7 +474,8 @@ namespace Nz::ShaderLang
|
||||||
{
|
{
|
||||||
Expect(Advance(), TokenType::Const);
|
Expect(Advance(), TokenType::Const);
|
||||||
|
|
||||||
switch (Peek().type)
|
const Token& token = Peek();
|
||||||
|
switch (token.type)
|
||||||
{
|
{
|
||||||
case TokenType::Identifier:
|
case TokenType::Identifier:
|
||||||
{
|
{
|
||||||
|
|
@ -486,7 +497,7 @@ namespace Nz::ShaderLang
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
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
|
NAZARA_USE_ANONYMOUS_NAMESPACE
|
||||||
|
|
||||||
Expect(Advance(), TokenType::External);
|
const Token& externalToken = Expect(Advance(), TokenType::External);
|
||||||
Expect(Advance(), TokenType::OpenCurlyBracket);
|
Expect(Advance(), TokenType::OpenCurlyBracket);
|
||||||
|
|
||||||
std::unique_ptr<ShaderAst::DeclareExternalStatement> externalStatement = std::make_unique<ShaderAst::DeclareExternalStatement>();
|
std::unique_ptr<ShaderAst::DeclareExternalStatement> externalStatement = std::make_unique<ShaderAst::DeclareExternalStatement>();
|
||||||
|
|
@ -514,15 +525,15 @@ namespace Nz::ShaderLang
|
||||||
switch (attributeType)
|
switch (attributeType)
|
||||||
{
|
{
|
||||||
case ShaderAst::AttributeType::Cond:
|
case ShaderAst::AttributeType::Cond:
|
||||||
HandleUniqueAttribute("cond", condition, std::move(arg));
|
HandleUniqueAttribute(externalToken, attributeType, condition, std::move(arg));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ShaderAst::AttributeType::Set:
|
case ShaderAst::AttributeType::Set:
|
||||||
HandleUniqueAttribute("set", externalStatement->bindingSet, std::move(arg));
|
HandleUniqueAttribute(externalToken, attributeType, externalStatement->bindingSet, std::move(arg));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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)
|
switch (attributeType)
|
||||||
{
|
{
|
||||||
case ShaderAst::AttributeType::Binding:
|
case ShaderAst::AttributeType::Binding:
|
||||||
HandleUniqueAttribute("binding", extVar.bindingIndex, std::move(arg));
|
HandleUniqueAttribute(externalToken, attributeType, extVar.bindingIndex, std::move(arg));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ShaderAst::AttributeType::Set:
|
case ShaderAst::AttributeType::Set:
|
||||||
HandleUniqueAttribute("set", extVar.bindingSet, std::move(arg));
|
HandleUniqueAttribute(externalToken, attributeType, extVar.bindingSet, std::move(arg));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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
|
NAZARA_USE_ANONYMOUS_NAMESPACE
|
||||||
|
|
||||||
Expect(Advance(), TokenType::For);
|
const Token& forToken = Expect(Advance(), TokenType::For);
|
||||||
|
|
||||||
std::string varName = ParseIdentifierAsName();
|
std::string varName = ParseIdentifierAsName();
|
||||||
|
|
||||||
|
|
@ -618,11 +629,11 @@ namespace Nz::ShaderLang
|
||||||
switch (attributeType)
|
switch (attributeType)
|
||||||
{
|
{
|
||||||
case ShaderAst::AttributeType::Unroll:
|
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;
|
break;
|
||||||
|
|
||||||
default:
|
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)
|
switch (attributeType)
|
||||||
{
|
{
|
||||||
case ShaderAst::AttributeType::Unroll:
|
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;
|
break;
|
||||||
|
|
||||||
default:
|
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
|
NAZARA_USE_ANONYMOUS_NAMESPACE
|
||||||
|
|
||||||
Expect(Advance(), TokenType::FunctionDeclaration);
|
const auto& funcToken = Expect(Advance(), TokenType::FunctionDeclaration);
|
||||||
|
|
||||||
std::string functionName = ParseIdentifierAsName();
|
std::string functionName = ParseIdentifierAsName();
|
||||||
|
|
||||||
|
|
@ -706,27 +717,27 @@ namespace Nz::ShaderLang
|
||||||
switch (attributeType)
|
switch (attributeType)
|
||||||
{
|
{
|
||||||
case ShaderAst::AttributeType::Cond:
|
case ShaderAst::AttributeType::Cond:
|
||||||
HandleUniqueAttribute("cond", condition, std::move(attributeParam));
|
HandleUniqueAttribute(funcToken, attributeType, condition, std::move(attributeParam));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ShaderAst::AttributeType::Entry:
|
case ShaderAst::AttributeType::Entry:
|
||||||
HandleUniqueStringAttribute("entry", s_entryPoints, func->entryStage, std::move(attributeParam));
|
HandleUniqueStringAttribute(funcToken, attributeType, s_entryPoints, func->entryStage, std::move(attributeParam));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ShaderAst::AttributeType::Export:
|
case ShaderAst::AttributeType::Export:
|
||||||
HandleUniqueAttribute("export", func->isExported, std::move(attributeParam), true);
|
HandleUniqueAttribute(funcToken, attributeType, func->isExported, std::move(attributeParam), true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ShaderAst::AttributeType::DepthWrite:
|
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;
|
break;
|
||||||
|
|
||||||
case ShaderAst::AttributeType::EarlyFragmentTests:
|
case ShaderAst::AttributeType::EarlyFragmentTests:
|
||||||
HandleUniqueAttribute("early_fragment_tests", func->earlyFragmentTests, std::move(attributeParam), true);
|
HandleUniqueAttribute(funcToken, attributeType, func->earlyFragmentTests, std::move(attributeParam), true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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:
|
case TokenType::Alias:
|
||||||
if (!attributes.empty())
|
if (!attributes.empty())
|
||||||
throw UnexpectedToken{};
|
throw ParserUnexpectedTokenError{ SourceLocation{ nextToken.line, nextToken.column, nextToken.file }, nextToken.type };
|
||||||
|
|
||||||
return ParseAliasDeclaration();
|
return ParseAliasDeclaration();
|
||||||
|
|
||||||
case TokenType::Const:
|
case TokenType::Const:
|
||||||
if (!attributes.empty())
|
if (!attributes.empty())
|
||||||
throw UnexpectedToken{};
|
throw ParserUnexpectedTokenError{ SourceLocation{ nextToken.line, nextToken.column, nextToken.file }, nextToken.type };
|
||||||
|
|
||||||
return ParseConstStatement();
|
return ParseConstStatement();
|
||||||
|
|
||||||
case TokenType::EndOfStream:
|
case TokenType::EndOfStream:
|
||||||
if (!attributes.empty())
|
if (!attributes.empty())
|
||||||
throw UnexpectedToken{};
|
throw ParserUnexpectedTokenError{ SourceLocation{ nextToken.line, nextToken.column, nextToken.file }, nextToken.type };
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
|
@ -824,7 +835,7 @@ namespace Nz::ShaderLang
|
||||||
|
|
||||||
case TokenType::Import:
|
case TokenType::Import:
|
||||||
if (!attributes.empty())
|
if (!attributes.empty())
|
||||||
throw UnexpectedToken{};
|
throw ParserUnexpectedTokenError{ SourceLocation{ nextToken.line, nextToken.column, nextToken.file }, nextToken.type };
|
||||||
|
|
||||||
return ParseImportStatement();
|
return ParseImportStatement();
|
||||||
|
|
||||||
|
|
@ -839,7 +850,7 @@ namespace Nz::ShaderLang
|
||||||
case TokenType::Option:
|
case TokenType::Option:
|
||||||
{
|
{
|
||||||
if (!attributes.empty())
|
if (!attributes.empty())
|
||||||
throw UnexpectedToken{};
|
throw ParserUnexpectedTokenError{ SourceLocation{ nextToken.line, nextToken.column, nextToken.file }, nextToken.type };
|
||||||
|
|
||||||
return ParseOptionDeclaration();
|
return ParseOptionDeclaration();
|
||||||
}
|
}
|
||||||
|
|
@ -851,7 +862,7 @@ namespace Nz::ShaderLang
|
||||||
return ParseStructDeclaration(std::move(attributes));
|
return ParseStructDeclaration(std::move(attributes));
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw UnexpectedToken{};
|
throw ParserUnexpectedTokenError{ SourceLocation{ nextToken.line, nextToken.column, nextToken.file }, nextToken.type };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -866,14 +877,14 @@ namespace Nz::ShaderLang
|
||||||
{
|
{
|
||||||
case TokenType::Const:
|
case TokenType::Const:
|
||||||
if (!attributes.empty())
|
if (!attributes.empty())
|
||||||
throw UnexpectedToken{};
|
throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type };
|
||||||
|
|
||||||
statement = ParseConstStatement();
|
statement = ParseConstStatement();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TokenType::Discard:
|
case TokenType::Discard:
|
||||||
if (!attributes.empty())
|
if (!attributes.empty())
|
||||||
throw UnexpectedToken{};
|
throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type };
|
||||||
|
|
||||||
statement = ParseDiscardStatement();
|
statement = ParseDiscardStatement();
|
||||||
break;
|
break;
|
||||||
|
|
@ -885,14 +896,14 @@ namespace Nz::ShaderLang
|
||||||
|
|
||||||
case TokenType::Let:
|
case TokenType::Let:
|
||||||
if (!attributes.empty())
|
if (!attributes.empty())
|
||||||
throw UnexpectedToken{};
|
throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type };
|
||||||
|
|
||||||
statement = ParseVariableDeclaration();
|
statement = ParseVariableDeclaration();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TokenType::Identifier:
|
case TokenType::Identifier:
|
||||||
if (!attributes.empty())
|
if (!attributes.empty())
|
||||||
throw UnexpectedToken{};
|
throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type };
|
||||||
|
|
||||||
statement = ShaderBuilder::ExpressionStatement(ParseVariableAssignation());
|
statement = ShaderBuilder::ExpressionStatement(ParseVariableAssignation());
|
||||||
Expect(Advance(), TokenType::Semicolon);
|
Expect(Advance(), TokenType::Semicolon);
|
||||||
|
|
@ -900,7 +911,7 @@ namespace Nz::ShaderLang
|
||||||
|
|
||||||
case TokenType::If:
|
case TokenType::If:
|
||||||
if (!attributes.empty())
|
if (!attributes.empty())
|
||||||
throw UnexpectedToken{};
|
throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type };
|
||||||
|
|
||||||
statement = ParseBranchStatement();
|
statement = ParseBranchStatement();
|
||||||
break;
|
break;
|
||||||
|
|
@ -912,7 +923,7 @@ namespace Nz::ShaderLang
|
||||||
|
|
||||||
case TokenType::Return:
|
case TokenType::Return:
|
||||||
if (!attributes.empty())
|
if (!attributes.empty())
|
||||||
throw UnexpectedToken{};
|
throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type };
|
||||||
|
|
||||||
statement = ParseReturnStatement();
|
statement = ParseReturnStatement();
|
||||||
break;
|
break;
|
||||||
|
|
@ -923,7 +934,7 @@ namespace Nz::ShaderLang
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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
|
while (!statement); //< small trick to repeat parsing once we got attributes
|
||||||
|
|
@ -958,7 +969,7 @@ namespace Nz::ShaderLang
|
||||||
{
|
{
|
||||||
NAZARA_USE_ANONYMOUS_NAMESPACE
|
NAZARA_USE_ANONYMOUS_NAMESPACE
|
||||||
|
|
||||||
Expect(Advance(), TokenType::Struct);
|
const auto& structToken = Expect(Advance(), TokenType::Struct);
|
||||||
|
|
||||||
ShaderAst::StructDescription description;
|
ShaderAst::StructDescription description;
|
||||||
description.name = ParseIdentifierAsName();
|
description.name = ParseIdentifierAsName();
|
||||||
|
|
@ -971,19 +982,19 @@ namespace Nz::ShaderLang
|
||||||
switch (attributeType)
|
switch (attributeType)
|
||||||
{
|
{
|
||||||
case ShaderAst::AttributeType::Cond:
|
case ShaderAst::AttributeType::Cond:
|
||||||
HandleUniqueAttribute("cond", condition, std::move(attributeParam));
|
HandleUniqueAttribute(structToken, attributeType, condition, std::move(attributeParam));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ShaderAst::AttributeType::Export:
|
case ShaderAst::AttributeType::Export:
|
||||||
HandleUniqueAttribute("export", exported, std::move(attributeParam), true);
|
HandleUniqueAttribute(structToken, attributeType, exported, std::move(attributeParam), true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ShaderAst::AttributeType::Layout:
|
case ShaderAst::AttributeType::Layout:
|
||||||
HandleUniqueStringAttribute("layout", s_layoutMapping, description.layout, std::move(attributeParam));
|
HandleUniqueStringAttribute(structToken, attributeType, s_layoutMapping, description.layout, std::move(attributeParam));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw AttributeError{ "unexpected attribute" };
|
throw ParserUnexpectedAttributeError{ SourceLocation{ structToken.line, structToken.column, structToken.file }, attributeType };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1020,24 +1031,21 @@ namespace Nz::ShaderLang
|
||||||
switch (attributeType)
|
switch (attributeType)
|
||||||
{
|
{
|
||||||
case ShaderAst::AttributeType::Builtin:
|
case ShaderAst::AttributeType::Builtin:
|
||||||
HandleUniqueStringAttribute("builtin", s_builtinMapping, structField.builtin, std::move(arg));
|
HandleUniqueStringAttribute(token, attributeType, s_builtinMapping, structField.builtin, std::move(arg));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ShaderAst::AttributeType::Cond:
|
case ShaderAst::AttributeType::Cond:
|
||||||
HandleUniqueAttribute("cond", structField.cond, std::move(arg));
|
HandleUniqueAttribute(token, attributeType, structField.cond, std::move(arg));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ShaderAst::AttributeType::Location:
|
case ShaderAst::AttributeType::Location:
|
||||||
HandleUniqueAttribute("location", structField.locationIndex, std::move(arg));
|
HandleUniqueAttribute(token, attributeType, structField.locationIndex, std::move(arg));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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();
|
structField.name = ParseIdentifierAsName();
|
||||||
|
|
@ -1063,7 +1071,8 @@ namespace Nz::ShaderLang
|
||||||
// Assignation type
|
// Assignation type
|
||||||
ShaderAst::AssignType assignType;
|
ShaderAst::AssignType assignType;
|
||||||
|
|
||||||
switch (Peek().type)
|
const Token& token = Peek();
|
||||||
|
switch (token.type)
|
||||||
{
|
{
|
||||||
case TokenType::Assign: assignType = ShaderAst::AssignType::Simple; break;
|
case TokenType::Assign: assignType = ShaderAst::AssignType::Simple; break;
|
||||||
case TokenType::DivideAssign: assignType = ShaderAst::AssignType::CompoundDivide; break;
|
case TokenType::DivideAssign: assignType = ShaderAst::AssignType::CompoundDivide; break;
|
||||||
|
|
@ -1074,7 +1083,7 @@ namespace Nz::ShaderLang
|
||||||
case TokenType::PlusAssign: assignType = ShaderAst::AssignType::CompoundAdd; break;
|
case TokenType::PlusAssign: assignType = ShaderAst::AssignType::CompoundAdd; break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw UnexpectedToken{};
|
throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file } };
|
||||||
}
|
}
|
||||||
|
|
||||||
Consume();
|
Consume();
|
||||||
|
|
@ -1102,7 +1111,7 @@ namespace Nz::ShaderLang
|
||||||
{
|
{
|
||||||
NAZARA_USE_ANONYMOUS_NAMESPACE
|
NAZARA_USE_ANONYMOUS_NAMESPACE
|
||||||
|
|
||||||
Expect(Advance(), TokenType::While);
|
const Token& whileToken = Expect(Advance(), TokenType::While);
|
||||||
|
|
||||||
Expect(Advance(), TokenType::OpenParenthesis);
|
Expect(Advance(), TokenType::OpenParenthesis);
|
||||||
|
|
||||||
|
|
@ -1119,11 +1128,11 @@ namespace Nz::ShaderLang
|
||||||
switch (attributeType)
|
switch (attributeType)
|
||||||
{
|
{
|
||||||
case ShaderAst::AttributeType::Unroll:
|
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;
|
break;
|
||||||
|
|
||||||
default:
|
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 (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
TokenType currentTokenType = Peek().type;
|
const Token& token = Peek();
|
||||||
|
TokenType currentTokenType = token.type;
|
||||||
if (currentTokenType == TokenType::EndOfStream)
|
if (currentTokenType == TokenType::EndOfStream)
|
||||||
throw UnexpectedToken{};
|
throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type };
|
||||||
|
|
||||||
int tokenPrecedence = GetTokenPrecedence(currentTokenType);
|
int tokenPrecedence = GetTokenPrecedence(currentTokenType);
|
||||||
if (tokenPrecedence < exprPrecedence)
|
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::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));
|
case TokenType::Plus: return BuildBinary(ShaderAst::BinaryType::Add, std::move(lhs), std::move(rhs));
|
||||||
default:
|
default:
|
||||||
throw UnexpectedToken{};
|
throw ParserUnexpectedTokenError{ SourceLocation{ token.line, token.column, token.file }, token.type };
|
||||||
|
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
}
|
}
|
||||||
|
|
@ -1326,7 +1335,7 @@ namespace Nz::ShaderLang
|
||||||
return ParseStringExpression();
|
return ParseStringExpression();
|
||||||
|
|
||||||
default:
|
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);
|
auto it = s_identifierToAttributeType.find(identifier);
|
||||||
if (it == s_identifierToAttributeType.end())
|
if (it == s_identifierToAttributeType.end())
|
||||||
throw UnknownAttribute{};
|
throw ParserUnknownAttributeError{ SourceLocation{ identifierToken.line, identifierToken.column, identifierToken.file } };
|
||||||
|
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
@ -1426,6 +1435,6 @@ namespace Nz::ShaderLang
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return Parse(std::string_view(reinterpret_cast<const char*>(source.data()), source.size()));
|
return Parse(std::string_view(reinterpret_cast<const char*>(source.data()), source.size()), sourcePath.generic_u8string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue