1 /*============================================================================= 2 Copyright (c) 2001-2014 Joel de Guzman 3 Copyright (c) 2013-2014 Agustin Berge 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. The AST, 11 // once created, is traversed, 1) To print its contents and 12 // 2) To evaluate the result. 13 // 14 // [ JDG April 28, 2008 ] For BoostCon 2008 15 // [ JDG February 18, 2011 ] Pure attributes. No semantic actions. 16 // [ JDG January 9, 2013 ] Spirit X3 17 // 18 /////////////////////////////////////////////////////////////////////////////// 19 20 #if defined(_MSC_VER) 21 # pragma warning(disable: 4345) 22 #endif 23 24 #include <boost/config/warning_disable.hpp> 25 #include <boost/spirit/home/x3.hpp> 26 #include <boost/spirit/home/x3/support/ast/variant.hpp> 27 #include <boost/variant/recursive_variant.hpp> 28 #include <boost/variant/apply_visitor.hpp> 29 #include <boost/fusion/include/adapt_struct.hpp> 30 31 #include <list> 32 #include <numeric> 33 34 namespace x3 = boost::spirit::x3; 35 36 namespace client { namespace ast 37 { 38 /////////////////////////////////////////////////////////////////////////// 39 // The AST 40 /////////////////////////////////////////////////////////////////////////// 41 struct nil {}; 42 struct signed_; 43 struct program; 44 45 typedef x3::variant< 46 nil 47 , unsigned int 48 , x3::forward_ast<signed_> 49 , x3::forward_ast<program> 50 > 51 operand; 52 53 struct signed_ 54 { 55 char sign; 56 operand operand_; 57 }; 58 59 struct operation 60 { 61 char operator_; 62 operand operand_; 63 }; 64 65 struct program 66 { 67 operand first; 68 std::list<operation> rest; 69 }; 70 }} 71 72 BOOST_FUSION_ADAPT_STRUCT(client::ast::signed_, 73 sign, operand_ 74 ) 75 76 BOOST_FUSION_ADAPT_STRUCT(client::ast::operation, 77 operator_, operand_ 78 ) 79 80 BOOST_FUSION_ADAPT_STRUCT(client::ast::program, 81 first, rest 82 ) 83 84 namespace client { namespace ast 85 { 86 /////////////////////////////////////////////////////////////////////////// 87 // The AST Printer 88 /////////////////////////////////////////////////////////////////////////// 89 struct printer 90 { 91 typedef void result_type; 92 operator ()client::ast::printer93 void operator()(nil) const {} operator ()client::ast::printer94 void operator()(unsigned int n) const { std::cout << n; } 95 operator ()client::ast::printer96 void operator()(operation const& x) const 97 { 98 boost::apply_visitor(*this, x.operand_); 99 switch (x.operator_) 100 { 101 case '+': std::cout << " add"; break; 102 case '-': std::cout << " subt"; break; 103 case '*': std::cout << " mult"; break; 104 case '/': std::cout << " div"; break; 105 } 106 } 107 operator ()client::ast::printer108 void operator()(signed_ const& x) const 109 { 110 boost::apply_visitor(*this, x.operand_); 111 switch (x.sign) 112 { 113 case '-': std::cout << " neg"; break; 114 case '+': std::cout << " pos"; break; 115 } 116 } 117 operator ()client::ast::printer118 void operator()(program const& x) const 119 { 120 boost::apply_visitor(*this, x.first); 121 for (operation const& oper: x.rest) 122 { 123 std::cout << ' '; 124 (*this)(oper); 125 } 126 } 127 }; 128 129 /////////////////////////////////////////////////////////////////////////// 130 // The AST evaluator 131 /////////////////////////////////////////////////////////////////////////// 132 struct eval 133 { 134 typedef int result_type; 135 operator ()client::ast::eval136 int operator()(nil) const { BOOST_ASSERT(0); return 0; } operator ()client::ast::eval137 int operator()(unsigned int n) const { return n; } 138 operator ()client::ast::eval139 int operator()(int lhs, operation const& x) const 140 { 141 int rhs = boost::apply_visitor(*this, x.operand_); 142 switch (x.operator_) 143 { 144 case '+': return lhs + rhs; 145 case '-': return lhs - rhs; 146 case '*': return lhs * rhs; 147 case '/': return lhs / rhs; 148 } 149 BOOST_ASSERT(0); 150 return 0; 151 } 152 operator ()client::ast::eval153 int operator()(signed_ const& x) const 154 { 155 int rhs = boost::apply_visitor(*this, x.operand_); 156 switch (x.sign) 157 { 158 case '-': return -rhs; 159 case '+': return +rhs; 160 } 161 BOOST_ASSERT(0); 162 return 0; 163 } 164 operator ()client::ast::eval165 int operator()(program const& x) const 166 { 167 return std::accumulate( x.rest.begin(), x.rest.end() 168 , boost::apply_visitor(*this, x.first) 169 , *this); 170 } 171 }; 172 }} 173 174 namespace client 175 { 176 /////////////////////////////////////////////////////////////////////////////// 177 // The calculator grammar 178 /////////////////////////////////////////////////////////////////////////////// 179 namespace calculator_grammar 180 { 181 using parser_type = 182 x3::any_parser< 183 std::string::const_iterator 184 , ast::program 185 , decltype(x3::make_context<x3::skipper_tag>(x3::ascii::space)) 186 >; 187 188 parser_type calculator(); 189 } 190 191 auto const calculator = calculator_grammar::calculator(); 192 } 193