From 012712b8d08a7cba8be5e67f10c622bc5175a2db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Mon, 7 Mar 2022 19:20:14 +0100 Subject: [PATCH] WIP --- include/Nazara/Shader/Ast/AstCompare.hpp | 1 + include/Nazara/Shader/Ast/AstCompare.inl | 17 + include/Nazara/Shader/Ast/Enums.hpp | 1 + include/Nazara/Shader/Ast/Module.hpp | 14 +- include/Nazara/Shader/Ast/Module.inl | 4 +- include/Nazara/Shader/Ast/Nodes.hpp | 5 - include/Nazara/Shader/Ast/SanitizeVisitor.hpp | 23 +- src/Nazara/Shader/Ast/AstCloner.cpp | 5 - src/Nazara/Shader/Ast/AstCompare.cpp | 10 - src/Nazara/Shader/Ast/AstSerializer.cpp | 5 - src/Nazara/Shader/Ast/SanitizeVisitor.cpp | 578 +++++++++--------- src/Nazara/Shader/ShaderLangParser.cpp | 60 +- tests/Engine/Core/UuidTest.cpp | 2 + 13 files changed, 398 insertions(+), 327 deletions(-) delete mode 100644 src/Nazara/Shader/Ast/AstCompare.cpp diff --git a/include/Nazara/Shader/Ast/AstCompare.hpp b/include/Nazara/Shader/Ast/AstCompare.hpp index 5ec128ff9..e7c4dd2fc 100644 --- a/include/Nazara/Shader/Ast/AstCompare.hpp +++ b/include/Nazara/Shader/Ast/AstCompare.hpp @@ -17,6 +17,7 @@ namespace Nz::ShaderAst { inline bool Compare(const Expression& lhs, const Expression& rhs); inline bool Compare(const Module& lhs, const Module& rhs); + inline bool Compare(const Module::ImportedModule& lhs, const Module::ImportedModule& rhs); inline bool Compare(const Module::Metadata& lhs, const Module::Metadata& rhs); inline bool Compare(const Statement& lhs, const Statement& rhs); diff --git a/include/Nazara/Shader/Ast/AstCompare.inl b/include/Nazara/Shader/Ast/AstCompare.inl index 86e2a73b3..e6ae5bb1a 100644 --- a/include/Nazara/Shader/Ast/AstCompare.inl +++ b/include/Nazara/Shader/Ast/AstCompare.inl @@ -31,12 +31,29 @@ namespace Nz::ShaderAst if (!Compare(*lhs.metadata, *rhs.metadata)) return false; + if (!Compare(lhs.importedModules, rhs.importedModules)) + return false; + if (!Compare(*lhs.rootNode, *rhs.rootNode)) return false; return true; } + bool Compare(const Module::ImportedModule& lhs, const Module::ImportedModule& rhs) + { + if (!Compare(lhs.identifier, rhs.identifier)) + return false; + + if (!Compare(lhs.dependencies, rhs.dependencies)) + return false; + + if (!Compare(*lhs.module, *rhs.module)) + return false; + + return false; + } + bool Compare(const Module::Metadata& lhs, const Module::Metadata& rhs) { if (!Compare(lhs.moduleId, rhs.moduleId)) diff --git a/include/Nazara/Shader/Ast/Enums.hpp b/include/Nazara/Shader/Ast/Enums.hpp index daa0b68e7..ff6939ae8 100644 --- a/include/Nazara/Shader/Ast/Enums.hpp +++ b/include/Nazara/Shader/Ast/Enums.hpp @@ -39,6 +39,7 @@ namespace Nz LangVersion, //< NZSL version - has argument version string Set, //< Binding set (external var only) - has argument index Unroll, //< Unroll (for/for each only) - has argument mode + Uuid, //< Uuid (module only) - has argument string }; enum class BinaryType diff --git a/include/Nazara/Shader/Ast/Module.hpp b/include/Nazara/Shader/Ast/Module.hpp index d735ffb1b..9c9a2ecf3 100644 --- a/include/Nazara/Shader/Ast/Module.hpp +++ b/include/Nazara/Shader/Ast/Module.hpp @@ -24,7 +24,7 @@ namespace Nz::ShaderAst public: struct Metadata; - inline Module(UInt32 shaderLangVersion); + inline Module(UInt32 shaderLangVersion, const Uuid& moduleId = Uuid::Generate()); inline Module(std::shared_ptr metadata); inline Module(std::shared_ptr metadata, MultiStatementPtr rootNode); Module(const Module&) = default; @@ -34,14 +34,22 @@ namespace Nz::ShaderAst Module& operator=(const Module&) = default; Module& operator=(Module&&) noexcept = default; - std::shared_ptr metadata; - MultiStatementPtr rootNode; + struct ImportedModule + { + std::string identifier; + std::vector dependencies; + ModulePtr module; + }; struct Metadata { UInt32 shaderLangVersion; Uuid moduleId; }; + + std::shared_ptr metadata; + std::vector importedModules; + MultiStatementPtr rootNode; }; } diff --git a/include/Nazara/Shader/Ast/Module.inl b/include/Nazara/Shader/Ast/Module.inl index 79e9ab898..2ec23107d 100644 --- a/include/Nazara/Shader/Ast/Module.inl +++ b/include/Nazara/Shader/Ast/Module.inl @@ -8,10 +8,10 @@ namespace Nz::ShaderAst { - inline Module::Module(UInt32 shaderLangVersion) + inline Module::Module(UInt32 shaderLangVersion, const Uuid& uuid) { auto mutMetadata = std::make_shared(); - mutMetadata->moduleId = Uuid::Generate(); + mutMetadata->moduleId = uuid; mutMetadata->shaderLangVersion = shaderLangVersion; metadata = std::move(mutMetadata); diff --git a/include/Nazara/Shader/Ast/Nodes.hpp b/include/Nazara/Shader/Ast/Nodes.hpp index 2b74767c2..47e4f2f95 100644 --- a/include/Nazara/Shader/Ast/Nodes.hpp +++ b/include/Nazara/Shader/Ast/Nodes.hpp @@ -250,7 +250,6 @@ namespace Nz::ShaderAst void Visit(AstStatementVisitor& visitor) override; std::optional constIndex; - std::optional hidden; std::string name; ExpressionPtr expression; ExpressionValue type; @@ -270,7 +269,6 @@ namespace Nz::ShaderAst ExpressionValue type; }; - std::optional hidden; std::vector externalVars; ExpressionValue bindingSet; }; @@ -288,7 +286,6 @@ namespace Nz::ShaderAst }; std::optional funcIndex; - std::optional hidden; std::string name; std::vector parameters; std::vector statements; @@ -304,7 +301,6 @@ namespace Nz::ShaderAst void Visit(AstStatementVisitor& visitor) override; std::optional optIndex; - std::optional hidden; std::string optName; ExpressionPtr defaultValue; ExpressionValue optType; @@ -316,7 +312,6 @@ namespace Nz::ShaderAst void Visit(AstStatementVisitor& visitor) override; std::optional structIndex; - std::optional hidden; ExpressionValue isExported; StructDescription description; }; diff --git a/include/Nazara/Shader/Ast/SanitizeVisitor.hpp b/include/Nazara/Shader/Ast/SanitizeVisitor.hpp index 743abff23..343bb7bff 100644 --- a/include/Nazara/Shader/Ast/SanitizeVisitor.hpp +++ b/include/Nazara/Shader/Ast/SanitizeVisitor.hpp @@ -57,8 +57,12 @@ namespace Nz::ShaderAst }; private: + struct CurrentFunctionData; + struct Environment; struct FunctionData; struct Identifier; + template struct IdentifierData; + struct Scope; using AstCloner::CloneExpression; ExpressionValue CloneType(const ExpressionValue& exprType) override; @@ -97,6 +101,8 @@ namespace Nz::ShaderAst const Identifier* FindIdentifier(const std::string_view& identifierName) const; template const Identifier* FindIdentifier(const std::string_view& identifierName, F&& functor) const; + const Identifier* FindIdentifier(const Environment& environment, const std::string_view& identifierName) const; + template const Identifier* FindIdentifier(const Environment& environment, const std::string_view& identifierName, F&& functor) const; TypeParameter FindTypeParameter(const std::string_view& identifierName) const; Expression& MandatoryExpr(const ExpressionPtr& node) const; @@ -114,13 +120,14 @@ namespace Nz::ShaderAst void PropagateFunctionFlags(std::size_t funcIndex, FunctionFlags flags, Bitset<>& seen); void RegisterBuiltin(); - std::size_t RegisterConstant(std::string name, ConstantValue value, bool hidden = false, std::optional index = {}); - std::size_t RegisterFunction(std::string name, FunctionData funcData, bool hidden = false, std::optional index = {}); - std::size_t RegisterIntrinsic(std::string name, IntrinsicType type, bool hidden = false, std::optional index = {}); - std::size_t RegisterStruct(std::string name, StructDescription* description, bool hidden = false, std::optional index = {}); - std::size_t RegisterType(std::string name, ExpressionType expressionType, bool hidden = false, std::optional index = {}); - std::size_t RegisterType(std::string name, PartialType partialType, bool hidden = false, std::optional index = {}); - std::size_t RegisterVariable(std::string name, ExpressionType type, bool hidden = false, std::optional index = {}); + std::size_t RegisterConstant(std::string name, ConstantValue value, std::optional index = {}); + std::size_t RegisterFunction(std::string name, FunctionData funcData, std::optional index = {}); + std::size_t RegisterIntrinsic(std::string name, IntrinsicType type); + std::size_t RegisterModule(std::string moduleIdentifier, std::size_t moduleIndex); + std::size_t RegisterStruct(std::string name, StructDescription* description, std::optional index = {}); + std::size_t RegisterType(std::string name, ExpressionType expressionType, std::optional index = {}); + std::size_t RegisterType(std::string name, PartialType partialType, std::optional index = {}); + std::size_t RegisterVariable(std::string name, ExpressionType type, std::optional index = {}); void ResolveFunctions(); const ExpressionPtr& ResolveCondExpression(ConditionalExpression& node); @@ -132,6 +139,7 @@ namespace Nz::ShaderAst ExpressionType ResolveType(const ExpressionValue& exprTypeValue); void SanitizeIdentifier(std::string& identifier); + MultiStatementPtr SanitizeInternal(MultiStatement& rootNode, std::string* error); void TypeMustMatch(const ExpressionPtr& left, const ExpressionPtr& right) const; void TypeMustMatch(const ExpressionType& left, const ExpressionType& right) const; @@ -167,6 +175,7 @@ namespace Nz::ShaderAst Constant, Function, Intrinsic, + Module, Struct, Type, Variable diff --git a/src/Nazara/Shader/Ast/AstCloner.cpp b/src/Nazara/Shader/Ast/AstCloner.cpp index 353c1071e..b40ab1ea0 100644 --- a/src/Nazara/Shader/Ast/AstCloner.cpp +++ b/src/Nazara/Shader/Ast/AstCloner.cpp @@ -81,7 +81,6 @@ namespace Nz::ShaderAst { auto clone = std::make_unique(); clone->constIndex = node.constIndex; - clone->hidden = node.hidden; clone->name = node.name; clone->type = Clone(node.type); clone->expression = CloneExpression(node.expression); @@ -93,7 +92,6 @@ namespace Nz::ShaderAst { auto clone = std::make_unique(); clone->bindingSet = Clone(node.bindingSet); - clone->hidden = node.hidden; clone->externalVars.reserve(node.externalVars.size()); for (const auto& var : node.externalVars) @@ -116,7 +114,6 @@ namespace Nz::ShaderAst clone->earlyFragmentTests = Clone(node.earlyFragmentTests); clone->entryStage = Clone(node.entryStage); clone->funcIndex = node.funcIndex; - clone->hidden = node.hidden; clone->name = node.name; clone->returnType = Clone(node.returnType); @@ -140,7 +137,6 @@ namespace Nz::ShaderAst { auto clone = std::make_unique(); clone->defaultValue = CloneExpression(node.defaultValue); - clone->hidden = node.hidden; clone->optIndex = node.optIndex; clone->optName = node.optName; clone->optType = Clone(node.optType); @@ -151,7 +147,6 @@ namespace Nz::ShaderAst StatementPtr AstCloner::Clone(DeclareStructStatement& node) { auto clone = std::make_unique(); - clone->hidden = node.hidden; clone->isExported = Clone(node.isExported); clone->structIndex = node.structIndex; diff --git a/src/Nazara/Shader/Ast/AstCompare.cpp b/src/Nazara/Shader/Ast/AstCompare.cpp deleted file mode 100644 index 03c0b8046..000000000 --- a/src/Nazara/Shader/Ast/AstCompare.cpp +++ /dev/null @@ -1,10 +0,0 @@ -// 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::ShaderAst -{ -} diff --git a/src/Nazara/Shader/Ast/AstSerializer.cpp b/src/Nazara/Shader/Ast/AstSerializer.cpp index 8f3ad1861..86b7a7b31 100644 --- a/src/Nazara/Shader/Ast/AstSerializer.cpp +++ b/src/Nazara/Shader/Ast/AstSerializer.cpp @@ -195,7 +195,6 @@ namespace Nz::ShaderAst void AstSerializerBase::Serialize(DeclareExternalStatement& node) { - OptVal(node.hidden); ExprValue(node.bindingSet); Container(node.externalVars); @@ -212,7 +211,6 @@ namespace Nz::ShaderAst void AstSerializerBase::Serialize(DeclareConstStatement& node) { OptVal(node.constIndex); - OptVal(node.hidden); Value(node.name); ExprValue(node.type); Node(node.expression); @@ -226,7 +224,6 @@ namespace Nz::ShaderAst ExprValue(node.earlyFragmentTests); ExprValue(node.entryStage); OptVal(node.funcIndex); - OptVal(node.hidden); Container(node.parameters); for (auto& parameter : node.parameters) @@ -244,7 +241,6 @@ namespace Nz::ShaderAst void AstSerializerBase::Serialize(DeclareOptionStatement& node) { OptVal(node.optIndex); - OptVal(node.hidden); Value(node.optName); ExprValue(node.optType); Node(node.defaultValue); @@ -253,7 +249,6 @@ namespace Nz::ShaderAst void AstSerializerBase::Serialize(DeclareStructStatement& node) { OptVal(node.structIndex); - OptVal(node.hidden); ExprValue(node.isExported); Value(node.description.name); diff --git a/src/Nazara/Shader/Ast/SanitizeVisitor.cpp b/src/Nazara/Shader/Ast/SanitizeVisitor.cpp index 330ddef00..b3201b687 100644 --- a/src/Nazara/Shader/Ast/SanitizeVisitor.cpp +++ b/src/Nazara/Shader/Ast/SanitizeVisitor.cpp @@ -37,144 +37,133 @@ namespace Nz::ShaderAst } } + struct SanitizeVisitor::CurrentFunctionData + { + std::optional stageType; + Bitset<> calledFunctions; + DeclareFunctionStatement* statement; + FunctionFlags flags; + }; + + template + struct SanitizeVisitor::IdentifierData + { + Bitset availableIndices; + Bitset preregisteredIndices; + std::unordered_map values; + + template + std::size_t Register(U&& data, std::optional index = {}) + { + std::size_t dataIndex; + if (index.has_value()) + { + dataIndex = *index; + + if (dataIndex >= availableIndices.GetSize()) + availableIndices.Resize(dataIndex + 1, true); + else if (!availableIndices.Test(dataIndex)) + { + if (preregisteredIndices.UnboundedTest(dataIndex)) + preregisteredIndices.Reset(dataIndex); + else + throw AstError{ "index " + std::to_string(dataIndex) + " is already used" }; + } + } + else + dataIndex = RegisterNewIndex(); + + assert(values.find(dataIndex) == values.end()); + + availableIndices.Set(dataIndex, false); + values.emplace(dataIndex, std::forward(data)); + return dataIndex; + } + + std::size_t RegisterNewIndex(bool preregister = false) + { + std::size_t index = availableIndices.FindFirst(); + if (index == availableIndices.npos) + { + index = availableIndices.GetSize(); + availableIndices.Resize(index + 1, true); + } + + availableIndices.Set(index, false); + + if (preregister) + preregisteredIndices.UnboundedSet(index); + + return index; + } + + T& Retrieve(std::size_t index) + { + auto it = values.find(index); + if (it == values.end()) + throw AstError{ "invalid index " + std::to_string(index) }; + + return it->second; + } + }; + + struct SanitizeVisitor::Scope + { + std::size_t previousSize; + }; + + struct SanitizeVisitor::Environment + { + std::shared_ptr parentEnv; + std::vector identifiersInScope; + std::vector scopes; + IdentifierData constantValues; + IdentifierData functions; + IdentifierData intrinsics; + IdentifierData moduleIndices; + IdentifierData structs; + IdentifierData> types; + IdentifierData variableTypes; + }; + struct SanitizeVisitor::Context { - struct CurrentFunctionData - { - std::optional stageType; - Bitset<> calledFunctions; - DeclareFunctionStatement* statement; - FunctionFlags flags; - }; - - template - struct IdentifierData - { - Bitset availableIndices; - Bitset preregisteredIndices; - std::unordered_map values; - - template - std::size_t Register(U&& data, std::optional index = {}) - { - std::size_t dataIndex; - if (index.has_value()) - { - dataIndex = *index; - - if (dataIndex >= availableIndices.GetSize()) - availableIndices.Resize(dataIndex + 1, true); - else if (!availableIndices.Test(dataIndex)) - { - if (preregisteredIndices.UnboundedTest(dataIndex)) - preregisteredIndices.Reset(dataIndex); - else - throw AstError{ "index " + std::to_string(dataIndex) + " is already used" }; - } - } - else - dataIndex = RegisterNewIndex(); - - assert(values.find(dataIndex) == values.end()); - - availableIndices.Set(dataIndex, false); - values.emplace(dataIndex, std::forward(data)); - return dataIndex; - } - - std::size_t RegisterNewIndex(bool preregister = false) - { - std::size_t index = availableIndices.FindFirst(); - if (index == availableIndices.npos) - { - index = availableIndices.GetSize(); - availableIndices.Resize(index + 1, true); - } - - availableIndices.Set(index, false); - - if (preregister) - preregisteredIndices.UnboundedSet(index); - - return index; - } - - T& Retrieve(std::size_t index) - { - auto it = values.find(index); - if (it == values.end()) - throw AstError{ "invalid index " + std::to_string(index) }; - - return it->second; - } - }; - struct PendingFunction { DeclareFunctionStatement* cloneNode; const DeclareFunctionStatement* node; }; - struct Scope - { - std::size_t previousSize; - }; - std::array entryFunctions = {}; - std::optional importUsage; - std::vector identifiersInScope; + std::vector> moduleEnvironments; std::vector pendingFunctions; - std::vector scopes; std::vector* currentStatementList = nullptr; + std::unordered_map moduleByUuid; std::unordered_set declaredExternalVar; std::unordered_set usedBindingIndexes; - IdentifierData constantValues; - IdentifierData functions; - IdentifierData intrinsics; - IdentifierData structs; - IdentifierData> types; - IdentifierData variableTypes; + std::shared_ptr globalEnv; + std::shared_ptr currentEnv; Options options; CurrentFunctionData* currentFunction = nullptr; }; ModulePtr SanitizeVisitor::Sanitize(const Module& module, const Options& options, std::string* error) { - ModulePtr clone = std::make_shared(module.metadata); - Context currentContext; currentContext.options = options; + ModulePtr clone = std::make_shared(module.metadata); + clone->importedModules = module.importedModules; + m_context = ¤tContext; CallOnExit resetContext([&] { m_context = nullptr; }); - PushScope(); //< Global scope - { - RegisterBuiltin(); + m_context->globalEnv = std::make_shared(); + m_context->currentEnv = m_context->globalEnv; - // First pass, evaluate everything except function code - try - { - clone->rootNode = static_unique_pointer_cast(AstCloner::Clone(*module.rootNode)); - } - catch (const AstError& err) - { - if (!error) - throw std::runtime_error(err.errMsg); - - *error = err.errMsg; - } - catch (const std::runtime_error& err) - { - if (!error) - throw; - - *error = err.what(); - } - - ResolveFunctions(); - } - PopScope(); + clone->rootNode = SanitizeInternal(*module.rootNode, error); + if (!clone->rootNode) + return {}; return clone; } @@ -252,7 +241,7 @@ namespace Nz::ShaderAst else if (IsStructType(exprType)) { std::size_t structIndex = ResolveStruct(exprType); - const StructDescription* s = m_context->structs.Retrieve(structIndex); + const StructDescription* s = m_context->currentEnv->structs.Retrieve(structIndex); // Retrieve member index (not counting disabled fields) Int32 fieldIndex = 0; @@ -580,7 +569,7 @@ namespace Nz::ShaderAst ExpressionPtr SanitizeVisitor::Clone(ConstantExpression& node) { // Replace by constant value - auto constant = ShaderBuilder::Constant(m_context->constantValues.Retrieve(node.constantId)); + auto constant = ShaderBuilder::Constant(m_context->currentEnv->constantValues.Retrieve(node.constantId)); constant->cachedExpressionType = GetExpressionType(constant->value); return constant; @@ -615,7 +604,7 @@ namespace Nz::ShaderAst case Identifier::Type::Intrinsic: { - IntrinsicType intrinsicType = m_context->intrinsics.Retrieve(identifier->index); + IntrinsicType intrinsicType = m_context->currentEnv->intrinsics.Retrieve(identifier->index); auto clone = AstCloner::Clone(node); clone->cachedExpressionType = IntrinsicFunctionType{ intrinsicType }; @@ -643,7 +632,7 @@ namespace Nz::ShaderAst { // Replace IdentifierExpression by VariableExpression auto varExpr = std::make_unique(); - varExpr->cachedExpressionType = m_context->variableTypes.Retrieve(identifier->index); + varExpr->cachedExpressionType = m_context->currentEnv->variableTypes.Retrieve(identifier->index); varExpr->variableId = identifier->index; return varExpr; @@ -794,10 +783,7 @@ namespace Nz::ShaderAst clone->type = expressionType; - if (m_context->importUsage.has_value()) - clone->hidden = true; - - clone->constIndex = RegisterConstant(clone->name, value, clone->hidden.value_or(false), clone->constIndex); + clone->constIndex = RegisterConstant(clone->name, value, clone->constIndex); if (m_context->options.removeConstDeclaration) return ShaderBuilder::NoOp(); @@ -811,13 +797,6 @@ namespace Nz::ShaderAst auto clone = static_unique_pointer_cast(AstCloner::Clone(node)); - if (m_context->importUsage.has_value()) - { - // Since unused variables have been removed when importing a module, every variable should be used - assert(!clone->externalVars.empty()); - clone->hidden = !m_context->importUsage->usedVariables.UnboundedTest(*clone->externalVars.front().varIndex); - } - UInt32 defaultBlockSet = 0; if (clone->bindingSet.HasValue()) defaultBlockSet = ComputeExprValue(clone->bindingSet); @@ -858,7 +837,7 @@ namespace Nz::ShaderAst throw AstError{ "external variable " + extVar.name + " is of wrong type: only uniform and sampler are allowed in external blocks" }; extVar.type = std::move(resolvedType); - extVar.varIndex = RegisterVariable(extVar.name, std::move(varType), clone->hidden.value_or(false), extVar.varIndex); + extVar.varIndex = RegisterVariable(extVar.name, std::move(varType), extVar.varIndex); SanitizeIdentifier(extVar.name); } @@ -916,12 +895,6 @@ namespace Nz::ShaderAst } } - if (m_context->importUsage.has_value()) - { - assert(clone->funcIndex); - clone->hidden = !m_context->importUsage->usedStructs.UnboundedTest(*clone->funcIndex); - } - // Function content is resolved in a second pass auto& pendingFunc = m_context->pendingFunctions.emplace_back(); pendingFunc.cloneNode = clone.get(); @@ -936,7 +909,7 @@ namespace Nz::ShaderAst FunctionData funcData; funcData.node = clone.get(); //< update function node - std::size_t funcIndex = RegisterFunction(clone->name, std::move(funcData), clone->hidden.value_or(false), node.funcIndex); + std::size_t funcIndex = RegisterFunction(clone->name, std::move(funcData), node.funcIndex); clone->funcIndex = funcIndex; SanitizeIdentifier(clone->name); @@ -962,13 +935,10 @@ namespace Nz::ShaderAst UInt32 optionHash = CRC32(reinterpret_cast(clone->optName.data()), clone->optName.size()); - if (m_context->importUsage.has_value()) - clone->hidden = true; - if (auto optionValueIt = m_context->options.optionValues.find(optionHash); optionValueIt != m_context->options.optionValues.end()) - clone->optIndex = RegisterConstant(clone->optName, optionValueIt->second, clone->hidden.value_or(false), clone->optIndex); + clone->optIndex = RegisterConstant(clone->optName, optionValueIt->second, clone->optIndex); else if (clone->defaultValue) - clone->optIndex = RegisterConstant(clone->optName, ComputeConstantValue(*clone->defaultValue), clone->hidden.value_or(false), clone->optIndex); + clone->optIndex = RegisterConstant(clone->optName, ComputeConstantValue(*clone->defaultValue), clone->optIndex); else throw AstError{ "missing option " + clone->optName + " value (has no default value)" }; @@ -988,12 +958,6 @@ namespace Nz::ShaderAst if (clone->isExported.HasValue()) clone->isExported = ComputeExprValue(clone->isExported); - if (m_context->importUsage.has_value()) - { - assert(clone->structIndex); - clone->hidden = !m_context->importUsage->usedStructs.UnboundedTest(*clone->structIndex); - } - std::unordered_set declaredMembers; for (auto& member : clone->description.members) { @@ -1023,7 +987,7 @@ namespace Nz::ShaderAst else if (IsStructType(resolvedType)) { std::size_t structIndex = std::get(resolvedType).structIndex; - const StructDescription* desc = m_context->structs.Retrieve(structIndex); + const StructDescription* desc = m_context->currentEnv->structs.Retrieve(structIndex); if (!desc->layout.HasValue() || desc->layout.GetResultingValue() != clone->description.layout.GetResultingValue()) throw AstError{ "inner struct layout mismatch" }; } @@ -1032,7 +996,7 @@ namespace Nz::ShaderAst member.type = std::move(resolvedType); } - clone->structIndex = RegisterStruct(clone->description.name, &clone->description, clone->hidden.value_or(false), clone->structIndex); + clone->structIndex = RegisterStruct(clone->description.name, &clone->description, clone->structIndex); SanitizeIdentifier(clone->description.name); @@ -1362,9 +1326,6 @@ namespace Nz::ShaderAst StatementPtr SanitizeVisitor::Clone(ImportStatement& node) { - // Nested import is handled separately - assert(!m_context->importUsage); - if (!m_context->options.moduleCallback) return static_unique_pointer_cast(AstCloner::Clone(node)); @@ -1392,8 +1353,14 @@ namespace Nz::ShaderAst targetModule->rootNode->sectionName = "Module " + targetModule->metadata->moduleId.ToString(); + m_context->currentEnv = m_context->moduleEnvironments.emplace_back(); + m_context->currentEnv->parentEnv = m_context->globalEnv; + CallOnExit restoreEnvOnExit([&] { m_context->currentEnv = m_context->globalEnv; }); + + ModulePtr sanitizedModule = std::make_shared(targetModule->metadata); + std::string error; - ModulePtr sanitizedModule = ShaderAst::Sanitize(*targetModule, m_context->options, &error); + sanitizedModule->rootNode = SanitizeInternal(*targetModule->rootNode, &error); if (!sanitizedModule) throw AstError{ "module " + ModulePathAsString() + " compilation failed: " + error }; @@ -1425,10 +1392,10 @@ namespace Nz::ShaderAst DependencyCheckerVisitor::UsageSet remappedExportedSet; IndexRemapperVisitor::Callbacks remapCallbacks; - remapCallbacks.constIndexGenerator = [this](std::size_t previousIndex) { return m_context->constantValues.RegisterNewIndex(true); }; + remapCallbacks.constIndexGenerator = [this](std::size_t previousIndex) { return m_context->currentEnv->constantValues.RegisterNewIndex(true); }; remapCallbacks.funcIndexGenerator = [&](std::size_t previousIndex) { - std::size_t newIndex = m_context->functions.RegisterNewIndex(true); + std::size_t newIndex = m_context->currentEnv->functions.RegisterNewIndex(true); if (exportedSet.usedFunctions.Test(previousIndex)) remappedExportedSet.usedFunctions.UnboundedSet(newIndex); @@ -1437,7 +1404,7 @@ namespace Nz::ShaderAst remapCallbacks.structIndexGenerator = [&](std::size_t previousIndex) { - std::size_t newIndex = m_context->structs.RegisterNewIndex(true); + std::size_t newIndex = m_context->currentEnv->structs.RegisterNewIndex(true); if (exportedSet.usedStructs.Test(previousIndex)) remappedExportedSet.usedStructs.UnboundedSet(newIndex); @@ -1446,7 +1413,7 @@ namespace Nz::ShaderAst remapCallbacks.varIndexGenerator = [&](std::size_t previousIndex) { - std::size_t newIndex = m_context->variableTypes.RegisterNewIndex(true); + std::size_t newIndex = m_context->currentEnv->variableTypes.RegisterNewIndex(true); if (exportedSet.usedVariables.Test(previousIndex)) remappedExportedSet.usedVariables.UnboundedSet(newIndex); @@ -1456,8 +1423,8 @@ namespace Nz::ShaderAst statementPtr = RemapIndices(*statementPtr, remapCallbacks); // Register exported variables (FIXME: This shouldn't be necessary and could be handled by the IndexRemapperVisitor) - m_context->importUsage = remappedExportedSet; - CallOnExit restoreImportOnExit([&] { m_context->importUsage.reset(); }); + //m_context->importUsage = remappedExportedSet; + //CallOnExit restoreImportOnExit([&] { m_context->importUsage.reset(); }); return AstCloner::Clone(*statementPtr); } @@ -1513,22 +1480,43 @@ namespace Nz::ShaderAst auto SanitizeVisitor::FindIdentifier(const std::string_view& identifierName) const -> const Identifier* { - auto it = std::find_if(m_context->identifiersInScope.rbegin(), m_context->identifiersInScope.rend(), [&](const Identifier& identifier) { return identifier.name == identifierName; }); - if (it == m_context->identifiersInScope.rend()) - return nullptr; - - return &*it; + return FindIdentifier(*m_context->currentEnv, identifierName); } template auto SanitizeVisitor::FindIdentifier(const std::string_view& identifierName, F&& functor) const -> const Identifier* { - auto it = std::find_if(m_context->identifiersInScope.rbegin(), m_context->identifiersInScope.rend(), [&](const Identifier& identifier) + return FindIdentifier(*m_context->currentEnv, identifierName, std::forward(functor)); + } + + auto SanitizeVisitor::FindIdentifier(const Environment& environment, const std::string_view& identifierName) const -> const Identifier* + { + auto it = std::find_if(environment.identifiersInScope.rbegin(), environment.identifiersInScope.rend(), [&](const Identifier& identifier) { return identifier.name == identifierName; }); + if (it == environment.identifiersInScope.rend()) + { + if (environment.parentEnv) + return FindIdentifier(*environment.parentEnv, identifierName); + else + return nullptr; + } + + return &*it; + } + + template + auto SanitizeVisitor::FindIdentifier(const Environment& environment, const std::string_view& identifierName, F&& functor) const -> const Identifier* + { + auto it = std::find_if(environment.identifiersInScope.rbegin(), environment.identifiersInScope.rend(), [&](const Identifier& identifier) { return identifier.name == identifierName && functor(identifier); }); - if (it == m_context->identifiersInScope.rend()) - return nullptr; + if (it == environment.identifiersInScope.rend()) + { + if (environment.parentEnv) + return FindIdentifier(*environment.parentEnv, identifierName, std::forward(functor)); + else + return nullptr; + } return &*it; } @@ -1542,7 +1530,7 @@ namespace Nz::ShaderAst switch (identifier->type) { case Identifier::Type::Constant: - return m_context->constantValues.Retrieve(identifier->index); + return m_context->currentEnv->constantValues.Retrieve(identifier->index); case Identifier::Type::Struct: return StructType{ identifier->index }; @@ -1551,7 +1539,7 @@ namespace Nz::ShaderAst return std::visit([&](auto&& arg) -> TypeParameter { return arg; - }, m_context->types.Retrieve(identifier->index)); + }, m_context->currentEnv->types.Retrieve(identifier->index)); case Identifier::Type::Alias: throw std::runtime_error("TODO"); @@ -1587,16 +1575,16 @@ namespace Nz::ShaderAst void SanitizeVisitor::PushScope() { - auto& scope = m_context->scopes.emplace_back(); - scope.previousSize = m_context->identifiersInScope.size(); + auto& scope = m_context->currentEnv->scopes.emplace_back(); + scope.previousSize = m_context->currentEnv->identifiersInScope.size(); } void SanitizeVisitor::PopScope() { - assert(!m_context->scopes.empty()); - auto& scope = m_context->scopes.back(); - m_context->identifiersInScope.resize(scope.previousSize); - m_context->scopes.pop_back(); + assert(!m_context->currentEnv->scopes.empty()); + auto& scope = m_context->currentEnv->scopes.back(); + m_context->currentEnv->identifiersInScope.resize(scope.previousSize); + m_context->currentEnv->scopes.pop_back(); } ExpressionPtr SanitizeVisitor::CacheResult(ExpressionPtr expression) @@ -1664,7 +1652,7 @@ namespace Nz::ShaderAst AstConstantPropagationVisitor::Options optimizerOptions; optimizerOptions.constantQueryCallback = [this](std::size_t constantId) -> const ConstantValue& { - return m_context->constantValues.Retrieve(constantId); + return m_context->currentEnv->constantValues.Retrieve(constantId); }; // Run optimizer on constant value to hopefully retrieve a single constant value @@ -1673,7 +1661,7 @@ namespace Nz::ShaderAst void SanitizeVisitor::PropagateFunctionFlags(std::size_t funcIndex, FunctionFlags flags, Bitset<>& seen) { - auto& funcData = m_context->functions.Retrieve(funcIndex); + auto& funcData = m_context->currentEnv->functions.Retrieve(funcIndex); funcData.flags |= flags; for (std::size_t i = funcData.calledByFunctions.FindFirst(); i != funcData.calledByFunctions.npos; i = funcData.calledByFunctions.FindNext(i)) @@ -1844,152 +1832,136 @@ namespace Nz::ShaderAst RegisterIntrinsic("reflect", IntrinsicType::Reflect); } - std::size_t SanitizeVisitor::RegisterConstant(std::string name, ConstantValue value, bool hidden, std::optional index) + std::size_t SanitizeVisitor::RegisterConstant(std::string name, ConstantValue value, std::optional index) { - std::size_t constantIndex = m_context->constantValues.Register(std::move(value), index); - if (!hidden) - { - if (FindIdentifier(name)) - throw AstError{ name + " is already used" }; + if (FindIdentifier(name)) + throw AstError{ name + " is already used" }; - m_context->identifiersInScope.push_back({ - std::move(name), - constantIndex, - Identifier::Type::Constant - }); - } + std::size_t constantIndex = m_context->currentEnv->constantValues.Register(std::move(value), index); + m_context->currentEnv->identifiersInScope.push_back({ + std::move(name), + constantIndex, + Identifier::Type::Constant + }); return constantIndex; } - std::size_t SanitizeVisitor::RegisterFunction(std::string name, FunctionData funcData, bool hidden, std::optional index) + std::size_t SanitizeVisitor::RegisterFunction(std::string name, FunctionData funcData, std::optional index) { - std::size_t functionIndex = m_context->functions.Register(std::move(funcData), index); - - if (!hidden) + if (auto* identifier = FindIdentifier(name)) { - if (auto* identifier = FindIdentifier(name)) + bool duplicate = true; + + // Functions cannot be declared twice, except for entry ones if their stages are different + if (funcData.node->entryStage.HasValue() && identifier->type == Identifier::Type::Function) { - bool duplicate = true; - - // Functions cannot be declared twice, except for entry ones if their stages are different - if (funcData.node->entryStage.HasValue() && identifier->type == Identifier::Type::Function) - { - auto& otherFunction = m_context->functions.Retrieve(identifier->index); - if (funcData.node->entryStage.GetResultingValue() != otherFunction.node->entryStage.GetResultingValue()) - duplicate = false; - } - - if (duplicate) - throw AstError{ funcData.node->name + " is already used" }; + auto& otherFunction = m_context->currentEnv->functions.Retrieve(identifier->index); + if (funcData.node->entryStage.GetResultingValue() != otherFunction.node->entryStage.GetResultingValue()) + duplicate = false; } - m_context->identifiersInScope.push_back({ - std::move(name), - functionIndex, - Identifier::Type::Function - }); + if (duplicate) + throw AstError{ funcData.node->name + " is already used" }; } + std::size_t functionIndex = m_context->currentEnv->functions.Register(std::move(funcData), index); + + m_context->currentEnv->identifiersInScope.push_back({ + std::move(name), + functionIndex, + Identifier::Type::Function + }); + return functionIndex; } - std::size_t SanitizeVisitor::RegisterIntrinsic(std::string name, IntrinsicType type, bool hidden, std::optional index) + std::size_t SanitizeVisitor::RegisterIntrinsic(std::string name, IntrinsicType type) { - std::size_t intrinsicIndex = m_context->intrinsics.Register(std::move(type), index); + if (FindIdentifier(name)) + throw AstError{ name + " is already used" }; - if (!hidden) - { - if (FindIdentifier(name)) - throw AstError{ name + " is already used" }; + std::size_t intrinsicIndex = m_context->currentEnv->intrinsics.Register(std::move(type)); - m_context->identifiersInScope.push_back({ - std::move(name), - intrinsicIndex, - Identifier::Type::Intrinsic - }); - } + m_context->currentEnv->identifiersInScope.push_back({ + std::move(name), + intrinsicIndex, + Identifier::Type::Intrinsic + }); return intrinsicIndex; } - std::size_t SanitizeVisitor::RegisterStruct(std::string name, StructDescription* description, bool hidden, std::optional index) + std::size_t SanitizeVisitor::RegisterModule(std::string moduleIdentifier, std::size_t moduleIndex) { - std::size_t structIndex = m_context->structs.Register(description, index); + return std::size_t(); + } - if (!hidden) - { - if (FindIdentifier(name)) - throw AstError{ name + " is already used" }; + std::size_t SanitizeVisitor::RegisterStruct(std::string name, StructDescription* description, std::optional index) + { + if (FindIdentifier(name)) + throw AstError{ name + " is already used" }; - m_context->identifiersInScope.push_back({ - std::move(name), - structIndex, - Identifier::Type::Struct - }); - } + std::size_t structIndex = m_context->currentEnv->structs.Register(description, index); + + m_context->currentEnv->identifiersInScope.push_back({ + std::move(name), + structIndex, + Identifier::Type::Struct + }); return structIndex; } - std::size_t SanitizeVisitor::RegisterType(std::string name, ExpressionType expressionType, bool hidden, std::optional index) + std::size_t SanitizeVisitor::RegisterType(std::string name, ExpressionType expressionType, std::optional index) { - std::size_t typeIndex = m_context->types.Register(std::move(expressionType), index); + if (FindIdentifier(name)) + throw AstError{ name + " is already used" }; - if (!hidden) - { - if (FindIdentifier(name)) - throw AstError{ name + " is already used" }; + std::size_t typeIndex = m_context->currentEnv->types.Register(std::move(expressionType), index); - m_context->identifiersInScope.push_back({ - std::move(name), - typeIndex, - Identifier::Type::Type - }); - } + m_context->currentEnv->identifiersInScope.push_back({ + std::move(name), + typeIndex, + Identifier::Type::Type + }); return typeIndex; } - std::size_t SanitizeVisitor::RegisterType(std::string name, PartialType partialType, bool hidden, std::optional index) + std::size_t SanitizeVisitor::RegisterType(std::string name, PartialType partialType, std::optional index) { - std::size_t typeIndex = m_context->types.Register(std::move(partialType), index); + if (FindIdentifier(name)) + throw AstError{ name + " is already used" }; - if (!hidden) - { - if (FindIdentifier(name)) - throw AstError{ name + " is already used" }; + std::size_t typeIndex = m_context->currentEnv->types.Register(std::move(partialType), index); - m_context->identifiersInScope.push_back({ - std::move(name), - typeIndex, - Identifier::Type::Type - }); - } + m_context->currentEnv->identifiersInScope.push_back({ + std::move(name), + typeIndex, + Identifier::Type::Type + }); return typeIndex; } - std::size_t SanitizeVisitor::RegisterVariable(std::string name, ExpressionType type, bool hidden, std::optional index) + std::size_t SanitizeVisitor::RegisterVariable(std::string name, ExpressionType type, std::optional index) { - std::size_t varIndex = m_context->variableTypes.Register(std::move(type), index); - - if (!hidden) + if (auto* identifier = FindIdentifier(name)) { - if (auto* identifier = FindIdentifier(name)) - { - // Allow variable shadowing - if (identifier->type != Identifier::Type::Variable) - throw AstError{ name + " is already used" }; - } - - m_context->identifiersInScope.push_back({ - std::move(name), - varIndex, - Identifier::Type::Variable - }); + // Allow variable shadowing + if (identifier->type != Identifier::Type::Variable) + throw AstError{ name + " is already used" }; } + std::size_t varIndex = m_context->currentEnv->variableTypes.Register(std::move(type), index); + + m_context->currentEnv->identifiersInScope.push_back({ + std::move(name), + varIndex, + Identifier::Type::Variable + }); + return varIndex; } @@ -2002,11 +1974,11 @@ namespace Nz::ShaderAst for (auto& parameter : pendingFunc.cloneNode->parameters) { - parameter.varIndex = RegisterVariable(parameter.name, parameter.type.GetResultingValue(), false, parameter.varIndex); + parameter.varIndex = RegisterVariable(parameter.name, parameter.type.GetResultingValue(), parameter.varIndex); SanitizeIdentifier(parameter.name); } - Context::CurrentFunctionData tempFuncData; + CurrentFunctionData tempFuncData; if (pendingFunc.cloneNode->entryStage.HasValue()) tempFuncData.stageType = pendingFunc.cloneNode->entryStage.GetResultingValue(); @@ -2025,21 +1997,22 @@ namespace Nz::ShaderAst std::size_t funcIndex = *pendingFunc.cloneNode->funcIndex; for (std::size_t i = tempFuncData.calledFunctions.FindFirst(); i != tempFuncData.calledFunctions.npos; i = tempFuncData.calledFunctions.FindNext(i)) { - auto& targetFunc = m_context->functions.Retrieve(i); + auto& targetFunc = m_context->currentEnv->functions.Retrieve(i); targetFunc.calledByFunctions.UnboundedSet(funcIndex); } PopScope(); } + m_context->pendingFunctions.clear(); Bitset<> seen; - for (const auto& [funcIndex, funcData] : m_context->functions.values) + for (const auto& [funcIndex, funcData] : m_context->currentEnv->functions.values) { PropagateFunctionFlags(funcIndex, funcData.flags, seen); seen.Clear(); } - for (const auto& [funcIndex, funcData] : m_context->functions.values) + for (const auto& [funcIndex, funcData] : m_context->currentEnv->functions.values) { if (funcData.flags.Test(ShaderAst::FunctionFlag::DoesDiscard) && funcData.node->entryStage.HasValue() && funcData.node->entryStage.GetResultingValue() != ShaderStageType::Fragment) throw AstError{ "discard can only be used in the fragment stage" }; @@ -2118,7 +2091,7 @@ namespace Nz::ShaderAst std::size_t typeIndex = std::get(exprType).typeIndex; - const auto& type = m_context->types.Retrieve(typeIndex); + const auto& type = m_context->currentEnv->types.Retrieve(typeIndex); if (std::holds_alternative(type)) throw AstError{ "full type expected" }; @@ -2156,6 +2129,41 @@ namespace Nz::ShaderAst while (FindIdentifier(identifier) != nullptr); } } + + MultiStatementPtr SanitizeVisitor::SanitizeInternal(MultiStatement& rootNode, std::string* error) + { + MultiStatementPtr output; + + PushScope(); //< Global scope + { + RegisterBuiltin(); + + // First pass, evaluate everything except function code + try + { + output = static_unique_pointer_cast(AstCloner::Clone(rootNode)); + } + catch (const AstError& err) + { + if (!error) + throw std::runtime_error(err.errMsg); + + *error = err.errMsg; + } + catch (const std::runtime_error& err) + { + if (!error) + throw; + + *error = err.what(); + } + + ResolveFunctions(); + } + PopScope(); + + return output; + } void SanitizeVisitor::TypeMustMatch(const ExpressionPtr& left, const ExpressionPtr& right) const { @@ -2190,7 +2198,7 @@ namespace Nz::ShaderAst if (IsTypeExpression(exprType)) { std::size_t typeIndex = std::get(exprType).typeIndex; - const auto& type = m_context->types.Retrieve(typeIndex); + const auto& type = m_context->currentEnv->types.Retrieve(typeIndex); if (!std::holds_alternative(type)) throw std::runtime_error("only partial types can be specialized"); @@ -2283,7 +2291,7 @@ namespace Nz::ShaderAst Int32 index = std::get(constantExpr.value); std::size_t structIndex = ResolveStruct(exprType); - const StructDescription* s = m_context->structs.Retrieve(structIndex); + const StructDescription* s = m_context->currentEnv->structs.Retrieve(structIndex); exprType = ResolveType(s->members[index].type); } @@ -2357,7 +2365,7 @@ namespace Nz::ShaderAst assert(std::holds_alternative(targetFuncType)); std::size_t targetFuncIndex = std::get(targetFuncType).funcIndex; - auto& funcData = m_context->functions.Retrieve(targetFuncIndex); + auto& funcData = m_context->currentEnv->functions.Retrieve(targetFuncIndex); const DeclareFunctionStatement* referenceDeclaration = funcData.node; @@ -2468,7 +2476,7 @@ namespace Nz::ShaderAst TypeMustMatch(resolvedType, GetExpressionType(*node.initialExpression)); } - node.varIndex = RegisterVariable(node.varName, resolvedType, false, node.varIndex); + node.varIndex = RegisterVariable(node.varName, resolvedType, node.varIndex); node.varType = std::move(resolvedType); if (m_context->options.makeVariableNameUnique) @@ -2720,7 +2728,7 @@ namespace Nz::ShaderAst void SanitizeVisitor::Validate(VariableExpression& node) { - node.cachedExpressionType = m_context->variableTypes.Retrieve(node.variableId); + node.cachedExpressionType = m_context->currentEnv->variableTypes.Retrieve(node.variableId); } ExpressionType SanitizeVisitor::ValidateBinaryOp(BinaryType op, const ExpressionPtr& leftExpr, const ExpressionPtr& rightExpr) diff --git a/src/Nazara/Shader/ShaderLangParser.cpp b/src/Nazara/Shader/ShaderLangParser.cpp index 8bfaa4d76..7e215ed00 100644 --- a/src/Nazara/Shader/ShaderLangParser.cpp +++ b/src/Nazara/Shader/ShaderLangParser.cpp @@ -34,6 +34,7 @@ namespace Nz::ShaderLang { "nzsl_version", ShaderAst::AttributeType::LangVersion }, { "set", ShaderAst::AttributeType::Set }, { "unroll", ShaderAst::AttributeType::Unroll }, + { "uuid", ShaderAst::AttributeType::Uuid }, }; std::unordered_map s_entryPoints = { @@ -247,10 +248,8 @@ namespace Nz::ShaderLang { Expect(Advance(), TokenType::Module); - if (m_context->module) - throw DuplicateModule{ "you must set one module statement per file" }; - std::optional moduleVersion; + std::optional moduleId; for (auto&& [attributeType, arg] : attributes) { @@ -296,6 +295,32 @@ namespace Nz::ShaderLang break; } + case ShaderAst::AttributeType::Uuid: + { + if (moduleId.has_value()) + throw AttributeError{ "attribute" + std::string("uuid") + " can only be present once" }; + + if (!arg) + throw AttributeError{ "attribute " + std::string("uuid") + " requires a parameter" }; + + const ShaderAst::ExpressionPtr& expr = *arg; + if (expr->GetType() != ShaderAst::NodeType::ConstantValueExpression) + throw AttributeError{ "attribute " + std::string("uuid") + " expect a single string parameter" }; + + auto& constantValue = SafeCast(*expr); + if (ShaderAst::GetExpressionType(constantValue.value) != ShaderAst::ExpressionType{ ShaderAst::PrimitiveType::String }) + throw AttributeError{ "attribute " + std::string("uuid") + " expect a single string parameter" }; + + 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" }; + + moduleId = uuid; + break; + } + default: throw AttributeError{ "unhandled attribute for module" }; } @@ -304,9 +329,34 @@ namespace Nz::ShaderLang if (!moduleVersion.has_value()) throw AttributeError{ "missing module version" }; - m_context->module = std::make_shared(*moduleVersion); + if (!moduleId) + moduleId = Uuid::Generate(); - Expect(Advance(), TokenType::Semicolon); + auto module = std::make_shared(*moduleVersion, *moduleId); + + if (Peek().type == TokenType::Identifier) + { + // Imported module + if (!m_context->module) + throw UnexpectedToken{}; //< "unexpected token before module declaration" + + const std::string& identifier = std::get(Peek().data); + Consume(); + + module->rootNode->statements = ParseStatementList(); + + auto& importedModule = m_context->module->importedModules.emplace_back(); + importedModule.module = std::move(module); + } + else + { + Expect(Advance(), TokenType::Semicolon); + + if (m_context->module) + throw DuplicateModule{ "you must set one module statement per file" }; + + m_context->module = std::move(module); + } } void Parser::ParseVariableDeclaration(std::string& name, ShaderAst::ExpressionValue& type, ShaderAst::ExpressionPtr& initialValue) diff --git a/tests/Engine/Core/UuidTest.cpp b/tests/Engine/Core/UuidTest.cpp index 1fa786c68..752709dfd 100644 --- a/tests/Engine/Core/UuidTest.cpp +++ b/tests/Engine/Core/UuidTest.cpp @@ -13,6 +13,8 @@ SCENARIO("Uuid", "[CORE][UUID]") // Testing some invalid cases CHECK(Nz::Uuid::FromString("Nazara Engine") == Nz::Uuid()); + CHECK(Nz::Uuid::FromString("1b0e29af_fd4a_43e0_ba4c_e9334183b1f1") == Nz::Uuid()); + CHECK(Nz::Uuid::FromString("1b0e29af-fd4a_43e0_ba4c_e93341-3b1f1") == Nz::Uuid()); CHECK(Nz::Uuid::FromString("Zb0e29af-fd4a-43e0-ba4c-e9334183b1f1") == Nz::Uuid()); CHECK(Nz::Uuid::FromString("1b0e29a\t-fd4a-43e0-ba4c-e9334183b1f1") == Nz::Uuid()); CHECK(Nz::Uuid::FromString("1b0e29af-fd4\v-43e0-ba4c-e9334183b1f1") == Nz::Uuid());