Skip to content
This repository was archived by the owner on Mar 28, 2020. It is now read-only.

Commit 568a8f4

Browse files
author
Zachary Turner
committed
Add some predicated searching functions to StringRef.
This adds 4 new functions to StringRef, which can be used to take or drop characters while a certain condition is met, or until a certain condition is met. They are: take_while - Return characters until a condition is not met. take_until - Return characters until a condition is met. drop_while - Remove characters until a condition is not met. drop_until - Remove characters until a condition is met. Internally, all of these functions delegate to two additional helper functions which can be used to search for the position of a character meeting or not meeting a condition, which are: find_if - Find the first character matching a predicate. find_if_not - Find the first character not matching a predicate. Differential Revision: https://reviews.llvm.org/D24842 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@282346 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 386ab19 commit 568a8f4

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed

include/llvm/ADT/StringRef.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#ifndef LLVM_ADT_STRINGREF_H
1111
#define LLVM_ADT_STRINGREF_H
1212

13+
#include "llvm/ADT/STLExtras.h"
1314
#include "llvm/ADT/iterator_range.h"
1415
#include "llvm/Support/Compiler.h"
1516
#include <algorithm>
@@ -276,6 +277,32 @@ namespace llvm {
276277
return npos;
277278
}
278279

280+
/// Search for the first character satisfying the predicate \p F
281+
///
282+
/// \returns The index of the first character satisfying \p F starting from
283+
/// \p From, or npos if not found.
284+
LLVM_ATTRIBUTE_ALWAYS_INLINE
285+
LLVM_ATTRIBUTE_UNUSED_RESULT
286+
size_t find_if(function_ref<bool(char)> F, size_t From = 0) const {
287+
StringRef S = drop_front(From);
288+
while (!S.empty()) {
289+
if (F(S.front()))
290+
return size() - S.size();
291+
S = S.drop_front();
292+
}
293+
return npos;
294+
}
295+
296+
/// Search for the first character not satisfying the predicate \p F
297+
///
298+
/// \returns The index of the first character not satisfying \p F starting
299+
/// from \p From, or npos if not found.
300+
LLVM_ATTRIBUTE_ALWAYS_INLINE
301+
LLVM_ATTRIBUTE_UNUSED_RESULT
302+
size_t find_if_not(function_ref<bool(char)> F, size_t From = 0) const {
303+
return find_if([F](char c) { return !F(c); }, From);
304+
}
305+
279306
/// Search for the first string \p Str in the string.
280307
///
281308
/// \returns The index of the first occurrence of \p Str, or npos if not
@@ -352,6 +379,9 @@ namespace llvm {
352379
LLVM_ATTRIBUTE_ALWAYS_INLINE
353380
bool contains(StringRef Other) const { return find(Other) != npos; }
354381

382+
LLVM_ATTRIBUTE_ALWAYS_INLINE
383+
bool contains(char C) const { return find_first_of(C) != npos; }
384+
355385
/// @}
356386
/// @name Helpful Algorithms
357387
/// @{
@@ -496,6 +526,22 @@ namespace llvm {
496526
return drop_front(size() - N);
497527
}
498528

529+
/// Return the longest prefix of 'this' such that every character
530+
/// in the prefix satisfies the given predicate.
531+
LLVM_ATTRIBUTE_ALWAYS_INLINE
532+
LLVM_ATTRIBUTE_UNUSED_RESULT
533+
StringRef take_while(function_ref<bool(char)> F) const {
534+
return substr(0, find_if_not(F));
535+
}
536+
537+
/// Return the longest prefix of 'this' such that no character in
538+
/// the prefix satisfies the given predicate.
539+
LLVM_ATTRIBUTE_ALWAYS_INLINE
540+
LLVM_ATTRIBUTE_UNUSED_RESULT
541+
StringRef take_until(function_ref<bool(char)> F) const {
542+
return substr(0, find_if(F));
543+
}
544+
499545
/// Return a StringRef equal to 'this' but with the first \p N elements
500546
/// dropped.
501547
LLVM_ATTRIBUTE_ALWAYS_INLINE
@@ -514,6 +560,22 @@ namespace llvm {
514560
return substr(0, size()-N);
515561
}
516562

563+
/// Return a StringRef equal to 'this', but with all characters satisfying
564+
/// the given predicate dropped from the beginning of the string.
565+
LLVM_ATTRIBUTE_ALWAYS_INLINE
566+
LLVM_ATTRIBUTE_UNUSED_RESULT
567+
StringRef drop_while(function_ref<bool(char)> F) const {
568+
return substr(find_if_not(F));
569+
}
570+
571+
/// Return a StringRef equal to 'this', but with all characters not
572+
/// satisfying the given predicate dropped from the beginning of the string.
573+
LLVM_ATTRIBUTE_ALWAYS_INLINE
574+
LLVM_ATTRIBUTE_UNUSED_RESULT
575+
StringRef drop_until(function_ref<bool(char)> F) const {
576+
return substr(find_if(F));
577+
}
578+
517579
/// Returns true if this StringRef has the given prefix and removes that
518580
/// prefix.
519581
LLVM_ATTRIBUTE_ALWAYS_INLINE

unittests/ADT/StringRefTest.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,4 +864,60 @@ TEST(StringRefTest, Take) {
864864
EXPECT_TRUE(Taken.empty());
865865
}
866866

867+
TEST(StringRefTest, FindIf) {
868+
StringRef Punct("Test.String");
869+
StringRef NoPunct("ABCDEFG");
870+
StringRef Empty;
871+
872+
auto IsPunct = [](char c) { return ::ispunct(c); };
873+
auto IsAlpha = [](char c) { return ::isalpha(c); };
874+
EXPECT_EQ(4, Punct.find_if(IsPunct));
875+
EXPECT_EQ(StringRef::npos, NoPunct.find_if(IsPunct));
876+
EXPECT_EQ(StringRef::npos, Empty.find_if(IsPunct));
877+
878+
EXPECT_EQ(4, Punct.find_if_not(IsAlpha));
879+
EXPECT_EQ(StringRef::npos, NoPunct.find_if_not(IsAlpha));
880+
EXPECT_EQ(StringRef::npos, Empty.find_if_not(IsAlpha));
881+
}
882+
883+
TEST(StringRefTest, TakeWhileUntil) {
884+
StringRef Test("String With 1 Number");
885+
886+
StringRef Taken = Test.take_while([](char c) { return ::isdigit(c); });
887+
EXPECT_EQ("", Taken);
888+
889+
Taken = Test.take_until([](char c) { return ::isdigit(c); });
890+
EXPECT_EQ("String With ", Taken);
891+
892+
Taken = Test.take_while([](char c) { return true; });
893+
EXPECT_EQ(Test, Taken);
894+
895+
Taken = Test.take_until([](char c) { return true; });
896+
EXPECT_EQ("", Taken);
897+
898+
Test = "";
899+
Taken = Test.take_while([](char c) { return true; });
900+
EXPECT_EQ("", Taken);
901+
}
902+
903+
TEST(StringRefTest, DropWhileUntil) {
904+
StringRef Test("String With 1 Number");
905+
906+
StringRef Taken = Test.drop_while([](char c) { return ::isdigit(c); });
907+
EXPECT_EQ(Test, Taken);
908+
909+
Taken = Test.drop_until([](char c) { return ::isdigit(c); });
910+
EXPECT_EQ("1 Number", Taken);
911+
912+
Taken = Test.drop_while([](char c) { return true; });
913+
EXPECT_EQ("", Taken);
914+
915+
Taken = Test.drop_until([](char c) { return true; });
916+
EXPECT_EQ(Test, Taken);
917+
918+
StringRef EmptyString = "";
919+
Taken = EmptyString.drop_while([](char c) { return true; });
920+
EXPECT_EQ("", Taken);
921+
}
922+
867923
} // end anonymous namespace

0 commit comments

Comments
 (0)