1 /*=============================================================================
2 Copyright (c) 2001-2003 Daniel Nuffer
3 http://spirit.sourceforge.net/
4
5 Use, modification and distribution is subject to the Boost Software
6 License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
7 http://www.boost.org/LICENSE_1_0.txt)
8 =============================================================================*/
9 // JDG 4-16-03 Modified from ast_calc.cpp as a test
10
11 #include <boost/spirit/include/classic_core.hpp>
12 #include <boost/spirit/include/classic_ast.hpp>
13 #include <boost/spirit/include/classic_tree_to_xml.hpp>
14 #include <boost/detail/workaround.hpp>
15
16 #include <iostream>
17 #include <stack>
18 #include <functional>
19 #include <string>
20 #include <boost/detail/lightweight_test.hpp>
21
22 using namespace BOOST_SPIRIT_CLASSIC_NS;
23
24 ////////////////////////////////////////////////////////////////////////////
25 //
26 // Our calculator grammar
27 //
28 ////////////////////////////////////////////////////////////////////////////
29 struct calculator : public grammar<calculator>
30 {
31 static const int integerID = 1;
32 static const int factorID = 2;
33 static const int termID = 3;
34 static const int expressionID = 4;
35
36 template <typename ScannerT>
37 struct definition
38 {
definitioncalculator::definition39 definition(calculator const& /*self*/)
40 {
41 // Start grammar definition
42 integer = leaf_node_d[real_p]; // we're not really using a real
43 // but just for compile checking
44 // the AST tree match code...
45 factor = integer
46 | inner_node_d[ch_p('(') >> expression >> ch_p(')')]
47 | (root_node_d[ch_p('-')] >> factor);
48
49 term = factor >>
50 *( (root_node_d[ch_p('*')] >> factor)
51 | (root_node_d[ch_p('/')] >> factor)
52 );
53
54 expression = term >>
55 *( (root_node_d[ch_p('+')] >> term)
56 | (root_node_d[ch_p('-')] >> term)
57 );
58 // End grammar definition
59 }
60
61 rule<ScannerT, parser_context<>, parser_tag<expressionID> > expression;
62 rule<ScannerT, parser_context<>, parser_tag<termID> > term;
63 rule<ScannerT, parser_context<>, parser_tag<factorID> > factor;
64 rule<ScannerT, parser_context<>, parser_tag<integerID> > integer;
65
66 rule<ScannerT, parser_context<>, parser_tag<expressionID> > const&
startcalculator::definition67 start() const { return expression; }
68 };
69 };
70
71 ////////////////////////////////////////////////////////////////////////////
72 //
73 // Our calculator grammar, but with dynamically assigned rule ID's
74 //
75 ////////////////////////////////////////////////////////////////////////////
76 struct dyn_calculator : public grammar<dyn_calculator>
77 {
78 static const int integerID = 1;
79 static const int factorID = 2;
80 static const int termID = 3;
81 static const int expressionID = 4;
82
83 template <typename ScannerT>
84 struct definition
85 {
definitiondyn_calculator::definition86 definition(dyn_calculator const& /*self*/)
87 {
88 expression.set_id(expressionID);
89 term.set_id(termID);
90 factor.set_id(factorID);
91 integer.set_id(integerID);
92
93 // Start grammar definition
94 integer = leaf_node_d[real_p]; // we're not really using a real
95 // but just for compile checking
96 // the AST tree match code...
97 factor = integer
98 | inner_node_d[ch_p('(') >> expression >> ch_p(')')]
99 | (root_node_d[ch_p('-')] >> factor);
100
101 term = factor >>
102 *( (root_node_d[ch_p('*')] >> factor)
103 | (root_node_d[ch_p('/')] >> factor)
104 );
105
106 expression = term >>
107 *( (root_node_d[ch_p('+')] >> term)
108 | (root_node_d[ch_p('-')] >> term)
109 );
110 // End grammar definition
111 }
112
113 rule<ScannerT, parser_context<>, dynamic_parser_tag> expression;
114 rule<ScannerT, parser_context<>, dynamic_parser_tag> term;
115 rule<ScannerT, parser_context<>, dynamic_parser_tag> factor;
116 rule<ScannerT, parser_context<>, dynamic_parser_tag> integer;
117
118 rule<ScannerT, parser_context<>, dynamic_parser_tag> const&
startdyn_calculator::definition119 start() const { return expression; }
120 };
121 };
122
123 ////////////////////////////////////////////////////////////////////////////
124 using namespace std;
125 using namespace BOOST_SPIRIT_CLASSIC_NS;
126
127 typedef char const* parser_iterator_t;
128 typedef tree_match<parser_iterator_t> parse_tree_match_t;
129 typedef parse_tree_match_t::tree_iterator iter_t;
130
131 ////////////////////////////////////////////////////////////////////////////
132 long evaluate(parse_tree_match_t hit);
133 long eval_expression(iter_t const& i);
134
evaluate(tree_parse_info<> info)135 long evaluate(tree_parse_info<> info)
136 {
137 return eval_expression(info.trees.begin());
138 }
139
eval_expression(iter_t const & i)140 long eval_expression(iter_t const& i)
141 {
142 switch (i->value.id().to_long())
143 {
144 case calculator::integerID:
145 {
146 BOOST_TEST(i->children.size() == 0);
147 // extract integer (not always delimited by '\0')
148 #if BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003))
149 // std::string(iter,iter) constructor has a bug in MWCW 8.3:
150 // in some situations, the null terminator won't be added
151 // and c_str() will return bogus data. Conservatively, I
152 // activate this workaround up to version 8.3.
153 std::vector<char> value(i->value.begin(), i->value.end());
154 value.push_back('\0');
155 return strtol(&value[0], 0, 10);
156 #else
157 string integer(i->value.begin(), i->value.end());
158 return strtol(integer.c_str(), 0, 10);
159 #endif
160 }
161
162 case calculator::factorID:
163 {
164 // factor can only be unary minus
165 BOOST_TEST(*i->value.begin() == '-');
166 return - eval_expression(i->children.begin());
167 }
168
169 case calculator::termID:
170 {
171 if (*i->value.begin() == '*')
172 {
173 BOOST_TEST(i->children.size() == 2);
174 return eval_expression(i->children.begin()) *
175 eval_expression(i->children.begin()+1);
176 }
177 else if (*i->value.begin() == '/')
178 {
179 BOOST_TEST(i->children.size() == 2);
180 return eval_expression(i->children.begin()) /
181 eval_expression(i->children.begin()+1);
182 }
183 else
184 std::abort();
185 }
186
187 case calculator::expressionID:
188 {
189 if (*i->value.begin() == '+')
190 {
191 BOOST_TEST(i->children.size() == 2);
192 return eval_expression(i->children.begin()) +
193 eval_expression(i->children.begin()+1);
194 }
195 else if (*i->value.begin() == '-')
196 {
197 BOOST_TEST(i->children.size() == 2);
198 return eval_expression(i->children.begin()) -
199 eval_expression(i->children.begin()+1);
200 }
201 else
202 std::abort();
203 }
204
205 default:
206 std::abort(); // error
207 }
208
209 #if defined(_MSC_VER) && (_MSC_VER < 1700)
210 return 0;
211 #endif
212 }
213
214 ////////////////////////////////////////////////////////////////////////////
215 int
parse(char const * str)216 parse(char const* str)
217 {
218 calculator calc;
219 tree_parse_info<> info = ast_parse(str, calc, space_p);
220
221 if (info.full)
222 return evaluate(info);
223 else
224 return -1;
225 }
226
227 int
parse_dyn(char const * str)228 parse_dyn(char const* str)
229 {
230 dyn_calculator calc;
231 tree_parse_info<> info = ast_parse(str, calc, space_p);
232
233 if (info.full)
234 return evaluate(info);
235 else
236 return -1;
237 }
238
239 int
main()240 main()
241 {
242 // test the calculator with statically assigned rule ID's
243 BOOST_TEST(parse("12345") == 12345);
244 BOOST_TEST(parse("-12345") == -12345);
245 BOOST_TEST(parse("1 + 2") == 1 + 2);
246 BOOST_TEST(parse("1 * 2") == 1 * 2);
247 BOOST_TEST(parse("1/2 + 3/4") == 1/2 + 3/4);
248 BOOST_TEST(parse("1 + 2 + 3 + 4") == 1 + 2 + 3 + 4);
249 BOOST_TEST(parse("1 * 2 * 3 * 4") == 1 * 2 * 3 * 4);
250 BOOST_TEST(parse("(1 + 2) * (3 + 4)") == (1 + 2) * (3 + 4));
251 BOOST_TEST(parse("(-1 + 2) * (3 + -4)") == (-1 + 2) * (3 + -4));
252 BOOST_TEST(parse("1 + ((6 * 200) - 20) / 6") == 1 + ((6 * 200) - 20) / 6);
253 BOOST_TEST(parse("(1 + (2 + (3 + (4 + 5))))") == (1 + (2 + (3 + (4 + 5)))));
254 BOOST_TEST(parse("1 + 2 + 3 + 4 + 5") == 1 + 2 + 3 + 4 + 5);
255 BOOST_TEST(parse("(12 * 22) + (36 + -4 + 5)") == (12 * 22) + (36 + -4 + 5));
256 BOOST_TEST(parse("(12 * 22) / (5 - 10 + 15)") == (12 * 22) / (5 - 10 + 15));
257 BOOST_TEST(parse("12 * 6 * 15 + 5 - 25") == 12 * 6 * 15 + 5 - 25);
258
259 // test the calculator with dynamically assigned rule ID's
260 BOOST_TEST(parse_dyn("12345") == 12345);
261 BOOST_TEST(parse_dyn("-12345") == -12345);
262 BOOST_TEST(parse_dyn("1 + 2") == 1 + 2);
263 BOOST_TEST(parse_dyn("1 * 2") == 1 * 2);
264 BOOST_TEST(parse_dyn("1/2 + 3/4") == 1/2 + 3/4);
265 BOOST_TEST(parse_dyn("1 + 2 + 3 + 4") == 1 + 2 + 3 + 4);
266 BOOST_TEST(parse_dyn("1 * 2 * 3 * 4") == 1 * 2 * 3 * 4);
267 BOOST_TEST(parse_dyn("(1 + 2) * (3 + 4)") == (1 + 2) * (3 + 4));
268 BOOST_TEST(parse_dyn("(-1 + 2) * (3 + -4)") == (-1 + 2) * (3 + -4));
269 BOOST_TEST(parse_dyn("1 + ((6 * 200) - 20) / 6") == 1 + ((6 * 200) - 20) / 6);
270 BOOST_TEST(parse_dyn("(1 + (2 + (3 + (4 + 5))))") == (1 + (2 + (3 + (4 + 5)))));
271 BOOST_TEST(parse_dyn("1 + 2 + 3 + 4 + 5") == 1 + 2 + 3 + 4 + 5);
272 BOOST_TEST(parse_dyn("(12 * 22) + (36 + -4 + 5)") == (12 * 22) + (36 + -4 + 5));
273 BOOST_TEST(parse_dyn("(12 * 22) / (5 - 10 + 15)") == (12 * 22) / (5 - 10 + 15));
274 BOOST_TEST(parse_dyn("12 * 6 * 15 + 5 - 25") == 12 * 6 * 15 + 5 - 25);
275
276 return boost::report_errors();
277 }
278
279
280