| /*============================================================================= |
| Copyright (c) 2008 Francois Barel |
| |
| Distributed under the Boost Software License, Version 1.0. (See accompanying |
| file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| =============================================================================*/ |
| #include <boost/detail/lightweight_test.hpp> |
| #include <boost/type_traits/is_same.hpp> |
| |
| #include <boost/spirit/include/qi_operator.hpp> |
| #include <boost/spirit/include/qi_char.hpp> |
| #include <boost/spirit/include/phoenix_core.hpp> |
| #include <boost/spirit/include/phoenix_operator.hpp> |
| |
| #include <iterator> |
| #include "test.hpp" |
| |
| |
| namespace testns |
| { |
| |
| BOOST_SPIRIT_TERMINAL_EX( ops ) |
| |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Parsers |
| /////////////////////////////////////////////////////////////////////////// |
| |
| template <typename T1> |
| struct ops_1_parser |
| : boost::spirit::qi::primitive_parser<ops_1_parser<T1> > |
| { |
| ops_1_parser(T1 t1) |
| : t1(t1) |
| {} |
| |
| template <typename Context, typename Iterator> |
| struct attribute |
| { |
| typedef int type; // Number of parsed chars. |
| }; |
| |
| template <typename Iterator, typename Context |
| , typename Skipper, typename Attribute> |
| bool parse(Iterator& first, Iterator const& last |
| , Context& /*context*/, Skipper const& skipper |
| , Attribute& attr) const |
| { |
| boost::spirit::qi::skip_over(first, last, skipper); |
| |
| int count = 0; |
| |
| Iterator it = first; |
| typedef typename std::iterator_traits<Iterator>::value_type Char; |
| for (T1 t = 0; t < t1; t++, count++) |
| if (it == last || *it++ != Char('+')) |
| return false; |
| |
| boost::spirit::traits::assign_to(count, attr); |
| first = it; |
| return true; |
| } |
| |
| template <typename Context> |
| boost::spirit::qi::info what(Context& /*context*/) const |
| { |
| return boost::spirit::qi::info("ops_1"); |
| } |
| |
| const T1 t1; |
| |
| private: |
| // silence MSVC warning C4512: assignment operator could not be generated |
| ops_1_parser& operator= (ops_1_parser const&); |
| }; |
| |
| template <typename T1, typename T2> |
| struct ops_2_parser |
| : boost::spirit::qi::primitive_parser<ops_2_parser<T1, T2> > |
| { |
| ops_2_parser(T1 t1, T2 t2) |
| : t1(t1) |
| , t2(t2) |
| {} |
| |
| template <typename Context, typename Iterator> |
| struct attribute |
| { |
| typedef int type; // Number of parsed chars. |
| }; |
| |
| template <typename Iterator, typename Context |
| , typename Skipper, typename Attribute> |
| bool parse(Iterator& first, Iterator const& last |
| , Context& /*context*/, Skipper const& skipper |
| , Attribute& attr) const |
| { |
| boost::spirit::qi::skip_over(first, last, skipper); |
| |
| int count = 0; |
| |
| Iterator it = first; |
| typedef typename std::iterator_traits<Iterator>::value_type Char; |
| for (T1 t = 0; t < t1; t++, count++) |
| if (it == last || *it++ != Char('+')) |
| return false; |
| for (T2 t = 0; t < t2; t++, count++) |
| if (it == last || *it++ != Char('-')) |
| return false; |
| |
| boost::spirit::traits::assign_to(count, attr); |
| first = it; |
| return true; |
| } |
| |
| template <typename Context> |
| boost::spirit::qi::info what(Context& /*context*/) const |
| { |
| return boost::spirit::qi::info("ops_2"); |
| } |
| |
| const T1 t1; |
| const T2 t2; |
| |
| private: |
| // silence MSVC warning C4512: assignment operator could not be generated |
| ops_2_parser& operator= (ops_2_parser const&); |
| }; |
| |
| template <typename T1, typename T2, typename T3> |
| struct ops_3_parser |
| : boost::spirit::qi::primitive_parser<ops_3_parser<T1, T2, T3> > |
| { |
| ops_3_parser(T1 t1, T2 t2, T3 t3) |
| : t1(t1) |
| , t2(t2) |
| , t3(t3) |
| {} |
| |
| template <typename Context, typename Iterator> |
| struct attribute |
| { |
| typedef int type; // Number of parsed chars. |
| }; |
| |
| template <typename Iterator, typename Context |
| , typename Skipper, typename Attribute> |
| bool parse(Iterator& first, Iterator const& last |
| , Context& /*context*/, Skipper const& skipper |
| , Attribute& attr) const |
| { |
| boost::spirit::qi::skip_over(first, last, skipper); |
| |
| int count = 0; |
| |
| Iterator it = first; |
| typedef typename std::iterator_traits<Iterator>::value_type Char; |
| for (T1 t = 0; t < t1; t++, count++) |
| if (it == last || *it++ != Char('+')) |
| return false; |
| for (T2 t = 0; t < t2; t++, count++) |
| if (it == last || *it++ != Char('-')) |
| return false; |
| for (T3 t = 0; t < t3; t++, count++) |
| if (it == last || *it++ != Char('*')) |
| return false; |
| |
| boost::spirit::traits::assign_to(count, attr); |
| first = it; |
| return true; |
| } |
| |
| template <typename Context> |
| boost::spirit::qi::info what(Context& /*context*/) const |
| { |
| return boost::spirit::qi::info("ops_3"); |
| } |
| |
| const T1 t1; |
| const T2 t2; |
| const T3 t3; |
| |
| private: |
| // silence MSVC warning C4512: assignment operator could not be generated |
| ops_3_parser& operator= (ops_3_parser const&); |
| }; |
| |
| } |
| |
| |
| namespace boost { namespace spirit |
| { |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Enablers |
| /////////////////////////////////////////////////////////////////////////// |
| |
| template <typename T1> |
| struct use_terminal<qi::domain |
| , terminal_ex<testns::tag::ops, fusion::vector1<T1> > > |
| : mpl::true_ {}; |
| |
| template <typename T1, typename T2> |
| struct use_terminal<qi::domain |
| , terminal_ex<testns::tag::ops, fusion::vector2<T1, T2> > > |
| : mpl::true_ {}; |
| |
| template <typename T1, typename T2, typename T3> |
| struct use_terminal<qi::domain |
| , terminal_ex<testns::tag::ops, fusion::vector3<T1, T2, T3> > > |
| : mpl::true_ {}; |
| |
| template <> |
| struct use_lazy_terminal<qi::domain, testns::tag::ops, 1> |
| : mpl::true_ {}; |
| |
| template <> |
| struct use_lazy_terminal<qi::domain, testns::tag::ops, 2> |
| : mpl::true_ {}; |
| |
| template <> |
| struct use_lazy_terminal<qi::domain, testns::tag::ops, 3> |
| : mpl::true_ {}; |
| |
| }} |
| |
| namespace boost { namespace spirit { namespace qi |
| { |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Parser generators: make_xxx function (objects) |
| /////////////////////////////////////////////////////////////////////////// |
| |
| template <typename Modifiers, typename T1> |
| struct make_primitive< |
| terminal_ex<testns::tag::ops, fusion::vector1<T1> > |
| , Modifiers> |
| { |
| typedef testns::ops_1_parser<T1> result_type; |
| template <typename Terminal> |
| result_type operator()(const Terminal& term, unused_type) const |
| { |
| return result_type( |
| fusion::at_c<0>(term.args) |
| ); |
| } |
| }; |
| |
| template <typename Modifiers, typename T1, typename T2> |
| struct make_primitive< |
| terminal_ex<testns::tag::ops, fusion::vector2<T1, T2> > |
| , Modifiers> |
| { |
| typedef testns::ops_2_parser<T1, T2> result_type; |
| template <typename Terminal> |
| result_type operator()(const Terminal& term, unused_type) const |
| { |
| return result_type( |
| fusion::at_c<0>(term.args) |
| , fusion::at_c<1>(term.args) |
| ); |
| } |
| }; |
| |
| template <typename Modifiers, typename T1, typename T2, typename T3> |
| struct make_primitive< |
| terminal_ex<testns::tag::ops, fusion::vector3<T1, T2, T3> > |
| , Modifiers> |
| { |
| typedef testns::ops_3_parser<T1, T2, T3> result_type; |
| template <typename Terminal> |
| result_type operator()(const Terminal& term, unused_type) const |
| { |
| return result_type( |
| fusion::at_c<0>(term.args) |
| , fusion::at_c<1>(term.args) |
| , fusion::at_c<2>(term.args) |
| ); |
| } |
| }; |
| |
| }}} |
| |
| |
| namespace testns |
| { |
| template <typename T1, typename T> |
| void check_type_1(const T& /*t*/) |
| { |
| namespace fusion = boost::fusion; |
| BOOST_STATIC_ASSERT(( boost::is_same<T |
| , typename boost::spirit::terminal<testns::tag::ops>::result<T1>::type >::value )); |
| } |
| |
| template <typename T1, typename T2, typename T> |
| void check_type_2(const T& /*t*/) |
| { |
| namespace fusion = boost::fusion; |
| BOOST_STATIC_ASSERT(( boost::is_same<T |
| , typename boost::spirit::terminal<testns::tag::ops>::result<T1, T2>::type >::value )); |
| } |
| |
| template <typename T1, typename T2, typename T3, typename T> |
| void check_type_3(const T& /*t*/) |
| { |
| namespace fusion = boost::fusion; |
| BOOST_STATIC_ASSERT(( boost::is_same<T |
| , typename boost::spirit::terminal<testns::tag::ops>::result<T1, T2, T3>::type >::value )); |
| } |
| } |
| |
| |
| int |
| main() |
| { |
| using spirit_test::test_attr; |
| using spirit_test::test; |
| |
| using boost::phoenix::val; |
| using boost::phoenix::actor; |
| using boost::phoenix::value; |
| |
| using testns::ops; |
| using testns::check_type_1; |
| using testns::check_type_2; |
| using testns::check_type_3; |
| |
| { // immediate args |
| int c = 0; |
| #define IP1 ops(2) |
| check_type_1<int>(IP1); |
| BOOST_TEST(test_attr("++/", IP1 >> '/', c) && c == 2); |
| |
| c = 0; |
| #define IP2 ops(2, 3) |
| check_type_2<int, int>(IP2); |
| BOOST_TEST(test_attr("++---/", IP2 >> '/', c) && c == 5); |
| |
| c = 0; |
| #define IP3 ops(2, 3, 4) |
| check_type_3<int, int, int>(IP3); |
| BOOST_TEST(!test("++---***/", IP3 >> '/')); |
| #define IP4 ops(2, 3, 4) |
| check_type_3<int, int, int>(IP4); |
| BOOST_TEST(test_attr("++---****/", IP4 >> '/', c) && c == 9); |
| } |
| |
| { // all lazy args |
| int c = 0; |
| #define LP1 ops(val(1)) |
| check_type_1<actor<value<int> > >(LP1); |
| BOOST_TEST(test_attr("+/", LP1 >> '/', c) && c == 1); |
| |
| c = 0; |
| #define LP2 ops(val(1), val(4)) |
| check_type_2<actor<value<int> >, actor<value<int> > >(LP2); |
| BOOST_TEST(test_attr("+----/", LP2 >> '/', c) && c == 5); |
| |
| c = 0; |
| #define LP3 ops(val((char)2), val(3.), val(4)) |
| check_type_3<actor<value<char> >, actor<value<double> >, actor<value<int> > >(LP3); |
| BOOST_TEST(!test("++---***/", LP3 >> '/')); |
| #define LP4 ops(val(1), val(2), val(3)) |
| check_type_3<actor<value<int> >, actor<value<int> >, actor<value<int> > >(LP4); |
| BOOST_TEST(test_attr("+--***/", LP4 >> '/', c) && c == 6); |
| } |
| |
| { // mixed immediate and lazy args |
| namespace fusion = boost::fusion; |
| namespace phx = boost::phoenix; |
| |
| int c = 0; |
| #define MP1 ops(val(3), 2) |
| check_type_2<actor<value<int> >, int>(MP1); |
| BOOST_TEST(test_attr("+++--/", MP1 >> '/', c) && c == 5); |
| |
| c = 0; |
| #define MP2 ops(4, val(1)) |
| check_type_2<int, actor<value<int> > >(MP2); |
| BOOST_TEST(test_attr("++++-/", MP2 >> '/', c) && c == 5); |
| |
| c = 0; |
| #define MP3 ops(2, val(2), val(2)) |
| check_type_3<int, actor<value<int> >, actor<value<int> > >(MP3); |
| BOOST_TEST(!test("++-**/", MP3 >> '/')); |
| #define MP4 ops(2, val(2), 2) |
| check_type_3<int, actor<value<int> >, int>(MP4); |
| BOOST_TEST(test_attr("++--**/", MP4 >> '/', c) && c == 6); |
| |
| c = 0; |
| #define MP5 ops(val(5) - val(3), 2, val(2)) |
| check_type_3<actor<phx::composite<phx::minus_eval, fusion::vector<value<int>, value<int> > > >, int, actor<value<int> > >(MP5); |
| BOOST_TEST(test_attr("++--**/", MP5 >> '/', c) && c == 6); |
| } |
| |
| return boost::report_errors(); |
| } |
| |