• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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