1 #include <boost/detail/lightweight_test.hpp>
2 #include <boost/spirit/home/x3.hpp>
3 #include <boost/fusion/include/vector.hpp>
4 #include <boost/fusion/include/make_vector.hpp>
5 #include <boost/fusion/include/equal_to.hpp>
6 #include <boost/type_traits/is_same.hpp>
7 #include <boost/optional.hpp>
8 #include <string>
9
10 namespace x3 = boost::spirit::x3;
11
12 // just an `attr` with added type checker
13 template <typename Value, typename Expected>
14 struct checked_attr_parser : x3::attr_parser<Value>
15 {
16 using base_t = x3::attr_parser<Value>;
17
checked_attr_parserchecked_attr_parser18 checked_attr_parser(Value const& value) : base_t(value) {}
checked_attr_parserchecked_attr_parser19 checked_attr_parser(Value&& value) : base_t(std::move(value)) {}
20
21 template <typename Iterator, typename Context
22 , typename RuleContext, typename Attribute>
parsechecked_attr_parser23 bool parse(Iterator& first, Iterator const& last
24 , Context const& ctx, RuleContext& rctx, Attribute& attr_) const
25 {
26 static_assert(boost::is_same<Expected, Attribute>::value,
27 "attribute type check failed");
28 return base_t::parse(first, last, ctx, rctx, attr_);
29 }
30 };
31
32 template <typename Expected, typename Value>
33 static inline checked_attr_parser<boost::decay_t<Value>, Expected>
checked_attr(Value && value)34 checked_attr(Value&& value) { return { std::forward<Value>(value) }; }
35
36 // instantiate our type checker
37 // (checks attribute value just to be sure we are ok)
38 template <typename Value, typename Expr>
test_expr(Value const & v,Expr && expr)39 static void test_expr(Value const& v, Expr&& expr)
40 {
41 char const* it = "";
42 Value r;
43 BOOST_TEST((x3::parse(it, it, std::forward<Expr>(expr), r)));
44 BOOST_TEST((r == v));
45 }
46
47 template <typename Expr, typename Attribute>
gen_sequence(Attribute const & attribute,Expr && expr)48 static void gen_sequence(Attribute const& attribute, Expr&& expr)
49 {
50 test_expr(attribute, expr);
51 test_expr(attribute, expr >> x3::eps);
52 }
53
54 template <typename Expected, typename... ExpectedTail, typename Attribute, typename Expr, typename Value, typename... Tail>
gen_sequence(Attribute const & attribute,Expr && expr,Value const & v,Tail const &...tail)55 static void gen_sequence(Attribute const& attribute, Expr&& expr, Value const& v, Tail const&... tail)
56 {
57 gen_sequence<ExpectedTail...>(attribute, expr >> checked_attr<Expected>(v), tail...);
58 gen_sequence<ExpectedTail...>(attribute, expr >> x3::eps >> checked_attr<Expected>(v), tail...);
59 gen_sequence<ExpectedTail...>(attribute, expr >> (x3::eps >> checked_attr<Expected>(v)), tail...);
60 }
61
62 template <typename Expected, typename... ExpectedTail, typename Attribute, typename Value, typename... Tail>
gen_sequence_tests(Attribute const & attribute,Value const & v,Tail const &...tail)63 static void gen_sequence_tests(Attribute const& attribute, Value const& v, Tail const&... tail)
64 {
65 gen_sequence<ExpectedTail...>(attribute, checked_attr<Expected>(v), tail...);
66 gen_sequence<ExpectedTail...>(attribute, x3::eps >> checked_attr<Expected>(v), tail...);
67 }
68
69 template <typename Expected, typename Value>
gen_single_item_tests(Value const & v)70 static void gen_single_item_tests(Value const& v)
71 {
72 Expected attribute(v);
73 gen_sequence(attribute, checked_attr<Expected>(v));
74 gen_sequence(attribute, x3::eps >> checked_attr<Expected>(v));
75 }
76
77 template <typename Expected, typename... ExpectedTail, typename Value, typename... Tail>
gen_single_item_tests(Value const & v,Tail const &...tail)78 static void gen_single_item_tests(Value const& v, Tail const&... tail)
79 {
80 gen_single_item_tests<Expected>(v);
81 gen_single_item_tests<ExpectedTail...>(tail...);
82 }
83
84 template <typename... Expected, typename... Values>
gen_tests(Values const &...values)85 static void gen_tests(Values const&... values)
86 {
87 gen_single_item_tests<Expected...>(values...);
88
89 boost::fusion::vector<Expected...> attribute = boost::fusion::make_vector(values...);
90 gen_sequence_tests<Expected...>(attribute, values...);
91 }
92
93 template <typename... Attributes>
make_test(Attributes const &...attrs)94 void make_test(Attributes const&... attrs)
95 {
96 // I would like to place all of this in a single call
97 // but it requires tremendous amount of heap to compile
98 gen_tests<Attributes...>(attrs...);
99 gen_tests<
100 boost::optional<Attributes>...
101 , boost::fusion::vector<Attributes>...
102 >(attrs..., attrs...);
103 gen_tests<
104 boost::optional<boost::fusion::vector<Attributes>>...
105 , boost::fusion::vector<boost::optional<Attributes>>...
106 >(boost::fusion::vector<Attributes>(attrs)..., attrs...);
107 }
108
main()109 int main()
110 {
111 make_test<int, std::string>(123, "hello");
112 return boost::report_errors();
113 }
114