123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- // Copyright 2011 Eric Niebler. 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)
- //
- // This is an example of how to specify a transform externally so
- // that a single grammar can be used to drive multiple differnt
- // calculations. In particular, it defines a calculator grammar
- // that computes the result of an expression with either checked
- // or non-checked division.
- #include <iostream>
- #include <boost/mpl/int.hpp>
- #include <boost/mpl/next.hpp>
- #include <boost/mpl/min_max.hpp>
- #include <boost/fusion/container/vector.hpp>
- #include <boost/fusion/container/generation/make_vector.hpp>
- #include <boost/proto/proto.hpp>
- #include <boost/test/unit_test.hpp>
- namespace mpl = boost::mpl;
- namespace proto = boost::proto;
- namespace fusion = boost::fusion;
- using proto::_;
- // The argument placeholder type
- template<typename I> struct placeholder : I {};
- // Give each rule in the grammar a "name". This is so that we
- // can easily dispatch on it later.
- struct calc_grammar;
- struct divides_rule : proto::divides<calc_grammar, calc_grammar> {};
- // Use external transforms in calc_gramar
- struct calc_grammar
- : proto::or_<
- proto::when<
- proto::terminal<placeholder<_> >
- , proto::functional::at(proto::_state, proto::_value)
- >
- , proto::when<
- proto::terminal<proto::convertible_to<double> >
- , proto::_value
- >
- , proto::when<
- proto::plus<calc_grammar, calc_grammar>
- , proto::_default<calc_grammar>
- >
- , proto::when<
- proto::minus<calc_grammar, calc_grammar>
- , proto::_default<calc_grammar>
- >
- , proto::when<
- proto::multiplies<calc_grammar, calc_grammar>
- , proto::_default<calc_grammar>
- >
- // Note that we don't specify how division nodes are
- // handled here. Proto::external_transform is a placeholder
- // for an actual transform.
- , proto::when<
- divides_rule
- , proto::external_transform
- >
- >
- {};
- template<typename E> struct calc_expr;
- struct calc_domain : proto::domain<proto::generator<calc_expr> > {};
- template<typename E>
- struct calc_expr
- : proto::extends<E, calc_expr<E>, calc_domain>
- {
- calc_expr(E const &e = E()) : calc_expr::proto_extends(e) {}
- };
- calc_expr<proto::terminal<placeholder<mpl::int_<0> > >::type> _1;
- calc_expr<proto::terminal<placeholder<mpl::int_<1> > >::type> _2;
- // Use proto::external_transforms to map from named grammar rules to
- // transforms.
- struct non_checked_division
- : proto::external_transforms<
- proto::when< divides_rule, proto::_default<calc_grammar> >
- >
- {};
- struct division_by_zero : std::exception {};
- struct do_checked_divide
- : proto::callable
- {
- typedef int result_type;
- int operator()(int left, int right) const
- {
- if (right == 0) throw division_by_zero();
- return left / right;
- }
- };
- // Use proto::external_transforms again, this time to map the divides_rule
- // to a transforms that performs checked division.
- struct checked_division
- : proto::external_transforms<
- proto::when<
- divides_rule
- , do_checked_divide(calc_grammar(proto::_left), calc_grammar(proto::_right))
- >
- >
- {};
- BOOST_PROTO_DEFINE_ENV_VAR(mydata_tag, mydata);
- void test_external_transforms()
- {
- non_checked_division non_checked;
- int result1 = calc_grammar()(_1 / _2, fusion::make_vector(6, 2), non_checked);
- BOOST_CHECK_EQUAL(result1, 3);
- // check that additional data slots are ignored
- int result2 = calc_grammar()(_1 / _2, fusion::make_vector(8, 2), (non_checked, mydata = "foo"));
- BOOST_CHECK_EQUAL(result2, 4);
- // check that we can use the dedicated slot for this purpose
- int result3 = calc_grammar()(_1 / _2, fusion::make_vector(8, 2), (42, proto::transforms = non_checked, mydata = "foo"));
- BOOST_CHECK_EQUAL(result2, 4);
- checked_division checked;
- try
- {
- // This should throw
- int result3 = calc_grammar()(_1 / _2, fusion::make_vector(6, 0), checked);
- BOOST_CHECK(!"Didn't throw an exception"); // shouldn't get here!
- }
- catch(division_by_zero)
- {
- ; // OK
- }
- catch(...)
- {
- BOOST_CHECK(!"Unexpected exception"); // shouldn't get here!
- }
- try
- {
- // This should throw
- int result4 = calc_grammar()(_1 / _2, fusion::make_vector(6, 0), (checked, mydata = test_external_transforms));
- BOOST_CHECK(!"Didn't throw an exception"); // shouldn't get here!
- }
- catch(division_by_zero)
- {
- ; // OK
- }
- catch(...)
- {
- BOOST_CHECK(!"Unexpected exception"); // shouldn't get here!
- }
- try
- {
- // This should throw
- int result5 = calc_grammar()(_1 / _2, fusion::make_vector(6, 0), (42, proto::transforms = checked, mydata = test_external_transforms));
- BOOST_CHECK(!"Didn't throw an exception"); // shouldn't get here!
- }
- catch(division_by_zero)
- {
- ; // OK
- }
- catch(...)
- {
- BOOST_CHECK(!"Unexpected exception"); // shouldn't get here!
- }
- }
- using namespace boost::unit_test;
- ///////////////////////////////////////////////////////////////////////////////
- // init_unit_test_suite
- //
- test_suite* init_unit_test_suite( int argc, char* argv[] )
- {
- test_suite *test = BOOST_TEST_SUITE("test for external transforms");
- test->add(BOOST_TEST_CASE(&test_external_transforms));
- return test;
- }
|