Skip to content

Commit

Permalink
Add parseStructValueString function (AcademySoftwareFoundation#2019)
Browse files Browse the repository at this point in the history
Adding a simple function that tokenize a c++ style list initializer string, which will be used to initialize struct values.
  • Loading branch information
ld-kerley authored Sep 18, 2024
1 parent e0d72e7 commit 322da6e
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 0 deletions.
54 changes: 54 additions & 0 deletions source/MaterialXCore/Value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,60 @@ template <class T> 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
//
Expand Down
5 changes: 5 additions & 0 deletions source/MaterialXCore/Value.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,11 @@ template <class T> MX_CORE_API string toValueString(const T& data);
/// @throws ExceptionTypeError if the conversion cannot be performed.
template <class T> 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<int>);
Expand Down
9 changes: 9 additions & 0 deletions source/MaterialXTest/MaterialXCore/Value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ TEST_CASE("Value strings", "[value]")
REQUIRE_THROWS_AS(mx::fromValueString<float>("text"), mx::ExceptionTypeError);
REQUIRE_THROWS_AS(mx::fromValueString<bool>("1"), mx::ExceptionTypeError);
REQUIRE_THROWS_AS(mx::fromValueString<mx::Color3>("1"), mx::ExceptionTypeError);

// Parse value strings using structure syntax features.
REQUIRE(mx::parseStructValueString("{{1;2;3};4}") == (std::vector<std::string>{"{1;2;3}","4"}));
REQUIRE(mx::parseStructValueString("{1;2;3;4}") == (std::vector<std::string>{"1","2","3","4"}));
REQUIRE(mx::parseStructValueString("{1;{2;3};4}") == (std::vector<std::string>{"1","{2;3}","4"}));
REQUIRE(mx::parseStructValueString("{1;{2;3;4}}") == (std::vector<std::string>{"1","{2;3;4}"}));
REQUIRE(mx::parseStructValueString("{1;{2;{3;4}}}") == (std::vector<std::string>{"1","{2;{3;4}}"}));
REQUIRE(mx::parseStructValueString("{1;2;{3};4}") == (std::vector<std::string>{"1","2","{3}","4"}));
REQUIRE(mx::parseStructValueString("{1;2;{3};4}") == (std::vector<std::string>{"1","2","{3}","4"}));
}

TEST_CASE("Typed values", "[value]")
Expand Down

0 comments on commit 322da6e

Please sign in to comment.