From 322da6ec57d133009424089563b6f2736ceeabcc Mon Sep 17 00:00:00 2001 From: Lee Kerley <154285602+ld-kerley@users.noreply.github.com> Date: Wed, 18 Sep 2024 08:24:23 -0700 Subject: [PATCH] Add `parseStructValueString` function (#2019) Adding a simple function that tokenize a c++ style list initializer string, which will be used to initialize struct values. --- source/MaterialXCore/Value.cpp | 54 ++++++++++++++++++++ source/MaterialXCore/Value.h | 5 ++ source/MaterialXTest/MaterialXCore/Value.cpp | 9 ++++ 3 files changed, 68 insertions(+) diff --git a/source/MaterialXCore/Value.cpp b/source/MaterialXCore/Value.cpp index 3e8c1f4462..68da4b5b9d 100644 --- a/source/MaterialXCore/Value.cpp +++ b/source/MaterialXCore/Value.cpp @@ -193,6 +193,60 @@ template T fromValueString(const string& value) return data; } +StringVec parseStructValueString(const string& value) +{ + static const char SEPARATOR = ';'; + static const char OPEN_BRACE = '{'; + static const char CLOSE_BRACE = '}'; + + if (value.empty()) + return StringVec(); + + // Validate the string is correctly formatted - must be at least 2 characters long and start and end with braces + if (value.size() < 2 || (value[0] != OPEN_BRACE || value[value.size()-1] != CLOSE_BRACE)) + { + return StringVec(); + } + + StringVec split; + + // Strip off the surrounding braces + string substring = value.substr(1, value.size()-2); + + // Sequentially examine each character to parse the list initializer. + string part = ""; + int braceDepth = 0; + for (const char c : substring) + { + if (c == OPEN_BRACE) + { + // We've already trimmed the starting brace, so any additional braces indicate members that are themselves list initializers. + // We will just return this as a string of the list initializer. + braceDepth += 1; + } + if (braceDepth > 0 && c == CLOSE_BRACE) + { + braceDepth -= 1; + } + + if (braceDepth == 0 && c == SEPARATOR) + { + // When we hit a separator we store the currently accumulated part, and clear to start collecting the next. + split.emplace_back(part); + part = ""; + } + else + { + part += c; + } + } + + if (!part.empty()) + split.emplace_back(part); + + return split; +} + // // TypedValue methods // diff --git a/source/MaterialXCore/Value.h b/source/MaterialXCore/Value.h index 1bd29b1d2b..764a0ccc01 100644 --- a/source/MaterialXCore/Value.h +++ b/source/MaterialXCore/Value.h @@ -216,6 +216,11 @@ template MX_CORE_API string toValueString(const T& data); /// @throws ExceptionTypeError if the conversion cannot be performed. template MX_CORE_API T fromValueString(const string& value); +/// Tokenize the string representation of a struct value i.e, "{1;2;3}" into a +/// vector of substrings. +/// Note: "{1;2;{3;4;5}}" will be split in to ["1", "2", "{3;4;5}"] +MX_CORE_API StringVec parseStructValueString(const string& value); + /// Forward declaration of specific template instantiations. /// Base types MX_CORE_EXTERN_TEMPLATE(TypedValue); diff --git a/source/MaterialXTest/MaterialXCore/Value.cpp b/source/MaterialXTest/MaterialXCore/Value.cpp index 40d6b120e7..31c333d211 100644 --- a/source/MaterialXTest/MaterialXCore/Value.cpp +++ b/source/MaterialXTest/MaterialXCore/Value.cpp @@ -78,6 +78,15 @@ TEST_CASE("Value strings", "[value]") REQUIRE_THROWS_AS(mx::fromValueString("text"), mx::ExceptionTypeError); REQUIRE_THROWS_AS(mx::fromValueString("1"), mx::ExceptionTypeError); REQUIRE_THROWS_AS(mx::fromValueString("1"), mx::ExceptionTypeError); + + // Parse value strings using structure syntax features. + REQUIRE(mx::parseStructValueString("{{1;2;3};4}") == (std::vector{"{1;2;3}","4"})); + REQUIRE(mx::parseStructValueString("{1;2;3;4}") == (std::vector{"1","2","3","4"})); + REQUIRE(mx::parseStructValueString("{1;{2;3};4}") == (std::vector{"1","{2;3}","4"})); + REQUIRE(mx::parseStructValueString("{1;{2;3;4}}") == (std::vector{"1","{2;3;4}"})); + REQUIRE(mx::parseStructValueString("{1;{2;{3;4}}}") == (std::vector{"1","{2;{3;4}}"})); + REQUIRE(mx::parseStructValueString("{1;2;{3};4}") == (std::vector{"1","2","{3}","4"})); + REQUIRE(mx::parseStructValueString("{1;2;{3};4}") == (std::vector{"1","2","{3}","4"})); } TEST_CASE("Typed values", "[value]")