diff --git a/src/YYCCLegacy/StringHelper.cpp b/src/YYCCLegacy/StringHelper.cpp deleted file mode 100644 index d529d33..0000000 --- a/src/YYCCLegacy/StringHelper.cpp +++ /dev/null @@ -1,223 +0,0 @@ -#include "StringHelper.hpp" -#include "EncodingHelper.hpp" -#include - -namespace YYCC::StringHelper { - -#pragma region Printf VPrintf - - bool Printf(yycc_u8string& strl, const yycc_char8_t* format, ...) { - va_list argptr; - va_start(argptr, format); - bool ret = VPrintf(strl, format, argptr); - va_end(argptr); - return ret; - } - - bool VPrintf(yycc_u8string& strl, const yycc_char8_t* format, va_list argptr) { - va_list args1; - va_copy(args1, argptr); - va_list args2; - va_copy(args2, argptr); - - // the return value is desired char count without NULL terminal. - // minus number means error - int count = std::vsnprintf( - nullptr, - 0, - EncodingHelper::ToOrdinary(format), - args1 - ); - if (count < 0) { - // invalid length returned by vsnprintf. - return false; - } - va_end(args1); - - // resize std::string to desired count. - // and pass its length + 1 to std::vsnprintf, - // because std::vsnprintf only can write "buf_size - 1" chars with a trailing NULL. - // however std::vsnprintf already have a trailing NULL, so we plus 1 for it. - strl.resize(count); - int write_result = std::vsnprintf( - EncodingHelper::ToOrdinary(strl.data()), - strl.size() + 1, - EncodingHelper::ToOrdinary(format), - args2 - ); - va_end(args2); - - if (write_result < 0 || write_result > count) { - // invalid write result in vsnprintf. - return false; - } - - return true; - } - - yycc_u8string Printf(const yycc_char8_t* format, ...) { - yycc_u8string ret; - - va_list argptr; - va_start(argptr, format); - VPrintf(ret, format, argptr); - va_end(argptr); - - return ret; - } - - yycc_u8string VPrintf(const yycc_char8_t* format, va_list argptr) { - yycc_u8string ret; - - va_list argcpy; - va_copy(argcpy, argptr); - VPrintf(ret, format, argcpy); - va_end(argcpy); - - return ret; - } - -#pragma endregion - -#pragma region Replace - - void Replace(yycc_u8string& strl, const yycc_u8string_view& _from_strl, const yycc_u8string_view& _to_strl) { - // Reference: https://stackoverflow.com/questions/3418231/replace-part-of-a-string-with-another-string - - // check requirements - // from string should not be empty - yycc_u8string from_strl(_from_strl); - yycc_u8string to_strl(_to_strl); - if (from_strl.empty()) return; - - // start replace one by one - size_t start_pos = 0; - while ((start_pos = strl.find(from_strl, start_pos)) != yycc_u8string::npos) { - strl.replace(start_pos, from_strl.size(), to_strl); - start_pos += to_strl.size(); // In case 'to' contains 'from', like replacing 'x' with 'yx' - } - } - - yycc_u8string Replace(const yycc_u8string_view& _strl, const yycc_u8string_view& _from_strl, const yycc_u8string_view& _to_strl) { - // prepare result - yycc_u8string strl(_strl); - Replace(strl, _from_strl, _to_strl); - // return value - return strl; - } - -#pragma endregion - -#pragma region Join - - yycc_u8string Join(JoinDataProvider fct_data, const yycc_u8string_view& delimiter) { - yycc_u8string ret; - bool is_first = true; - yycc_u8string_view element; - - // fetch element - while (fct_data(element)) { - // insert delimiter - if (is_first) is_first = false; - else { - // append delimiter. - ret.append(delimiter); - } - - // insert element if it is not empty - if (!element.empty()) - ret.append(element); - } - - return ret; - } - -#pragma endregion - -#pragma region Upper Lower - - template - static void GeneralStringLowerUpper(yycc_u8string& strl) { - // References: - // https://en.cppreference.com/w/cpp/algorithm/transform - // https://en.cppreference.com/w/cpp/string/byte/tolower - std::transform( - strl.cbegin(), strl.cend(), strl.begin(), - [](unsigned char c) -> char { - if constexpr (bIsToLower) return std::tolower(c); - else return std::toupper(c); - } - ); - } - - void Lower(yycc_u8string& strl) { - GeneralStringLowerUpper(strl); - } - - yycc_u8string Lower(const yycc_u8string_view& strl) { - yycc_u8string ret(strl); - Lower(ret); - return ret; - } - - void Upper(yycc_u8string& strl) { - GeneralStringLowerUpper(strl); - } - - yycc_u8string Upper(const yycc_u8string_view& strl) { - // same as Lower, just replace char transform function. - yycc_u8string ret(strl); - Upper(ret); - return ret; - } - -#pragma endregion - -#pragma region Split - - std::vector Split(const yycc_u8string_view& strl, const yycc_u8string_view& _delimiter) { - // call split view - auto view_result = SplitView(strl, _delimiter); - - // copy string view result to string - std::vector elems; - elems.reserve(view_result.size()); - for (const auto& strl_view : view_result) { - elems.emplace_back(yycc_u8string(strl_view)); - } - // return copied result - return elems; - } - - std::vector SplitView(const yycc_u8string_view& strl, const yycc_u8string_view& _delimiter) { - // Reference: - // https://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c - - // prepare return value - std::vector elems; - - // if string need to be splitted is empty, return original string (empty string). - // if delimiter is empty, return original string. - yycc_u8string delimiter(_delimiter); - if (strl.empty() || delimiter.empty()) { - elems.emplace_back(strl); - return elems; - } - - // start spliting - std::size_t previous = 0, current; - while ((current = strl.find(delimiter.c_str(), previous)) != yycc_u8string::npos) { - elems.emplace_back(strl.substr(previous, current - previous)); - previous = current + delimiter.size(); - } - // try insert last part but prevent possible out of range exception - if (previous <= strl.size()) { - elems.emplace_back(strl.substr(previous)); - } - return elems; - } - -#pragma endregion - - -} diff --git a/src/YYCCLegacy/StringHelper.hpp b/src/YYCCLegacy/StringHelper.hpp deleted file mode 100644 index 084a34c..0000000 --- a/src/YYCCLegacy/StringHelper.hpp +++ /dev/null @@ -1,159 +0,0 @@ -#pragma once -#include "YYCCInternal.hpp" - -#include -#include -#include -#include - -/** - * @brief The helper containing string operations - * @details - * See also \ref string_helper. -*/ -namespace YYCC::StringHelper { - - /** - * @brief Perform a string formatting operation. - * @param[out] strl - * The string container receiving the result. - * There is no guarantee that the content is not modified when function failed. - * @param[in] format The format string. - * @param[in] ... Argument list of format string. - * @return True if success, otherwise false. - */ - bool Printf(yycc_u8string& strl, const yycc_char8_t* format, ...); - /** - * @brief Perform a string formatting operation. - * @param[out] strl - * The string container receiving the result. - * There is no guarantee that the content is not modified when function failed. - * @param[in] format The format string. - * @param[in] argptr Argument list of format string. - * @return True if success, otherwise false. - */ - bool VPrintf(yycc_u8string& strl, const yycc_char8_t* format, va_list argptr); - /** - * @brief Perform a string formatting operation. - * @param[in] format The format string. - * @param[in] ... Argument list of format string. - * @return The formatting result. Empty string if error happened. - */ - yycc_u8string Printf(const yycc_char8_t* format, ...); - /** - * @brief Perform a string formatting operation. - * @param[in] format The format string. - * @param[in] argptr Argument list of format string. - * @return The formatting result. Empty string if error happened. - */ - yycc_u8string VPrintf(const yycc_char8_t* format, va_list argptr); - - /** - * @brief Modify given string with all occurrences of substring \e old replaced by \e new. - * @param[in,out] strl The string for replacing - * @param[in] _from_strl The \e old string. - * @param[in] _to_strl The \e new string. - */ - void Replace(yycc_u8string& strl, const yycc_u8string_view& _from_strl, const yycc_u8string_view& _to_strl); - /** - * @brief Return a copy with all occurrences of substring \e old replaced by \e new. - * @param[in] _strl The string for replacing - * @param[in] _from_strl The \e old string. - * @param[in] _to_strl The \e new string. - * @return The result of replacement. - */ - yycc_u8string Replace(const yycc_u8string_view& _strl, const yycc_u8string_view& _from_strl, const yycc_u8string_view& _to_strl); - - /** - * @brief The data provider of general join function. - * @details - * For programmer using lambda to implement this function pointer: - * \li During calling, implementation should assign the reference of string view passed in argument - * to the string which need to be joined. - * \li Function return true to continue joining. otherwise return false to stop joining. - * The argument content assigned in the calling returning false is not included in join process. - */ - using JoinDataProvider = std::function; - /** - * @brief Universal join function. - * @details - * This function use function pointer as a general data provider interface, - * so this function suit for all types container. - * You can use this universal join function for any custom container by - * using C++ lambda syntax to create a code block adapted to this function pointer. - * @param[in] fct_data The function pointer in JoinDataProvider type prividing the data to be joined. - * @param[in] delimiter The delimiter used for joining. - * @return The result string of joining. - */ - yycc_u8string Join(JoinDataProvider fct_data, const yycc_u8string_view& delimiter); - /** - * @brief Specialized join function for standard library container. - * @tparam InputIt - * Must meet the requirements of LegacyInputIterator. - * It also can be dereferenced and then implicitly converted to yycc_u8string_view. - * @param[in] first The beginning of the range of elements to join. - * @param[in] last The terminal of the range of elements to join (exclusive). - * @param[in] delimiter The delimiter used for joining. - * @return The result string of joining. - */ - template - yycc_u8string Join(InputIt first, InputIt last, const yycc_u8string_view& delimiter) { - return Join([&first, &last](yycc_u8string_view& view) -> bool { - // if we reach tail, return false to stop join process - if (first == last) return false; - // otherwise fetch data, inc iterator and return. - view = *first; - ++first; - return true; - }, delimiter); - } - - /** - * @brief Convert given string to lowercase. - * @param[in,out] strl The string to be lowercase. - */ - void Lower(yycc_u8string& strl); - /** - * @brief Return a copy of the string converted to lowercase. - * @param[in] strl The string to be lowercase. - * @return The copy of the string converted to lowercase. - */ - yycc_u8string Lower(const yycc_u8string_view& strl); - /** - * @brief Convert given string to uppercase. - * @param[in,out] strl The string to be uppercase. - */ - void Upper(yycc_u8string& strl); - /** - * @brief Return a copy of the string converted to uppercase. - * @param[in] strl The string to be uppercase. - * @return The copy of the string converted to uppercase. - */ - yycc_u8string Upper(const yycc_u8string_view& strl); - - /** - * @brief Split given string with specified delimiter. - * @param[in] strl The string need to be splitting. - * @param[in] _delimiter The delimiter for splitting. - * @return - * The split result. - * \par - * If given string or delimiter are empty, - * the result container will only contain 1 entry which is equal to given string. - */ - std::vector Split(const yycc_u8string_view& strl, const yycc_u8string_view& _delimiter); - /** - * @brief Split given string with specified delimiter as string view. - * @param[in] strl The string need to be splitting. - * @param[in] _delimiter The delimiter for splitting. - * @return - * The split result with string view format. - * This will not produce any copy of original string. - * \par - * If given string or delimiter are empty, - * the result container will only contain 1 entry which is equal to given string. - * @see Split(const yycc_u8string_view&, const yycc_char8_t*) - */ - std::vector SplitView(const yycc_u8string_view& strl, const yycc_u8string_view& _delimiter); - -} diff --git a/src/YYCCLegacy/WinImportPrefix.hpp b/src/YYCCLegacy/WinImportPrefix.hpp deleted file mode 100644 index 8a19a87..0000000 --- a/src/YYCCLegacy/WinImportPrefix.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// It is by design that no pragma once or #if to prevent deplicated including. -// Because this header is the part of wrapper, not a real header. -// #pragma once - -#include "YYCCInternal.hpp" - -#if YYCC_OS == YYCC_OS_WINDOWS - -// Define 2 macros to disallow Windows generate MIN and MAX macros -// which cause std::min and std::max can not function as normal. -#if !defined(WIN32_LEAN_AND_MEAN) -#define WIN32_LEAN_AND_MEAN -#endif - -#if !defined(NOMINMAX) -#define NOMINMAX -#endif - -#endif \ No newline at end of file diff --git a/src/YYCCLegacy/WinImportSuffix.hpp b/src/YYCCLegacy/WinImportSuffix.hpp deleted file mode 100644 index 8e42d0a..0000000 --- a/src/YYCCLegacy/WinImportSuffix.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// It is by design that no pragma once or #if to prevent deplicated including. -// Because this header is the part of wrapper, not a real header. -// #pragma once - -#include "YYCCInternal.hpp" - -#if YYCC_OS == YYCC_OS_WINDOWS - -// Windows also will generate following macros -// which may cause the function sign is different in Windows and other platforms. -// So we simply remove them. -// Because #undef will not throw error if there are no matched macro, -// so we simply #undef them directly. -#undef GetObject -#undef GetClassName -#undef LoadImage -#undef GetTempPath -#undef GetModuleFileName -#undef CopyFile -#undef MoveFile -#undef DeleteFile - -#endif \ No newline at end of file diff --git a/src/yycc/string/parse.hpp b/src/yycc/string/parse.hpp index 9a5e93f..0373450 100644 --- a/src/yycc/string/parse.hpp +++ b/src/yycc/string/parse.hpp @@ -106,8 +106,8 @@ namespace yycc::string::parse { // Get lower case auto lower_case = NS_YYCC_STRING_OP::to_lower(strl); // Compare result - if (strl == YYCC_U8("true")) return true; - else if (strl == YYCC_U8("false")) return false; + if (lower_case == YYCC_U8("true")) return true; + else if (lower_case == YYCC_U8("false")) return false; else return ParseError::InvalidString; } @@ -126,10 +126,10 @@ namespace yycc::string::parse { T& num, std::chars_format fmt = std::chars_format::general) { auto rv = priv_parse(strl, fmt); - if (const auto* ptr = std::get_if(rv)) { + if (const auto* ptr = std::get_if(&rv)) { num = *ptr; return true; - } else if (const auto* ptr = std::get_if(rv)) { + } else if (const auto* ptr = std::get_if(&rv)) { return false; } else { // Unreachable @@ -149,10 +149,10 @@ namespace yycc::string::parse { template && !std::is_same_v, int> = 0> bool try_parse(const NS_YYCC_STRING::u8string_view& strl, T& num, int base = 10) { auto rv = priv_parse(strl, base); - if (const auto* ptr = std::get_if(rv)) { + if (const auto* ptr = std::get_if(&rv)) { num = *ptr; return true; - } else if (const auto* ptr = std::get_if(rv)) { + } else if (const auto* ptr = std::get_if(&rv)) { return false; } else { // Unreachable @@ -171,10 +171,10 @@ namespace yycc::string::parse { template, int> = 0> bool try_parse(const NS_YYCC_STRING::u8string_view& strl, T& num) { auto rv = priv_parse(strl); - if (const auto* ptr = std::get_if(rv)) { + if (const auto* ptr = std::get_if(&rv)) { num = *ptr; return true; - } else if (const auto* ptr = std::get_if(rv)) { + } else if (const auto* ptr = std::get_if(&rv)) { return false; } else { // Unreachable diff --git a/testbench/CMakeLists.txt b/testbench/CMakeLists.txt index 0c6cf35..c648fdc 100644 --- a/testbench/CMakeLists.txt +++ b/testbench/CMakeLists.txt @@ -8,6 +8,8 @@ PRIVATE yycc/constraint/builder.cpp yycc/string/op.cpp yycc/string/reinterpret.cpp + yycc/string/parse.cpp + yycc/string/stringify.cpp ) # Setup headers target_include_directories(YYCCTestbench diff --git a/testbench/yycc/string/parse.cpp b/testbench/yycc/string/parse.cpp new file mode 100644 index 0000000..17fd0f2 --- /dev/null +++ b/testbench/yycc/string/parse.cpp @@ -0,0 +1,82 @@ +#include +#include +#include +#include + +#include + +#define PARSE ::yycc::string::parse + +namespace yycctest::string::parse { + + // These 2 test macros build string container via given string. + // Check `try_parse` first, and then check `parse`. + +#define TEST_SUCCESS(type_t, value, string_value, ...) { \ + u8string cache_string(YYCC_U8(string_value)); \ + type_t cache; \ + ASSERT_TRUE(PARSE::try_parse(cache_string, cache, ##__VA_ARGS__)); \ + EXPECT_EQ(cache, value); \ + EXPECT_EQ(PARSE::parse(cache_string, ##__VA_ARGS__), value); \ +} + +#define TEST_FAIL(type_t, string_value, ...) { \ + u8string cache_string(YYCC_U8(string_value)); \ + type_t cache; \ + EXPECT_FALSE(PARSE::try_parse(cache_string, cache, ##__VA_ARGS__)); \ + EXPECT_ANY_THROW(PARSE::parse(cache_string, ##__VA_ARGS__)); \ +} + + TEST(StringParse, Common) { + TEST_SUCCESS(i8, INT8_C(-61), "-61"); + TEST_SUCCESS(u8, UINT8_C(200), "200"); + TEST_SUCCESS(i16, INT16_C(6161), "6161"); + TEST_SUCCESS(u16, UINT16_C(32800), "32800"); + TEST_SUCCESS(i32, INT32_C(61616161), "61616161"); + TEST_SUCCESS(u32, UINT32_C(4294967293), "4294967293"); + TEST_SUCCESS(i64, INT64_C(616161616161), "616161616161"); + TEST_SUCCESS(u64, UINT64_C(9223372036854775807), "9223372036854775807"); + + TEST_SUCCESS(float, 1.0f, "1.0"); + TEST_SUCCESS(double, 1.0, "1.0"); + + TEST_SUCCESS(bool, true, "true"); + TEST_SUCCESS(bool, false, "false"); + } + + TEST(StringParse, Radix) { + TEST_SUCCESS(u32, UINT32_C(0xffff), "ffff", 16); + TEST_SUCCESS(u32, UINT32_C(032), "032", 8); + TEST_SUCCESS(u32, UINT32_C(0B1011), "1011", 2); + } + + TEST(StringParse, CaseInsensitive) { + TEST_SUCCESS(bool, true, "tRUE"); + } + + TEST(StringParse, Overflow) { + TEST_FAIL(i8, "6161"); + TEST_FAIL(u8, "32800"); + TEST_FAIL(i16, "61616161"); + TEST_FAIL(u16, "4294967293"); + TEST_FAIL(i32, "616161616161"); + TEST_FAIL(u32, "9223372036854775807"); + TEST_FAIL(i64, "616161616161616161616161"); + TEST_FAIL(u64, "92233720368547758079223372036854775807"); + + TEST_FAIL(float, "1e40"); + TEST_FAIL(double, "1e114514"); + } + + TEST(StringParse, BadRadix) { + TEST_FAIL(u32, "fghj", 16); + TEST_FAIL(u32, "099", 8); + TEST_FAIL(u32, "12345", 2); + } + + TEST(StringParse, InvalidWords) { + TEST_FAIL(u32, "hello, world!"); + TEST_FAIL(bool, "hello, world!"); + } + +} diff --git a/testbench/yycc/string/stringify.cpp b/testbench/yycc/string/stringify.cpp new file mode 100644 index 0000000..2fa566a --- /dev/null +++ b/testbench/yycc/string/stringify.cpp @@ -0,0 +1,41 @@ +#include +#include +#include +#include + +#include + +#define STRINGIFY ::yycc::string::stringify + +namespace yycctest::string::stringify { + +#define TEST_SUCCESS(type_t, value, string_value, ...) { \ + type_t cache = value; \ + u8string ret = STRINGIFY::stringify(cache, ##__VA_ARGS__); \ + EXPECT_EQ(ret, YYCC_U8(string_value)); \ +} + + TEST(StringStringify, Common) { + TEST_SUCCESS(i8, INT8_C(-61), "-61"); + TEST_SUCCESS(u8, UINT8_C(200), "200"); + TEST_SUCCESS(i16, INT16_C(6161), "6161"); + TEST_SUCCESS(u16, UINT16_C(32800), "32800"); + TEST_SUCCESS(i32, INT32_C(61616161), "61616161"); + TEST_SUCCESS(u32, UINT32_C(4294967293), "4294967293"); + TEST_SUCCESS(i64, INT64_C(616161616161), "616161616161"); + TEST_SUCCESS(u64, UINT64_C(9223372036854775807), "9223372036854775807"); + + TEST_SUCCESS(float, 1.0f, "1.0", std::chars_format::fixed, 1); + TEST_SUCCESS(double, 1.0, "1.0", std::chars_format::fixed, 1); + + TEST_SUCCESS(bool, true, "true"); + TEST_SUCCESS(bool, false, "false"); + } + + TEST(StringStringify, Radix) { + TEST_SUCCESS(u32, UINT32_C(0xffff), "ffff", 16); + TEST_SUCCESS(u32, UINT32_C(032), "32", 8); + TEST_SUCCESS(u32, UINT32_C(0B1011), "1011", 2); + } + +}