// (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com) // (C) Copyright 2005-2007 Jonathan Turkanis // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.) // See http://www.boost.org/libs/iostreams for documentation. // Adapted from an example of James Kanze, with suggestions from Peter Dimov. // See http://www.gabi-soft.fr/codebase-en.html. #ifndef BOOST_IOSTREAMS_FINITE_STATE_FILTER_HPP_INCLUDED #define BOOST_IOSTREAMS_FINITE_STATE_FILTER_HPP_INCLUDED #include <cassert> #include <cstdio> // EOF. #include <iostream> // cin, cout. #include <locale> #include <string> #include <boost/config.hpp> // JOIN, member template friends. #include <boost/detail/workaround.hpp> #include <boost/iostreams/categories.hpp> #include <boost/iostreams/char_traits.hpp> #include <boost/iostreams/checked_operations.hpp> // put_if. #include <boost/iostreams/concepts.hpp> #include <boost/iostreams/detail/ios.hpp> // openmode. #include <boost/iostreams/filter/stdio.hpp> #include <boost/iostreams/operations.hpp> #include <boost/mpl/begin_end.hpp> #include <boost/mpl/deref.hpp> #include <boost/preprocessor/control/expr_if.hpp> #include <boost/static_assert.hpp> #include <boost/type_traits/is_base_and_derived.hpp> namespace boost { namespace iostreams { //------------------Definition of basic character classes---------------------// struct finite_state_machine_base { static const int initial_state = 0; // All-inclusive character class. struct is_any { template<typename Ch> static bool test(Ch, const std::locale&) { return true; } }; // Locale-sensitive character classes. #define BOOST_IOSTREAMS_CHARACTER_CLASS(class) \ struct BOOST_JOIN(is_, class) { \ template<typename Ch> \ static bool test(Ch event, const std::locale& loc) \ { return std::BOOST_JOIN(is, class)(event, loc); } \ }; \ /**/ BOOST_IOSTREAMS_CHARACTER_CLASS(alnum) BOOST_IOSTREAMS_CHARACTER_CLASS(alpha) BOOST_IOSTREAMS_CHARACTER_CLASS(cntrl) BOOST_IOSTREAMS_CHARACTER_CLASS(digit) BOOST_IOSTREAMS_CHARACTER_CLASS(graph) BOOST_IOSTREAMS_CHARACTER_CLASS(lower) BOOST_IOSTREAMS_CHARACTER_CLASS(print) BOOST_IOSTREAMS_CHARACTER_CLASS(punct) BOOST_IOSTREAMS_CHARACTER_CLASS(space) BOOST_IOSTREAMS_CHARACTER_CLASS(upper) BOOST_IOSTREAMS_CHARACTER_CLASS(xdigit) #undef BOOST_IOSTREAMS_CHARACTER_CLASS }; template<typename Ch> struct finite_state_machine_base_ex : finite_state_machine_base { template<Ch C> struct is { static bool test(Ch event, const std::locale&) { return event == C; } }; }; //------------------Definition of base class for finite state filters---------// namespace detail { template<typename FiniteStateMachine> class finite_state_filter_impl; } // End namespace detail. template<typename Derived, typename Ch = char> class finite_state_machine : public finite_state_machine_base_ex<Ch> { public: typedef Ch char_type; typedef typename char_traits<Ch>::int_type int_type; void imbue(const std::locale& loc) { loc_ = loc; } const std::locale& getloc() const { return loc_; } protected: finite_state_machine() : off_(0) { } // Template whose instantiations make up transition table. template< int State, typename CharacterClass, int NextState, void (Derived::*Action)(char_type) > struct row { typedef CharacterClass character_class; static const int state = State; static const int next_state = NextState; static void execute(Derived& d, char_type event) { (d.*Action)(event); } }; // Stack interface. bool empty() const { return off_ == buf_.size(); } void push(char c) { buf_ += c; } char_type pop() { char_type result = buf_[off_++]; if (off_ == buf_.size()) clear(); return result; } char_type& top() { return buf_[off_]; } void clear() { buf_.clear(); off_ = 0; } // Default event handlers. void on_eof() { } void skip(char_type) { } #if BOOST_WORKAROUND(__MWERKS__, <= 0x3206) template<typename Ch> void _push_impl(Ch c) { push(c); } #endif #ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS template<typename FiniteStateFilter> friend class detail::finite_state_filter_impl; #else public: #endif void on_any(char_type) { } private: typedef std::basic_string<char_type> string_type; typedef typename string_type::size_type size_type; std::locale loc_; string_type buf_; size_type off_; }; #if !BOOST_WORKAROUND(__MWERKS__, <= 0x3206) # define BOOST_IOSTREAMS_FSM(fsm) \ template<typename Ch> \ void push(Ch c) \ { ::boost::iostreams::finite_state_machine<fsm, Ch>::push(c); } \ template<typename Ch> \ void skip(Ch c) { (void) c; } \ /**/ #else // #ifndef __MWERKS__ # define BOOST_IOSTREAMS_FSM(fsm) \ void push(char c) { this->_push_impl(c); } \ void push(wchar_t c) { this->_push_impl(c); } \ void skip(char c) { (void) c; } \ void skip(wchar_t c) { (void) c; } \ /**/ #endif //------------------Definition of finite_state_filter_impl--------------------// namespace detail { template<typename FiniteStateMachine> class finite_state_filter_impl : protected FiniteStateMachine { private: template<typename First, typename Last> struct process_event_impl; public: typedef typename char_type_of<FiniteStateMachine>::type char_type; finite_state_filter_impl() : state_(FiniteStateMachine::initial_state) { } template<typename T0> explicit finite_state_filter_impl(const T0& t0) : FiniteStateMachine(t0), state_(FiniteStateMachine::initial_state) { } template<typename T0, typename T1> finite_state_filter_impl(const T0& t0, const T1& t1) : FiniteStateMachine(t0, t1), state_(FiniteStateMachine::initial_state) { } template<typename T0, typename T1, typename T2> finite_state_filter_impl(const T0& t0, const T1& t1, const T2& t2) : FiniteStateMachine(t0, t1, t2), state_(FiniteStateMachine::initial_state) { } protected: void process_event(char_type c) { typedef typename FiniteStateMachine::transition_table transitions; typedef typename mpl::begin<transitions>::type first; typedef typename mpl::end<transitions>::type last; state_ = process_event_impl<first, last>::execute(*this, state_, c); } int& state() { return state_; } void reset() { state_ = FiniteStateMachine::initial_state; this->clear(); } private: template<typename First, typename Last> struct process_event_impl { static int execute(FiniteStateMachine& fsm, int state, char_type event) { typedef typename mpl::deref<First>::type rule; typedef typename mpl::next<First>::type next; typedef typename rule::character_class character_class; if ( state == rule::state && character_class::test(event, fsm.getloc()) ) { // Rule applies. rule::execute(fsm, event); return rule::next_state; } // Rule is inapplicable: try next rule. return process_event_impl<next, Last>::execute(fsm, state, event); } }; template<typename Last> struct process_event_impl<Last, Last> { static int execute(FiniteStateMachine& fsm, int state, char_type c) { on_any(fsm, c); return state; } }; #if BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042)) /* Tru64 */ \ || BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3205)) /* CW9.4 */ public: #endif template<typename FSM> static void on_any(FSM& fsm, char_type c) { fsm.on_any(c); } private: int state_; }; } // End namespace detail. //------------------Definition of finite_state_filter-------------------------// template<typename FiniteStateMachine> class finite_state_filter : public detail::finite_state_filter_impl<FiniteStateMachine> { private: typedef detail::finite_state_filter_impl<FiniteStateMachine> base_type; public: typedef typename base_type::char_type char_type; typedef char_traits<char_type> traits_type; typedef typename base_type::int_type int_type; struct category : dual_use, filter_tag, closable_tag, localizable_tag { }; finite_state_filter() : flags_(0) { } template<typename T0> finite_state_filter(const T0& t0) : base_type(t0), flags_(0) { } template<typename T0, typename T1> finite_state_filter(const T0& t0, const T1& t1) : base_type(t0, t1), flags_(0) { } template<typename T0, typename T1, typename T2> finite_state_filter(const T0& t0, const T1& t1, const T2& t2) : base_type(t0, t1, t2), flags_(0) { } template<typename Source> int_type get(Source& src) { assert((flags_ & f_write) == 0); flags_ |= f_read; while (true) { if ((flags_ & f_eof) == 0) { // Read a character and process it. int_type c; if (traits_type::is_eof(c = iostreams::get(src))) { flags_ |= f_eof; this->on_eof(); } else if (!traits_type::would_block(c)) { this->process_event(c); } } // Return a character, if available. if (!this->empty()) return this->pop(); else if ((flags_ & f_eof) != 0) return traits_type::eof(); } } template<typename Sink> bool put(Sink& dest, char_type c) { assert((flags_ & f_read) == 0); flags_ |= f_write; this->process_event(c); while (!this->empty() && iostreams::put(dest, this->top())) this->pop(); return true; } template<typename Device> void close(Device& dev, BOOST_IOS::openmode which) { if (which == BOOST_IOS::out) { if (flags_ & f_write) while (!this->empty()) iostreams::put_if(dev, this->pop()); this->reset(); flags_ = 0; } } private: enum flags { f_read = 1, f_write = f_read << 1, f_eof = f_write << 1 }; int flags_; }; } } // End namespaces iostreams, boost. #endif // #ifndef BOOST_IOSTREAMS_FINITE_STATE_FILTER_HPP_INCLUDED