[tint][utils] Add more string helpers
Add utils::Split(), utils::Join() for splitting and joining strings with delimiters. Add options to SuggestAlternatives(). Add Quote() for surrounding a string with '. Requires some include shuffling and minor tweaks to other utilities. Change-Id: If75058e68cd986450fecc0b27bcc9a94c765a665 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/134580 Commit-Queue: Ben Clayton <bclayton@google.com> Reviewed-by: James Price <jrprice@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Auto-Submit: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
0b82a3ba15
commit
ca0b9ef49e
|
@ -22,6 +22,7 @@
|
|||
#include "src/tint/ast/transform/test_helper.h"
|
||||
#include "src/tint/builtin/builtin.h"
|
||||
#include "src/tint/builtin/texel_format.h"
|
||||
#include "src/tint/utils/string.h"
|
||||
|
||||
namespace tint::ast::transform {
|
||||
namespace {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "src/tint/utils/string.h"
|
||||
#include "src/tint/utils/string_stream.h"
|
||||
|
||||
namespace tint::bench {
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
#include "src/tint/utils/defer.h"
|
||||
#include "src/tint/utils/map.h"
|
||||
#include "src/tint/utils/scoped_assignment.h"
|
||||
#include "src/tint/utils/string.h"
|
||||
#include "src/tint/utils/string_stream.h"
|
||||
#include "src/tint/utils/unique_vector.h"
|
||||
|
||||
|
|
|
@ -3761,8 +3761,9 @@ bool Resolver::DiagnosticControl(const ast::DiagnosticControl& control) {
|
|||
} else {
|
||||
utils::StringStream ss;
|
||||
ss << "unrecognized diagnostic rule 'chromium." << name << "'\n";
|
||||
utils::SuggestAlternatives(name, builtin::kChromiumDiagnosticRuleStrings, ss,
|
||||
"chromium.");
|
||||
utils::SuggestAlternativeOptions opts;
|
||||
opts.prefix = "chromium.";
|
||||
utils::SuggestAlternatives(name, builtin::kChromiumDiagnosticRuleStrings, ss, opts);
|
||||
AddWarning(ss.str(), control.rule_name->source);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include "src/tint/utils/string.h"
|
||||
#include "src/tint/utils/transform.h"
|
||||
#include "src/tint/utils/vector.h"
|
||||
|
||||
namespace tint::utils {
|
||||
|
@ -51,33 +52,46 @@ size_t Distance(std::string_view str_a, std::string_view str_b) {
|
|||
void SuggestAlternatives(std::string_view got,
|
||||
Slice<char const* const> strings,
|
||||
utils::StringStream& ss,
|
||||
std::string_view prefix /* = "" */) {
|
||||
const SuggestAlternativeOptions& options /* = {} */) {
|
||||
auto views = Transform<8>(strings, [](char const* const str) { return std::string_view(str); });
|
||||
SuggestAlternatives(got, views.Slice(), ss, options);
|
||||
}
|
||||
|
||||
void SuggestAlternatives(std::string_view got,
|
||||
Slice<std::string_view> strings,
|
||||
utils::StringStream& ss,
|
||||
const SuggestAlternativeOptions& options /* = {} */) {
|
||||
// If the string typed was within kSuggestionDistance of one of the possible enum values,
|
||||
// suggest that. Don't bother with suggestions if the string was extremely long.
|
||||
constexpr size_t kSuggestionDistance = 5;
|
||||
constexpr size_t kSuggestionMaxLength = 64;
|
||||
if (!got.empty() && got.size() < kSuggestionMaxLength) {
|
||||
size_t candidate_dist = kSuggestionDistance;
|
||||
const char* candidate = nullptr;
|
||||
for (auto* str : strings) {
|
||||
std::string_view candidate;
|
||||
for (auto str : strings) {
|
||||
auto dist = utils::Distance(str, got);
|
||||
if (dist < candidate_dist) {
|
||||
candidate = str;
|
||||
candidate_dist = dist;
|
||||
}
|
||||
}
|
||||
if (candidate) {
|
||||
ss << "Did you mean '" << prefix << candidate << "'?\n";
|
||||
if (!candidate.empty()) {
|
||||
ss << "Did you mean '" << options.prefix << candidate << "'?";
|
||||
if (options.list_possible_values) {
|
||||
ss << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options.list_possible_values) {
|
||||
// List all the possible enumerator values
|
||||
ss << "Possible values: ";
|
||||
for (auto* str : strings) {
|
||||
for (auto str : strings) {
|
||||
if (str != strings[0]) {
|
||||
ss << ", ";
|
||||
}
|
||||
ss << "'" << prefix << str << "'";
|
||||
ss << "'" << options.prefix << str << "'";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "src/tint/utils/slice.h"
|
||||
#include "src/tint/utils/string_stream.h"
|
||||
#include "src/tint/utils/vector.h"
|
||||
|
||||
namespace tint::utils {
|
||||
|
||||
|
@ -38,6 +39,12 @@ namespace tint::utils {
|
|||
return str;
|
||||
}
|
||||
|
||||
/// @param value the boolean value to be printed as a string
|
||||
/// @returns value printed as a string via the stream `<<` operator
|
||||
inline std::string ToString(bool value) {
|
||||
return value ? "true" : "false";
|
||||
}
|
||||
|
||||
/// @param value the value to be printed as a string
|
||||
/// @returns value printed as a string via the stream `<<` operator
|
||||
template <typename T>
|
||||
|
@ -75,15 +82,33 @@ inline size_t HasSuffix(std::string_view str, std::string_view suffix) {
|
|||
/// @returns the Levenshtein distance between @p a and @p b
|
||||
size_t Distance(std::string_view a, std::string_view b);
|
||||
|
||||
/// Options for SuggestAlternatives()
|
||||
struct SuggestAlternativeOptions {
|
||||
/// The prefix to apply to the strings when printing
|
||||
std::string_view prefix;
|
||||
/// List all the possible values
|
||||
bool list_possible_values = true;
|
||||
};
|
||||
|
||||
/// Suggest alternatives for an unrecognized string from a list of possible values.
|
||||
/// @param got the unrecognized string
|
||||
/// @param strings the list of possible values
|
||||
/// @param ss the stream to write the suggest and list of possible values to
|
||||
/// @param prefix the prefix to apply to the strings when printing (optional)
|
||||
/// @param options options for the suggestion
|
||||
void SuggestAlternatives(std::string_view got,
|
||||
Slice<char const* const> strings,
|
||||
utils::StringStream& ss,
|
||||
std::string_view prefix = "");
|
||||
const SuggestAlternativeOptions& options = {});
|
||||
|
||||
/// Suggest alternatives for an unrecognized string from a list of possible values.
|
||||
/// @param got the unrecognized string
|
||||
/// @param strings the list of possible values
|
||||
/// @param ss the stream to write the suggest and list of possible values to
|
||||
/// @param options options for the suggestion
|
||||
void SuggestAlternatives(std::string_view got,
|
||||
Slice<std::string_view> strings,
|
||||
utils::StringStream& ss,
|
||||
const SuggestAlternativeOptions& options = {});
|
||||
|
||||
/// @param str the input string
|
||||
/// @param pred the predicate function
|
||||
|
@ -150,6 +175,51 @@ inline std::string_view TrimSpace(std::string_view str) {
|
|||
return Trim(str, IsSpace);
|
||||
}
|
||||
|
||||
/// @param str the input string
|
||||
/// @param delimiter the delimiter
|
||||
/// @return @p str split at each occurrence of @p delimiter
|
||||
inline utils::Vector<std::string_view, 8> Split(std::string_view str, std::string_view delimiter) {
|
||||
utils::Vector<std::string_view, 8> out;
|
||||
while (str.length() > delimiter.length()) {
|
||||
auto pos = str.find(delimiter);
|
||||
if (pos == std::string_view::npos) {
|
||||
break;
|
||||
}
|
||||
out.Push(str.substr(0, pos));
|
||||
str = str.substr(pos + delimiter.length());
|
||||
}
|
||||
out.Push(str);
|
||||
return out;
|
||||
}
|
||||
|
||||
/// @returns @p str quoted with <code>'</code>
|
||||
inline std::string Quote(std::string_view str) {
|
||||
return "'" + std::string(str) + "'";
|
||||
}
|
||||
|
||||
/// @param parts the input parts
|
||||
/// @param delimiter the delimiter
|
||||
/// @return @p parts joined as a string, delimited with @p delimiter
|
||||
template <typename T>
|
||||
inline std::string Join(utils::VectorRef<T> parts, std::string_view delimiter) {
|
||||
utils::StringStream s;
|
||||
for (auto& part : parts) {
|
||||
if (part != parts.Front()) {
|
||||
s << delimiter;
|
||||
}
|
||||
s << part;
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
|
||||
/// @param parts the input parts
|
||||
/// @param delimiter the delimiter
|
||||
/// @return @p parts joined as a string, delimited with @p delimiter
|
||||
template <typename T, size_t N>
|
||||
inline std::string Join(const utils::Vector<T, N>& parts, std::string_view delimiter) {
|
||||
return Join(utils::VectorRef<T>(parts), delimiter);
|
||||
}
|
||||
|
||||
} // namespace tint::utils
|
||||
|
||||
#endif // SRC_TINT_UTILS_STRING_H_
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <utility>
|
||||
|
||||
#include "src/tint/utils/unicode.h"
|
||||
#include "src/tint/utils/vector.h"
|
||||
|
||||
namespace tint::utils {
|
||||
|
||||
|
@ -189,6 +190,44 @@ class StringStream {
|
|||
/// @returns out so calls can be chained
|
||||
utils::StringStream& operator<<(utils::StringStream& out, CodePoint codepoint);
|
||||
|
||||
/// Prints the vector @p vec to @p o
|
||||
/// @param o the stream to write to
|
||||
/// @param vec the vector
|
||||
/// @return the stream so calls can be chained
|
||||
template <typename T, size_t N>
|
||||
inline utils::StringStream& operator<<(utils::StringStream& o, const utils::Vector<T, N>& vec) {
|
||||
o << "[";
|
||||
bool first = true;
|
||||
for (auto& el : vec) {
|
||||
if (!first) {
|
||||
o << ", ";
|
||||
}
|
||||
first = false;
|
||||
o << el;
|
||||
}
|
||||
o << "]";
|
||||
return o;
|
||||
}
|
||||
|
||||
/// Prints the vector @p vec to @p o
|
||||
/// @param o the stream to write to
|
||||
/// @param vec the vector reference
|
||||
/// @return the stream so calls can be chained
|
||||
template <typename T>
|
||||
inline utils::StringStream& operator<<(utils::StringStream& o, utils::VectorRef<T> vec) {
|
||||
o << "[";
|
||||
bool first = true;
|
||||
for (auto& el : vec) {
|
||||
if (!first) {
|
||||
o << ", ";
|
||||
}
|
||||
first = false;
|
||||
o << el;
|
||||
}
|
||||
o << "]";
|
||||
return o;
|
||||
}
|
||||
|
||||
} // namespace tint::utils
|
||||
|
||||
#endif // SRC_TINT_UTILS_STRING_STREAM_H_
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
#include "src/tint/utils/string.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "src/tint/utils/string_stream.h"
|
||||
|
||||
namespace tint::utils {
|
||||
|
@ -34,6 +34,8 @@ TEST(StringTest, ReplaceAll) {
|
|||
}
|
||||
|
||||
TEST(StringTest, ToString) {
|
||||
EXPECT_EQ("true", ToString(true));
|
||||
EXPECT_EQ("false", ToString(false));
|
||||
EXPECT_EQ("123", ToString(123));
|
||||
EXPECT_EQ("hello", ToString("hello"));
|
||||
}
|
||||
|
@ -82,6 +84,23 @@ Possible values: 'hello world', 'Hello World')");
|
|||
SuggestAlternatives("hello world", alternatives, ss);
|
||||
EXPECT_EQ(ss.str(), R"(Possible values: 'foobar', 'something else')");
|
||||
}
|
||||
{
|
||||
const char* alternatives[] = {"hello world", "Hello World"};
|
||||
utils::StringStream ss;
|
||||
SuggestAlternativeOptions opts;
|
||||
opts.prefix = "$";
|
||||
SuggestAlternatives("hello wordl", alternatives, ss, opts);
|
||||
EXPECT_EQ(ss.str(), R"(Did you mean '$hello world'?
|
||||
Possible values: '$hello world', '$Hello World')");
|
||||
}
|
||||
{
|
||||
const char* alternatives[] = {"hello world", "Hello World"};
|
||||
utils::StringStream ss;
|
||||
SuggestAlternativeOptions opts;
|
||||
opts.list_possible_values = false;
|
||||
SuggestAlternatives("hello world", alternatives, ss, opts);
|
||||
EXPECT_EQ(ss.str(), R"(Did you mean 'hello world'?)");
|
||||
}
|
||||
}
|
||||
|
||||
TEST(StringTest, TrimLeft) {
|
||||
|
@ -153,5 +172,27 @@ TEST(StringTest, TrimSpace) {
|
|||
EXPECT_EQ(TrimSpace(""), "");
|
||||
}
|
||||
|
||||
TEST(StringTest, Quote) {
|
||||
EXPECT_EQ("'meow'", Quote("meow"));
|
||||
}
|
||||
|
||||
#if 0 // Enable when moved to C++20 (https://github.com/google/googletest/issues/3081)
|
||||
TEST(StringTest, Split) {
|
||||
EXPECT_THAT(Split("", ","), testing::ElementsAre(""));
|
||||
EXPECT_THAT(Split("cat", ","), testing::ElementsAre("cat"));
|
||||
EXPECT_THAT(Split("cat,", ","), testing::ElementsAre("cat", ""));
|
||||
EXPECT_THAT(Split(",cat", ","), testing::ElementsAre("", "cat"));
|
||||
EXPECT_THAT(Split("cat,dog,fish", ","), testing::ElementsAre("cat", "dog", "fish"));
|
||||
EXPECT_THAT(Split("catdogfish", "dog"), testing::ElementsAre("cat", "fish"));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(StringTest, Join) {
|
||||
EXPECT_EQ(Join(utils::Vector<int, 1>{}, ","), "");
|
||||
EXPECT_EQ(Join(utils::Vector{1, 2, 3}, ","), "1,2,3");
|
||||
EXPECT_EQ(Join(utils::Vector{"cat"}, ","), "cat");
|
||||
EXPECT_EQ(Join(utils::Vector{"cat", "dog"}, ","), "cat,dog");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint::utils
|
||||
|
|
|
@ -85,6 +85,36 @@ auto Transform(const Vector<IN, N>& in, TRANSFORMER&& transform)
|
|||
return result;
|
||||
}
|
||||
|
||||
/// Transform performs an element-wise transformation of a slice.
|
||||
/// @param in the input slice.
|
||||
/// @param transform the transformation function with signature: `OUT(IN)`
|
||||
/// @tparam N the small-array size of the returned Vector
|
||||
/// @returns a new vector with each element of the source vector transformed by `transform`.
|
||||
template <size_t N, typename IN, typename TRANSFORMER>
|
||||
auto Transform(Slice<IN> in, TRANSFORMER&& transform) -> Vector<decltype(transform(in[0])), N> {
|
||||
Vector<decltype(transform(in[0])), N> result;
|
||||
result.Reserve(in.len);
|
||||
for (size_t i = 0; i < in.len; ++i) {
|
||||
result.Push(transform(in[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Transform performs an element-wise transformation of a slice.
|
||||
/// @param in the input slice.
|
||||
/// @param transform the transformation function with signature: `OUT(IN, size_t)`
|
||||
/// @tparam N the small-array size of the returned Vector
|
||||
/// @returns a new vector with each element of the source vector transformed by `transform`.
|
||||
template <size_t N, typename IN, typename TRANSFORMER>
|
||||
auto Transform(Slice<IN> in, TRANSFORMER&& transform) -> Vector<decltype(transform(in[0], 1u)), N> {
|
||||
Vector<decltype(transform(in[0], 1u)), N> result;
|
||||
result.Reserve(in.len);
|
||||
for (size_t i = 0; i < in.len; ++i) {
|
||||
result.Push(transform(in[i], i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Transform performs an element-wise transformation of a vector reference.
|
||||
/// @param in the input vector.
|
||||
/// @param transform the transformation function with signature: `OUT(IN)`
|
||||
|
@ -92,13 +122,7 @@ auto Transform(const Vector<IN, N>& in, TRANSFORMER&& transform)
|
|||
/// @returns a new vector with each element of the source vector transformed by `transform`.
|
||||
template <size_t N, typename IN, typename TRANSFORMER>
|
||||
auto Transform(VectorRef<IN> in, TRANSFORMER&& transform) -> Vector<decltype(transform(in[0])), N> {
|
||||
const auto count = in.Length();
|
||||
Vector<decltype(transform(in[0])), N> result;
|
||||
result.Reserve(count);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
result.Push(transform(in[i]));
|
||||
}
|
||||
return result;
|
||||
return Transform<N>(in.Slice(), std::forward<TRANSFORMER>(transform));
|
||||
}
|
||||
|
||||
/// Transform performs an element-wise transformation of a vector reference.
|
||||
|
@ -109,13 +133,7 @@ auto Transform(VectorRef<IN> in, TRANSFORMER&& transform) -> Vector<decltype(tra
|
|||
template <size_t N, typename IN, typename TRANSFORMER>
|
||||
auto Transform(VectorRef<IN> in, TRANSFORMER&& transform)
|
||||
-> Vector<decltype(transform(in[0], 1u)), N> {
|
||||
const auto count = in.Length();
|
||||
Vector<decltype(transform(in[0], 1u)), N> result;
|
||||
result.Reserve(count);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
result.Push(transform(in[i], i));
|
||||
}
|
||||
return result;
|
||||
return Transform<N>(in.Slice(), std::forward<TRANSFORMER>(transform));
|
||||
}
|
||||
|
||||
/// TransformN performs an element-wise transformation of a vector, transforming and returning at
|
||||
|
|
|
@ -26,8 +26,6 @@
|
|||
#include "src/tint/utils/bitcast.h"
|
||||
#include "src/tint/utils/compiler_macros.h"
|
||||
#include "src/tint/utils/slice.h"
|
||||
#include "src/tint/utils/string.h"
|
||||
#include "src/tint/utils/string_stream.h"
|
||||
|
||||
namespace tint::utils {
|
||||
|
||||
|
@ -587,12 +585,9 @@ Vector(Ts...) -> Vector<VectorCommonType<Ts...>, sizeof...(Ts)>;
|
|||
/// Aside from this move pattern, a VectorRef provides an immutable reference to the Vector.
|
||||
template <typename T>
|
||||
class VectorRef {
|
||||
/// The slice type used by this vector reference
|
||||
using Slice = utils::Slice<T>;
|
||||
|
||||
/// @returns an empty slice.
|
||||
static Slice& EmptySlice() {
|
||||
static Slice empty;
|
||||
static utils::Slice<T>& EmptySlice() {
|
||||
static utils::Slice<T> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
|
@ -608,7 +603,7 @@ class VectorRef {
|
|||
|
||||
/// Constructor from a Slice
|
||||
/// @param slice the slice
|
||||
VectorRef(Slice& slice) // NOLINT(runtime/explicit)
|
||||
VectorRef(utils::Slice<T>& slice) // NOLINT(runtime/explicit)
|
||||
: slice_(slice) {}
|
||||
|
||||
/// Constructor from a Vector
|
||||
|
@ -621,7 +616,7 @@ class VectorRef {
|
|||
/// @param vector the vector to create a reference of
|
||||
template <size_t N>
|
||||
VectorRef(const Vector<T, N>& vector) // NOLINT(runtime/explicit)
|
||||
: slice_(const_cast<Slice&>(vector.impl_.slice)) {}
|
||||
: slice_(const_cast<utils::Slice<T>&>(vector.impl_.slice)) {}
|
||||
|
||||
/// Constructor from a moved Vector
|
||||
/// @param vector the vector being moved
|
||||
|
@ -689,6 +684,9 @@ class VectorRef {
|
|||
return {slice_.template Reinterpret<U, ReinterpretMode::kUnsafe>()};
|
||||
}
|
||||
|
||||
/// @returns the internal slice of the vector
|
||||
utils::Slice<T> Slice() { return slice_; }
|
||||
|
||||
/// @returns true if the vector is empty.
|
||||
bool IsEmpty() const { return slice_.len == 0; }
|
||||
|
||||
|
@ -724,7 +722,7 @@ class VectorRef {
|
|||
friend class VectorRef;
|
||||
|
||||
/// The slice of the vector being referenced.
|
||||
Slice& slice_;
|
||||
utils::Slice<T>& slice_;
|
||||
/// Whether the slice data is passed by r-value reference, and can be moved.
|
||||
bool can_move_ = false;
|
||||
};
|
||||
|
@ -753,44 +751,6 @@ Vector<T, N> ToVector(const std::vector<T>& vector) {
|
|||
return out;
|
||||
}
|
||||
|
||||
/// Prints the vector @p vec to @p o
|
||||
/// @param o the stream to write to
|
||||
/// @param vec the vector
|
||||
/// @return the stream so calls can be chained
|
||||
template <typename T, size_t N>
|
||||
inline utils::StringStream& operator<<(utils::StringStream& o, const utils::Vector<T, N>& vec) {
|
||||
o << "[";
|
||||
bool first = true;
|
||||
for (auto& el : vec) {
|
||||
if (!first) {
|
||||
o << ", ";
|
||||
}
|
||||
first = false;
|
||||
o << ToString(el);
|
||||
}
|
||||
o << "]";
|
||||
return o;
|
||||
}
|
||||
|
||||
/// Prints the vector @p vec to @p o
|
||||
/// @param o the stream to write to
|
||||
/// @param vec the vector reference
|
||||
/// @return the stream so calls can be chained
|
||||
template <typename T>
|
||||
inline utils::StringStream& operator<<(utils::StringStream& o, utils::VectorRef<T> vec) {
|
||||
o << "[";
|
||||
bool first = true;
|
||||
for (auto& el : vec) {
|
||||
if (!first) {
|
||||
o << ", ";
|
||||
}
|
||||
first = false;
|
||||
o << ToString(el);
|
||||
}
|
||||
o << "]";
|
||||
return o;
|
||||
}
|
||||
|
||||
} // namespace tint::utils
|
||||
|
||||
#endif // SRC_TINT_UTILS_VECTOR_H_
|
||||
|
|
Loading…
Reference in New Issue