blob: 72cd78b0e68a0f088b32bc48e2b99a26ac06f1aa [file] [log] [blame]
// (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);
}
}
}