1 //[ CheckedCalc
2 // Copyright 2011 Eric Niebler. Distributed under the Boost
3 // Software License, Version 1.0. (See accompanying file
4 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 //
6 // This is an example of how to specify a transform externally so
7 // that a single grammar can be used to drive multiple differnt
8 // calculations. In particular, it defines a calculator grammar
9 // that computes the result of an expression with either checked
10 // or non-checked division.
11
12 #include <iostream>
13 #include <boost/assert.hpp>
14 #include <boost/mpl/int.hpp>
15 #include <boost/mpl/next.hpp>
16 #include <boost/mpl/min_max.hpp>
17 #include <boost/fusion/container/vector.hpp>
18 #include <boost/fusion/container/generation/make_vector.hpp>
19 #include <boost/proto/proto.hpp>
20 namespace mpl = boost::mpl;
21 namespace proto = boost::proto;
22 namespace fusion = boost::fusion;
23
24 // The argument placeholder type
25 template<typename I> struct placeholder : I {};
26
27 // Give each rule in the grammar a "name". This is so that we
28 // can easily dispatch on it later.
29 struct calc_grammar;
30 struct divides_rule : proto::divides<calc_grammar, calc_grammar> {};
31
32 // Use external transforms in calc_gramar
33 struct calc_grammar
34 : proto::or_<
35 proto::when<
36 proto::terminal<placeholder<proto::_> >
37 , proto::functional::at(proto::_state, proto::_value)
38 >
39 , proto::when<
40 proto::terminal<proto::convertible_to<double> >
41 , proto::_value
42 >
43 , proto::when<
44 proto::plus<calc_grammar, calc_grammar>
45 , proto::_default<calc_grammar>
46 >
47 , proto::when<
48 proto::minus<calc_grammar, calc_grammar>
49 , proto::_default<calc_grammar>
50 >
51 , proto::when<
52 proto::multiplies<calc_grammar, calc_grammar>
53 , proto::_default<calc_grammar>
54 >
55 // Note that we don't specify how division nodes are
56 // handled here. Proto::external_transform is a placeholder
57 // for an actual transform.
58 , proto::when<
59 divides_rule
60 , proto::external_transform
61 >
62 >
63 {};
64
65 template<typename E> struct calc_expr;
66 struct calc_domain : proto::domain<proto::generator<calc_expr> > {};
67
68 template<typename E>
69 struct calc_expr
70 : proto::extends<E, calc_expr<E>, calc_domain>
71 {
calc_exprcalc_expr72 calc_expr(E const &e = E()) : calc_expr::proto_extends(e) {}
73 };
74
75 calc_expr<proto::terminal<placeholder<mpl::int_<0> > >::type> _1;
76 calc_expr<proto::terminal<placeholder<mpl::int_<1> > >::type> _2;
77
78 // Use proto::external_transforms to map from named grammar rules to
79 // transforms.
80 struct non_checked_division
81 : proto::external_transforms<
82 proto::when< divides_rule, proto::_default<calc_grammar> >
83 >
84 {};
85
86 struct division_by_zero : std::exception {};
87
88 struct do_checked_divide
89 : proto::callable
90 {
91 typedef int result_type;
operator ()do_checked_divide92 int operator()(int left, int right) const
93 {
94 if (right == 0) throw division_by_zero();
95 return left / right;
96 }
97 };
98
99 // Use proto::external_transforms again, this time to map the divides_rule
100 // to a transforms that performs checked division.
101 struct checked_division
102 : proto::external_transforms<
103 proto::when<
104 divides_rule
105 , do_checked_divide(calc_grammar(proto::_left), calc_grammar(proto::_right))
106 >
107 >
108 {};
109
main()110 int main()
111 {
112 non_checked_division non_checked;
113 int result2 = calc_grammar()(_1 / _2, fusion::make_vector(6, 2), non_checked);
114 BOOST_ASSERT(result2 == 3);
115
116 try
117 {
118 checked_division checked;
119 // This should throw
120 int result3 = calc_grammar()(_1 / _2, fusion::make_vector(6, 0), checked);
121 BOOST_ASSERT(false); // shouldn't get here!
122 }
123 catch(division_by_zero)
124 {
125 std::cout << "caught division by zero!\n";
126 }
127 }
128 //]
129