• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <boost/config/warning_disable.hpp>
20 #include <boost/spirit/home/x3.hpp>
21 #include <boost/spirit/home/x3/support/ast/variant.hpp>
22 #include <boost/fusion/include/adapt_struct.hpp>
23 
24 #include <iostream>
25 #include <string>
26 #include <list>
27 #include <numeric>
28 
29 namespace x3 = boost::spirit::x3;
30 
31 namespace client { namespace ast
32 {
33     ///////////////////////////////////////////////////////////////////////////
34     //  The AST
35     ///////////////////////////////////////////////////////////////////////////
36     struct nil {};
37     struct signed_;
38     struct program;
39 
40     struct operand : x3::variant<
41             nil
42           , unsigned int
43           , x3::forward_ast<signed_>
44           , x3::forward_ast<program>
45         >
46     {
47         using base_type::base_type;
48         using base_type::operator=;
49     };
50 
51     struct signed_
52     {
53         char sign;
54         operand operand_;
55     };
56 
57     struct operation
58     {
59         char operator_;
60         operand operand_;
61     };
62 
63     struct program
64     {
65         operand first;
66         std::list<operation> rest;
67     };
68 }}
69 
70 BOOST_FUSION_ADAPT_STRUCT(client::ast::signed_,
71     sign, operand_
72 )
73 
74 BOOST_FUSION_ADAPT_STRUCT(client::ast::operation,
75     operator_, operand_
76 )
77 
78 BOOST_FUSION_ADAPT_STRUCT(client::ast::program,
79     first, rest
80 )
81 
82 namespace client { namespace ast
83 {
84     ///////////////////////////////////////////////////////////////////////////
85     //  The AST Printer
86     ///////////////////////////////////////////////////////////////////////////
87     struct printer
88     {
89         typedef void result_type;
90 
operator ()client::ast::printer91         void operator()(nil) const {}
operator ()client::ast::printer92         void operator()(unsigned int n) const { std::cout << n; }
93 
operator ()client::ast::printer94         void operator()(operation const& x) const
95         {
96             boost::apply_visitor(*this, x.operand_);
97             switch (x.operator_)
98             {
99                 case '+': std::cout << " add"; break;
100                 case '-': std::cout << " subt"; break;
101                 case '*': std::cout << " mult"; break;
102                 case '/': std::cout << " div"; break;
103             }
104         }
105 
operator ()client::ast::printer106         void operator()(signed_ const& x) const
107         {
108             boost::apply_visitor(*this, x.operand_);
109             switch (x.sign)
110             {
111                 case '-': std::cout << " neg"; break;
112                 case '+': std::cout << " pos"; break;
113             }
114         }
115 
operator ()client::ast::printer116         void operator()(program const& x) const
117         {
118             boost::apply_visitor(*this, x.first);
119             for (operation const& oper: x.rest)
120             {
121                 std::cout << ' ';
122                 (*this)(oper);
123             }
124         }
125     };
126 
127     ///////////////////////////////////////////////////////////////////////////
128     //  The AST evaluator
129     ///////////////////////////////////////////////////////////////////////////
130     struct eval
131     {
132         typedef int result_type;
133 
operator ()client::ast::eval134         int operator()(nil) const { BOOST_ASSERT(0); return 0; }
operator ()client::ast::eval135         int operator()(unsigned int n) const { return n; }
136 
operator ()client::ast::eval137         int operator()(int lhs, operation const& x) const
138         {
139             int rhs = boost::apply_visitor(*this, x.operand_);
140             switch (x.operator_)
141             {
142                 case '+': return lhs + rhs;
143                 case '-': return lhs - rhs;
144                 case '*': return lhs * rhs;
145                 case '/': return lhs / rhs;
146             }
147             BOOST_ASSERT(0);
148             return 0;
149         }
150 
operator ()client::ast::eval151         int operator()(signed_ const& x) const
152         {
153             int rhs = boost::apply_visitor(*this, x.operand_);
154             switch (x.sign)
155             {
156                 case '-': return -rhs;
157                 case '+': return +rhs;
158             }
159             BOOST_ASSERT(0);
160             return 0;
161         }
162 
operator ()client::ast::eval163         int operator()(program const& x) const
164         {
165             return std::accumulate(
166                 x.rest.begin(), x.rest.end()
167               , boost::apply_visitor(*this, x.first)
168               , *this);
169         }
170     };
171 }}
172 
173 namespace client
174 {
175     ///////////////////////////////////////////////////////////////////////////////
176     //  The calculator grammar
177     ///////////////////////////////////////////////////////////////////////////////
178     namespace calculator_grammar
179     {
180         using x3::uint_;
181         using x3::char_;
182 
183         x3::rule<class expression, ast::program> const expression("expression");
184         x3::rule<class term, ast::program> const term("term");
185         x3::rule<class factor, ast::operand> const factor("factor");
186 
187         auto const expression_def =
188             term
189             >> *(   (char_('+') >> term)
190                 |   (char_('-') >> term)
191                 )
192             ;
193 
194         auto const term_def =
195             factor
196             >> *(   (char_('*') >> factor)
197                 |   (char_('/') >> factor)
198                 )
199             ;
200 
201         auto const factor_def =
202                 uint_
203             |   '(' >> expression >> ')'
204             |   (char_('-') >> factor)
205             |   (char_('+') >> factor)
206             ;
207 
208         BOOST_SPIRIT_DEFINE(
209             expression
210           , term
211           , factor
212         );
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