// Change of behaviour in Spirit 2.5.1 regarding collapsing optionals // in sequential-or productions. // // Previous discussion from 2009 related to this: // http://lists.boost.org/Archives/boost/2009/12/159680.php // // // Given the STDIN // // s[:] // // the program below is expected to produce the following output: // // Have range spec... // No begin specified, using container begin. // No end specified, using container end. // Got: 'Range[B:E)' // // However, with 2.5.1, it produces the following: // // Have range spec... // No begin specified, using container begin. // Have range end '' // Got: 'Range[B:)' // // 2.5.1 also fails with the simpler STDIN: // // s[7] // // the program is expected to output: // // Have plain index 7 // Got: 'Index[7]' // // but with 2.5.1 it outputs: // // Have range spec... // Have range begin '7' // No end specified, using container end. // Got: 'Range[7:E)' // // This can be remedied to an extent by switching the #if 1 below // to be #if 0 enabling the collapsed optional code; however this // makes the presence of range-end indistinguishable from the // presence of a range-spec. // #include #include #include // The following dependency was attached to the aforementioned mail // thread, it can be fetched from: // http://lists.boost.org/Archives/boost/att-159667/boost-spirit-nonstandard-qi-directive-identity.hpp // #include using namespace boost::spirit; struct access_index_fn_t { template struct result { typedef std::string type; }; template std::string operator()(Arg1 const& arg1, Arg2 const& access_spec) const; }; boost::phoenix::function access_index; template struct test_grammar : qi::grammar { test_grammar() : test_grammar::base_type(start) { using qi::_1; using qi::ascii::char_; using qi::int_; start = varname >> -idxspec(_val) | digit ; varname = char_('a', 'z'); digit = char_('0', '9'); idxspec = '[' >> identity[start || (':' >> -start)] [ _val = access_index (_r1,_1) ] >> ']'; } qi::rule start; qi::rule varname; qi::rule digit; qi::rule idxspec; }; template std::string access_index_fn_t::operator()(Arg1 const& arg1, Arg2 const& access_spec) const { using boost::optional; using boost::fusion::at_c; #if 1 // XXX: EXPECTED OPERATION: // XXX: // XXX: Works as expected in Spirit 2.5, preserves independent // XXX: optionality of range-spec and range-end. // XXX: // XXX: Breaks badly in Spirit 2.5.1; causing all inputs to be // XXX: considered range-specs // XXX: optional > const& range_spec = at_c<1>(access_spec); #else // XXX: ALMOST WORKAROUND FOR SPIRIT 2.5.1: // XXX: // XXX: Based on supposition that optional<> collapsing has // XXX: something to do with the issue above. // XXX: // XXX: Works to a degree but now impossible to determine // XXX: whether range-end is specified within the optional // XXX: range-spec due to optional collapse. // XXX: // XXX: In effect a tristate scenario (no range-spec, range-spec // XXX: without range-end, or range-spec including range-end) // XXX: has been reduced to a boolean result (no range-spec, or // XXX: range-spec including range-end). // XXX: // XXX: FWIW, in the case where range-end is not specified, the // XXX: value of the optional is blank -- which should not be // XXX: possible if the optional's boolean conversion returns // XXX: true since the 'start' production can never yield an // XXX: empty string. // XXX: optional const& range_spec = at_c<1>(access_spec); #endif if (range_spec) { std::cerr << "Have range spec...\n"; optional const& range_begin = at_c<0>(access_spec); optional const& range_end = *range_spec; std::string res = "Range["; if (range_begin) { std::cerr << "Have range begin '" << *range_begin << "'\n"; res += *range_begin; } else { std::cerr << "No begin specified, using container begin.\n"; res += "B"; } res += ":"; if (range_end) { std::cerr << "Have range end '" << *range_end << "'\n"; res += *range_end; } else { // XXX: impossible to get to with Spirit 2.5.1 due to // XXX: optional collapsing. std::cerr << "No end specified, using container end.\n"; res += "E"; } res += ")"; return res; } else { std::string const& index = *at_c<0>(access_spec); std::cerr << "Have plain index " << index << "\n"; return "Index["+index+"]"; } } #include int main() { test_grammar grammar; istream_iterator it(std::cin), end; std::string res; if (qi::parse(it, end, grammar, res)) std::cerr << "Got: '" << res << "'\n"; else std::cerr << "Failed parse.\n"; }