Index: boost/spirit/repository/home/qi/nonterminal/subrule.hpp =================================================================== --- boost/spirit/repository/home/qi/nonterminal/subrule.hpp (revision 75120) +++ boost/spirit/repository/home/qi/nonterminal/subrule.hpp (working copy) @@ -213,12 +213,16 @@ // without passing values for them. context_type context(*this, attr_); - if (def.binder(first, last, context, skipper)) + Iterator i = first; + + if (def.binder(i, last, context, skipper)) { // do up-stream transformation, this integrates the results // back into the original attribute value, if appropriate - traits::post_transform(attr, attr_); - return true; + if (traits::post_transform(attr, attr_)) { + first = i; + return true; + } } // inform attribute transformation of failed rhs @@ -267,12 +271,16 @@ // passing values of incompatible types for them. context_type context(*this, attr_, params, caller_context); - if (def.binder(first, last, context, skipper)) + Iterator i = first; + + if (def.binder(i, last, context, skipper)) { // do up-stream transformation, this integrates the results // back into the original attribute value, if appropriate - traits::post_transform(attr, attr_); - return true; + if (traits::post_transform(attr, attr_)) { + first = i; + return true; + } } // inform attribute transformation of failed rhs Index: boost/spirit/home/qi/nonterminal/rule.hpp =================================================================== --- boost/spirit/home/qi/nonterminal/rule.hpp (revision 75120) +++ boost/spirit/home/qi/nonterminal/rule.hpp (working copy) @@ -276,9 +276,11 @@ { if (f) { + Iterator iter = first; + // do a preskip if this is an implied lexeme if (is_same::value) - qi::skip_over(first, last, skipper); + qi::skip_over(iter, last, skipper); typedef traits::make_attribute make_attribute; @@ -300,12 +302,14 @@ // fourth parameter can't be converted to a required target type // then you are probably trying to use a rule or a grammar with // an incompatible skipper type. - if (f(first, last, context, skipper)) + if (f(iter, last, context, skipper)) { // do up-stream transformation, this integrates the results // back into the original attribute value, if appropriate - traits::post_transform(attr, attr_); - return true; + if (traits::post_transform(attr, attr_)) { + first = iter; + return true; + } } // inform attribute transformation of failed rhs @@ -322,9 +326,11 @@ { if (f) { + Iterator iter = first; + // do a preskip if this is an implied lexeme if (is_same::value) - qi::skip_over(first, last, skipper); + qi::skip_over(iter, last, skipper); typedef traits::make_attribute make_attribute; @@ -346,12 +352,14 @@ // fourth parameter can't be converted to a required target type // then you are probably trying to use a rule or a grammar with // an incompatible skipper type. - if (f(first, last, context, skipper)) + if (f(iter, last, context, skipper)) { // do up-stream transformation, this integrates the results // back into the original attribute value, if appropriate - traits::post_transform(attr, attr_); - return true; + if (traits::post_transform(attr, attr_)) { + first = iter; + return true; + } } // inform attribute transformation of failed rhs Index: boost/spirit/home/qi/detail/attributes.hpp =================================================================== --- boost/spirit/home/qi/detail/attributes.hpp (revision 75120) +++ boost/spirit/home/qi/detail/attributes.hpp (working copy) @@ -22,9 +22,10 @@ static Transformed pre(Exposed&) { return Transformed(); } - static void post(Exposed& val, Transformed const& attr) + static bool post(Exposed& val, Transformed const& attr) { traits::assign_to(attr, val); + return true; } // fail() will be called by Qi rule's if the rhs failed parsing @@ -37,7 +38,7 @@ { typedef Attribute& type; static Attribute& pre(Attribute& val) { return val; } - static void post(Attribute&, Attribute const&) {} + static bool post(Attribute&, Attribute const&) { return true; } static void fail(Attribute&) {} }; @@ -47,7 +48,7 @@ typedef Transformed type; static Transformed pre(Exposed& val) { return Transformed(val); } - static void post(Exposed&, Transformed const&) { /* no-op */ } + static bool post(Exposed&, Transformed const&) { return true; } // fail() will be called by Qi rule's if the rhs failed parsing static void fail(Exposed&) {} @@ -59,7 +60,7 @@ { typedef Attribute& type; static Attribute& pre(Attribute& val) { return val; } - static void post(Attribute&, Attribute const&) {} + static bool post(Attribute&, Attribute const&) { return true; } static void fail(Attribute&) {} }; @@ -87,7 +88,7 @@ val = Transformed(); return boost::get(val); } - static void post(boost::optional&, Transformed const&) {} + static bool post(boost::optional&, Transformed const&) { return true; } static void fail(boost::optional& val) { val = none_t(); // leave optional uninitialized if rhs failed @@ -100,7 +101,7 @@ { typedef Attribute& type; static Attribute& pre(Attribute& val) { return val; } - static void post(Attribute&, Attribute const&) {} + static bool post(Attribute&, Attribute const&) { return true; } static void fail(Attribute&) {} }; @@ -110,7 +111,7 @@ { typedef unused_type type; static unused_type pre(unused_type) { return unused; } - static void post(unused_type, unused_type) {} + static bool post(unused_type, unused_type) { return true; } static void fail(unused_type) {} }; @@ -160,7 +161,7 @@ /////////////////////////////////////////////////////////////////////////// template - void post_transform(Exposed& dest, Transformed const& attr) + bool post_transform(Exposed& dest, Transformed const& attr) { return transform_attribute::post(dest, attr); } Index: boost/spirit/home/qi/auxiliary/attr_cast.hpp =================================================================== --- boost/spirit/home/qi/auxiliary/attr_cast.hpp (revision 75120) +++ boost/spirit/home/qi/auxiliary/attr_cast.hpp (working copy) @@ -92,17 +92,21 @@ typename transform::type attr_ = transform::pre(attr); - if (!compile(subject). - parse(first, last, context, skipper, attr_)) + Iterator iter = first; + + if (compile(subject). + parse(iter, last, context, skipper, attr_)) { - transform::fail(attr); - return false; + // do up-stream transformation, this mainly integrates the results + // back into the original attribute value, if appropriate + if (traits::post_transform(attr, attr_)) { + first = iter; + return true; + } } - // do up-stream transformation, this mainly integrates the results - // back into the original attribute value, if appropriate - traits::post_transform(attr, attr_); - return true; + transform::fail(attr); + return false; } template Index: boost/spirit/home/qi/action/action.hpp =================================================================== --- boost/spirit/home/qi/action/action.hpp (revision 75120) +++ boost/spirit/home/qi/action/action.hpp (working copy) @@ -61,8 +61,8 @@ typename make_attribute::type made_attr = make_attribute::call(attr_); typename transform::type attr = transform::pre(made_attr); - Iterator save = first; - if (subject.parse(first, last, context, skipper, attr)) + Iterator iter = first; + if (subject.parse(iter, last, context, skipper, attr)) { // call the function, passing the attribute, the context. // The client can return false to fail parsing. @@ -70,13 +70,11 @@ { // Do up-stream transformation, this integrates the results // back into the original attribute value, if appropriate. - traits::post_transform(attr_, attr); - return true; + if (traits::post_transform(attr_, attr)) { + first = iter; + return true; + } } - - // reset iterators if semantic action failed the match - // retrospectively - first = save; } return false; } Index: boost/spirit/home/support/adapt_adt_attributes.hpp =================================================================== --- boost/spirit/home/support/adapt_adt_attributes.hpp (revision 75120) +++ boost/spirit/home/support/adapt_adt_attributes.hpp (working copy) @@ -257,12 +257,13 @@ { return val; } - static void + static bool post( fusion::extension::adt_attribute_proxy& val , Attribute const& attr) { val = attr; + return true; } static void fail(fusion::extension::adt_attribute_proxy&) @@ -289,11 +290,12 @@ { return val; } - static void + static bool post( fusion::extension::adt_attribute_proxy& , Attribute const&) { + return true; } static void fail(fusion::extension::adt_attribute_proxy&) Index: libs/spirit/test/qi/attribute1.cpp =================================================================== --- libs/spirit/test/qi/attribute1.cpp (revision 75120) +++ libs/spirit/test/qi/attribute1.cpp (working copy) @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -60,7 +61,7 @@ { typedef int type; static int pre(test_int_data1& d) { return d.i; } - static void post(test_int_data1& d, int i) { d.i = i; } + static bool post(test_int_data1& d, int i) { d.i = i; return true; } static void fail(test_int_data1&) {} }; }}} @@ -80,12 +81,47 @@ { typedef int& type; static int& pre(test_int_data2& d) { return d.i; } - static void post(test_int_data2&, int const&) {} + static bool post(test_int_data2&, int const&) { return true; } static void fail(test_int_data2&) {} }; }}} /////////////////////////////////////////////////////////////////////////////// +struct test_int_data3 +{ + int i; +}; + +// we provide a custom attribute transformation taking copy of the actual +// attribute value, simulating more complex type transformations which +// may fail due to target type limitations (in this case, restricted range). +namespace boost { namespace spirit { namespace traits +{ + template <> + struct transform_attribute + { + typedef int type; + static int pre(test_int_data3& d) { return d.i; } + static bool post(test_int_data3& d, int i) + { + if (i < 5) + { + d.i = i; + return true; + } + else + { + return false; + } + } + static void fail(test_int_data3& d) + { + d.i = -1; + } + }; +}}} + +/////////////////////////////////////////////////////////////////////////////// int main() { @@ -173,5 +209,19 @@ BOOST_TEST(v.size() == 2 && v[0].i == 1 && v[1].i == 2); } + // testing controlled failure of attribute transformation + { + // the parser is expected to fail at the second value + v.clear(); + BOOST_TEST(!test_attr("3,7,2" + , qi::attr_cast(qi::int_) % ',', v)); + BOOST_TEST(v.size() == 1 && v[0].i == 3); + + // the parser is expected to hit an alternative and proceed + v.clear(); + BOOST_TEST(test_attr("3,7,2" + , (qi::attr_cast(qi::int_) | qi::omit[qi::int_]) % ',', v)); + BOOST_TEST(v.size() == 3 && v[0].i == 3 && v[1].i == -1 && v[2].i == 2); + } return boost::report_errors(); } Index: libs/spirit/test/qi/attribute2.cpp =================================================================== --- libs/spirit/test/qi/attribute2.cpp (revision 75120) +++ libs/spirit/test/qi/attribute2.cpp (working copy) @@ -60,7 +60,7 @@ { typedef int type; static int pre(test_int_data1& d) { return d.i; } - static void post(test_int_data1& d, int i) { d.i = i; } + static bool post(test_int_data1& d, int i) { d.i = i; return true; } static void fail(test_int_data1&) {} }; }}} @@ -80,7 +80,7 @@ { typedef int& type; static int& pre(test_int_data2& d) { return d.i; } - static void post(test_int_data2&, int const&) {} + static bool post(test_int_data2&, int const&) { return true; } static void fail(test_int_data2&) {} }; }}} Index: libs/spirit/test/qi/regression_transform_assignment.cpp =================================================================== --- libs/spirit/test/qi/regression_transform_assignment.cpp (revision 75120) +++ libs/spirit/test/qi/regression_transform_assignment.cpp (working copy) @@ -31,7 +31,7 @@ return fusion::tie(parts.second, parts.first); } - static void post(foo_parts &, type const &) {} + static bool post(foo_parts &, type const &) { return true; } static void fail(foo_parts &) {} }; }}} Index: libs/spirit/doc/advanced/customization_points.qbk =================================================================== --- libs/spirit/doc/advanced/customization_points.qbk (revision 75120) +++ libs/spirit/doc/advanced/customization_points.qbk (working copy) @@ -477,7 +477,7 @@ typedef type; static type pre(Exposed& val); - static void post(Exposed& val, type attr); // Qi only + static bool post(Exposed& val, type attr); // Qi only static void fail(Exposed&); // Qi only }; @@ -533,15 +533,17 @@ as exposed by the metafunction `type`). This function will be called in /Qi/ and for /Karma/.]] [[ -``void transform_attribute::post(exposed, transformed)``] +``bool transform_attribute::post(exposed, transformed)``] [Do `post`-transformation after the invocation of the right hand side component for `rule` (or the embedded component for `attr_cast`). This takes the original attribute as supplied by the user and the attribute as returned from the right hand side (embedded) component and is expected to propagate the result back - into the supplied attribute instance. This function - will be called in /Qi/ only.]] + into the supplied attribute instance. Can return `false` + if propagation can not be successfully performed, thus + failing the parser which invoked the transormation. This + function will be called in /Qi/ only.]] [[ ``void transform_attribute::fail(exposed)``] [Handling failing parse operations of the Index: libs/spirit/doc/qi/auxiliary.qbk =================================================================== --- libs/spirit/doc/qi/auxiliary.qbk (revision 75120) +++ libs/spirit/doc/qi/auxiliary.qbk (working copy) @@ -217,6 +217,26 @@ [reference_qi_attr_cast1] +[heading Advanced Example] + +Just like semantic actions, __customize_transform_attribute__ has the ability +to signal failure back to the parser. This may happen if there's no obvious +way to convert the parsed attribute value back to the exposed attribute type. +Common examples of such behavior are enumerations and range restricted +arithmetic types, but, of course, other similar use cases are also possible. + +Lets expand the definitions above into a slightly more complex example: + +[import ../../example/qi/attr_cast.cpp] +[reference_qi_auxiliary_attr_cast_data2] + +Now we can attempt to parse some values which may not necessarily fit into +a range limited type we defined: + +[reference_qi_attr_cast2] + +The full cpp file for this example can be found here: [@../../example/qi/attr_cast.cpp] + [endsect] Index: libs/spirit/example/qi/Jamfile =================================================================== --- libs/spirit/example/qi/Jamfile (revision 75120) +++ libs/spirit/example/qi/Jamfile (working copy) @@ -36,6 +36,7 @@ exe boost_array : boost_array.cpp ; exe display_attribute_type : display_attribute_type.cpp ; exe adapt_template_struct : adapt_template_struct.cpp ; +exe attr_cast : attr_cast.cpp ; exe unescaped_string : unescaped_string.cpp ; Index: libs/spirit/example/qi/reference.cpp =================================================================== --- libs/spirit/example/qi/reference.cpp (revision 75120) +++ libs/spirit/example/qi/reference.cpp (working copy) @@ -292,7 +292,8 @@ { typedef int& type; static int& pre(int_data& d) { return d.i; } - static void post(int_data& val, int const& attr) {} + // this particular transformation is always successful + static bool post(int_data& val, int const& attr) { return true; } static void fail(int_data&) {} }; }}} Index: libs/spirit/example/qi/parse_date.cpp =================================================================== --- libs/spirit/example/qi/parse_date.cpp (revision 75120) +++ libs/spirit/example/qi/parse_date.cpp (working copy) @@ -54,10 +54,11 @@ // We need to initialize the attribute supplied to the rule (referenced // by the first argument) with the values taken from the parsing // results (referenced by the second argument). - static void post(boost::gregorian::date& d, date_parts const& v) + static bool post(boost::gregorian::date& d, date_parts const& v) { d = boost::gregorian::date(fusion::at_c<0>(v), fusion::at_c<1>(v) , fusion::at_c<2>(v)); + return true; } // The function fail() is called whenever the parsing of the right hand Index: libs/spirit/example/qi/attr_cast.cpp =================================================================== --- libs/spirit/example/qi/attr_cast.cpp (revision 0) +++ libs/spirit/example/qi/attr_cast.cpp (revision 0) @@ -0,0 +1,127 @@ +// Copyright (c) 2011 Alex Dubov +// +// 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) + +// This example demonstrates a method to conditionally propagate +// a parsed value to a value restricted attribute type. + +#include +#include + +namespace qi = boost::spirit::qi; +namespace fusion = boost::fusion; + +template +void test_parser_attr( + char const* input, P const& p, T& attr, bool full_match = true) +{ + using boost::spirit::qi::parse; + + char const* f(input); + char const* l(f + strlen(f)); + if (parse(f, l, p, attr) && (!full_match || (f == l))) + std::cout << "ok" << std::endl; + else + std::cout << "fail" << std::endl; +} + +//[reference_qi_auxiliary_attr_cast_data2 +// Test structure we want to use in place of an int. Lets pretend we only +// want it to hold values strictly less that 100. +struct less_than_100 +{ + int i; +}; + +// Counter variable we shall use later on. +int greater_than_100_cnt = 0; + +// Custom attribute transformations for our primitive range restricted type. +namespace boost { namespace spirit { namespace traits +{ + template <> + struct transform_attribute + { + typedef int type; + + // Any value less than 100 is still a valid int, so no problem here. + static type pre(less_than_100& val) { return val.i; } + + // Not every int is less than 100, so we better do some checking. + static bool post(less_than_100& val, type const& attr) + { + if (attr < 100) + { + val.i = attr; + return true; + } + else + { + return false; + } + } + static void fail(less_than_100&) {} + }; +}}} +//] + +namespace std +{ + ostream& operator<<(ostream &os, less_than_100 val) + { + return (os << val.i); + } + + ostream& operator<<(ostream &os, std::vector const& val) + { + for (size_t cnt = 0; cnt < val.size(); ++cnt) + os << val[cnt] << ", "; + return os; + } + +} + + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + //[reference_qi_attr_cast2 + using qi::int_; + using qi::attr_cast; + using qi::omit; + using boost::phoenix::ref; + + less_than_100 d = { 0 }; + + // Just like the previous example, the parser succedes. + test_parser_attr("1", attr_cast(int_), d); + std::cout << d << std::endl; + + // Yet, if we try a value, larger than 100 (as defined above), the parser + // fails. + test_parser_attr("101", attr_cast(int_), d); + std::cout << d << std::endl; + + // To demonstrate the facility even further, lets pretend we want to parse + // a list of numbers, storing good values into a container and counting + // the bad ones. We will try to convert the numbers from the comma + // separated list into less_than_100 values, and if a number or two happen + // to be out of range we will drop them using the qi::omit directive and + // increment our greater_than_100_cnt variable. + std::vector v1; + + test_parser_attr("10,20,105,39,114,3,121", + (attr_cast(int_) + | omit[int_][ref(greater_than_100_cnt)++]) % ',', + v1); + // The below print outs should correctly report the 3 out of range values, + // as well as 4 correctly assigned ones. The container will still hold + // 7 values, with "bad" unassigned values substituted by defaul constructed + // ones (the specific value to substitute can be defined in + // transform_attribute::fail() method). + std::cout << "Values out of range: " << greater_than_100_cnt << std::endl; + std::cout << "Good values: " << v1 << std::endl; + //] + return 0; +} Index: libs/spirit/example/qi/adapt_template_struct.cpp =================================================================== --- libs/spirit/example/qi/adapt_template_struct.cpp (revision 75120) +++ libs/spirit/example/qi/adapt_template_struct.cpp (working copy) @@ -44,7 +44,7 @@ typedef fusion::vector type; static type pre(client::data& val) { return type(val.a, val.b); } - static void post(client::data&, fusion::vector const&) {} + static bool post(client::data&, fusion::vector const&) { return true; } static void fail(client::data&) {} }; }}} Index: libs/spirit/example/support/utree/sexpr_parser.hpp =================================================================== --- libs/spirit/example/support/utree/sexpr_parser.hpp (revision 75120) +++ libs/spirit/example/support/utree/sexpr_parser.hpp (working copy) @@ -21,7 +21,7 @@ typedef unused_type type; static unused_type pre (utree::nil_type&) { return unused_type(); } - static void post (utree::nil_type&, unused_type) { } + static bool post (utree::nil_type&, unused_type) { return true; } static void fail (utree::nil_type&) { } };