Opened 7 years ago

Closed 7 years ago

#12094 closed Bugs (fixed)

Operator > changes attribute type

Reported by: mikhail.strelnikov@… Owned by: Joel de Guzman
Milestone: To Be Determined Component: spirit
Version: Boost 1.61.0 Severity: Problem
Keywords: Cc:

Description

Following code should compile, but it does not.

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

#include <string>

using namespace boost::spirit::x3;

auto r = rule<class r, std::string>{} = +char_;

int main()
{
    std::string input = "";

    std::string output;

    auto b = std::begin(input);
    auto e = std::end(input);

    parse(b, e, 'a' > (r | r), output);
}

Change History (15)

comment:1 by Joel de Guzman, 7 years ago

It may be unintuitive, but it is not really a bug. Sequences (including expect operators in x3) may accept a tuple or container (e.g. string). If it gets a container, it expects its nodes to have an attribute of either a container, or the container's value type. See http://www.boost.org/doc/libs/1_60_0/libs/spirit/doc/html/spirit/qi/reference/operator/sequence.html

But what is the natural (inherent) attribute type of (r | r)? It is a variant, not a container. That is why X3 got tripped. It can't handle the ambiguity. X3's attribute handling mechanism is not perfect. As it descends into the alternative, it already decided that its attribute cannot handle the container, and thus tries to pass the element type of the container.

Tip: Don't fight the attribute mechanism. Try to form your attributes as closely as possible to the rules and grammars that models it.

Last edited 7 years ago by Joel de Guzman (previous) (diff)

comment:2 by Joel de Guzman, 7 years ago

Resolution: wontfix
Status: newclosed

comment:3 by mikhail.strelnikov@…, 7 years ago

Why 'a' >> (r | r) compiles then? :-)

I'd expect attr of (r | r) to be std::string because attr of r is std::string, why it should be variant?

comment:4 by mikhail.strelnikov@…, 7 years ago

Resolution: wontfix
Status: closedreopened

Sorry, I had to reopen this issue - not sure if commenting on closed issues does anything.

'a' >> (r | r) - compiles, attr is std::string

*('a' >> (r | r)) - compiles, attr is std::string



'a' > (r | r) - does not compile

*('a' > (r | r)) - attr is std::vector<std::string>. Wat?

comment:5 by Joel de Guzman, 7 years ago

Hmmm... Does 'a' >> (r | r) give you the correct behavior? It's not enough that it compiles.

comment:6 by mikhail.strelnikov@…, 7 years ago

Yes, it works as expected http://melpon.org/wandbox/permlink/htuj4CIXodD02pSb (skips character 'a', stores 'b'). I don't quite see why it shouldn't.

comment:7 by anonymous, 7 years ago

I see that now and I know the reason why: I made some special treatment for alternatives. Alas the special case handling does not apply for expect_directive<alternative>, or whatever else that might hold an alternative. I'm not sure if I should 'fix' this. But that special case becomes a precedent indeed ;-/

In any case, allow me to repeat myself again: don't fight the attribute mechanism. Try to form your attributes as closely as possible to the rules and grammars that models it.

comment:8 by mikhail.strelnikov@…, 7 years ago

So, I could write ('a' >> expect[r | r]) instead? Does not compile, but now it is as expected. :-)

About "don't fight":

std::string input = "\\n\\t\\u1234";
...
parse(b, e, *('\\' > (escape_seq | unicode)), ...);

What type of attribute it should return? It can be std::vector<boost::variant<esc_ast, unicode_ast>> (where esc_ast and unicode_ast are inheriting from x3::position_tagged because you need this) or in can be simple std::string. I think you can't tell the attribute type just by looking at the rule.

comment:9 by Joel de Guzman, 7 years ago

So you say "Does not compile, but now it is as expected. :-)". Actually, this:

    'a' > (r | r)

is the same as this:

    'a' >> expect[r | r]

But you say that the first is not expected and the second is expected.

in reply to:  8 comment:10 by Joel de Guzman, 7 years ago

Replying to mikhail.strelnikov@…:

About "don't fight":

std::string input = "\\n\\t\\u1234";
...
parse(b, e, *('\\' > (escape_seq | unicode)), ...);

What type of attribute it should return? It can be std::vector<boost::variant<esc_ast, unicode_ast>> (where esc_ast and unicode_ast are inheriting from x3::position_tagged because you need this) or in can be simple std::string. I think you can't tell the attribute type just by looking at the rule.

If esc_ast, unicode_ast can both be a string, then the natural attribute would be vector<std::string>. An attribute of std::string is just pushing it.

in reply to:  9 ; comment:11 by mikhail.strelnikov@…, 7 years ago

Why this works?

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

#include <string>

using namespace boost::spirit::x3;

auto r = rule<class r, std::string>{} = +char_;
auto r_or_r = rule<class r, std::string>{} = r | r;
auto expect_r_or_r = rule<class r, std::string>{} = expect[r_or_r];
auto a_expect_r_or_r = rule<class r, std::string>{} = 'a' > expect_r_or_r;


int main()
{
    std::string input = "a";

    std::string output;

    auto b = std::begin(input);
    auto e = std::end(input);

    parse(b, e, a_expect_r_or_r, output);
}

in reply to:  11 comment:12 by Joel de Guzman, 7 years ago

Replying to mikhail.strelnikov@…:

Why this works?

[snip]

Why? Because you placed it in a rule with an exact attribute. Spirit does not have to deduce (guess) what you want.

comment:13 by Joel de Guzman, 7 years ago

OK, alright, I pushed a "fix" for this issue. The reason I did it anyway, despite my comments above, is that the special case handling for alternatives became a precedent and it would be unwise not to give the expect operator another spacial treatment due to the implicit nature of the expression:

a > (b | c)

comment:14 by Joel de Guzman, 7 years ago

Please try the Boost develop branch.

comment:15 by Joel de Guzman, 7 years ago

Resolution: fixed
Status: reopenedclosed

Oh and BTW, thanks for your persistence! I really appreciate it.

Note: See TracTickets for help on using tickets.