• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*=============================================================================
2     Copyright (c) 2001-2010 Joel de Guzman
3     Copyright (c) 2001-2010 Hartmut Kaiser
4 
5     Distributed under the Boost Software License, Version 1.0. (See accompanying
6     file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 =============================================================================*/
8 ///////////////////////////////////////////////////////////////////////////////
9 //
10 //  A Calculator example demonstrating generation of AST from which we generate
11 //  a simple byte code representation being interpreted by a similar virtual
12 //  machine.
13 //
14 //  [ JDG April 28, 2008 ]
15 //  [ HK May 05, 2008 ]
16 //
17 ///////////////////////////////////////////////////////////////////////////////
18 #include <boost/config/warning_disable.hpp>
19 
20 #include <iostream>
21 #include <vector>
22 #include <string>
23 
24 #include "calc2_ast_vm.hpp"
25 
26 #include <boost/spirit/include/qi.hpp>
27 #include <boost/spirit/include/karma.hpp>
28 #include <boost/fusion/include/adapt_struct.hpp>
29 
30 using namespace boost::spirit;
31 using namespace boost::spirit::ascii;
32 
33 ///////////////////////////////////////////////////////////////////////////////
34 //  Our calculator parser grammar
35 ///////////////////////////////////////////////////////////////////////////////
36 template <typename Iterator>
37 struct calculator
38   : qi::grammar<Iterator, expression_ast(), space_type>
39 {
calculatorcalculator40     calculator() : calculator::base_type(expression)
41     {
42         expression =
43             term                            [_val = _1]
44             >> *(   ('+' >> term            [_val += _1])
45                 |   ('-' >> term            [_val -= _1])
46                 )
47             ;
48 
49         term =
50             factor                          [_val = _1]
51             >> *(   ('*' >> factor          [_val *= _1])
52                 |   ('/' >> factor          [_val /= _1])
53                 )
54             ;
55 
56         factor =
57             uint_                           [_val = _1]
58             |   '(' >> expression           [_val = _1] >> ')'
59             |   ('-' >> factor              [_val = neg(_1)])
60             |   ('+' >> factor              [_val = pos(_1)])
61             ;
62     }
63 
64     qi::rule<Iterator, expression_ast(), space_type> expression, term, factor;
65 };
66 
67 ///////////////////////////////////////////////////////////////////////////////
68 //  The Virtual Machine
69 ///////////////////////////////////////////////////////////////////////////////
70 class vmachine
71 {
72 public:
73     union element {
74         int code;
75         char bytes[sizeof(int)];
76     };
77 
vmachine(unsigned stackSize=4096)78     vmachine(unsigned stackSize = 4096)
79       : stack(stackSize)
80       , stack_ptr(stack.begin())
81     {
82     }
83 
top() const84     int top() const { return stack_ptr[-1]; };
85     void execute(std::vector<element> const& code);
86 
87 private:
88     std::vector<int> stack;
89     std::vector<int>::iterator stack_ptr;
90 };
91 
execute(std::vector<element> const & code)92 void vmachine::execute(std::vector<element> const& code)
93 {
94     std::vector<element>::const_iterator pc = code.begin();
95     stack_ptr = stack.begin();
96 
97     while ((*pc).code && pc != code.end())
98     {
99         switch ((*pc++).code)
100         {
101             case op_neg:
102                 stack_ptr[-1] = -stack_ptr[-1];
103                 break;
104 
105             case op_add:
106                 --stack_ptr;
107                 stack_ptr[-1] += stack_ptr[0];
108                 break;
109 
110             case op_sub:
111                 --stack_ptr;
112                 stack_ptr[-1] -= stack_ptr[0];
113                 break;
114 
115             case op_mul:
116                 --stack_ptr;
117                 stack_ptr[-1] *= stack_ptr[0];
118                 break;
119 
120             case op_div:
121                 --stack_ptr;
122                 stack_ptr[-1] /= stack_ptr[0];
123                 break;
124 
125             case op_int:
126                 *stack_ptr++ = (*pc++).code;
127                 break;
128         }
129     }
130 }
131 
132 // We need to tell fusion about our binary_op and unary_op structs
133 // to make them a first-class fusion citizen
134 //
135 // Note: we register the members exactly in the same sequence as we need them
136 //       in the grammar
137 BOOST_FUSION_ADAPT_STRUCT(
138     binary_op,
139     (expression_ast, left)
140     (expression_ast, right)
141     (int, op)
142 )
143 
144 BOOST_FUSION_ADAPT_STRUCT(
145     unary_op,
146     (expression_ast, right)
147     (int, op)
148 )
149 
150 ///////////////////////////////////////////////////////////////////////////////
151 //  Our AST grammar for the generator, this just dumps the AST as a expression
152 ///////////////////////////////////////////////////////////////////////////////
153 template <typename OuputIterator, typename Delimiter>
154 struct generate_byte_code
155   : karma::grammar<OuputIterator, expression_ast(), Delimiter>
156 {
generate_byte_codegenerate_byte_code157     generate_byte_code() : generate_byte_code::base_type(ast_node)
158     {
159         ast_node %= int_node | binary_node | unary_node;
160         int_node %= dword(op_int) << dword;
161         binary_node %= ast_node << ast_node << byte_;
162         unary_node %= ast_node << byte_;
163     }
164 
165     karma::rule<OuputIterator, expression_ast(), Delimiter> ast_node;
166     karma::rule<OuputIterator, int(), Delimiter> int_node;
167     karma::rule<OuputIterator, binary_op(), Delimiter> binary_node;
168     karma::rule<OuputIterator, unary_op(), Delimiter> unary_node;
169 };
170 
171 ///////////////////////////////////////////////////////////////////////////////
172 // helper function helping to deduce the delimiter type
173 template <typename Delimiter>
generate_vm_code(expression_ast const & ast,std::vector<vmachine::element> & code,Delimiter const & d)174 bool generate_vm_code(expression_ast const& ast,
175     std::vector<vmachine::element>& code, Delimiter const& d)
176 {
177     // Our generator grammar definitions
178     typedef char* output_iterator_type;
179     typedef generate_byte_code<output_iterator_type, Delimiter> generate_byte_code;
180 
181     char* outbuffer = (*code.begin()).bytes;
182     generate_byte_code gen_vm;
183     return karma::generate_delimited(outbuffer, gen_vm, d, ast);
184 }
185 
186 ///////////////////////////////////////////////////////////////////////////////
187 //  Main program
188 ///////////////////////////////////////////////////////////////////////////////
189 int
main()190 main()
191 {
192     std::cout << "/////////////////////////////////////////////////////////\n\n";
193     std::cout << "Compile simple expressions to bytecode...\n\n";
194     std::cout << "/////////////////////////////////////////////////////////\n\n";
195     std::cout << "Type an expression...or [q or Q] to quit\n\n";
196 
197     //  Our parser grammar definitions
198     typedef std::string::const_iterator iterator_type;
199     typedef calculator<iterator_type> calculator;
200 
201     calculator calc;
202 
203     std::string str;
204     while (std::getline(std::cin, str))
205     {
206         if (str.empty() || str[0] == 'q' || str[0] == 'Q')
207             break;
208 
209         expression_ast ast;
210         std::string::const_iterator iter = str.begin();
211         std::string::const_iterator end = str.end();
212         bool r = qi::phrase_parse(iter, end, calc, space, ast);
213 
214         if (r && iter == end)
215         {
216             // we assume a vm code size of 4096 is sufficient
217             std::vector<vmachine::element> code (4096);
218             r = generate_vm_code(ast, code, pad(4));
219 
220             if (r)
221             {
222                 vmachine vm;
223                 vm.execute(code);
224                 std::cout << "\nresult = " << vm.top() << std::endl;
225                 std::cout << "-------------------------\n";
226             }
227             else
228             {
229                 std::cout << "-------------------------\n";
230                 std::cout << "Generating failed\n";
231                 std::cout << "-------------------------\n";
232             }
233         }
234         else
235         {
236             std::string rest(iter, end);
237             std::cout << "-------------------------\n";
238             std::cout << "Parsing failed\n";
239             std::cout << "stopped at: \": " << rest << "\"\n";
240             std::cout << "-------------------------\n";
241         }
242     }
243 
244     std::cout << "Bye... :-) \n\n";
245     return 0;
246 }
247 
248 
249