]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /*============================================================================= |
2 | Copyright (c) 2001-2015 Joel de Guzman | |
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/spirit/home/x3.hpp> | |
9 | #include <boost/fusion/include/vector.hpp> | |
10 | #include <boost/fusion/include/deque.hpp> | |
11 | #include <boost/fusion/include/at.hpp> | |
12 | #include <boost/fusion/include/comparison.hpp> | |
13 | ||
14 | #include <string> | |
15 | #include <iostream> | |
16 | #include "test.hpp" | |
92f5a8d4 | 17 | #include "utils.hpp" |
7c673cae FG |
18 | |
19 | int | |
20 | main() | |
21 | { | |
22 | using boost::spirit::x3::unused_type; | |
23 | ||
24 | using boost::spirit::x3::char_; | |
25 | using boost::spirit::x3::space; | |
26 | using boost::spirit::x3::string; | |
27 | using boost::spirit::x3::attr; | |
28 | using boost::spirit::x3::omit; | |
29 | using boost::spirit::x3::lit; | |
30 | using boost::spirit::x3::unused; | |
31 | using boost::spirit::x3::int_; | |
32 | using boost::spirit::x3::float_; | |
33 | using boost::spirit::x3::no_case; | |
34 | using boost::spirit::x3::rule; | |
35 | using boost::spirit::x3::alnum; | |
36 | ||
37 | using boost::spirit::x3::traits::attribute_of; | |
38 | ||
39 | using boost::fusion::vector; | |
40 | using boost::fusion::deque; | |
41 | using boost::fusion::at_c; | |
42 | ||
43 | using spirit_test::test; | |
44 | using spirit_test::test_attr; | |
45 | ||
f67539c2 TL |
46 | BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(char_ >> char_); |
47 | ||
7c673cae FG |
48 | { |
49 | BOOST_TEST((test("aa", char_ >> char_))); | |
50 | BOOST_TEST((test("aa", char_ >> 'a'))); | |
51 | BOOST_TEST((test("aaa", char_ >> char_ >> char_('a')))); | |
52 | BOOST_TEST((test("xi", char_('x') >> char_('i')))); | |
53 | BOOST_TEST((!test("xi", char_('x') >> char_('o')))); | |
54 | BOOST_TEST((test("xin", char_('x') >> char_('i') >> char_('n')))); | |
55 | } | |
56 | ||
57 | #ifdef BOOST_SPIRIT_COMPILE_ERROR_CHECK | |
58 | { | |
59 | // Compile check only | |
60 | struct x {}; | |
61 | char_ >> x(); // this should give a reasonable error message | |
62 | } | |
63 | #endif | |
64 | ||
65 | { | |
66 | BOOST_TEST((test(" a a", char_ >> char_, space))); | |
67 | BOOST_TEST((test(" x i", char_('x') >> char_('i'), space))); | |
68 | BOOST_TEST((!test(" x i", char_('x') >> char_('o'), space))); | |
69 | } | |
70 | ||
71 | ||
72 | { | |
73 | BOOST_TEST((test(" Hello, World", lit("Hello") >> ',' >> "World", space))); | |
74 | } | |
75 | ||
76 | ||
77 | { | |
78 | vector<char, char> attr; | |
79 | BOOST_TEST((test_attr("ab", char_ >> char_, attr))); | |
80 | BOOST_TEST((at_c<0>(attr) == 'a')); | |
81 | BOOST_TEST((at_c<1>(attr) == 'b')); | |
82 | } | |
83 | ||
84 | #ifdef BOOST_SPIRIT_COMPILE_ERROR_CHECK | |
85 | { | |
86 | // Compile check only | |
87 | vector<char, char> attr; | |
88 | ||
89 | // error: attr does not have enough elements | |
90 | test_attr("abc", char_ >> char_ >> char_, attr); | |
91 | } | |
92 | #endif | |
93 | ||
94 | { | |
95 | vector<char, char, char> attr; | |
96 | BOOST_TEST((test_attr(" a\n b\n c", char_ >> char_ >> char_, attr, space))); | |
97 | BOOST_TEST((at_c<0>(attr) == 'a')); | |
98 | BOOST_TEST((at_c<1>(attr) == 'b')); | |
99 | BOOST_TEST((at_c<2>(attr) == 'c')); | |
100 | } | |
101 | ||
102 | { | |
103 | // 'b' has an unused_type. unused attributes are not part of the sequence | |
104 | vector<char, char> attr; | |
105 | BOOST_TEST((test_attr("abc", char_ >> 'b' >> char_, attr))); | |
106 | BOOST_TEST((at_c<0>(attr) == 'a')); | |
107 | BOOST_TEST((at_c<1>(attr) == 'c')); | |
108 | } | |
109 | ||
110 | { | |
111 | // 'b' has an unused_type. unused attributes are not part of the sequence | |
112 | vector<char, char> attr; | |
113 | BOOST_TEST((test_attr("acb", char_ >> char_ >> 'b', attr))); | |
114 | BOOST_TEST((at_c<0>(attr) == 'a')); | |
115 | BOOST_TEST((at_c<1>(attr) == 'c')); | |
116 | } | |
117 | ||
118 | { | |
119 | // "hello" has an unused_type. unused attributes are not part of the sequence | |
120 | vector<char, char> attr; | |
121 | BOOST_TEST((test_attr("a hello c", char_ >> "hello" >> char_, attr, space))); | |
122 | BOOST_TEST((at_c<0>(attr) == 'a')); | |
123 | BOOST_TEST((at_c<1>(attr) == 'c')); | |
124 | } | |
125 | ||
126 | { | |
127 | // a single element | |
128 | char attr; | |
129 | BOOST_TEST((test_attr("ab", char_ >> 'b', attr))); | |
130 | BOOST_TEST((attr == 'a')); | |
131 | } | |
132 | ||
133 | { | |
134 | // a single element fusion sequence | |
135 | vector<char> attr; | |
136 | BOOST_TEST((test_attr("ab", char_ >> 'b', attr))); | |
137 | BOOST_TEST((at_c<0>(attr) == 'a')); | |
138 | } | |
139 | ||
140 | { | |
141 | // make sure single element tuples get passed through if the rhs | |
142 | // has a single element tuple as its attribute. Edit JDG 2014: | |
143 | // actually he issue here is that if the rhs in this case a rule | |
144 | // (r), it should get it (i.e. the sequence parser should not | |
145 | // unwrap it). It's odd that the RHS (r) does not really have a | |
146 | // single element tuple (it's a deque<char, int>), so the original | |
147 | // comment is not accurate. | |
148 | ||
149 | typedef deque<char, int> attr_type; | |
150 | attr_type fv; | |
151 | ||
152 | auto r = rule<class r, attr_type>() | |
153 | = char_ >> ',' >> int_; | |
154 | ||
155 | BOOST_TEST((test_attr("test:x,1", "test:" >> r, fv) && | |
156 | fv == attr_type('x', 1))); | |
157 | } | |
158 | ||
159 | { | |
160 | // make sure single element tuples get passed through if the rhs | |
161 | // has a single element tuple as its attribute. This is a correction | |
162 | // of the test above. | |
163 | ||
164 | typedef deque<int> attr_type; | |
165 | attr_type fv; | |
166 | ||
167 | auto r = rule<class r, attr_type>() | |
168 | = int_; | |
169 | ||
170 | BOOST_TEST((test_attr("test:1", "test:" >> r, fv) && | |
171 | fv == attr_type(1))); | |
172 | } | |
173 | ||
174 | { | |
175 | // unused means we don't care about the attribute | |
176 | BOOST_TEST((test_attr("abc", char_ >> 'b' >> char_, unused))); | |
177 | } | |
178 | ||
179 | { | |
180 | BOOST_TEST((test("aA", no_case[char_('a') >> 'a']))); | |
181 | BOOST_TEST((test("BEGIN END", no_case[lit("begin") >> "end"], space))); | |
182 | BOOST_TEST((!test("BEGIN END", no_case[lit("begin") >> "nend"], space))); | |
183 | } | |
184 | ||
92f5a8d4 TL |
185 | { // check attribute is passed through unary to another sequence |
186 | using boost::spirit::x3::eps; | |
187 | std::string s; | |
188 | BOOST_TEST(test_attr("ab", eps >> no_case[char_ >> char_], s)); | |
189 | BOOST_TEST("ab" == s); | |
190 | s.clear(); | |
191 | BOOST_TEST(test_attr("ab", no_case[char_ >> char_] >> eps, s)); | |
192 | BOOST_TEST("ab" == s); | |
193 | s.clear(); | |
194 | BOOST_TEST(test_attr("abc", char_ >> no_case[char_ >> char_], s)); | |
195 | BOOST_TEST("abc" == s); | |
196 | s.clear(); | |
197 | BOOST_TEST(test_attr("abc", no_case[char_ >> char_] >> char_, s)); | |
198 | BOOST_TEST("abc" == s); | |
199 | } | |
200 | ||
7c673cae FG |
201 | { |
202 | #ifdef SPIRIT_NO_COMPILE_CHECK | |
203 | char_ >> char_ = char_ >> char_; // disallow this! | |
204 | #endif | |
205 | } | |
206 | ||
207 | { // alternative forms of attributes. Allow sequences to take in | |
208 | // stl containers. | |
209 | ||
210 | std::vector<char> v; | |
211 | BOOST_TEST(test_attr("abc", char_ >> char_ >> char_, v)); | |
212 | BOOST_TEST(v.size() == 3); | |
213 | BOOST_TEST(v[0] == 'a'); | |
214 | BOOST_TEST(v[1] == 'b'); | |
215 | BOOST_TEST(v[2] == 'c'); | |
216 | } | |
217 | ||
218 | { // alternative forms of attributes. Allow sequences to take in | |
219 | // stl containers. | |
220 | ||
221 | std::vector<char> v; | |
222 | BOOST_TEST(test_attr("a,b,c", char_ >> *(',' >> char_), v)); | |
223 | BOOST_TEST(v.size() == 3); | |
224 | BOOST_TEST(v[0] == 'a'); | |
225 | BOOST_TEST(v[1] == 'b'); | |
226 | BOOST_TEST(v[2] == 'c'); | |
227 | } | |
228 | ||
229 | { // alternative forms of attributes. Allow sequences to take in | |
230 | // stl containers. | |
231 | ||
232 | std::vector<char> v; | |
233 | BOOST_TEST(test_attr("abc", char_ >> *char_, v)); | |
234 | BOOST_TEST(v.size() == 3); | |
235 | BOOST_TEST(v[0] == 'a'); | |
236 | BOOST_TEST(v[1] == 'b'); | |
237 | BOOST_TEST(v[2] == 'c'); | |
238 | } | |
239 | ||
240 | { // alternative forms of attributes. Allow sequences to take in | |
241 | // stl containers. | |
242 | //~ using boost::spirit::x3::hold; | |
243 | ||
244 | std::vector<char> v; | |
245 | BOOST_TEST(test_attr("abc", char_ >> *(char_ >> char_), v)); | |
246 | BOOST_TEST(v.size() == 3); | |
247 | BOOST_TEST(v[0] == 'a'); | |
248 | BOOST_TEST(v[1] == 'b'); | |
249 | BOOST_TEST(v[2] == 'c'); | |
250 | ||
251 | v.clear(); | |
252 | BOOST_TEST(!test_attr("abcd", char_ >> *(char_ >> char_), v)); | |
253 | ||
f67539c2 | 254 | // $$$ hold not yet implemented $$$ |
7c673cae FG |
255 | //~ v.clear(); |
256 | //~ BOOST_TEST(test_attr("abcdef", char_ >> *hold[char_ >> char_] >> char_, v)); | |
257 | //~ BOOST_TEST(v.size() == 6); | |
258 | //~ BOOST_TEST(v[0] == 'a'); | |
259 | //~ BOOST_TEST(v[1] == 'b'); | |
260 | //~ BOOST_TEST(v[2] == 'c'); | |
261 | //~ BOOST_TEST(v[3] == 'd'); | |
262 | //~ BOOST_TEST(v[4] == 'e'); | |
263 | //~ BOOST_TEST(v[5] == 'f'); | |
264 | ||
265 | v.clear(); | |
266 | BOOST_TEST(test_attr("abc", char_ >> +(char_ >> char_), v)); | |
267 | BOOST_TEST(v.size() == 3); | |
268 | BOOST_TEST(v[0] == 'a'); | |
269 | BOOST_TEST(v[1] == 'b'); | |
270 | BOOST_TEST(v[2] == 'c'); | |
271 | } | |
272 | ||
273 | { // alternative forms of attributes. Allow sequences to take in | |
274 | // stl containers. | |
275 | ||
276 | std::vector<char> v; | |
277 | BOOST_TEST(test_attr("abc", char_ >> -(+char_), v)); | |
278 | BOOST_TEST(v.size() == 3); | |
279 | BOOST_TEST(v[0] == 'a'); | |
280 | BOOST_TEST(v[1] == 'b'); | |
281 | BOOST_TEST(v[2] == 'c'); | |
282 | } | |
283 | ||
284 | { // alternative forms of attributes. Allow sequences to take in | |
285 | // stl containers. | |
286 | ||
287 | std::string s; | |
288 | BOOST_TEST(test_attr("foobar", string("foo") >> string("bar"), s)); | |
289 | BOOST_TEST(s == "foobar"); | |
290 | ||
291 | s.clear(); | |
292 | ||
293 | // $$$ hold not yet implemented $$$ | |
294 | //~ using boost::spirit::x3::hold; | |
295 | ||
296 | //~ rule<char const*, std::string()> word = +char_("abc"); | |
297 | //~ BOOST_TEST(test_attr("ab.bc.ca", *hold[word >> string(".")] >> word, s)); | |
298 | //~ BOOST_TEST(s == "ab.bc.ca"); | |
299 | } | |
300 | ||
301 | // Make sure get_sequence_types works for sequences of sequences. | |
302 | { | |
303 | std::vector<char> v; | |
304 | BOOST_TEST(test_attr(" a b", (' ' >> char_) >> (' ' >> char_), v)); | |
305 | BOOST_TEST(v.size() == 2); | |
306 | BOOST_TEST(v[0] == 'a'); | |
307 | BOOST_TEST(v[1] == 'b'); | |
308 | } | |
309 | ||
310 | // alternative forms of attributes. Allow sequences to take in | |
311 | // stl containers of stl containers. | |
312 | { | |
313 | std::vector<std::string> v; | |
314 | BOOST_TEST(test_attr("abc1,abc2", | |
315 | *~char_(',') >> *(',' >> *~char_(',')), v)); | |
316 | BOOST_TEST(v.size() == 2 && v[0] == "abc1" && v[1] == "abc2"); | |
317 | } | |
318 | ||
319 | { | |
320 | std::vector<std::string> v; | |
321 | ||
322 | auto e = rule<class e, std::string>() | |
323 | = *~char_(','); | |
324 | ||
325 | auto l = rule<class l, std::vector<std::string>>() | |
326 | = e >> *(',' >> e); | |
327 | ||
328 | BOOST_TEST(test_attr("abc1,abc2,abc3", l, v)); | |
329 | BOOST_TEST(v.size() == 3); | |
330 | BOOST_TEST(v[0] == "abc1"); | |
331 | BOOST_TEST(v[1] == "abc2"); | |
332 | BOOST_TEST(v[2] == "abc3"); | |
333 | } | |
334 | ||
335 | // do the same with a plain string object | |
336 | { | |
337 | std::string s; | |
338 | BOOST_TEST(test_attr("abc1,abc2", | |
339 | *~char_(',') >> *(',' >> *~char_(',')), s)); | |
340 | BOOST_TEST(s == "abc1abc2"); | |
341 | } | |
342 | ||
343 | { | |
344 | std::string s; | |
345 | auto e = rule<class e, std::string>() | |
346 | = *~char_(','); | |
347 | ||
348 | auto l = rule<class l, std::string>() | |
349 | = e >> *(',' >> e); | |
350 | ||
351 | BOOST_TEST(test_attr("abc1,abc2,abc3", l, s)); | |
352 | BOOST_TEST(s == "abc1abc2abc3"); | |
353 | } | |
354 | ||
355 | { | |
356 | std::vector<char> v; | |
357 | BOOST_TEST(test_attr("ab", char_ >> -char_, v)); | |
358 | BOOST_TEST(v.size() == 2 && v[0] == 'a' && v[1] == 'b'); | |
359 | ||
360 | v.clear(); | |
361 | BOOST_TEST(test_attr("a", char_ >> -char_, v)); | |
362 | BOOST_TEST(v.size() == 1 && v[0] == 'a'); | |
363 | ||
364 | // $$$ should this be allowed? I don't think so... $$$ | |
365 | //~ v.clear(); | |
366 | //~ BOOST_TEST(test_attr("a", char_, v)); | |
367 | //~ BOOST_TEST(v.size() == 1 && v[0] == 'a'); | |
368 | } | |
369 | ||
370 | { | |
371 | std::vector<boost::optional<char>> v; | |
372 | BOOST_TEST(test_attr("ab", char_ >> -char_, v)); | |
373 | BOOST_TEST(v.size() == 2 && v[0] == 'a' && v[1] == 'b'); | |
374 | ||
375 | v.clear(); | |
376 | BOOST_TEST(test_attr("a", char_ >> -char_, v)); | |
377 | BOOST_TEST(v.size() == 2 && v[0] == 'a' && !v[1]); | |
378 | ||
379 | // $$$ should this be allowed? I don't think so... $$$ | |
380 | //~ v.clear(); | |
381 | //~ BOOST_TEST(test_attr("a", char_, v)); | |
382 | //~ BOOST_TEST(v.size() == 1 && v[0] == 'a'); | |
383 | } | |
384 | ||
385 | // test from spirit mailing list | |
386 | // "Optional operator causes string attribute concatenation" | |
387 | { | |
388 | typedef vector<char, char, int> attr_type; | |
389 | attr_type attr; | |
390 | ||
391 | auto node = alnum >> -('[' >> alnum >> '=' >> int_ >> ']'); | |
392 | ||
393 | BOOST_TEST(test_attr("x[y=123]", node, attr)); | |
394 | BOOST_TEST(attr == attr_type('x', 'y', 123)); | |
395 | } | |
396 | ||
397 | // test from spirit mailing list (variation of above) | |
398 | // "Optional operator causes string attribute concatenation" | |
399 | { | |
400 | typedef vector<std::string, std::string, int> attr_type; | |
401 | attr_type attr; | |
402 | ||
403 | auto node = +alnum >> -('[' >> +alnum >> '=' >> int_ >> ']'); | |
404 | ||
405 | BOOST_TEST(test_attr("xxx[yyy=123]", node, attr)); | |
406 | BOOST_TEST(attr == attr_type("xxx", "yyy", 123)); | |
407 | } | |
408 | ||
409 | // test from spirit mailing list | |
410 | // "Error with container within sequence" | |
411 | { | |
412 | typedef vector<std::string> attr_type; | |
413 | attr_type attr; | |
414 | ||
415 | auto r = *alnum; | |
416 | ||
417 | BOOST_TEST(test_attr("abcdef", r, attr)); | |
418 | BOOST_TEST(at_c<0>(attr) == "abcdef"); | |
419 | } | |
420 | ||
421 | // test from spirit mailing list (variation of above) | |
422 | // "Error with container within sequence" | |
423 | { | |
424 | typedef vector<std::vector<int>> attr_type; | |
425 | attr_type attr; | |
426 | ||
427 | auto r = *int_; | |
428 | ||
429 | BOOST_TEST(test_attr("123 456", r, attr, space)); | |
430 | BOOST_TEST(at_c<0>(attr).size() == 2); | |
431 | BOOST_TEST(at_c<0>(attr)[0] == 123); | |
432 | BOOST_TEST(at_c<0>(attr)[1] == 456); | |
433 | } | |
434 | ||
435 | { | |
436 | using Attr = boost::variant<int, float>; | |
437 | Attr attr; | |
438 | auto const term = rule<class term, Attr>("term") = int_ | float_; | |
439 | auto const expr = rule<class expr, Attr>("expr") = term | ('(' > term > ')'); | |
440 | BOOST_TEST((test_attr("(1)", expr, attr, space))); | |
441 | } | |
442 | ||
443 | // test that failing sequence leaves attribute consistent | |
444 | { | |
445 | std::string attr; | |
446 | //no need to use omit[], but lit() is buggy ATM | |
447 | BOOST_TEST(test_attr("A\nB\nC", *(char_ >> omit[lit("\n")]), attr, false)); | |
448 | BOOST_TEST(attr == "AB"); | |
449 | } | |
450 | ||
451 | // test that sequence with only one parser producing attribute | |
452 | // makes it unwrapped | |
453 | { | |
454 | BOOST_TEST((boost::is_same< | |
455 | typename attribute_of<decltype(lit("abc") >> attr(long())), unused_type>::type, | |
456 | long>() )); | |
457 | } | |
458 | ||
459 | { // test action | |
460 | using boost::fusion::at_c; | |
461 | ||
462 | char c = 0; | |
463 | int n = 0; | |
464 | auto f = [&](auto& ctx) | |
465 | { | |
466 | c = at_c<0>(_attr(ctx)); | |
467 | n = at_c<1>(_attr(ctx)); | |
468 | }; | |
469 | ||
470 | BOOST_TEST(test("x123\"a string\"", (char_ >> int_ >> "\"a string\"")[f])); | |
471 | BOOST_TEST(c == 'x'); | |
472 | BOOST_TEST(n == 123); | |
473 | } | |
474 | ||
475 | { // test action | |
476 | char c = 0; | |
477 | int n = 0; | |
478 | auto f = [&](auto& ctx) | |
479 | { | |
480 | c = at_c<0>(_attr(ctx)); | |
481 | n = at_c<1>(_attr(ctx)); | |
482 | }; | |
483 | ||
484 | BOOST_TEST(test("x 123 \"a string\"", (char_ >> int_ >> "\"a string\"")[f], space)); | |
485 | BOOST_TEST(c == 'x'); | |
486 | BOOST_TEST(n == 123); | |
487 | } | |
488 | ||
92f5a8d4 TL |
489 | { |
490 | #ifdef SPIRIT_NO_COMPILE_CHECK | |
491 | char const* const s = ""; | |
492 | int i; | |
493 | parse(s, s, int_ >> int_, i); | |
494 | #endif | |
495 | } | |
496 | ||
497 | { // test move only types | |
498 | using boost::spirit::x3::eps; | |
499 | std::vector<move_only> v; | |
500 | BOOST_TEST(test_attr("ssszs", *synth_move_only >> 'z' >> synth_move_only, v)); | |
501 | BOOST_TEST_EQ(v.size(), 4); | |
502 | } | |
503 | ||
7c673cae FG |
504 | return boost::report_errors(); |
505 | } |