为简单的语法提升精神编译错误

时间:2017-06-23 22:55:25

标签: c++11 compiler-errors boost-spirit

我正在尝试使用以下规则编译解析器:

else_statement =
    lit("else") > statement;

if_statement =
    lit("if") >> '(' >> expression >> ')' >> statement >> -else_statement;

else_statement的属性为statement,与其消耗的statement规则一样。 if_statement的属性是一个结构,其成员分别为expressionstatement和可选的statementboost::optional<statement>)。

使用以下BOOST_FUSION_ADAPT_STRUCT

BOOST_FUSION_ADAPT_STRUCT(ast::statement, m_statement_node)
BOOST_FUSION_ADAPT_STRUCT(ast::if_statement, m_condition, m_then, m_else)

其中m_statement_node对于可能的不同语句是boost::variant

我希望如果else_statement存在,则会将其放入boost::optional<statement>,因为else_statement的属性为statement。如果我lit("else") >规则中注释掉else_statement ,那么有效!但是lit("else")出现了一些奇怪的事情:现在,boost :: spirit试图将statement放入可选statement成员中(提升: :变种)显然不会编译,因为它只需要A或B.

生成的编译错误如下所示:

/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&)’

我做错了什么?我该如何解决这个问题?

在显示错误的完整测试代码段下方(并在lit("else") >注释时编译)。

// 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;
}

1 个答案:

答案 0 :(得分:1)

自动属性传播规则在由单个元素组成的Fusion序列中存在一些问题。您可以通过声明:

来解决此问题
rule<ast::statement_node> statement;

(从ast::statement更改为ast::statement_node)。

这有效: Live On Coliru

替代解决方法

更繁琐的解决方法是避免在那里使用单元素融合序列。您可以向statement添加虚拟字段:

struct statement
{
    statement_node m_statement_node;
    int dummy;

    friend std::ostream& operator<<(std::ostream& os, statement const& statement)
    { return os << "STATEMENT:" << statement.m_statement_node; }
};

BOOST_FUSION_ADAPT_STRUCT(ast::statement, m_statement_node, dummy)

然后为它添加一个值:

statement = (a | b) >> attr(42);

这也消除了混乱。

<强> Live On Wandbox

// File: so.cpp
// Compile as: g++ -std=c++11 so.cpp
//#define BOOST_SPIRIT_DEBUG

#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;
        int dummy;

        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, dummy)
    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) >> attr(42);

            else_statement = lit("else") > statement;

            if_statement = lit("if") >> '(' >> expression >> ')' >> statement >> -else_statement;

            expression = int_;

            a = 'A' >> attr(1);
            b = 'B' >> attr(2);

            BOOST_SPIRIT_DEBUG_NODES( (statement) (else_statement) (if_statement) (expression) (a) (b));
        }
    };

} // namespace client

int main()
{
    for (std::string const input : {
                "if (1) A else 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(), 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;
    }
}

打印

Parsed: IF_STATEMENT:expression; STATEMENT:A;  STATEMENT:B

背景

但请注意,如果您错误地使else_statement规则 的长度相同,则会出现同样的混淆:

else_statement = lit("else") > statement > attr(42); // this is wrong

当然,这实际上并没有意义,但错误信息有助于解释引擎盖下的真正问题(如果&#34;上游&#34;融合序列出现&#34;兼容& #34;然后#34;解构&#34;用于传播)。 qi / nonterminal / rule.hpp中的相关注释:

// do up-stream transformation, this integrates the results
// back into the original attribute value, if appropriate
traits::post_transform(attr_param, attr_);