Home | History | Annotate | Line # | Download | only in ADT
      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