// Copyright (C) 2021 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 #include #include #include namespace Nz::ShaderLang { namespace { std::unordered_map s_depthWriteModes = { { "greater", ShaderAst::DepthWriteMode::Greater }, { "less", ShaderAst::DepthWriteMode::Less }, { "replace", ShaderAst::DepthWriteMode::Replace }, { "unchanged", ShaderAst::DepthWriteMode::Unchanged }, }; std::unordered_map s_identifierToBasicType = { { "bool", ShaderAst::PrimitiveType::Boolean }, { "i32", ShaderAst::PrimitiveType::Int32 }, { "f32", ShaderAst::PrimitiveType::Float32 }, { "u32", ShaderAst::PrimitiveType::UInt32 } }; std::unordered_map s_identifierToAttributeType = { { "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 }, { "layout", ShaderAst::AttributeType::Layout }, { "location", ShaderAst::AttributeType::Location }, { "set", ShaderAst::AttributeType::Set }, }; std::unordered_map s_entryPoints = { { "frag", ShaderStageType::Fragment }, { "vert", ShaderStageType::Vertex }, }; std::unordered_map s_builtinMapping = { { "fragcoord", ShaderAst::BuiltinEntry::FragCoord }, { "fragdepth", ShaderAst::BuiltinEntry::FragDepth }, { "position", ShaderAst::BuiltinEntry::VertexPosition } }; std::unordered_map s_layoutMapping = { { "std140", StructLayout::Std140 } }; template std::optional BoundCast(U val) { if (val < std::numeric_limits::min() || val > std::numeric_limits::max()) return std::nullopt; return static_cast(val); } template void HandleUniqueAttribute(const std::string_view& attributeName, ShaderAst::AttributeValue& targetAttribute, ShaderAst::Attribute::Param&& param, bool requireValue = true) { if (targetAttribute.HasValue()) throw AttributeError{ "attribute " + std::string(attributeName) + " must be present once" }; if (!param && requireValue) throw AttributeError{ "attribute " + std::string(attributeName) + " requires a parameter" }; targetAttribute = std::move(*param); } template void HandleUniqueStringAttribute(const std::string_view& attributeName, const std::unordered_map& map, ShaderAst::AttributeValue& targetAttribute, ShaderAst::Attribute::Param&& param) { if (targetAttribute.HasValue()) throw AttributeError{ "attribute " + std::string(attributeName) + " must be present once" }; //FIXME: This should be handled with global values at sanitization stage if (!param) throw AttributeError{ "attribute " + std::string(attributeName) + " requires a value" }; const ShaderAst::ExpressionPtr& expr = *param; if (expr->GetType() != ShaderAst::NodeType::IdentifierExpression) throw AttributeError{ "attribute " + std::string(attributeName) + " can only be an identifier for now" }; const std::string& exprStr = static_cast(*expr).identifier; auto it = map.find(exprStr); if (it == map.end()) throw AttributeError{ ("invalid parameter " + exprStr + " for " + std::string(attributeName) + " attribute").c_str() }; targetAttribute = it->second; } } ShaderAst::StatementPtr Parser::Parse(const std::vector& tokens) { Context context; context.tokenCount = tokens.size(); context.tokens = tokens.data(); context.root = std::make_unique(); m_context = &context; std::vector attributes; EnterScope(); bool reachedEndOfStream = false; while (!reachedEndOfStream) { const Token& nextToken = Peek(); switch (nextToken.type) { case TokenType::Const: if (!attributes.empty()) throw UnexpectedToken{}; context.root->statements.push_back(ParseConstStatement()); break; case TokenType::EndOfStream: if (!attributes.empty()) throw UnexpectedToken{}; reachedEndOfStream = true; break; case TokenType::External: context.root->statements.push_back(ParseExternalBlock(std::move(attributes))); attributes.clear(); break; case TokenType::OpenSquareBracket: assert(attributes.empty()); attributes = ParseAttributes(); break; case TokenType::Option: if (!attributes.empty()) throw UnexpectedToken{}; context.root->statements.push_back(ParseOptionDeclaration()); break; case TokenType::FunctionDeclaration: context.root->statements.push_back(ParseFunctionDeclaration(std::move(attributes))); attributes.clear(); break; case TokenType::Struct: context.root->statements.push_back(ParseStructDeclaration(std::move(attributes))); attributes.clear(); break; default: throw UnexpectedToken{}; } } LeaveScope(); return std::move(context.root); } 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; } std::optional Parser::DecodeType(const std::string& identifier) { if (auto it = s_identifierToBasicType.find(identifier); it != s_identifierToBasicType.end()) { Consume(); return it->second; } //FIXME: Handle this better if (identifier == "mat4") { Consume(); ShaderAst::MatrixType matrixType; matrixType.columnCount = 4; matrixType.rowCount = 4; Expect(Advance(), TokenType::LessThan); //< '<' matrixType.type = ParsePrimitiveType(); Expect(Advance(), TokenType::GreaterThan); //< '>' return matrixType; } else if (identifier == "mat3") { Consume(); ShaderAst::MatrixType matrixType; matrixType.columnCount = 3; matrixType.rowCount = 3; Expect(Advance(), TokenType::LessThan); //< '<' matrixType.type = ParsePrimitiveType(); Expect(Advance(), TokenType::GreaterThan); //< '>' return matrixType; } else if (identifier == "sampler2D") { Consume(); ShaderAst::SamplerType samplerType; samplerType.dim = ImageType::E2D; Expect(Advance(), TokenType::LessThan); //< '<' samplerType.sampledType = ParsePrimitiveType(); Expect(Advance(), TokenType::GreaterThan); //< '>' return samplerType; } else if (identifier == "samplerCube") { Consume(); ShaderAst::SamplerType samplerType; samplerType.dim = ImageType::Cubemap; Expect(Advance(), TokenType::LessThan); //< '<' samplerType.sampledType = ParsePrimitiveType(); Expect(Advance(), TokenType::GreaterThan); //< '>' return samplerType; } else if (identifier == "uniform") { Consume(); ShaderAst::UniformType uniformType; Expect(Advance(), TokenType::LessThan); //< '<' uniformType.containedType = ShaderAst::IdentifierType{ ParseIdentifierAsName() }; Expect(Advance(), TokenType::GreaterThan); //< '>' return uniformType; } else if (identifier == "vec2") { Consume(); ShaderAst::VectorType vectorType; vectorType.componentCount = 2; Expect(Advance(), TokenType::LessThan); //< '<' vectorType.type = ParsePrimitiveType(); Expect(Advance(), TokenType::GreaterThan); //< '>' return vectorType; } else if (identifier == "vec3") { Consume(); ShaderAst::VectorType vectorType; vectorType.componentCount = 3; Expect(Advance(), TokenType::LessThan); //< '<' vectorType.type = ParsePrimitiveType(); Expect(Advance(), TokenType::GreaterThan); //< '>' return vectorType; } else if (identifier == "vec4") { Consume(); ShaderAst::VectorType vectorType; vectorType.componentCount = 4; Expect(Advance(), TokenType::LessThan); //< '<' vectorType.type = ParsePrimitiveType(); Expect(Advance(), TokenType::GreaterThan); //< '>' return vectorType; } else return std::nullopt; } void Parser::EnterScope() { m_context->scopeSizes.push_back(m_context->identifiersInScope.size()); } const Token& Parser::Expect(const Token& token, TokenType type) { if (token.type != type) throw ExpectedToken{}; return token; } const Token& Parser::ExpectNot(const Token& token, TokenType type) { if (token.type == type) throw ExpectedToken{}; return token; } const Token& Parser::Expect(TokenType type) { const Token& token = Peek(); Expect(token, type); return token; } void Parser::LeaveScope() { assert(!m_context->scopeSizes.empty()); m_context->identifiersInScope.resize(m_context->scopeSizes.back()); m_context->scopeSizes.pop_back(); } bool Parser::IsVariableInScope(const std::string_view& identifier) const { return std::find(m_context->identifiersInScope.rbegin(), m_context->identifiersInScope.rend(), identifier) != m_context->identifiersInScope.rend(); } void Parser::RegisterVariable(std::string identifier) { assert(!m_context->scopeSizes.empty()); m_context->identifiersInScope.push_back(std::move(identifier)); } 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::ParseAttributes() { std::vector 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); ShaderAst::AttributeType attributeType = ParseIdentifierAsAttributeType(); ShaderAst::Attribute::Param arg; if (Peek().type == TokenType::OpenParenthesis) { Consume(); arg = ParseExpression(); Expect(Advance(), TokenType::ClosingParenthesis); } expectComma = true; attributes.push_back({ attributeType, std::move(arg) }); } Expect(Advance(), TokenType::ClosingSquareBracket); return attributes; } void Parser::ParseVariableDeclaration(std::string& name, ShaderAst::ExpressionType& type, ShaderAst::ExpressionPtr& initialValue) { name = ParseIdentifierAsName(); if (Peek().type == TokenType::Colon) { Expect(Advance(), TokenType::Colon); type = ParseType(); } else type = ShaderAst::NoType{}; if (IsNoType(type) || Peek().type == TokenType::Assign) { Expect(Advance(), TokenType::Assign); initialValue = ParseExpression(); } Expect(Advance(), TokenType::Semicolon); } ShaderAst::StatementPtr Parser::ParseBranchStatement() { std::unique_ptr branch = std::make_unique(); bool first = true; for (;;) { if (!first) Expect(Advance(), TokenType::Else); first = false; Expect(Advance(), TokenType::If); auto& condStatement = branch->condStatements.emplace_back(); Expect(Advance(), TokenType::OpenParenthesis); condStatement.condition = ParseExpression(); Expect(Advance(), TokenType::ClosingParenthesis); condStatement.statement = ParseStatement(); if (Peek().type != TokenType::Else || Peek(1).type != TokenType::If) break; } if (Peek().type == TokenType::Else) { Consume(); branch->elseStatement = ParseStatement(); } return branch; } ShaderAst::StatementPtr Parser::ParseConstStatement() { Expect(Advance(), TokenType::Const); switch (Peek().type) { case TokenType::Identifier: { std::string constName; ShaderAst::ExpressionType constType; ShaderAst::ExpressionPtr initialValue; ParseVariableDeclaration(constName, constType, initialValue); RegisterVariable(constName); return ShaderBuilder::DeclareConst(std::move(constName), std::move(constType), std::move(initialValue)); } case TokenType::If: { auto branch = ParseBranchStatement(); static_cast(*branch).isConst = true; return branch; } default: throw UnexpectedToken{}; } } ShaderAst::StatementPtr Parser::ParseDiscardStatement() { Expect(Advance(), TokenType::Discard); Expect(Advance(), TokenType::Semicolon); return ShaderBuilder::Discard(); } ShaderAst::StatementPtr Parser::ParseExternalBlock(std::vector attributes) { Expect(Advance(), TokenType::External); Expect(Advance(), TokenType::OpenCurlyBracket); std::unique_ptr externalStatement = std::make_unique(); ShaderAst::AttributeValue condition; for (auto&& [attributeType, arg] : attributes) { switch (attributeType) { case ShaderAst::AttributeType::Cond: HandleUniqueAttribute("cond", condition, std::move(arg)); break; case ShaderAst::AttributeType::Set: HandleUniqueAttribute("set", externalStatement->bindingSet, std::move(arg)); break; default: throw AttributeError{ "unhandled attribute for external block" }; } } 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&& [attributeType, arg] : ParseAttributes()) { switch (attributeType) { case ShaderAst::AttributeType::Binding: HandleUniqueAttribute("binding", extVar.bindingIndex, std::move(arg)); break; case ShaderAst::AttributeType::Set: HandleUniqueAttribute("set", extVar.bindingSet, std::move(arg)); break; default: throw AttributeError{ "unhandled attribute for external variable" }; } } } extVar.name = ParseIdentifierAsName(); Expect(Advance(), TokenType::Colon); extVar.type = ParseType(); RegisterVariable(extVar.name); } Expect(Advance(), TokenType::ClosingCurlyBracket); if (condition.HasValue()) return ShaderBuilder::ConditionalStatement(std::move(condition).GetExpression(), std::move(externalStatement)); else return externalStatement; } std::vector Parser::ParseFunctionBody() { return ParseStatementList(); } ShaderAst::StatementPtr Parser::ParseFunctionDeclaration(std::vector attributes) { Expect(Advance(), TokenType::FunctionDeclaration); std::string functionName = ParseIdentifierAsName(); Expect(Advance(), TokenType::OpenParenthesis); std::vector 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::ExpressionType returnType; if (Peek().type == TokenType::FunctionReturn) { Consume(); returnType = ParseType(); } EnterScope(); for (const auto& parameter : parameters) RegisterVariable(parameter.name); std::vector functionBody = ParseFunctionBody(); LeaveScope(); auto func = ShaderBuilder::DeclareFunction(std::move(functionName), std::move(parameters), std::move(functionBody), std::move(returnType)); ShaderAst::AttributeValue condition; for (auto&& [attributeType, arg] : attributes) { switch (attributeType) { case ShaderAst::AttributeType::Cond: HandleUniqueAttribute("cond", condition, std::move(arg)); break; case ShaderAst::AttributeType::Entry: HandleUniqueStringAttribute("entry", s_entryPoints, func->entryStage, std::move(arg)); break; case ShaderAst::AttributeType::DepthWrite: HandleUniqueStringAttribute("depth_write", s_depthWriteModes, func->depthWrite, std::move(arg)); break; case ShaderAst::AttributeType::EarlyFragmentTests: HandleUniqueAttribute("early_fragment_tests", func->earlyFragmentTests, std::move(arg), false); break; default: throw AttributeError{ "unhandled attribute for function" }; } } if (condition.HasValue()) return ShaderBuilder::ConditionalStatement(std::move(condition).GetExpression(), std::move(func)); else return func; } ShaderAst::DeclareFunctionStatement::Parameter Parser::ParseFunctionParameter() { std::string parameterName = ParseIdentifierAsName(); Expect(Advance(), TokenType::Colon); ShaderAst::ExpressionType parameterType = ParseType(); return { parameterName, parameterType }; } ShaderAst::StatementPtr Parser::ParseOptionDeclaration() { Expect(Advance(), TokenType::Option); std::string optionName = ParseIdentifierAsName(); Expect(Advance(), TokenType::Colon); ShaderAst::ExpressionType optionType = ParseType(); ShaderAst::ExpressionPtr initialValue; if (Peek().type == TokenType::Assign) { Consume(); initialValue = ParseExpression(); } Expect(Advance(), TokenType::Semicolon); return ShaderBuilder::DeclareOption(std::move(optionName), std::move(optionType), std::move(initialValue)); } ShaderAst::StatementPtr Parser::ParseReturnStatement() { Expect(Advance(), TokenType::Return); ShaderAst::ExpressionPtr expr; if (Peek().type != TokenType::Semicolon) expr = ParseExpression(); Expect(Advance(), TokenType::Semicolon); return ShaderBuilder::Return(std::move(expr)); } ShaderAst::StatementPtr Parser::ParseSingleStatement() { const Token& token = Peek(); ShaderAst::StatementPtr statement; switch (token.type) { case TokenType::Const: statement = ParseConstStatement(); break; case TokenType::Discard: statement = ParseDiscardStatement(); break; case TokenType::Let: statement = ParseVariableDeclaration(); break; case TokenType::Identifier: statement = ShaderBuilder::ExpressionStatement(ParseVariableAssignation()); Expect(Advance(), TokenType::Semicolon); break; case TokenType::If: statement = ParseBranchStatement(); break; case TokenType::Return: statement = ParseReturnStatement(); break; case TokenType::While: statement = ParseWhileStatement(); break; default: throw UnexpectedToken{}; } return statement; } ShaderAst::StatementPtr Parser::ParseStatement() { if (Peek().type == TokenType::OpenCurlyBracket) return ShaderBuilder::MultiStatement(ParseStatementList()); else return ParseSingleStatement(); } std::vector Parser::ParseStatementList() { EnterScope(); Expect(Advance(), TokenType::OpenCurlyBracket); std::vector statements; while (Peek().type != TokenType::ClosingCurlyBracket) { ExpectNot(Peek(), TokenType::EndOfStream); statements.push_back(ParseStatement()); } Consume(); //< Consume closing curly bracket LeaveScope(); return statements; } ShaderAst::StatementPtr Parser::ParseStructDeclaration(std::vector attributes) { Expect(Advance(), TokenType::Struct); ShaderAst::StructDescription description; description.name = ParseIdentifierAsName(); ShaderAst::AttributeValue condition; for (auto&& [attributeType, attributeParam] : attributes) { switch (attributeType) { case ShaderAst::AttributeType::Cond: HandleUniqueAttribute("cond", condition, std::move(attributeParam)); break; case ShaderAst::AttributeType::Layout: HandleUniqueStringAttribute("layout", s_layoutMapping, description.layout, std::move(attributeParam)); break; default: throw AttributeError{ "unexpected attribute" }; } } 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&& [attributeType, arg] : ParseAttributes()) { switch (attributeType) { case ShaderAst::AttributeType::Builtin: HandleUniqueStringAttribute("builtin", s_builtinMapping, structField.builtin, std::move(arg)); break; case ShaderAst::AttributeType::Cond: HandleUniqueAttribute("cond", structField.cond, std::move(arg)); break; case ShaderAst::AttributeType::Location: HandleUniqueAttribute("location", structField.locationIndex, std::move(arg)); break; default: throw AttributeError{ "unexpected attribute" }; } } if (structField.builtin.HasValue() && structField.locationIndex.HasValue()) throw AttributeError{ "A struct field cannot have both builtin and location attributes" }; } structField.name = ParseIdentifierAsName(); Expect(Advance(), TokenType::Colon); structField.type = ParseType(); } Expect(Advance(), TokenType::ClosingCurlyBracket); if (condition.HasValue()) return ShaderBuilder::ConditionalStatement(std::move(condition).GetExpression(), ShaderBuilder::DeclareStruct(std::move(description))); else return ShaderBuilder::DeclareStruct(std::move(description)); } ShaderAst::ExpressionPtr Parser::ParseVariableAssignation() { // Variable expression ShaderAst::ExpressionPtr left = ParseExpression(); // Assignation type ShaderAst::AssignType assignType; switch (Peek().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 UnexpectedToken{}; } Consume(); // Value expression ShaderAst::ExpressionPtr right = ParseExpression(); return ShaderBuilder::Assign(assignType, std::move(left), std::move(right)); } ShaderAst::StatementPtr Parser::ParseVariableDeclaration() { Expect(Advance(), TokenType::Let); std::string variableName; ShaderAst::ExpressionType variableType; ShaderAst::ExpressionPtr expression; ParseVariableDeclaration(variableName, variableType, expression); RegisterVariable(variableName); return ShaderBuilder::DeclareVariable(std::move(variableName), std::move(variableType), std::move(expression)); } ShaderAst::StatementPtr Parser::ParseWhileStatement() { Expect(Advance(), TokenType::While); Expect(Advance(), TokenType::OpenParenthesis); ShaderAst::ExpressionPtr condition = ParseExpression(); Expect(Advance(), TokenType::ClosingParenthesis); ShaderAst::StatementPtr body = ParseStatement(); return ShaderBuilder::While(std::move(condition), std::move(body)); } ShaderAst::ExpressionPtr Parser::ParseBinOpRhs(int exprPrecedence, ShaderAst::ExpressionPtr lhs) { for (;;) { TokenType currentTokenType = Peek().type; if (currentTokenType == TokenType::EndOfStream) throw UnexpectedToken{}; int tokenPrecedence = GetTokenPrecedence(currentTokenType); if (tokenPrecedence < exprPrecedence) return lhs; bool c = false; while (currentTokenType == TokenType::Dot || currentTokenType == TokenType::OpenSquareBracket) { c = true; if (currentTokenType == TokenType::Dot) { std::unique_ptr accessMemberNode = std::make_unique(); accessMemberNode->expr = std::move(lhs); do { Consume(); accessMemberNode->identifiers.push_back(ParseIdentifierAsName()); } while (Peek().type == TokenType::Dot); // FIXME if (!accessMemberNode->identifiers.empty() && accessMemberNode->identifiers.front() == "Sample") { if (Peek().type == TokenType::OpenParenthesis) { auto parameters = ParseParameters(); parameters.insert(parameters.begin(), std::move(accessMemberNode->expr)); lhs = ShaderBuilder::Intrinsic(ShaderAst::IntrinsicType::SampleTexture, std::move(parameters)); break; } } lhs = std::move(accessMemberNode); } else { assert(currentTokenType == TokenType::OpenSquareBracket); std::unique_ptr indexNode = std::make_unique(); indexNode->expr = std::move(lhs); do { Consume(); indexNode->indices.push_back(ParseExpression()); Expect(Advance(), TokenType::ClosingSquareBracket); } while (Peek().type == TokenType::OpenSquareBracket); lhs = std::move(indexNode); } currentTokenType = Peek().type; } if (c) 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)); ShaderAst::BinaryType binaryType; { switch (currentTokenType) { case TokenType::Divide: binaryType = ShaderAst::BinaryType::Divide; break; case TokenType::Equal: binaryType = ShaderAst::BinaryType::CompEq; break; case TokenType::LessThan: binaryType = ShaderAst::BinaryType::CompLt; break; case TokenType::LessThanEqual: binaryType = ShaderAst::BinaryType::CompLe; break; case TokenType::LogicalAnd: binaryType = ShaderAst::BinaryType::LogicalAnd; break; case TokenType::LogicalOr: binaryType = ShaderAst::BinaryType::LogicalOr; break; case TokenType::GreaterThan: binaryType = ShaderAst::BinaryType::CompGt; break; case TokenType::GreaterThanEqual: binaryType = ShaderAst::BinaryType::CompGe; break; case TokenType::Minus: binaryType = ShaderAst::BinaryType::Subtract; break; case TokenType::Multiply: binaryType = ShaderAst::BinaryType::Multiply; break; case TokenType::NotEqual: binaryType = ShaderAst::BinaryType::CompNe; break; case TokenType::Plus: binaryType = ShaderAst::BinaryType::Add; break; default: throw UnexpectedToken{}; } } lhs = ShaderBuilder::Binary(binaryType, std::move(lhs), std::move(rhs)); } } ShaderAst::ExpressionPtr Parser::ParseConstSelectExpression() { 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(); Expect(Advance(), TokenType::ClosingParenthesis); return ShaderBuilder::ConditionalExpression(std::move(cond), std::move(trueExpr), std::move(falseExpr)); } ShaderAst::ExpressionPtr Parser::ParseExpression() { return ParseBinOpRhs(0, ParsePrimaryExpression()); } ShaderAst::ExpressionPtr Parser::ParseFloatingPointExpression() { const Token& floatingPointToken = Expect(Advance(), TokenType::FloatingPointValue); return ShaderBuilder::Constant(float(std::get(floatingPointToken.data))); //< FIXME } ShaderAst::ExpressionPtr Parser::ParseIdentifier() { const Token& identifierToken = Expect(Advance(), TokenType::Identifier); const std::string& identifier = std::get(identifierToken.data); return ShaderBuilder::Identifier(identifier); } ShaderAst::ExpressionPtr Parser::ParseIntegerExpression() { const Token& integerToken = Expect(Advance(), TokenType::IntegerValue); return ShaderBuilder::Constant(static_cast(std::get(integerToken.data))); //< FIXME } std::vector Parser::ParseParameters() { Expect(Advance(), TokenType::OpenParenthesis); std::vector parameters; bool first = true; while (Peek().type != TokenType::ClosingParenthesis) { if (!first) Expect(Advance(), TokenType::Comma); first = false; parameters.push_back(ParseExpression()); } Expect(Advance(), TokenType::ClosingParenthesis); return parameters; } ShaderAst::ExpressionPtr Parser::ParseParenthesisExpression() { Expect(Advance(), TokenType::OpenParenthesis); ShaderAst::ExpressionPtr expression = ParseExpression(); Expect(Advance(), TokenType::ClosingParenthesis); return expression; } ShaderAst::ExpressionPtr Parser::ParsePrimaryExpression() { const Token& token = Peek(); switch (token.type) { case TokenType::BoolFalse: Consume(); return ShaderBuilder::Constant(false); case TokenType::BoolTrue: Consume(); return ShaderBuilder::Constant(true); case TokenType::ConstSelect: return ParseConstSelectExpression(); case TokenType::FloatingPointValue: return ParseFloatingPointExpression(); case TokenType::Identifier: { const std::string& identifier = std::get(token.data); // Is it a cast? std::optional exprType = DecodeType(identifier); if (exprType) return ShaderBuilder::Cast(std::move(*exprType), ParseParameters()); if (Peek(1).type == TokenType::OpenParenthesis) { // Function call Consume(); return ShaderBuilder::CallFunction(identifier, ParseParameters()); } else return ParseIdentifier(); } case TokenType::IntegerValue: return ParseIntegerExpression(); case TokenType::Minus: { Consume(); ShaderAst::ExpressionPtr expr = ParsePrimaryExpression(); return ShaderBuilder::Unary(ShaderAst::UnaryType::Minus, std::move(expr)); } case TokenType::Plus: { Consume(); ShaderAst::ExpressionPtr expr = ParsePrimaryExpression(); return ShaderBuilder::Unary(ShaderAst::UnaryType::Plus, std::move(expr)); } case TokenType::Not: { Consume(); ShaderAst::ExpressionPtr expr = ParsePrimaryExpression(); return ShaderBuilder::Unary(ShaderAst::UnaryType::LogicalNot, std::move(expr)); } case TokenType::OpenParenthesis: return ParseParenthesisExpression(); default: throw UnexpectedToken{}; } } ShaderAst::AttributeType Parser::ParseIdentifierAsAttributeType() { const Token& identifierToken = Expect(Advance(), TokenType::Identifier); const std::string& identifier = std::get(identifierToken.data); auto it = s_identifierToAttributeType.find(identifier); if (it == s_identifierToAttributeType.end()) throw UnknownAttribute{}; return it->second; } const std::string& Parser::ParseIdentifierAsName() { const Token& identifierToken = Expect(Advance(), TokenType::Identifier); const std::string& identifier = std::get(identifierToken.data); auto it = s_identifierToBasicType.find(identifier); if (it != s_identifierToBasicType.end()) throw ReservedKeyword{}; return identifier; } ShaderAst::PrimitiveType Parser::ParsePrimitiveType() { const Token& identifierToken = Expect(Advance(), TokenType::Identifier); const std::string& identifier = std::get(identifierToken.data); auto it = s_identifierToBasicType.find(identifier); if (it == s_identifierToBasicType.end()) throw UnknownType{}; return it->second; } ShaderAst::ExpressionType Parser::ParseType() { // Handle () as no type if (Peek().type == TokenType::OpenParenthesis) { Consume(); Expect(Advance(), TokenType::ClosingParenthesis); return ShaderAst::NoType{}; } const Token& identifierToken = Expect(Peek(), TokenType::Identifier); const std::string& identifier = std::get(identifierToken.data); auto type = DecodeType(identifier); if (!type) { Consume(); return ShaderAst::IdentifierType{ identifier }; } return *type; } int Parser::GetTokenPrecedence(TokenType token) { switch (token) { case TokenType::Divide: return 80; case TokenType::Dot: return 100; case TokenType::Equal: return 50; case TokenType::LessThan: return 40; case TokenType::LessThanEqual: return 40; case TokenType::LogicalAnd: return 120; case TokenType::LogicalOr: return 140; 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; default: return -1; } } ShaderAst::StatementPtr 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(file.GetSize()); std::vector source(length); if (file.Read(&source[0], length) != length) { NazaraError("Failed to read program file"); return {}; } return Parse(std::string_view(reinterpret_cast(source.data()), source.size())); } }