123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- // (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);
- }
- }
- }
|