• 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 //  Same as Calc4, this time, we'll incorporate debugging support,
10 //  plus error handling and reporting.
11 //
12 //  [ JDG April 28, 2008 ]      For BoostCon 2008
13 //  [ JDG February 18, 2011 ]   Pure attributes. No semantic actions.
14 //  [ JDG April 9, 2014 ]       Spirit X3
15 //
16 ///////////////////////////////////////////////////////////////////////////////
17 
18 ///////////////////////////////////////////////////////////////////////////////
19 // Uncomment this if you want to enable debugging
20 //#define BOOST_SPIRIT_X3_DEBUG
21 
22 #include <boost/config/warning_disable.hpp>
23 #include <boost/spirit/home/x3.hpp>
24 #include <boost/spirit/home/x3/support/ast/variant.hpp>
25 #include <boost/fusion/include/adapt_struct.hpp>
26 
27 #include <iostream>
28 #include <string>
29 #include <list>
30 #include <numeric>
31 
32 namespace x3 = boost::spirit::x3;
33 
34 namespace client { namespace ast
35 {
36     ///////////////////////////////////////////////////////////////////////////
37     //  The AST
38     ///////////////////////////////////////////////////////////////////////////
39     struct nil {};
40     struct signed_;
41     struct program;
42 
43     struct operand : x3::variant<
44             nil
45           , unsigned int
46           , x3::forward_ast<signed_>
47           , x3::forward_ast<program>
48         >
49     {
50         using base_type::base_type;
51         using base_type::operator=;
52     };
53 
54     struct signed_
55     {
56         char sign;
57         operand operand_;
58     };
59 
60     struct operation
61     {
62         char operator_;
63         operand operand_;
64     };
65 
66     struct program
67     {
68         operand first;
69         std::list<operation> rest;
70     };
71 
72     // print function for debugging
operator <<(std::ostream & out,nil)73     inline std::ostream& operator<<(std::ostream& out, nil) { out << "nil"; return out; }
74 }}
75 
76 BOOST_FUSION_ADAPT_STRUCT(client::ast::signed_,
77     sign, operand_
78 )
79 
80 BOOST_FUSION_ADAPT_STRUCT(client::ast::operation,
81     operator_, operand_
82 )
83 
84 BOOST_FUSION_ADAPT_STRUCT(client::ast::program,
85     first, rest
86 )
87 
88 namespace client { namespace ast
89 {
90     ///////////////////////////////////////////////////////////////////////////
91     //  The AST Printer
92     ///////////////////////////////////////////////////////////////////////////
93     struct printer
94     {
95         typedef void result_type;
96 
operator ()client::ast::printer97         void operator()(nil) const {}
operator ()client::ast::printer98         void operator()(unsigned int n) const { std::cout << n; }
99 
operator ()client::ast::printer100         void operator()(operation const& x) const
101         {
102             boost::apply_visitor(*this, x.operand_);
103             switch (x.operator_)
104             {
105                 case '+': std::cout << " add"; break;
106                 case '-': std::cout << " subt"; break;
107                 case '*': std::cout << " mult"; break;
108                 case '/': std::cout << " div"; break;
109             }
110         }
111 
operator ()client::ast::printer112         void operator()(signed_ const& x) const
113         {
114             boost::apply_visitor(*this, x.operand_);
115             switch (x.sign)
116             {
117                 case '-': std::cout << " neg"; break;
118                 case '+': std::cout << " pos"; break;
119             }
120         }
121 
operator ()client::ast::printer122         void operator()(program const& x) const
123         {
124             boost::apply_visitor(*this, x.first);
125             for (operation const& oper : x.rest)
126             {
127                 std::cout << ' ';
128                 (*this)(oper);
129             }
130         }
131     };
132 
133     ///////////////////////////////////////////////////////////////////////////
134     //  The AST evaluator
135     ///////////////////////////////////////////////////////////////////////////
136     struct eval
137     {
138         typedef int result_type;
139 
operator ()client::ast::eval140         int operator()(nil) const { BOOST_ASSERT(0); return 0; }
operator ()client::ast::eval141         int operator()(unsigned int n) const { return n; }
142 
operator ()client::ast::eval143         int operator()(operation const& x, int lhs) const
144         {
145             int rhs = boost::apply_visitor(*this, x.operand_);
146             switch (x.operator_)
147             {
148                 case '+': return lhs + rhs;
149                 case '-': return lhs - rhs;
150                 case '*': return lhs * rhs;
151                 case '/': return lhs / rhs;
152             }
153             BOOST_ASSERT(0);
154             return 0;
155         }
156 
operator ()client::ast::eval157         int operator()(signed_ const& x) const
158         {
159             int rhs = boost::apply_visitor(*this, x.operand_);
160             switch (x.sign)
161             {
162                 case '-': return -rhs;
163                 case '+': return +rhs;
164             }
165             BOOST_ASSERT(0);
166             return 0;
167         }
168 
operator ()client::ast::eval169         int operator()(program const& x) const
170         {
171             int state = boost::apply_visitor(*this, x.first);
172             for (operation const& oper : x.rest)
173             {
174                 state = (*this)(oper, state);
175             }
176             return state;
177         }
178     };
179 }}
180 
181 namespace client
182 {
183     ///////////////////////////////////////////////////////////////////////////////
184     //  The calculator grammar
185     ///////////////////////////////////////////////////////////////////////////////
186     namespace calculator_grammar
187     {
188         using x3::uint_;
189         using x3::char_;
190 
191         struct expression_class;
192         struct term_class;
193         struct factor_class;
194 
195         x3::rule<expression_class, ast::program> const expression("expression");
196         x3::rule<term_class, ast::program> const term("term");
197         x3::rule<factor_class, ast::operand> const factor("factor");
198 
199         auto const expression_def =
200             term
201             >> *(   (char_('+') > term)
202                 |   (char_('-') > term)
203                 )
204             ;
205 
206         auto const term_def =
207             factor
208             >> *(   (char_('*') > factor)
209                 |   (char_('/') > factor)
210                 )
211             ;
212 
213         auto const factor_def =
214                 uint_
215             |   '(' > expression > ')'
216             |   (char_('-') > factor)
217             |   (char_('+') > factor)
218             ;
219 
220         BOOST_SPIRIT_DEFINE(
221             expression
222           , term
223           , factor
224         );
225 
226         struct expression_class
227         {
228             //  Our error handler
229             template <typename Iterator, typename Exception, typename Context>
230             x3::error_handler_result
on_errorclient::calculator_grammar::expression_class231             on_error(Iterator&, Iterator const& last, Exception const& x, Context const& context)
232             {
233                 std::cout
234                     << "Error! Expecting: "
235                     << x.which()
236                     << " here: \""
237                     << std::string(x.where(), last)
238                     << "\""
239                     << std::endl
240                     ;
241                 return x3::error_handler_result::fail;
242             }
243         };
244 
245         auto calculator = expression;
246     }
247 
248     using calculator_grammar::calculator;
249 }
250 
251 ///////////////////////////////////////////////////////////////////////////////
252 //  Main program
253 ///////////////////////////////////////////////////////////////////////////////
254 int
main()255 main()
256 {
257     std::cout << "/////////////////////////////////////////////////////////\n\n";
258     std::cout << "Expression parser...\n\n";
259     std::cout << "/////////////////////////////////////////////////////////\n\n";
260     std::cout << "Type an expression...or [q or Q] to quit\n\n";
261 
262     typedef std::string::const_iterator iterator_type;
263     typedef client::ast::program ast_program;
264     typedef client::ast::printer ast_print;
265     typedef client::ast::eval ast_eval;
266 
267     std::string str;
268     while (std::getline(std::cin, str))
269     {
270         if (str.empty() || str[0] == 'q' || str[0] == 'Q')
271             break;
272 
273         auto& calc = client::calculator;    // Our grammar
274         ast_program program;                // Our program (AST)
275         ast_print print;                    // Prints the program
276         ast_eval eval;                      // Evaluates the program
277 
278         iterator_type iter = str.begin();
279         iterator_type end = str.end();
280         boost::spirit::x3::ascii::space_type space;
281         bool r = phrase_parse(iter, end, calc, space, program);
282 
283         if (r && iter == end)
284         {
285             std::cout << "-------------------------\n";
286             std::cout << "Parsing succeeded\n";
287             print(program);
288             std::cout << "\nResult: " << eval(program) << std::endl;
289             std::cout << "-------------------------\n";
290         }
291         else
292         {
293             std::cout << "-------------------------\n";
294             std::cout << "Parsing failed\n";
295             std::cout << "-------------------------\n";
296         }
297     }
298 
299     std::cout << "Bye... :-) \n\n";
300     return 0;
301 }
302