diff options
author | crupest <crupest@outlook.com> | 2022-01-18 22:40:39 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2022-01-18 22:40:39 +0800 |
commit | bc37fdb4b746ce0f5f5c49ad9779464acb488284 (patch) | |
tree | 53aa5b98ebba25016be1ec74a2ff161a70de4762 | |
parent | d28248cc4ccb1a1d81f0d82b8317767f4e2bff44 (diff) | |
download | cru-bc37fdb4b746ce0f5f5c49ad9779464acb488284.tar.gz cru-bc37fdb4b746ce0f5f5c49ad9779464acb488284.tar.bz2 cru-bc37fdb4b746ce0f5f5c49ad9779464acb488284.zip |
...
-rw-r--r-- | include/cru/common/Format.hpp | 140 | ||||
-rw-r--r-- | include/cru/common/Logger.hpp | 1 | ||||
-rw-r--r-- | include/cru/common/String.hpp | 79 | ||||
-rw-r--r-- | include/cru/common/StringUtil.hpp | 1 | ||||
-rw-r--r-- | include/cru/platform/Check.hpp | 1 | ||||
-rw-r--r-- | include/cru/platform/Color.hpp | 1 | ||||
-rw-r--r-- | include/cru/platform/GraphicsBase.hpp | 1 | ||||
-rw-r--r-- | include/cru/platform/gui/InputMethod.hpp | 25 | ||||
-rw-r--r-- | src/common/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/common/ErrnoException.cpp | 2 | ||||
-rw-r--r-- | src/common/Format.cpp | 111 | ||||
-rw-r--r-- | src/common/String.cpp | 50 | ||||
-rw-r--r-- | src/common/StringUtil.cpp | 2 | ||||
-rw-r--r-- | src/common/io/UnixFileStream.cpp | 1 | ||||
-rw-r--r-- | src/osx/graphics/quartz/Brush.cpp | 1 | ||||
-rw-r--r-- | src/osx/graphics/quartz/TextLayout.cpp | 1 | ||||
-rw-r--r-- | src/parse/Grammar.cpp | 1 | ||||
-rw-r--r-- | src/win/Exception.cpp | 1 | ||||
-rw-r--r-- | src/win/graphics/direct/Font.cpp | 1 | ||||
-rw-r--r-- | test/common/StringTest.cpp | 1 |
20 files changed, 269 insertions, 153 deletions
diff --git a/include/cru/common/Format.hpp b/include/cru/common/Format.hpp new file mode 100644 index 00000000..d53a5720 --- /dev/null +++ b/include/cru/common/Format.hpp @@ -0,0 +1,140 @@ +#pragma once + +#include "Exception.hpp" +#include "String.hpp" + +namespace cru { +inline String ToString(bool value) { + return value ? String(u"true") : String(u"false"); +} + +template <typename T> +std::enable_if_t<std::is_integral_v<T>, String> ToString(T value) { + std::array<char, 50> buffer; + auto result = + std::to_chars(buffer.data(), buffer.data() + buffer.size(), value); + + if (result.ec == std::errc{}) { + } else { + throw std::invalid_argument("Failed to convert value to chars."); + } + + auto size = result.ptr - buffer.data(); + auto b = new char16_t[size + 1]; + b[size] = 0; + std::copy(buffer.data(), result.ptr, b); + return String::FromBuffer(b, size, size); +} + +template <typename T> +std::enable_if_t<std::is_floating_point_v<T>, String> ToString(T value) { + auto str = std::to_string(value); + return String(str.cbegin(), str.cend()); +} + +template <typename T> +std::enable_if_t< + std::is_convertible_v<decltype(ToString(std::declval<const T&>)), String>, + String> +ToString(const T& value, StringView option) { + return ToString(value); +} + +inline String ToString(String value) { return value; } + +namespace details { +enum class FormatTokenType { PlaceHolder, Text }; +enum class FormatPlaceHolderType { None, Positioned, Named }; + +struct FormatToken { + static FormatToken Text() { + return FormatToken{FormatTokenType::Text, {}, {}, 0, {}, {}}; + } + + static FormatToken NonePlaceHolder(String option) { + return FormatToken(FormatTokenType::PlaceHolder, {}, + FormatPlaceHolderType::None, 0, {}, std::move(option)); + } + + static FormatToken PositionedPlaceHolder(int position, String option) { + return FormatToken(FormatTokenType::PlaceHolder, {}, + FormatPlaceHolderType::Positioned, position, {}, + std::move(option)); + } + + static FormatToken NamedPlaceHolder(String name, String option) { + return FormatToken(FormatTokenType::PlaceHolder, {}, + FormatPlaceHolderType::Named, 0, std::move(name), + std::move(option)); + } + + FormatToken(FormatTokenType type, String data, + FormatPlaceHolderType place_holder_type, + int place_holder_position, String place_holder_name, + String place_holder_option) + : type(type), + data(std::move(data)), + place_holder_type(place_holder_type), + place_holder_position(place_holder_position), + place_holder_name(std::move(place_holder_name)), + place_holder_option(std::move(place_holder_option)) {} + + CRU_DEFAULT_COPY(FormatToken) + CRU_DEFAULT_MOVE(FormatToken) + + CRU_DEFAULT_DESTRUCTOR(FormatToken) + + FormatTokenType type; + String data; + FormatPlaceHolderType place_holder_type; + int place_holder_position; + String place_holder_name; + String place_holder_option; +}; + +std::vector<FormatToken> CRU_BASE_API ParseToFormatTokenList(const String& str); + +void CRU_BASE_API FormatAppendFromFormatTokenList( + String& current, const std::vector<FormatToken>& format_token_list, + Index index); + +template <typename TA, typename... T> +void FormatAppendFromFormatTokenList( + String& current, const std::vector<FormatToken>& format_token_list, + Index index, TA&& args0, T&&... args) { + for (Index i = index; i < static_cast<Index>(format_token_list.size()); i++) { + const auto& token = format_token_list[i]; + if (token.type == FormatTokenType::PlaceHolder) { + if (token.place_holder_type == FormatPlaceHolderType::None) { + current += ToString(std::forward<TA>(args0), token.place_holder_option); + FormatAppendFromFormatTokenList(current, format_token_list, i + 1, + std::forward<T>(args)...); + + return; + } else { + throw Exception( + u"Currently do not support positional or named place holder."); + } + } else { + current += token.data; + } + } +} +} // namespace details + +template <typename... T> +String Format(const String& format, T&&... args) { + String result; + + details::FormatAppendFromFormatTokenList( + result, details::ParseToFormatTokenList(format), 0, + std::forward<T>(args)...); + + return result; +} + +template <typename... T> +String String::Format(T&&... args) const { + return cru::Format(*this, std::forward<T>(args)...); +} +} // namespace cru diff --git a/include/cru/common/Logger.hpp b/include/cru/common/Logger.hpp index 7d43fc5a..c4a3e54a 100644 --- a/include/cru/common/Logger.hpp +++ b/include/cru/common/Logger.hpp @@ -2,6 +2,7 @@ #include "Base.hpp" #include "String.hpp" +#include "Format.hpp" #include <list> #include <memory> diff --git a/include/cru/common/String.hpp b/include/cru/common/String.hpp index dd3da52f..8db012cb 100644 --- a/include/cru/common/String.hpp +++ b/include/cru/common/String.hpp @@ -14,6 +14,7 @@ #include <string> #include <string_view> #include <system_error> +#include <tuple> #include <type_traits> #include <vector> @@ -320,84 +321,6 @@ inline String operator+(const String& left, const String& right) { return result; } -inline String ToString(bool value) { - return value ? String(u"true") : String(u"false"); -} - -template <typename T> -std::enable_if_t<std::is_integral_v<T>, String> ToString(T value) { - std::array<char, 50> buffer; - auto result = - std::to_chars(buffer.data(), buffer.data() + buffer.size(), value); - - if (result.ec == std::errc{}) { - } else { - throw std::invalid_argument("Failed to convert value to chars."); - } - - auto size = result.ptr - buffer.data(); - auto b = new char16_t[size + 1]; - b[size] = 0; - std::copy(buffer.data(), result.ptr, b); - return String::FromBuffer(b, size, size); -} - -template <typename T> -std::enable_if_t<std::is_floating_point_v<T>, String> ToString(T value) { - auto str = std::to_string(value); - return String(str.cbegin(), str.cend()); -} - -inline String ToString(String value) { return value; } - -namespace details { -enum class FormatTokenType { PlaceHolder, Text }; - -struct FormatToken { - FormatTokenType type; - String data; -}; - -std::vector<FormatToken> CRU_BASE_API ParseToFormatTokenList(const String& str); - -void CRU_BASE_API FormatAppendFromFormatTokenList( - String& current, const std::vector<FormatToken>& format_token_list, - Index index); - -template <typename TA, typename... T> -void FormatAppendFromFormatTokenList( - String& current, const std::vector<FormatToken>& format_token_list, - Index index, TA&& args0, T&&... args) { - for (Index i = index; i < static_cast<Index>(format_token_list.size()); i++) { - const auto& token = format_token_list[i]; - if (token.type == FormatTokenType::PlaceHolder) { - current += ToString(std::forward<TA>(args0)); - FormatAppendFromFormatTokenList(current, format_token_list, i + 1, - std::forward<T>(args)...); - return; - } else { - current += token.data; - } - } -} -} // namespace details - -template <typename... T> -String Format(const String& format, T&&... args) { - String result; - - details::FormatAppendFromFormatTokenList( - result, details::ParseToFormatTokenList(format), 0, - std::forward<T>(args)...); - - return result; -} - -template <typename... T> -String String::Format(T&&... args) const { - return cru::Format(*this, std::forward<T>(args)...); -} - CRU_DEFINE_COMPARE_OPERATORS(StringView) inline String::iterator String::insert(const_iterator pos, StringView str) { diff --git a/include/cru/common/StringUtil.hpp b/include/cru/common/StringUtil.hpp index 3c673db1..2d53fc2b 100644 --- a/include/cru/common/StringUtil.hpp +++ b/include/cru/common/StringUtil.hpp @@ -223,6 +223,7 @@ char16_t CRU_BASE_API ToLower(char16_t c); char16_t CRU_BASE_API ToUpper(char16_t c); bool CRU_BASE_API IsWhitespace(char16_t c); +bool CRU_BASE_API IsDigit(char16_t c); Utf8CodePointIterator CRU_BASE_API CreateUtf8Iterator(const std::byte* buffer, Index size); diff --git a/include/cru/platform/Check.hpp b/include/cru/platform/Check.hpp index 5d99aa9c..121e3505 100644 --- a/include/cru/platform/Check.hpp +++ b/include/cru/platform/Check.hpp @@ -3,6 +3,7 @@ #include "Resource.hpp" #include "cru/common/String.hpp" +#include "cru/common/Format.hpp" #include <memory> #include <type_traits> diff --git a/include/cru/platform/Color.hpp b/include/cru/platform/Color.hpp index 94c6c485..cdc6518b 100644 --- a/include/cru/platform/Color.hpp +++ b/include/cru/platform/Color.hpp @@ -3,6 +3,7 @@ #include "cru/platform/Base.hpp" #include "cru/common/String.hpp" +#include "cru/common/Format.hpp" #include <cstdint> #include <optional> diff --git a/include/cru/platform/GraphicsBase.hpp b/include/cru/platform/GraphicsBase.hpp index 73b41bb5..088086e9 100644 --- a/include/cru/platform/GraphicsBase.hpp +++ b/include/cru/platform/GraphicsBase.hpp @@ -3,6 +3,7 @@ #include "cru/common/Range.hpp" #include "cru/common/String.hpp" +#include "cru/common/Format.hpp" #include <cstdint> #include <limits> diff --git a/include/cru/platform/gui/InputMethod.hpp b/include/cru/platform/gui/InputMethod.hpp index 923a2088..a34aba74 100644 --- a/include/cru/platform/gui/InputMethod.hpp +++ b/include/cru/platform/gui/InputMethod.hpp @@ -53,28 +53,3 @@ struct IInputMethodContext : virtual IPlatformResource { }; } // namespace cru::platform::gui -// template <> -// struct fmt::formatter<cru::platform::gui::CompositionText, char16_t> -// : fmt::formatter<std::u16string_view, char16_t> { -// auto parse(fmt::basic_format_parse_context<char16_t>& ctx) { -// return fmt::formatter<std::u16string_view, char16_t>::parse(ctx); -// } - -// template <typename FormatContext> -// auto format(const cru::platform::gui::CompositionText& ct, -// FormatContext& ctx) { -// auto output = ctx.out(); -// output = format_to(output, u"text: {}\n", ct.text); -// output = format_to(output, u"clauses:\n"); -// for (gsl::index i = 0; i < static_cast<gsl::index>(ct.clauses.size()); -// i++) { -// const auto& clause = ct.clauses[i]; -// output = -// format_to(output, u"\t{}. start: {} end: {}{}\n", i, clause.start, -// clause.end, clause.target ? u" target" : u""); -// } -// output = format_to(output, u"selection: position: {} count: {}", -// ct.selection.position, ct.selection.count); -// return output; -// } -// }; diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 300fa03e..40c85ef0 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -1,6 +1,7 @@ add_library(cru_base SHARED Base.cpp Exception.cpp + Format.cpp Logger.cpp PropertyTree.cpp String.cpp diff --git a/src/common/ErrnoException.cpp b/src/common/ErrnoException.cpp index 15d298b3..731ae3e5 100644 --- a/src/common/ErrnoException.cpp +++ b/src/common/ErrnoException.cpp @@ -1,5 +1,7 @@ #include "cru/common/ErrnoException.hpp" +#include "cru/common/Format.hpp" + #include <errno.h> namespace cru { diff --git a/src/common/Format.cpp b/src/common/Format.cpp new file mode 100644 index 00000000..2d3b50eb --- /dev/null +++ b/src/common/Format.cpp @@ -0,0 +1,111 @@ +#include "cru/common/Format.hpp" + +namespace cru { +namespace details { +FormatToken ParsePlaceHolder(String place_holder_string) { + if (place_holder_string.empty()) { + return FormatToken::NonePlaceHolder({}); + } + + if (place_holder_string.StartWith(u":")) { + if (place_holder_string.Find(u':', 1) != -1) { + throw Exception(u"Two ':' inside placeholder."); + } + + return FormatToken::NonePlaceHolder(place_holder_string.substr(1)); + } + if (IsDigit(place_holder_string[0])) { + int position = 0; + int index = 0; + while (index < place_holder_string.size() && + IsDigit(place_holder_string[index])) { + position = position * 10 + place_holder_string[index] - '0'; + index++; + } + + String option; + + if (index != place_holder_string.size()) { + if (place_holder_string[index] != ':') { + throw Exception(u"Invalid placeholder in format."); + } + + option = place_holder_string.substr(index + 1); + } + + return FormatToken::PositionedPlaceHolder(position, std::move(option)); + } + + auto separator_index = place_holder_string.Find(':'); + if (separator_index == -1) { + return FormatToken::NamedPlaceHolder(place_holder_string, {}); + } else { + return FormatToken::NamedPlaceHolder( + place_holder_string.substr(0, separator_index), + place_holder_string.substr(separator_index + 1)); + } +} + +std::vector<FormatToken> ParseToFormatTokenList(const String& str) { + std::vector<FormatToken> result; + + auto push_char = [&result](char16_t c) { + if (result.empty() || result.back().type == FormatTokenType::PlaceHolder) { + result.push_back(FormatToken::Text()); + } + result.back().data.append(c); + }; + + bool try_to_escape = false; + bool is_in_place_holder = false; + String place_holder_string; + + for (auto c : str) { + if (c == u'{') { + if (try_to_escape) { + push_char(u'{'); + try_to_escape = false; + is_in_place_holder = false; + } else { + if (is_in_place_holder) { + throw Exception(u"Invalid format string: '{' inside placeholder."); + } + + try_to_escape = true; + is_in_place_holder = true; + } + } else if (c == u'}') { + if (is_in_place_holder) { + is_in_place_holder = false; + result.push_back(ParsePlaceHolder(std::move(place_holder_string))); + place_holder_string.clear(); + } else { + push_char(u'}'); + } + try_to_escape = false; + } else { + if (is_in_place_holder) { + place_holder_string.push_back(c); + } else { + push_char(c); + } + try_to_escape = false; + } + } + return result; +} + +void FormatAppendFromFormatTokenList( + String& current, const std::vector<FormatToken>& format_token_list, + Index index) { + for (Index i = index; i < static_cast<Index>(format_token_list.size()); i++) { + const auto& token = format_token_list[i]; + if (token.type == FormatTokenType::PlaceHolder) { + throw Exception(u"More placeholder than args."); + } else { + current += token.data; + } + } +} +} // namespace details +} // namespace cru diff --git a/src/common/String.cpp b/src/common/String.cpp index 21f3f235..9366abeb 100644 --- a/src/common/String.cpp +++ b/src/common/String.cpp @@ -394,56 +394,6 @@ int String::Compare(const String& other) const { } } -namespace details { -std::vector<FormatToken> ParseToFormatTokenList(const String& str) { - std::vector<FormatToken> result; - - auto push_char = [&result](char16_t c) { - if (result.empty() || result.back().type == FormatTokenType::PlaceHolder) { - result.push_back(FormatToken{FormatTokenType::Text, String{}}); - } - result.back().data.append(c); - }; - - bool last_is_left_bracket = false; - for (auto c : str) { - if (c == u'{') { - if (last_is_left_bracket) { - push_char(u'{'); - last_is_left_bracket = false; - } else { - last_is_left_bracket = true; - } - } else if (c == u'}') { - if (last_is_left_bracket) { - result.push_back(FormatToken{FormatTokenType::PlaceHolder, String{}}); - } - last_is_left_bracket = false; - } else { - if (last_is_left_bracket) { - push_char(u'{'); - } - push_char(c); - last_is_left_bracket = false; - } - } - return result; -} - -void FormatAppendFromFormatTokenList( - String& current, const std::vector<FormatToken>& format_token_list, - Index index) { - for (Index i = index; i < static_cast<Index>(format_token_list.size()); i++) { - const auto& token = format_token_list[i]; - if (token.type == FormatTokenType::PlaceHolder) { - current += u"{}"; - } else { - current += token.data; - } - } -} -} // namespace details - int StringView::Compare(const StringView& other) const { const_iterator i1 = cbegin(); const_iterator i2 = other.cbegin(); diff --git a/src/common/StringUtil.cpp b/src/common/StringUtil.cpp index 04435a0a..1f5f6150 100644 --- a/src/common/StringUtil.cpp +++ b/src/common/StringUtil.cpp @@ -258,6 +258,8 @@ bool IsWhitespace(char16_t c) { return c == u' ' || c == u'\t' || c == u'\n' || c == u'\r'; } +bool IsDigit(char16_t c) { return c >= u'0' && c <= u'9'; } + Utf8CodePointIterator CreateUtf8Iterator(const std::byte* buffer, Index size) { return Utf8CodePointIterator(reinterpret_cast<const char*>(buffer), size); } diff --git a/src/common/io/UnixFileStream.cpp b/src/common/io/UnixFileStream.cpp index 9c9abba5..615c36f1 100644 --- a/src/common/io/UnixFileStream.cpp +++ b/src/common/io/UnixFileStream.cpp @@ -1,6 +1,7 @@ #include "cru/common/io/UnixFileStream.hpp" #include "cru/common/ErrnoException.hpp" #include "cru/common/io/OpenFileFlag.hpp" +#include "cru/common/Format.hpp" #include <fcntl.h> #include <sys/_types/_s_ifmt.h> diff --git a/src/osx/graphics/quartz/Brush.cpp b/src/osx/graphics/quartz/Brush.cpp index 83ac20a2..637ce34f 100644 --- a/src/osx/graphics/quartz/Brush.cpp +++ b/src/osx/graphics/quartz/Brush.cpp @@ -1,5 +1,6 @@ #include "cru/osx/graphics/quartz/Brush.hpp" #include "cru/common/String.hpp" +#include "cru/common/Format.hpp" namespace cru::platform::graphics::osx::quartz { QuartzSolidColorBrush::QuartzSolidColorBrush(IGraphicsFactory* graphics_factory, diff --git a/src/osx/graphics/quartz/TextLayout.cpp b/src/osx/graphics/quartz/TextLayout.cpp index de14816d..3f33acdb 100644 --- a/src/osx/graphics/quartz/TextLayout.cpp +++ b/src/osx/graphics/quartz/TextLayout.cpp @@ -6,6 +6,7 @@ #include "cru/osx/graphics/quartz/Resource.hpp" #include "cru/platform/Check.hpp" #include "cru/platform/graphics/Base.hpp" +#include "cru/common/Format.hpp" #include <algorithm> #include <limits> diff --git a/src/parse/Grammar.cpp b/src/parse/Grammar.cpp index 19095150..52933f91 100644 --- a/src/parse/Grammar.cpp +++ b/src/parse/Grammar.cpp @@ -1,6 +1,7 @@ #include "cru/parse/Grammar.hpp" #include "cru/common/String.hpp" #include "cru/parse/Symbol.hpp" +#include "cru/common/Format.hpp" #include <algorithm> #include <iterator> diff --git a/src/win/Exception.cpp b/src/win/Exception.cpp index 2664b092..fb46f184 100644 --- a/src/win/Exception.cpp +++ b/src/win/Exception.cpp @@ -1,4 +1,5 @@ #include "cru/win/Exception.hpp" +#include "cru/common/Format.hpp" #include <optional> diff --git a/src/win/graphics/direct/Font.cpp b/src/win/graphics/direct/Font.cpp index 413c998b..ffc16214 100644 --- a/src/win/graphics/direct/Font.cpp +++ b/src/win/graphics/direct/Font.cpp @@ -1,5 +1,6 @@ #include "cru/win/graphics/direct/Font.hpp" +#include "cru/common/Format.hpp" #include "cru/win/graphics/direct/Exception.hpp" #include "cru/win/graphics/direct/Factory.hpp" diff --git a/test/common/StringTest.cpp b/test/common/StringTest.cpp index 2dc72478..7198f47b 100644 --- a/test/common/StringTest.cpp +++ b/test/common/StringTest.cpp @@ -1,4 +1,5 @@ #include "cru/common/String.hpp" +#include "cru/common/Format.hpp" #include <gtest/gtest.h> |