123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- ///////////////////////////////////////////////////////////////////////////////
- // toy_spirit3.cpp
- //
- // Copyright 2008 Eric Niebler. Distributed under the Boost
- // Software License, Version 1.0. (See accompanying file
- // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- #include <cctype>
- #include <string>
- #include <cstring>
- #include <iomanip>
- #include <iostream>
- #include <boost/assert.hpp>
- #include <boost/mpl/assert.hpp>
- #include <boost/utility/result_of.hpp>
- #include <boost/type_traits/is_same.hpp>
- #include <boost/proto/core.hpp>
- #include <boost/proto/transform.hpp>
- #include <boost/fusion/include/for_each.hpp>
- #include <boost/fusion/include/fold.hpp>
- #include <boost/fusion/include/cons.hpp>
- #include <boost/fusion/include/any.hpp>
- #include <boost/test/unit_test.hpp>
- namespace boost
- {
- // global tags
- struct char_tag {};
- struct space_tag {};
- // global primitives
- proto::terminal<char_tag>::type const char_ = {{}};
- proto::terminal<space_tag>::type const space = {{}};
- using proto::lit;
- using proto::literal;
- }
- namespace boost { namespace spirit2
- {
- namespace utility
- {
- inline bool char_icmp(char ch, char lo, char hi)
- {
- return ch == lo || ch == hi;
- }
- template<typename FwdIter>
- inline bool string_cmp(char const *sz, FwdIter &begin, FwdIter end)
- {
- FwdIter tmp = begin;
- for(; *sz; ++tmp, ++sz)
- if(tmp == end || *tmp != *sz)
- return false;
- begin = tmp;
- return true;
- }
- template<typename FwdIter>
- inline bool string_icmp(std::string const &str, FwdIter &begin, FwdIter end)
- {
- BOOST_ASSERT(0 == str.size() % 2);
- FwdIter tmp = begin;
- std::string::const_iterator istr = str.begin(), estr = str.end();
- for(; istr != estr; ++tmp, istr += 2)
- if(tmp == end || (*tmp != *istr && *tmp != *(istr+1)))
- return false;
- begin = tmp;
- return true;
- }
- inline bool in_range(char ch, char lo, char hi)
- {
- return ch >= lo && ch <= hi;
- }
- inline bool in_irange(char ch, char lo, char hi)
- {
- return in_range(ch, lo, hi)
- || in_range(std::tolower(ch), lo, hi)
- || in_range(std::toupper(ch), lo, hi);
- }
- inline std::string to_istr(char const *sz)
- {
- std::string res;
- res.reserve(std::strlen(sz) * 2);
- for(; *sz; ++sz)
- {
- res.push_back(std::tolower(*sz));
- res.push_back(std::toupper(*sz));
- }
- return res;
- }
- } // namespace utility
- template<typename List>
- struct alternate
- {
- explicit alternate(List const &list)
- : elems(list)
- {}
- List elems;
- };
- template<typename List>
- struct sequence
- {
- explicit sequence(List const &list)
- : elems(list)
- {}
- List elems;
- };
- struct char_range
- : std::pair<char, char>
- {
- char_range(char from, char to)
- : std::pair<char, char>(from, to)
- {}
- };
- struct ichar
- {
- ichar(char ch)
- : lo_(std::tolower(ch))
- , hi_(std::toupper(ch))
- {}
- char lo_, hi_;
- };
- struct istr
- {
- istr(char const *sz)
- : str_(utility::to_istr(sz))
- {}
- std::string str_;
- };
- struct ichar_range
- : std::pair<char, char>
- {
- ichar_range(char from, char to)
- : std::pair<char, char>(from, to)
- {}
- };
- // The no-case directive
- struct no_case_tag {};
- struct True : mpl::true_ {};
- ///////////////////////////////////////////////////////////////////////////////
- /// Begin Spirit grammar here
- ///////////////////////////////////////////////////////////////////////////////
- namespace grammar
- {
- using namespace proto;
- using namespace fusion;
- struct SpiritExpr;
- struct AnyChar
- : terminal<char_tag>
- {};
- struct CharLiteral
- : terminal<char>
- {};
- struct NTBSLiteral
- : terminal<char const *>
- {};
- struct CharParser
- : proto::function<AnyChar, CharLiteral>
- {};
- struct CharRangeParser
- : proto::function<AnyChar, CharLiteral, CharLiteral>
- {};
- struct NoCase
- : terminal<no_case_tag>
- {};
- // The data determines the case-sensitivity of the terminals
- typedef _data _icase;
- // Ugh, would be nice to find a work-around for this:
- #if BOOST_WORKAROUND(BOOST_MSVC, == 1310)
- #define _value(x) call<_value(x)>
- #define True() make<True()>
- #endif
- // Extract the child from terminals
- struct SpiritTerminal
- : or_<
- when< AnyChar, _value >
- , when< CharLiteral, if_<_icase, ichar(_value), _value> >
- , when< CharParser, if_<_icase, ichar(_value(_child1)), _value(_child1)> > // char_('a')
- , when< NTBSLiteral, if_<_icase, istr(_value), char const*(_value)> >
- , when< CharRangeParser, if_<_icase
- , ichar_range(_value(_child1), _value(_child2))
- , char_range(_value(_child1), _value(_child2))> > // char_('a','z')
- >
- {};
- struct FoldToList
- : reverse_fold_tree<_, nil(), cons<SpiritExpr, _state>(SpiritExpr, _state)>
- {};
- // sequence rule folds all >>'s together into a list
- // and wraps the result in a sequence<> wrapper
- struct SpiritSequence
- : when< shift_right<SpiritExpr, SpiritExpr>, sequence<FoldToList>(FoldToList) >
- {};
- // alternate rule folds all |'s together into a list
- // and wraps the result in a alternate<> wrapper
- struct SpiritAlternate
- : when< bitwise_or<SpiritExpr, SpiritExpr>, alternate<FoldToList>(FoldToList) >
- {};
- // Directives such as no_case are handled here
- struct SpiritDirective
- : when< subscript<NoCase, SpiritExpr>, SpiritExpr(_right, _state, True()) >
- {};
- // A SpiritExpr is an alternate, a sequence, a directive or a terminal
- struct SpiritExpr
- : or_<
- SpiritSequence
- , SpiritAlternate
- , SpiritDirective
- , SpiritTerminal
- >
- {};
- } // namespace grammar
- using grammar::SpiritExpr;
- using grammar::NoCase;
- ///////////////////////////////////////////////////////////////////////////////
- /// End SpiritExpr
- ///////////////////////////////////////////////////////////////////////////////
- // Globals
- NoCase::type const no_case = {{}};
- template<typename Iterator>
- struct parser;
- template<typename Iterator>
- struct fold_alternate
- {
- parser<Iterator> const &parse;
- explicit fold_alternate(parser<Iterator> const &p)
- : parse(p)
- {}
- template<typename T>
- bool operator ()(T const &t) const
- {
- Iterator tmp = this->parse.first;
- if(this->parse(t))
- return true;
- this->parse.first = tmp;
- return false;
- }
- };
- template<typename Iterator>
- struct fold_sequence
- {
- parser<Iterator> const &parse;
- explicit fold_sequence(parser<Iterator> const &p)
- : parse(p)
- {}
- typedef bool result_type;
- template<typename T>
- bool operator ()(bool success, T const &t) const
- {
- return success && this->parse(t);
- }
- };
- template<typename Iterator>
- struct parser
- {
- mutable Iterator first;
- Iterator second;
- parser(Iterator begin, Iterator end)
- : first(begin)
- , second(end)
- {}
- bool done() const
- {
- return this->first == this->second;
- }
- template<typename List>
- bool operator ()(alternate<List> const &alternates) const
- {
- return fusion::any(alternates.elems, fold_alternate<Iterator>(*this));
- }
- template<typename List>
- bool operator ()(sequence<List> const &sequence) const
- {
- return fusion::fold(sequence.elems, true, fold_sequence<Iterator>(*this));
- }
- bool operator ()(char_tag ch) const
- {
- if(this->done())
- return false;
- ++this->first;
- return true;
- }
- bool operator ()(char ch) const
- {
- if(this->done() || ch != *this->first)
- return false;
- ++this->first;
- return true;
- }
- bool operator ()(ichar ich) const
- {
- if(this->done() || !utility::char_icmp(*this->first, ich.lo_, ich.hi_))
- return false;
- ++this->first;
- return true;
- }
- bool operator ()(char const *sz) const
- {
- return utility::string_cmp(sz, this->first, this->second);
- }
- bool operator ()(istr const &s) const
- {
- return utility::string_icmp(s.str_, this->first, this->second);
- }
- bool operator ()(char_range rng) const
- {
- if(this->done() || !utility::in_range(*this->first, rng.first, rng.second))
- return false;
- ++this->first;
- return true;
- }
- bool operator ()(ichar_range rng) const
- {
- if(this->done() || !utility::in_irange(*this->first, rng.first, rng.second))
- return false;
- ++this->first;
- return true;
- }
- };
- template<typename Rule, typename Iterator>
- typename enable_if<proto::matches< Rule, SpiritExpr >, bool >::type
- parse_impl(Rule const &rule, Iterator begin, Iterator end)
- {
- mpl::false_ is_case_sensitive;
- parser<Iterator> parse_fun(begin, end);
- return parse_fun(SpiritExpr()(rule, proto::ignore(), is_case_sensitive));
- }
- // 2nd overload provides a short error message for invalid rules
- template<typename Rule, typename Iterator>
- typename disable_if<proto::matches< Rule, SpiritExpr >, bool >::type
- parse_impl(Rule const &rule, Iterator begin, Iterator end)
- {
- BOOST_MPL_ASSERT((proto::matches<Rule, SpiritExpr>));
- return false;
- }
- // parse() converts rule literals to proto expressions if necessary
- // and dispatches to parse_impl
- template<typename Rule, typename Iterator>
- bool parse(Rule const &rule, Iterator begin, Iterator end)
- {
- return parse_impl(proto::as_expr(rule), begin, end);
- }
- }}
- void test_toy_spirit3()
- {
- using boost::spirit2::no_case;
- using boost::char_;
- std::string hello("abcd");
- BOOST_CHECK(
- boost::spirit2::parse(
- "abcd"
- , hello.begin()
- , hello.end()
- )
- );
- BOOST_CHECK(
- boost::spirit2::parse(
- char_ >> char_('b') >> 'c' >> char_
- , hello.begin()
- , hello.end()
- )
- );
- BOOST_CHECK(
- !boost::spirit2::parse(
- char_ >> char_('b') >> 'c' >> 'D'
- , hello.begin()
- , hello.end()
- )
- );
- BOOST_CHECK(
- boost::spirit2::parse(
- char_ >> char_('b') >> 'c' >> 'e'
- | char_ >> no_case[char_('B') >> "C" >> char_('D','Z')]
- , hello.begin()
- , hello.end()
- )
- );
- std::string nest_alt_input("abd");
- BOOST_CHECK(
- boost::spirit2::parse(
- char_('a')
- >> ( char_('b')
- | char_('c')
- )
- >> char_('d')
- , nest_alt_input.begin()
- , nest_alt_input.end()
- )
- );
- }
- using namespace boost::unit_test;
- ///////////////////////////////////////////////////////////////////////////////
- // init_unit_test_suite
- //
- test_suite* init_unit_test_suite( int argc, char* argv[] )
- {
- test_suite *test = BOOST_TEST_SUITE("test proto, grammars and tree transforms");
- test->add(BOOST_TEST_CASE(&test_toy_spirit3));
- return test;
- }
|