c++ - Parsing a list of doubles with boost::spirit::qi -


i'm getting familiar boost::spirit , want parse strings below:

double_1 | double_2 | ... | double_n | double_1% | double_2% ... | double_m%

where m>=0, n>=0.

for example, lines below should parse ok:

91.3 | 44 | 5e-3 | 12% | 11%

91.3 | 44 | 5e-3

12% | 11%

i want use boost::spirit::qi.

so, i've written 2 parsers below:

namespace client {     namespace qi = boost::spirit::qi;     namespace ascii = boost::spirit::ascii;      template <typename iterator>     bool parse_numbers(iterator& first, iterator last, std::vector<double>& v)     {         using qi::double_;         using qi::phrase_parse;                 using ascii::space;          bool r = phrase_parse(first, last,              //  begin grammar             (                 // double_ % '|'                 double_ >> *('|' >> double_ >> '|')             )             ,             //  end grammar              space, v);         return r;     }      template <typename iterator>     bool parse_numberswithpercents(iterator& first, iterator last, std::vector<double>& v)     {         using qi::double_;         using qi::phrase_parse;         using ascii::space;          bool r = phrase_parse(first, last,              //  begin grammar             (                 (double_ >> '%') % '|'             )             ,             //  end grammar              space, v);          if (first != last) // fail if did not full match             return false;         return r;     } } 

and, then, i'm calling them in main below:

int main() {     std::cout << "give me list of numbers in format  double_1 | double_2 | ... | double_n | double_1% | double_2% ... | double_m%\n";     std::cout << "the numbers inserted in vector of numbers\n";     std::cout << "type [q or q] quit\n\n";      std::string str;     while (getline(std::cin, str))     {         if (str.empty() || str[0] == 'q' || str[0] == 'q')             break;          std::vector<double> v;         std::string::iterator begin = str.begin(), end = str.end();         if (client::parse_numbers(begin, end, v))         {             std::cout << "-------------------------\n";             std::cout << "first part parsing succeeded\n";              (std::vector<double>::size_type = 0; < v.size(); ++i)                 std::cout << << ": " << v[i] << std::endl;              std::cout << "\n-------------------------\n";             if(begin != end) {                 if('|' == *begin) ++begin;                 if(begin != end) {                     std::cout << "parsing second part: " << std::string(begin, end) << std::endl;                     std::vector<double> v1;                      if (client::parse_numberswithpercents(begin, end, v1))                     {                         std::cout << "-------------------------\n";                         std::cout << "second part parsing succeeded\n";                          (std::vector<double>::size_type = 0; < v1.size(); ++i)                             std::cout << << ": " << v1[i] << std::endl;                          std::cout << "\n-------------------------\n";                 } else {                     std::cout << "-------------------------\n";                     std::cout << "second part parsing failed\n";                     std::cout << "-------------------------\n";                      if(begin != end) {                         std::cout << "remaining part is: " << std::string(begin, end) << std::endl; }                     }             }         }         }         else         {             std::cout << "-------------------------\n";             std::cout << "first part parsing failed\n";             std::cout << "-------------------------\n";              if(begin != end) {                 std::cout << "remaining part is: " << std::string(begin, end) << std::endl; }         }     }      std::cout << "bye... :-) \n\n";     return 0; } 

as can see, method doesn't work correct corner cases like:

91.3 | 44 | 5e-3

12% | 11%

i'm interested there way same in more simple way using boost library. or somehow correct parsers right parsing of above corner cases. nice have first , second parts in separate containers.

thanks in advance.

hah. intuition should enormously simple. however, i've come conclude indeed it's bit nontrivial.

the problem making non-list-repeat separator optional. thought long , hard elegant way make optional , came this:

live on coliru

#include <boost/spirit/include/qi.hpp>  namespace qi = boost::spirit::qi;  namespace {     using double_vec = std::vector<double>;     using         = std::string::const_iterator;      static const qi::rule<it, double_vec(bool percent), qi::blank_type> doubles_         = (qi::double_ >> (qi::eps(qi::_r1) >> '%' | !qi::lit('%'))) % '|'; }  int main() {     std::string str;     while (std::getline(std::cin, str)) {         f = str.begin(), l = str.end();          double_vec v, w;          bool ok = qi::phrase_parse(f, l,                    (doubles_(false) >> -('|' >> doubles_(true)))                  | qi::attr(double_vec{}) >> doubles_(true),                 qi::blank, v, w);          if (ok && f == l) {             std::cout << "parsed " << v.size() << "/" << w.size() << " elements\n";         } else {             std::istringstream iss(str);             if (iss >> str && (str == "q" || str == "q"))                 break;             std::cout << "invalid input. remaining '" << std::string(f,l) << "'\n";         }     } } 

which produces following result given test inputs:

./test <<input 91.3 | 44 | 5e-3 | 12% | 11%  91.3 | 44 | 5e-3  12% | 11% q input parsed 3/2 elements parsed 3/0 elements parsed 0/2 elements 

depending on you're trying /actually/ achieve here, things more elegant

update in response comments, here's how i'd improve relaxing grammar. note how shift ignoring '|' skipper:

live on coliru

qi::phrase_parse(         f, l, *(qi::double_>>!qi::lit('%')) >> *(qi::double_>>'%'),         qi::blank | '|', v, w); 

Popular posts from this blog