0ac381dcf3
[SVN r44371]
384 lines
9.3 KiB
C++
384 lines
9.3 KiB
C++
/*=============================================================================
|
|
Copyright (c) 2003 Giovanni Bajo
|
|
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)
|
|
=============================================================================*/
|
|
#include <boost/spirit/include/classic_core.hpp>
|
|
#include <boost/spirit/include/classic_ast.hpp>
|
|
#include <boost/spirit/include/classic_tree_to_xml.hpp>
|
|
#include <boost/preprocessor/arithmetic/inc.hpp>
|
|
#include <boost/preprocessor/punctuation/comma_if.hpp>
|
|
#include <boost/preprocessor/repetition.hpp>
|
|
#include <boost/preprocessor/arithmetic/sub.hpp>
|
|
#include <boost/mpl/list.hpp>
|
|
#include <boost/mpl/apply.hpp>
|
|
#include <boost/mpl/remove.hpp>
|
|
#include <boost/mpl/size.hpp>
|
|
#include <boost/mpl/for_each.hpp>
|
|
|
|
#include <map>
|
|
#include <string>
|
|
|
|
#include <fstream>
|
|
#include <boost/detail/lightweight_test.hpp>
|
|
#include "impl/string_length.hpp"
|
|
|
|
#define DEBUG_DUMP_TREES (1)
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// rule_id helper
|
|
// http://sf.net/tracker/index.php?func=detail&aid=715483&group_id=28447&atid=393389)
|
|
|
|
namespace boost { namespace spirit {
|
|
|
|
template <
|
|
typename ScannerT,
|
|
unsigned long ID = 0,
|
|
typename ContextT = parser_context<> >
|
|
class rule_id
|
|
: public rule<ScannerT, ContextT, parser_tag<ID> >
|
|
{
|
|
typedef rule<ScannerT, ContextT, parser_tag<ID> > base_t;
|
|
|
|
public:
|
|
// Forward ctors and operator=.
|
|
rule_id()
|
|
{}
|
|
|
|
template <typename T>
|
|
rule_id(const T& a)
|
|
: base_t(a) {}
|
|
|
|
template <typename T>
|
|
rule_id& operator=(const T& a)
|
|
{ base_t::operator=(a); return *this; }
|
|
};
|
|
}}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Framework setup
|
|
|
|
namespace mpl = boost::mpl;
|
|
using namespace BOOST_SPIRIT_CLASSIC_NS;
|
|
using namespace std;
|
|
|
|
|
|
enum RULE_ID
|
|
{
|
|
ID_A = 1,
|
|
ID_B,
|
|
ID_C,
|
|
ID_ROOT
|
|
};
|
|
|
|
map<parser_id, string> rule_names;
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Generic tree manipulation tools
|
|
|
|
template <typename TreeT>
|
|
RULE_ID id(TreeT& t)
|
|
{ return (RULE_ID)t.value.id().to_long(); }
|
|
|
|
|
|
template <typename TreeT>
|
|
TreeT& child(TreeT& t, unsigned n)
|
|
{
|
|
return t.children[n];
|
|
}
|
|
|
|
template <typename TreeT>
|
|
size_t num_children(const TreeT& t)
|
|
{ return t.children.size(); }
|
|
|
|
template <typename TreeT>
|
|
typename TreeT::parse_node_t::iterator_t ValueBeginIterator(TreeT& t)
|
|
{ return t.value.begin(); }
|
|
|
|
template <typename TreeT>
|
|
typename TreeT::parse_node_t::iterator_t ValueEndIterator(TreeT& t)
|
|
{ return t.value.end(); }
|
|
|
|
template <typename TreeT>
|
|
bool equal(TreeT& a, TreeT& b)
|
|
{
|
|
if (id(a) != id(b))
|
|
return false;
|
|
|
|
if (num_children(a) != num_children(b))
|
|
return false;
|
|
|
|
unsigned n = num_children(a);
|
|
for (unsigned i=0;i<n;i++)
|
|
if (!equal(child(a, i), child(b, i)))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
template <typename TreeT>
|
|
void dump(ostream& o, TreeT& t, int level = 0)
|
|
{
|
|
string name;
|
|
string value;
|
|
map<parser_id, string>::iterator iter =
|
|
rule_names.find(id(t));
|
|
|
|
if (iter == rule_names.end())
|
|
name = "noname";
|
|
else
|
|
name = iter->second;
|
|
|
|
value.assign(ValueBeginIterator(t), ValueEndIterator(t));
|
|
|
|
for (int i=0;i<level;i++)
|
|
o << " ";
|
|
|
|
|
|
o << name << ": " << value << endl;
|
|
|
|
unsigned n = num_children(t);
|
|
for (unsigned c=0;c<n;c++)
|
|
dump(o, child(t, c), level+1);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Tree folding
|
|
|
|
namespace test_impl {
|
|
|
|
template <typename ParmT>
|
|
struct fold_node
|
|
{
|
|
// assign a subtree
|
|
void operator()(ParmT& t, ParmT ch) const
|
|
{ t = ch; }
|
|
|
|
// wrong specialization
|
|
template <typename TreeT>
|
|
void operator()(TreeT& t, ParmT p) const
|
|
{ typedef typename TreeT::this_should_never_be_compiled type; }
|
|
};
|
|
|
|
template <>
|
|
struct fold_node<nil_t>
|
|
{
|
|
template <typename TreeT>
|
|
void operator()(TreeT& t, nil_t) const
|
|
{ typedef typename TreeT::this_should_never_be_compiled type; }
|
|
};
|
|
|
|
template <>
|
|
struct fold_node<RULE_ID>
|
|
{
|
|
template <typename TreeT>
|
|
void operator()(TreeT& t, RULE_ID id) const
|
|
{ t.value.id(id); }
|
|
};
|
|
|
|
template <typename ParmT>
|
|
struct fold_child
|
|
{
|
|
template <typename TreeT>
|
|
void operator()(TreeT& t, ParmT p, unsigned n) const
|
|
{ fold_node<ParmT>()(t.children[n], p); }
|
|
};
|
|
|
|
template <>
|
|
struct fold_child<nil_t>
|
|
{
|
|
template <typename TreeT>
|
|
void operator()(TreeT& t, nil_t, unsigned n) const
|
|
{}
|
|
};
|
|
}
|
|
|
|
template <typename TreeT,
|
|
typename T, typename T1, typename T2, typename T3, typename T4,
|
|
typename T5, typename T6, typename T7, typename T8>
|
|
TreeT fold(T p, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7, T8 p8)
|
|
{
|
|
// Prepare a list with all the template types
|
|
typedef mpl::list<T1,T2,T3,T4,T5,T6,T7,T8> full_list_t;
|
|
|
|
// Remove the ones equal to nil_t: they are the default parameters
|
|
// unspecified from the user
|
|
typedef typename mpl::remove<full_list_t, nil_t>::type parm_list_t;
|
|
|
|
// Get the size of the list = number of parameters specified by the user
|
|
typedef typename mpl::size<parm_list_t>::type parm_list_size_t;
|
|
enum { NUM_CHILDREN = parm_list_size_t::value };
|
|
|
|
TreeT t;
|
|
|
|
// Generate the root of the tree (specialized for the first parameter)
|
|
test_impl::fold_node<T>()(t, p);
|
|
|
|
// Make room for the children
|
|
if (NUM_CHILDREN > 0)
|
|
t.children.resize(NUM_CHILDREN);
|
|
|
|
// For each children, call the GenerateChild function, which is specialized
|
|
// on the different types
|
|
test_impl::fold_child<T1>()(t, p1, 0);
|
|
test_impl::fold_child<T2>()(t, p2, 1);
|
|
test_impl::fold_child<T3>()(t, p3, 2);
|
|
test_impl::fold_child<T4>()(t, p4, 3);
|
|
test_impl::fold_child<T5>()(t, p5, 4);
|
|
test_impl::fold_child<T6>()(t, p6, 5);
|
|
test_impl::fold_child<T7>()(t, p7, 6);
|
|
test_impl::fold_child<T8>()(t, p8, 7);
|
|
|
|
return t;
|
|
}
|
|
|
|
|
|
// Define fold() wrapper for 1->7 parameters: they just call the 8 parameter
|
|
// version passing nil_t for the other arguments
|
|
#define PUT_EMPTY(Z, N, _) nil_t()
|
|
|
|
#define DEFINE_FOLD(Z, N, _) \
|
|
template <typename TreeT, typename T BOOST_PP_COMMA_IF(N) \
|
|
BOOST_PP_ENUM_PARAMS(N, typename T) > \
|
|
TreeT fold(T p BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_BINARY_PARAMS(N, T, p)) \
|
|
{ \
|
|
return fold<TreeT>(p \
|
|
BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_PARAMS(N, p) \
|
|
BOOST_PP_COMMA_IF(BOOST_PP_SUB(8,N)) \
|
|
BOOST_PP_ENUM(BOOST_PP_SUB(8,N), PUT_EMPTY, _)); \
|
|
}
|
|
|
|
BOOST_PP_REPEAT(7, DEFINE_FOLD, _)
|
|
|
|
#undef PUT_EMPTY
|
|
#undef DEFINE_FOLD
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// test_banal: simple tree construction
|
|
|
|
struct test_banal : public grammar<test_banal>
|
|
{
|
|
template <class T>
|
|
struct definition
|
|
{
|
|
rule_id<T, ID_ROOT> root;
|
|
rule_id<T, ID_A> a;
|
|
rule_id<T, ID_B> b;
|
|
rule_id<T, ID_C> c;
|
|
|
|
definition(const test_banal&)
|
|
{
|
|
root = a >> c;
|
|
a = b;
|
|
b = chlit<>('b');
|
|
c = chlit<>('c');
|
|
}
|
|
|
|
const rule_id<T, ID_ROOT>& start()
|
|
{ return root; }
|
|
};
|
|
|
|
const char* pattern(void)
|
|
{
|
|
return "bc";
|
|
}
|
|
|
|
template <typename TreeT>
|
|
TreeT expected_tree(void)
|
|
{
|
|
return fold<TreeT>(
|
|
ID_ROOT, fold<TreeT>(
|
|
ID_A,
|
|
ID_B),
|
|
ID_C);
|
|
}
|
|
};
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// All the tests
|
|
|
|
typedef mpl::list
|
|
<
|
|
test_banal
|
|
|
|
> tests_t;
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// run_test - code to run a test
|
|
|
|
struct run_test
|
|
{
|
|
template <typename TestT>
|
|
void operator()(TestT gram)
|
|
{
|
|
typedef const char* iterator_t;
|
|
typedef node_val_data_factory<nil_t> factory_t;
|
|
typedef typename
|
|
factory_t
|
|
::BOOST_NESTED_TEMPLATE factory<iterator_t>
|
|
::node_t node_t;
|
|
typedef tree_node<node_t> tree_t;
|
|
|
|
iterator_t text_begin = gram.pattern();
|
|
iterator_t text_end = text_begin + test_impl::string_length(text_begin);
|
|
|
|
tree_parse_info<iterator_t, factory_t> info =
|
|
ast_parse(text_begin, text_end, gram);
|
|
|
|
BOOST_TEST(info.full);
|
|
|
|
tree_t expected = gram.template expected_tree<tree_t>();
|
|
|
|
#if DEBUG_DUMP_TREES
|
|
dump(cout, info.trees[0]);
|
|
dump(cout, expected);
|
|
#endif
|
|
|
|
BOOST_TEST(equal(info.trees[0], expected));
|
|
}
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// main() stuff
|
|
|
|
#ifdef BOOST_NO_EXCEPTIONS
|
|
namespace boost
|
|
{
|
|
void throw_exception(std::exception const & )
|
|
{
|
|
std::cerr << "Exception caught" << std::endl;
|
|
BOOST_TEST(0);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
void init(void)
|
|
{
|
|
rule_names[ID_ROOT] = "ID_ROOT";
|
|
rule_names[ID_A] = "ID_A";
|
|
rule_names[ID_B] = "ID_B";
|
|
rule_names[ID_C] = "ID_C";
|
|
}
|
|
|
|
|
|
int main()
|
|
{
|
|
init();
|
|
|
|
mpl::for_each<tests_t, mpl::_> (run_test());
|
|
|
|
return boost::report_errors();
|
|
}
|