• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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