X3, what is attr_gen?

2019-07-08 09:57发布

I end up getting these move errors a lot and am not quite sure why other than having something to do with the way I'm parsing strings. Remove everything having to do with 'dummy' and the errors come back.

Someone mentioned using attr_gen (couldn't find this in the docs) and by doing so, I can get past these "traits::move_to" compile errors, but the parser still fails. I've marked the lines that I've added to get it to compile, but don't think are necessary with "<---".

#define BOOST_SPIRIT_X3_DEBUG

#include <complex>
#include <iostream>
#include <string>
#include <vector>

#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 {
            int num1;
            int num2;  
        };

        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, num1, num2)
BOOST_FUSION_ADAPT_STRUCT(client::ast::input, comments, numbers)

namespace client {      
    namespace parser {

        namespace x3 = boost::spirit::x3;
        namespace ascii = boost::spirit::x3::ascii;

        using namespace x3;
        x3::attr_gen dummy;   // <---                   

        auto const comment = char_ % ' ' >> dummy(false);       // <---
        //auto const comment = lexeme[+graph] >> dummy(false);
        auto const number = int_ >> int_;

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

        auto const input = skip(blank) [
            lines(comment) >> 
            lines(number)
        ];
    }
}

int main()
{
    namespace x3 = boost::spirit::x3;
    using boost::spirit::x3::ascii::blank;
    using x3::char_;

    std::string const iss(R"(this is a test
    1 2)");

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

    client::ast::input types;

    bool ok = parse(iter, eof, client::parser::input, 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 << boost::fusion::as_deque(item) << "\n"; }
    for (auto& item : types.numbers)    { std::cout << boost::fusion::as_deque(item) << "\n"; }
}

2条回答
在下西门庆
2楼-- · 2019-07-08 10:04

x3::attr() is perfectly well documented (the generator type behind it is an implementation detail, much like you'd use x3::int_, not x3::int_gen).

The fact that you need it has been answered before. The key is having single-element fusion sequences.

查看更多
【Aperson】
3楼-- · 2019-07-08 10:16
comment = char_ % ' ';

This parses 1 or more arbitrary characters separated by single space literals. In your case it may match only 't' because what follows is 'h', not a ' '.

I'd naturally spell that

comment = *(char_ - eol);

So it would match anything until eol. If you insist on matching "wordy" things, by all means

comment = skip(blank) [ *lexeme[+graph_] ];

To synthesize the matched input into a string attribute, wrap it in raw[]:

comment = raw [ skip(blank) [ *graph_ ] ];

Or if, indeed you want no other whitespace than ' ', make it so

comment = raw[ skip(' ') [ *graph_ ] ];

It's slightly weird to spell it like

comment = raw[ (+graph_) % ' ' ];

Because

  • it doesn't match an empty line
  • it doesn't match "hello world" or `"hello\tworld"'
查看更多
登录 后发表回答