1

I tried to adjust this example to make use of a variable number of elements using a group vector instead of hard coding 3 ints (n1, n2, n3), but to no avail.

Here is the example I've tried to modify. coliru.stacked-crooked.com/a/90110f91a4ac466a

Here's the code.

#define BOOST_SPIRIT_X3_DEBUG
#include <iostream>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>

namespace client { 
    namespace ast {

        struct number {
            //struct group { int n1, n2, n3; }; // instead of 3 ints ...
            struct group {
                std::vector<int> persons;       // get a variable number
                //bool dummy;
            };

            std::vector<group> groups;
            bool dummy;
        };

        struct comment {
            std::string text;
            bool dummy;
        };

        struct input {
            std::vector<comment> comments;  
            std::vector<number> numbers;
        }; 
    } 
}

BOOST_FUSION_ADAPT_STRUCT(client::ast::comment, text, dummy)
BOOST_FUSION_ADAPT_STRUCT(client::ast::number::group, persons)
BOOST_FUSION_ADAPT_STRUCT(client::ast::number, groups, dummy)
BOOST_FUSION_ADAPT_STRUCT(client::ast::input, comments, numbers)

namespace client {      
    namespace parser {

        namespace x3 = boost::spirit::x3;
        using namespace x3;

        typedef std::string::const_iterator It;

        using namespace x3;

        auto const comment = rule<struct _c, ast::comment> {"comment"} = lexeme[*(char_ - eol)] >> attr(false);
        auto const number  = rule<struct _n, ast::number> {"number"}   = *(int_ >> int_ >> int_) >> attr(false);

        auto lines = [](auto p) { return *(p >> eol); };

        auto const input = 
            repeat(1)[comment] >> eol >>
            lines(number);
    }
}

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

    std::string const iss("any char string here\n1 2 3\n1 2 3 4 5 6\n1 2 3 4 5 6 7 8 9\n");

    auto iter = iss.begin(), eof = iss.end();

    client::ast::input types;

    bool ok = phrase_parse(iter, eof, client::parser::input, x3::blank, types);

    if (iter != eof) {
        std::cout << "Remaining unparsed: '" << std::string(iter, eof) << "'\n";
    }
    std::cout << "Parsed: " << (100.0 * std::distance(iss.begin(), iter) / iss.size()) << "%\n";
    std::cout << "ok = " << ok << std::endl;

    for (auto &item : types.comments) {
        std::cout << "comment: " << boost::fusion::as_deque(item) << "\n";
    }
    /*for (auto& item : types.numbers) {
        std::cout << "number:  ";
        for (auto& g : item.groups)
            std::cout << boost::fusion::as_deque(g) << " ";
        std::cout << "\n";
    }*/
}

The error messages always look the same, deep in the templates. So instead of hard-coding the number of int's to be parsed, the number of int's should be variable, but still divisible by 3 (i.e., total number of ints on a line is 3, 6, 9, etc...).

Ender
  • 1,652
  • 2
  • 25
  • 50
  • What didn't work for you in the solution [here](https://stackoverflow.com/a/45932905/85371)? – sehe Aug 31 '17 at 22:22
  • Wait, why do you have a `vector` when `group` is already a `vector`? – sehe Aug 31 '17 at 22:24
  • I think I see your point. Let me look at that again. I initially tried to modify this previous example where the ints were placed in a struct and left it as such. ... http://coliru.stacked-crooked.com/a/07c176a99c2d5371 – Ender Aug 31 '17 at 22:41

1 Answers1

2

Here's a vast simplification.

If you just want to parse lines with n numbers:

*int_

If you want to validate that the number n is divisible by 3:

(*int_) [is_valid_group]

The semantic action can just do the check

auto is_valid_group = [](auto& ctx) {
    _pass(ctx) = 0 == (_val(ctx).size() % 3);
};

Now the whole AST can simply be:

using person = int;
using groups_line = std::vector<person>;
using comment_line = std::string;

struct input {
    comment_line comments;
    std::vector<groups_line> numbers;
}; 

And the whole grammar:

auto const comment_line 
    = lexeme[*(char_ - eol)];
auto const groups_line  
    = rule<struct _n, ast::groups_line, true>  {"groups_line"}   
    = *int_ >> eps [ is_valid_group ];

auto const input = 
    comment_line >> eol >>
    *(groups_line >> eol);

DEMO

Live On Coliru

//#define BOOST_SPIRIT_X3_DEBUG
#include <iostream>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

namespace client { 
    namespace ast {

        using person = int;
        using groups_line = std::vector<person>;
        using comment_line = std::string;

        struct input {
            comment_line comments;
            std::vector<groups_line> numbers;
        }; 
    } 
}

BOOST_FUSION_ADAPT_STRUCT(client::ast::input, comments, numbers)

namespace client {      
    namespace parser {

        namespace x3 = boost::spirit::x3;
        using namespace x3;

        typedef std::string::const_iterator It;

        using namespace x3;

        auto is_valid_group = [](auto& ctx) {
            _pass(ctx) = 0 == (_val(ctx).size() % 3);
        };

        auto const comment_line 
            //= rule<struct _c, ast::comment_line> {"comment_line"} 
            = lexeme[*(char_ - eol)];
        auto const groups_line  
            = rule<struct _n, ast::groups_line, true>  {"groups_line"}   
            = *int_ >> eps [ is_valid_group ];

        auto const input = 
            comment_line >> eol >>
            *(groups_line >> eol);
    }
}

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

    std::string const iss("any char string here\n1 2 3\n1 2 3 4 5 6\n1 2 3 4 5 6 7 8 9\n1 2 3 4");

    auto iter = iss.begin(), eof = iss.end();

    client::ast::input types;

    bool ok = phrase_parse(iter, eof, client::parser::input, x3::blank, types);

    if (iter != eof) {
        std::cout << "Remaining unparsed: '" << std::string(iter, eof) << "'\n";
    }
    std::cout << "Parsed: " << (100.0 * std::distance(iss.begin(), iter) / iss.size()) << "%\n";
    std::cout << "ok = " << ok << std::endl;

    std::cout << "comment: " << types.comments << "\n";
    for (auto& group : types.numbers) {
        std::cout << "number:  ";
        for (auto& person : group) std::cout << person << " ";
        std::cout << "\n";
    }
}

Prints

Remaining unparsed: '1 2 3 4'
Parsed: 89.0625%
ok = 1
comment: any char string here
number:  1 2 3 
number:  1 2 3 4 5 6 
number:  1 2 3 4 5 6 7 8 9 
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thanks, not exactly what I was aiming for, but the response still helps in other ways (using semantic actions), so I'll mark it. I removed 'struct group' and added the vector as a member of 'struct number'. – Ender Aug 31 '17 at 22:57
  • By the way, here's a simplistic approach to actually doing the table you show at the start of [your older question](https://stackoverflow.com/questions/45930038/x3-how-to-create-parser-to-read-in-sets/45932905#45932905): **[Live On Coliru](http://coliru.stacked-crooked.com/a/d0013352f6af8020)**. Note the consistent simplification due to separation of concerns. – sehe Aug 31 '17 at 22:57
  • Re.: "not exactly what I was aiming for" - asking the right question isn't that simple. Note how I asked for _["specify the goal functionally, often best expressed with concrete examples"](https://stackoverflow.com/questions/45930038/x3-how-to-create-parser-to-read-in-sets/45932905#comment78940652_45932905)_. I'm not saying it's easy. I'm saying you can help us help you. – sehe Aug 31 '17 at 22:59
  • "instead of hard-coding the number of int's to be parsed, the number of int's should be variable, but still divisible by 3 (i.e., total number of ints on a line is 3, 6, 9, etc...). ", fields, not values. "... to make use of a variable number of elements using a group vector instead of hard coding 3 ints (n1, n2, n3)" <- var names as shown, so I wasn't referring to the value divisible by 3, rather, and as the parser shows, that the number of fields parsed is divisible by 3. Hey, I get it, things aren't easy to explain over text. I could say the same in so many ways. I appreciate the help. – Ender Aug 31 '17 at 23:15
  • That's why examples speak better. – sehe Aug 31 '17 at 23:16
  • Didn't I give one w/ comments within it? Now I'm confused on what you're asking. – Ender Aug 31 '17 at 23:29
  • @Ender Functional examples are not code. They are examples of input and output (potentially with illustrative type definitions) – sehe Aug 31 '17 at 23:38
  • Ah, functional, now if I could get it working, I wouldn't be here. Thanks. – Ender Aug 31 '17 at 23:41
  • @Ender functional. Not functioning. Functional as opposed to technical. By giving examples of requirements you don't need any code at all. The code helps people fit the answer into your frame of reference. But in your case I've continuously been looking for the _functional_ (non-technical) goal. The code only showed what you do not want. – sehe Aug 31 '17 at 23:47
  • 1
    Fair enough, I'll start aiming for that. Thanks again. – Ender Sep 01 '17 at 00:14