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
21namespace 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
42template<typename T, typename R = T>
43class 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
51public:
52  LLVM_ATTRIBUTE_ALWAYS_INLINE
53  explicit StringSwitch(StringRef S)
54  : Str(S), Result(nullptr) { }
55
56  template<unsigned N>
57  LLVM_ATTRIBUTE_ALWAYS_INLINE
58  StringSwitch& Case(const char (&S)[N], const T& Value) {
59    if (!Result && N-1 == Str.size() &&
60        (std::memcmp(S, Str.data(), N-1) == 0)) {
61      Result = &Value;
62    }
63
64    return *this;
65  }
66
67  template<unsigned N>
68  LLVM_ATTRIBUTE_ALWAYS_INLINE
69  StringSwitch& EndsWith(const char (&S)[N], const T &Value) {
70    if (!Result && Str.size() >= N-1 &&
71        std::memcmp(S, Str.data() + Str.size() + 1 - N, N-1) == 0) {
72      Result = &Value;
73    }
74
75    return *this;
76  }
77
78  template<unsigned N>
79  LLVM_ATTRIBUTE_ALWAYS_INLINE
80  StringSwitch& StartsWith(const char (&S)[N], const T &Value) {
81    if (!Result && Str.size() >= N-1 &&
82        std::memcmp(S, Str.data(), N-1) == 0) {
83      Result = &Value;
84    }
85
86    return *this;
87  }
88
89  template<unsigned N0, unsigned N1>
90  LLVM_ATTRIBUTE_ALWAYS_INLINE
91  StringSwitch& Cases(const char (&S0)[N0], const char (&S1)[N1],
92                      const T& Value) {
93    if (!Result && (
94        (N0-1 == Str.size() && std::memcmp(S0, Str.data(), N0-1) == 0) ||
95        (N1-1 == Str.size() && std::memcmp(S1, Str.data(), N1-1) == 0))) {
96      Result = &Value;
97    }
98
99    return *this;
100  }
101
102  template<unsigned N0, unsigned N1, unsigned N2>
103  LLVM_ATTRIBUTE_ALWAYS_INLINE
104  StringSwitch& Cases(const char (&S0)[N0], const char (&S1)[N1],
105                      const char (&S2)[N2], const T& Value) {
106    if (!Result && (
107        (N0-1 == Str.size() && std::memcmp(S0, Str.data(), N0-1) == 0) ||
108        (N1-1 == Str.size() && std::memcmp(S1, Str.data(), N1-1) == 0) ||
109        (N2-1 == Str.size() && std::memcmp(S2, Str.data(), N2-1) == 0))) {
110      Result = &Value;
111    }
112
113    return *this;
114  }
115
116  template<unsigned N0, unsigned N1, unsigned N2, unsigned N3>
117  LLVM_ATTRIBUTE_ALWAYS_INLINE
118  StringSwitch& Cases(const char (&S0)[N0], const char (&S1)[N1],
119                      const char (&S2)[N2], const char (&S3)[N3],
120                      const T& Value) {
121    if (!Result && (
122        (N0-1 == Str.size() && std::memcmp(S0, Str.data(), N0-1) == 0) ||
123        (N1-1 == Str.size() && std::memcmp(S1, Str.data(), N1-1) == 0) ||
124        (N2-1 == Str.size() && std::memcmp(S2, Str.data(), N2-1) == 0) ||
125        (N3-1 == Str.size() && std::memcmp(S3, Str.data(), N3-1) == 0))) {
126      Result = &Value;
127    }
128
129    return *this;
130  }
131
132  template<unsigned N0, unsigned N1, unsigned N2, unsigned N3, unsigned N4>
133  LLVM_ATTRIBUTE_ALWAYS_INLINE
134  StringSwitch& Cases(const char (&S0)[N0], const char (&S1)[N1],
135                      const char (&S2)[N2], const char (&S3)[N3],
136                      const char (&S4)[N4], const T& Value) {
137    if (!Result && (
138        (N0-1 == Str.size() && std::memcmp(S0, Str.data(), N0-1) == 0) ||
139        (N1-1 == Str.size() && std::memcmp(S1, Str.data(), N1-1) == 0) ||
140        (N2-1 == Str.size() && std::memcmp(S2, Str.data(), N2-1) == 0) ||
141        (N3-1 == Str.size() && std::memcmp(S3, Str.data(), N3-1) == 0) ||
142        (N4-1 == Str.size() && std::memcmp(S4, Str.data(), N4-1) == 0))) {
143      Result = &Value;
144    }
145
146    return *this;
147  }
148
149  LLVM_ATTRIBUTE_ALWAYS_INLINE
150  R Default(const T& Value) const {
151    if (Result)
152      return *Result;
153
154    return Value;
155  }
156
157  LLVM_ATTRIBUTE_ALWAYS_INLINE
158  operator R() const {
159    assert(Result && "Fell off the end of a string-switch");
160    return *Result;
161  }
162};
163
164} // end namespace llvm
165
166#endif // LLVM_ADT_STRINGSWITCH_H
167