spirit/classic/test/traverse_tests.cpp

518 lines
16 KiB
C++

/*=============================================================================
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 <boost/detail/lightweight_test.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <boost/config.hpp>
#include <boost/static_assert.hpp>
#ifdef BOOST_NO_STRINGSTREAM
#include <strstream>
#define OSSTREAM std::ostrstream
std::string GETSTRING(std::ostrstream& ss)
{
ss << ends;
std::string rval = ss.str();
ss.freeze(false);
return rval;
}
#else
#include <sstream>
#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 <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_assign_actor.hpp>
#include <boost/spirit/include/classic_meta.hpp>
using namespace BOOST_SPIRIT_CLASSIC_NS;
typedef ref_value_actor<char, assign_action> assign_actor;
///////////////////////////////////////////////////////////////////////////////
//
// Test identity transformation
//
///////////////////////////////////////////////////////////////////////////////
void
traverse_identity_tests()
{
// test type equality
typedef sequence<chlit<char>, chlit<char> > test_sequence1_t;
BOOST_STATIC_ASSERT((
::boost::is_same<
test_sequence1_t,
post_order::result<identity_transform, test_sequence1_t>::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<chlit<> >,
action<chlit<>, assign_actor>
>,
chlit<>
>,
optional<chlit<> >
> test_sequence2_t;
BOOST_STATIC_ASSERT((
::boost::is_same<
test_sequence2_t,
post_order::result<identity_transform, test_sequence2_t>::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<chlit<> >,
sequence<
action<chlit<>, assign_actor>,
chlit<>
>
>,
optional<chlit<char> >
> test_sequence3_t;
BOOST_STATIC_ASSERT((
::boost::is_same<
test_sequence3_t,
post_order::result<identity_transform, test_sequence3_t>::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<chlit<> >,
sequence<
action<chlit<>, assign_actor>,
sequence<
chlit<>,
optional<chlit<> >
>
>
> test_sequence4_t;
BOOST_STATIC_ASSERT((
::boost::is_same<
test_sequence4_t,
post_order::result<identity_transform, test_sequence4_t>::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<chlit<> >,
sequence<
sequence<
action<chlit<>, assign_actor>,
chlit<>
>,
optional<chlit<> >
>
> test_sequence5_t;
BOOST_STATIC_ASSERT((
::boost::is_same<
test_sequence5_t,
post_order::result<identity_transform, test_sequence5_t>::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<chlit<> >,
action<chlit<>, assign_actor>
>,
sequence<
chlit<>,
optional<chlit<> >
>
> test_sequence6_t;
BOOST_STATIC_ASSERT((
::boost::is_same<
test_sequence6_t,
post_order::result<identity_transform, test_sequence6_t>::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<trace_identity_transform> {
public:
typedef trace_identity_transform self_t;
typedef transform_policies<trace_identity_transform> base_t;
template <typename ParserT, typename EnvT>
typename parser_traversal_plain_result<self_t, ParserT, EnvT>::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 UnaryT, typename SubjectT, typename EnvT>
typename parser_traversal_unary_result<self_t, UnaryT, SubjectT, EnvT>::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 ActionT, typename SubjectT, typename EnvT>
typename parser_traversal_action_result<self_t, ActionT, SubjectT, EnvT>::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 BinaryT, typename LeftT, typename RightT, typename EnvT>
typename parser_traversal_binary_result<self_t, BinaryT, LeftT, RightT, EnvT>::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<std::string> const &get_output() const { return traces; }
private:
mutable std::vector<std::string> traces;
};
template <typename ParserT>
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<std::string>::const_iterator it = trace_vector.get_output().begin();
std::vector<std::string>::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)
std::cerr << "node in question: " << *it << std::endl;
BOOST_TEST(std::find(first, first + cnt, *it) != first + cnt);
}
// re-find all expected strings in the vector of trace strings
std::vector<std::string>::const_iterator begin = trace_vector.get_output().begin();
char const *expected = first[0];
for (size_t i = 0; i < cnt - 1; expected = first[++i])
{
if (std::find(begin, end, std::string(expected)) == end)
std::cerr << "node in question: " << expected << std::endl;
BOOST_TEST(std::find(begin, end, std::string(expected)) != end);
}
}
#if defined(_countof)
#undef _countof
#endif
#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();
}