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