1 //===--- StringSwitch.h - Switch-on-literal-string Construct --------------===/
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //===----------------------------------------------------------------------===/
8 //
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 "llvm/Support/Compiler.h"
18 #include <cassert>
19 #include <cstring>
20 
21 namespace llvm {
22 
23 /// \brief 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   /// \brief The string we are matching.
45   StringRef Str;
46 
47   /// \brief The pointer to the result of this switch statement, once known,
48   /// null before that.
49   const T *Result;
50 
51 public:
52   LLVM_ATTRIBUTE_ALWAYS_INLINE
StringSwitch(StringRef S)53   explicit StringSwitch(StringRef S)
54   : Str(S), Result(nullptr) { }
55 
56   // StringSwitch is not copyable.
57   StringSwitch(const StringSwitch &) = delete;
58   void operator=(const StringSwitch &) = delete;
59 
StringSwitch(StringSwitch && other)60   StringSwitch(StringSwitch &&other) {
61     *this = std::move(other);
62   }
63   StringSwitch &operator=(StringSwitch &&other) {
64     Str = other.Str;
65     Result = other.Result;
66     return *this;
67   }
68 
69   ~StringSwitch() = default;
70 
71   // Case-sensitive case matchers
72   template<unsigned N>
73   LLVM_ATTRIBUTE_ALWAYS_INLINE
Case(const char (& S)[N],const T & Value)74   StringSwitch& Case(const char (&S)[N], const T& Value) {
75     assert(N);
76     if (!Result && N-1 == Str.size() &&
77         (N == 1 || std::memcmp(S, Str.data(), N-1) == 0)) {
78       Result = &Value;
79     }
80     return *this;
81   }
82 
83   template<unsigned N>
84   LLVM_ATTRIBUTE_ALWAYS_INLINE
EndsWith(const char (& S)[N],const T & Value)85   StringSwitch& EndsWith(const char (&S)[N], const T &Value) {
86     assert(N);
87     if (!Result && Str.size() >= N-1 &&
88         (N == 1 || std::memcmp(S, Str.data() + Str.size() + 1 - N, N-1) == 0)) {
89       Result = &Value;
90     }
91     return *this;
92   }
93 
94   template<unsigned N>
95   LLVM_ATTRIBUTE_ALWAYS_INLINE
StartsWith(const char (& S)[N],const T & Value)96   StringSwitch& StartsWith(const char (&S)[N], const T &Value) {
97     assert(N);
98     if (!Result && Str.size() >= N-1 &&
99         (N == 1 || std::memcmp(S, Str.data(), N-1) == 0)) {
100       Result = &Value;
101     }
102     return *this;
103   }
104 
105   template<unsigned N0, unsigned N1>
106   LLVM_ATTRIBUTE_ALWAYS_INLINE
Cases(const char (& S0)[N0],const char (& S1)[N1],const T & Value)107   StringSwitch &Cases(const char (&S0)[N0], const char (&S1)[N1],
108                       const T& Value) {
109     return Case(S0, Value).Case(S1, Value);
110   }
111 
112   template<unsigned N0, unsigned N1, unsigned N2>
113   LLVM_ATTRIBUTE_ALWAYS_INLINE
Cases(const char (& S0)[N0],const char (& S1)[N1],const char (& S2)[N2],const T & Value)114   StringSwitch &Cases(const char (&S0)[N0], const char (&S1)[N1],
115                       const char (&S2)[N2], const T& Value) {
116     return Case(S0, Value).Cases(S1, S2, Value);
117   }
118 
119   template<unsigned N0, unsigned N1, unsigned N2, unsigned N3>
120   LLVM_ATTRIBUTE_ALWAYS_INLINE
Cases(const char (& S0)[N0],const char (& S1)[N1],const char (& S2)[N2],const char (& S3)[N3],const T & Value)121   StringSwitch &Cases(const char (&S0)[N0], const char (&S1)[N1],
122                       const char (&S2)[N2], const char (&S3)[N3],
123                       const T& Value) {
124     return Case(S0, Value).Cases(S1, S2, S3, Value);
125   }
126 
127   template<unsigned N0, unsigned N1, unsigned N2, unsigned N3, unsigned N4>
128   LLVM_ATTRIBUTE_ALWAYS_INLINE
Cases(const char (& S0)[N0],const char (& S1)[N1],const char (& S2)[N2],const char (& S3)[N3],const char (& S4)[N4],const T & Value)129   StringSwitch &Cases(const char (&S0)[N0], const char (&S1)[N1],
130                       const char (&S2)[N2], const char (&S3)[N3],
131                       const char (&S4)[N4], const T& Value) {
132     return Case(S0, Value).Cases(S1, S2, S3, S4, Value);
133   }
134 
135   template <unsigned N0, unsigned N1, unsigned N2, unsigned N3, unsigned N4,
136             unsigned N5>
137   LLVM_ATTRIBUTE_ALWAYS_INLINE
Cases(const char (& S0)[N0],const char (& S1)[N1],const char (& S2)[N2],const char (& S3)[N3],const char (& S4)[N4],const char (& S5)[N5],const T & Value)138   StringSwitch &Cases(const char (&S0)[N0], const char (&S1)[N1],
139                       const char (&S2)[N2], const char (&S3)[N3],
140                       const char (&S4)[N4], const char (&S5)[N5],
141                       const T &Value) {
142     return Case(S0, Value).Cases(S1, S2, S3, S4, S5, Value);
143   }
144 
145   template <unsigned N0, unsigned N1, unsigned N2, unsigned N3, unsigned N4,
146             unsigned N5, unsigned N6>
147   LLVM_ATTRIBUTE_ALWAYS_INLINE
Cases(const char (& S0)[N0],const char (& S1)[N1],const char (& S2)[N2],const char (& S3)[N3],const char (& S4)[N4],const char (& S5)[N5],const char (& S6)[N6],const T & Value)148   StringSwitch &Cases(const char (&S0)[N0], const char (&S1)[N1],
149                       const char (&S2)[N2], const char (&S3)[N3],
150                       const char (&S4)[N4], const char (&S5)[N5],
151                       const char (&S6)[N6], const T &Value) {
152     return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, Value);
153   }
154 
155   template <unsigned N0, unsigned N1, unsigned N2, unsigned N3, unsigned N4,
156             unsigned N5, unsigned N6, unsigned N7>
157   LLVM_ATTRIBUTE_ALWAYS_INLINE
Cases(const char (& S0)[N0],const char (& S1)[N1],const char (& S2)[N2],const char (& S3)[N3],const char (& S4)[N4],const char (& S5)[N5],const char (& S6)[N6],const char (& S7)[N7],const T & Value)158   StringSwitch &Cases(const char (&S0)[N0], const char (&S1)[N1],
159                       const char (&S2)[N2], const char (&S3)[N3],
160                       const char (&S4)[N4], const char (&S5)[N5],
161                       const char (&S6)[N6], const char (&S7)[N7],
162                       const T &Value) {
163     return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, S7, Value);
164   }
165 
166   template <unsigned N0, unsigned N1, unsigned N2, unsigned N3, unsigned N4,
167             unsigned N5, unsigned N6, unsigned N7, unsigned N8>
168   LLVM_ATTRIBUTE_ALWAYS_INLINE
Cases(const char (& S0)[N0],const char (& S1)[N1],const char (& S2)[N2],const char (& S3)[N3],const char (& S4)[N4],const char (& S5)[N5],const char (& S6)[N6],const char (& S7)[N7],const char (& S8)[N8],const T & Value)169   StringSwitch &Cases(const char (&S0)[N0], const char (&S1)[N1],
170                       const char (&S2)[N2], const char (&S3)[N3],
171                       const char (&S4)[N4], const char (&S5)[N5],
172                       const char (&S6)[N6], const char (&S7)[N7],
173                       const char (&S8)[N8], const T &Value) {
174     return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, S7, S8, Value);
175   }
176 
177   template <unsigned N0, unsigned N1, unsigned N2, unsigned N3, unsigned N4,
178             unsigned N5, unsigned N6, unsigned N7, unsigned N8, unsigned N9>
179   LLVM_ATTRIBUTE_ALWAYS_INLINE
Cases(const char (& S0)[N0],const char (& S1)[N1],const char (& S2)[N2],const char (& S3)[N3],const char (& S4)[N4],const char (& S5)[N5],const char (& S6)[N6],const char (& S7)[N7],const char (& S8)[N8],const char (& S9)[N9],const T & Value)180   StringSwitch &Cases(const char (&S0)[N0], const char (&S1)[N1],
181                       const char (&S2)[N2], const char (&S3)[N3],
182                       const char (&S4)[N4], const char (&S5)[N5],
183                       const char (&S6)[N6], const char (&S7)[N7],
184                       const char (&S8)[N8], const char (&S9)[N9],
185                       const T &Value) {
186     return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, S7, S8, S9, Value);
187   }
188 
189   // Case-insensitive case matchers.
190   template <unsigned N>
CaseLower(const char (& S)[N],const T & Value)191   LLVM_ATTRIBUTE_ALWAYS_INLINE StringSwitch &CaseLower(const char (&S)[N],
192                                                        const T &Value) {
193     if (!Result && Str.equals_lower(StringRef(S, N - 1)))
194       Result = &Value;
195 
196     return *this;
197   }
198 
199   template <unsigned N>
EndsWithLower(const char (& S)[N],const T & Value)200   LLVM_ATTRIBUTE_ALWAYS_INLINE StringSwitch &EndsWithLower(const char (&S)[N],
201                                                            const T &Value) {
202     if (!Result && Str.endswith_lower(StringRef(S, N - 1)))
203       Result = &Value;
204 
205     return *this;
206   }
207 
208   template <unsigned N>
StartsWithLower(const char (& S)[N],const T & Value)209   LLVM_ATTRIBUTE_ALWAYS_INLINE StringSwitch &StartsWithLower(const char (&S)[N],
210                                                              const T &Value) {
211     if (!Result && Str.startswith_lower(StringRef(S, N - 1)))
212       Result = &Value;
213 
214     return *this;
215   }
216   template <unsigned N0, unsigned N1>
217   LLVM_ATTRIBUTE_ALWAYS_INLINE StringSwitch &
CasesLower(const char (& S0)[N0],const char (& S1)[N1],const T & Value)218   CasesLower(const char (&S0)[N0], const char (&S1)[N1], const T &Value) {
219     return CaseLower(S0, Value).CaseLower(S1, Value);
220   }
221 
222   template <unsigned N0, unsigned N1, unsigned N2>
223   LLVM_ATTRIBUTE_ALWAYS_INLINE StringSwitch &
CasesLower(const char (& S0)[N0],const char (& S1)[N1],const char (& S2)[N2],const T & Value)224   CasesLower(const char (&S0)[N0], const char (&S1)[N1], const char (&S2)[N2],
225              const T &Value) {
226     return CaseLower(S0, Value).CasesLower(S1, S2, Value);
227   }
228 
229   template <unsigned N0, unsigned N1, unsigned N2, unsigned N3>
230   LLVM_ATTRIBUTE_ALWAYS_INLINE StringSwitch &
CasesLower(const char (& S0)[N0],const char (& S1)[N1],const char (& S2)[N2],const char (& S3)[N3],const T & Value)231   CasesLower(const char (&S0)[N0], const char (&S1)[N1], const char (&S2)[N2],
232              const char (&S3)[N3], const T &Value) {
233     return CaseLower(S0, Value).CasesLower(S1, S2, S3, Value);
234   }
235 
236   template <unsigned N0, unsigned N1, unsigned N2, unsigned N3, unsigned N4>
237   LLVM_ATTRIBUTE_ALWAYS_INLINE StringSwitch &
CasesLower(const char (& S0)[N0],const char (& S1)[N1],const char (& S2)[N2],const char (& S3)[N3],const char (& S4)[N4],const T & Value)238   CasesLower(const char (&S0)[N0], const char (&S1)[N1], const char (&S2)[N2],
239              const char (&S3)[N3], const char (&S4)[N4], const T &Value) {
240     return CaseLower(S0, Value).CasesLower(S1, S2, S3, S4, Value);
241   }
242 
243   LLVM_ATTRIBUTE_ALWAYS_INLINE
Default(const T & Value)244   R Default(const T &Value) const {
245     if (Result)
246       return *Result;
247     return Value;
248   }
249 
250   LLVM_ATTRIBUTE_ALWAYS_INLINE
R()251   operator R() const {
252     assert(Result && "Fell off the end of a string-switch");
253     return *Result;
254   }
255 };
256 
257 } // end namespace llvm
258 
259 #endif // LLVM_ADT_STRINGSWITCH_H
260