/*============================================================================= Copyright (c) 2002-2003 Hartmut Kaiser http://spirit.sourceforge.net/ Use, modification and distribution is subject to 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) =============================================================================*/ /////////////////////////////////////////////////////////////////////////////// // // Traversal tests // /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #ifdef BOOST_NO_STRINGSTREAM #include #define OSSTREAM std::ostrstream std::string GETSTRING(std::ostrstream& ss) { ss << ends; std::string rval = ss.str(); ss.freeze(false); return rval; } #else #include #define GETSTRING(ss) ss.str() #define OSSTREAM std::ostringstream #endif #ifndef BOOST_SPIRIT_DEBUG #define BOOST_SPIRIT_DEBUG // needed for parser_name functions #endif #include #include #include using namespace std; using namespace boost::spirit; typedef ref_value_actor assign_actor; /////////////////////////////////////////////////////////////////////////////// // // Test identity transformation // /////////////////////////////////////////////////////////////////////////////// void traverse_identity_tests() { // test type equality typedef sequence, chlit > test_sequence1_t; BOOST_STATIC_ASSERT(( ::boost::is_same< test_sequence1_t, post_order::result::type >::value )); // test (rough) runtime equality BOOST_TEST( parse( "ab", post_order::traverse(identity_transform(), ch_p('a') >> 'b') ).full ); BOOST_TEST( !parse( "ba", post_order::traverse(identity_transform(), ch_p('a') >> 'b') ).hit ); /////////////////////////////////////////////////////////////////////////// BOOST_TEST( !parse( "cba", post_order::traverse( identity_transform(), ch_p('a') >> 'b' >> 'c' ) ).hit ); /////////////////////////////////////////////////////////////////////////////// // Test more complex sequences char c; /////////////////////////////////////////////////////////////////////////////// // test: ((a >> b) >> c) >> d typedef sequence< sequence< sequence< kleene_star >, action, assign_actor> >, chlit<> >, optional > > test_sequence2_t; BOOST_STATIC_ASSERT(( ::boost::is_same< test_sequence2_t, post_order::result::type >::value )); c = 0; BOOST_TEST( parse( "aabcd", post_order::traverse( identity_transform(), ((*ch_p('a') >> ch_p('b')[assign_a(c)]) >> 'c') >> !ch_p('d') ) ).full ); BOOST_TEST(c == 'b'); /////////////////////////////////////////////////////////////////////////////// // test: (a >> (b >> c)) >> d typedef sequence< sequence< kleene_star >, sequence< action, assign_actor>, chlit<> > >, optional > > test_sequence3_t; BOOST_STATIC_ASSERT(( ::boost::is_same< test_sequence3_t, post_order::result::type >::value )); c = 0; BOOST_TEST( parse( "aabcd", post_order::traverse( identity_transform(), (*ch_p('a') >> (ch_p('b')[assign_a(c)] >> 'c')) >> !ch_p('d') ) ).full ); BOOST_TEST(c == 'b'); /////////////////////////////////////////////////////////////////////////////// // test: a >> (b >> (c >> d)) typedef sequence< kleene_star >, sequence< action, assign_actor>, sequence< chlit<>, optional > > > > test_sequence4_t; BOOST_STATIC_ASSERT(( ::boost::is_same< test_sequence4_t, post_order::result::type >::value )); c = 0; BOOST_TEST( parse( "aabcd", post_order::traverse( identity_transform(), *ch_p('a') >> (ch_p('b')[assign_a(c)] >> ('c' >> !ch_p('d'))) ) ).full ); BOOST_TEST(c == 'b'); /////////////////////////////////////////////////////////////////////////////// // test: a >> ((b >> c) >> d) typedef sequence< kleene_star >, sequence< sequence< action, assign_actor>, chlit<> >, optional > > > test_sequence5_t; BOOST_STATIC_ASSERT(( ::boost::is_same< test_sequence5_t, post_order::result::type >::value )); c = 0; BOOST_TEST( parse( "aabcd", post_order::traverse( identity_transform(), *ch_p('a') >> ((ch_p('b')[assign_a(c)] >> 'c') >> !ch_p('d')) ) ).full ); BOOST_TEST(c == 'b'); /////////////////////////////////////////////////////////////////////////////// // test: (a >> b) >> (c >> d) typedef sequence< sequence< kleene_star >, action, assign_actor> >, sequence< chlit<>, optional > > > test_sequence6_t; BOOST_STATIC_ASSERT(( ::boost::is_same< test_sequence6_t, post_order::result::type >::value )); c = 0; BOOST_TEST( parse( "aabcd", post_order::traverse( identity_transform(), (*ch_p('a') >> ch_p('b')[assign_a(c)]) >> ('c' >> !ch_p('d')) ) ).full ); BOOST_TEST(c == 'b'); } /////////////////////////////////////////////////////////////////////////////// // // The following is a tracing identity_transform traverse metafunction // /////////////////////////////////////////////////////////////////////////////// class trace_identity_transform : public transform_policies { public: typedef trace_identity_transform self_t; typedef transform_policies base_t; template typename parser_traversal_plain_result::type generate_plain(ParserT const &parser_, EnvT const &env) const { OSSTREAM strout; strout << EnvT::node << ": plain (" << EnvT::level << ", " << EnvT::index << "): " << parser_name(parser_); traces.push_back(GETSTRING(strout)); return this->base_t::generate_plain(parser_, env); } template typename parser_traversal_unary_result::type generate_unary(UnaryT const &unary_, SubjectT const &subject_, EnvT const &env) const { OSSTREAM strout; strout << EnvT::node << ": unary (" << EnvT::level << "): " << parser_name(unary_); traces.push_back(GETSTRING(strout)); return this->base_t::generate_unary(unary_, subject_, env); } template typename parser_traversal_action_result::type generate_action(ActionT const &action_, SubjectT const &subject_, EnvT const &env) const { OSSTREAM strout; strout << EnvT::node << ": action(" << EnvT::level << "): " << parser_name(action_); traces.push_back(GETSTRING(strout)); return this->base_t::generate_action(action_, subject_, env); } template typename parser_traversal_binary_result::type generate_binary(BinaryT const &binary_, LeftT const& left_, RightT const& right_, EnvT const &env) const { OSSTREAM strout; strout << EnvT::node << ": binary(" << EnvT::level << "): " << parser_name(binary_); traces.push_back(GETSTRING(strout)); return this->base_t::generate_binary(binary_, left_, right_, env); } std::vector const &get_output() const { return traces; } private: mutable std::vector traces; }; template void post_order_trace_test(ParserT const &parser_, char const *first[], size_t cnt) { // traverse trace_identity_transform trace_vector; post_order::traverse(trace_vector, parser_); // The following two re-find loops ensure, that both string arrays contain the // same entries, only their order may differ. The differences in the trace // string order is based on the different parameter evaluation order as it is // implemented by different compilers. // re-find all trace strings in the array of expected strings std::vector::const_iterator it = trace_vector.get_output().begin(); std::vector::const_iterator end = trace_vector.get_output().end(); BOOST_TEST(cnt == trace_vector.get_output().size()); for (/**/; it != end; ++it) { if (std::find(first, first + cnt, *it) == first + cnt) cerr << "node in question: " << *it << endl; BOOST_TEST(std::find(first, first + cnt, *it) != first + cnt); } // re-find all expected strings in the vector of trace strings std::vector::const_iterator begin = trace_vector.get_output().begin(); char const *expected = first[0]; for (size_t i = 0; i < cnt; expected = first[++i]) { if (std::find(begin, end, std::string(expected)) == end) cerr << "node in question: " << expected << endl; BOOST_TEST(std::find(begin, end, std::string(expected)) != end); } } #define _countof(x) (sizeof(x)/sizeof(x[0])) void traverse_trace_tests() { const char *test_result1[] = { "0: plain (1, 0): chlit('a')", "1: plain (1, 1): chlit('b')", "2: binary(0): sequence[chlit('a'), chlit('b')]", }; post_order_trace_test( ch_p('a') >> 'b', test_result1, _countof(test_result1) ); char c = 0; // test: ((a >> b) >> c) >> d const char *test_result2[] = { "0: plain (4, 0): chlit('a')", "1: unary (3): kleene_star[chlit('a')]", "2: plain (4, 1): chlit('b')", "3: action(3): action[chlit('b')]", "4: binary(2): sequence[kleene_star[chlit('a')], action[chlit('b')]]", "5: plain (2, 2): chlit('c')", "6: binary(1): sequence[sequence[kleene_star[chlit('a')], action[chlit('b')]], chlit('c')]", "7: plain (2, 3): chlit('d')", "8: unary (1): optional[chlit('d')]", "9: binary(0): sequence[sequence[sequence[kleene_star[chlit('a')], action[chlit('b')]], chlit('c')], optional[chlit('d')]]", }; post_order_trace_test( ((*ch_p('a') >> ch_p('b')[assign_a(c)]) >> 'c') >> !ch_p('d'), test_result2, _countof(test_result2) ); // test: (a >> (b >> c)) >> d const char *test_result3[] = { "0: plain (3, 0): chlit('a')", "1: unary (2): kleene_star[chlit('a')]", "2: plain (4, 1): chlit('b')", "3: action(3): action[chlit('b')]", "4: plain (3, 2): chlit('c')", "5: binary(2): sequence[action[chlit('b')], chlit('c')]", "6: binary(1): sequence[kleene_star[chlit('a')], sequence[action[chlit('b')], chlit('c')]]", "7: plain (2, 3): chlit('d')", "8: unary (1): optional[chlit('d')]", "9: binary(0): sequence[sequence[kleene_star[chlit('a')], sequence[action[chlit('b')], chlit('c')]], optional[chlit('d')]]", }; post_order_trace_test( (*ch_p('a') >> (ch_p('b')[assign_a(c)] >> 'c')) >> !ch_p('d'), test_result3, _countof(test_result3) ); // test: a >> (b >> (c >> d)) const char *test_result4[] = { "0: plain (2, 0): chlit('a')", "1: unary (1): kleene_star[chlit('a')]", "2: plain (3, 1): chlit('b')", "3: action(2): action[chlit('b')]", "4: plain (3, 2): chlit('c')", "5: plain (4, 3): chlit('d')", "6: unary (3): optional[chlit('d')]", "7: binary(2): sequence[chlit('c'), optional[chlit('d')]]", "8: binary(1): sequence[action[chlit('b')], sequence[chlit('c'), optional[chlit('d')]]]", "9: binary(0): sequence[kleene_star[chlit('a')], sequence[action[chlit('b')], sequence[chlit('c'), optional[chlit('d')]]]]", }; post_order_trace_test( *ch_p('a') >> (ch_p('b')[assign_a(c)] >> ('c' >> !ch_p('d'))), test_result4, _countof(test_result4) ); // test: a >> ((b >> c) >> d) const char *test_result5[] = { "0: plain (2, 0): chlit('a')", "1: unary (1): kleene_star[chlit('a')]", "2: plain (4, 1): chlit('b')", "3: action(3): action[chlit('b')]", "4: plain (3, 2): chlit('c')", "5: binary(2): sequence[action[chlit('b')], chlit('c')]", "6: plain (3, 3): chlit('d')", "7: unary (2): optional[chlit('d')]", "8: binary(1): sequence[sequence[action[chlit('b')], chlit('c')], optional[chlit('d')]]", "9: binary(0): sequence[kleene_star[chlit('a')], sequence[sequence[action[chlit('b')], chlit('c')], optional[chlit('d')]]]", }; post_order_trace_test( *ch_p('a') >> ((ch_p('b')[assign_a(c)] >> 'c') >> !ch_p('d')), test_result5, _countof(test_result5) ); // test: (a >> b) >> (c >> d) const char *test_result6[] = { "0: plain (3, 0): chlit('a')", "1: unary (2): kleene_star[chlit('a')]", "2: plain (3, 1): chlit('b')", "3: action(2): action[chlit('b')]", "4: binary(1): sequence[kleene_star[chlit('a')], action[chlit('b')]]", "5: plain (2, 2): chlit('c')", "6: plain (3, 3): chlit('d')", "7: unary (2): optional[chlit('d')]", "8: binary(1): sequence[chlit('c'), optional[chlit('d')]]", "9: binary(0): sequence[sequence[kleene_star[chlit('a')], action[chlit('b')]], sequence[chlit('c'), optional[chlit('d')]]]", }; post_order_trace_test( (*ch_p('a') >> ch_p('b')[assign_a(c)]) >> ('c' >> !ch_p('d')), test_result6, _countof(test_result6) ); } /////////////////////////////////////////////////////////////////////////////// // // Main // /////////////////////////////////////////////////////////////////////////////// int main() { traverse_identity_tests(); traverse_trace_tests(); return boost::report_errors(); }