]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com) |
2 | // (C) Copyright 2005-2007 Jonathan Turkanis | |
3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.) | |
5 | ||
6 | // See http://www.boost.org/libs/iostreams for documentation. | |
7 | ||
8 | // Adapted from an example of James Kanze, with suggestions from Peter Dimov. | |
92f5a8d4 | 9 | // See https://web.archive.org/web/20041222094942/http://www.gabi-soft.fr/codebase-en.html. |
7c673cae FG |
10 | |
11 | #ifndef BOOST_IOSTREAMS_FINITE_STATE_FILTER_HPP_INCLUDED | |
12 | #define BOOST_IOSTREAMS_FINITE_STATE_FILTER_HPP_INCLUDED | |
13 | ||
14 | #include <cassert> | |
15 | #include <cstdio> // EOF. | |
16 | #include <iostream> // cin, cout. | |
17 | #include <locale> | |
18 | #include <string> | |
19 | #include <boost/config.hpp> // JOIN, member template friends. | |
20 | #include <boost/detail/workaround.hpp> | |
21 | #include <boost/iostreams/categories.hpp> | |
22 | #include <boost/iostreams/char_traits.hpp> | |
23 | #include <boost/iostreams/checked_operations.hpp> // put_if. | |
24 | #include <boost/iostreams/concepts.hpp> | |
25 | #include <boost/iostreams/detail/ios.hpp> // openmode. | |
26 | #include <boost/iostreams/filter/stdio.hpp> | |
27 | #include <boost/iostreams/operations.hpp> | |
28 | #include <boost/mpl/begin_end.hpp> | |
29 | #include <boost/mpl/deref.hpp> | |
30 | #include <boost/preprocessor/control/expr_if.hpp> | |
31 | #include <boost/static_assert.hpp> | |
32 | #include <boost/type_traits/is_base_and_derived.hpp> | |
33 | ||
34 | namespace boost { namespace iostreams { | |
35 | ||
36 | //------------------Definition of basic character classes---------------------// | |
37 | ||
38 | struct finite_state_machine_base { | |
39 | ||
40 | static const int initial_state = 0; | |
41 | ||
42 | // All-inclusive character class. | |
43 | ||
44 | struct is_any { | |
45 | template<typename Ch> | |
46 | static bool test(Ch, const std::locale&) { return true; } | |
47 | }; | |
48 | ||
49 | // Locale-sensitive character classes. | |
50 | ||
51 | #define BOOST_IOSTREAMS_CHARACTER_CLASS(class) \ | |
52 | struct BOOST_JOIN(is_, class) { \ | |
53 | template<typename Ch> \ | |
54 | static bool test(Ch event, const std::locale& loc) \ | |
55 | { return std::BOOST_JOIN(is, class)(event, loc); } \ | |
56 | }; \ | |
57 | /**/ | |
58 | ||
59 | BOOST_IOSTREAMS_CHARACTER_CLASS(alnum) | |
60 | BOOST_IOSTREAMS_CHARACTER_CLASS(alpha) | |
61 | BOOST_IOSTREAMS_CHARACTER_CLASS(cntrl) | |
62 | BOOST_IOSTREAMS_CHARACTER_CLASS(digit) | |
63 | BOOST_IOSTREAMS_CHARACTER_CLASS(graph) | |
64 | BOOST_IOSTREAMS_CHARACTER_CLASS(lower) | |
65 | BOOST_IOSTREAMS_CHARACTER_CLASS(print) | |
66 | BOOST_IOSTREAMS_CHARACTER_CLASS(punct) | |
67 | BOOST_IOSTREAMS_CHARACTER_CLASS(space) | |
68 | BOOST_IOSTREAMS_CHARACTER_CLASS(upper) | |
69 | BOOST_IOSTREAMS_CHARACTER_CLASS(xdigit) | |
70 | ||
71 | #undef BOOST_IOSTREAMS_CHARACTER_CLASS | |
72 | }; | |
73 | ||
74 | template<typename Ch> | |
75 | struct finite_state_machine_base_ex : finite_state_machine_base { | |
76 | template<Ch C> | |
77 | struct is { | |
78 | static bool test(Ch event, const std::locale&) | |
79 | { | |
80 | return event == C; | |
81 | } | |
82 | }; | |
83 | }; | |
84 | ||
85 | //------------------Definition of base class for finite state filters---------// | |
86 | ||
87 | namespace detail { | |
88 | ||
89 | template<typename FiniteStateMachine> | |
90 | class finite_state_filter_impl; | |
91 | ||
92 | } // End namespace detail. | |
93 | ||
94 | template<typename Derived, typename Ch = char> | |
95 | class finite_state_machine : public finite_state_machine_base_ex<Ch> { | |
96 | public: | |
97 | typedef Ch char_type; | |
98 | typedef typename char_traits<Ch>::int_type int_type; | |
99 | void imbue(const std::locale& loc) { loc_ = loc; } | |
100 | const std::locale& getloc() const { return loc_; } | |
101 | protected: | |
102 | finite_state_machine() : off_(0) { } | |
103 | ||
104 | // Template whose instantiations make up transition table. | |
105 | ||
106 | template< int State, | |
107 | typename CharacterClass, | |
108 | int NextState, | |
109 | void (Derived::*Action)(char_type) > | |
110 | struct row { | |
111 | typedef CharacterClass character_class; | |
112 | static const int state = State; | |
113 | static const int next_state = NextState; | |
114 | static void execute(Derived& d, char_type event) | |
115 | { | |
116 | (d.*Action)(event); | |
117 | } | |
118 | }; | |
119 | ||
120 | // Stack interface. | |
121 | ||
122 | bool empty() const | |
123 | { | |
124 | return off_ == buf_.size(); | |
125 | } | |
126 | void push(char c) { buf_ += c; } | |
127 | char_type pop() | |
128 | { | |
129 | char_type result = buf_[off_++]; | |
130 | if (off_ == buf_.size()) | |
131 | clear(); | |
132 | return result; | |
133 | } | |
134 | char_type& top() { return buf_[off_]; } | |
135 | void clear() | |
136 | { | |
137 | buf_.clear(); | |
138 | off_ = 0; | |
139 | } | |
140 | ||
141 | // Default event handlers. | |
142 | ||
143 | void on_eof() { } | |
144 | void skip(char_type) { } | |
145 | ||
146 | #if BOOST_WORKAROUND(__MWERKS__, <= 0x3206) | |
147 | template<typename Ch> | |
148 | void _push_impl(Ch c) { push(c); } | |
149 | #endif | |
150 | ||
151 | #ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS | |
152 | template<typename FiniteStateFilter> | |
153 | friend class detail::finite_state_filter_impl; | |
154 | #else | |
155 | public: | |
156 | #endif | |
157 | void on_any(char_type) { } | |
158 | private: | |
159 | typedef std::basic_string<char_type> string_type; | |
160 | typedef typename string_type::size_type size_type; | |
161 | std::locale loc_; | |
162 | string_type buf_; | |
163 | size_type off_; | |
164 | }; | |
165 | ||
166 | #if !BOOST_WORKAROUND(__MWERKS__, <= 0x3206) | |
167 | # define BOOST_IOSTREAMS_FSM(fsm) \ | |
168 | template<typename Ch> \ | |
169 | void push(Ch c) \ | |
170 | { ::boost::iostreams::finite_state_machine<fsm, Ch>::push(c); } \ | |
171 | template<typename Ch> \ | |
172 | void skip(Ch c) { (void) c; } \ | |
173 | /**/ | |
174 | #else // #ifndef __MWERKS__ | |
175 | # define BOOST_IOSTREAMS_FSM(fsm) \ | |
176 | void push(char c) { this->_push_impl(c); } \ | |
177 | void push(wchar_t c) { this->_push_impl(c); } \ | |
178 | void skip(char c) { (void) c; } \ | |
179 | void skip(wchar_t c) { (void) c; } \ | |
180 | /**/ | |
181 | #endif | |
182 | ||
183 | //------------------Definition of finite_state_filter_impl--------------------// | |
184 | ||
185 | namespace detail { | |
186 | ||
187 | template<typename FiniteStateMachine> | |
188 | class finite_state_filter_impl : protected FiniteStateMachine | |
189 | { | |
190 | private: | |
191 | template<typename First, typename Last> | |
192 | struct process_event_impl; | |
193 | public: | |
194 | typedef typename char_type_of<FiniteStateMachine>::type char_type; | |
195 | ||
196 | finite_state_filter_impl() : state_(FiniteStateMachine::initial_state) { } | |
197 | ||
198 | template<typename T0> | |
199 | explicit finite_state_filter_impl(const T0& t0) | |
200 | : FiniteStateMachine(t0), state_(FiniteStateMachine::initial_state) | |
201 | { } | |
202 | ||
203 | template<typename T0, typename T1> | |
204 | finite_state_filter_impl(const T0& t0, const T1& t1) | |
205 | : FiniteStateMachine(t0, t1), state_(FiniteStateMachine::initial_state) | |
206 | { } | |
207 | ||
208 | template<typename T0, typename T1, typename T2> | |
209 | finite_state_filter_impl(const T0& t0, const T1& t1, const T2& t2) | |
210 | : FiniteStateMachine(t0, t1, t2), | |
211 | state_(FiniteStateMachine::initial_state) | |
212 | { } | |
213 | protected: | |
214 | void process_event(char_type c) | |
215 | { | |
216 | typedef typename FiniteStateMachine::transition_table transitions; | |
217 | typedef typename mpl::begin<transitions>::type first; | |
218 | typedef typename mpl::end<transitions>::type last; | |
219 | state_ = process_event_impl<first, last>::execute(*this, state_, c); | |
220 | } | |
221 | int& state() { return state_; } | |
222 | void reset() | |
223 | { | |
224 | state_ = FiniteStateMachine::initial_state; | |
225 | this->clear(); | |
226 | } | |
227 | private: | |
228 | template<typename First, typename Last> | |
229 | struct process_event_impl { | |
230 | static int execute(FiniteStateMachine& fsm, int state, char_type event) | |
231 | { | |
232 | typedef typename mpl::deref<First>::type rule; | |
233 | typedef typename mpl::next<First>::type next; | |
234 | typedef typename rule::character_class character_class; | |
235 | ||
236 | if ( state == rule::state && | |
237 | character_class::test(event, fsm.getloc()) ) | |
238 | { | |
239 | // Rule applies. | |
240 | rule::execute(fsm, event); | |
241 | return rule::next_state; | |
242 | } | |
243 | ||
244 | // Rule is inapplicable: try next rule. | |
245 | return process_event_impl<next, Last>::execute(fsm, state, event); | |
246 | } | |
247 | }; | |
248 | ||
249 | template<typename Last> | |
250 | struct process_event_impl<Last, Last> { | |
251 | static int execute(FiniteStateMachine& fsm, int state, char_type c) | |
252 | { | |
253 | on_any(fsm, c); | |
254 | return state; | |
255 | } | |
256 | }; | |
257 | #if BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042)) /* Tru64 */ \ | |
258 | || BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3205)) /* CW9.4 */ | |
259 | public: | |
260 | #endif | |
261 | template<typename FSM> | |
262 | static void on_any(FSM& fsm, char_type c) { fsm.on_any(c); } | |
263 | private: | |
264 | int state_; | |
265 | }; | |
266 | ||
267 | } // End namespace detail. | |
268 | ||
269 | //------------------Definition of finite_state_filter-------------------------// | |
270 | ||
271 | template<typename FiniteStateMachine> | |
272 | class finite_state_filter | |
273 | : public detail::finite_state_filter_impl<FiniteStateMachine> | |
274 | { | |
275 | private: | |
276 | typedef detail::finite_state_filter_impl<FiniteStateMachine> base_type; | |
277 | public: | |
278 | typedef typename base_type::char_type char_type; | |
279 | typedef char_traits<char_type> traits_type; | |
280 | typedef typename base_type::int_type int_type; | |
281 | struct category | |
282 | : dual_use, filter_tag, closable_tag, localizable_tag | |
283 | { }; | |
284 | ||
285 | finite_state_filter() : flags_(0) { } | |
286 | ||
287 | template<typename T0> | |
288 | finite_state_filter(const T0& t0) | |
289 | : base_type(t0), flags_(0) | |
290 | { } | |
291 | ||
292 | template<typename T0, typename T1> | |
293 | finite_state_filter(const T0& t0, const T1& t1) | |
294 | : base_type(t0, t1), flags_(0) | |
295 | { } | |
296 | ||
297 | template<typename T0, typename T1, typename T2> | |
298 | finite_state_filter(const T0& t0, const T1& t1, const T2& t2) | |
299 | : base_type(t0, t1, t2), flags_(0) | |
300 | { } | |
301 | ||
302 | template<typename Source> | |
303 | int_type get(Source& src) | |
304 | { | |
305 | assert((flags_ & f_write) == 0); | |
306 | flags_ |= f_read; | |
307 | ||
308 | while (true) { | |
309 | if ((flags_ & f_eof) == 0) { | |
310 | ||
311 | // Read a character and process it. | |
312 | int_type c; | |
313 | if (traits_type::is_eof(c = iostreams::get(src))) { | |
314 | flags_ |= f_eof; | |
315 | this->on_eof(); | |
316 | } else if (!traits_type::would_block(c)) { | |
317 | this->process_event(c); | |
318 | } | |
319 | } | |
320 | ||
321 | // Return a character, if available. | |
322 | if (!this->empty()) | |
323 | return this->pop(); | |
324 | else if ((flags_ & f_eof) != 0) | |
325 | return traits_type::eof(); | |
326 | } | |
327 | } | |
328 | ||
329 | template<typename Sink> | |
330 | bool put(Sink& dest, char_type c) | |
331 | { | |
332 | assert((flags_ & f_read) == 0); | |
333 | flags_ |= f_write; | |
334 | ||
335 | this->process_event(c); | |
336 | while (!this->empty() && iostreams::put(dest, this->top())) | |
337 | this->pop(); | |
338 | ||
339 | return true; | |
340 | } | |
341 | ||
342 | template<typename Device> | |
343 | void close(Device& dev, BOOST_IOS::openmode which) | |
344 | { | |
345 | if (which == BOOST_IOS::out) { | |
346 | if (flags_ & f_write) | |
347 | while (!this->empty()) | |
348 | iostreams::put_if(dev, this->pop()); | |
349 | this->reset(); | |
350 | flags_ = 0; | |
351 | } | |
352 | } | |
353 | private: | |
354 | enum flags { | |
355 | f_read = 1, | |
356 | f_write = f_read << 1, | |
357 | f_eof = f_write << 1 | |
358 | }; | |
359 | ||
360 | int flags_; | |
361 | }; | |
362 | ||
363 | } } // End namespaces iostreams, boost. | |
364 | ||
365 | #endif // #ifndef BOOST_IOSTREAMS_FINITE_STATE_FILTER_HPP_INCLUDED |