[22] | 1 | #include <boost/regex.hpp> |
---|
| 2 | #include <t3devlib/t3devlib.h> |
---|
| 3 | |
---|
| 4 | namespace t3devlib { namespace gen { |
---|
| 5 | |
---|
| 6 | /** Implementation of perl-like regular expressions to be used in a decoder |
---|
| 7 | * generated by T3DevKit. |
---|
| 8 | * |
---|
| 9 | * This class handles the regex compilation and execution. The result of |
---|
| 10 | * group matching is stored internally. |
---|
| 11 | * |
---|
| 12 | * It is designed to be used seamlessly in codets for T3DevKit, thus: |
---|
| 13 | * - it can interact with t3devlib data (Buffer, Variable, DecodeError) |
---|
| 14 | * - it reports errors using T3DevKit's exceptions |
---|
| 15 | * - it returns the length of the matches in bits |
---|
| 16 | * |
---|
| 17 | * |
---|
| 18 | * The regular expression is compiled at the construction of the object. Then |
---|
| 19 | * it can be used multiple times to search the regex in the buffer (with |
---|
| 20 | * Match() or AssertMatch()). |
---|
| 21 | * |
---|
| 22 | * After a regex search, it is possible to inspect the result at to take |
---|
| 23 | * actions with other member functions (GetMatchedLength(), MoveAt(), ...) |
---|
| 24 | * |
---|
| 25 | * \warning Apart from moving the cursor, the buffer MUST NOT be modified |
---|
| 26 | * between the call of Match() or AssertMatch() and the use of |
---|
| 27 | * inspection/action member functions (GetMatchedLength(), |
---|
| 28 | * MoveAt() ...) |
---|
| 29 | */ |
---|
| 30 | class Regex { |
---|
| 31 | public: |
---|
| 32 | typedef std::string::const_iterator iterator; |
---|
| 33 | |
---|
| 34 | /** Constructor |
---|
| 35 | * |
---|
| 36 | * This constructor compiles the regular expression given as a |
---|
| 37 | * parameter. It is based on the format of perl regular expressions. |
---|
| 38 | * |
---|
| 39 | * \warning it is assumed that regex is a static const string |
---|
| 40 | */ |
---|
| 41 | Regex(const char* regex) |
---|
| 42 | : mSource (regex), mRegex (regex, boost::regex_constants::perl | boost::regex_constants::no_mod_m) |
---|
| 43 | {} |
---|
| 44 | |
---|
| 45 | /** Try to match the regular expression in the buffer. |
---|
| 46 | * |
---|
| 47 | * The regular expression is searched in the buffer between the |
---|
| 48 | * position of its cursor and the first end marker set (thus between |
---|
| 49 | * the positions given by Buffer::GetPosition() and |
---|
| 50 | * Buffer::GetEndMarker()) |
---|
| 51 | * |
---|
| 52 | * \param buffer buffer on which the regex shall be searched |
---|
| 53 | * |
---|
| 54 | * \return true in case of success |
---|
| 55 | */ |
---|
| 56 | bool Match (Buffer& buffer) { |
---|
| 57 | mStart = iterator (reinterpret_cast<const char*> (buffer.GetValueBin()) + (buffer.GetPosition() / 8)); |
---|
| 58 | mStop = iterator (reinterpret_cast<const char*> (buffer.GetValueBin()) + (buffer.GetEndMarker() / 8)); |
---|
| 59 | |
---|
| 60 | int result = boost::regex_search (mStart, mStop, mResults, mRegex); |
---|
| 61 | |
---|
| 62 | return result; |
---|
| 63 | } |
---|
| 64 | |
---|
| 65 | /** Match the regular expression in the buffer or throw an exeption |
---|
| 66 | * if not successful. |
---|
| 67 | * |
---|
| 68 | * \param buffer buffer on which the regex shall be searched |
---|
| 69 | * \param v reference of the value being decoded; it is |
---|
| 70 | * used to allow locating the error in case an |
---|
| 71 | * exception is thrown |
---|
| 72 | */ |
---|
| 73 | void AssertMatch (Buffer& buffer, Variable* v) throw (DecodeError) { |
---|
| 74 | if (!Match (buffer)) { |
---|
| 75 | Error (v, buffer); |
---|
| 76 | } |
---|
| 77 | } |
---|
| 78 | |
---|
| 79 | /** Get the length of a group match |
---|
| 80 | * |
---|
| 81 | * \param id index of the '(...)' group to analyse (or 0 |
---|
| 82 | * to refer to the whole regular expression) |
---|
| 83 | * |
---|
| 84 | * \return the length of the group in bits |
---|
| 85 | */ |
---|
| 86 | int GetMatchedLength(int id = 0) { |
---|
| 87 | return mResults[id].length()*8; |
---|
| 88 | } |
---|
| 89 | |
---|
| 90 | /** Get the content of a group match |
---|
| 91 | * |
---|
| 92 | * \param id index of the '(...)' group to analyse (or 0 |
---|
| 93 | * to refer to the whole regular expression) |
---|
| 94 | * |
---|
| 95 | * \return the string matched by the group |
---|
| 96 | */ |
---|
| 97 | std::string GetMatchedString(int id = 0) { |
---|
| 98 | return std::string (mResults[id].first, mResults[id].second); |
---|
| 99 | } |
---|
| 100 | |
---|
| 101 | /** Move the cursor of the buffer past a group match |
---|
| 102 | * |
---|
| 103 | * \param id index of the '(...)' group to analyse (or 0 |
---|
| 104 | * to refer to the whole regular expression) |
---|
| 105 | */ |
---|
| 106 | void MovePast (Buffer& buffer, int id = 0) |
---|
| 107 | { |
---|
| 108 | buffer.SetPosition ((&*mResults[id].second - reinterpret_cast<const char*> (buffer.GetValueBin())) * 8); |
---|
| 109 | } |
---|
| 110 | |
---|
| 111 | /** Move the cursor of the buffer at the beginning of group match |
---|
| 112 | * |
---|
| 113 | * \param id index of the '(...)' group to analyse (or 0 |
---|
| 114 | * to refer to the whole regular expression) |
---|
| 115 | */ |
---|
| 116 | void MoveAt (Buffer& buffer, int id = 0) |
---|
| 117 | { |
---|
| 118 | buffer.SetPosition ((&*mResults[id].first - reinterpret_cast<const char*> (buffer.GetValueBin())) * 8); |
---|
| 119 | } |
---|
| 120 | |
---|
| 121 | /** Throw a decoding error |
---|
| 122 | * |
---|
| 123 | * This function will throw an exception telling that the codec |
---|
| 124 | * could not match the regular expression. The error message will |
---|
| 125 | * also contains details about the regular expression and the |
---|
| 126 | * content of the buffer at the position where the exception could |
---|
| 127 | * not be matched. |
---|
| 128 | * |
---|
| 129 | * \param v reference to the value being decoded |
---|
| 130 | * (used to locate the position of the error) |
---|
| 131 | * \param buffer buffer on which the regex could no be matched |
---|
| 132 | */ |
---|
| 133 | void Error (Variable* v, Buffer& buffer) throw (DecodeError) { |
---|
| 134 | |
---|
| 135 | std::string message ("cannot match regex /"); |
---|
| 136 | message += mSource; |
---|
| 137 | message += "/ in '"; |
---|
| 138 | if ((mStop - mStart) > 40) { |
---|
| 139 | message.append (&*mStart, 40); |
---|
| 140 | message += "..."; |
---|
| 141 | } else { |
---|
| 142 | message.append (mStart, mStop); |
---|
| 143 | } |
---|
| 144 | message += "'\n"; |
---|
| 145 | throw DecodeError (v, message); |
---|
| 146 | } |
---|
| 147 | |
---|
| 148 | private: |
---|
| 149 | /** source of the regular expression (should be a static string) */ |
---|
| 150 | const char* mSource; |
---|
| 151 | |
---|
| 152 | /** boost regex object used to implement the regular expression */ |
---|
| 153 | boost::regex mRegex; |
---|
| 154 | |
---|
| 155 | /** iterators delimiting the positions in the buffer where the regex |
---|
| 156 | * is applied (between GetPosition() and GetEndMarker) |
---|
| 157 | */ |
---|
| 158 | iterator mStart, mStop; |
---|
| 159 | |
---|
| 160 | /** boost match result used to store the results of the regex search */ |
---|
| 161 | boost::match_results<iterator> mResults; |
---|
| 162 | }; |
---|
| 163 | |
---|
| 164 | |
---|
| 165 | }} //namespaces |
---|