1

I am trying to write generic parser generator in boost spirit. I have come up with following code:

auto attr_to_val = [](auto& ctx) { _val(ctx) = boost::fusion::at_c<2>(_attr(ctx)); };

auto parser_gen = [](const std::string a, auto&& p) {
    return((boost::spirit::x3::string(a) >> boost::spirit::x3::blank >> p)[attr_to_val]);
};

and tried to use it like this:

int a;
auto action = [&a](auto& ctx) { a = _val(ctx); };
auto parser = (parser_gen("aaa", boost::spirit::x3::uint_))[action];
parse(bar.begin(), bar.end(), parser);

but it gives a lot of errors about being unable to convert boost::fusion::deque to int. On the other hand when I change it a bit like that, which is IMHO equivalent to the expansion of above template code:

auto pars = (
    boost::spirit::x3::string("aaa") >>
    boost::spirit::x3::blank >> boost::spirit::x3::uint_)[attr_to_val];

int a;
auto action = [&a](auto& ctx) { a = _val(ctx); };
parse(bar.begin(), bar.end(), pars);

It is all fine. What am I doing wrong and how can I make parser_gen work?

bartop
  • 9,971
  • 1
  • 23
  • 54

1 Answers1

2
  1. You don't need to expose all attributes, simplifying the attribute types considerably.

  2. For matching a string literal without exposing it as a key (which, apparently, you don't want anyways, because you're ignoring it in the semantic action), use x3::lit("aaa") instead of x3::string("aaa"). In x3 expression, a bare "aaa" will automatically be interpreted as x3::lit("aaa") (due to x3::as_parser).

  3. What's more, you are addressing at_c<2> implying that you didn't want x3::blank exposed either. Why not simply x3::omit[x3::blank]? Better yet, consider the use of a skipper, and have that implicit.

  4. In action you are using x3::_val, which depends on the declared rule's attribute (there is no x3::rule in sight?) OR the actual bound reference (you don't pass anything to x3::parse).

    Since your action binds to the parser argument it seems you wanted its attribute, which can be queried with x3::_attr() instead.

    It seems you might be able to do without semantic actions altogether, see below

Fix Ideas:

This combines all the above:

See it Live On Coliru

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

namespace x3 = boost::spirit::x3;

int main() {
    auto parser_gen = [=](std::string const a, auto&& p) {
        return x3::skip(x3::blank)[ x3::lit(a) >> p ];
    };

    for (std::string const bar : { "aaa 42", "aaa99", }) {
        int a;
        if (parse(begin(bar), end(bar), parser_gen("aaa", x3::uint_), a)) {
            std::cout << "Parsed " << a << "\n";
        } else {
            std::cout << "Failed\n";
        }
    }
}

Prints

Parsed 42
Parsed 99

See it Live On Coliru

namespace {
    template <typename P>
    auto label_gen(P p) {
        return x3::omit[ x3::lexeme[ x3::as_parser(p) >> (&x3::punct | !x3::graph) ] ];
    }

    template <typename L, typename P> auto parser_gen(L l, P p) {
        return x3::skip(x3::blank)[ label_gen(l) >> p ];
    }
}

Now prints one less match:

Parsed 42
Failed

BONUS: Doing Useful Stuff

So, my guess is you wanted to combine multiple of these label/value pairs in a useful fashion, perhaps explaining the actions. Now, you could take a page from this answer: Boost Spirit x3: parse into structs.

Actually, I will refrain from reproducing the code from that example here, but I think it might apply to your use case very well.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • One more question if you don't mind. What for is this `&punct`? – bartop Feb 12 '20 at 11:50
  • I made up constraints for delimiting "identifier" or "keyword" tokens there. The rationale was linked to in the text just above: _"using a label() helper that checks for mandatory whitespace/token boundary (see [Stop X3 symbols from matching substrings](https://stackoverflow.com/questions/33725521/stop-x3-symbols-from-matching-substrings/33727419#33727419))"_ [In your sample, you want to avoid "aaaa 42" matching the "aaa" prefix.] Just a thing I thought of from experience. – sehe Feb 12 '20 at 12:34
  • The `&` operator is documented here: https://www.boost.org/doc/libs/develop/libs/spirit/doc/x3/html/spirit_x3/quick_reference/operator.html – sehe Feb 12 '20 at 12:34