Shader: Add module statement

This commit is contained in:
Jérôme Leclercq
2022-03-01 19:36:18 +01:00
parent ad892dfb43
commit 99e07e6e1e
56 changed files with 418 additions and 123 deletions

View File

@@ -7,6 +7,7 @@
#include <Nazara/Core/File.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <cassert>
#include <regex>
#include <Nazara/Shader/Debug.hpp>
namespace Nz::ShaderLang
@@ -29,6 +30,7 @@ namespace Nz::ShaderLang
{ "entry", ShaderAst::AttributeType::Entry },
{ "layout", ShaderAst::AttributeType::Layout },
{ "location", ShaderAst::AttributeType::Location },
{ "nzsl_version", ShaderAst::AttributeType::LangVersion },
{ "set", ShaderAst::AttributeType::Set },
{ "unroll", ShaderAst::AttributeType::Unroll },
};
@@ -106,18 +108,24 @@ namespace Nz::ShaderLang
}
}
ShaderAst::StatementPtr Parser::Parse(const std::vector<Token>& tokens)
ShaderAst::ModulePtr Parser::Parse(const std::vector<Token>& tokens)
{
Context context;
context.tokenCount = tokens.size();
context.tokens = tokens.data();
context.root = std::make_unique<ShaderAst::MultiStatement>();
m_context = &context;
std::vector<ShaderAst::ExprValue> attributes;
auto EnsureModule = [this]() -> ShaderAst::Module&
{
if (!m_context->module)
throw UnexpectedToken{ "unexpected token before module declaration" };
return *m_context->module;
};
bool reachedEndOfStream = false;
while (!reachedEndOfStream)
{
@@ -125,11 +133,14 @@ namespace Nz::ShaderLang
switch (nextToken.type)
{
case TokenType::Const:
{
if (!attributes.empty())
throw UnexpectedToken{};
context.root->statements.push_back(ParseConstStatement());
const auto& module = EnsureModule();
module.rootNode->statements.push_back(ParseConstStatement());
break;
}
case TokenType::EndOfStream:
if (!attributes.empty())
@@ -139,38 +150,58 @@ namespace Nz::ShaderLang
break;
case TokenType::External:
context.root->statements.push_back(ParseExternalBlock(std::move(attributes)));
{
const auto& module = EnsureModule();
module.rootNode->statements.push_back(ParseExternalBlock(std::move(attributes)));
attributes.clear();
break;
}
case TokenType::OpenSquareBracket:
assert(attributes.empty());
attributes = ParseAttributes();
break;
case TokenType::Module:
if (attributes.empty())
throw UnexpectedToken{};
ParseModuleStatement(std::move(attributes));
attributes.clear();
break;
case TokenType::Option:
{
if (!attributes.empty())
throw UnexpectedToken{};
context.root->statements.push_back(ParseOptionDeclaration());
const auto& module = EnsureModule();
module.rootNode->statements.push_back(ParseOptionDeclaration());
break;
}
case TokenType::FunctionDeclaration:
context.root->statements.push_back(ParseFunctionDeclaration(std::move(attributes)));
{
const auto& module = EnsureModule();
module.rootNode->statements.push_back(ParseFunctionDeclaration(std::move(attributes)));
attributes.clear();
break;
}
case TokenType::Struct:
context.root->statements.push_back(ParseStructDeclaration(std::move(attributes)));
{
const auto& module = EnsureModule();
module.rootNode->statements.push_back(ParseStructDeclaration(std::move(attributes)));
attributes.clear();
break;
}
default:
throw UnexpectedToken{};
}
}
return std::move(context.root);
return std::move(context.module);
}
const Token& Parser::Advance()
@@ -604,6 +635,74 @@ namespace Nz::ShaderLang
return { parameterName, std::move(parameterType) };
}
void Parser::ParseModuleStatement(std::vector<ShaderAst::ExprValue> attributes)
{
Expect(Advance(), TokenType::Module);
if (m_context->module)
throw DuplicateModule{ "you must set one module statement per file" };
std::optional<UInt32> moduleVersion;
for (auto&& [attributeType, arg] : attributes)
{
switch (attributeType)
{
case ShaderAst::AttributeType::LangVersion:
{
// Version parsing
if (moduleVersion.has_value())
throw AttributeError{ "attribute " + std::string("nzsl_version") + " must be present once" };
if (!arg)
throw AttributeError{ "attribute " + std::string("nzsl_version") + " requires a parameter"};
const ShaderAst::ExpressionPtr& expr = *arg;
if (expr->GetType() != ShaderAst::NodeType::ConstantValueExpression)
throw AttributeError{ "attribute " + std::string("nzsl_version") + " expect a single string parameter" };
auto& constantValue = SafeCast<ShaderAst::ConstantValueExpression&>(*expr);
if (ShaderAst::GetExpressionType(constantValue.value) != ShaderAst::ExpressionType{ ShaderAst::PrimitiveType::String })
throw AttributeError{ "attribute " + std::string("nzsl_version") + " expect a single string parameter" };
const std::string& versionStr = std::get<std::string>(constantValue.value);
std::regex versionRegex(R"(^(\d+)(\.(\d+)(\.(\d+))?)?$)", std::regex::ECMAScript);
std::smatch versionMatch;
if (!std::regex_match(versionStr, versionMatch, versionRegex))
throw AttributeError("invalid version for attribute nzsl");
assert(versionMatch.size() == 6);
std::uint32_t version = 0;
version += std::stoi(versionMatch[1]) * 100;
if (versionMatch.length(3) > 0)
version += std::stoi(versionMatch[3]) * 10;
if (versionMatch.length(5) > 0)
version += std::stoi(versionMatch[5]) * 1;
moduleVersion = version;
break;
}
default:
throw AttributeError{ "unhandled attribute for module" };
}
}
if (!moduleVersion.has_value())
throw AttributeError{ "missing module version" };
m_context->module = std::make_shared<ShaderAst::Module>();
m_context->module->rootNode = ShaderBuilder::MultiStatement();
m_context->module->shaderLangVersion = *moduleVersion;
Expect(Advance(), TokenType::Semicolon);
}
ShaderAst::StatementPtr Parser::ParseOptionDeclaration()
{
Expect(Advance(), TokenType::Option);
@@ -1132,11 +1231,20 @@ namespace Nz::ShaderLang
case TokenType::OpenParenthesis:
return ParseParenthesisExpression();
case TokenType::StringValue:
return ParseStringExpression();
default:
throw UnexpectedToken{};
}
}
ShaderAst::ExpressionPtr Parser::ParseStringExpression()
{
const Token& litteralToken = Expect(Advance(), TokenType::StringValue);
return ShaderBuilder::Constant(std::get<std::string>(litteralToken.data));
}
ShaderAst::AttributeType Parser::ParseIdentifierAsAttributeType()
{
const Token& identifierToken = Expect(Advance(), TokenType::Identifier);
@@ -1192,7 +1300,7 @@ namespace Nz::ShaderLang
}
}
ShaderAst::StatementPtr ParseFromFile(const std::filesystem::path& sourcePath)
ShaderAst::ModulePtr ParseFromFile(const std::filesystem::path& sourcePath)
{
File file(sourcePath);
if (!file.Open(OpenMode::ReadOnly | OpenMode::Text))