1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
// (C) Copyright Tobias Schwinger
//
// Use modification and distribution are subject to the boost Software License,
// Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt).
//------------------------------------------------------------------------------
//
// This example implements a simple batch-style interpreter that is capable of
// calling functions previously registered with it. The parameter types of the
// functions are used to control the parsing of the input.
//
// Implementation description
// ==========================
//
// When a function is registered, an 'invoker' template is instantiated with
// the function's type. The 'invoker' fetches a value from the 'token_parser'
// for each parameter of the function into a tuple and finally invokes the the
// function with these values as arguments. The invoker's entrypoint, which
// is a function of the callable builtin that describes the function to call and
// a reference to the 'token_parser', is partially bound to the registered
// function and put into a map so it can be found by name during parsing.
#include <map>
#include <string>
#include <stdexcept>
#include <boost/token_iterator.hpp>
#include <boost/token_functions.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/type_traits/remove_cv.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/fusion/include/push_back.hpp>
#include <boost/fusion/include/cons.hpp>
#include <boost/fusion/include/invoke.hpp>
#include <boost/mpl/begin.hpp>
#include <boost/mpl/end.hpp>
#include <boost/mpl/next.hpp>
#include <boost/mpl/deref.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/function_types/is_nonmember_callable_builtin.hpp>
#include <boost/function_types/parameter_types.hpp>
namespace example
{
namespace fusion = boost::fusion;
namespace ft = boost::function_types;
namespace mpl = boost::mpl;
class interpreter
{
class token_parser;
typedef boost::function<void(token_parser &)> invoker_function;
typedef std::map<std::string, invoker_function> dictionary;
dictionary map_invokers;
public:
// Registers a function with the interpreter.
template<typename Function>
typename boost::enable_if< ft::is_nonmember_callable_builtin<Function>
>::type register_function(std::string const & name, Function f);
// Parse input for functions to call.
void parse_input(std::string const & text) const;
private:
template< typename Function
, class From = typename mpl::begin< ft::parameter_types<Function> >::type
, class To = typename mpl::end< ft::parameter_types<Function> >::type
>
struct invoker;
};
class interpreter::token_parser
{
typedef boost::token_iterator_generator<
boost::char_separator<char> >::type token_iterator;
token_iterator itr_at, itr_to;
public:
token_parser(token_iterator from, token_iterator to)
: itr_at(from), itr_to(to)
{ }
private:
template<typename T>
struct remove_cv_ref
: boost::remove_cv< typename boost::remove_reference<T>::type >
{ };
public:
// Returns a token of given type.
// We just apply boost::lexical_cast to whitespace separated string tokens
// for simplicity.
template<typename RequestedType>
typename remove_cv_ref<RequestedType>::type get()
{
if (! this->has_more_tokens())
throw std::runtime_error("unexpected end of input");
try
{
typedef typename remove_cv_ref<RequestedType>::type result_type;
result_type result = boost::lexical_cast
<typename remove_cv_ref<result_type>::type>(*this->itr_at);
++this->itr_at;
return result;
}
catch (boost::bad_lexical_cast &)
{ throw std::runtime_error("invalid argument: " + *this->itr_at); }
}
// Any more tokens?
bool has_more_tokens() const { return this->itr_at != this->itr_to; }
};
template<typename Function, class From, class To>
struct interpreter::invoker
{
// add an argument to a Fusion cons-list for each parameter type
template<typename Args>
static inline
void apply(Function func, token_parser & parser, Args const & args)
{
typedef typename mpl::deref<From>::type arg_type;
typedef typename mpl::next<From>::type next_iter_type;
interpreter::invoker<Function, next_iter_type, To>::apply
( func, parser, fusion::push_back(args, parser.get<arg_type>()) );
}
};
template<typename Function, class To>
struct interpreter::invoker<Function,To,To>
{
// the argument list is complete, now call the function
template<typename Args>
static inline
void apply(Function func, token_parser &, Args const & args)
{
fusion::invoke(func,args);
}
};
template<typename Function>
typename boost::enable_if< ft::is_nonmember_callable_builtin<Function> >::type
interpreter::register_function(std::string const & name, Function f)
{
// instantiate and store the invoker by name
this->map_invokers[name] = boost::bind(
& invoker<Function>::template apply<fusion::nil>, f,_1,fusion::nil() );
}
void interpreter::parse_input(std::string const & text) const
{
boost::char_separator<char> s(" \t\n\r");
token_parser parser
( boost::make_token_iterator<std::string>(text.begin(), text.end(), s)
, boost::make_token_iterator<std::string>(text.end() , text.end(), s) );
while (parser.has_more_tokens())
{
// read function name
std::string func_name = parser.get<std::string>();
// look up function
dictionary::const_iterator entry = map_invokers.find( func_name );
if (entry == map_invokers.end())
throw std::runtime_error("unknown function: " + func_name);
// call the invoker which controls argument parsing
entry->second(parser);
}
}
}
|