]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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> | |
1e59de90 | 20 | #include <boost/core/lightweight_test.hpp> |
7c673cae FG |
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 | { | |
39 | 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& | |
67 | 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 | { | |
86 | 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& | |
119 | start() const { return expression; } | |
120 | }; | |
121 | }; | |
122 | ||
123 | //////////////////////////////////////////////////////////////////////////// | |
124 | using namespace std; | |
125 | using namespace BOOST_SPIRIT_CLASSIC_NS; | |
126 | ||
11fdf7f2 TL |
127 | typedef char const* parser_iterator_t; |
128 | typedef tree_match<parser_iterator_t> parse_tree_match_t; | |
7c673cae FG |
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 | ||
135 | long evaluate(tree_parse_info<> info) | |
136 | { | |
137 | return eval_expression(info.trees.begin()); | |
138 | } | |
139 | ||
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 | |
92f5a8d4 | 184 | std::abort(); |
7c673cae FG |
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 | |
92f5a8d4 | 202 | std::abort(); |
7c673cae FG |
203 | } |
204 | ||
205 | default: | |
92f5a8d4 | 206 | std::abort(); // error |
7c673cae FG |
207 | } |
208 | ||
92f5a8d4 | 209 | #if defined(_MSC_VER) && (_MSC_VER < 1700) |
7c673cae | 210 | return 0; |
92f5a8d4 | 211 | #endif |
7c673cae FG |
212 | } |
213 | ||
214 | //////////////////////////////////////////////////////////////////////////// | |
215 | int | |
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 | |
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 | |
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 |