interpreter.hpp 5.8 KB

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