1 /*=============================================================================
2     Copyright (c) 2001-2015 Joel de Guzman
3     Copyright (c) 2001-2011 Hartmut Kaiser
4 
5     Distributed under the Boost Software License, Version 1.0. (See accompanying
6     file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 =============================================================================*/
8 #include <boost/detail/lightweight_test.hpp>
9 #include <boost/spirit/home/x3.hpp>
10 #include <boost/fusion/include/adapt_struct.hpp>
11 #include <boost/variant.hpp>
12 #include <boost/fusion/include/vector.hpp>
13 #include <boost/fusion/include/at.hpp>
14 
15 #include <string>
16 #include <iostream>
17 #include <vector>
18 #include "test.hpp"
19 
20 struct di_ignore
21 {
22     std::string text;
23 };
24 
25 struct di_include
26 {
27     std::string FileName;
28 };
29 
30 BOOST_FUSION_ADAPT_STRUCT(di_ignore,
31     text
32 )
33 
34 BOOST_FUSION_ADAPT_STRUCT(di_include,
35     FileName
36 )
37 
38 struct undefined {};
39 
40 
41 struct stationary : boost::noncopyable
42 {
stationarystationary43     explicit stationary(int i) : val{i} {}
44     // TODO: fix unneeded self move in alternative
operator =stationary45     stationary& operator=(stationary&&) { std::abort(); }
operator =stationary46     stationary& operator=(int i) { val = i; return *this; }
47 
48     int val;
49 };
50 
51 
52 int
main()53 main()
54 {
55     using spirit_test::test;
56     using spirit_test::test_attr;
57 
58     using boost::spirit::x3::attr;
59     using boost::spirit::x3::char_;
60     using boost::spirit::x3::int_;
61     using boost::spirit::x3::lit;
62     using boost::spirit::x3::unused_type;
63     using boost::spirit::x3::unused;
64     using boost::spirit::x3::omit;
65     using boost::spirit::x3::eps;
66 
67 
68     {
69         BOOST_TEST((test("a", char_ | char_)));
70         BOOST_TEST((test("x", lit('x') | lit('i'))));
71         BOOST_TEST((test("i", lit('x') | lit('i'))));
72         BOOST_TEST((!test("z", lit('x') | lit('o'))));
73         BOOST_TEST((test("rock", lit("rock") | lit("roll"))));
74         BOOST_TEST((test("roll", lit("rock") | lit("roll"))));
75         BOOST_TEST((test("rock", lit("rock") | int_)));
76         BOOST_TEST((test("12345", lit("rock") | int_)));
77     }
78 
79     {
80         typedef boost::variant<undefined, int, char> attr_type;
81         attr_type v;
82 
83         BOOST_TEST((test_attr("12345", int_ | char_, v)));
84         BOOST_TEST(boost::get<int>(v) == 12345);
85 
86         BOOST_TEST((test_attr("12345", lit("rock") | int_ | char_, v)));
87         BOOST_TEST(boost::get<int>(v) == 12345);
88 
89         v = attr_type();
90         BOOST_TEST((test_attr("rock", lit("rock") | int_ | char_, v)));
91         BOOST_TEST(v.which() == 0);
92 
93         BOOST_TEST((test_attr("x", lit("rock") | int_ | char_, v)));
94         BOOST_TEST(boost::get<char>(v) == 'x');
95     }
96 
97     {   // Make sure that we are using the actual supplied attribute types
98         // from the variant and not the expected type.
99         boost::variant<int, std::string> v;
100         BOOST_TEST((test_attr("12345", int_ | +char_, v)));
101         BOOST_TEST(boost::get<int>(v) == 12345);
102 
103         BOOST_TEST((test_attr("abc", int_ | +char_, v)));
104         BOOST_TEST(boost::get<std::string>(v) == "abc");
105 
106         BOOST_TEST((test_attr("12345", +char_ | int_, v)));
107         BOOST_TEST(boost::get<std::string>(v) == "12345");
108     }
109 
110     {
111         unused_type x;
112         BOOST_TEST((test_attr("rock", lit("rock") | lit('x'), x)));
113     }
114 
115     {
116         // test if alternatives with all components having unused
117         // attributes have an unused attribute
118 
119         using boost::fusion::vector;
120         using boost::fusion::at_c;
121 
122         vector<char, char> v;
123         BOOST_TEST((test_attr("abc",
124             char_ >> (omit[char_] | omit[char_]) >> char_, v)));
125         BOOST_TEST((at_c<0>(v) == 'a'));
126         BOOST_TEST((at_c<1>(v) == 'c'));
127     }
128 
129     {
130         // Test that we can still pass a "compatible" attribute to
131         // an alternate even if its "expected" attribute is unused type.
132 
133         std::string s;
134         BOOST_TEST((test_attr("...", *(char_('.') | char_(',')), s)));
135         BOOST_TEST(s == "...");
136     }
137 
138     {   // make sure collapsing eps works as expected
139         // (compile check only)
140 
141         using boost::spirit::x3::rule;
142         using boost::spirit::x3::eps;
143         using boost::spirit::x3::_attr;
144         using boost::spirit::x3::_val;
145 
146         rule<class r1, wchar_t> r1;
147         rule<class r2, wchar_t> r2;
148         rule<class r3, wchar_t> r3;
149 
150         auto f = [&](auto& ctx){ _val(ctx) = _attr(ctx); };
151 
152         r3  = ((eps >> r1))[f];
153         r3  = ((r1) | r2)[f];
154         r3 = ((eps >> r1) | r2);
155     }
156 
157     {
158         std::string s;
159         using boost::spirit::x3::eps;
160 
161         // test having a variant<container, ...>
162         BOOST_TEST( (test_attr("a,b", (char_ % ',') | eps, s )) );
163         BOOST_TEST(s == "ab");
164     }
165 
166     {
167         using boost::spirit::x3::eps;
168 
169         // testing a sequence taking a container as attribute
170         std::string s;
171         BOOST_TEST( (test_attr("abc,a,b,c",
172             char_ >> char_ >> (char_ % ','), s )) );
173         BOOST_TEST(s == "abcabc");
174 
175         // test having an optional<container> inside a sequence
176         s.erase();
177         BOOST_TEST( (test_attr("ab",
178             char_ >> char_ >> -(char_ % ','), s )) );
179         BOOST_TEST(s == "ab");
180 
181         // test having a variant<container, ...> inside a sequence
182         s.erase();
183         BOOST_TEST( (test_attr("ab",
184             char_ >> char_ >> ((char_ % ',') | eps), s )) );
185         BOOST_TEST(s == "ab");
186         s.erase();
187         BOOST_TEST( (test_attr("abc",
188             char_ >> char_ >> ((char_ % ',') | eps), s )) );
189         BOOST_TEST(s == "abc");
190     }
191 
192     {
193         //compile test only (bug_march_10_2011_8_35_am)
194         typedef boost::variant<double, std::string> value_type;
195 
196         using boost::spirit::x3::rule;
197         using boost::spirit::x3::eps;
198 
199         rule<class r1, value_type> r1;
200         auto r1_ = r1 = r1 | eps; // left recursive!
201 
202         unused = r1_; // silence unused local warning
203     }
204 
205     {
206         using boost::spirit::x3::rule;
207         typedef boost::variant<di_ignore, di_include> d_line;
208 
209         rule<class ignore, di_ignore> ignore;
210         rule<class include, di_include> include;
211         rule<class line, d_line> line;
212 
213         auto start =
214             line = include | ignore;
215 
216         unused = start; // silence unused local warning
217     }
218 
219     // single-element fusion vector tests
220     {
221         boost::fusion::vector<boost::variant<int, std::string>> fv;
222         BOOST_TEST((test_attr("12345", int_ | +char_, fv)));
223         BOOST_TEST(boost::get<int>(boost::fusion::at_c<0>(fv)) == 12345);
224 
225         boost::fusion::vector<boost::variant<int, std::string>> fvi;
226         BOOST_TEST((test_attr("12345", int_ | int_, fvi)));
227         BOOST_TEST(boost::get<int>(boost::fusion::at_c<0>(fvi)) == 12345);
228     }
229 
230     // alternative over single element sequences as part of another sequence
231     {
232         auto  key1 = lit("long") >> attr(long());
233         auto  key2 = lit("char") >> attr(char());
234         auto  keys = key1 | key2;
235         auto pair = keys >> lit("=") >> +char_;
236 
237         boost::fusion::deque<boost::variant<long, char>, std::string> attr_;
238 
239         BOOST_TEST(test_attr("long=ABC", pair, attr_));
240         BOOST_TEST(boost::get<long>(&boost::fusion::front(attr_)) != nullptr);
241         BOOST_TEST(boost::get<char>(&boost::fusion::front(attr_)) == nullptr);
242     }
243 
244     { // ensure no unneded synthesization, copying and moving occured
245         auto p = '{' >> int_ >> '}';
246 
247         stationary st { 0 };
248         BOOST_TEST(test_attr("{42}", p | eps | p, st));
249         BOOST_TEST_EQ(st.val, 42);
250     }
251 
252     return boost::report_errors();
253 }
254