summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/proto/test/external_transforms.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/boost/libs/proto/test/external_transforms.cpp')
-rw-r--r--src/boost/libs/proto/test/external_transforms.cpp185
1 files changed, 185 insertions, 0 deletions
diff --git a/src/boost/libs/proto/test/external_transforms.cpp b/src/boost/libs/proto/test/external_transforms.cpp
new file mode 100644
index 00000000..f72e9458
--- /dev/null
+++ b/src/boost/libs/proto/test/external_transforms.cpp
@@ -0,0 +1,185 @@
+// 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;
+}