blob: 9cdc745ab5b98dd16cac0114ebdeb7afb88f5633 [file] [log] [blame]
// (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