diff --git a/doc/src/Developer_utils.rst b/doc/src/Developer_utils.rst index e1a5f7741d..46d15cc007 100644 --- a/doc/src/Developer_utils.rst +++ b/doc/src/Developer_utils.rst @@ -175,6 +175,12 @@ and parsing files or arguments. .. doxygenfunction:: is_double :project: progguide +.. doxygenfunction:: is_id + :project: progguide + +.. doxygenfunction:: is_type + :project: progguide + Potential file functions ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/utils.cpp b/src/utils.cpp index eb9e48985a..c11ae8433c 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -1139,6 +1139,34 @@ bool utils::is_id(const std::string &str) return true; } +/* ---------------------------------------------------------------------- + Check whether string is a valid type or type label string +------------------------------------------------------------------------- */ + +int utils::is_type(const std::string &str) +{ + if (str.empty()) return -1; + + bool numeric = true; + int nstar = 0; + for (auto c : str) { + if (isdigit(c)) continue; + if (c == '*') { + ++nstar; + continue; + } + numeric = false; + } + if (numeric && (nstar < 2)) return 0; + + // TODO: the first two checks below are not really needed with this function. + // If a type label has at least one character that is not a digit or '*' + // it can be identified by this function as type label due to the check above. + if (isdigit(str[0]) || (str[0] == '*') || (str[0] == '#')) return -1; + if (str.find_first_of(" \t\r\n\f") != std::string::npos) return -1; + return 1; +} + /* ---------------------------------------------------------------------- try to find potential file as specified by name search current directory and the LAMMPS_POTENTIALS directory if diff --git a/src/utils.h b/src/utils.h index 279491e501..8e3a3e35b8 100644 --- a/src/utils.h +++ b/src/utils.h @@ -544,6 +544,19 @@ namespace utils { bool is_id(const std::string &str); + /*! Check if string is a valid type label, or numeric type, or numeric type range. + * Numeric type or type range may only contain digits or the '*' character. + * Type label strings may not contain a digit, or a '*', or a '#' character as the + * first character to distinguish them from comments and numeric types or type ranges. + * They also may not contain any whitespace. If the string is a valid numeric type + * or type range the function returns 0, if it is a valid type label the function + * returns 1, otherwise it returns -1. + * + * \param str string that should be checked + * \return 0, 1, or -1, depending on whether the string is valid numeric type, valid type label or neither, respectively */ + + int is_type(const std::string &str); + /*! Determine full path of potential file. If file is not found in current directory, * search directories listed in LAMMPS_POTENTIALS environment variable * diff --git a/unittest/utils/test_utils.cpp b/unittest/utils/test_utils.cpp index decd7d6379..7cadcfdb5a 100644 --- a/unittest/utils/test_utils.cpp +++ b/unittest/utils/test_utils.cpp @@ -440,6 +440,51 @@ TEST(Utils, invalid_id4) ASSERT_FALSE(utils::is_id("a$12")); } +TEST(Utils, valid_numeric) +{ + ASSERT_EQ(utils::is_type("1"), 0); + ASSERT_EQ(utils::is_type("21"), 0); + ASSERT_EQ(utils::is_type("05"), 0); + ASSERT_EQ(utils::is_type("1*"), 0); + ASSERT_EQ(utils::is_type("*2"), 0); + ASSERT_EQ(utils::is_type("1*4"), 0); +} + +TEST(Utils, invalid_numeric) +{ + ASSERT_EQ(utils::is_type("1*2*"), -1); + ASSERT_EQ(utils::is_type("**2"), -1); + ASSERT_EQ(utils::is_type("*4*"), -1); + ASSERT_EQ(utils::is_type("30**"), -1); +} + +TEST(Utils, valid_label) +{ + ASSERT_EQ(utils::is_type("A"), 1); + ASSERT_EQ(utils::is_type("c1"), 1); + ASSERT_EQ(utils::is_type("o1_"), 1); + ASSERT_EQ(utils::is_type("C1'"), 1); + ASSERT_EQ(utils::is_type("N2\"-C1'"), 1); + ASSERT_EQ(utils::is_type("[N2\"][C1']"), 1); + ASSERT_EQ(utils::is_type("@X2=&X1"), 1); + ASSERT_EQ(utils::is_type("|Na|Cl|H2O|"), 1); + ASSERT_EQ(utils::is_type("CA(1)/CB(1)"), 1); +} + +TEST(Utils, invalid_label) +{ + ASSERT_EQ(utils::is_type("1A"), -1); + ASSERT_EQ(utils::is_type("#c"), -1); + ASSERT_EQ(utils::is_type("*B"), -1); + ASSERT_EQ(utils::is_type(" B"), -1); + ASSERT_EQ(utils::is_type("A "), -1); + ASSERT_EQ(utils::is_type("A B"), -1); + ASSERT_EQ(utils::is_type("\tB"), -1); + ASSERT_EQ(utils::is_type("C\n"), -1); + ASSERT_EQ(utils::is_type("d\r"), -1); + ASSERT_EQ(utils::is_type(""), -1); +} + TEST(Utils, strmatch_beg) { ASSERT_TRUE(utils::strmatch("rigid/small/omp", "^rigid"));