518 lines
16 KiB
C++
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();
|
|
}
|
|
|