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 | // This file implements the StringSwitch template, which mimics a switch() |

9 | // statement whose cases are string literals. |

10 | // |

11 | //===----------------------------------------------------------------------===/ |

12 | #ifndef LLVM_ADT_STRINGSWITCH_H |

13 | #define LLVM_ADT_STRINGSWITCH_H |

14 | |

15 | #include "llvm/ADT/StringRef.h" |

16 | #include "llvm/Support/Compiler.h" |

17 | #include <cassert> |

18 | #include <cstring> |

19 | |

20 | namespace llvm { |

21 | |

22 | /// A switch()-like statement whose cases are string literals. |

23 | /// |

24 | /// The StringSwitch class is a simple form of a switch() statement that |

25 | /// determines whether the given string matches one of the given string |

26 | /// literals. The template type parameter \p T is the type of the value that |

27 | /// will be returned from the string-switch expression. For example, |

28 | /// the following code switches on the name of a color in \c argv[i]: |

29 | /// |

30 | /// \code |

31 | /// Color color = StringSwitch<Color>(argv[i]) |

32 | /// .Case("red", Red) |

33 | /// .Case("orange", Orange) |

34 | /// .Case("yellow", Yellow) |

35 | /// .Case("green", Green) |

36 | /// .Case("blue", Blue) |

37 | /// .Case("indigo", Indigo) |

38 | /// .Cases("violet", "purple", Violet) |

39 | /// .Default(UnknownColor); |

40 | /// \endcode |

41 | template<typename T, typename R = T> |

42 | class StringSwitch { |

43 | /// The string we are matching. |

44 | const StringRef Str; |

45 | |

46 | /// The pointer to the result of this switch statement, once known, |

47 | /// null before that. |

48 | Optional<T> Result; |

49 | |

50 | public: |

51 | explicit StringSwitch(StringRef S) |

52 | : Str(S), Result() { } |

53 | |

54 | // StringSwitch is not copyable. |

55 | StringSwitch(const StringSwitch &) = delete; |

56 | |

57 | // StringSwitch is not assignable due to 'Str' being 'const'. |

58 | void operator=(const StringSwitch &) = delete; |

59 | void operator=(StringSwitch &&other) = delete; |

60 | |

61 | StringSwitch(StringSwitch &&other) |

62 | : Str(other.Str), Result(std::move(other.Result)) { } |

63 | |

64 | ~StringSwitch() = default; |

65 | |

66 | // Case-sensitive case matchers |

67 | StringSwitch &Case(StringLiteral S, T Value) { |

68 | if (!Result && Str == S) { |

69 | Result = std::move(Value); |

70 | } |

71 | return *this; |

72 | } |

73 | |

74 | StringSwitch& EndsWith(StringLiteral S, T Value) { |

75 | if (!Result && Str.endswith(S)) { |

76 | Result = std::move(Value); |

77 | } |

78 | return *this; |

79 | } |

80 | |

81 | StringSwitch& StartsWith(StringLiteral S, T Value) { |

82 | if (!Result && Str.startswith(S)) { |

83 | Result = std::move(Value); |

84 | } |

85 | return *this; |

86 | } |

87 | |

88 | StringSwitch &Cases(StringLiteral S0, StringLiteral S1, T Value) { |

89 | return Case(S0, Value).Case(S1, Value); |

90 | } |

91 | |

92 | StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, |

93 | T Value) { |

94 | return Case(S0, Value).Cases(S1, S2, Value); |

95 | } |

96 | |

97 | StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, |

98 | StringLiteral S3, T Value) { |

99 | return Case(S0, Value).Cases(S1, S2, S3, Value); |

100 | } |

101 | |

102 | StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, |

103 | StringLiteral S3, StringLiteral S4, T Value) { |

104 | return Case(S0, Value).Cases(S1, S2, S3, S4, Value); |

105 | } |

106 | |

107 | StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, |

108 | StringLiteral S3, StringLiteral S4, StringLiteral S5, |

109 | T Value) { |

110 | return Case(S0, Value).Cases(S1, S2, S3, S4, S5, Value); |

111 | } |

112 | |

113 | StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, |

114 | StringLiteral S3, StringLiteral S4, StringLiteral S5, |

115 | StringLiteral S6, T Value) { |

116 | return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, Value); |

117 | } |

118 | |

119 | StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, |

120 | StringLiteral S3, StringLiteral S4, StringLiteral S5, |

121 | StringLiteral S6, StringLiteral S7, T Value) { |

122 | return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, S7, Value); |

123 | } |

124 | |

125 | StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, |

126 | StringLiteral S3, StringLiteral S4, StringLiteral S5, |

127 | StringLiteral S6, StringLiteral S7, StringLiteral S8, |

128 | T Value) { |

129 | return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, S7, S8, Value); |

130 | } |

131 | |

132 | StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, |

133 | StringLiteral S3, StringLiteral S4, StringLiteral S5, |

134 | StringLiteral S6, StringLiteral S7, StringLiteral S8, |

135 | StringLiteral S9, T Value) { |

136 | return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, S7, S8, S9, Value); |

137 | } |

138 | |

139 | // Case-insensitive case matchers. |

140 | StringSwitch &CaseLower(StringLiteral S, T Value) { |

141 | if (!Result && Str.equals_lower(S)) |

142 | Result = std::move(Value); |

143 | |

144 | return *this; |

145 | } |

146 | |

147 | StringSwitch &EndsWithLower(StringLiteral S, T Value) { |

148 | if (!Result && Str.endswith_lower(S)) |

149 | Result = Value; |

150 | |

151 | return *this; |

152 | } |

153 | |

154 | StringSwitch &StartsWithLower(StringLiteral S, T Value) { |

155 | if (!Result && Str.startswith_lower(S)) |

156 | Result = std::move(Value); |

157 | |

158 | return *this; |

159 | } |

160 | |

161 | StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, T Value) { |

162 | return CaseLower(S0, Value).CaseLower(S1, Value); |

163 | } |

164 | |

165 | StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2, |

166 | T Value) { |

167 | return CaseLower(S0, Value).CasesLower(S1, S2, Value); |

168 | } |

169 | |

170 | StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2, |

171 | StringLiteral S3, T Value) { |

172 | return CaseLower(S0, Value).CasesLower(S1, S2, S3, Value); |

173 | } |

174 | |

175 | StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2, |

176 | StringLiteral S3, StringLiteral S4, T Value) { |

177 | return CaseLower(S0, Value).CasesLower(S1, S2, S3, S4, Value); |

178 | } |

179 | |

180 | LLVM_NODISCARD |

181 | R Default(T Value) { |

182 | if (Result) |

183 | return std::move(*Result); |

184 | return Value; |

185 | } |

186 | |

187 | LLVM_NODISCARD |

188 | operator R() { |

189 | assert(Result && "Fell off the end of a string-switch"); |

190 | return std::move(*Result); |

191 | } |

192 | }; |

193 | |

194 | } // end namespace llvm |

195 | |

196 | #endif // LLVM_ADT_STRINGSWITCH_H |

197 |