Shader: Add module statement
This commit is contained in:
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user