1 ///////////////////////////////////////////////////////////////////////////////
2 // toy_spirit3.cpp
3 //
4 //  Copyright 2008 Eric Niebler. Distributed under the Boost
5 //  Software License, Version 1.0. (See accompanying file
6 //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 
8 #include <cctype>
9 #include <string>
10 #include <cstring>
11 #include <iomanip>
12 #include <iostream>
13 #include <boost/assert.hpp>
14 #include <boost/mpl/assert.hpp>
15 #include <boost/utility/result_of.hpp>
16 #include <boost/type_traits/is_same.hpp>
17 #include <boost/proto/core.hpp>
18 #include <boost/proto/transform.hpp>
19 #include <boost/fusion/include/for_each.hpp>
20 #include <boost/fusion/include/fold.hpp>
21 #include <boost/fusion/include/cons.hpp>
22 #include <boost/fusion/include/any.hpp>
23 #include <boost/test/unit_test.hpp>
24 
25 namespace boost
26 {
27     // global tags
28     struct char_tag {};
29     struct space_tag {};
30 
31     // global primitives
32     proto::terminal<char_tag>::type const char_ = {{}};
33     proto::terminal<space_tag>::type const space = {{}};
34 
35     using proto::lit;
36     using proto::literal;
37 }
38 
39 namespace boost { namespace spirit2
40 {
41     namespace utility
42     {
char_icmp(char ch,char lo,char hi)43         inline bool char_icmp(char ch, char lo, char hi)
44         {
45             return ch == lo || ch == hi;
46         }
47 
48         template<typename FwdIter>
string_cmp(char const * sz,FwdIter & begin,FwdIter end)49         inline bool string_cmp(char const *sz, FwdIter &begin, FwdIter end)
50         {
51             FwdIter tmp = begin;
52             for(; *sz; ++tmp, ++sz)
53                 if(tmp == end || *tmp != *sz)
54                     return false;
55             begin = tmp;
56             return true;
57         }
58 
59         template<typename FwdIter>
string_icmp(std::string const & str,FwdIter & begin,FwdIter end)60         inline bool string_icmp(std::string const &str, FwdIter &begin, FwdIter end)
61         {
62             BOOST_ASSERT(0 == str.size() % 2);
63             FwdIter tmp = begin;
64             std::string::const_iterator istr = str.begin(), estr = str.end();
65             for(; istr != estr; ++tmp, istr += 2)
66                 if(tmp == end || (*tmp != *istr && *tmp != *(istr+1)))
67                     return false;
68             begin = tmp;
69             return true;
70         }
71 
in_range(char ch,char lo,char hi)72         inline bool in_range(char ch, char lo, char hi)
73         {
74             return ch >= lo && ch <= hi;
75         }
76 
in_irange(char ch,char lo,char hi)77         inline bool in_irange(char ch, char lo, char hi)
78         {
79             return in_range(ch, lo, hi)
80                 || in_range(std::tolower(ch), lo, hi)
81                 || in_range(std::toupper(ch), lo, hi);
82         }
83 
to_istr(char const * sz)84         inline std::string to_istr(char const *sz)
85         {
86             std::string res;
87             res.reserve(std::strlen(sz) * 2);
88             for(; *sz; ++sz)
89             {
90                 res.push_back(std::tolower(*sz));
91                 res.push_back(std::toupper(*sz));
92             }
93             return res;
94         }
95     } // namespace utility
96 
97     template<typename List>
98     struct alternate
99     {
alternateboost::spirit2::alternate100         explicit alternate(List const &list)
101           : elems(list)
102         {}
103         List elems;
104     };
105 
106     template<typename List>
107     struct sequence
108     {
sequenceboost::spirit2::sequence109         explicit sequence(List const &list)
110           : elems(list)
111         {}
112         List elems;
113     };
114 
115     struct char_range
116       : std::pair<char, char>
117     {
char_rangeboost::spirit2::char_range118         char_range(char from, char to)
119           : std::pair<char, char>(from, to)
120         {}
121     };
122 
123     struct ichar
124     {
icharboost::spirit2::ichar125         ichar(char ch)
126           : lo_(std::tolower(ch))
127           , hi_(std::toupper(ch))
128         {}
129 
130         char lo_, hi_;
131     };
132 
133     struct istr
134     {
istrboost::spirit2::istr135         istr(char const *sz)
136           : str_(utility::to_istr(sz))
137         {}
138 
139         std::string str_;
140     };
141 
142     struct ichar_range
143       : std::pair<char, char>
144     {
ichar_rangeboost::spirit2::ichar_range145         ichar_range(char from, char to)
146           : std::pair<char, char>(from, to)
147         {}
148     };
149 
150     // The no-case directive
151     struct no_case_tag {};
152 
153     struct True : mpl::true_ {};
154 
155     ///////////////////////////////////////////////////////////////////////////////
156     /// Begin Spirit grammar here
157     ///////////////////////////////////////////////////////////////////////////////
158     namespace grammar
159     {
160         using namespace proto;
161         using namespace fusion;
162 
163         struct SpiritExpr;
164 
165         struct AnyChar
166           : terminal<char_tag>
167         {};
168 
169         struct CharLiteral
170           : terminal<char>
171         {};
172 
173         struct NTBSLiteral
174           : terminal<char const *>
175         {};
176 
177         struct CharParser
178           : proto::function<AnyChar, CharLiteral>
179         {};
180 
181         struct CharRangeParser
182           : proto::function<AnyChar, CharLiteral, CharLiteral>
183         {};
184 
185         struct NoCase
186           : terminal<no_case_tag>
187         {};
188 
189         // The data determines the case-sensitivity of the terminals
190         typedef _data _icase;
191 
192         // Ugh, would be nice to find a work-around for this:
193         #if BOOST_WORKAROUND(BOOST_MSVC, == 1310)
194         #define _value(x) call<_value(x)>
195         #define True() make<True()>
196         #endif
197 
198         // Extract the child from terminals
199         struct SpiritTerminal
200           : or_<
201                 when< AnyChar,          _value >
202               , when< CharLiteral,      if_<_icase, ichar(_value), _value> >
203               , when< CharParser,       if_<_icase, ichar(_value(_child1)), _value(_child1)> >  // char_('a')
204               , when< NTBSLiteral,      if_<_icase, istr(_value), char const*(_value)> >
205               , when< CharRangeParser,  if_<_icase
206                                             , ichar_range(_value(_child1), _value(_child2))
207                                             , char_range(_value(_child1), _value(_child2))> >   // char_('a','z')
208             >
209         {};
210 
211         struct FoldToList
212           : reverse_fold_tree<_, nil(), cons<SpiritExpr, _state>(SpiritExpr, _state)>
213         {};
214 
215         // sequence rule folds all >>'s together into a list
216         // and wraps the result in a sequence<> wrapper
217         struct SpiritSequence
218           : when< shift_right<SpiritExpr, SpiritExpr>,  sequence<FoldToList>(FoldToList)  >
219         {};
220 
221         // alternate rule folds all |'s together into a list
222         // and wraps the result in a alternate<> wrapper
223         struct SpiritAlternate
224           : when< bitwise_or<SpiritExpr, SpiritExpr>,   alternate<FoldToList>(FoldToList) >
225         {};
226 
227         // Directives such as no_case are handled here
228         struct SpiritDirective
229           : when< subscript<NoCase, SpiritExpr>, SpiritExpr(_right, _state, True()) >
230         {};
231 
232         // A SpiritExpr is an alternate, a sequence, a directive or a terminal
233         struct SpiritExpr
234           : or_<
235                 SpiritSequence
236               , SpiritAlternate
237               , SpiritDirective
238               , SpiritTerminal
239             >
240         {};
241 
242     } // namespace grammar
243 
244     using grammar::SpiritExpr;
245     using grammar::NoCase;
246 
247     ///////////////////////////////////////////////////////////////////////////////
248     /// End SpiritExpr
249     ///////////////////////////////////////////////////////////////////////////////
250 
251     // Globals
252     NoCase::type const no_case = {{}};
253 
254     template<typename Iterator>
255     struct parser;
256 
257     template<typename Iterator>
258     struct fold_alternate
259     {
260         parser<Iterator> const &parse;
261 
fold_alternateboost::spirit2::fold_alternate262         explicit fold_alternate(parser<Iterator> const &p)
263           : parse(p)
264         {}
265 
266         template<typename T>
operator ()boost::spirit2::fold_alternate267         bool operator ()(T const &t) const
268         {
269             Iterator tmp = this->parse.first;
270             if(this->parse(t))
271                 return true;
272             this->parse.first = tmp;
273             return false;
274         }
275     };
276 
277     template<typename Iterator>
278     struct fold_sequence
279     {
280         parser<Iterator> const &parse;
281 
fold_sequenceboost::spirit2::fold_sequence282         explicit fold_sequence(parser<Iterator> const &p)
283           : parse(p)
284         {}
285 
286         typedef bool result_type;
287 
288         template<typename T>
operator ()boost::spirit2::fold_sequence289         bool operator ()(bool success, T const &t) const
290         {
291             return success && this->parse(t);
292         }
293     };
294 
295     template<typename Iterator>
296     struct parser
297     {
298         mutable Iterator first;
299         Iterator second;
300 
parserboost::spirit2::parser301         parser(Iterator begin, Iterator end)
302           : first(begin)
303           , second(end)
304         {}
305 
doneboost::spirit2::parser306         bool done() const
307         {
308             return this->first == this->second;
309         }
310 
311         template<typename List>
operator ()boost::spirit2::parser312         bool operator ()(alternate<List> const &alternates) const
313         {
314             return fusion::any(alternates.elems, fold_alternate<Iterator>(*this));
315         }
316 
317         template<typename List>
operator ()boost::spirit2::parser318         bool operator ()(sequence<List> const &sequence) const
319         {
320             return fusion::fold(sequence.elems, true, fold_sequence<Iterator>(*this));
321         }
322 
operator ()boost::spirit2::parser323         bool operator ()(char_tag ch) const
324         {
325             if(this->done())
326                 return false;
327             ++this->first;
328             return true;
329         }
330 
operator ()boost::spirit2::parser331         bool operator ()(char ch) const
332         {
333             if(this->done() || ch != *this->first)
334                 return false;
335             ++this->first;
336             return true;
337         }
338 
operator ()boost::spirit2::parser339         bool operator ()(ichar ich) const
340         {
341             if(this->done() || !utility::char_icmp(*this->first, ich.lo_, ich.hi_))
342                 return false;
343             ++this->first;
344             return true;
345         }
346 
operator ()boost::spirit2::parser347         bool operator ()(char const *sz) const
348         {
349             return utility::string_cmp(sz, this->first, this->second);
350         }
351 
operator ()boost::spirit2::parser352         bool operator ()(istr const &s) const
353         {
354             return utility::string_icmp(s.str_, this->first, this->second);
355         }
356 
operator ()boost::spirit2::parser357         bool operator ()(char_range rng) const
358         {
359             if(this->done() || !utility::in_range(*this->first, rng.first, rng.second))
360                 return false;
361             ++this->first;
362             return true;
363         }
364 
operator ()boost::spirit2::parser365         bool operator ()(ichar_range rng) const
366         {
367             if(this->done() || !utility::in_irange(*this->first, rng.first, rng.second))
368                 return false;
369             ++this->first;
370             return true;
371         }
372     };
373 
374     template<typename Rule, typename Iterator>
375     typename enable_if<proto::matches< Rule, SpiritExpr >, bool >::type
parse_impl(Rule const & rule,Iterator begin,Iterator end)376     parse_impl(Rule const &rule, Iterator begin, Iterator end)
377     {
378         mpl::false_ is_case_sensitive;
379         parser<Iterator> parse_fun(begin, end);
380         return parse_fun(SpiritExpr()(rule, proto::ignore(), is_case_sensitive));
381     }
382 
383     // 2nd overload provides a short error message for invalid rules
384     template<typename Rule, typename Iterator>
385     typename disable_if<proto::matches< Rule, SpiritExpr >, bool >::type
parse_impl(Rule const & rule,Iterator begin,Iterator end)386     parse_impl(Rule const &rule, Iterator begin, Iterator end)
387     {
388         BOOST_MPL_ASSERT((proto::matches<Rule, SpiritExpr>));
389         return false;
390     }
391 
392     // parse() converts rule literals to proto expressions if necessary
393     // and dispatches to parse_impl
394     template<typename Rule, typename Iterator>
parse(Rule const & rule,Iterator begin,Iterator end)395     bool parse(Rule const &rule, Iterator begin, Iterator end)
396     {
397         return parse_impl(proto::as_expr(rule), begin, end);
398     }
399 
400 }}
401 
test_toy_spirit3()402 void test_toy_spirit3()
403 {
404     using boost::spirit2::no_case;
405     using boost::char_;
406     std::string hello("abcd");
407 
408     BOOST_CHECK(
409         boost::spirit2::parse(
410             "abcd"
411           , hello.begin()
412           , hello.end()
413         )
414     );
415 
416     BOOST_CHECK(
417         boost::spirit2::parse(
418             char_ >> char_('b') >> 'c' >> char_
419           , hello.begin()
420           , hello.end()
421         )
422     );
423 
424     BOOST_CHECK(
425        !boost::spirit2::parse(
426             char_ >> char_('b') >> 'c' >> 'D'
427           , hello.begin()
428           , hello.end()
429         )
430     );
431 
432     BOOST_CHECK(
433         boost::spirit2::parse(
434             char_ >> char_('b') >> 'c' >> 'e'
435           | char_ >> no_case[char_('B') >> "C" >> char_('D','Z')]
436           , hello.begin()
437           , hello.end()
438         )
439     );
440 
441     std::string nest_alt_input("abd");
442     BOOST_CHECK(
443         boost::spirit2::parse(
444             char_('a')
445           >> ( char_('b')
446              | char_('c')
447              )
448           >>  char_('d')
449           , nest_alt_input.begin()
450           , nest_alt_input.end()
451         )
452     );
453 }
454 
455 using namespace boost::unit_test;
456 ///////////////////////////////////////////////////////////////////////////////
457 // init_unit_test_suite
458 //
init_unit_test_suite(int argc,char * argv[])459 test_suite* init_unit_test_suite( int argc, char* argv[] )
460 {
461     test_suite *test = BOOST_TEST_SUITE("test proto, grammars and tree transforms");
462 
463     test->add(BOOST_TEST_CASE(&test_toy_spirit3));
464 
465     return test;
466 }
467