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