Re: x3 semantic action and attribute transformation

Olaf Peter <[email protected]>
Newsgroups gmane.comp.parsers.spirit.general
Message-ID <[email protected]>
Hello Seth,

thank you much!

> On 08-03-17 10:35, Olaf Peter wrote:
>>    auto combine_to_uint = [](auto &ctx) {
>>      auto const &v = x3::_val(ctx);
>>      std::ostringstream ss;
>>      std::copy(v.begin(), v.end(), std::ostream_iterator<int>(ss, ""));
>>      uint int_ { 0 };
>>      x3::_pass(ctx) = x3::parse(ss.str().begin(), ss.str().end(),
>> x3::uint_, int_);
>>      std::cout << int_ << '\n';
>>      x3::_attr(ctx) = int_;
>>    };
>
> Here it seems you mainly confused `_attr` and `_val`. Avoid confusing
> names like `int_`.

maybe it was left from prior test; _attr is the attribute from rule and 
_val from lexeme which calls the semantic action, isn't it?

> Here's your own sample "fixed" (I still think it's too complicated):
> http://coliru.stacked-crooked.com/a/a6e053b5f552f766

thank you for this approach, I did try to solve the overflow problem and 
come to this solution attached (for some reason, stacked-crooked doesn't 
share my code), which works so far but looks quite complicated.

Also, I changed the rule to be more restrict, but this requires changes 
in the 'combine' lambda function. These are required to cover the last 4 
test cases which aren't valid ints.

Thanks,
Olaf

---8<---
#include <iosfwd>
//#define BOOST_SPIRIT_X3_DEBUG

#include <boost/spirit/home/x3.hpp>

#include <iostream>


namespace parser {

   namespace x3 = boost::spirit::x3;

   using x3::char_;

   typedef x3::rule<struct integer_class, uint32_t> integer_type;

   integer_type const integer { "integer" };

   constexpr uint32_t digit_treshold(auto i) {
       return std::numeric_limits<decltype(i)>::max() % 10;
   }

   auto const combine = [](auto &ctx) {
       //typedef decltype(x3::_attr(ctx)) base_type; // =>> templated?
       typedef uint32_t base_type;
       base_type result { 0 };
       uint32_t iter_cnt { 0 };
       for (auto&& ch : x3::_attr(ctx)) {
           switch (ch) {
               case '_': break;
               default:
                   if(iter_cnt > std::numeric_limits<base_type>::digits10) {
                       x3::_pass(ctx) = false;
                       continue;
                   }
                   if(iter_cnt++ < 
std::numeric_limits<base_type>::digits10) {
                       result = result*10 + (ch - '0');
                   }
                   else {
#if 0
                       switch(ch - '0') {
                       case 0: // [[fallthrough]]
                       case 1: // [[fallthrough]]
                       case 2: // [[fallthrough]]
                       case 3: // [[fallthrough]]
                       case 4: // [[fallthrough]]
                       case 5:
                           result = result*10 + (ch - '0');
                           break;
                       default:
                           x3::_pass(ctx) = false;
                       }
#else
                       if((unsigned)(ch - '0') > digit_treshold(result)) {
                     	  x3::_pass(ctx) = false;
                       }
                       else {
                     	  result = result*10 + (ch - '0');
                       }
#endif
                   }
           }
       }

       x3::_val(ctx) = result;
   };

   // Parse '1_000' as 1000
   auto const integer_def =
     //x3::lexeme [ char_("0-9") >> *(char_("0-9_")) ] [combine] /* 
_val(ctx) => uint */
     x3::lexeme [ +char_("0-9_") ] [combine]
     ;

   BOOST_SPIRIT_DEFINE(integer)
}


int main()
{
   namespace x3 = boost::spirit::x3;

   std::vector<std::string> const test_cases {
       "0",
       "1",
       "01",
       "1_000",
       "42_666_4711",
       "4_294_967_295", // uint32::max
       // below must fail to parse
       "4_294_967_296",
       "4_294_967_295_0",
       "4_294_967_295_00",
       "_42", "42_",
       " 4 2 ", "4 _2"
       };

   typedef std::string::const_iterator iterator_type;

   for(auto str: test_cases) {
     iterator_type iter = str.begin();
     iterator_type const end = str.end();

     auto& rule = parser::integer;

     std::cout << "parse `" << str << "´:\n";

     uint32_t i;

     bool r = x3::phrase_parse(iter, end, rule, x3::space, i);

     if (r && iter == end) {
       std::cout << "succeeded: \"" << str << "\" -> " << i << "\n";
     } else {
       std::cout << "failed\n";
     }
   }

   return 0;
}

--->8---

------------------------------------------------------------------------------
Announcing the Oxford Dictionaries API! The API offers world-renowned
dictionary content that is easy and intuitive to access. Sign up for an
account today to start using our lexical data to power your apps and
projects. Get started today and enter our developer competition.
http://sdm.link/oxford
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.