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