I am trying to compile a parser with the following rules:
else_statement =
lit("else") > statement;
if_statement =
lit("if") >> '(' >> expression >> ')' >> statement >> -else_statement;
The attribute of else_statement
is statement
, as is the statement
rule that it consumes. The attribute of if_statement
is a struct with members respectively types expression
, statement
and an optional statement
(boost::optional<statement>
).
Using the following BOOST_FUSION_ADAPT_STRUCT
's:
BOOST_FUSION_ADAPT_STRUCT(ast::statement, m_statement_node)
BOOST_FUSION_ADAPT_STRUCT(ast::if_statement, m_condition, m_then, m_else)
where m_statement_node
is a boost::variant
for different statements that are possible.
I'd expect that if an else_statement
is present, it will be put into the boost::optional<statement>
, since the attribute of else_statement
is statement
. And this does work if I comment out the lit("else") >
in the else_statement
rule! But with the lit("else")
present something strange happens: now boost::spirit is trying to fit a statement
into the member of the optional statement
(the boost::variant) which obviously won't compile because that only takes an A or B.
The resulting compile error looks like this:
/usr/include/boost/variant/variant.hpp:1534:38: error: no matching function for call to ‘boost::variant<ast::A, ast::B>::initializer::initialize(void*, const ast::statement&)’
What am I doing wrong? How can I solve this?
Below a complete test snippet that shows the error (and compiles when the lit("else") >
is comment out).
// File: so.cpp
// Compile as: g++ -std=c++11 so.cpp
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/optional/optional_io.hpp>
#include <iostream>
#include <string>
#include <vector>
namespace ast
{
struct A { int a; friend std::ostream& operator<<(std::ostream& os, A const&) { return os << "A"; } };
struct B { int b; friend std::ostream& operator<<(std::ostream& os, B const&) { return os << "B"; } };
struct expression { int e; friend std::ostream& operator<<(std::ostream& os, expression const&) { return os << "expression"; } };
using statement_node = boost::variant<A, B>;
struct statement
{
statement_node m_statement_node;
friend std::ostream& operator<<(std::ostream& os, statement const& statement)
{ return os << "STATEMENT:" << statement.m_statement_node; }
};
struct if_statement
{
expression m_condition;
statement m_then;
boost::optional<statement> m_else;
friend std::ostream& operator<<(std::ostream& os, if_statement const& if_statement)
{
os << "IF_STATEMENT:" << if_statement.m_condition << "; " << if_statement.m_then;
if (if_statement.m_else)
os << "; " << if_statement.m_else;
return os;
}
};
} // namespace ast
BOOST_FUSION_ADAPT_STRUCT(ast::expression, e)
BOOST_FUSION_ADAPT_STRUCT(ast::A, a)
BOOST_FUSION_ADAPT_STRUCT(ast::B, b)
BOOST_FUSION_ADAPT_STRUCT(ast::statement, m_statement_node)
BOOST_FUSION_ADAPT_STRUCT(ast::if_statement, m_condition, m_then, m_else)
namespace client
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
template <typename Iterator>
class test_grammar : public qi::grammar<Iterator, ast::if_statement(), qi::space_type>
{
private:
template<typename T> using rule = qi::rule<Iterator, T(), qi::space_type>;
rule<ast::A> a;
rule<ast::B> b;
rule<ast::statement> statement;
rule<ast::statement> else_statement;
rule<ast::if_statement> if_statement;
rule<int> expression;
public:
test_grammar() : test_grammar::base_type(if_statement, "result_grammar")
{
using namespace qi;
statement =
a | b;
else_statement =
lit("else") > statement;
if_statement =
lit("if") >> '(' >> expression >> ')' >> statement >> -else_statement;
expression =
int_;
a = 'A';
b = 'B';
BOOST_SPIRIT_DEBUG_NODES(
(statement)
(else_statement)
(if_statement)
(expression)
(a)
(b)
);
}
};
} // namespace client
int main()
{
std::string const input{"if (1) A B"};
using iterator_type = std::string::const_iterator;
using test_grammar = client::test_grammar<iterator_type>;
namespace qi = boost::spirit::qi;
test_grammar program;
iterator_type iter{input.begin()};
iterator_type const end{input.end()};
ast::if_statement out;
bool r = qi::phrase_parse(iter, end, program, qi::space, out);
if (!r || iter != end)
{
std::cerr << "Parsing failed." << std::endl;
return 1;
}
std::cout << "Parsed: " << out << std::endl;
}