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 |
---|