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