<gh_stars>0
// Copyright (C) 2022 <NAME>" Leclercq (<EMAIL>)
// 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/ShaderLangParser.hpp>
#include <Nazara/Core/Algorithm.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/ShaderLangErrors.hpp>
#include <frozen/string.h>
#include <frozen/unordered_map.h>
#include <cassert>
#include <regex>
#include <Nazara/Shader/Debug.hpp>

namespace Nz::ShaderLang
{
	namespace NAZARA_ANONYMOUS_NAMESPACE
	{
		constexpr auto s_depthWriteModes = frozen::make_unordered_map<frozen::string, ShaderAst::DepthWriteMode>({
			{ "greater",   ShaderAst::DepthWriteMode::Greater },
			{ "less",      ShaderAst::DepthWriteMode::Less },
			{ "replace",   ShaderAst::DepthWriteMode::Replace },
			{ "unchanged", ShaderAst::DepthWriteMode::Unchanged },
		});

		constexpr auto s_identifierToAttributeType = frozen::make_unordered_map<frozen::string, ShaderAst::AttributeType>({
			{ "binding",              ShaderAst::AttributeType::Binding },
			{ "builtin",              ShaderAst::AttributeType::Builtin },
			{ "cond",                 ShaderAst::AttributeType::Cond },
			{ "depth_write",          ShaderAst::AttributeType::DepthWrite },
			{ "early_fragment_tests", ShaderAst::AttributeType::EarlyFragmentTests },
			{ "entry",                ShaderAst::AttributeType::Entry },
			{ "export",               ShaderAst::AttributeType::Export },
			{ "layout",               ShaderAst::AttributeType::Layout },
			{ "location",             ShaderAst::AttributeType::Location },
			{ "nzsl_version",         ShaderAst::AttributeType::LangVersion },
			{ "set",                  ShaderAst::AttributeType::Set },
			{ "unroll",               ShaderAst::AttributeType::Unroll },
			{ "uuid",                 ShaderAst::AttributeType::Uuid },
		});

		constexpr auto s_entryPoints = frozen::make_unordered_map<frozen::string, ShaderStageType>({
			{ "frag", ShaderStageType::Fragment },
			{ "vert", ShaderStageType::Vertex },
		});

		constexpr auto s_builtinMapping = frozen::make_unordered_map<frozen::string, ShaderAst::BuiltinEntry>({
			{ "fragcoord", ShaderAst::BuiltinEntry::FragCoord },
			{ "fragdepth", ShaderAst::BuiltinEntry::FragDepth },
			{ "position", ShaderAst::BuiltinEntry::VertexPosition }
		});

		constexpr auto s_layoutMapping = frozen::make_unordered_map<frozen::string, StructLayout>({
			{ "std140", StructLayout::Std140 }
		});

		constexpr auto s_unrollModes = frozen::make_unordered_map<frozen::string, ShaderAst::LoopUnroll>({
			{ "always", ShaderAst::LoopUnroll::Always },
			{ "hint",   ShaderAst::LoopUnroll::Hint },
			{ "never",  ShaderAst::LoopUnroll::Never }
		});
	}

	ShaderAst::ModulePtr Parser::Parse(const std::vector<Token>& tokens)
	{
		Context context;
		context.tokenCount = tokens.size();
		context.tokens = tokens.data();

		m_context = &context;

		std::vector<Attribute> attributes;

		for (;;)
		{
			ShaderAst::StatementPtr statement = ParseRootStatement();
			if (!m_context->module)
			{
				const Token& nextToken = Peek();
				throw ParserUnexpectedTokenError{ nextToken.location };
			}

			if (!statement)
				break;

			m_context->module->rootNode->statements.push_back(std::move(statement));
		}

		return std::move(context.module);
	}

	const Token& Parser::Advance()
	{
		const Token& token = Peek();
		m_context->tokenIndex++;

		return token;
	}

	void Parser::Consume(std::size_t count)
	{
		assert(m_context->tokenIndex + count < m_context->tokenCount);
		m_context->tokenIndex += count;
	}

	const Token& Parser::Expect(const Token& token, TokenType type)
	{
		if (token.type != type)
			throw ParserExpectedTokenError{ token.location, type, token.type };

		return token;
	}

	const Token& Parser::ExpectNot(const Token& token, TokenType type)
	{
		if (token.type == type)
			throw ParserUnexpectedTokenError{ token.location, type };

		return token;
	}

	const Token& Parser::Expect(TokenType type)
	{
		const Token& token = Peek();
		Expect(token, type);

		return token;
	}

	const Token& Parser::Peek(std::size_t advance)
	{
		assert(m_context->tokenIndex + advance < m_context->tokenCount);
		return m_context->tokens[m_context->tokenIndex + advance];
	}

	std::vector<Parser::Attribute> Parser::ParseAttributes()
	{
		NAZARA_USE_ANONYMOUS_NAMESPACE

		std::vector<Parser::Attribute> attributes;

		Expect(Advance(), TokenType::OpenSquareBracket);

		bool expectComma = false;
		for (;;)
		{
			const Token& t = Peek();
			ExpectNot(t, TokenType::EndOfStream);

			if (t.type == TokenType::ClosingSquareBracket)
			{
				// Parse [attribute1] [attribute2] the same as [attribute1, attribute2]
				if (Peek(1).type == TokenType::OpenSquareBracket)
				{
					Consume(2);
					expectComma = false;
					continue;
				}

				break;
			}

			if (expectComma)
				Expect(Advance(), TokenType::Comma);

			const Token& identifierToken = Expect(Advance(), TokenType::Identifier);
			std::string_view identifier = std::get<std::string>(identifierToken.data);

			SourceLocation attributeLocation = identifierToken.location;

			auto it = s_identifierToAttributeType.find(identifier);
			if (it == s_identifierToAttributeType.end())
				throw ParserUnknownAttributeError{ identifierToken.location };

			ShaderAst::AttributeType attributeType = it->second;

			ShaderAst::ExpressionPtr arg;
			if (Peek().type == TokenType::OpenParenthesis)
			{
				Consume();

				arg = ParseExpression();

				const Token& closeToken = Expect(Advance(), TokenType::ClosingParenthesis);
				attributeLocation.ExtendToRight(closeToken.location);
			}

			expectComma = true;

			attributes.push_back({
				attributeType,
				std::move(arg),
				attributeLocation
			});
		}

		Expect(Advance(), TokenType::ClosingSquareBracket);

		return attributes;
	}

	void Parser::ParseModuleStatement(std::vector<Attribute> attributes)
	{
		if (m_context->parsingImportedModule)
		{
			const Token& token = Peek();
			throw ParserUnexpectedTokenError{ token.location, token.type };
		}

		const Token& moduleToken = Expect(Advance(), TokenType::Module);

		std::optional<UInt32> moduleVersion;
		std::optional<Uuid> moduleId;
		
		for (auto&& [attributeType, expr, location] : attributes)
		{
			switch (attributeType)
			{
				case ShaderAst::AttributeType::LangVersion:
				{
					// Version parsing
					if (moduleVersion.has_value())
						throw ParserAttributeMultipleUniqueError{ location, attributeType };

					if (!expr)
						throw ParserAttributeMissingParameterError{ location, attributeType };

					if (expr->GetType() != ShaderAst::NodeType::ConstantValueExpression)
						throw ParserAttributeExpectStringError{ location, attributeType };

					auto& constantValue = SafeCast<ShaderAst::ConstantValueExpression&>(*expr);
					if (ShaderAst::GetConstantType(constantValue.value) != ShaderAst::ExpressionType{ ShaderAst::PrimitiveType::String })
						throw ParserAttributeExpectStringError{ location, attributeType };

					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 ParserInvalidVersionError{ location, versionStr };

					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;
				}

				case ShaderAst::AttributeType::Uuid:
				{
					if (moduleId.has_value())
						throw ParserAttributeMultipleUniqueError{ location, attributeType };

					if (!expr)
						throw ParserAttributeMissingParameterError{ location, attributeType };

					if (expr->GetType() != ShaderAst::NodeType::ConstantValueExpression)
						throw ParserAttributeExpectStringError{ location, attributeType };

					auto& constantValue = SafeCast<ShaderAst::ConstantValueExpression&>(*expr);
					if (ShaderAst::GetConstantType(constantValue.value) != ShaderAst::ExpressionType{ ShaderAst::PrimitiveType::String })
						throw ParserAttributeExpectStringError{ location, attributeType };

					const std::string& uuidStr = std::get<std::string>(constantValue.value);

					Uuid uuid = Uuid::FromString(uuidStr);
					if (uuid.IsNull())
						throw ParserInvalidUuidError{ location, uuidStr };

					moduleId = uuid;
					break;
				}

				default:
					throw ParserUnexpectedAttributeError{ location, attributeType };
			}
		}

		if (!moduleVersion.has_value())
			throw ParserMissingAttributeError{ moduleToken.location, ShaderAst::AttributeType::LangVersion };

		if (!moduleId)
			moduleId = Uuid::Generate();

		if (m_context->module)
		{
			std::string moduleName = ParseModuleName();
			auto module = std::make_shared<ShaderAst::Module>(*moduleVersion, std::move(moduleName), *moduleId);

			// Imported module
			Expect(Advance(), TokenType::OpenCurlyBracket);

			m_context->parsingImportedModule = true;

			while (Peek().type != TokenType::ClosingCurlyBracket)
			{
				ShaderAst::StatementPtr statement = ParseRootStatement();
				if (!statement)
				{
					const Token& token = Peek();
					throw ParserUnexpectedEndOfFileError{ token.location };
				}

				module->rootNode->statements.push_back(std::move(statement));
			}
			Consume(); //< Consume ClosingCurlyBracket

			m_context->parsingImportedModule = false;

			auto& importedModule = m_context->module->importedModules.emplace_back();
			importedModule.module = std::move(module);
			importedModule.identifier = importedModule.module->metadata->moduleName;
		}
		else
		{
			std::string moduleName;
			if (Peek().type == TokenType::Identifier)
				moduleName = ParseModuleName();

			auto module = std::make_shared<ShaderAst::Module>(*moduleVersion, std::move(moduleName), *moduleId);

			// First declaration
			Expect(Advance(), TokenType::Semicolon);

			if (m_context->module)
				throw ParserDuplicateModuleError{ moduleToken.location };

			m_context->module = std::move(module);
		}
	}

	void Parser::ParseVariableDeclaration(std::string& name, ShaderAst::ExpressionValue<ShaderAst::ExpressionType>& type, ShaderAst::ExpressionPtr& initialValue, SourceLocation& sourceLocation)
	{
		name = ParseIdentifierAsName(nullptr);

		if (Peek().type == TokenType::Colon)
		{
			Expect(Advance(), TokenType::Colon);

			type = ParseType();
		}

		if (!type.HasValue() || Peek().type == TokenType::Assign)
		{
			Expect(Advance(), TokenType::Assign);
			initialValue = ParseExpression();
		}

		const Token& endToken = Expect(Advance(), TokenType::Semicolon);
		sourceLocation.ExtendToRight(endToken.location);
	}

	ShaderAst::ExpressionPtr Parser::BuildIdentifierAccess(ShaderAst::ExpressionPtr lhs, ShaderAst::ExpressionPtr rhs)
	{
		if (rhs->GetType() == ShaderAst::NodeType::IdentifierExpression)
		{
			SourceLocation location = SourceLocation::BuildFromTo(lhs->sourceLocation, rhs->sourceLocation);

			auto accessMemberExpr = ShaderBuilder::AccessMember(std::move(lhs), { std::move(SafeCast<ShaderAst::IdentifierExpression&>(*rhs).identifier) });
			accessMemberExpr->sourceLocation = std::move(location);

			return accessMemberExpr;
		}
		else
			return BuildIndexAccess(std::move(lhs), std::move(rhs));
	}

	ShaderAst::ExpressionPtr Parser::BuildIndexAccess(ShaderAst::ExpressionPtr lhs, ShaderAst::ExpressionPtr rhs)
	{
		SourceLocation location = SourceLocation::BuildFromTo(lhs->sourceLocation, rhs->sourceLocation);

		auto accessIndexExpr = ShaderBuilder::AccessIndex(std::move(lhs), std::move(rhs));
		accessIndexExpr->sourceLocation = std::move(location);

		return accessIndexExpr;
	}

	ShaderAst::ExpressionPtr Parser::BuildBinary(ShaderAst::BinaryType binaryType, ShaderAst::ExpressionPtr lhs, ShaderAst::ExpressionPtr rhs)
	{
		SourceLocation location = SourceLocation::BuildFromTo(lhs->sourceLocation, rhs->sourceLocation);

		auto accessIndexExpr = ShaderBuilder::Binary(binaryType, std::move(lhs), std::move(rhs));
		accessIndexExpr->sourceLocation = std::move(location);

		return accessIndexExpr;
	}

	ShaderAst::StatementPtr Parser::ParseAliasDeclaration()
	{
		const Token& aliasToken = Expect(Advance(), TokenType::Alias);

		std::string name = ParseIdentifierAsName(nullptr);

		Expect(Advance(), TokenType::Assign);

		ShaderAst::ExpressionPtr expr = ParseExpression();

		const Token& endToken = Expect(Advance(), TokenType::Semicolon);

		auto aliasStatement = ShaderBuilder::DeclareAlias(std::move(name), std::move(expr));
		aliasStatement->sourceLocation = SourceLocation::BuildFromTo(aliasToken.location, endToken.location);

		return aliasStatement;
	}

	ShaderAst::StatementPtr Parser::ParseBranchStatement()
	{
		std::unique_ptr<ShaderAst::BranchStatement> branch = std::make_unique<ShaderAst::BranchStatement>();

		bool first = true;
		for (;;)
		{
			if (!first)
				Expect(Advance(), TokenType::Else);

			const Token& ifToken = Expect(Advance(), TokenType::If);
			if (first)
				branch->sourceLocation = ifToken.location;

			first = false;

			auto& condStatement = branch->condStatements.emplace_back();

			Expect(Advance(), TokenType::OpenParenthesis);

			condStatement.condition = ParseExpression();

			Expect(Advance(), TokenType::ClosingParenthesis);

			condStatement.statement = ParseStatement();
			branch->sourceLocation.ExtendToRight(condStatement.statement->sourceLocation);

			if (Peek().type != TokenType::Else || Peek(1).type != TokenType::If)
				break;
		}

		if (Peek().type == TokenType::Else)
		{
			Consume();
			branch->elseStatement = ParseStatement();
			branch->sourceLocation.ExtendToRight(branch->elseStatement->sourceLocation);
		}

		return branch;
	}

	ShaderAst::StatementPtr Parser::ParseConstStatement()
	{
		const Token& constToken = Expect(Advance(), TokenType::Const);

		SourceLocation constLocation = constToken.location;

		const Token& token = Peek();
		switch (token.type)
		{
			case TokenType::Identifier:
			{
				std::string constName;
				ShaderAst::ExpressionValue<ShaderAst::ExpressionType> constType;
				ShaderAst::ExpressionPtr initialValue;

				ParseVariableDeclaration(constName, constType, initialValue, constLocation);

				auto constDeclaration = ShaderBuilder::DeclareConst(std::move(constName), std::move(constType), std::move(initialValue));
				constDeclaration->sourceLocation = std::move(constLocation);

				return constDeclaration;
			}

			case TokenType::If:
			{
				auto branch = ParseBranchStatement();
				SafeCast<ShaderAst::BranchStatement&>(*branch).isConst = true;
				branch->sourceLocation.ExtendToLeft(constLocation);

				return branch;
			}

			default:
				throw ParserUnexpectedTokenError{ token.location, token.type };
		}
	}

	ShaderAst::StatementPtr Parser::ParseDiscardStatement()
	{
		const Token& discardToken = Expect(Advance(), TokenType::Discard);
		const Token& endToken = Expect(Advance(), TokenType::Semicolon);

		auto discardStatement = ShaderBuilder::Discard();
		discardStatement->sourceLocation = SourceLocation::BuildFromTo(discardToken.location, endToken.location);

		return discardStatement;
	}

	ShaderAst::StatementPtr Parser::ParseExternalBlock(std::vector<Attribute> attributes)
	{
		NAZARA_USE_ANONYMOUS_NAMESPACE

		const Token& externalToken = Expect(Advance(), TokenType::External);
		Expect(Advance(), TokenType::OpenCurlyBracket);

		std::unique_ptr<ShaderAst::DeclareExternalStatement> externalStatement = std::make_unique<ShaderAst::DeclareExternalStatement>();
		externalStatement->sourceLocation = externalToken.location;

		ShaderAst::ExpressionValue<bool> condition;

		for (auto&& attribute : attributes)
		{
			switch (attribute.type)
			{
				case ShaderAst::AttributeType::Cond:
					HandleUniqueAttribute(condition, std::move(attribute));
					break;

				case ShaderAst::AttributeType::Set:
					HandleUniqueAttribute(externalStatement->bindingSet, std::move(attribute));
					break;

				default:
					throw ParserUnexpectedAttributeError{ attribute.sourceLocation, attribute.type };
			}
		}

		bool first = true;
		for (;;)
		{
			if (!first)
			{
				const Token& nextToken = Peek();
				if (nextToken.type == TokenType::Comma)
					Consume();
				else
				{
					Expect(nextToken, TokenType::ClosingCurlyBracket);
					break;
				}
			}

			first = false;

			const Token& token = Peek();
			if (token.type == TokenType::ClosingCurlyBracket)
				break;

			auto& extVar = externalStatement->externalVars.emplace_back();

			if (token.type == TokenType::OpenSquareBracket)
			{
				for (auto&& attribute : ParseAttributes())
				{
					switch (attribute.type)
					{
						case ShaderAst::AttributeType::Binding:
							HandleUniqueAttribute(extVar.bindingIndex, std::move(attribute));
							break;

						case ShaderAst::AttributeType::Set:
							HandleUniqueAttribute(extVar.bindingSet, std::move(attribute));
							break;

						default:
							throw ParserUnexpectedAttributeError{ attribute.sourceLocation, attribute.type };
					}
				}
			}

			extVar.name = ParseIdentifierAsName(&extVar.sourceLocation);
			Expect(Advance(), TokenType::Colon);

			auto typeExpr = ParseType();
			extVar.sourceLocation.ExtendToRight(typeExpr->sourceLocation);

			extVar.type = std::move(typeExpr);
		}

		Expect(Advance(), TokenType::ClosingCurlyBracket);

		if (condition.HasValue())
			return ShaderBuilder::ConditionalStatement(std::move(condition).GetExpression(), std::move(externalStatement));
		else
			return externalStatement;
	}

	ShaderAst::StatementPtr Parser::ParseForDeclaration(std::vector<Attribute> attributes)
	{
		NAZARA_USE_ANONYMOUS_NAMESPACE

		const Token& forToken = Expect(Advance(), TokenType::For);

		std::string varName = ParseIdentifierAsName(nullptr);

		Expect(Advance(), TokenType::In);

		ShaderAst::ExpressionPtr expr = ParseExpression();

		if (Peek().type == TokenType::Arrow)
		{
			// Numerical for
			Consume();

			ShaderAst::ExpressionPtr toExpr = ParseExpression();

			ShaderAst::ExpressionPtr stepExpr;
			if (Peek().type == TokenType::Colon)
			{
				Consume();
				stepExpr = ParseExpression();
			}

			ShaderAst::StatementPtr statement = ParseStatement();

			auto forNode = ShaderBuilder::For(std::move(varName), std::move(expr), std::move(toExpr), std::move(stepExpr), std::move(statement));
			forNode->sourceLocation = SourceLocation::BuildFromTo(forToken.location, forNode->statement->sourceLocation);

			// TODO: Deduplicate code
			for (auto&& attribute : attributes)
			{
				switch (attribute.type)
				{
					case ShaderAst::AttributeType::Unroll:
						HandleUniqueStringAttribute(forNode->unroll, std::move(attribute), s_unrollModes, std::make_optional(ShaderAst::LoopUnroll::Always));
						break;

					default:
						throw ParserUnexpectedAttributeError{ attribute.sourceLocation, attribute.type };
				}
			}

			return forNode;
		}
		else
		{
			// For each
			ShaderAst::StatementPtr statement = ParseStatement();

			auto forEachNode = ShaderBuilder::ForEach(std::move(varName), std::move(expr), std::move(statement));
			forEachNode->sourceLocation = SourceLocation::BuildFromTo(forToken.location, forEachNode->statement->sourceLocation);

			// TODO: Deduplicate code
			for (auto&& attribute : attributes)
			{
				switch (attribute.type)
				{
					case ShaderAst::AttributeType::Unroll:
						HandleUniqueStringAttribute(forEachNode->unroll, std::move(attribute), s_unrollModes, std::make_optional(ShaderAst::LoopUnroll::Always));
						break;

					default:
						throw ParserUnexpectedAttributeError{ attribute.sourceLocation, attribute.type };
				}
			}

			return forEachNode;
		}
	}

	ShaderAst::StatementPtr Parser::ParseFunctionDeclaration(std::vector<Attribute> attributes)
	{
		NAZARA_USE_ANONYMOUS_NAMESPACE

		const auto& funcToken = Expect(Advance(), TokenType::FunctionDeclaration);

		std::string functionName = ParseIdentifierAsName(nullptr);

		Expect(Advance(), TokenType::OpenParenthesis);

		std::vector<ShaderAst::DeclareFunctionStatement::Parameter> parameters;

		bool firstParameter = true;
		for (;;)
		{
			const Token& t = Peek();
			ExpectNot(t, TokenType::EndOfStream);

			if (t.type == TokenType::ClosingParenthesis)
				break;

			if (!firstParameter)
				Expect(Advance(), TokenType::Comma);

			parameters.push_back(ParseFunctionParameter());
			firstParameter = false;
		}

		Expect(Advance(), TokenType::ClosingParenthesis);

		ShaderAst::ExpressionValue<ShaderAst::ExpressionType> returnType;
		if (Peek().type == TokenType::Arrow)
		{
			Consume();
			returnType = ParseType();
		}

		SourceLocation functionLocation;
		std::vector<ShaderAst::StatementPtr> functionBody = ParseStatementList(&functionLocation);

		functionLocation.ExtendToLeft(funcToken.location);

		auto func = ShaderBuilder::DeclareFunction(std::move(functionName), std::move(parameters), std::move(functionBody), std::move(returnType));
		func->sourceLocation = std::move(functionLocation);

		ShaderAst::ExpressionValue<bool> condition;

		for (auto&& attribute : attributes)
		{
			switch (attribute.type)
			{
				case ShaderAst::AttributeType::Cond:
					HandleUniqueAttribute(condition, std::move(attribute));
					break;

				case ShaderAst::AttributeType::Entry:
					HandleUniqueStringAttribute(func->entryStage, std::move(attribute), s_entryPoints);
					break;

				case ShaderAst::AttributeType::Export:
					HandleUniqueAttribute(func->isExported, std::move(attribute), true);
					break;

				case ShaderAst::AttributeType::DepthWrite:
					HandleUniqueStringAttribute(func->depthWrite, std::move(attribute), s_depthWriteModes);
					break;

				case ShaderAst::AttributeType::EarlyFragmentTests:
					HandleUniqueAttribute(func->earlyFragmentTests, std::move(attribute));
					break;

				default:
					throw ParserUnexpectedAttributeError{ attribute.sourceLocation, attribute.type };
			}
		}

		if (condition.HasValue())
			return ShaderBuilder::ConditionalStatement(std::move(condition).GetExpression(), std::move(func));
		else
			return func;
	}

	ShaderAst::DeclareFunctionStatement::Parameter Parser::ParseFunctionParameter()
	{
		ShaderAst::DeclareFunctionStatement::Parameter parameter;

		parameter.name = ParseIdentifierAsName(&parameter.sourceLocation);

		Expect(Advance(), TokenType::Colon);

		auto typeExpr = ParseType();
		parameter.sourceLocation.ExtendToRight(typeExpr->sourceLocation);

		parameter.type = std::move(typeExpr);

		return parameter;
	}

	ShaderAst::StatementPtr Parser::ParseImportStatement()
	{
		const Token& importToken = Expect(Advance(), TokenType::Import);

		std::string moduleName = ParseModuleName();

		const Token& endtoken = Expect(Advance(), TokenType::Semicolon);

		auto importStatement = ShaderBuilder::Import(std::move(moduleName));
		importStatement->sourceLocation = SourceLocation::BuildFromTo(importToken.location, endtoken.location);

		return importStatement;
	}

	ShaderAst::StatementPtr Parser::ParseOptionDeclaration()
	{
		const Token& optionToken = Expect(Advance(), TokenType::Option);

		std::string optionName = ParseIdentifierAsName(nullptr);

		Expect(Advance(), TokenType::Colon);

		ShaderAst::ExpressionPtr optionType = ParseType();

		ShaderAst::ExpressionPtr initialValue;
		if (Peek().type == TokenType::Assign)
		{
			Consume();

			initialValue = ParseExpression();
		}

		const Token& endToken = Expect(Advance(), TokenType::Semicolon);

		auto optionDeclarationStatement = ShaderBuilder::DeclareOption(std::move(optionName), std::move(optionType), std::move(initialValue));
		optionDeclarationStatement->sourceLocation = SourceLocation::BuildFromTo(optionToken.location, endToken.location);

		return optionDeclarationStatement;
	}

	ShaderAst::StatementPtr Parser::ParseReturnStatement()
	{
		const Token& returnToken = Expect(Advance(), TokenType::Return);

		ShaderAst::ExpressionPtr expr;
		if (Peek().type != TokenType::Semicolon)
			expr = ParseExpression();

		const Token& endToken = Expect(Advance(), TokenType::Semicolon);

		auto returnStatement = ShaderBuilder::Return(std::move(expr));
		returnStatement->sourceLocation = SourceLocation::BuildFromTo(returnToken.location, endToken.location);

		return returnStatement;
	}

	ShaderAst::StatementPtr Parser::ParseRootStatement(std::vector<Attribute> attributes)
	{
		const Token& nextToken = Peek();
		switch (nextToken.type)
		{
			case TokenType::Alias:
				if (!attributes.empty())
					throw ParserUnexpectedTokenError{ nextToken.location, nextToken.type };

				return ParseAliasDeclaration();

			case TokenType::Const:
				if (!attributes.empty())
					throw ParserUnexpectedTokenError{ nextToken.location, nextToken.type };

				return ParseConstStatement();

			case TokenType::EndOfStream:
				if (!attributes.empty())
					throw ParserUnexpectedTokenError{ nextToken.location, nextToken.type };

				return {};

			case TokenType::External:
				return ParseExternalBlock(std::move(attributes));

			case TokenType::Import:
				if (!attributes.empty())
					throw ParserUnexpectedTokenError{ nextToken.location, nextToken.type };

				return ParseImportStatement();

			case TokenType::OpenSquareBracket:
				assert(attributes.empty());
				return ParseRootStatement(ParseAttributes());

			case TokenType::Module:
				ParseModuleStatement(std::move(attributes));
				return ParseRootStatement();

			case TokenType::Option:
			{
				if (!attributes.empty())
					throw ParserUnexpectedTokenError{ nextToken.location, nextToken.type };

				return ParseOptionDeclaration();
			}

			case TokenType::FunctionDeclaration:
				return ParseFunctionDeclaration(std::move(attributes));

			case TokenType::Struct:
				return ParseStructDeclaration(std::move(attributes));

			default:
				throw ParserUnexpectedTokenError{ nextToken.location, nextToken.type };
		}
	}

	ShaderAst::StatementPtr Parser::ParseSingleStatement()
	{
		std::vector<Attribute> attributes;
		ShaderAst::StatementPtr statement;
		do 
		{
			const Token& token = Peek();
			switch (token.type)
			{
				case TokenType::Const:
					if (!attributes.empty())
						throw ParserUnexpectedTokenError{ token.location, token.type };

					statement = ParseConstStatement();
					break;

				case TokenType::Discard:
					if (!attributes.empty())
						throw ParserUnexpectedTokenError{ token.location, token.type };

					statement = ParseDiscardStatement();
					break;

				case TokenType::For:
					statement = ParseForDeclaration(std::move(attributes));
					attributes.clear();
					break;

				case TokenType::Let:
					if (!attributes.empty())
						throw ParserUnexpectedTokenError{ token.location, token.type };

					statement = ParseVariableDeclaration();
					break;

				case TokenType::Identifier:
					if (!attributes.empty())
						throw ParserUnexpectedTokenError{ token.location, token.type };

					statement = ShaderBuilder::ExpressionStatement(ParseVariableAssignation());
					Expect(Advance(), TokenType::Semicolon);
					break;

				case TokenType::If:
					if (!attributes.empty())
						throw ParserUnexpectedTokenError{ token.location, token.type };

					statement = ParseBranchStatement();
					break;

				case TokenType::OpenSquareBracket:
					assert(attributes.empty());
					attributes = ParseAttributes();
					break;

				case TokenType::Return:
					if (!attributes.empty())
						throw ParserUnexpectedTokenError{ token.location, token.type };

					statement = ParseReturnStatement();
					break;

				case TokenType::While:
					statement = ParseWhileStatement(std::move(attributes));
					attributes.clear();
					break;

				default:
					throw ParserUnexpectedTokenError{ token.location, token.type };
			}
		}
		while (!statement); //< small trick to repeat parsing once we got attributes

		return statement;
	}

	ShaderAst::StatementPtr Parser::ParseStatement()
	{
		if (Peek().type == TokenType::OpenCurlyBracket)
		{
			auto multiStatement = ShaderBuilder::MultiStatement();
			multiStatement->statements = ParseStatementList(&multiStatement->sourceLocation);

			return ShaderBuilder::Scoped(std::move(multiStatement));
		}
		else
			return ParseSingleStatement();
	}

	std::vector<ShaderAst::StatementPtr> Parser::ParseStatementList(SourceLocation* sourceLocation)
	{
		const Token& openToken = Expect(Advance(), TokenType::OpenCurlyBracket);

		std::vector<ShaderAst::StatementPtr> statements;
		while (Peek().type != TokenType::ClosingCurlyBracket)
		{
			ExpectNot(Peek(), TokenType::EndOfStream);
			statements.push_back(ParseStatement());
		}
		const Token& closeToken = Expect(Advance(), TokenType::ClosingCurlyBracket);

		if (sourceLocation)
			*sourceLocation = SourceLocation::BuildFromTo(openToken.location, closeToken.location);

		return statements;
	}
	
	ShaderAst::StatementPtr Parser::ParseStructDeclaration(std::vector<Attribute> attributes)
	{
		NAZARA_USE_ANONYMOUS_NAMESPACE

		const auto& structToken = Expect(Advance(), TokenType::Struct);

		ShaderAst::StructDescription description;
		description.name = ParseIdentifierAsName(nullptr);
		
		ShaderAst::ExpressionValue<bool> condition;
		ShaderAst::ExpressionValue<bool> exported;

		for (auto&& attribute : attributes)
		{
			switch (attribute.type)
			{
				case ShaderAst::AttributeType::Cond:
					HandleUniqueAttribute(condition, std::move(attribute));
					break;

				case ShaderAst::AttributeType::Export:
					HandleUniqueAttribute(exported, std::move(attribute), true);
					break;

				case ShaderAst::AttributeType::Layout:
					HandleUniqueStringAttribute(description.layout, std::move(attribute), s_layoutMapping);
					break;

				default:
					throw ParserUnexpectedAttributeError{ attribute.sourceLocation, attribute.type };
			}
		}

		Expect(Advance(), TokenType::OpenCurlyBracket);

		bool first = true;

		for (;;)
		{
			if (!first)
			{
				const Token& nextToken = Peek();
				if (nextToken.type == TokenType::Comma)
					Consume();
				else
				{
					Expect(nextToken, TokenType::ClosingCurlyBracket);
					break;
				}
			}

			first = false;

			const Token& token = Peek();
			if (token.type == TokenType::ClosingCurlyBracket)
				break;

			auto& structField = description.members.emplace_back();

			if (token.type == TokenType::OpenSquareBracket)
			{
				for (auto&& attribute : ParseAttributes())
				{
					switch (attribute.type)
					{
						case ShaderAst::AttributeType::Builtin:
							HandleUniqueStringAttribute(structField.builtin, std::move(attribute), s_builtinMapping);
							break;

						case ShaderAst::AttributeType::Cond:
							HandleUniqueAttribute(structField.cond, std::move(attribute));
							break;

						case ShaderAst::AttributeType::Location:
							HandleUniqueAttribute(structField.locationIndex, std::move(attribute));
							break;

						default:
							throw ParserUnexpectedAttributeError{ attribute.sourceLocation, attribute.type };
					}
				}
			}

			structField.name = ParseIdentifierAsName(&structField.sourceLocation);

			Expect(Advance(), TokenType::Colon);

			structField.type = ParseType();
		}

		const Token& endToken = Expect(Advance(), TokenType::ClosingCurlyBracket);

		auto structDeclStatement = ShaderBuilder::DeclareStruct(std::move(description), std::move(exported));
		structDeclStatement->sourceLocation = SourceLocation::BuildFromTo(structToken.location, endToken.location);

		if (condition.HasValue())
		{
			auto condStatement = ShaderBuilder::ConditionalStatement(std::move(condition).GetExpression(), std::move(structDeclStatement));
			condStatement->sourceLocation = condStatement->statement->sourceLocation;

			return condStatement;
		}
		else
			return structDeclStatement;
	}

	ShaderAst::ExpressionPtr Parser::ParseVariableAssignation()
	{
		// Variable expression
		ShaderAst::ExpressionPtr left = ParseExpression();

		// Assignation type 
		ShaderAst::AssignType assignType;

		const Token& token = Peek();
		switch (token.type)
		{
			case TokenType::Assign: assignType = ShaderAst::AssignType::Simple; break;
			case TokenType::DivideAssign: assignType = ShaderAst::AssignType::CompoundDivide; break;
			case TokenType::LogicalAndAssign: assignType = ShaderAst::AssignType::CompoundLogicalAnd; break;
			case TokenType::LogicalOrAssign: assignType = ShaderAst::AssignType::CompoundLogicalOr; break;
			case TokenType::MultiplyAssign: assignType = ShaderAst::AssignType::CompoundMultiply; break;
			case TokenType::MinusAssign: assignType = ShaderAst::AssignType::CompoundSubtract; break;
			case TokenType::PlusAssign: assignType = ShaderAst::AssignType::CompoundAdd; break;

			default:
				throw ParserUnexpectedTokenError{ token.location };
		}

		Consume();

		// Value expression
		ShaderAst::ExpressionPtr right = ParseExpression();

		auto assignExpr = ShaderBuilder::Assign(assignType, std::move(left), std::move(right));
		assignExpr->sourceLocation = SourceLocation::BuildFromTo(assignExpr->left->sourceLocation, assignExpr->right->sourceLocation);

		return assignExpr;
	}

	ShaderAst::StatementPtr Parser::ParseVariableDeclaration()
	{
		const auto& letToken = Expect(Advance(), TokenType::Let);

		SourceLocation letLocation = letToken.location;

		std::string variableName;
		ShaderAst::ExpressionValue<ShaderAst::ExpressionType> variableType;
		ShaderAst::ExpressionPtr expression;

		ParseVariableDeclaration(variableName, variableType, expression, letLocation);

		auto variableDeclStatement = ShaderBuilder::DeclareVariable(std::move(variableName), std::move(variableType), std::move(expression));
		variableDeclStatement->sourceLocation = std::move(letLocation);

		return variableDeclStatement;
	}

	ShaderAst::StatementPtr Parser::ParseWhileStatement(std::vector<Attribute> attributes)
	{
		NAZARA_USE_ANONYMOUS_NAMESPACE

		const Token& whileToken = Expect(Advance(), TokenType::While);

		Expect(Advance(), TokenType::OpenParenthesis);

		ShaderAst::ExpressionPtr condition = ParseExpression();

		Expect(Advance(), TokenType::ClosingParenthesis);

		ShaderAst::StatementPtr body = ParseStatement();

		auto whileStatement = ShaderBuilder::While(std::move(condition), std::move(body));
		whileStatement->sourceLocation = SourceLocation::BuildFromTo(whileToken.location, whileStatement->body->sourceLocation);

		for (auto&& attribute : attributes)
		{
			switch (attribute.type)
			{
				case ShaderAst::AttributeType::Unroll:
					HandleUniqueStringAttribute(whileStatement->unroll, std::move(attribute), s_unrollModes, std::make_optional(ShaderAst::LoopUnroll::Always));
					break;

				default:
					throw ParserUnexpectedAttributeError{ attribute.sourceLocation, attribute.type };
			}
		}

		return whileStatement;
	}

	ShaderAst::ExpressionPtr Parser::ParseBinOpRhs(int exprPrecedence, ShaderAst::ExpressionPtr lhs)
	{
		for (;;)
		{
			const Token& token = Peek();
			TokenType currentTokenType = token.type;
			if (currentTokenType == TokenType::EndOfStream)
				throw ParserUnexpectedTokenError{ token.location, token.type };

			int tokenPrecedence = GetTokenPrecedence(currentTokenType);
			if (tokenPrecedence < exprPrecedence)
				return lhs;

			if (currentTokenType == TokenType::OpenParenthesis)
			{
				Consume();

				// Function call
				SourceLocation closingLocation;
				auto parameters = ParseExpressionList(TokenType::ClosingParenthesis, &closingLocation);

				const SourceLocation& lhsLoc = lhs->sourceLocation;
				lhs = ShaderBuilder::CallFunction(std::move(lhs), std::move(parameters));
				lhs->sourceLocation = SourceLocation::BuildFromTo(lhsLoc, closingLocation);
				continue;
			}

			if (currentTokenType == TokenType::OpenSquareBracket)
			{
				Consume();

				// Indices
				SourceLocation closingLocation;
				auto parameters = ParseExpressionList(TokenType::ClosingSquareBracket, &closingLocation);

				const SourceLocation& lhsLoc = lhs->sourceLocation;
				lhs = ShaderBuilder::AccessIndex(std::move(lhs), std::move(parameters));
				lhs->sourceLocation = SourceLocation::BuildFromTo(lhsLoc, closingLocation);
				continue;
			}

			Consume();
			ShaderAst::ExpressionPtr rhs = ParsePrimaryExpression();

			const Token& nextOp = Peek();

			int nextTokenPrecedence = GetTokenPrecedence(nextOp.type);
			if (tokenPrecedence < nextTokenPrecedence)
				rhs = ParseBinOpRhs(tokenPrecedence + 1, std::move(rhs));

			lhs = [&]
			{
				switch (currentTokenType)
				{
					case TokenType::Dot:
						return BuildIdentifierAccess(std::move(lhs), std::move(rhs));

					case TokenType::Divide:            return BuildBinary(ShaderAst::BinaryType::Divide,     std::move(lhs), std::move(rhs));
					case TokenType::Equal:             return BuildBinary(ShaderAst::BinaryType::CompEq,     std::move(lhs), std::move(rhs));
					case TokenType::LessThan:          return BuildBinary(ShaderAst::BinaryType::CompLt,     std::move(lhs), std::move(rhs));
					case TokenType::LessThanEqual:     return BuildBinary(ShaderAst::BinaryType::CompLe,     std::move(lhs), std::move(rhs));
					case TokenType::LogicalAnd:        return BuildBinary(ShaderAst::BinaryType::LogicalAnd, std::move(lhs), std::move(rhs));
					case TokenType::LogicalOr:         return BuildBinary(ShaderAst::BinaryType::LogicalOr,  std::move(lhs), std::move(rhs));
					case TokenType::GreaterThan:       return BuildBinary(ShaderAst::BinaryType::CompGt,     std::move(lhs), std::move(rhs));
					case TokenType::GreaterThanEqual:  return BuildBinary(ShaderAst::BinaryType::CompGe,     std::move(lhs), std::move(rhs));
					case TokenType::Minus:             return BuildBinary(ShaderAst::BinaryType::Subtract,   std::move(lhs), std::move(rhs));
					case TokenType::Multiply:          return BuildBinary(ShaderAst::BinaryType::Multiply,   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));
					default:
						throw ParserUnexpectedTokenError{ token.location, token.type };
				}
			}();
		}
	}

	ShaderAst::ExpressionPtr Parser::ParseConstSelectExpression()
	{
		const Token& constSelectToken = Expect(Advance(), TokenType::ConstSelect);
		Expect(Advance(), TokenType::OpenParenthesis);

		ShaderAst::ExpressionPtr cond = ParseExpression();

		Expect(Advance(), TokenType::Comma);

		ShaderAst::ExpressionPtr trueExpr = ParseExpression();

		Expect(Advance(), TokenType::Comma);

		ShaderAst::ExpressionPtr falseExpr = ParseExpression();

		const Token& closeToken = Expect(Advance(), TokenType::ClosingParenthesis);

		auto condExpr = ShaderBuilder::ConditionalExpression(std::move(cond), std::move(trueExpr), std::move(falseExpr));
		condExpr->sourceLocation = SourceLocation::BuildFromTo(constSelectToken.location, closeToken.location);

		return condExpr;
	}

	ShaderAst::ExpressionPtr Parser::ParseExpression()
	{
		return ParseBinOpRhs(0, ParsePrimaryExpression());
	}

	std::vector<ShaderAst::ExpressionPtr> Parser::ParseExpressionList(TokenType terminationToken, SourceLocation* terminationLocation)
	{
		std::vector<ShaderAst::ExpressionPtr> parameters;
		bool first = true;
		while (Peek().type != terminationToken)
		{
			if (!first)
				Expect(Advance(), TokenType::Comma);

			first = false;
			parameters.push_back(ParseExpression());
		}

		const Token& endToken = Expect(Advance(), terminationToken);
		if (terminationLocation)
			*terminationLocation = endToken.location;

		return parameters;
	}

	ShaderAst::ExpressionPtr Parser::ParseFloatingPointExpression()
	{
		const Token& floatingPointToken = Expect(Advance(), TokenType::FloatingPointValue);
		auto constantExpr = ShaderBuilder::Constant(float(std::get<double>(floatingPointToken.data))); //< FIXME
		constantExpr->sourceLocation = floatingPointToken.location;

		return constantExpr;
	}

	ShaderAst::ExpressionPtr Parser::ParseIdentifier()
	{
		const Token& identifierToken = Expect(Advance(), TokenType::Identifier);
		const std::string& identifier = std::get<std::string>(identifierToken.data);

		auto identifierExpr = ShaderBuilder::Identifier(identifier);
		identifierExpr->sourceLocation = identifierToken.location;

		return identifierExpr;
	}

	ShaderAst::ExpressionPtr Parser::ParseIntegerExpression()
	{
		const Token& integerToken = Expect(Advance(), TokenType::IntegerValue);
		auto constantExpr = ShaderBuilder::Constant(SafeCast<Int32>(std::get<long long>(integerToken.data))); //< FIXME
		constantExpr->sourceLocation = integerToken.location;

		return constantExpr;
	}

	ShaderAst::ExpressionPtr Parser::ParseParenthesisExpression()
	{
		const Token& openToken = Expect(Advance(), TokenType::OpenParenthesis);
		ShaderAst::ExpressionPtr expression = ParseExpression();
		const Token& closeToken = Expect(Advance(), TokenType::ClosingParenthesis);

		expression->sourceLocation = SourceLocation::BuildFromTo(openToken.location, closeToken.location);

		return expression;
	}

	ShaderAst::ExpressionPtr Parser::ParsePrimaryExpression()
	{
		const Token& token = Peek();

		ShaderAst::ExpressionPtr primaryExpr;
		switch (token.type)
		{
			case TokenType::BoolFalse:
				Consume();
				primaryExpr = ShaderBuilder::Constant(false);
				primaryExpr->sourceLocation = token.location;
				break;

			case TokenType::BoolTrue:
				Consume();
				primaryExpr = ShaderBuilder::Constant(true);
				primaryExpr->sourceLocation = token.location;
				break;

			case TokenType::ConstSelect:
				primaryExpr = ParseConstSelectExpression();
				break;

			case TokenType::FloatingPointValue:
				primaryExpr = ParseFloatingPointExpression();
				break;

			case TokenType::Identifier:
				primaryExpr = ParseIdentifier();
				break;

			case TokenType::IntegerValue:
				primaryExpr = ParseIntegerExpression();
				break;

			case TokenType::Minus:
			{
				Consume();
				ShaderAst::ExpressionPtr expr = ParseExpression();

				auto minusExpr = ShaderBuilder::Unary(ShaderAst::UnaryType::Minus, std::move(expr));
				minusExpr->sourceLocation = SourceLocation::BuildFromTo(token.location, minusExpr->expression->sourceLocation);

				primaryExpr = std::move(minusExpr);
				break;
			}

			case TokenType::Plus:
			{
				Consume();
				ShaderAst::ExpressionPtr expr = ParseExpression();

				auto plusExpr = ShaderBuilder::Unary(ShaderAst::UnaryType::Plus, std::move(expr));
				plusExpr->sourceLocation = SourceLocation::BuildFromTo(token.location, plusExpr->expression->sourceLocation);

				primaryExpr = std::move(plusExpr);
				break;
			}

			case TokenType::Not:
			{
				Consume();
				ShaderAst::ExpressionPtr expr = ParseExpression();

				auto notExpr = ShaderBuilder::Unary(ShaderAst::UnaryType::LogicalNot, std::move(expr));
				notExpr->sourceLocation = SourceLocation::BuildFromTo(token.location, notExpr->expression->sourceLocation);

				primaryExpr = std::move(notExpr);
				break;
			}

			case TokenType::OpenParenthesis:
				primaryExpr = ParseParenthesisExpression();
				break;

			case TokenType::StringValue:
				primaryExpr = ParseStringExpression();
				break;

			default:
				throw ParserUnexpectedTokenError{ token.location, token.type };
		}

		primaryExpr->sourceLocation.ExtendToLeft(token.location);

		return primaryExpr;
	}

	ShaderAst::ExpressionPtr Parser::ParseStringExpression()
	{
		const Token& litteralToken = Expect(Advance(), TokenType::StringValue);
		auto constantExpr = ShaderBuilder::Constant(std::get<std::string>(litteralToken.data));
		constantExpr->sourceLocation = litteralToken.location;

		return constantExpr;
	}

	const std::string& Parser::ParseIdentifierAsName(SourceLocation* sourceLocation)
	{
		const Token& identifierToken = Expect(Advance(), TokenType::Identifier);
		if (sourceLocation)
			*sourceLocation = identifierToken.location;

		return std::get<std::string>(identifierToken.data);
	}

	std::string Parser::ParseModuleName()
	{
		std::string moduleName = ParseIdentifierAsName(nullptr);
		while (Peek().type == TokenType::Dot)
		{
			Consume();
			moduleName += '.';
			moduleName += ParseIdentifierAsName(nullptr);
		}

		return moduleName;
	}

	ShaderAst::ExpressionPtr Parser::ParseType()
	{
		// Handle () as no type
		const Token& openToken = Peek();
		if (openToken.type == TokenType::OpenParenthesis)
		{
			Consume();
			const Token& closeToken = Expect(Advance(), TokenType::ClosingParenthesis);

			auto constantExpr = ShaderBuilder::Constant(ShaderAst::NoValue{});
			constantExpr->sourceLocation = closeToken.location;

			return constantExpr;
		}

		return ParseExpression();
	}

	template<typename T>
	void Parser::HandleUniqueAttribute(ShaderAst::ExpressionValue<T>& targetAttribute, Parser::Attribute&& attribute)
	{
		if (targetAttribute.HasValue())
			throw ParserAttributeMultipleUniqueError{ attribute.sourceLocation, attribute.type };

		if (!attribute.args)
			throw ParserAttributeMissingParameterError{ attribute.sourceLocation, attribute.type };

		targetAttribute = std::move(attribute.args);
	}

	template<typename T>
	void Parser::HandleUniqueAttribute(ShaderAst::ExpressionValue<T>& targetAttribute, Parser::Attribute&& attribute, T defaultValue)
	{
		if (targetAttribute.HasValue())
			throw ParserAttributeMultipleUniqueError{ attribute.sourceLocation, attribute.type };

		if (attribute.args)
			targetAttribute = std::move(attribute.args);
		else
			targetAttribute = std::move(defaultValue);
	}

	template<typename T, typename M>
	void Parser::HandleUniqueStringAttribute(ShaderAst::ExpressionValue<T>& targetAttribute, Parser::Attribute&& attribute, const M& map, std::optional<T> defaultValue)
	{
		if (targetAttribute.HasValue())
			throw ParserAttributeMultipleUniqueError{ attribute.sourceLocation, attribute.type };

		//FIXME: This should be handled with global values at sanitization stage
		if (attribute.args)
		{
			if (attribute.args->GetType() != ShaderAst::NodeType::IdentifierExpression)
				throw ParserAttributeParameterIdentifierError{ attribute.args->sourceLocation, attribute.type };

			std::string_view exprStr = static_cast<ShaderAst::IdentifierExpression&>(*attribute.args).identifier;

			auto it = map.find(exprStr);
			if (it == map.end())
				throw ParserAttributeInvalidParameterError{ attribute.args->sourceLocation, exprStr, attribute.type };

			targetAttribute = it->second;
		}
		else
		{
			if (!defaultValue)
				throw ParserAttributeMissingParameterError{ attribute.sourceLocation, attribute.type };

			targetAttribute = defaultValue.value();
		}
	}

	int Parser::GetTokenPrecedence(TokenType token)
	{
		switch (token)
		{
			case TokenType::Divide:            return 80;
			case TokenType::Dot:               return 150;
			case TokenType::Equal:             return 50;
			case TokenType::LessThan:          return 40;
			case TokenType::LessThanEqual:     return 40;
			case TokenType::LogicalAnd:        return 20;
			case TokenType::LogicalOr:         return 10;
			case TokenType::GreaterThan:       return 40;
			case TokenType::GreaterThanEqual:  return 40;
			case TokenType::Multiply:          return 80;
			case TokenType::Minus:             return 60;
			case TokenType::NotEqual:          return 50;
			case TokenType::Plus:              return 60;
			case TokenType::OpenSquareBracket: return 100;
			case TokenType::OpenParenthesis:   return 100;
			default: return -1;
		}
	}

	ShaderAst::ModulePtr ParseFromFile(const std::filesystem::path& sourcePath)
	{
		File file(sourcePath);
		if (!file.Open(OpenMode::ReadOnly | OpenMode::Text))
		{
			NazaraError("Failed to open \"" + sourcePath.generic_u8string() + '"');
			return {};
		}

		std::size_t length = static_cast<std::size_t>(file.GetSize());
		if (length == 0)
			return {};

		std::vector<Nz::UInt8> source(length);
		if (file.Read(&source[0], length) != length)
		{
			NazaraError("Failed to read shader file");
			return {};
		}

		return Parse(std::string_view(reinterpret_cast<const char*>(source.data()), source.size()), sourcePath.generic_u8string());
	}
}
