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