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 
token_parser(token_iterator from,token_iterator to)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>
get()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?
has_more_tokens() const124     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
applyexample::interpreter::invoker133     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
applyexample::interpreter::invoker149     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
register_function(std::string const & name,Function f)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 
parse_input(std::string const & text) const165   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 
190