1

I'm having some trouble using the alternative parser, I need the parser to rollback changes if the first option failed. I tried using hold[] but I get the error that it "could not deduce template argument"

I'm trying to parse an AST, and I'm storing it in:

struct node;

BOOST_FUSION_DEFINE_STRUCT(
    , node,
    (std::string, element)
    (std::string, category)
    (std::vector<node>, children)
    )

'element' is the element parsed, 'category' it's classification, and then I have a vector to store his children

So, to exemplify I have several cases similar to:

ABC=
    (qi::char_('a')[at_c<0>(qi::_val) = "a", at_c<1>(qi::_val) = "ABC"]
    >> B[push_back(at_c<2>(qi::_val), qi::_1)]
    >> qi::char_('c'))
    ;

ABD=
    (qi::char_('a')[at_c<0>(qi::_val) = "a", at_c<1>(qi::_val) = "ABD"]
    >> B[push_back(at_c<2>(qi::_val), qi::_1)]
    >> qi::char_('d')
    )
    ;

test = ABC | ABD;

B is some rule that returns a node. If I run this I get B repeated for the second alternative, since it was pushed in the first. As I said I tried:

test = hold[ABC]| ABD;

but I get that error. The solution I've come to is to store the result of rule B in a local variable and then push it only at the end of the rule (meaning the rule matches). But that results in very complicated semantic actions, any alternative?

ildjarn
  • 62,044
  • 9
  • 127
  • 211
kaladin
  • 55
  • 4
  • A SSCCE helps. You can see me struggle to get a working test: https://www.livecoding.tv/sehe/ – sehe Nov 03 '15 at 00:19

1 Answers1

1

I would say all the semantic actions are useless here[1].

All of them can be replaced by a well-placed qi::attr to simply synthesize the desired attribute in-place and you can again use the automatic attribute propagation magic of Spirit.

In my experience, if you start using semantic actions for the simplest things, you're better of without Spirit

Simplify first

So, here's my take at the AST node type:

struct node {
    std::string element, category;
    std::vector<node> children;
};

BOOST_FUSION_ADAPT_STRUCT(node, element, category, children)

It's functionally equivalent to yours, but more c++-like, and therefore somewhat more friendly. Now, you didn't give the rule declarations, so "imagined" them as follows:

qi::rule<It, node(), qi::space_type> ABC, ABD, test;
qi::rule<It, std::vector<node>(), qi::space_type> B;

// also for demo purposes:
B = '{' >> -(test % ',') >> '}';

Now, my suggestion of you rules, simplified:

ABC = qi::string("a") >> qi::attr("ABC") >> B >> 'c';
ABD = qi::string("a") >> qi::attr("ABD") >> B >> 'd';

test = ABC | ABD;

Indeed, you need to hold because /otherwise/ both ABC/ABD will bind to the same internal synthesized attribut node&:

ABC = qi::hold[ qi::string("a") >> qi::attr("ABC") >> B >> 'c' ];
ABD = qi::hold[ qi::string("a") >> qi::attr("ABD") >> B >> 'd' ];

This is all you wanted/needed.

Test/Demo Program

I constructed a few test cases for the grammar as I "reverse-engi-magined" it and added debug code:

//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>

struct node {
    std::string element, category;
    std::vector<node> children;
};

BOOST_FUSION_ADAPT_STRUCT(node, element, category, children)

static std::ostream& operator<<(std::ostream& os, node const& n) {
    return os << boost::fusion::as_vector(n);
}

static std::ostream& operator<<(std::ostream& os, std::vector<node> const& v) {
    os << "{";
    for (size_t i = 0; v.size()>1 &&  i<v.size()-1; ++i)
        os << v.at(i) << ", ";

    if (!v.empty()) os << v.back();
    return os << "}";
}

namespace qi = boost::spirit::qi;

int main() {
    using It = std::string::const_iterator;

    qi::rule<It, node(), qi::space_type> ABC, ABD, test;
    qi::rule<It, std::vector<node>(), qi::space_type> B;

#if 1
    ABC = qi::hold[ qi::string("a") >> qi::attr("ABC") >> B >> 'c' ];
    ABD = qi::hold[ qi::string("a") >> qi::attr("ABD") >> B >> 'd' ];
#else
    ABC = qi::string("a") >> qi::attr("ABC") >> B >> 'c' ;
    ABD = qi::string("a") >> qi::attr("ABD") >> B >> 'd' ;
#endif

    test = ABC | ABD;
    B    = '{' >> -(test % ',') >> '}';

    for (std::string const s : {
            "a{a{}c}c",
            "a{a{}d}d",
            "a{a{}c}d",
            "a{a{}d,a{}c}c",
        })
    {
        std::cout << "\n-- Parsing '" << s << "'\n";
        It f = s.begin(), l = s.end();

        node parsed;
        bool ok = qi::phrase_parse(f, l, test, qi::space, parsed);

        if (ok)
            std::cout << "Parsed: " << parsed << "\n";
        else
            std::cout << "Parse failed\n";

        if (f!=l)
            std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
    }
}

Output (with holds):

-- Parsing 'a{a{}c}c'
Parsed: (a ABC {(a ABC {})})

-- Parsing 'a{a{}d}d'
Parsed: (a ABD {(a ABD {})})

-- Parsing 'a{a{}c}d'
Parsed: (a ABD {(a ABC {})})

-- Parsing 'a{a{}d,a{}c}c'
Parsed: (a ABC {(a ABD {}), (a ABC {})})

[1]. See also Boost Spirit: "Semantic actions are evil"?

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Here's the recorded live feed in case you want to know how I went about things: https://www.livecoding.tv/video/showcase-qi-hold-and-semanticactionsareevil/ ([experiment](http://chat.stackoverflow.com/transcript/10?m=24182469#24182469)) – sehe Nov 03 '15 at 01:09
  • But how can I use the automatic attribute propagation if the order of the elements I am parsing sometimes does not matches the order of the struct. For example I may have an expression: `A >> operator >> B` , for instance "a+b", so the element of the node is the operator '+' and 'a','b' are his children. – kaladin Nov 03 '15 at 15:02
  • You can just ADAPT_STRUCT in a different order or USE ADAPT_STRUCT_NAMED – sehe Nov 03 '15 at 15:03
  • I tried it in coliru and it works, but when I try the same code in Visual Studio 2013 with boost version 1.59, qi::hold gets the error that it could no deduce template argument – kaladin Nov 26 '15 at 14:54
  • @kaladin: it may be a bug in Visual Studio 2013, can you try Visual Studio 2015 – Chris Beck Jan 10 '16 at 05:05