From 5eb03a90dda0efce4291128c1b573b96d43eb4d8 Mon Sep 17 00:00:00 2001 From: Caleb Schilly Date: Mon, 11 Dec 2023 17:54:41 -0500 Subject: [PATCH 1/7] add optional yaml-cpp parser and flag --- .../core/cmake/TeuchosCore_config.h.in | 2 + packages/teuchos/core/src/CMakeLists.txt | 1 + .../parameterlist/src/Teuchos_YamlParser.cpp | 409 +++++++++++++++++- .../src/Teuchos_YamlParser_decl.hpp | 34 +- .../test/yaml/YamlParameterList.cpp | 54 ++- 5 files changed, 485 insertions(+), 15 deletions(-) diff --git a/packages/teuchos/core/cmake/TeuchosCore_config.h.in b/packages/teuchos/core/cmake/TeuchosCore_config.h.in index c0705327830c..94590b3a29c0 100644 --- a/packages/teuchos/core/cmake/TeuchosCore_config.h.in +++ b/packages/teuchos/core/cmake/TeuchosCore_config.h.in @@ -11,6 +11,8 @@ #cmakedefine HAVE_TEUCHOSCORE_QT +#cmakedefine HAVE_TEUCHOSCORE_YAMLCPP + /* Deprecated */ #cmakedefine HAVE_TEUCHOSCORE_QD diff --git a/packages/teuchos/core/src/CMakeLists.txt b/packages/teuchos/core/src/CMakeLists.txt index 8b547b4d4a18..713b536be574 100644 --- a/packages/teuchos/core/src/CMakeLists.txt +++ b/packages/teuchos/core/src/CMakeLists.txt @@ -32,6 +32,7 @@ SET(HAVE_TEUCHOSCORE_CXX11 ON) SET(YAML_CPP_DEF 0) IF(${PACKAGE_NAME}_ENABLE_yaml-cpp) SET(YAML_CPP_DEF 1) + SET(HAVE_TEUCHOSCORE_YAMLCPP ON) ENDIF() TRIBITS_CONFIGURE_FILE(${PARENT_PACKAGE_NAME}_config.h) diff --git a/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp b/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp index 90727b30ffd9..1c445fd9d995 100644 --- a/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp +++ b/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp @@ -55,10 +55,159 @@ #include "Teuchos_TwoDArray.hpp" #include "Teuchos_Reader.hpp" + +#ifdef HAVE_TEUCHOSCORE_YAMLCPP +#include "yaml-cpp/yaml.h" +#endif // HAVE_TEUCHOSCORE_YAMLCPP #include "Teuchos_YAML.hpp" + namespace Teuchos { +#ifdef HAVE_TEUCHOSCORE_YAMLCPP + +/* see https://github.com/jbeder/yaml-cpp/issues/261 + there are times when we want to insist that a parameter + value be interpreted as a string despite it being parseable + as a number. + the standard way to do this in YAML is to put the number in quotes, + i.e. '1e-3' instead of 1e-3. + however, the usual YAML::Node::as system doesn't respect quoting + when trying to cast to numbers. + so, this is our own version of as, called quoted_as, using + the Tag workaround suggested in the issue linked above. */ + +template +struct QuotedAs { + static T eval(::YAML::Node const& node) { + // this "!" tag apparently denotes that the value was quoted + if (node.Tag() == "!") { + throw std::runtime_error("quoted_as from quoted string to number"); + } + return node.as(); + } +}; + +template <> +struct QuotedAs { + // only a cast to string will succeed if quoted + static std::string eval(::YAML::Node const& node) { return node.as(); } +}; + +template +static T quoted_as(::YAML::Node const& node) { return QuotedAs::eval(node); } + +template +Teuchos::Array getYamlArray(const ::YAML::Node& node) +{ + Teuchos::Array arr; + for(::YAML::const_iterator it = node.begin(); it != node.end(); it++) + { + arr.push_back(quoted_as(*it)); + } + return arr; +} + +bool checkYamlTwoDArrayIsRagged(const ::YAML::Node& node) +{ + bool ragged = false; + for (::YAML::const_iterator it = node.begin(); it != node.end(); ++it) + { + if (it->size() != node.begin()->size()) + { + ragged=true; + } + } + return ragged; +} + +template Teuchos::TwoDArray getYamlTwoDArray(const ::YAML::Node& node) +{ + Teuchos::TwoDArray arr; + typename Teuchos::TwoDArray::size_type i, j; + arr.resizeRows(node.size()); + arr.resizeCols(node.begin()->size()); + i = 0; + for (::YAML::const_iterator rit = node.begin(); rit != node.end(); ++rit) + { + j = 0; + for (::YAML::const_iterator cit = rit->begin(); cit != rit->end(); ++cit) + { + arr(i, j) = quoted_as(*cit); + ++j; + } + ++i; + } + return arr; +} + +int getYamlArrayDim(const ::YAML::Node& node) +{ + int ndim = 0; + if (node.Type() == ::YAML::NodeType::Sequence) + { + ++ndim; + if (node.begin()->Type() == ::YAML::NodeType::Sequence) + { + ++ndim; + if (node.begin()->begin()->Type() == ::YAML::NodeType::Sequence) + { + ++ndim; + } + } + } + return ndim; +} + +template +tarray_t getYaml2DRaggedArray(::YAML::Node node, int ndim, std::string key) +{ + tarray_t base_arr; + if (ndim == 2) { + Teuchos::Array sub_arr; + for (::YAML::const_iterator it1 = node.begin(); it1 != node.end(); ++it1) { + for (::YAML::const_iterator it2 = it1->begin(); it2 != it1->end(); ++it2) { + sub_arr.push_back(quoted_as(*it2)); + } base_arr.push_back(sub_arr); + sub_arr.clear(); + } + } + else + { + throw YamlSequenceError(std::string("MDArray \"" + key + "\" must have dim 2.")); + } + return base_arr; +} + +// This handles the requested use case of a list of 2D arrays; further nesting would require a getYaml4DArray() function, +// which could be straightforwardly implemented along the lines of the below function. + +template +tarray_t getYaml3DArray(::YAML::Node node, int ndim, std::string key) +{ + tarray_t base_arr; + if (ndim == 3) { + Teuchos::Array> sub_arr; + Teuchos::Array sub_sub_arr; + for (::YAML::const_iterator it1 = node.begin(); it1 != node.end(); ++it1) { + for (::YAML::const_iterator it2 = it1->begin(); it2 != it1->end(); ++it2) { + for (::YAML::const_iterator it3 = it2->begin(); it3 != it2->end(); ++it3) { + sub_sub_arr.push_back(quoted_as(*it3)); + } sub_arr.push_back(sub_sub_arr); + sub_sub_arr.clear(); + } base_arr.push_back(sub_arr); + sub_arr.clear(); + } + } + else + { + throw YamlSequenceError(std::string("MDArray \"" + key + "\" must have dim 3.")); + } + return base_arr; +} + +#endif // HAVE_TEUCHOSCORE_YAMLCPP + std::string remove_trailing_whitespace(std::string const& in) { std::size_t new_end = 0; for (std::size_t ri = 0; ri < in.size(); ++ri) { @@ -910,7 +1059,7 @@ class Reader : public Teuchos::Reader { /* per Trilinos issue #2090, there can be trailing comments after the block scalar which are less indented than it, but they will be included in the final NEWLINE token. - this code removes all contiguous trailing lines which are less indented + this code removes all contiguous trailing lines which are less indented than the content. */ while (true) { @@ -1076,36 +1225,274 @@ namespace YAMLParameterList Teuchos::RCP parseYamlText(const std::string& text, const std::string& name) { - Teuchos::YAMLParameterList::Reader reader; +#ifdef HAVE_TEUCHOSCORE_YAMLCPP + auto yaml_input = ::YAML::LoadAll(text); // std::vector<::YAML::Node> + return readParams(yaml_input); +#else any result; + Teuchos::YAMLParameterList::Reader reader; reader.read_string(result, text, name); ParameterList& pl = any_ref_cast(result); return Teuchos::rcp(new ParameterList(pl)); +#endif // HAVE_TEUCHOSCORE_YAMLCPP } Teuchos::RCP parseYamlFile(const std::string& yamlFile) { - Teuchos::YAMLParameterList::Reader reader; +#ifdef HAVE_TEUCHOSCORE_YAMLCPP + auto yaml_input = ::YAML::LoadAllFromFile(yamlFile); + return readParams(yaml_input); +#else any result; + Teuchos::YAMLParameterList::Reader reader; reader.read_file(result, yamlFile); ParameterList& pl = any_ref_cast(result); return Teuchos::rcp(new ParameterList(pl)); +#endif // HAVE_TEUCHOSCORE_YAMLCPP } Teuchos::RCP parseYamlStream(std::istream& yaml) { - Teuchos::YAMLParameterList::Reader reader; +#ifdef HAVE_TEUCHOSCORE_YAMLCPP + auto yaml_input = ::YAML::LoadAll(yaml); + return readParams(yaml_input); +#else any result; + Teuchos::YAMLParameterList::Reader reader; reader.read_stream(result, yaml, "parseYamlStream"); ParameterList& pl = any_ref_cast(result); return Teuchos::rcp(new ParameterList(pl)); +#endif // HAVE_TEUCHOSCORE_YAMLCPP +} + +// The following three functions (readParams, processMapNode, and processKeyValueNode) +// were previously removed from Trilinos in PR 1779 (Teuchos: use Parser, not yaml-cpp, to read YAML PL). + +#ifdef HAVE_TEUCHOSCORE_YAMLCPP + +Teuchos::RCP readParams(std::vector<::YAML::Node>& lists) +{ + Teuchos::RCP pl = rcp(new Teuchos::ParameterList); //pl is the root ParameterList to be returned + // If there is exactly one element in "lists", assume it is the anonymous top-level parameter list + // If there are more than one, place them all in the anonymous top-level list + for(size_t i = 0; i < lists.size(); i++) + { + processMapNode(lists[i], *pl, true); + } + return pl; +} + +void processMapNode(const ::YAML::Node& node, Teuchos::ParameterList& parent, bool topLevel) +{ + if (node.Type() != ::YAML::NodeType::Map) + { + throw YamlStructureError("All top-level elements of the YAML file must be maps."); + } + if (topLevel) + { + parent.setName("ANONYMOUS"); + processMapNode(node.begin()->second, parent); + } + else + { + for (::YAML::const_iterator i = node.begin(); i != node.end(); i++) + { + // make sure the key type is a string + if(i->first.Type() != ::YAML::NodeType::Scalar) + { + throw YamlKeyError("Keys must be plain strings"); + } + // if this conversion fails and throws for any reason (shouldn't), let the caller handle it + const std::string key = quoted_as(i->first); + processKeyValueNode(key, i->second, parent, topLevel); + } + } +} + +void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuchos::ParameterList& parent, bool topLevel) +{ + // node (value) type can be a map (for nested param lists), + // a scalar (int, double, string), or a sequence of doubles (vector) + if(node.Type() == ::YAML::NodeType::Scalar) + { + try + { + parent.set(key, quoted_as(node)); + } + catch(...) + { + try + { + parent.set(key, quoted_as(node)); + } + catch(...) + { + try + { + std::string rawString = quoted_as(node); + if(rawString == "true") + { + parent.set(key, true); + } + else if(rawString == "false") + { + parent.set(key, false); + } + else + { + parent.set(key, rawString); + } + } + catch(...) + { + throw YamlScalarError("YAML scalars must be int, double, bool or string."); + } + } + } + } + else if(node.Type() == ::YAML::NodeType::Map) + { + if(topLevel) + { + processMapNode(node, parent); + } + else + { + Teuchos::ParameterList& sublist = parent.sublist(key); + processMapNode(node, sublist); + } + } + else if(node.Type() == ::YAML::NodeType::Sequence) + { + int ndim = getYamlArrayDim(node); + if (ndim == 1) + { + ::YAML::Node const& first_value = *(node.begin()); + try + { + quoted_as(first_value); + parent.set(key, getYamlArray(node)); + } + catch(...) + { + try + { + quoted_as(first_value); + parent.set(key, getYamlArray(node)); + } + catch(...) + { + try + { + quoted_as(first_value); + parent.set(key, getYamlArray(node)); + } + catch(...) + { + throw YamlSequenceError(std::string("Array \"") + key + "\" must contain int, double, bool or string"); + } + } + } + } + else if (ndim == 2) + { + bool is_ragged = checkYamlTwoDArrayIsRagged(node); + ::YAML::Node const& first_value = *(node.begin()->begin()); + try + { + quoted_as(first_value); + using arr_t = Teuchos::Array>; + if (is_ragged) { + parent.set(key, getYaml2DRaggedArray(node, ndim, key)); + } else { + parent.set(key, getYamlTwoDArray(node)); + } + } + catch(...) + { + try + { + quoted_as(first_value); + using arr_t = Teuchos::Array>; + if (is_ragged) { + parent.set(key, getYaml2DRaggedArray(node, ndim, key)); + } else { + parent.set(key, getYamlTwoDArray(node)); + } + } + catch(...) + { + try + { + quoted_as(first_value); + using arr_t = Teuchos::Array>; + if (is_ragged) { + parent.set(key, getYaml2DRaggedArray(node, ndim, key)); + } else { + parent.set(key, getYamlTwoDArray(node)); + } + } + catch(...) + { + throw YamlSequenceError(std::string("TwoDArray \"") + key + "\" must contain int, double, bool or string"); + } + } + } + } + else if (ndim == 3) + { + ::YAML::Node const& first_value = *(node.begin()->begin()->begin()); + try + { + quoted_as(first_value); + using arr_t = Teuchos::Array>>; + parent.set(key, getYaml3DArray(node, ndim, key)); + } + catch(...) + { + try + { + quoted_as(first_value); + using arr_t = Teuchos::Array>>; + parent.set(key, getYaml3DArray(node, ndim, key)); + + } + catch(...) + { + try + { + quoted_as(first_value); + using arr_t = Teuchos::Array>>; + parent.set(key, getYaml3DArray(node, ndim, key)); + + } + catch(...) + { + throw YamlSequenceError(std::string("3DArray \"") + key + "\" must contain int, double, bool or string"); + } + } + } + } + } + else if(node.Type() == ::YAML::NodeType::Null) + { + // treat NULL as empty string (not an error) + parent.set(key, std::string()); + } + else + { + // Undefined + throw YamlUndefinedNodeError("Value type in a key-value pair must be one of: int, double, string, array, sublist."); + } } +#endif // HAVE_TEUCHOSCORE_YAMLCPP + void writeYamlStream(std::ostream& yaml, const Teuchos::ParameterList& pl) { - //warn the user if floats/doubles with integer values will be printed incorrectly + // warn the user if floats/doubles with integer values will be printed incorrectly std::ios_base::fmtflags flags = yaml.flags(); - //make temporary stringstream to test flags + // make temporary stringstream to test flags std::ostringstream testStream; testStream.flags(flags); double testVal = 1; @@ -1113,10 +1500,10 @@ void writeYamlStream(std::ostream& yaml, const Teuchos::ParameterList& pl) bool popFlags = false; if(testStream.str() == "1") { - //must add showpoint to flags while writing yaml - //this will always disambiguate (double) n and n, even if stream precision is 0 - //prints as "n.0000" where the number of trailing zeros is the stream precision - //note: in YAML, "5." is a double but not an int + // must add showpoint to flags while writing yaml + // this will always disambiguate (double) n and n, even if stream precision is 0 + // prints as "n.0000" where the number of trailing zeros is the stream precision + // note: in YAML, "5." is a double but not an int std::cout << "Warning: yaml stream format flags would confuse double with integer value with int.\n"; std::cout << "Setting std::ios::showpoint on the stream to fix this (will restore flags when done)\n"; std::ios_base::fmtflags flagsCopy = flags; @@ -1134,7 +1521,7 @@ void writeYamlStream(std::ostream& yaml, const Teuchos::ParameterList& pl) writeParameterList(pl, yaml, 2); } yaml << "...\n"; - //restore flags + // restore flags if(popFlags) { yaml.flags(flags); diff --git a/packages/teuchos/parameterlist/src/Teuchos_YamlParser_decl.hpp b/packages/teuchos/parameterlist/src/Teuchos_YamlParser_decl.hpp index 7a738fd40055..407a8b4acc7a 100644 --- a/packages/teuchos/parameterlist/src/Teuchos_YamlParser_decl.hpp +++ b/packages/teuchos/parameterlist/src/Teuchos_YamlParser_decl.hpp @@ -50,9 +50,10 @@ /*! \file Teuchos_YamlParser_decl.hpp \brief Functions to convert between ParameterList and YAML -YAML is a human-readable data serialization format. Teuchos provides a -YAML parameter list interpreter. It produces Teuchos::ParameterList -objects equivalent to those produced by the Teuchos XML helper functions. +YAML is a human-readable data serialization format. In addition to +supporting the yaml-cpp TPL, Teuchos provides an in-house YAML parameter +list interpreter. It produces Teuchos::ParameterList objects equivalent +to those produced by the Teuchos XML helper functions. Here is a simple example XML parameter list: \code{.xml} @@ -140,6 +141,9 @@ and looks better in YAML itself. #include "Teuchos_RCP.hpp" #include "Teuchos_PtrDecl.hpp" #include "Teuchos_FileInputSource.hpp" +#ifdef HAVE_TEUCHOSCORE_YAMLCPP +#include "yaml-cpp/yaml.h" +#endif // HAVE_TEUCHOSCORE_YAMLCPP #include #include @@ -147,6 +151,23 @@ and looks better in YAML itself. namespace Teuchos { +#ifdef HAVE_TEUCHOSCORE_YAMLCPP +#define MAKE_EXCEPTION_TYPE(Name) \ +class Name : public Teuchos::ExceptionBase \ +{ \ + public: \ + Name(const std::string& arg) : ExceptionBase(arg) {} \ +}; + +MAKE_EXCEPTION_TYPE(YamlKeyError) +MAKE_EXCEPTION_TYPE(YamlScalarError) +MAKE_EXCEPTION_TYPE(YamlSequenceError) +MAKE_EXCEPTION_TYPE(YamlStructureError) +MAKE_EXCEPTION_TYPE(YamlUndefinedNodeError) + +#undef MAKE_EXCEPTION_TYPE +#endif // HAVE_TEUCHOSCORE_YAMLCPP + std::string convertXmlToYaml(const std::string& xmlFileName); //returns filename of produced YAML file void convertXmlToYaml(const std::string& xmlFileName, const std::string& yamlFileName); //writes to given filename void convertXmlToYaml(std::istream& xmlStream, std::ostream& yamlStream); @@ -160,6 +181,13 @@ namespace YAMLParameterList Teuchos::RCP parseYamlStream(std::istream& yaml); void writeYamlStream(std::ostream& yamlFile, const Teuchos::ParameterList& pl); void writeYamlFile(const std::string& yamlFile, const Teuchos::ParameterList& pl); + + #ifdef HAVE_TEUCHOSCORE_YAMLCPP + Teuchos::RCP readParams(std::vector<::YAML::Node>& lists); + void processMapNode(const ::YAML::Node& node, Teuchos::ParameterList& parent, bool topLevel = false); + void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuchos::ParameterList& parent, bool topLevel = false); + #endif // HAVE_TEUCHOSCORE_YAMLCPP + void writeParameterList(const Teuchos::ParameterList& pl, std::ostream& yaml, int indentLevel); void writeParameter(const std::string& paramName, const Teuchos::ParameterEntry& entry, std::ostream& yaml, int indentLevel); //throws if the entry's type is not supported void generalWriteString(const std::string& str, std::ostream& yaml); diff --git a/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp b/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp index 9d7446261f74..6a581ee4a4eb 100644 --- a/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp +++ b/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp @@ -309,5 +309,57 @@ namespace TeuchosTests TEST_EQUALITY(sublist.name(), "mycode->sublist"); } -} //namespace TeuchosTests +#ifdef HAVE_TEUCHOSCORE_YAMLCPP + TEUCHOS_UNIT_TEST(YAML, yamlcpp_parser) + { + RCP pl = Teuchos::getParametersFromYamlString( + "mycode:\n" + " list_of_2d_arrays:\n" + " - [[1,2,3], [4,5,6]]\n" + " - [[7,8,9], [10,11,12]]\n" + " ragged_array:\n" + " - [1,2,3]\n" + " - [1,2,3,4]\n" + " line_continuation: [\n" + " 1,2,3,\n" + " 4,5,6\n" + " ]\n" + " # allow unicode comments: ±\n" + ); + + using threeDarr_t = Teuchos::Array>>; + threeDarr_t& list_of_arrs = pl->get("list_of_2d_arrays"); + threeDarr_t correct_list_of_arrs = { + {{1, 2, 3}, {4, 5, 6}}, + {{7, 8, 9}, {10, 11, 12}} + }; + for (int i=0; i>; + twoDarr_t ragged_arr = pl->get("ragged_array"); + int correct_ragged_arr = { + {1, 2, 3}, + {1, 2, 3, 4} + }; + for (int i=0; i; + arr_t arr = pl->get("line_continuation"); + int correct_arr = {1, 2, 3, 4, 5, 6}; + for (int i=0; i Date: Wed, 3 Jan 2024 17:24:30 -0500 Subject: [PATCH 2/7] Teuchos: move yaml-cpp dependency to TeuchosParameterList and refine unit testing --- .../teuchos/core/cmake/Dependencies.cmake | 2 +- .../core/cmake/TeuchosCore_config.h.in | 2 - packages/teuchos/core/src/CMakeLists.txt | 6 -- packages/teuchos/parameterlist/CMakeLists.txt | 4 + .../parameterlist/cmake/Dependencies.cmake | 1 + .../cmake/TeuchosParameterList_config.h.in | 6 ++ .../teuchos/parameterlist/src/CMakeLists.txt | 10 ++ .../src/Teuchos_ParameterList.hpp | 1 + .../parameterlist/src/Teuchos_YamlParser.cpp | 98 +++++++++---------- .../src/Teuchos_YamlParser_decl.hpp | 13 ++- .../test/yaml/YamlParameterList.cpp | 44 +++++++-- 11 files changed, 113 insertions(+), 74 deletions(-) create mode 100644 packages/teuchos/parameterlist/cmake/TeuchosParameterList_config.h.in diff --git a/packages/teuchos/core/cmake/Dependencies.cmake b/packages/teuchos/core/cmake/Dependencies.cmake index ada5b0c16a0a..a22e3b607ecd 100644 --- a/packages/teuchos/core/cmake/Dependencies.cmake +++ b/packages/teuchos/core/cmake/Dependencies.cmake @@ -1,6 +1,6 @@ TRIBITS_PACKAGE_DEFINE_DEPENDENCIES( LIB_OPTIONAL_PACKAGES Kokkos - LIB_OPTIONAL_TPLS BinUtils Boost MPI ARPREC QD QT quadmath yaml-cpp Pthread Valgrind + LIB_OPTIONAL_TPLS BinUtils Boost MPI ARPREC QD QT quadmath Pthread Valgrind ) TRIBITS_ALLOW_MISSING_EXTERNAL_PACKAGES(Kokkos) diff --git a/packages/teuchos/core/cmake/TeuchosCore_config.h.in b/packages/teuchos/core/cmake/TeuchosCore_config.h.in index 94590b3a29c0..c0705327830c 100644 --- a/packages/teuchos/core/cmake/TeuchosCore_config.h.in +++ b/packages/teuchos/core/cmake/TeuchosCore_config.h.in @@ -11,8 +11,6 @@ #cmakedefine HAVE_TEUCHOSCORE_QT -#cmakedefine HAVE_TEUCHOSCORE_YAMLCPP - /* Deprecated */ #cmakedefine HAVE_TEUCHOSCORE_QD diff --git a/packages/teuchos/core/src/CMakeLists.txt b/packages/teuchos/core/src/CMakeLists.txt index 713b536be574..7b404b3f3489 100644 --- a/packages/teuchos/core/src/CMakeLists.txt +++ b/packages/teuchos/core/src/CMakeLists.txt @@ -29,12 +29,6 @@ ENDIF() # FIXME: Remove this hard-coded setting. We always have C++11 now. SET(HAVE_TEUCHOSCORE_CXX11 ON) -SET(YAML_CPP_DEF 0) -IF(${PACKAGE_NAME}_ENABLE_yaml-cpp) - SET(YAML_CPP_DEF 1) - SET(HAVE_TEUCHOSCORE_YAMLCPP ON) -ENDIF() - TRIBITS_CONFIGURE_FILE(${PARENT_PACKAGE_NAME}_config.h) TRIBITS_CONFIGURE_FILE(${PACKAGE_NAME}_config.h) diff --git a/packages/teuchos/parameterlist/CMakeLists.txt b/packages/teuchos/parameterlist/CMakeLists.txt index 0b68d597af22..a34008ec408f 100644 --- a/packages/teuchos/parameterlist/CMakeLists.txt +++ b/packages/teuchos/parameterlist/CMakeLists.txt @@ -1,6 +1,10 @@ TRIBITS_SUBPACKAGE(ParameterList) +IF(TPL_ENABLE_yaml-cpp) + SET(HAVE_TEUCHOSPARAMETERLIST_YAMLCPP ON) +ENDIF() + ADD_SUBDIRECTORY(src) TRIBITS_ADD_TEST_DIRECTORIES(test) diff --git a/packages/teuchos/parameterlist/cmake/Dependencies.cmake b/packages/teuchos/parameterlist/cmake/Dependencies.cmake index d60725ac5375..09dd63dd68c8 100644 --- a/packages/teuchos/parameterlist/cmake/Dependencies.cmake +++ b/packages/teuchos/parameterlist/cmake/Dependencies.cmake @@ -1,3 +1,4 @@ TRIBITS_PACKAGE_DEFINE_DEPENDENCIES( LIB_REQUIRED_PACKAGES TeuchosCore TeuchosParser + LIB_OPTIONAL_TPLS yaml-cpp ) diff --git a/packages/teuchos/parameterlist/cmake/TeuchosParameterList_config.h.in b/packages/teuchos/parameterlist/cmake/TeuchosParameterList_config.h.in new file mode 100644 index 000000000000..a357937c582a --- /dev/null +++ b/packages/teuchos/parameterlist/cmake/TeuchosParameterList_config.h.in @@ -0,0 +1,6 @@ +#ifndef TEUCHOSPARAMETERLIST_CONFIG_H +#define TEUCHOSPARAMETERLIST_CONFIG_H + +#cmakedefine HAVE_TEUCHOSPARAMETERLIST_YAMLCPP + +#endif // TEUCHOSPARAMETERLIST_CONFIG_H diff --git a/packages/teuchos/parameterlist/src/CMakeLists.txt b/packages/teuchos/parameterlist/src/CMakeLists.txt index 74e2651a65d7..15c8ed0606d4 100644 --- a/packages/teuchos/parameterlist/src/CMakeLists.txt +++ b/packages/teuchos/parameterlist/src/CMakeLists.txt @@ -1,4 +1,9 @@ +# +# A) Package-specific configuration options +# + +TRIBITS_CONFIGURE_FILE(${PACKAGE_NAME}_config.h) # # B) Define the header and source files (and include directories) @@ -7,7 +12,12 @@ SET(HEADERS "") SET(SOURCES "") +APPEND_SET(HEADERS + ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}_config.h + ) + TRIBITS_INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) +TRIBITS_INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) TRIBITS_SET_AND_INC_DIRS(DIR ${CMAKE_CURRENT_SOURCE_DIR}) APPEND_GLOB(HEADERS ${DIR}/*.hpp) diff --git a/packages/teuchos/parameterlist/src/Teuchos_ParameterList.hpp b/packages/teuchos/parameterlist/src/Teuchos_ParameterList.hpp index ff0d7a298d12..de42b3cb288d 100644 --- a/packages/teuchos/parameterlist/src/Teuchos_ParameterList.hpp +++ b/packages/teuchos/parameterlist/src/Teuchos_ParameterList.hpp @@ -47,6 +47,7 @@ \brief Templated Parameter List class */ +#include "TeuchosParameterList_config.h" #include "Teuchos_ParameterListExceptions.hpp" #include "Teuchos_ParameterListModifier.hpp" #include "Teuchos_ParameterEntry.hpp" diff --git a/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp b/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp index 1c445fd9d995..19d13a6051c5 100644 --- a/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp +++ b/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp @@ -56,15 +56,15 @@ #include "Teuchos_Reader.hpp" -#ifdef HAVE_TEUCHOSCORE_YAMLCPP +#ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP #include "yaml-cpp/yaml.h" -#endif // HAVE_TEUCHOSCORE_YAMLCPP +#endif // HAVE_TEUCHOSPARAMETERLIST_YAMLCPP #include "Teuchos_YAML.hpp" namespace Teuchos { -#ifdef HAVE_TEUCHOSCORE_YAMLCPP +#ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP /* see https://github.com/jbeder/yaml-cpp/issues/261 there are times when we want to insist that a parameter @@ -167,7 +167,7 @@ tarray_t getYaml2DRaggedArray(::YAML::Node node, int ndim, std::string key) Teuchos::Array sub_arr; for (::YAML::const_iterator it1 = node.begin(); it1 != node.end(); ++it1) { for (::YAML::const_iterator it2 = it1->begin(); it2 != it1->end(); ++it2) { - sub_arr.push_back(quoted_as(*it2)); + sub_arr.push_back(quoted_as(*it2)); } base_arr.push_back(sub_arr); sub_arr.clear(); } @@ -192,11 +192,11 @@ tarray_t getYaml3DArray(::YAML::Node node, int ndim, std::string key) for (::YAML::const_iterator it1 = node.begin(); it1 != node.end(); ++it1) { for (::YAML::const_iterator it2 = it1->begin(); it2 != it1->end(); ++it2) { for (::YAML::const_iterator it3 = it2->begin(); it3 != it2->end(); ++it3) { - sub_sub_arr.push_back(quoted_as(*it3)); + sub_sub_arr.push_back(quoted_as(*it3)); } sub_arr.push_back(sub_sub_arr); - sub_sub_arr.clear(); + sub_sub_arr.clear(); } base_arr.push_back(sub_arr); - sub_arr.clear(); + sub_arr.clear(); } } else @@ -206,7 +206,13 @@ tarray_t getYaml3DArray(::YAML::Node node, int ndim, std::string key) return base_arr; } -#endif // HAVE_TEUCHOSCORE_YAMLCPP +template +void safe_set_entry(ParameterList& list, std::string const& name_in, T const& entry_in) { + TEUCHOS_TEST_FOR_EXCEPTION(list.isParameter(name_in), ParserFail, + "Parameter \"" << name_in << "\" already exists in list \"" << list.name() << "\"\n"); + list.set(name_in, entry_in); +} +#endif // HAVE_TEUCHOSPARAMETERLIST_YAMLCPP std::string remove_trailing_whitespace(std::string const& in) { std::size_t new_end = 0; @@ -1225,21 +1231,22 @@ namespace YAMLParameterList Teuchos::RCP parseYamlText(const std::string& text, const std::string& name) { -#ifdef HAVE_TEUCHOSCORE_YAMLCPP +#ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP auto yaml_input = ::YAML::LoadAll(text); // std::vector<::YAML::Node> return readParams(yaml_input); #else + std::cout << "parseYamlText Not using yaml-cpp" << std::endl; any result; Teuchos::YAMLParameterList::Reader reader; reader.read_string(result, text, name); ParameterList& pl = any_ref_cast(result); return Teuchos::rcp(new ParameterList(pl)); -#endif // HAVE_TEUCHOSCORE_YAMLCPP +#endif // HAVE_TEUCHOSPARAMETERLIST_YAMLCPP } Teuchos::RCP parseYamlFile(const std::string& yamlFile) { -#ifdef HAVE_TEUCHOSCORE_YAMLCPP +#ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP auto yaml_input = ::YAML::LoadAllFromFile(yamlFile); return readParams(yaml_input); #else @@ -1248,12 +1255,12 @@ Teuchos::RCP parseYamlFile(const std::string& yamlFile) reader.read_file(result, yamlFile); ParameterList& pl = any_ref_cast(result); return Teuchos::rcp(new ParameterList(pl)); -#endif // HAVE_TEUCHOSCORE_YAMLCPP +#endif // HAVE_TEUCHOSPARAMETERLIST_YAMLCPP } Teuchos::RCP parseYamlStream(std::istream& yaml) { -#ifdef HAVE_TEUCHOSCORE_YAMLCPP +#ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP auto yaml_input = ::YAML::LoadAll(yaml); return readParams(yaml_input); #else @@ -1262,13 +1269,13 @@ Teuchos::RCP parseYamlStream(std::istream& yaml) reader.read_stream(result, yaml, "parseYamlStream"); ParameterList& pl = any_ref_cast(result); return Teuchos::rcp(new ParameterList(pl)); -#endif // HAVE_TEUCHOSCORE_YAMLCPP +#endif // HAVE_TEUCHOSPARAMETERLIST_YAMLCPP } // The following three functions (readParams, processMapNode, and processKeyValueNode) // were previously removed from Trilinos in PR 1779 (Teuchos: use Parser, not yaml-cpp, to read YAML PL). -#ifdef HAVE_TEUCHOSCORE_YAMLCPP +#ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP Teuchos::RCP readParams(std::vector<::YAML::Node>& lists) { @@ -1290,7 +1297,7 @@ void processMapNode(const ::YAML::Node& node, Teuchos::ParameterList& parent, bo } if (topLevel) { - parent.setName("ANONYMOUS"); + parent.setName(node.begin()->first.as()); processMapNode(node.begin()->second, parent); } else @@ -1300,7 +1307,7 @@ void processMapNode(const ::YAML::Node& node, Teuchos::ParameterList& parent, bo // make sure the key type is a string if(i->first.Type() != ::YAML::NodeType::Scalar) { - throw YamlKeyError("Keys must be plain strings"); + throw YamlKeyError("Keys must be YAML scalars (int, double, or string)"); } // if this conversion fails and throws for any reason (shouldn't), let the caller handle it const std::string key = quoted_as(i->first); @@ -1317,35 +1324,28 @@ void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuch { try { - parent.set(key, quoted_as(node)); + safe_set_entry(parent, key, quoted_as(node)); } catch(...) { try { - parent.set(key, quoted_as(node)); + safe_set_entry(parent, key, quoted_as(node)); } catch(...) { - try + std::string rawString = quoted_as(node); + if(rawString == "true") { - std::string rawString = quoted_as(node); - if(rawString == "true") - { - parent.set(key, true); - } - else if(rawString == "false") - { - parent.set(key, false); - } - else - { - parent.set(key, rawString); - } + safe_set_entry(parent, key, true); } - catch(...) + else if(rawString == "false") + { + safe_set_entry(parent, key, false); + } + else { - throw YamlScalarError("YAML scalars must be int, double, bool or string."); + safe_set_entry(parent, key, rawString); } } } @@ -1371,21 +1371,21 @@ void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuch try { quoted_as(first_value); - parent.set(key, getYamlArray(node)); + safe_set_entry>(parent, key, getYamlArray(node)); } catch(...) { try { quoted_as(first_value); - parent.set(key, getYamlArray(node)); + safe_set_entry>(parent, key, getYamlArray(node)); } catch(...) { try { quoted_as(first_value); - parent.set(key, getYamlArray(node)); + safe_set_entry>(parent, key, getYamlArray(node)); } catch(...) { @@ -1403,9 +1403,9 @@ void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuch quoted_as(first_value); using arr_t = Teuchos::Array>; if (is_ragged) { - parent.set(key, getYaml2DRaggedArray(node, ndim, key)); + safe_set_entry(parent, key, getYaml2DRaggedArray(node, ndim, key)); } else { - parent.set(key, getYamlTwoDArray(node)); + safe_set_entry>(parent, key, getYamlTwoDArray(node)); } } catch(...) @@ -1415,9 +1415,9 @@ void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuch quoted_as(first_value); using arr_t = Teuchos::Array>; if (is_ragged) { - parent.set(key, getYaml2DRaggedArray(node, ndim, key)); + safe_set_entry(parent, key, getYaml2DRaggedArray(node, ndim, key)); } else { - parent.set(key, getYamlTwoDArray(node)); + safe_set_entry>(parent, key, getYamlTwoDArray(node)); } } catch(...) @@ -1427,9 +1427,9 @@ void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuch quoted_as(first_value); using arr_t = Teuchos::Array>; if (is_ragged) { - parent.set(key, getYaml2DRaggedArray(node, ndim, key)); + safe_set_entry(parent, key, getYaml2DRaggedArray(node, ndim, key)); } else { - parent.set(key, getYamlTwoDArray(node)); + safe_set_entry>(parent, key, getYamlTwoDArray(node)); } } catch(...) @@ -1446,7 +1446,7 @@ void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuch { quoted_as(first_value); using arr_t = Teuchos::Array>>; - parent.set(key, getYaml3DArray(node, ndim, key)); + safe_set_entry(parent, key, getYaml3DArray(node, ndim, key)); } catch(...) { @@ -1454,7 +1454,7 @@ void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuch { quoted_as(first_value); using arr_t = Teuchos::Array>>; - parent.set(key, getYaml3DArray(node, ndim, key)); + safe_set_entry(parent, key, getYaml3DArray(node, ndim, key)); } catch(...) @@ -1463,7 +1463,7 @@ void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuch { quoted_as(first_value); using arr_t = Teuchos::Array>>; - parent.set(key, getYaml3DArray(node, ndim, key)); + safe_set_entry(parent, key, getYaml3DArray(node, ndim, key)); } catch(...) @@ -1477,7 +1477,7 @@ void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuch else if(node.Type() == ::YAML::NodeType::Null) { // treat NULL as empty string (not an error) - parent.set(key, std::string()); + safe_set_entry(parent, key, std::string()); } else { @@ -1486,7 +1486,7 @@ void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuch } } -#endif // HAVE_TEUCHOSCORE_YAMLCPP +#endif // HAVE_TEUCHOSPARAMETERLIST_YAMLCPP void writeYamlStream(std::ostream& yaml, const Teuchos::ParameterList& pl) { diff --git a/packages/teuchos/parameterlist/src/Teuchos_YamlParser_decl.hpp b/packages/teuchos/parameterlist/src/Teuchos_YamlParser_decl.hpp index 407a8b4acc7a..1fadc4ea83c2 100644 --- a/packages/teuchos/parameterlist/src/Teuchos_YamlParser_decl.hpp +++ b/packages/teuchos/parameterlist/src/Teuchos_YamlParser_decl.hpp @@ -141,9 +141,9 @@ and looks better in YAML itself. #include "Teuchos_RCP.hpp" #include "Teuchos_PtrDecl.hpp" #include "Teuchos_FileInputSource.hpp" -#ifdef HAVE_TEUCHOSCORE_YAMLCPP +#ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP #include "yaml-cpp/yaml.h" -#endif // HAVE_TEUCHOSCORE_YAMLCPP +#endif // HAVE_TEUCHOSPARAMETERLIST_YAMLCPP #include #include @@ -151,7 +151,7 @@ and looks better in YAML itself. namespace Teuchos { -#ifdef HAVE_TEUCHOSCORE_YAMLCPP +#ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP #define MAKE_EXCEPTION_TYPE(Name) \ class Name : public Teuchos::ExceptionBase \ { \ @@ -160,13 +160,12 @@ class Name : public Teuchos::ExceptionBase \ }; MAKE_EXCEPTION_TYPE(YamlKeyError) -MAKE_EXCEPTION_TYPE(YamlScalarError) MAKE_EXCEPTION_TYPE(YamlSequenceError) MAKE_EXCEPTION_TYPE(YamlStructureError) MAKE_EXCEPTION_TYPE(YamlUndefinedNodeError) #undef MAKE_EXCEPTION_TYPE -#endif // HAVE_TEUCHOSCORE_YAMLCPP +#endif // HAVE_TEUCHOSPARAMETERLIST_YAMLCPP std::string convertXmlToYaml(const std::string& xmlFileName); //returns filename of produced YAML file void convertXmlToYaml(const std::string& xmlFileName, const std::string& yamlFileName); //writes to given filename @@ -182,11 +181,11 @@ namespace YAMLParameterList void writeYamlStream(std::ostream& yamlFile, const Teuchos::ParameterList& pl); void writeYamlFile(const std::string& yamlFile, const Teuchos::ParameterList& pl); - #ifdef HAVE_TEUCHOSCORE_YAMLCPP + #ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP Teuchos::RCP readParams(std::vector<::YAML::Node>& lists); void processMapNode(const ::YAML::Node& node, Teuchos::ParameterList& parent, bool topLevel = false); void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuchos::ParameterList& parent, bool topLevel = false); - #endif // HAVE_TEUCHOSCORE_YAMLCPP + #endif // HAVE_TEUCHOSPARAMETERLIST_YAMLCPP void writeParameterList(const Teuchos::ParameterList& pl, std::ostream& yaml, int indentLevel); void writeParameter(const std::string& paramName, const Teuchos::ParameterEntry& entry, std::ostream& yaml, int indentLevel); //throws if the entry's type is not supported diff --git a/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp b/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp index 6a581ee4a4eb..5b5d7e36a95a 100644 --- a/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp +++ b/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp @@ -104,14 +104,14 @@ namespace TeuchosTests ); } - TEUCHOS_UNIT_TEST(YAML, PR1805) + TEUCHOS_UNIT_TEST(YAML, PR1805) // "\t" has been removed since yaml does not support tabs { RCP params = Teuchos::getParametersFromYamlString( "My Awesome Problem:\n" - "\tMesh:\n" - "\t\tInline:\n" - "\t\t\tType: Quad\n" - "\t\t\tElements: [ 10, 10 ]\n" + " Mesh:\n" + " Inline:\n" + " Type: Quad\n" + " Elements: [ 10, 10 ]\n" "...\n" ); } @@ -281,8 +281,14 @@ namespace TeuchosTests " small number: 54\n" " big number: 72057594037927936\n"); TEST_EQUALITY(pl->isType("small number"), true); +#ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP + TEST_EQUALITY(pl->isType("big number"), true); + long long big_number = static_cast(pl->get("big number")); + TEST_EQUALITY(big_number, 72057594037927936ll); +#else TEST_EQUALITY(pl->isType("big number"), true); TEST_EQUALITY(pl->get("big number"), 72057594037927936ll); +#endif } TEUCHOS_UNIT_TEST(YAML, flow_map) @@ -309,7 +315,7 @@ namespace TeuchosTests TEST_EQUALITY(sublist.name(), "mycode->sublist"); } -#ifdef HAVE_TEUCHOSCORE_YAMLCPP +#ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP TEUCHOS_UNIT_TEST(YAML, yamlcpp_parser) { RCP pl = Teuchos::getParametersFromYamlString( @@ -343,7 +349,7 @@ namespace TeuchosTests using twoDarr_t = Teuchos::Array>; twoDarr_t ragged_arr = pl->get("ragged_array"); - int correct_ragged_arr = { + twoDarr_t correct_ragged_arr = { {1, 2, 3}, {1, 2, 3, 4} }; @@ -355,11 +361,31 @@ namespace TeuchosTests using arr_t = Teuchos::Array; arr_t arr = pl->get("line_continuation"); - int correct_arr = {1, 2, 3, 4, 5, 6}; + arr_t correct_arr = {1, 2, 3, 4, 5, 6}; for (int i=0; i Date: Thu, 4 Jan 2024 13:50:34 -0500 Subject: [PATCH 3/7] Teuchos: remove leftover debugging statement --- packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp b/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp index 19d13a6051c5..67f44c51494f 100644 --- a/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp +++ b/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp @@ -1235,7 +1235,6 @@ Teuchos::RCP parseYamlText(const std::string& text, cons auto yaml_input = ::YAML::LoadAll(text); // std::vector<::YAML::Node> return readParams(yaml_input); #else - std::cout << "parseYamlText Not using yaml-cpp" << std::endl; any result; Teuchos::YAMLParameterList::Reader reader; reader.read_string(result, text, name); From 3490517063daec54c9c2979d8ceb04f7e85e6005 Mon Sep 17 00:00:00 2001 From: Caleb Schilly Date: Wed, 10 Jan 2024 12:51:11 -0500 Subject: [PATCH 4/7] Teuchos: support long long parsing and read in null nodes as empty sublists --- .../parameterlist/src/Teuchos_YamlParser.cpp | 8 +++++-- .../test/yaml/YamlParameterList.cpp | 23 ++++++++++++++----- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp b/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp index 67f44c51494f..f7691fba5287 100644 --- a/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp +++ b/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp @@ -1342,6 +1342,10 @@ void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuch { safe_set_entry(parent, key, false); } + else if (rawString.length() > 2 && (rawString.substr(rawString.length()-2) == "ll" || rawString.substr(rawString.length()-2) == "LL")) + { + safe_set_entry(parent, key, std::stoll(rawString)); + } else { safe_set_entry(parent, key, rawString); @@ -1475,8 +1479,8 @@ void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuch } else if(node.Type() == ::YAML::NodeType::Null) { - // treat NULL as empty string (not an error) - safe_set_entry(parent, key, std::string()); + // treat NULL as empty sublist (not an error) + parent.sublist(key); } else { diff --git a/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp b/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp index 5b5d7e36a95a..949ee6c4234a 100644 --- a/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp +++ b/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp @@ -279,16 +279,18 @@ namespace TeuchosTests auto pl = Teuchos::getParametersFromYamlString( "List:\n" " small number: 54\n" - " big number: 72057594037927936\n"); - TEST_EQUALITY(pl->isType("small number"), true); #ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP - TEST_EQUALITY(pl->isType("big number"), true); - long long big_number = static_cast(pl->get("big number")); - TEST_EQUALITY(big_number, 72057594037927936ll); + " big number: 72057594037927936ll\n" + " other big number: 92057594037927936LL\n" #else + " big number: 72057594037927936\n" + " other big number: 92057594037927936\n" +#endif // HAVE_TEUCHOSPARAMETERLIST_YAMLCPP + ); + TEST_EQUALITY(pl->isType("small number"), true); TEST_EQUALITY(pl->isType("big number"), true); TEST_EQUALITY(pl->get("big number"), 72057594037927936ll); -#endif + TEST_EQUALITY(pl->isType("other big number"), true); } TEUCHOS_UNIT_TEST(YAML, flow_map) @@ -315,6 +317,15 @@ namespace TeuchosTests TEST_EQUALITY(sublist.name(), "mycode->sublist"); } + TEUCHOS_UNIT_TEST(YAML, null_node) + { + RCP pl = Teuchos::getParametersFromYamlString( + "mycode:\n" + " empty_node:\n" + ); + TEST_EQUALITY(pl->isSublist("empty_node"), true); + } + #ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP TEUCHOS_UNIT_TEST(YAML, yamlcpp_parser) { From 60b9c7dab49eae8ad4ac0e7d753e93ede2b29543 Mon Sep 17 00:00:00 2001 From: Caleb Schilly Date: Wed, 10 Jan 2024 13:24:48 -0500 Subject: [PATCH 5/7] Teuchos: allow for strings ending in ll or LL --- .../teuchos/parameterlist/src/Teuchos_YamlParser.cpp | 9 ++++++++- .../parameterlist/test/yaml/YamlParameterList.cpp | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp b/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp index f7691fba5287..2076f2ef2d76 100644 --- a/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp +++ b/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp @@ -1344,7 +1344,14 @@ void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuch } else if (rawString.length() > 2 && (rawString.substr(rawString.length()-2) == "ll" || rawString.substr(rawString.length()-2) == "LL")) { - safe_set_entry(parent, key, std::stoll(rawString)); + try + { + safe_set_entry(parent, key, std::stoll(rawString)); + } + catch(...) + { + safe_set_entry(parent, key, rawString); + } } else { diff --git a/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp b/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp index 949ee6c4234a..033fd0820fd5 100644 --- a/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp +++ b/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp @@ -279,6 +279,7 @@ namespace TeuchosTests auto pl = Teuchos::getParametersFromYamlString( "List:\n" " small number: 54\n" + " test string: ends with ll\n" #ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP " big number: 72057594037927936ll\n" " other big number: 92057594037927936LL\n" @@ -288,6 +289,7 @@ namespace TeuchosTests #endif // HAVE_TEUCHOSPARAMETERLIST_YAMLCPP ); TEST_EQUALITY(pl->isType("small number"), true); + TEST_EQUALITY(pl->isType("test string"), true); TEST_EQUALITY(pl->isType("big number"), true); TEST_EQUALITY(pl->get("big number"), 72057594037927936ll); TEST_EQUALITY(pl->isType("other big number"), true); From 9eddcb7c866277452d8be0c5cb4310f59b166528 Mon Sep 17 00:00:00 2001 From: Caleb Schilly Date: Mon, 15 Jan 2024 15:19:04 -0500 Subject: [PATCH 6/7] Teuchos: improve support for long long, add yes/no functionality for bools --- .../parameterlist/src/Teuchos_YamlParser.cpp | 30 ++++++++--------- .../test/yaml/YamlParameterList.cpp | 32 +++++++++++++------ 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp b/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp index 2076f2ef2d76..7ec51905d661 100644 --- a/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp +++ b/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp @@ -1329,33 +1329,29 @@ void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuch { try { - safe_set_entry(parent, key, quoted_as(node)); + safe_set_entry(parent, key, quoted_as(node)); } catch(...) { - std::string rawString = quoted_as(node); - if(rawString == "true") - { - safe_set_entry(parent, key, true); - } - else if(rawString == "false") + try { - safe_set_entry(parent, key, false); + safe_set_entry(parent, key, quoted_as(node)); } - else if (rawString.length() > 2 && (rawString.substr(rawString.length()-2) == "ll" || rawString.substr(rawString.length()-2) == "LL")) + catch(...) { - try + std::string raw_string = quoted_as(node); + if(raw_string == "true" || raw_string == "yes") { - safe_set_entry(parent, key, std::stoll(rawString)); + safe_set_entry(parent, key, true); } - catch(...) + else if(raw_string == "false" || raw_string == "no") { - safe_set_entry(parent, key, rawString); + safe_set_entry(parent, key, false); + } + else + { + safe_set_entry(parent, key, raw_string); } - } - else - { - safe_set_entry(parent, key, rawString); } } } diff --git a/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp b/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp index 033fd0820fd5..54ac02b160e3 100644 --- a/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp +++ b/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp @@ -279,20 +279,34 @@ namespace TeuchosTests auto pl = Teuchos::getParametersFromYamlString( "List:\n" " small number: 54\n" - " test string: ends with ll\n" -#ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP - " big number: 72057594037927936ll\n" - " other big number: 92057594037927936LL\n" -#else + " small double: 3.0\n" + " scientific: 3.123e02\n" " big number: 72057594037927936\n" - " other big number: 92057594037927936\n" -#endif // HAVE_TEUCHOSPARAMETERLIST_YAMLCPP ); TEST_EQUALITY(pl->isType("small number"), true); - TEST_EQUALITY(pl->isType("test string"), true); + TEST_EQUALITY(pl->isType("small double"), true); + TEST_EQUALITY(pl->isType("scientific"), true); TEST_EQUALITY(pl->isType("big number"), true); TEST_EQUALITY(pl->get("big number"), 72057594037927936ll); - TEST_EQUALITY(pl->isType("other big number"), true); + } + + TEUCHOS_UNIT_TEST(YAML, bools) + { + auto pl = Teuchos::getParametersFromYamlString( + "List:\n" + " input_true: true\n" + " input_false: false\n" + " input_yes: yes\n" + " input_no: no\n" + ); + TEST_EQUALITY(pl->isType("input_true"), true); + TEST_EQUALITY(pl->isType("input_false"), true); + TEST_EQUALITY(pl->isType("input_yes"), true); + TEST_EQUALITY(pl->isType("input_no"), true); + TEST_EQUALITY(pl->get("input_true"), true); + TEST_EQUALITY(pl->get("input_false"), false); + TEST_EQUALITY(pl->get("input_yes"), true); + TEST_EQUALITY(pl->get("input_no"), false); } TEUCHOS_UNIT_TEST(YAML, flow_map) From 492d668e10ef7a4478565e44b6fb9979d0f23618 Mon Sep 17 00:00:00 2001 From: Caleb Schilly Date: Thu, 18 Jan 2024 13:01:12 -0500 Subject: [PATCH 7/7] Teuchos: improve support for bool parsing --- .../parameterlist/src/Teuchos_YamlParser.cpp | 29 +++++++++++++------ .../test/yaml/YamlParameterList.cpp | 12 ++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp b/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp index 7ec51905d661..dc4fb3758c51 100644 --- a/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp +++ b/packages/teuchos/parameterlist/src/Teuchos_YamlParser.cpp @@ -1339,18 +1339,29 @@ void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuch } catch(...) { - std::string raw_string = quoted_as(node); - if(raw_string == "true" || raw_string == "yes") - { - safe_set_entry(parent, key, true); - } - else if(raw_string == "false" || raw_string == "no") + try { - safe_set_entry(parent, key, false); + bool raw_bool = quoted_as(node); + + /* yaml-cpp parses ON/OFF as a bool, but the in-house parser does not. + To preserve backwards compatibility, make sure the string passes + the in-house parser's is_parseable_as function (which protects + against the ON/OFF case). + Otherwise, a failure is observed in YAML_ConvertFromXML unit test.*/ + + std::string raw_string = quoted_as(node); + if (is_parseable_as(raw_string)) + { + safe_set_entry(parent, key, raw_bool); + } + else + { + safe_set_entry(parent, key, raw_string); + } } - else + catch(...) { - safe_set_entry(parent, key, raw_string); + safe_set_entry(parent, key, quoted_as(node)); } } } diff --git a/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp b/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp index 54ac02b160e3..6327f6dd3785 100644 --- a/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp +++ b/packages/teuchos/parameterlist/test/yaml/YamlParameterList.cpp @@ -296,6 +296,10 @@ namespace TeuchosTests "List:\n" " input_true: true\n" " input_false: false\n" + " input_TRUE: TRUE\n" + " input_FALSE: FALSE\n" + " input_True: True\n" + " input_False: False\n" " input_yes: yes\n" " input_no: no\n" ); @@ -303,10 +307,18 @@ namespace TeuchosTests TEST_EQUALITY(pl->isType("input_false"), true); TEST_EQUALITY(pl->isType("input_yes"), true); TEST_EQUALITY(pl->isType("input_no"), true); + TEST_EQUALITY(pl->isType("input_TRUE"), true); + TEST_EQUALITY(pl->isType("input_True"), true); + TEST_EQUALITY(pl->isType("input_FALSE"), true); + TEST_EQUALITY(pl->isType("input_False"), true); TEST_EQUALITY(pl->get("input_true"), true); TEST_EQUALITY(pl->get("input_false"), false); TEST_EQUALITY(pl->get("input_yes"), true); TEST_EQUALITY(pl->get("input_no"), false); + TEST_EQUALITY(pl->get("input_TRUE"), true); + TEST_EQUALITY(pl->get("input_True"), true); + TEST_EQUALITY(pl->get("input_FALSE"), false); + TEST_EQUALITY(pl->get("input_False"), false); } TEUCHOS_UNIT_TEST(YAML, flow_map)