Ticket #6050: spirit-transform-attribute-post-bool-v3.patch

File spirit-transform-attribute-post-bool-v3.patch, 24.3 KB (added by oakad@…, 11 years ago)

Added test cases, example and updated documentation

  • boost/spirit/repository/home/qi/nonterminal/subrule.hpp

     
    213213            // without passing values for them.
    214214            context_type context(*this, attr_);
    215215
    216             if (def.binder(first, last, context, skipper))
     216            Iterator i = first;
     217
     218            if (def.binder(i, last, context, skipper))
    217219            {
    218220                // do up-stream transformation, this integrates the results
    219221                // back into the original attribute value, if appropriate
    220                 traits::post_transform(attr, attr_);
    221                 return true;
     222                if (traits::post_transform(attr, attr_)) {
     223                    first = i;
     224                    return true;
     225                }
    222226            }
    223227
    224228            // inform attribute transformation of failed rhs
     
    267271            // passing values of incompatible types for them.
    268272            context_type context(*this, attr_, params, caller_context);
    269273
    270             if (def.binder(first, last, context, skipper))
     274            Iterator i = first;
     275
     276            if (def.binder(i, last, context, skipper))
    271277            {
    272278                // do up-stream transformation, this integrates the results
    273279                // back into the original attribute value, if appropriate
    274                 traits::post_transform(attr, attr_);
    275                 return true;
     280                if (traits::post_transform(attr, attr_)) {
     281                    first = i;
     282                    return true;
     283                }
    276284            }
    277285
    278286            // inform attribute transformation of failed rhs
  • boost/spirit/home/qi/nonterminal/rule.hpp

     
    296296                // attributes, without passing values for them.
    297297                context_type context(attr_);
    298298
     299                Iterator i = first;
     300
    299301                // If you are seeing a compilation error here stating that the
    300302                // fourth parameter can't be converted to a required target type
    301303                // then you are probably trying to use a rule or a grammar with
    302304                // an incompatible skipper type.
    303                 if (f(first, last, context, skipper))
     305                if (f(i, last, context, skipper))
    304306                {
    305307                    // do up-stream transformation, this integrates the results
    306308                    // back into the original attribute value, if appropriate
    307                     traits::post_transform(attr, attr_);
    308                     return true;
     309                    if (traits::post_transform(attr, attr_)) {
     310                        first = i;
     311                        return true;
     312                    }
    309313                }
    310314
    311315                // inform attribute transformation of failed rhs
     
    342346                // attributes, passing values of incompatible types for them.
    343347                context_type context(attr_, params, caller_context);
    344348
     349                Iterator i = first;
     350
    345351                // If you are seeing a compilation error here stating that the
    346352                // fourth parameter can't be converted to a required target type
    347353                // then you are probably trying to use a rule or a grammar with
    348354                // an incompatible skipper type.
    349                 if (f(first, last, context, skipper))
     355                if (f(i, last, context, skipper))
    350356                {
    351357                    // do up-stream transformation, this integrates the results
    352358                    // back into the original attribute value, if appropriate
    353                     traits::post_transform(attr, attr_);
    354                     return true;
     359                    if (traits::post_transform(attr, attr_)) {
     360                        first = i;
     361                        return true;
     362                    }
    355363                }
    356364
    357365                // inform attribute transformation of failed rhs
  • boost/spirit/home/qi/detail/attributes.hpp

     
    2222
    2323        static Transformed pre(Exposed&) { return Transformed(); }
    2424
    25         static void post(Exposed& val, Transformed const& attr)
     25        static bool post(Exposed& val, Transformed const& attr)
    2626        {
    2727            traits::assign_to(attr, val);
     28            return true;
    2829        }
    2930
    3031        // fail() will be called by Qi rule's if the rhs failed parsing
     
    3738    {
    3839        typedef Attribute& type;
    3940        static Attribute& pre(Attribute& val) { return val; }
    40         static void post(Attribute&, Attribute const&) {}
     41        static bool post(Attribute&, Attribute const&) { return true; }
    4142        static void fail(Attribute&) {}
    4243    };
    4344
     
    4748        typedef Transformed type;
    4849
    4950        static Transformed pre(Exposed& val) { return Transformed(val); }
    50         static void post(Exposed&, Transformed const&) { /* no-op */ }
     51        static bool post(Exposed&, Transformed const&) { return true; }
    5152
    5253        // fail() will be called by Qi rule's if the rhs failed parsing
    5354        static void fail(Exposed&) {}
     
    5960    {
    6061        typedef Attribute& type;
    6162        static Attribute& pre(Attribute& val) { return val; }
    62         static void post(Attribute&, Attribute const&) {}
     63        static bool post(Attribute&, Attribute const&) { return true; }
    6364        static void fail(Attribute&) {}
    6465    };
    6566
     
    8788                val = Transformed();
    8889            return boost::get<Transformed>(val);
    8990        }
    90         static void post(boost::optional<Exposed>&, Transformed const&) {}
     91        static bool post(boost::optional<Exposed>&, Transformed const&) { return true; }
    9192        static void fail(boost::optional<Exposed>& val)
    9293        {
    9394             val = none_t();    // leave optional uninitialized if rhs failed
     
    100101    {
    101102        typedef Attribute& type;
    102103        static Attribute& pre(Attribute& val) { return val; }
    103         static void post(Attribute&, Attribute const&) {}
     104        static bool post(Attribute&, Attribute const&) { return true; }
    104105        static void fail(Attribute&) {}
    105106    };
    106107
     
    110111    {
    111112        typedef unused_type type;
    112113        static unused_type pre(unused_type) { return unused; }
    113         static void post(unused_type, unused_type) {}
     114        static bool post(unused_type, unused_type) { return true; }
    114115        static void fail(unused_type) {}
    115116    };
    116117
     
    160161
    161162    ///////////////////////////////////////////////////////////////////////////
    162163    template <typename Exposed, typename Transformed>
    163     void post_transform(Exposed& dest, Transformed const& attr)
     164    bool post_transform(Exposed& dest, Transformed const& attr)
    164165    {
    165166        return transform_attribute<Exposed, Transformed, qi::domain>::post(dest, attr);
    166167    }
  • boost/spirit/home/qi/auxiliary/attr_cast.hpp

     
    9292
    9393            typename transform::type attr_ = transform::pre(attr);
    9494
    95             if (!compile<qi::domain>(subject).
    96                     parse(first, last, context, skipper, attr_))
     95            Iterator i = first;
     96
     97            if (compile<qi::domain>(subject).
     98                    parse(i, last, context, skipper, attr_))
    9799            {
    98                 transform::fail(attr);
    99                 return false;
     100                // do up-stream transformation, this mainly integrates the results
     101                // back into the original attribute value, if appropriate
     102                if (traits::post_transform(attr, attr_)) {
     103                    first = i;
     104                    return true;
     105                }
    100106            }
    101107
    102             // do up-stream transformation, this mainly integrates the results
    103             // back into the original attribute value, if appropriate
    104             traits::post_transform(attr, attr_);
    105             return true;
     108            transform::fail(attr);
     109            return false;
    106110        }
    107111
    108112        template <typename Context>
  • boost/spirit/home/support/adapt_adt_attributes.hpp

     
    257257        {
    258258            return val;
    259259        }
    260         static void
     260        static bool
    261261        post(
    262262            fusion::extension::adt_attribute_proxy<T, N, false>& val
    263263          , Attribute const& attr)
    264264        {
    265265            val = attr;
     266            return true;
    266267        }
    267268        static void
    268269        fail(fusion::extension::adt_attribute_proxy<T, N, false>&)
     
    289290        {
    290291            return val;
    291292        }
    292         static void
     293        static bool
    293294        post(
    294295            fusion::extension::adt_attribute_proxy<T, N, Const>&
    295296          , Attribute const&)
    296297        {
     298            return true;
    297299        }
    298300        static void
    299301        fail(fusion::extension::adt_attribute_proxy<T, N, Const>&)
  • libs/spirit/test/qi/attribute1.cpp

     
    2020#include <boost/spirit/include/qi_operator.hpp>
    2121#include <boost/spirit/include/qi_nonterminal.hpp>
    2222#include <boost/spirit/include/qi_auxiliary.hpp>
     23#include <boost/spirit/include/qi_directive.hpp>
    2324
    2425#include <iostream>
    2526#include <vector>
     
    6061    {
    6162        typedef int type;
    6263        static int pre(test_int_data1& d) { return d.i; }
    63         static void post(test_int_data1& d, int i) { d.i = i; }
     64        static bool post(test_int_data1& d, int i) { d.i = i; return true; }
    6465        static void fail(test_int_data1&) {}
    6566    };
    6667}}}
     
    8081    {
    8182        typedef int& type;
    8283        static int& pre(test_int_data2& d) { return d.i; }
    83         static void post(test_int_data2&, int const&) {}
     84        static bool post(test_int_data2&, int const&) { return true; }
    8485        static void fail(test_int_data2&) {}
    8586    };
    8687}}}
    8788
    8889///////////////////////////////////////////////////////////////////////////////
     90struct test_int_data3
     91{
     92    int i;
     93};
     94
     95// we provide a custom attribute transformation taking copy of the actual
     96// attribute value, simulating more complex type transformations which
     97// may fail due to target type limitations (in this case, restricted range).
     98namespace boost { namespace spirit { namespace traits
     99{
     100    template <>
     101    struct transform_attribute<test_int_data3, int, qi::domain>
     102    {
     103        typedef int type;
     104        static int pre(test_int_data3& d) { return d.i; }
     105        static bool post(test_int_data3& d, int i)
     106        {
     107            if (i < 5)
     108            {
     109                d.i = i;
     110                return true;
     111            }
     112            else
     113            {
     114                return false;
     115            }
     116        }
     117        static void fail(test_int_data3& d)
     118        {
     119           d.i = -1;
     120        }
     121    };
     122}}}
     123
     124///////////////////////////////////////////////////////////////////////////////
    89125int
    90126main()
    91127{
     
    173209        BOOST_TEST(v.size() == 2 && v[0].i == 1 && v[1].i == 2);
    174210    }
    175211
     212    // testing controlled failure of attribute transformation
     213    {
     214        // the parser is expected to fail at the second value
     215        std::vector<test_int_data3> v;
     216        BOOST_TEST(!test_attr("3,7,2", qi::attr_cast(qi::int_) % ',', v));
     217        BOOST_TEST(v.size() == 1 && v[0].i == 3);
     218
     219        v.clear();
     220        BOOST_TEST(!test_attr("3,7,2"
     221          , qi::attr_cast<test_int_data3>(qi::int_) % ',', v));
     222        BOOST_TEST(v.size() == 1 && v[0].i == 3);
     223
     224        v.clear();
     225        BOOST_TEST(!test_attr("3,7,2"
     226          , qi::attr_cast<test_int_data3, int>(qi::int_) % ',', v));
     227        BOOST_TEST(v.size() == 1 && v[0].i == 3);
     228
     229        // the parser is expected to hit an alternative and proceed
     230        v.clear();
     231        BOOST_TEST(test_attr("3,7,2"
     232          , (qi::attr_cast<test_int_data3>(qi::int_) | qi::omit[qi::int_]) % ',', v));
     233        BOOST_TEST(v.size() == 3 && v[0].i == 3 && v[1].i == -1 && v[2].i == 2);
     234    }
    176235    return boost::report_errors();
    177236}
  • libs/spirit/test/qi/attribute2.cpp

     
    6060    {
    6161        typedef int type;
    6262        static int pre(test_int_data1& d) { return d.i; }
    63         static void post(test_int_data1& d, int i) { d.i = i; }
     63        static bool post(test_int_data1& d, int i) { d.i = i; return true; }
    6464        static void fail(test_int_data1&) {}
    6565    };
    6666}}}
     
    8080    {
    8181        typedef int& type;
    8282        static int& pre(test_int_data2& d) { return d.i; }
    83         static void post(test_int_data2&, int const&) {}
     83        static bool post(test_int_data2&, int const&) { return true; }
    8484        static void fail(test_int_data2&) {}
    8585    };
    8686}}}
  • libs/spirit/test/qi/regression_transform_assignment.cpp

     
    3131            return fusion::tie(parts.second, parts.first);
    3232        }
    3333
    34         static void post(foo_parts &, type const &) {}
     34        static bool post(foo_parts &, type const &) { return true; }
    3535        static void fail(foo_parts &) {}
    3636    };
    3737}}}   
  • libs/spirit/doc/advanced/customization_points.qbk

     
    477477        typedef <unspecified> type;
    478478
    479479        static type pre(Exposed& val);
    480         static void post(Exposed& val, type attr);    // Qi only
     480        static bool post(Exposed& val, type attr);    // Qi only
    481481        static void fail(Exposed&);                   // Qi only
    482482    };
    483483
     
    533533                         as exposed by the metafunction `type`). This function
    534534                         will be called in /Qi/ and for /Karma/.]]
    535535    [[
    536 ``void transform_attribute<Exposed, Transformed, Domain>::post(exposed, transformed)``]
     536``bool transform_attribute<Exposed, Transformed, Domain>::post(exposed, transformed)``]
    537537                        [Do `post`-transformation after the invocation of the
    538538                         right hand side component for `rule` (or the embedded
    539539                         component for `attr_cast`). This takes the original
    540540                         attribute as supplied by the user and the attribute
    541541                         as returned from the right hand side (embedded)
    542542                         component and is expected to propagate the result back
    543                          into the supplied attribute instance. This function
    544                          will be called in /Qi/ only.]]
     543                         into the supplied attribute instance. Can return `false`
     544                         if propagation can not be successfully performed, thus
     545                         failing the parser which invoked the transormation. This
     546                         function will be called in /Qi/ only.]]
    545547    [[
    546548``void transform_attribute<Exposed, Transformed, Domain>::fail(exposed)``]
    547549                        [Handling failing parse operations of the
  • libs/spirit/doc/qi/auxiliary.qbk

     
    217217
    218218[reference_qi_attr_cast1]
    219219
     220[heading Advanced Example]
     221
     222Just like semantic actions, __customize_transform_attribute__ has the ability
     223to signal failure back to the parser. This may happen if there's no obvious
     224way to convert the parsed attribute value back to the exposed attribute type.
     225Common examples of such behavior are enumerations and range restricted
     226arithmetic types, but, of course, other similar use cases are also possible.
     227
     228Lets expand the definitions above into a slightly more complex example:
     229
     230[import ../../example/qi/attr_cast.cpp]
     231[reference_qi_auxiliary_attr_cast_data2]
     232
     233Now we can attempt to parse some values which may not necessarily fit into
     234a range limited type we defined:
     235
     236[reference_qi_attr_cast2]
     237
     238The full cpp file for this example can be found here: [@../../example/qi/attr_cast.cpp]
     239
    220240[endsect]
    221241
    222242
  • libs/spirit/example/qi/Jamfile

     
    3636exe boost_array : boost_array.cpp ;
    3737exe display_attribute_type : display_attribute_type.cpp ;
    3838exe adapt_template_struct : adapt_template_struct.cpp ;
     39exe attr_cast : attr_cast.cpp ;
    3940
    4041exe unescaped_string : unescaped_string.cpp ;
    4142
  • libs/spirit/example/qi/reference.cpp

     
    292292    {
    293293        typedef int& type;
    294294        static int& pre(int_data& d) { return d.i; }
    295         static void post(int_data& val, int const& attr) {}
     295        // this particular transformation is always successful
     296        static bool post(int_data& val, int const& attr) { return true; }
    296297        static void fail(int_data&) {}
    297298    };
    298299}}}
  • libs/spirit/example/qi/parse_date.cpp

     
    5454        // We need to initialize the attribute supplied to the rule (referenced
    5555        // by the first argument) with the values taken from the parsing
    5656        // results (referenced by the second argument).
    57         static void post(boost::gregorian::date& d, date_parts const& v)
     57        static bool post(boost::gregorian::date& d, date_parts const& v)
    5858        {
    5959            d = boost::gregorian::date(fusion::at_c<0>(v), fusion::at_c<1>(v)
    6060              , fusion::at_c<2>(v));
     61            return true;
    6162        }
    6263
    6364        // The function fail() is called whenever the parsing of the right hand
  • libs/spirit/example/qi/attr_cast.cpp

     
     1//  Copyright (c) 2011 Alex Dubov
     2//
     3//  Distributed under the Boost Software License, Version 1.0. (See accompanying
     4//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
     5
     6//  This example demonstrates a method to conditionally propagate
     7//  a parsed value to a value restricted attribute type.
     8
     9#include <boost/spirit/include/qi.hpp>
     10#include <boost/spirit/include/phoenix_operator.hpp>
     11
     12namespace qi = boost::spirit::qi;
     13namespace fusion = boost::fusion;
     14
     15template <typename P, typename T>
     16void test_parser_attr(
     17    char const* input, P const& p, T& attr, bool full_match = true)
     18{
     19    using boost::spirit::qi::parse;
     20
     21    char const* f(input);
     22    char const* l(f + strlen(f));
     23    if (parse(f, l, p, attr) && (!full_match || (f == l)))
     24        std::cout << "ok" << std::endl;
     25    else
     26        std::cout << "fail" << std::endl;
     27}
     28
     29//[reference_qi_auxiliary_attr_cast_data2
     30// Test structure we want to use in place of an int. Lets pretend we only
     31// want it to hold values strictly less that 100.
     32struct less_than_100
     33{
     34    int i;
     35};
     36
     37// Counter variable we shall use later on.
     38int greater_than_100_cnt = 0;
     39
     40// Custom attribute transformations for our primitive range restricted type.
     41namespace boost { namespace spirit { namespace traits
     42{
     43    template <>
     44    struct transform_attribute<less_than_100, int, qi::domain>
     45    {
     46        typedef int type;
     47
     48        // Any value less than 100 is still a valid int, so no problem here.
     49        static type pre(less_than_100& val) { return val.i; }
     50
     51        // Not every int is less than 100, so we better do some checking.
     52        static bool post(less_than_100& val, type const& attr)
     53        {
     54            if (attr < 100)
     55            {
     56                val.i = attr;
     57                return true;
     58            }
     59            else
     60            {
     61                return false;
     62            }
     63        }
     64        static void fail(less_than_100&) {}
     65    };
     66}}}
     67//]
     68
     69namespace std
     70{
     71    ostream& operator<<(ostream &os, less_than_100 val)
     72    {
     73        return (os << val.i);
     74    }
     75
     76    ostream& operator<<(ostream &os, std::vector<less_than_100> const& val)
     77    {
     78        for (size_t cnt = 0; cnt < val.size(); ++cnt)
     79            os << val[cnt] << ", ";
     80        return os;
     81    }
     82
     83}
     84
     85
     86///////////////////////////////////////////////////////////////////////////////
     87int main()
     88{
     89    //[reference_qi_attr_cast2
     90    using qi::int_;
     91    using qi::attr_cast;
     92    using qi::omit;
     93    using boost::phoenix::ref;
     94
     95    less_than_100 d = { 0 };
     96
     97    // Just like the previous example, the parser succedes.
     98    test_parser_attr("1", attr_cast(int_), d);
     99    std::cout << d << std::endl;
     100
     101    // Yet, if we try a value, larger than 100 (as defined above), the parser
     102    // fails.
     103    test_parser_attr("101", attr_cast(int_), d);
     104    std::cout << d << std::endl;
     105
     106    // To demonstrate the facility even further, lets pretend we want to parse
     107    // a list of numbers, storing good values into a container and counting
     108    // the bad ones. We will try to convert the numbers from the comma
     109    // separated list into less_than_100 values, and if a number or two happen
     110    // to be out of range we will drop them using the qi::omit directive and
     111    // increment our greater_than_100_cnt variable.
     112    std::vector<less_than_100> v1;
     113
     114    test_parser_attr("10,20,105,39,114,3,121",
     115                     (attr_cast<less_than_100>(int_)
     116                      | omit[int_][ref(greater_than_100_cnt)++]) % ',',
     117                     v1);
     118    // The below print outs should correctly report the 3 out of range values,
     119    // as well as 4 correctly assigned ones. The container will still hold
     120    // 7 values, with "bad" unassigned values substituted by defaul constructed
     121    // ones (the specific value to substitute can be defined in
     122    // transform_attribute::fail() method).
     123    std::cout << "Values out of range: " << greater_than_100_cnt << std::endl;
     124    std::cout << "Good values: " << v1 << std::endl;
     125    //]
     126    return 0;
     127}
  • libs/spirit/example/qi/adapt_template_struct.cpp

     
    4444        typedef fusion::vector<A&, B&> type;
    4545
    4646        static type pre(client::data<A, B>& val) { return type(val.a, val.b); }
    47         static void post(client::data<A, B>&, fusion::vector<A&, B&> const&) {}
     47        static bool post(client::data<A, B>&, fusion::vector<A&, B&> const&) { return true; }
    4848        static void fail(client::data<A, B>&) {}
    4949    };
    5050}}}
  • libs/spirit/example/support/utree/sexpr_parser.hpp

     
    2121  typedef unused_type type;
    2222
    2323  static unused_type pre (utree::nil_type&) { return unused_type(); }
    24   static void post (utree::nil_type&, unused_type) { }
     24  static bool post (utree::nil_type&, unused_type) { return true; }
    2525  static void fail (utree::nil_type&) { }
    2626};
    2727