1 /*=============================================================================
2     Copyright (c) 2001-2014 Joel de Guzman
3 
4     Distributed under the Boost Software License, Version 1.0. (See accompanying
5     file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 =============================================================================*/
7 ///////////////////////////////////////////////////////////////////////////////
8 //
9 //  A Calculator example demonstrating generation of AST. The AST,
10 //  once created, is traversed, 1) To print its contents and
11 //  2) To evaluate the result.
12 //
13 //  [ JDG April 28, 2008 ]      For BoostCon 2008
14 //  [ JDG February 18, 2011 ]   Pure attributes. No semantic actions.
15 //  [ JDG January 9, 2013 ]     Spirit X3
16 //
17 ///////////////////////////////////////////////////////////////////////////////
18 
19 #if defined(_MSC_VER)
20 # pragma warning(disable: 4345)
21 #endif
22 
23 #include <boost/config/warning_disable.hpp>
24 #include <boost/spirit/home/x3.hpp>
25 #include <boost/spirit/home/x3/support/ast/variant.hpp>
26 #include <boost/fusion/include/adapt_struct.hpp>
27 
28 #include <iostream>
29 #include <string>
30 #include <list>
31 #include <numeric>
32 
33 namespace x3 = boost::spirit::x3;
34 
35 namespace client { namespace ast
36 {
37     ///////////////////////////////////////////////////////////////////////////
38     //  The AST
39     ///////////////////////////////////////////////////////////////////////////
40     struct nil {};
41     struct signed_;
42     struct program;
43 
44     struct operand : x3::variant<
45             nil
46           , unsigned int
47           , x3::forward_ast<signed_>
48           , x3::forward_ast<program>
49         >
50     {
51         using base_type::base_type;
52         using base_type::operator=;
53     };
54 
55     struct signed_
56     {
57         char sign;
58         operand operand_;
59     };
60 
61     struct operation
62     {
63         char operator_;
64         operand operand_;
65     };
66 
67     struct program
68     {
69         operand first;
70         std::list<operation> rest;
71     };
72 }}
73 
74 BOOST_FUSION_ADAPT_STRUCT(client::ast::signed_,
75     sign, operand_
76 )
77 
78 BOOST_FUSION_ADAPT_STRUCT(client::ast::operation,
79     operator_, operand_
80 )
81 
82 BOOST_FUSION_ADAPT_STRUCT(client::ast::program,
83     first, rest
84 )
85 
86 namespace client { namespace ast
87 {
88     ///////////////////////////////////////////////////////////////////////////
89     //  The AST Printer
90     ///////////////////////////////////////////////////////////////////////////
91     struct printer
92     {
93         typedef void result_type;
94 
operator ()client::ast::printer95         void operator()(nil) const {}
operator ()client::ast::printer96         void operator()(unsigned int n) const { std::cout << n; }
97 
operator ()client::ast::printer98         void operator()(operation const& x) const
99         {
100             boost::apply_visitor(*this, x.operand_);
101             switch (x.operator_)
102             {
103                 case '+': std::cout << " add"; break;
104                 case '-': std::cout << " subt"; break;
105                 case '*': std::cout << " mult"; break;
106                 case '/': std::cout << " div"; break;
107             }
108         }
109 
operator ()client::ast::printer110         void operator()(signed_ const& x) const
111         {
112             boost::apply_visitor(*this, x.operand_);
113             switch (x.sign)
114             {
115                 case '-': std::cout << " neg"; break;
116                 case '+': std::cout << " pos"; break;
117             }
118         }
119 
operator ()client::ast::printer120         void operator()(program const& x) const
121         {
122             boost::apply_visitor(*this, x.first);
123             for (operation const& oper: x.rest)
124             {
125                 std::cout << ' ';
126                 (*this)(oper);
127             }
128         }
129     };
130 
131     ///////////////////////////////////////////////////////////////////////////
132     //  The AST evaluator
133     ///////////////////////////////////////////////////////////////////////////
134     struct eval
135     {
136         typedef int result_type;
137 
operator ()client::ast::eval138         int operator()(nil) const { BOOST_ASSERT(0); return 0; }
operator ()client::ast::eval139         int operator()(unsigned int n) const { return n; }
140 
operator ()client::ast::eval141         int operator()(int lhs, operation const& x) const
142         {
143             int rhs = boost::apply_visitor(*this, x.operand_);
144             switch (x.operator_)
145             {
146                 case '+': return lhs + rhs;
147                 case '-': return lhs - rhs;
148                 case '*': return lhs * rhs;
149                 case '/': return lhs / rhs;
150             }
151             BOOST_ASSERT(0);
152             return 0;
153         }
154 
operator ()client::ast::eval155         int operator()(signed_ const& x) const
156         {
157             int rhs = boost::apply_visitor(*this, x.operand_);
158             switch (x.sign)
159             {
160                 case '-': return -rhs;
161                 case '+': return +rhs;
162             }
163             BOOST_ASSERT(0);
164             return 0;
165         }
166 
operator ()client::ast::eval167         int operator()(program const& x) const
168         {
169             return std::accumulate(
170                 x.rest.begin(), x.rest.end()
171               , boost::apply_visitor(*this, x.first)
172               , *this);
173         }
174     };
175 }}
176 
177 namespace client
178 {
179     ///////////////////////////////////////////////////////////////////////////////
180     //  The calculator grammar
181     ///////////////////////////////////////////////////////////////////////////////
182     namespace calculator_grammar
183     {
184         using x3::uint_;
185         using x3::char_;
186 
187         x3::rule<class expression, ast::program> const expression("expression");
188         x3::rule<class term, ast::program> const term("term");
189         x3::rule<class factor, ast::operand> const factor("factor");
190 
191         auto const expression_def =
192                 term
193                 >> *(   (char_('+') >> term)
194                     |   (char_('-') >> term)
195                     )
196                 ;
197 
198         auto const term_def =
199                 factor
200                 >> *(   (char_('*') >> factor)
201                     |   (char_('/') >> factor)
202                     )
203                 ;
204 
205         auto const factor_def =
206                     uint_
207                 |   '(' >> expression >> ')'
208                 |   (char_('-') >> factor)
209                 |   (char_('+') >> factor)
210                 ;
211 
212         BOOST_SPIRIT_DEFINE(expression, term, factor);
213 
214         auto calculator = expression;
215     }
216 
217     using calculator_grammar::calculator;
218 
219 }
220 
221 ///////////////////////////////////////////////////////////////////////////////
222 //  Main program
223 ///////////////////////////////////////////////////////////////////////////////
224 int
main()225 main()
226 {
227     std::cout << "/////////////////////////////////////////////////////////\n\n";
228     std::cout << "Expression parser...\n\n";
229     std::cout << "/////////////////////////////////////////////////////////\n\n";
230     std::cout << "Type an expression...or [q or Q] to quit\n\n";
231 
232     typedef std::string::const_iterator iterator_type;
233     typedef client::ast::program ast_program;
234     typedef client::ast::printer ast_print;
235     typedef client::ast::eval ast_eval;
236 
237     std::string str;
238     while (std::getline(std::cin, str))
239     {
240         if (str.empty() || str[0] == 'q' || str[0] == 'Q')
241             break;
242 
243         auto& calc = client::calculator;    // Our grammar
244         ast_program program;                // Our program (AST)
245         ast_print print;                    // Prints the program
246         ast_eval eval;                      // Evaluates the program
247 
248         iterator_type iter = str.begin();
249         iterator_type end = str.end();
250         boost::spirit::x3::ascii::space_type space;
251         bool r = phrase_parse(iter, end, calc, space, program);
252 
253         if (r && iter == end)
254         {
255             std::cout << "-------------------------\n";
256             std::cout << "Parsing succeeded\n";
257             print(program);
258             std::cout << "\nResult: " << eval(program) << std::endl;
259             std::cout << "-------------------------\n";
260         }
261         else
262         {
263             std::string rest(iter, end);
264             std::cout << "-------------------------\n";
265             std::cout << "Parsing failed\n";
266             std::cout << "stopped at: \"" << rest << "\"\n";
267             std::cout << "-------------------------\n";
268         }
269     }
270 
271     std::cout << "Bye... :-) \n\n";
272     return 0;
273 }
274