Subparser Attribute

I am having trouble writing grammars. Suppose I have a class Derived

that inherits from Base

. GrammarDerived

has an attribute Derived

, but GrammarBase

has an attribute Base

. How to use GrammarBase

in a parse rule GrammarDerived

? I believe this should be possible because I can bind Base &

to Derived &

, but nothing seems to work.

In other words, how do I get GrammarBase

to interact with _val

the link below?

template<typename Iterator>
struct GrammarDerived : public grammar <Iterator, Derived()> {
    GrammarDerived() : GrammarDerived::base_type(start) {
        start = rule1[bind(someFunc, _val)] >> grammarBase;
        rule1 = /* ... */;
    }
    rule<Iterator, Derived()> start;
    rule<Iterator, Derived()> rule1;
    GrammarBase grammarBase;
};

      

+3


source to share


1 answer


In a simpler setup, this shows how basically this type inference constraint is here:

Derived parse_result;
bool ok = qi::phrase_parse(f, l, base_, qi::space, data);

      

will not work if the parser provides a base, however you can fix it with a "type hint" for the template instance [1] :

bool ok = qi::phrase_parse(f, l, base_, qi::space, static_cast<Base&>(data));

      

Full Live On Coliru Demo

#include <algorithm>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>

namespace qi      = boost::spirit::qi;
namespace phoenix = boost::phoenix;

struct Base {
    int x;
    double y;
};

BOOST_FUSION_ADAPT_STRUCT(Base, (int,x)(double,y))

struct Derived : Base { };

int main()
{
    typedef std::string::const_iterator It;
    qi::rule<It, Base(), qi::space_type> base_ = qi::int_ >> qi::double_;

    std::string const input = "1 3.14";
    auto f(input.begin()), l(input.end());

    Derived parse_result;
    bool ok = qi::phrase_parse(f, l, base_, qi::space, static_cast<Base&>(parse_result));
    if (ok)
    {
        std::cout << "Parsed: " << parse_result.x << " " << parse_result.y << "\n";
    } else
    {
        std::cout << "Parse failed\n";
    }

    if (f != l)
    {
        std::cout << "Input remaining: '" << std::string(f,l) << "'\n";
    }
}

      

As an alternative



You can avoid confusion by explicitly passing the exposed attribute reference to the underlying parser / rule:

template <typename It, typename Skipper = qi::space_type>
struct derived_grammar : qi::grammar<It, Derived(), Skipper>
{
    derived_grammar() : derived_grammar::base_type(start) {
        base_ = qi::int_ >> qi::double_;
        glue_ = base_ [ qi::_r1 = qi::_1 ];
        start = "derived:" >> glue_(qi::_val); // passing the exposed attribute for the `Base&` reference
    }
  private:
    qi::rule<It, Derived(),   Skipper> start;
    qi::rule<It, void(Base&), Skipper> glue_;
    qi::rule<It, Base(),      Skipper> base_; // could be a grammar instead of a rule
};

      

If you really insist, you can do without separating glue_

/ base_

by using qi::attr_cast<Base, Base>

(but I wouldn't do that for readability).

Full code again for Live On Coliru reference

#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <algorithm>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi  = boost::spirit::qi;
namespace phx = boost::phoenix;

struct Base {
    int x;
    double y;
};

BOOST_FUSION_ADAPT_STRUCT(Base, (int,x)(double,y))

struct Derived : Base { };

template <typename It, typename Skipper = qi::space_type>
struct derived_grammar : qi::grammar<It, Derived(), Skipper>
{
    derived_grammar() : derived_grammar::base_type(start) {
        base_ = qi::int_ >> qi::double_;
        glue_ = base_ [ qi::_r1 = qi::_1 ];
        start = "derived:" >> glue_(qi::_val); // passing the exposed attribute for the `Base&` reference
    }
  private:
    qi::rule<It, Derived(),   Skipper> start;
    qi::rule<It, void(Base&), Skipper> glue_;
    qi::rule<It, Base(),      Skipper> base_; // could be a grammar instead of a rule
};

int main()
{
    typedef std::string::const_iterator It;
    derived_grammar<It> g;

    std::string const input = "derived:1 3.14";
    auto f(input.begin()), l(input.end());

    Derived parse_result;
    bool ok = qi::phrase_parse(f, l, g, qi::space, parse_result);
    if (ok)
    {
        std::cout << "Parsed: " << parse_result.x << " " << parse_result.y << "\n";
    } else
    {
        std::cout << "Parse failed\n";
    }

    if (f != l)
    {
        std::cout << "Input remaining: '" << std::string(f,l) << "'\n";
    }
}

      


[1] referring to creating a function template qi::phrase_parse

here

+2


source







All Articles