I am having trouble composing grammars. Suppose I have a class Derived
which inherits from Base
. GrammarDerived
has a Derived
synthesized attribute while GrammarBase
has a Base
synthesized attribute. How can I use GrammarBase
in a GrammarDerived
parsing rule? I feel this should be possible because I can bind a Base &
to a Derived &
but nothing seems to work.
In other words, how do I get grammarBase
to interact with _val
by reference 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;
};
In a simpler setting, this shows how it's mostly a limitation of type deduction here:
Derived parse_result;
bool ok = qi::phrase_parse(f, l, base_, qi::space, data);
will not work when the parser exposes a Base, however you can fix it with a "type hint" for the template instantion[1]:
bool ok = qi::phrase_parse(f, l, base_, qi::space, static_cast<Base&>(data));
Full demo Live On Coliru
#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";
}
}
You can avoid the confusion by explicitly passing a reference to the exposable attribute to the base 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 the glue_
/base_
separationby using qi::attr_cast<Base, Base>
(but I wouldn't do this for legibility).
Full code again for reference Live On Coliru
#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 the instantiation of function template qi::phrase_parse
here