calc2_ast_vm.cpp 7.6 KB

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