1 //===--- StringSwitch.h - Switch-on-literal-string Construct --------------===/ 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 //===----------------------------------------------------------------------===/ 7 /// 8 /// \file 9 /// This file implements the StringSwitch template, which mimics a switch() 10 /// statement whose cases are string literals. 11 /// 12 //===----------------------------------------------------------------------===/ 13 #ifndef LLVM_ADT_STRINGSWITCH_H 14 #define LLVM_ADT_STRINGSWITCH_H 15 16 #include "llvm/ADT/StringRef.h" 17 #include <cassert> 18 #include <cstring> 19 #include <optional> 20 21 namespace llvm { 22 23 /// A switch()-like statement whose cases are string literals. 24 /// 25 /// The StringSwitch class is a simple form of a switch() statement that 26 /// determines whether the given string matches one of the given string 27 /// literals. The template type parameter \p T is the type of the value that 28 /// will be returned from the string-switch expression. For example, 29 /// the following code switches on the name of a color in \c argv[i]: 30 /// 31 /// \code 32 /// Color color = StringSwitch<Color>(argv[i]) 33 /// .Case("red", Red) 34 /// .Case("orange", Orange) 35 /// .Case("yellow", Yellow) 36 /// .Case("green", Green) 37 /// .Case("blue", Blue) 38 /// .Case("indigo", Indigo) 39 /// .Cases("violet", "purple", Violet) 40 /// .Default(UnknownColor); 41 /// \endcode 42 template<typename T, typename R = T> 43 class StringSwitch { 44 /// The string we are matching. 45 const StringRef Str; 46 47 /// The pointer to the result of this switch statement, once known, 48 /// null before that. 49 std::optional<T> Result; 50 51 public: StringSwitch(StringRef S)52 explicit StringSwitch(StringRef S) 53 : Str(S), Result() { } 54 55 // StringSwitch is not copyable. 56 StringSwitch(const StringSwitch &) = delete; 57 58 // StringSwitch is not assignable due to 'Str' being 'const'. 59 void operator=(const StringSwitch &) = delete; 60 void operator=(StringSwitch &&other) = delete; 61 StringSwitch(StringSwitch && other)62 StringSwitch(StringSwitch &&other) 63 : Str(other.Str), Result(std::move(other.Result)) { } 64 65 ~StringSwitch() = default; 66 67 // Case-sensitive case matchers Case(StringLiteral S,T Value)68 StringSwitch &Case(StringLiteral S, T Value) { 69 CaseImpl(Value, S); 70 return *this; 71 } 72 EndsWith(StringLiteral S,T Value)73 StringSwitch& EndsWith(StringLiteral S, T Value) { 74 if (!Result && Str.ends_with(S)) { 75 Result = std::move(Value); 76 } 77 return *this; 78 } 79 StartsWith(StringLiteral S,T Value)80 StringSwitch& StartsWith(StringLiteral S, T Value) { 81 if (!Result && Str.starts_with(S)) { 82 Result = std::move(Value); 83 } 84 return *this; 85 } 86 Cases(StringLiteral S0,StringLiteral S1,T Value)87 StringSwitch &Cases(StringLiteral S0, StringLiteral S1, T Value) { 88 return CasesImpl(Value, S0, S1); 89 } 90 Cases(StringLiteral S0,StringLiteral S1,StringLiteral S2,T Value)91 StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, 92 T Value) { 93 return CasesImpl(Value, S0, S1, S2); 94 } 95 Cases(StringLiteral S0,StringLiteral S1,StringLiteral S2,StringLiteral S3,T Value)96 StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, 97 StringLiteral S3, T Value) { 98 return CasesImpl(Value, S0, S1, S2, S3); 99 } 100 Cases(StringLiteral S0,StringLiteral S1,StringLiteral S2,StringLiteral S3,StringLiteral S4,T Value)101 StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, 102 StringLiteral S3, StringLiteral S4, T Value) { 103 return CasesImpl(Value, S0, S1, S2, S3, S4); 104 } 105 Cases(StringLiteral S0,StringLiteral S1,StringLiteral S2,StringLiteral S3,StringLiteral S4,StringLiteral S5,T Value)106 StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, 107 StringLiteral S3, StringLiteral S4, StringLiteral S5, 108 T Value) { 109 return CasesImpl(Value, S0, S1, S2, S3, S4, S5); 110 } 111 Cases(StringLiteral S0,StringLiteral S1,StringLiteral S2,StringLiteral S3,StringLiteral S4,StringLiteral S5,StringLiteral S6,T Value)112 StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, 113 StringLiteral S3, StringLiteral S4, StringLiteral S5, 114 StringLiteral S6, T Value) { 115 return CasesImpl(Value, S0, S1, S2, S3, S4, S5, S6); 116 } 117 Cases(StringLiteral S0,StringLiteral S1,StringLiteral S2,StringLiteral S3,StringLiteral S4,StringLiteral S5,StringLiteral S6,StringLiteral S7,T Value)118 StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, 119 StringLiteral S3, StringLiteral S4, StringLiteral S5, 120 StringLiteral S6, StringLiteral S7, T Value) { 121 return CasesImpl(Value, S0, S1, S2, S3, S4, S5, S6, S7); 122 } 123 Cases(StringLiteral S0,StringLiteral S1,StringLiteral S2,StringLiteral S3,StringLiteral S4,StringLiteral S5,StringLiteral S6,StringLiteral S7,StringLiteral S8,T Value)124 StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, 125 StringLiteral S3, StringLiteral S4, StringLiteral S5, 126 StringLiteral S6, StringLiteral S7, StringLiteral S8, 127 T Value) { 128 return CasesImpl(Value, S0, S1, S2, S3, S4, S5, S6, S7, S8); 129 } 130 Cases(StringLiteral S0,StringLiteral S1,StringLiteral S2,StringLiteral S3,StringLiteral S4,StringLiteral S5,StringLiteral S6,StringLiteral S7,StringLiteral S8,StringLiteral S9,T Value)131 StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, 132 StringLiteral S3, StringLiteral S4, StringLiteral S5, 133 StringLiteral S6, StringLiteral S7, StringLiteral S8, 134 StringLiteral S9, T Value) { 135 return CasesImpl(Value, S0, S1, S2, S3, S4, S5, S6, S7, S8, S9); 136 } 137 138 // Case-insensitive case matchers. CaseLower(StringLiteral S,T Value)139 StringSwitch &CaseLower(StringLiteral S, T Value) { 140 CaseLowerImpl(Value, S); 141 return *this; 142 } 143 EndsWithLower(StringLiteral S,T Value)144 StringSwitch &EndsWithLower(StringLiteral S, T Value) { 145 if (!Result && Str.ends_with_insensitive(S)) 146 Result = Value; 147 148 return *this; 149 } 150 StartsWithLower(StringLiteral S,T Value)151 StringSwitch &StartsWithLower(StringLiteral S, T Value) { 152 if (!Result && Str.starts_with_insensitive(S)) 153 Result = std::move(Value); 154 155 return *this; 156 } 157 CasesLower(StringLiteral S0,StringLiteral S1,T Value)158 StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, T Value) { 159 return CasesLowerImpl(Value, S0, S1); 160 } 161 CasesLower(StringLiteral S0,StringLiteral S1,StringLiteral S2,T Value)162 StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2, 163 T Value) { 164 return CasesLowerImpl(Value, S0, S1, S2); 165 } 166 CasesLower(StringLiteral S0,StringLiteral S1,StringLiteral S2,StringLiteral S3,T Value)167 StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2, 168 StringLiteral S3, T Value) { 169 return CasesLowerImpl(Value, S0, S1, S2, S3); 170 } 171 CasesLower(StringLiteral S0,StringLiteral S1,StringLiteral S2,StringLiteral S3,StringLiteral S4,T Value)172 StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2, 173 StringLiteral S3, StringLiteral S4, T Value) { 174 return CasesLowerImpl(Value, S0, S1, S2, S3, S4); 175 } 176 Default(T Value)177 [[nodiscard]] R Default(T Value) { 178 if (Result) 179 return std::move(*Result); 180 return Value; 181 } 182 R()183 [[nodiscard]] operator R() { 184 assert(Result && "Fell off the end of a string-switch"); 185 return std::move(*Result); 186 } 187 188 private: 189 // Returns true when `Str` matches the `S` argument, and stores the result. CaseImpl(T & Value,StringLiteral S)190 bool CaseImpl(T &Value, StringLiteral S) { 191 if (!Result && Str == S) { 192 Result = std::move(Value); 193 return true; 194 } 195 return false; 196 } 197 198 // Returns true when `Str` matches the `S` argument (case-insensitive), and 199 // stores the result. CaseLowerImpl(T & Value,StringLiteral S)200 bool CaseLowerImpl(T &Value, StringLiteral S) { 201 if (!Result && Str.equals_insensitive(S)) { 202 Result = std::move(Value); 203 return true; 204 } 205 return false; 206 } 207 CasesImpl(T & Value,Args...Cases)208 template <typename... Args> StringSwitch &CasesImpl(T &Value, Args... Cases) { 209 // Stop matching after the string is found. 210 (... || CaseImpl(Value, Cases)); 211 return *this; 212 } 213 214 template <typename... Args> CasesLowerImpl(T & Value,Args...Cases)215 StringSwitch &CasesLowerImpl(T &Value, Args... Cases) { 216 // Stop matching after the string is found. 217 (... || CaseLowerImpl(Value, Cases)); 218 return *this; 219 } 220 }; 221 222 } // end namespace llvm 223 224 #endif // LLVM_ADT_STRINGSWITCH_H 225