Index: include/llvm/ADT/StringRef.h =================================================================== --- include/llvm/ADT/StringRef.h +++ include/llvm/ADT/StringRef.h @@ -10,6 +10,7 @@ #ifndef LLVM_ADT_STRINGREF_H #define LLVM_ADT_STRINGREF_H +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/Compiler.h" #include @@ -276,6 +277,22 @@ return npos; } + LLVM_ATTRIBUTE_ALWAYS_INLINE + size_t find_if(function_ref F, size_t From = 0) const { + StringRef S = drop_front(From); + while (!S.empty()) { + if (F(S.front())) + return size() - S.size(); + S = S.drop_front(); + } + return npos; + } + + LLVM_ATTRIBUTE_ALWAYS_INLINE + size_t find_if_not(function_ref F, size_t From = 0) const { + return find_if([F](char c) { return !F(c); }, From); + } + /// Search for the first string \p Str in the string. /// /// \returns The index of the first occurrence of \p Str, or npos if not @@ -496,6 +513,16 @@ return drop_front(size() - N); } + LLVM_ATTRIBUTE_UNUSED_RESULT StringRef + take_while(function_ref F) const { + return substr(0, find_if_not(F)); + } + + LLVM_ATTRIBUTE_UNUSED_RESULT StringRef + take_until(function_ref F) const { + return substr(0, find_if(F)); + } + /// Return a StringRef equal to 'this' but with the first \p N elements /// dropped. LLVM_ATTRIBUTE_ALWAYS_INLINE @@ -514,6 +541,16 @@ return substr(0, size()-N); } + template + LLVM_ATTRIBUTE_UNUSED_RESULT StringRef drop_while(Func F) const { + return substr(find_if_not(F)); + } + + template + LLVM_ATTRIBUTE_UNUSED_RESULT StringRef drop_until(Func F) const { + return substr(find_if(F)); + } + /// Returns true if this StringRef has the given prefix and removes that /// prefix. LLVM_ATTRIBUTE_ALWAYS_INLINE Index: unittests/ADT/StringRefTest.cpp =================================================================== --- unittests/ADT/StringRefTest.cpp +++ unittests/ADT/StringRefTest.cpp @@ -864,4 +864,60 @@ EXPECT_TRUE(Taken.empty()); } +TEST(StringRefTest, FindIf) { + StringRef Punct("Test.String"); + StringRef NoPunct("ABCDEFG"); + StringRef Empty; + + auto IsPunct = [](char c) { return ::ispunct(c); }; + auto IsAlpha = [](char c) { return ::isalpha(c); }; + EXPECT_EQ(4, Punct.find_if(IsPunct)); + EXPECT_EQ(StringRef::npos, NoPunct.find_if(IsPunct)); + EXPECT_EQ(StringRef::npos, Empty.find_if(IsPunct)); + + EXPECT_EQ(4, Punct.find_if_not(IsAlpha)); + EXPECT_EQ(StringRef::npos, NoPunct.find_if_not(IsAlpha)); + EXPECT_EQ(StringRef::npos, Empty.find_if_not(IsAlpha)); +} + +TEST(StringRefTest, TakeWhileUntil) { + StringRef Test("String With 1 Number"); + + StringRef Taken = Test.take_while([](char c) { return ::isdigit(c); }); + EXPECT_EQ("", Taken); + + Taken = Test.take_until([](char c) { return ::isdigit(c); }); + EXPECT_EQ("String With ", Taken); + + Taken = Test.take_while([](char c) { return true; }); + EXPECT_EQ(Test, Taken); + + Taken = Test.take_until([](char c) { return true; }); + EXPECT_EQ("", Taken); + + Test = ""; + Taken = Test.take_while([](char c) { return true; }); + EXPECT_EQ("", Taken); +} + +TEST(StringRefTest, DropWhileUntil) { + StringRef Test("String With 1 Number"); + + StringRef Taken = Test.drop_while([](char c) { return ::isdigit(c); }); + EXPECT_EQ(Test, Taken); + + Taken = Test.drop_until([](char c) { return ::isdigit(c); }); + EXPECT_EQ("1 Number", Taken); + + Taken = Test.drop_while([](char c) { return true; }); + EXPECT_EQ("", Taken); + + Taken = Test.drop_until([](char c) { return true; }); + EXPECT_EQ(Test, Taken); + + Test = ""; + Taken = Test.drop_while([](char c) { return true; }); + EXPECT_EQ("", Taken); +} + } // end anonymous namespace