// // Copyright Contributors to the MaterialX Project // SPDX-License-Identifier: Apache-2.0 // #include #include #include #include #include MATERIALX_NAMESPACE_BEGIN const string Syntax::NEWLINE = "\n"; const string Syntax::SEMICOLON = ";"; const string Syntax::COMMA = ","; const string Syntax::INDENTATION = " "; const string Syntax::STRING_QUOTE = "\""; const string Syntax::INCLUDE_STATEMENT = "#include"; const string Syntax::SINGLE_LINE_COMMENT = "// "; const string Syntax::BEGIN_MULTI_LINE_COMMENT = "/* "; const string Syntax::END_MULTI_LINE_COMMENT = " */"; const std::unordered_map Syntax::CHANNELS_MAPPING = { { 'r', 0 }, { 'x', 0 }, { 'g', 1 }, { 'y', 1 }, { 'b', 2 }, { 'z', 2 }, { 'a', 3 }, { 'w', 3 } }; // // Syntax methods // Syntax::Syntax(TypeSystemPtr typeSystem) : _typeSystem(typeSystem) { } void Syntax::registerTypeSyntax(TypeDesc type, TypeSyntaxPtr syntax) { auto it = _typeSyntaxIndexByType.find(type); if (it != _typeSyntaxIndexByType.end()) { _typeSyntaxes[it->second] = syntax; } else { _typeSyntaxes.push_back(syntax); _typeSyntaxIndexByType[type] = _typeSyntaxes.size() - 1; } // Make this type a restricted name registerReservedWords({ syntax->getName() }); } void Syntax::registerReservedWords(const StringSet& names) { _reservedWords.insert(names.begin(), names.end()); } void Syntax::registerInvalidTokens(const StringMap& tokens) { _invalidTokens.insert(tokens.begin(), tokens.end()); } /// Returns the type syntax object for a named type. /// Throws an exception if a type syntax is not defined for the given type. const TypeSyntax& Syntax::getTypeSyntax(TypeDesc type) const { auto it = _typeSyntaxIndexByType.find(type); if (it == _typeSyntaxIndexByType.end()) { throw ExceptionShaderGenError("No syntax is defined for the given type '" + type.getName() + "'."); } return *_typeSyntaxes[it->second]; } string Syntax::getValue(const ShaderPort* port, bool uniform) const { const TypeSyntax& syntax = getTypeSyntax(port->getType()); return syntax.getValue(port, uniform); } string Syntax::getValue(TypeDesc type, const Value& value, bool uniform) const { const TypeSyntax& syntax = getTypeSyntax(type); return syntax.getValue(value, uniform); } const string& Syntax::getDefaultValue(TypeDesc type, bool uniform) const { const TypeSyntax& syntax = getTypeSyntax(type); return syntax.getDefaultValue(uniform); } const string& Syntax::getTypeName(TypeDesc type) const { const TypeSyntax& syntax = getTypeSyntax(type); return syntax.getName(); } string Syntax::getOutputTypeName(TypeDesc type) const { const TypeSyntax& syntax = getTypeSyntax(type); const string& outputModifier = getOutputQualifier(); return outputModifier.size() ? outputModifier + " " + syntax.getName() : syntax.getName(); } const string& Syntax::getTypeAlias(TypeDesc type) const { const TypeSyntax& syntax = getTypeSyntax(type); return syntax.getTypeAlias(); } const string& Syntax::getTypeDefinition(TypeDesc type) const { const TypeSyntax& syntax = getTypeSyntax(type); return syntax.getTypeDefinition(); } bool Syntax::typeSupported(const TypeDesc*) const { return true; } string Syntax::getArrayVariableSuffix(TypeDesc type, const Value& value) const { if (type.isArray()) { if (value.isA>()) { const size_t size = value.asA>().size(); return "[" + std::to_string(size) + "]"; } else if (value.isA>()) { const size_t size = value.asA>().size(); return "[" + std::to_string(size) + "]"; } } return string(); } static bool isInvalidChar(char c) { return !isalnum(c) && c != '_'; } void Syntax::makeValidName(string& name) const { std::replace_if(name.begin(), name.end(), isInvalidChar, '_'); name = replaceSubstrings(name, _invalidTokens); } void Syntax::makeIdentifier(string& name, IdentifierMap& identifiers) const { makeValidName(name); auto it = identifiers.find(name); if (it != identifiers.end()) { // Name is not unique so append the counter and keep // incrementing until a unique name is found. string name2; do { name2 = name + std::to_string(it->second++); } while (identifiers.count(name2)); name = name2; } // Save it among the known identifiers. identifiers[name] = 1; } string Syntax::getVariableName(const string& name, TypeDesc /*type*/, IdentifierMap& identifiers) const { // Default implementation just makes an identifier, but derived // classes can override this for custom variable naming. string variable = name; makeIdentifier(variable, identifiers); return variable; } bool Syntax::remapEnumeration(const string&, TypeDesc, const string&, std::pair&) const { return false; } StructTypeSyntaxPtr Syntax::createStructSyntax(const string& structTypeName, const string& defaultValue, const string& uniformDefaultValue, const string& typeAlias, const string& typeDefinition) const { return std::make_shared( this, structTypeName, defaultValue, uniformDefaultValue, typeAlias, typeDefinition); } const StringVec TypeSyntax::EMPTY_MEMBERS; TypeSyntax::TypeSyntax(const Syntax* parent, const string& name, const string& defaultValue, const string& uniformDefaultValue, const string& typeAlias, const string& typeDefinition, const StringVec& members) : _parent(parent), _name(name), _defaultValue(defaultValue), _uniformDefaultValue(uniformDefaultValue), _typeAlias(typeAlias), _typeDefinition(typeDefinition), _members(members) { } string TypeSyntax::getValue(const ShaderPort* port, bool uniform) const { if (!port || !port->getValue()) { return getDefaultValue(uniform); } return getValue(*port->getValue(), uniform); } ScalarTypeSyntax::ScalarTypeSyntax(const Syntax* parent, const string& name, const string& defaultValue, const string& uniformDefaultValue, const string& typeAlias, const string& typeDefinition) : TypeSyntax(parent, name, defaultValue, uniformDefaultValue, typeAlias, typeDefinition, EMPTY_MEMBERS) { } string ScalarTypeSyntax::getValue(const Value& value, bool /*uniform*/) const { return value.getValueString(); } StringTypeSyntax::StringTypeSyntax(const Syntax* parent, const string& name, const string& defaultValue, const string& uniformDefaultValue, const string& typeAlias, const string& typeDefinition) : ScalarTypeSyntax(parent, name, defaultValue, uniformDefaultValue, typeAlias, typeDefinition) { } string StringTypeSyntax::getValue(const Value& value, bool /*uniform*/) const { return "\"" + value.getValueString() + "\""; } AggregateTypeSyntax::AggregateTypeSyntax(const Syntax* parent, const string& name, const string& defaultValue, const string& uniformDefaultValue, const string& typeAlias, const string& typeDefinition, const StringVec& members) : TypeSyntax(parent, name, defaultValue, uniformDefaultValue, typeAlias, typeDefinition, members) { } string AggregateTypeSyntax::getValue(const Value& value, bool /*uniform*/) const { const string valueString = value.getValueString(); return valueString.empty() ? valueString : getName() + "(" + valueString + ")"; } StructTypeSyntax::StructTypeSyntax(const Syntax* parent, const string& name, const string& defaultValue, const string& uniformDefaultValue, const string& typeAlias, const string& typeDefinition, const StringVec& members) : TypeSyntax(parent, name, defaultValue, uniformDefaultValue, typeAlias, typeDefinition, members) { } string StructTypeSyntax::getValue(const Value& value, bool /*uniform*/) const { const AggregateValue& aggValue = static_cast(value); string result = "{"; string separator = ""; for (const auto& memberValue : aggValue.getMembers()) { result += separator; separator = ";"; const string& memberTypeName = memberValue->getTypeString(); TypeDesc memberTypeDesc = _parent->getType(memberTypeName); // Recursively use the syntax to generate the output, so we can support nested structs. const string valueStr = _parent->getValue(memberTypeDesc, *memberValue, true); result += valueStr; } result += "}"; return result; } MATERIALX_NAMESPACE_END