1 /*=============================================================================
2 Copyright (c) 2008 Francois Barel
3
4 Distributed under the Boost Software License, Version 1.0. (See accompanying
5 file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 =============================================================================*/
7 #include <boost/detail/lightweight_test.hpp>
8 #include <boost/type_traits/is_same.hpp>
9
10 #include <boost/spirit/include/qi_operator.hpp>
11 #include <boost/spirit/include/qi_char.hpp>
12 #include <boost/spirit/include/phoenix_core.hpp>
13 #include <boost/spirit/include/phoenix_operator.hpp>
14
15 #include <iterator>
16 #include "test.hpp"
17
18
19 namespace testns
20 {
21
22 BOOST_SPIRIT_TERMINAL_NAME_EX( ops, ops_type )
23
24
25 ///////////////////////////////////////////////////////////////////////////
26 // Parsers
27 ///////////////////////////////////////////////////////////////////////////
28
29 template <typename T1>
30 struct ops_1_parser
31 : boost::spirit::qi::primitive_parser<ops_1_parser<T1> >
32 {
ops_1_parsertestns::ops_1_parser33 ops_1_parser(T1 t1)
34 : t1(t1)
35 {}
36
37 template <typename Context, typename Iterator>
38 struct attribute
39 {
40 typedef int type; // Number of parsed chars.
41 };
42
43 template <typename Iterator, typename Context
44 , typename Skipper, typename Attribute>
parsetestns::ops_1_parser45 bool parse(Iterator& first, Iterator const& last
46 , Context& /*context*/, Skipper const& skipper
47 , Attribute& attr) const
48 {
49 boost::spirit::qi::skip_over(first, last, skipper);
50
51 int count = 0;
52
53 Iterator it = first;
54 typedef typename std::iterator_traits<Iterator>::value_type Char;
55 for (T1 t = 0; t < t1; t++, count++)
56 if (it == last || *it++ != Char('+'))
57 return false;
58
59 boost::spirit::traits::assign_to(count, attr);
60 first = it;
61 return true;
62 }
63
64 template <typename Context>
whattestns::ops_1_parser65 boost::spirit::qi::info what(Context& /*context*/) const
66 {
67 return boost::spirit::qi::info("ops_1");
68 }
69
70 const T1 t1;
71
72 // silence MSVC warning C4512: assignment operator could not be generated
73 BOOST_DELETED_FUNCTION(ops_1_parser& operator= (ops_1_parser const&));
74 };
75
76 template <typename T1, typename T2>
77 struct ops_2_parser
78 : boost::spirit::qi::primitive_parser<ops_2_parser<T1, T2> >
79 {
ops_2_parsertestns::ops_2_parser80 ops_2_parser(T1 t1, T2 t2)
81 : t1(t1)
82 , t2(t2)
83 {}
84
85 template <typename Context, typename Iterator>
86 struct attribute
87 {
88 typedef int type; // Number of parsed chars.
89 };
90
91 template <typename Iterator, typename Context
92 , typename Skipper, typename Attribute>
parsetestns::ops_2_parser93 bool parse(Iterator& first, Iterator const& last
94 , Context& /*context*/, Skipper const& skipper
95 , Attribute& attr) const
96 {
97 boost::spirit::qi::skip_over(first, last, skipper);
98
99 int count = 0;
100
101 Iterator it = first;
102 typedef typename std::iterator_traits<Iterator>::value_type Char;
103 for (T1 t = 0; t < t1; t++, count++)
104 if (it == last || *it++ != Char('+'))
105 return false;
106 for (T2 t = 0; t < t2; t++, count++)
107 if (it == last || *it++ != Char('-'))
108 return false;
109
110 boost::spirit::traits::assign_to(count, attr);
111 first = it;
112 return true;
113 }
114
115 template <typename Context>
whattestns::ops_2_parser116 boost::spirit::qi::info what(Context& /*context*/) const
117 {
118 return boost::spirit::qi::info("ops_2");
119 }
120
121 const T1 t1;
122 const T2 t2;
123
124 // silence MSVC warning C4512: assignment operator could not be generated
125 BOOST_DELETED_FUNCTION(ops_2_parser& operator= (ops_2_parser const&));
126 };
127
128 template <typename T1, typename T2, typename T3>
129 struct ops_3_parser
130 : boost::spirit::qi::primitive_parser<ops_3_parser<T1, T2, T3> >
131 {
ops_3_parsertestns::ops_3_parser132 ops_3_parser(T1 t1, T2 t2, T3 t3)
133 : t1(t1)
134 , t2(t2)
135 , t3(t3)
136 {}
137
138 template <typename Context, typename Iterator>
139 struct attribute
140 {
141 typedef int type; // Number of parsed chars.
142 };
143
144 template <typename Iterator, typename Context
145 , typename Skipper, typename Attribute>
parsetestns::ops_3_parser146 bool parse(Iterator& first, Iterator const& last
147 , Context& /*context*/, Skipper const& skipper
148 , Attribute& attr) const
149 {
150 boost::spirit::qi::skip_over(first, last, skipper);
151
152 int count = 0;
153
154 Iterator it = first;
155 typedef typename std::iterator_traits<Iterator>::value_type Char;
156 for (T1 t = 0; t < t1; t++, count++)
157 if (it == last || *it++ != Char('+'))
158 return false;
159 for (T2 t = 0; t < t2; t++, count++)
160 if (it == last || *it++ != Char('-'))
161 return false;
162 for (T3 t = 0; t < t3; t++, count++)
163 if (it == last || *it++ != Char('*'))
164 return false;
165
166 boost::spirit::traits::assign_to(count, attr);
167 first = it;
168 return true;
169 }
170
171 template <typename Context>
whattestns::ops_3_parser172 boost::spirit::qi::info what(Context& /*context*/) const
173 {
174 return boost::spirit::qi::info("ops_3");
175 }
176
177 const T1 t1;
178 const T2 t2;
179 const T3 t3;
180
181 // silence MSVC warning C4512: assignment operator could not be generated
182 BOOST_DELETED_FUNCTION(ops_3_parser& operator= (ops_3_parser const&));
183 };
184
185 }
186
187
188 namespace boost { namespace spirit
189 {
190
191 ///////////////////////////////////////////////////////////////////////////
192 // Enablers
193 ///////////////////////////////////////////////////////////////////////////
194
195 template <typename T1>
196 struct use_terminal<qi::domain
197 , terminal_ex<testns::tag::ops, fusion::vector1<T1> > >
198 : mpl::true_ {};
199
200 template <typename T1, typename T2>
201 struct use_terminal<qi::domain
202 , terminal_ex<testns::tag::ops, fusion::vector2<T1, T2> > >
203 : mpl::true_ {};
204
205 template <typename T1, typename T2, typename T3>
206 struct use_terminal<qi::domain
207 , terminal_ex<testns::tag::ops, fusion::vector3<T1, T2, T3> > >
208 : mpl::true_ {};
209
210 template <>
211 struct use_lazy_terminal<qi::domain, testns::tag::ops, 1>
212 : mpl::true_ {};
213
214 template <>
215 struct use_lazy_terminal<qi::domain, testns::tag::ops, 2>
216 : mpl::true_ {};
217
218 template <>
219 struct use_lazy_terminal<qi::domain, testns::tag::ops, 3>
220 : mpl::true_ {};
221
222 }}
223
224 namespace boost { namespace spirit { namespace qi
225 {
226
227 ///////////////////////////////////////////////////////////////////////////
228 // Parser generators: make_xxx function (objects)
229 ///////////////////////////////////////////////////////////////////////////
230
231 template <typename Modifiers, typename T1>
232 struct make_primitive<
233 terminal_ex<testns::tag::ops, fusion::vector1<T1> >
234 , Modifiers>
235 {
236 typedef testns::ops_1_parser<T1> result_type;
237 template <typename Terminal>
operator ()boost::spirit::qi::make_primitive238 result_type operator()(const Terminal& term, unused_type) const
239 {
240 return result_type(
241 fusion::at_c<0>(term.args)
242 );
243 }
244 };
245
246 template <typename Modifiers, typename T1, typename T2>
247 struct make_primitive<
248 terminal_ex<testns::tag::ops, fusion::vector2<T1, T2> >
249 , Modifiers>
250 {
251 typedef testns::ops_2_parser<T1, T2> result_type;
252 template <typename Terminal>
operator ()boost::spirit::qi::make_primitive253 result_type operator()(const Terminal& term, unused_type) const
254 {
255 return result_type(
256 fusion::at_c<0>(term.args)
257 , fusion::at_c<1>(term.args)
258 );
259 }
260 };
261
262 template <typename Modifiers, typename T1, typename T2, typename T3>
263 struct make_primitive<
264 terminal_ex<testns::tag::ops, fusion::vector3<T1, T2, T3> >
265 , Modifiers>
266 {
267 typedef testns::ops_3_parser<T1, T2, T3> result_type;
268 template <typename Terminal>
operator ()boost::spirit::qi::make_primitive269 result_type operator()(const Terminal& term, unused_type) const
270 {
271 return result_type(
272 fusion::at_c<0>(term.args)
273 , fusion::at_c<1>(term.args)
274 , fusion::at_c<2>(term.args)
275 );
276 }
277 };
278
279 }}}
280
281
282 namespace testns
283 {
284 template <typename T1, typename T>
check_type_1(const T &)285 void check_type_1(const T& /*t*/)
286 {
287 BOOST_STATIC_ASSERT(( boost::is_same<T
288 , typename boost::spirit::terminal<testns::tag::ops>::result<T1>::type >::value ));
289 }
290
291 template <typename T1, typename T2, typename T>
check_type_2(const T &)292 void check_type_2(const T& /*t*/)
293 {
294 BOOST_STATIC_ASSERT(( boost::is_same<T
295 , typename boost::spirit::terminal<testns::tag::ops>::result<T1, T2>::type >::value ));
296 }
297
298 template <typename T1, typename T2, typename T3, typename T>
check_type_3(const T &)299 void check_type_3(const T& /*t*/)
300 {
301 BOOST_STATIC_ASSERT(( boost::is_same<T
302 , typename boost::spirit::terminal<testns::tag::ops>::result<T1, T2, T3>::type >::value ));
303 }
304 }
305
306
307 int
main()308 main()
309 {
310 using spirit_test::test_attr;
311 using spirit_test::test;
312
313 using testns::ops;
314 using testns::check_type_1;
315 using testns::check_type_2;
316 using testns::check_type_3;
317
318 { // immediate args
319 int c = 0;
320 #define IP1 ops(2)
321 check_type_1<int>(IP1);
322 BOOST_TEST(test_attr("++/", IP1 >> '/', c) && c == 2);
323
324 c = 0;
325 #define IP2 ops(2, 3)
326 check_type_2<int, int>(IP2);
327 BOOST_TEST(test_attr("++---/", IP2 >> '/', c) && c == 5);
328
329 c = 0;
330 #define IP3 ops(2, 3, 4)
331 check_type_3<int, int, int>(IP3);
332 BOOST_TEST(!test("++---***/", IP3 >> '/'));
333 #define IP4 ops(2, 3, 4)
334 check_type_3<int, int, int>(IP4);
335 BOOST_TEST(test_attr("++---****/", IP4 >> '/', c) && c == 9);
336 }
337
338 using boost::phoenix::val;
339 using boost::phoenix::actor;
340 using boost::phoenix::expression::value;
341
342 { // all lazy args
343 int c = 0;
344 #define LP1 ops(val(1))
345 check_type_1<value<int>::type>(LP1);
346 BOOST_TEST(test_attr("+/", LP1 >> '/', c) && c == 1);
347
348 c = 0;
349 #define LP2 ops(val(1), val(4))
350 check_type_2<value<int>::type, value<int>::type>(LP2);
351 BOOST_TEST(test_attr("+----/", LP2 >> '/', c) && c == 5);
352
353 c = 0;
354 #define LP3 ops(val((char)2), val(3.), val(4))
355 check_type_3<value<char>::type, value<double>::type, value<int>::type>(LP3);
356 BOOST_TEST(!test("++---***/", LP3 >> '/'));
357 #define LP4 ops(val(1), val(2), val(3))
358 check_type_3<value<int>::type, value<int>::type, value<int>::type>(LP4);
359 BOOST_TEST(test_attr("+--***/", LP4 >> '/', c) && c == 6);
360 }
361
362 { // mixed immediate and lazy args
363 namespace fusion = boost::fusion;
364 namespace phx = boost::phoenix;
365
366 int c = 0;
367 #define MP1 ops(val(3), 2)
368 check_type_2<value<int>::type, int>(MP1);
369 BOOST_TEST(test_attr("+++--/", MP1 >> '/', c) && c == 5);
370
371 c = 0;
372 #define MP2 ops(4, val(1))
373 check_type_2<int, value<int>::type>(MP2);
374 BOOST_TEST(test_attr("++++-/", MP2 >> '/', c) && c == 5);
375
376 c = 0;
377 #define MP3 ops(2, val(2), val(2))
378 check_type_3<int, value<int>::type, value<int>::type>(MP3);
379 BOOST_TEST(!test("++-**/", MP3 >> '/'));
380 #define MP4 ops(2, val(2), 2)
381 check_type_3<int, value<int>::type, int>(MP4);
382 BOOST_TEST(test_attr("++--**/", MP4 >> '/', c) && c == 6);
383
384 c = 0;
385 #define MP5 ops(val(5) - val(3), 2, val(2))
386 check_type_3<phx::expression::minus<value<int>::type, value<int>::type>::type, int, value<int>::type>(MP5);
387 BOOST_TEST(test_attr("++--**/", MP5 >> '/', c) && c == 6);
388 }
389
390 return boost::report_errors();
391 }
392
393