/////////////////////////////////////////////////////////////////////////////// // 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { // global tags struct char_tag {}; struct space_tag {}; // global primitives proto::terminal::type const char_ = {{}}; proto::terminal::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 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 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 struct alternate { explicit alternate(List const &list) : elems(list) {} List elems; }; template struct sequence { explicit sequence(List const &list) : elems(list) {} List elems; }; struct char_range : std::pair { char_range(char from, char to) : std::pair(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 { ichar_range(char from, char to) : std::pair(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 {}; struct CharLiteral : terminal {}; struct NTBSLiteral : terminal {}; struct CharParser : proto::function {}; struct CharRangeParser : proto::function {}; struct NoCase : terminal {}; // 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 #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)> {}; // sequence rule folds all >>'s together into a list // and wraps the result in a sequence<> wrapper struct SpiritSequence : when< shift_right, sequence(FoldToList) > {}; // alternate rule folds all |'s together into a list // and wraps the result in a alternate<> wrapper struct SpiritAlternate : when< bitwise_or, alternate(FoldToList) > {}; // Directives such as no_case are handled here struct SpiritDirective : when< subscript, 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 struct parser; template struct fold_alternate { parser const &parse; explicit fold_alternate(parser const &p) : parse(p) {} template bool operator ()(T const &t) const { Iterator tmp = this->parse.first; if(this->parse(t)) return true; this->parse.first = tmp; return false; } }; template struct fold_sequence { parser const &parse; explicit fold_sequence(parser const &p) : parse(p) {} typedef bool result_type; template bool operator ()(bool success, T const &t) const { return success && this->parse(t); } }; template 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 bool operator ()(alternate const &alternates) const { return fusion::any(alternates.elems, fold_alternate(*this)); } template bool operator ()(sequence const &sequence) const { return fusion::fold(sequence.elems, true, fold_sequence(*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 enable_if, bool >::type parse_impl(Rule const &rule, Iterator begin, Iterator end) { mpl::false_ is_case_sensitive; parser 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 disable_if, bool >::type parse_impl(Rule const &rule, Iterator begin, Iterator end) { BOOST_MPL_ASSERT((proto::matches)); return false; } // parse() converts rule literals to proto expressions if necessary // and dispatches to parse_impl template 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; }