blob: 72cd78b0e68a0f088b32bc48e2b99a26ac06f1aa [file] [log] [blame]
Brian Silverman57be3162018-08-04 23:36:33 -07001
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
53namespace 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