1 /*=============================================================================
2 Copyright (c) 2001-2011 Joel de Guzman
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 ///////////////////////////////////////////////////////////////////////////////
9 // Yet another calculator example! This time, we will compile to a simple
10 // virtual machine. This is actually one of the very first Spirit example
11 // circa 2000. Now, it's ported to Spirit2.
13 // [ JDG Sometime 2000 ] pre-boost
14 // [ JDG September 18, 2002 ] spirit1
15 // [ JDG April 8, 2007 ] spirit2
16 // [ JDG February 18, 2011 ] Pure attributes. No semantic actions.
18 ///////////////////////////////////////////////////////////////////////////////
20 ///////////////////////////////////////////////////////////////////////////////
21 // Spirit v2.5 allows you to suppress automatic generation
22 // of predefined terminals to speed up complation. With
23 // BOOST_SPIRIT_NO_PREDEFINED_TERMINALS defined, you are
24 // responsible in creating instances of the terminals that
25 // you need (e.g. see qi::uint_type uint_ below).
26 #define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
27 ///////////////////////////////////////////////////////////////////////////////
29 ///////////////////////////////////////////////////////////////////////////////
30 // Define this to enable debugging
31 //#define BOOST_SPIRIT_QI_DEBUG
33 ///////////////////////////////////////////////////////////////////////////////
34 // Uncomment this if you want to enable debugging
35 //#define BOOST_SPIRIT_QI_DEBUG
36 ///////////////////////////////////////////////////////////////////////////////
39 # pragma warning(disable: 4345)
42 #include <boost/spirit/include/qi.hpp>
43 #include <boost/variant/recursive_variant.hpp>
44 #include <boost/variant/apply_visitor.hpp>
45 #include <boost/fusion/include/adapt_struct.hpp>
46 #include <boost/phoenix/function.hpp>
47 #include <boost/foreach.hpp>
52 namespace client
{ namespace ast
54 ///////////////////////////////////////////////////////////////////////////
56 ///////////////////////////////////////////////////////////////////////////
61 typedef boost::variant
<
64 , boost::recursive_wrapper
<signed_
>
65 , boost::recursive_wrapper
<expression
>
84 std::list
<operation
> rest
;
87 // print function for debugging
88 inline std::ostream
& operator<<(std::ostream
& out
, nil
) { out
<< "nil"; return out
; }
91 BOOST_FUSION_ADAPT_STRUCT(
94 (client::ast::operand
, operand_
)
97 BOOST_FUSION_ADAPT_STRUCT(
98 client::ast::operation
,
100 (client::ast::operand
, operand_
)
103 BOOST_FUSION_ADAPT_STRUCT(
104 client::ast::expression
,
105 (client::ast::operand
, first
)
106 (std::list
<client::ast::operation
>, rest
)
111 ///////////////////////////////////////////////////////////////////////////
112 // The Virtual Machine
113 ///////////////////////////////////////////////////////////////////////////
116 op_neg
, // negate the top stack entry
117 op_add
, // add top two stack entries
118 op_sub
, // subtract top two stack entries
119 op_mul
, // multiply top two stack entries
120 op_div
, // divide top two stack entries
121 op_int
, // push constant integer into the stack
128 vmachine(unsigned stackSize
= 4096)
130 , stack_ptr(stack
.begin())
134 int top() const { return stack_ptr
[-1]; };
135 void execute(std::vector
<int> const& code
);
139 std::vector
<int> stack
;
140 std::vector
<int>::iterator stack_ptr
;
143 void vmachine::execute(std::vector
<int> const& code
)
145 std::vector
<int>::const_iterator pc
= code
.begin();
146 stack_ptr
= stack
.begin();
148 while (pc
!= code
.end())
153 stack_ptr
[-1] = -stack_ptr
[-1];
158 stack_ptr
[-1] += stack_ptr
[0];
163 stack_ptr
[-1] -= stack_ptr
[0];
168 stack_ptr
[-1] *= stack_ptr
[0];
173 stack_ptr
[-1] /= stack_ptr
[0];
177 *stack_ptr
++ = *pc
++;
183 ///////////////////////////////////////////////////////////////////////////
185 ///////////////////////////////////////////////////////////////////////////
188 typedef void result_type
;
190 std::vector
<int>& code
;
191 compiler(std::vector
<int>& code
)
194 void operator()(ast::nil
) const { BOOST_ASSERT(0); }
195 void operator()(unsigned int n
) const
197 code
.push_back(op_int
);
201 void operator()(ast::operation
const& x
) const
203 boost::apply_visitor(*this, x
.operand_
);
206 case '+': code
.push_back(op_add
); break;
207 case '-': code
.push_back(op_sub
); break;
208 case '*': code
.push_back(op_mul
); break;
209 case '/': code
.push_back(op_div
); break;
210 default: BOOST_ASSERT(0); break;
214 void operator()(ast::signed_
const& x
) const
216 boost::apply_visitor(*this, x
.operand_
);
219 case '-': code
.push_back(op_neg
); break;
221 default: BOOST_ASSERT(0); break;
225 void operator()(ast::expression
const& x
) const
227 boost::apply_visitor(*this, x
.first
);
228 BOOST_FOREACH(ast::operation
const& oper
, x
.rest
)
235 namespace qi
= boost::spirit::qi
;
236 namespace ascii
= boost::spirit::ascii
;
237 using boost::phoenix::function
;
239 ///////////////////////////////////////////////////////////////////////////////
241 ///////////////////////////////////////////////////////////////////////////////
242 struct error_handler_
244 template <typename
, typename
, typename
>
245 struct result
{ typedef void type
; };
247 template <typename Iterator
>
250 , Iterator err_pos
, Iterator last
) const
253 << "Error! Expecting "
254 << what
// what failed?
256 << std::string(err_pos
, last
) // iterators to error-pos, end
263 function
<error_handler_
> const error_handler
= error_handler_();
265 ///////////////////////////////////////////////////////////////////////////////
266 // The calculator grammar
267 ///////////////////////////////////////////////////////////////////////////////
268 template <typename Iterator
>
269 struct calculator
: qi::grammar
<Iterator
, ast::expression(), ascii::space_type
>
271 calculator() : calculator::base_type(expression
)
284 >> *( (char_('+') > term
)
285 | (char_('-') > term
)
291 >> *( (char_('*') > factor
)
292 | (char_('/') > factor
)
298 | '(' > expression
> ')'
299 | (char_('-') > factor
)
300 | (char_('+') > factor
)
303 // Debugging and error handling and reporting support.
304 BOOST_SPIRIT_DEBUG_NODES(
305 (expression
)(term
)(factor
));
308 on_error
<fail
>(expression
, error_handler(_4
, _3
, _2
));
311 qi::rule
<Iterator
, ast::expression(), ascii::space_type
> expression
;
312 qi::rule
<Iterator
, ast::expression(), ascii::space_type
> term
;
313 qi::rule
<Iterator
, ast::operand(), ascii::space_type
> factor
;
317 ///////////////////////////////////////////////////////////////////////////////
319 ///////////////////////////////////////////////////////////////////////////////
323 std::cout
<< "/////////////////////////////////////////////////////////\n\n";
324 std::cout
<< "Expression parser...\n\n";
325 std::cout
<< "/////////////////////////////////////////////////////////\n\n";
326 std::cout
<< "Type an expression...or [q or Q] to quit\n\n";
328 typedef std::string::const_iterator iterator_type
;
329 typedef client::calculator
<iterator_type
> calculator
;
330 typedef client::ast::expression ast_expression
;
331 typedef client::compiler compiler
;
334 while (std::getline(std::cin
, str
))
336 if (str
.empty() || str
[0] == 'q' || str
[0] == 'Q')
339 client::vmachine mach
; // Our virtual machine
340 std::vector
<int> code
; // Our VM code
341 calculator calc
; // Our grammar
342 ast_expression expression
; // Our program (AST)
343 compiler
compile(code
); // Compiles the program
345 std::string::const_iterator iter
= str
.begin();
346 std::string::const_iterator end
= str
.end();
347 boost::spirit::ascii::space_type space
;
348 bool r
= phrase_parse(iter
, end
, calc
, space
, expression
);
350 if (r
&& iter
== end
)
352 std::cout
<< "-------------------------\n";
353 std::cout
<< "Parsing succeeded\n";
356 std::cout
<< "\nResult: " << mach
.top() << std::endl
;
357 std::cout
<< "-------------------------\n";
361 std::string
rest(iter
, end
);
362 std::cout
<< "-------------------------\n";
363 std::cout
<< "Parsing failed\n";
364 std::cout
<< "-------------------------\n";
368 std::cout
<< "Bye... :-) \n\n";