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;
};
source to share
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
source to share